Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[pandora-kernel.git] / drivers / media / video / cx18 / cx18-av-core.c
index 0e5006b..f164b7f 100644 (file)
@@ -4,7 +4,7 @@
  *  Derived from cx25840-core.c
  *
  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
- *  Copyright (C) 2008  Andy Walls <awalls@radix.net>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
  *
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
@@ -129,6 +129,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd)
 {
        struct cx18_av_state *state = to_cx18_av_state(sd);
        struct cx18 *cx = v4l2_get_subdevdata(sd);
+       int default_volume;
        u32 v;
 
        cx18_av_loadfw(cx);
@@ -247,8 +248,23 @@ static void cx18_av_initialize(struct v4l2_subdev *sd)
 /*             CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
 /*    } */
        cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
-       state->default_volume = 228 - cx18_av_read(cx, 0x8d4);
-       state->default_volume = ((state->default_volume / 2) + 23) << 9;
+       default_volume = cx18_av_read(cx, 0x8d4);
+       /*
+        * Enforce the legacy volume scale mapping limits to avoid
+        * -ERANGE errors when initializing the volume control
+        */
+       if (default_volume > 228) {
+               /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */
+               default_volume = 228;
+               cx18_av_write(cx, 0x8d4, 228);
+       } else if (default_volume < 20) {
+               /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */
+               default_volume = 20;
+               cx18_av_write(cx, 0x8d4, 20);
+       }
+       default_volume = (((228 - default_volume) >> 1) + 23) << 9;
+       state->volume->cur.val = state->volume->default_value = default_volume;
+       v4l2_ctrl_handler_setup(&state->hdl);
 }
 
 static int cx18_av_reset(struct v4l2_subdev *sd, u32 val)
@@ -901,206 +917,103 @@ static int cx18_av_s_radio(struct v4l2_subdev *sd)
        return 0;
 }
 
