V4L/DVB (10757): cx18, v4l2-chip-ident: Finish conversion of AV decoder core to v4l2_subdev

Added a new chip identifer to v4l2-chip-ident for the integrated A/V broadcast
decoder core internal to the CX23418.  Completed separation and encapsulation
of the A/V decoder core interface as a v4l2_subdevice.  The cx18 driver now
compiles and links again.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 9b30f77..f9bb77a 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -201,15 +201,45 @@
 	return 0;
 }
 
-static int cx18_av_init_hardware(struct v4l2_subdev *sd, u32 val)
+static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
 {
 	struct cx18_av_state *state = to_cx18_av_state(sd);
 	struct cx18 *cx = v4l2_get_subdevdata(sd);
 
-	if (!state->is_initialized) {
-		/* initialize on first use */
-		state->is_initialized = 1;
-		cx18_av_initialize(cx);
+	switch (val) {
+	case CX18_AV_INIT_PLLS:
+		/*
+		 * The crystal freq used in calculations in this driver will be
+		 * 28.636360 MHz.
+		 * Aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+		 */
+
+		/*
+		 * VDCLK  Integer = 0x0f, Post Divider = 0x04
+		 * AIMCLK Integer = 0x0e, Post Divider = 0x16
+		 */
+		cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+
+		/* VDCLK Fraction = 0x2be2fe */
+		/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+		cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+
+		/* AIMCLK Fraction = 0x05227ad */
+		/* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/
+		cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+
+		/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+		cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+		break;
+
+	case CX18_AV_INIT_NORMAL:
+	default:
+		if (!state->is_initialized) {
+			/* initialize on first use */
+			state->is_initialized = 1;
+			cx18_av_initialize(cx);
+		}
+		break;
 	}
 	return 0;
 }
@@ -1095,14 +1125,11 @@
 static int cx18_av_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *chip)
 {
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+
 	if (cx18_av_dbg_match(&chip->match)) {
-		/*
-		 * Nothing else is going to claim to be this combination,
-		 * and the real host chip revision will be returned by a host
-		 * match on address 0.
-		 */
-		chip->ident = V4L2_IDENT_CX25843;
-		chip->revision = V4L2_IDENT_CX23418; /* Why not */
+		chip->ident = state->id;
+		chip->revision = state->rev;
 	}
 	return 0;
 }
@@ -1143,7 +1170,7 @@
 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,
-	.init = cx18_av_init_hardware,
+	.init = cx18_av_init,
 	.reset = cx18_av_reset,
 	.queryctrl = cx18_av_queryctrl,
 	.g_ctrl = cx18_av_g_ctrl,
@@ -1182,19 +1209,31 @@
 	.video = &cx18_av_video_ops,
 };
 
-int cx18_av_init(struct cx18 *cx)
+int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd)
 {
-	struct v4l2_subdev *sd = &cx->av_state.sd;
+	struct cx18_av_state *state = &cx->av_state;
 
-	v4l2_subdev_init(sd, &cx18_av_ops);
-	v4l2_set_subdevdata(sd, cx);
-	snprintf(sd->name, sizeof(sd->name),
-		 "%s-internal A/V decoder", cx->v4l2_dev.name);
-	sd->grp_id = CX18_HW_CX23418;
-	return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+	state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
+	state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
+		    ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN;
+
+	state->vid_input = CX18_AV_COMPOSITE7;
+	state->aud_input = CX18_AV_AUDIO8;
+	state->audclk_freq = 48000;
+	state->audmode = V4L2_TUNER_MODE_LANG1;
+	state->slicer_line_delay = 0;
+	state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
+
+	*sd = &state->sd;
+	v4l2_subdev_init(*sd, &cx18_av_ops);
+	v4l2_set_subdevdata(*sd, cx);
+	snprintf((*sd)->name, sizeof((*sd)->name),
+		 "%s internal A/V decoder", cx->v4l2_dev.name);
+	(*sd)->grp_id = CX18_HW_CX23418;
+	return v4l2_device_register_subdev(&cx->v4l2_dev, *sd);
 }
 
-void cx18_av_fini(struct cx18 *cx)
+void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *sd)
 {
 	v4l2_device_unregister_subdev(&cx->av_state.sd);
 }
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index 025100a..90bdca9 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -323,6 +323,11 @@
 	return container_of(sd, struct cx18_av_state, sd);
 }
 
