+ v4l_info(client, "Selected 45 MHz format: %s\n", p);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* This load_fw operation must be called to load the driver's firmware.
+ Without this the audio standard detection will fail and you will
+ only get mono.
+
+ Since loading the firmware is often problematic when the driver is
+ compiled into the kernel I recommend postponing calling this function
+ until the first open of the video device. Another reason for
+ postponing it is that loading this firmware takes a long time (seconds)
+ due to the slow i2c bus speed. So it will speed up the boot process if
+ you can avoid loading the fw as long as the video device isn't used. */
+static int cx25840_load_fw(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!state->is_initialized) {
+ /* initialize and load firmware */
+ state->is_initialized = 1;
+ if (is_cx2583x(state))
+ cx25836_initialize(client);
+ else if (is_cx2388x(state))
+ cx23885_initialize(client);
+ else if (is_cx231xx(state))
+ cx231xx_initialize(client);
+ else
+ cx25840_initialize(client);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 1;
+ reg->val = cx25840_read(client, reg->reg & 0x0fff);
+ return 0;
+}
+
+static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 v;
+
+ if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state))
+ return 0;
+
+ v4l_dbg(1, cx25840_debug, client, "%s audio output\n",
+ enable ? "enable" : "disable");
+
+ if (enable) {
+ v = cx25840_read(client, 0x115) | 0x80;
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) | 0x03;
+ cx25840_write(client, 0x116, v);
+ } else {
+ v = cx25840_read(client, 0x115) & ~(0x80);
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) & ~(0x03);
+ cx25840_write(client, 0x116, v);
+ }
+ return 0;
+}
+
+static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 v;
+
+ v4l_dbg(1, cx25840_debug, client, "%s video output\n",
+ enable ? "enable" : "disable");
+ if (enable) {
+ if (is_cx2388x(state) || is_cx231xx(state)) {
+ v = cx25840_read(client, 0x421) | 0x0b;
+ cx25840_write(client, 0x421, v);
+ } else {
+ v = cx25840_read(client, 0x115) | 0x0c;
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) | 0x04;
+ cx25840_write(client, 0x116, v);
+ }
+ } else {
+ if (is_cx2388x(state) || is_cx231xx(state)) {
+ v = cx25840_read(client, 0x421) & ~(0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ v = cx25840_read(client, 0x115) & ~(0x0c);
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) & ~(0x04);
+ cx25840_write(client, 0x116, v);
+ }
+ }
+ return 0;
+}
+
+static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct cx25840_state *state = to_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;
+ }
+ if (is_cx2583x(state))
+ return -EINVAL;
+
+ 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 cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio == 0 && state->std == std)
+ return 0;
+ state->radio = 0;
+ state->std = std;
+ return set_v4lstd(client);
+}
+
+static int cx25840_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ state->radio = 1;
+ return 0;
+}
+
+static int cx25840_s_video_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return set_input(client, input, state->aud_input);
+}
+
+static int cx25840_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (is_cx2583x(state))
+ return -EINVAL;
+ return set_input(client, state->vid_input, input);
+}
+
+static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!is_cx2583x(state))
+ input_change(client);
+ return 0;
+}
+
+static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 vpres = cx25840_read(client, 0x40e) & 0x20;
+ u8 mode;
+ int val = 0;
+
+ if (state->radio)
+ return 0;
+
+ vt->signal = vpres ? 0xffff : 0x0;
+ if (is_cx2583x(state))
+ return 0;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ mode = cx25840_read(client, 0x804);
+
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
+
+ if (mode == 2 || mode == 4)
+ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
+
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ return 0;
+}
+
+static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio || is_cx2583x(state))
+ return 0;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ /* mono -> mono
+ stereo -> mono
+ bilingual -> lang1 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x00);
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x04);
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1/lang2 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x07);
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang2 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x01);
+ break;
+ default:
+ return -EINVAL;
+ }
+ state->audmode = vt->audmode;
+ return 0;
+}
+
+static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (is_cx2583x(state))
+ cx25836_initialize(client);
+ else if (is_cx2388x(state))
+ cx23885_initialize(client);
+ else if (is_cx231xx(state))
+ cx231xx_initialize(client);
+ else
+ cx25840_initialize(client);
+ return 0;
+}
+
+static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
+}
+
+static int cx25840_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ log_video_status(client);
+ if (!is_cx2583x(state))
+ log_audio_status(client);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cx25840_core_ops = {
+ .log_status = cx25840_log_status,
+ .g_chip_ident = cx25840_g_chip_ident,
+ .g_ctrl = cx25840_g_ctrl,
+ .s_ctrl = cx25840_s_ctrl,
+ .queryctrl = cx25840_queryctrl,
+ .s_std = cx25840_s_std,
+ .reset = cx25840_reset,
+ .load_fw = cx25840_load_fw,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx25840_g_register,
+ .s_register = cx25840_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
+ .s_frequency = cx25840_s_frequency,
+ .s_radio = cx25840_s_radio,
+ .g_tuner = cx25840_g_tuner,
+ .s_tuner = cx25840_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
+ .s_clock_freq = cx25840_s_clock_freq,
+ .s_routing = cx25840_s_audio_routing,
+ .s_stream = cx25840_s_audio_stream,
+};
+
+static const struct v4l2_subdev_video_ops cx25840_video_ops = {
+ .s_routing = cx25840_s_video_routing,
+ .g_fmt = cx25840_g_fmt,
+ .s_fmt = cx25840_s_fmt,
+ .decode_vbi_line = cx25840_decode_vbi_line,
+ .s_stream = cx25840_s_stream,
+};
+
+static const struct v4l2_subdev_ops cx25840_ops = {
+ .core = &cx25840_core_ops,
+ .tuner = &cx25840_tuner_ops,
+ .audio = &cx25840_audio_ops,
+ .video = &cx25840_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static u32 get_cx2388x_ident(struct i2c_client *client)
+{
+ u32 ret;
+
+ /* Come out of digital power down */
+ cx25840_write(client, 0x000, 0);
+
+ /* Detecting whether the part is cx23885/7/8 is more
+ * difficult than it needs to be. No ID register. Instead we
+ * probe certain registers indicated in the datasheets to look
+ * for specific defaults that differ between the silicon designs. */
+
+ /* It's either 885/7 if the IR Tx Clk Divider register exists */
+ if (cx25840_read4(client, 0x204) & 0xffff) {
+ /* CX23885 returns bogus repetitive byte values for the DIF,
+ * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */
+ ret = cx25840_read4(client, 0x300);
+ if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) {
+ /* No DIF */
+ ret = V4L2_IDENT_CX23885_AV;
+ } else {
+ /* CX23887 has a broken DIF, but the registers
+ * appear valid (but unsed), good enough to detect. */
+ ret = V4L2_IDENT_CX23887_AV;
+ }
+ } else if (cx25840_read4(client, 0x300) & 0x0fffffff) {
+ /* DIF PLL Freq Word reg exists; chip must be a CX23888 */
+ ret = V4L2_IDENT_CX23888_AV;
+ } else {
+ v4l_err(client, "Unable to detect h/w, assuming cx23887\n");
+ ret = V4L2_IDENT_CX23887_AV;
+ }
+
+ /* Back into digital power down */
+ cx25840_write(client, 0x000, 2);
+ return ret;
+}
+
+static int cx25840_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct cx25840_state *state;
+ struct v4l2_subdev *sd;
+ u32 id = V4L2_IDENT_NONE;
+ u16 device_id;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1);
+
+ device_id = cx25840_read(client, 0x101) << 8;
+ device_id |= cx25840_read(client, 0x100);
+ v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id);
+
+ /* The high byte of the device ID should be
+ * 0x83 for the cx2583x and 0x84 for the cx2584x */
+ if ((device_id & 0xff00) == 0x8300) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ } else if ((device_id & 0xff00) == 0x8400) {
+ id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
+ } else if (device_id == 0x0000) {
+ id = get_cx2388x_ident(client);
+ } else if ((device_id & 0xfff0) == 0x5A30) {
+ /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */
+ id = V4L2_IDENT_CX2310X_AV;
+ } else if ((device_id & 0xff) == (device_id >> 8)) {
+ v4l_err(client,
+ "likely a confused/unresponsive cx2388[578] A/V decoder"
+ " found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ v4l_err(client, "A method to reset it from the cx25840 driver"
+ " software is not known at this time\n");
+ return -ENODEV;
+ } else {
+ v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
+ return -ENODEV;