-static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct cx18 *cx = v4l2_get_subdevdata(sd);
 
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
-               if (ctrl->value < 0 || ctrl->value > 255) {
-                       CX18_ERR_DEV(sd, "invalid brightness setting %d\n",
-                                    ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx18_av_write(cx, 0x414, ctrl->value - 128);
+               cx18_av_write(cx, 0x414, ctrl->val - 128);
                break;
 
        case V4L2_CID_CONTRAST:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       CX18_ERR_DEV(sd, "invalid contrast setting %d\n",
-                                    ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx18_av_write(cx, 0x415, ctrl->value << 1);
+               cx18_av_write(cx, 0x415, ctrl->val << 1);
                break;
 
        case V4L2_CID_SATURATION:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       CX18_ERR_DEV(sd, "invalid saturation setting %d\n",
-                                    ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx18_av_write(cx, 0x420, ctrl->value << 1);
-               cx18_av_write(cx, 0x421, ctrl->value << 1);
+               cx18_av_write(cx, 0x420, ctrl->val << 1);
+               cx18_av_write(cx, 0x421, ctrl->val << 1);
                break;
 
        case V4L2_CID_HUE:
-               if (ctrl->value < -128 || ctrl->value > 127) {
-                       CX18_ERR_DEV(sd, "invalid hue setting %d\n",
-                                    ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx18_av_write(cx, 0x422, ctrl->value);
+               cx18_av_write(cx, 0x422, ctrl->val);
                break;
 
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_MUTE:
-               return cx18_av_audio_s_ctrl(cx, ctrl);
-
        default:
                return -EINVAL;
        }
        return 0;
 }
 
-static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct cx18 *cx = v4l2_get_subdevdata(sd);
-
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128;
-               break;
-       case V4L2_CID_CONTRAST:
-               ctrl->value = cx18_av_read(cx, 0x415) >> 1;
-               break;
-       case V4L2_CID_SATURATION:
-               ctrl->value = cx18_av_read(cx, 0x420) >> 1;
-               break;
-       case V4L2_CID_HUE:
-               ctrl->value = (s8)cx18_av_read(cx, 0x422);
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_MUTE:
-               return cx18_av_audio_g_ctrl(cx, ctrl);
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       struct cx18_av_state *state = to_cx18_av_state(sd);
-
-       switch (qc->id) {
-       case V4L2_CID_BRIGHTNESS:
-               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
-       case V4L2_CID_CONTRAST:
-       case V4L2_CID_SATURATION:
-               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
-       case V4L2_CID_HUE:
-               return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
-       default:
-               break;
-       }
-
-       switch (qc->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               return v4l2_ctrl_query_fill(qc, 0, 65535,
-                       65535 / 100, state->default_volume);
-       case V4L2_CID_AUDIO_MUTE:
-               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-               return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
-       default:
-               return -EINVAL;
-       }
-       return -EINVAL;
-}
-
-static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
-{
-       if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
-               return -EINVAL;
-       return cx18_av_g_sliced_fmt(sd, &fmt->fmt.sliced);
-}
-
-static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
 {
        struct cx18_av_state *state = to_cx18_av_state(sd);
        struct cx18 *cx = v4l2_get_subdevdata(sd);
-
-       struct v4l2_pix_format *pix;
        int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
        int is_50Hz = !(state->std & V4L2_STD_525_60);
 
-       switch (fmt->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               pix = &(fmt->fmt.pix);
+       if (fmt->code != V4L2_MBUS_FMT_FIXED)
+               return -EINVAL;
 
-               Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4;
-               Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4;
+       fmt->field = V4L2_FIELD_INTERLACED;
+       fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
 
-               Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
-               Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
+       Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4;
+       Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4;
 
-               /*
-                * This adjustment reflects the excess of vactive, set in
-                * cx18_av_std_setup(), above standard values:
-                *
-                * 480 + 1 for 60 Hz systems
-                * 576 + 3 for 50 Hz systems
-                */
-               Vlines = pix->height + (is_50Hz ? 3 : 1);
-
-               /*
-                * Invalid height and width scaling requests are:
-                * 1. width less than 1/16 of the source width
-                * 2. width greater than the source width
-                * 3. height less than 1/8 of the source height
-                * 4. height greater than the source height
-                */
-               if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
-                   (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
-                       CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n",
-                                    pix->width, pix->height);
-                       return -ERANGE;
-               }
+       Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
+       Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
 
-               HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20);
-               VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
-               VSC &= 0x1fff;
+       /*
+        * This adjustment reflects the excess of vactive, set in
+        * cx18_av_std_setup(), above standard values:
+        *
+        * 480 + 1 for 60 Hz systems
+        * 576 + 3 for 50 Hz systems
+        */
+       Vlines = fmt->height + (is_50Hz ? 3 : 1);
 
-               if (pix->width >= 385)
-                       filter = 0;
-               else if (pix->width > 192)
-                       filter = 1;
-               else if (pix->width > 96)
-                       filter = 2;
-               else
-                       filter = 3;
+       /*
+        * Invalid height and width scaling requests are:
+        * 1. width less than 1/16 of the source width
+        * 2. width greater than the source width
+        * 3. height less than 1/8 of the source height
+        * 4. height greater than the source height
+        */
+       if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) ||
+           (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+               CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n",
+                            fmt->width, fmt->height);
+               return -ERANGE;
+       }
 
-               CX18_DEBUG_INFO_DEV(sd,
-                                   "decoder set size %dx%d -> scale  %ux%u\n",
-                                   pix->width, pix->height, HSC, VSC);
-
-               /* HSCALE=HSC */
-               cx18_av_write(cx, 0x418, HSC & 0xff);
-               cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff);
-               cx18_av_write(cx, 0x41a, HSC >> 16);
-               /* VSCALE=VSC */
-               cx18_av_write(cx, 0x41c, VSC & 0xff);
-               cx18_av_write(cx, 0x41d, VSC >> 8);
-               /* VS_INTRLACE=1 VFILT=filter */
-               cx18_av_write(cx, 0x41e, 0x8 | filter);
-               break;
+       HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20);
+       VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+       VSC &= 0x1fff;
 