+enum cx18_av_subdev_init_arg {
+	CX18_AV_INIT_NORMAL = 0,
+	CX18_AV_INIT_PLLS = 1,
+};
+
 /* ----------------------------------------------------------------------- */
 /* cx18_av-core.c 							   */
 int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
@@ -337,9 +342,8 @@
 int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
 void cx18_av_std_setup(struct cx18 *cx);
 
-int cx18_av_cmd(struct cx18 *cx, int cmd, void *arg); /* FIXME - Remove */
-int cx18_av_init(struct cx18 *cx);
-void cx18_av_fini(struct cx18 *cx);
+int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd);
+void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *sd);
 
 /* ----------------------------------------------------------------------- */
 /* cx18_av-firmware.c                                                      */
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index 10a4e07..9505c41 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -67,7 +67,7 @@
 	case V4L2_CID_HUE:
 	case V4L2_CID_SATURATION:
 	case V4L2_CID_CONTRAST:
-		if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl))
+		if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
 			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
 		return 0;
 
@@ -126,7 +126,7 @@
 	case V4L2_CID_HUE:
 	case V4L2_CID_SATURATION:
 	case V4L2_CID_CONTRAST:
-		return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl);
+		return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
 
 	case V4L2_CID_AUDIO_VOLUME:
 	case V4L2_CID_AUDIO_MUTE:
@@ -151,7 +151,7 @@
 	case V4L2_CID_HUE:
 	case V4L2_CID_SATURATION:
 	case V4L2_CID_CONTRAST:
-		return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl);
+		return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
 
 	case V4L2_CID_AUDIO_VOLUME:
 	case V4L2_CID_AUDIO_MUTE:
@@ -278,7 +278,7 @@
 			fmt.fmt.pix.width = cx->params.width
 						/ (is_mpeg1 ? 2 : 1);
 			fmt.fmt.pix.height = cx->params.height;
-			cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
+			v4l2_subdev_call(cx->sd_av, video, s_fmt, &fmt);
 		}
 		priv.cx = cx;
 		priv.s = &cx->streams[id->type];
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index f69e688..f5a41dd 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -621,13 +621,6 @@
 		i = 0;
 	cx->active_input = i;
 	cx->audio_input = cx->card->video_inputs[i].audio_index;
-	cx->av_state.vid_input = CX18_AV_COMPOSITE7;
-	cx->av_state.aud_input = CX18_AV_AUDIO8;
-	cx->av_state.audclk_freq = 48000;
-	cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
-	cx->av_state.slicer_line_delay = 0;
-	cx->av_state.slicer_line_offset =
-		(10 + cx->av_state.slicer_line_delay - 2);
 }
 
 static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
@@ -812,6 +805,14 @@
 
 	cx18_gpio_init(cx);
 
+	retval = cx18_av_probe(cx, &cx->sd_av);
+	if (retval) {
+		CX18_ERR("Could not register A/V decoder subdevice\n");
+		goto free_map;
+	}
+	/* Initialize the A/V decoder PLLs to sane defaults */
+	v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_PLLS);
+
 	/* active i2c  */
 	CX18_DEBUG_INFO("activating i2c...\n");
 	retval = init_cx18_i2c(cx);
@@ -1020,6 +1021,9 @@
 	cx18_vapi(cx, CX18_APU_RESETAI, 0);
 	cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
 
+	/* Init the A/V decoder, if it hasn't been already */
+	v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_NORMAL);
+
 	vf.tuner = 0;
 	vf.type = V4L2_TUNER_ANALOG_TV;
 	vf.frequency = 6400; /* the tuner 'baseline' frequency */
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index def82bd..4b50878 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -448,6 +448,7 @@
 	int instance;
 	struct pci_dev *pci_dev;
 	struct v4l2_device v4l2_dev;
+	struct v4l2_subdev *sd_av;
 
 	const struct cx18_card *card;	/* card information */
 	const char *card_name;  /* full name of the card */
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index aa89bee..83cd559 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -26,7 +26,6 @@
 #include "cx18-irq.h"
 #include "cx18-firmware.h"
 #include "cx18-cards.h"
-#include "cx18-av-core.h"
 #include <linux/firmware.h>
 
 #define CX18_PROC_SOFT_RESET 		0xc70010
@@ -286,23 +285,6 @@
 	cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
 	cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
 
-	/*
-	 * VDCLK  Integer = 0x0f, Post Divider = 0x04
-	 * AIMCLK Integer = 0x0e, Post Divider = 0x16
-	 */
-	cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
-
-	/* VDCLK Fraction = 0x2be2fe */
-	/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
-	cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
-
-	/* AIMCLK Fraction = 0x05227ad */
-	/* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */
-	cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
-
-	/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
-	cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
-
 	/* Defaults */
 	/* APU = SC or SC/2 = 125/62.5 */
 	/* EPU = SC = 125 */
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index db2c3e6..db7b552 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -304,7 +304,7 @@
 		return cx18_gpio(cx, cmd, arg);
 
 	if (hw == CX18_HW_CX23418)
-		return cx18_av_cmd(cx, cmd, arg);
+		return v4l2_subdev_command(cx->sd_av, cmd, arg);
 
 	addr = cx18_i2c_hw_addr(cx, hw);
 	if (addr < 0) {
@@ -322,7 +322,7 @@
 		CX18_ERR("adapter is not set\n");
 		return;
 	}
-	cx18_av_cmd(cx, cmd, arg);
+	v4l2_subdev_command(cx->sd_av, cmd, arg);
 	i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
 	i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
 	if (cx->hw_flags & CX18_HW_GPIO)
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 3277b3d..0ddf4dd 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -208,7 +208,7 @@
 	 * digitizer/slicer.  Note, cx18_av_vbi() wipes the passed in
 	 * fmt->fmt.sliced under valid calling conditions
 	 */
-	if (cx18_av_cmd(cx, VIDIOC_G_FMT, fmt))
+	if (v4l2_subdev_call(cx->sd_av, video, g_fmt, fmt))
 		return -EINVAL;
 
 	/* Ensure V4L2 spec compliant output */
@@ -295,7 +295,7 @@
 
 	cx->params.width = w;
 	cx->params.height = h;
-	cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+	v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
 	return cx18_g_fmt_vid_cap(file, fh, fmt);
 }
 
@@ -322,7 +322,7 @@
 	 * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid
 	 * calling conditions
 	 */
-	ret = cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+	ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
 	if (ret)
 		return ret;
 
@@ -359,7 +359,7 @@
 	 * Note, cx18_av_vbi() wipes some "impossible" service lines in the
 	 * passed in fmt->fmt.sliced under valid calling conditions
 	 */
-	ret = cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+	ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
 	if (ret)
 		return ret;
 	/* Store our current v4l2 sliced VBI settings */
@@ -516,7 +516,8 @@
 
 	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
-	return cx18_av_cmd(cx, VIDIOC_S_CROP, crop);
+	CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
+	return -EINVAL;
 }
 
 static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
@@ -525,7 +526,8 @@
 
 	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
-	return cx18_av_cmd(cx, VIDIOC_G_CROP, crop);
+	CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
+	return -EINVAL;
 }
 
 static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index eff4a14..1d17884 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -348,7 +348,7 @@
 	}
 
 	/* setup VBI registers */
-	cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+	v4l2_subdev_call(cx->sd_av, video, s_fmt, &cx->vbi.in);
 
 	/*
 	 * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index 8e6f4d4..91c21dc 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -154,7 +154,7 @@
 		if (p[0] != 0xff || p[1] || p[2] || p[3] != eav)
 			continue;
 		vbi.p = p + 4;
-		cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+		v4l2_subdev_call(cx->sd_av, video, decode_vbi_line, &vbi);
 		if (vbi.type) {
 			cx->vbi.sliced_data[line].id = vbi.type;
 			cx->vbi.sliced_data[line].field = vbi.is_second_field;
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c
index 2e5c419..fabacb0 100644
--- a/drivers/media/video/cx18/cx18-video.c
+++ b/drivers/media/video/cx18/cx18-video.c
@@ -32,7 +32,7 @@
 
 	route.input = cx->card->video_inputs[inp].video_input;
 	route.output = 0;
-	cx18_av_cmd(cx, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+	v4l2_subdev_call(cx->sd_av, video, s_routing, &route);
 
 	type = cx->card->video_inputs[inp].video_type;
 
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index a5339d8..70117e7 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -64,7 +64,8 @@
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,
 
-	/* Conexant MPEG encoder/decoders: reserved range 410-420 */
+	/* Conexant MPEG encoder/decoders: reserved range 400-420 */
+	V4L2_IDENT_CX23418_843 = 403, /* Integrated A/V Decoder on the '418 */
 	V4L2_IDENT_CX23415 = 415,
 	V4L2_IDENT_CX23416 = 416,
 	V4L2_IDENT_CX23418 = 418,