-       default:
-               return -EINVAL;
-       }
+       if (fmt->width >= 385)
+               filter = 0;
+       else if (fmt->width > 192)
+               filter = 1;
+       else if (fmt->width > 96)
+               filter = 2;
+       else
+               filter = 3;
+
+       CX18_DEBUG_INFO_DEV(sd,
+                           "decoder set size %dx%d -> scale  %ux%u\n",
+                           fmt->width, fmt->height, HSC, VSC);
+
+       /* HSCALE=HSC */
+       cx18_av_write(cx, 0x418, HSC & 0xff);
+       cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff);
+       cx18_av_write(cx, 0x41a, HSC >> 16);
+       /* VSCALE=VSC */
+       cx18_av_write(cx, 0x41c, VSC & 0xff);
+       cx18_av_write(cx, 0x41d, VSC >> 8);
+       /* VS_INTRLACE=1 VFILT=filter */
+       cx18_av_write(cx, 0x41e, 0x8 | filter);
        return 0;
 }
 
@@ -1368,14 +1281,22 @@ static int cx18_av_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
+static const struct v4l2_ctrl_ops cx18_av_ctrl_ops = {
+       .s_ctrl = cx18_av_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
        .g_chip_ident = cx18_av_g_chip_ident,
        .log_status = cx18_av_log_status,
        .load_fw = cx18_av_load_fw,
        .reset = cx18_av_reset,
-       .queryctrl = cx18_av_queryctrl,
-       .g_ctrl = cx18_av_g_ctrl,
-       .s_ctrl = cx18_av_s_ctrl,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
        .s_std = cx18_av_s_std,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register = cx18_av_g_register,
@@ -1398,8 +1319,7 @@ static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = {
 static const struct v4l2_subdev_video_ops cx18_av_video_ops = {
        .s_routing = cx18_av_s_video_routing,
        .s_stream = cx18_av_s_stream,
-       .g_fmt = cx18_av_g_fmt,
-       .s_fmt = cx18_av_s_fmt,
+       .s_mbus_fmt = cx18_av_s_mbus_fmt,
 };
 
 static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = {
@@ -1440,8 +1360,42 @@ int cx18_av_probe(struct cx18 *cx)
        snprintf(sd->name, sizeof(sd->name),
                 "%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
        sd->grp_id = CX18_HW_418_AV;
+       v4l2_ctrl_handler_init(&state->hdl, 9);
+       v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 127, 1, 64);
+       v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 127, 1, 64);
+       v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+                       V4L2_CID_HUE, -128, 127, 1, 0);
+
+       state->volume = v4l2_ctrl_new_std(&state->hdl,
+                       &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+                       0, 65535, 65535 / 100, 0);
+       v4l2_ctrl_new_std(&state->hdl,
+                       &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+                       0, 1, 1, 0);
+       v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+                       V4L2_CID_AUDIO_BALANCE,
+                       0, 65535, 65535 / 100, 32768);
+       v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+                       V4L2_CID_AUDIO_BASS,
+                       0, 65535, 65535 / 100, 32768);
+       v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+                       V4L2_CID_AUDIO_TREBLE,
+                       0, 65535, 65535 / 100, 32768);
+       sd->ctrl_handler = &state->hdl;
+       if (state->hdl.error) {
+               int err = state->hdl.error;
+
+               v4l2_ctrl_handler_free(&state->hdl);
+               return err;
+       }
        err = v4l2_device_register_subdev(&cx->v4l2_dev, sd);
-       if (!err)
+       if (err)
+               v4l2_ctrl_handler_free(&state->hdl);
+       else
                cx18_av_init(cx);
        return err;
 }