V4L/DVB (9823): cx25840: convert to v4l2_subdev.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 4da8cd7..2ad2771 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -191,7 +191,7 @@
 static void cx25840_initialize(struct i2c_client *client)
 {
 	DEFINE_WAIT(wait);
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	struct workqueue_struct *q;
 
 	/* datasheet startup in numbered steps, refer to page 3-77 */
@@ -259,7 +259,7 @@
 static void cx23885_initialize(struct i2c_client *client)
 {
 	DEFINE_WAIT(wait);
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	struct workqueue_struct *q;
 
 	/* Internal Reset */
@@ -350,7 +350,7 @@
 
 void cx25840_std_setup(struct i2c_client *client)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	v4l2_std_id std = state->std;
 	int hblank, hactive, burst, vblank, vactive, sc;
 	int vblank656, src_decimation;
@@ -497,7 +497,7 @@
 
 static void input_change(struct i2c_client *client)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	v4l2_std_id std = state->std;
 
 	/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
@@ -551,7 +551,7 @@
 static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
 						enum cx25840_audio_input aud_input)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	u8 is_composite = (vid_input >= CX25840_COMPOSITE1 &&
 			   vid_input <= CX25840_COMPOSITE8);
 	u8 reg;
@@ -671,7 +671,7 @@
 
 static int set_v4lstd(struct i2c_client *client)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	u8 fmt = 0; 	/* zero is autodetect */
 	u8 pal_m = 0;
 
@@ -720,9 +720,10 @@
 
 /* ----------------------------------------------------------------------- */
 
-static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	switch (ctrl->id) {
 	case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -786,9 +787,10 @@
 	return 0;
 }
 
-static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	switch (ctrl->id) {
 	case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -823,21 +825,23 @@
 
 /* ----------------------------------------------------------------------- */
 
-static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
 	switch (fmt->type) {
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 		return cx25840_vbi(client, VIDIOC_G_FMT, fmt);
 	default:
 		return -EINVAL;
 	}
-
 	return 0;
 }
 
-static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(sd);
+	struct i2c_client *client = 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);
@@ -914,7 +918,7 @@
 		"0xD", "0xE", "0xF"
 	};
 
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
 	u8 gen_stat1 = cx25840_read(client, 0x40d);
 	u8 gen_stat2 = cx25840_read(client, 0x40e);
@@ -944,7 +948,7 @@
 
 static void log_audio_status(struct i2c_client *client)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	u8 download_ctl = cx25840_read(client, 0x803);
 	u8 mod_det_stat0 = cx25840_read(client, 0x804);
 	u8 mod_det_stat1 = cx25840_read(client, 0x805);
@@ -1097,21 +1101,12 @@
 
 /* ----------------------------------------------------------------------- */
 
-static int cx25840_command(struct i2c_client *client, unsigned int cmd,
-			   void *arg)
+static int cx25840_init(struct v4l2_subdev *sd, u32 val)
 {
-	struct cx25840_state *state = i2c_get_clientdata(client);
-	struct v4l2_tuner *vt = arg;
-	struct v4l2_routing *route = arg;
-
-	/* ignore these commands */
-	switch (cmd) {
-		case TUNER_SET_TYPE_ADDR:
-			return 0;
-	}
+	struct cx25840_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (!state->is_initialized) {
-		v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd);
 		/* initialize on first use */
 		state->is_initialized = 1;
 		if (state->is_cx25836)
@@ -1121,50 +1116,69 @@
 		else
 			cx25840_initialize(client);
 	}
+	return 0;
+}
 
-	switch (cmd) {
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-	/* ioctls to allow direct access to the
-	 * cx25840 registers for testing */
-	case VIDIOC_DBG_G_REGISTER:
-	case VIDIOC_DBG_S_REGISTER:
-	{
-		struct v4l2_register *reg = arg;
+static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-		if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
-			return -EINVAL;
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
+	if (!v4l2_chip_match_i2c_client(client,
+				reg->match_type, reg->match_chip))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	reg->val = cx25840_read(client, reg->reg & 0x0fff);
+	return 0;
+}
 
-		if (cmd == VIDIOC_DBG_G_REGISTER)
-			reg->val = cx25840_read(client, reg->reg & 0x0fff);
-		else
-			cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
-		break;
-	}
+static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (!v4l2_chip_match_i2c_client(client,
+				reg->match_type, reg->match_chip))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+	return 0;
+}
 #endif
 
-	case VIDIOC_INT_DECODE_VBI_LINE:
-		return cx25840_vbi(client, cmd, arg);
+static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
-		return cx25840_audio(client, cmd, arg);
+	return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi);
+}
 
-	case VIDIOC_STREAMON:
-		v4l_dbg(1, cx25840_debug, client, "enable output\n");
+static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq);
+}
+
+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);
+
+	v4l_dbg(1, cx25840_debug, client, "%s output\n",
+			enable ? "enable" : "disable");
+	if (enable) {
 		if (state->is_cx23885) {
 			u8 v = (cx25840_read(client, 0x421) | 0x0b);
 			cx25840_write(client, 0x421, v);
 		} else {
 			cx25840_write(client, 0x115,
-				state->is_cx25836 ? 0x0c : 0x8c);
+					state->is_cx25836 ? 0x0c : 0x8c);
 			cx25840_write(client, 0x116,
-				state->is_cx25836 ? 0x04 : 0x07);
+					state->is_cx25836 ? 0x04 : 0x07);
 		}
-		break;
-
-	case VIDIOC_STREAMOFF:
-		v4l_dbg(1, cx25840_debug, client, "disable output\n");
+	} else {
 		if (state->is_cx23885) {
 			u8 v = cx25840_read(client, 0x421) & ~(0x0b);
 			cx25840_write(client, 0x421, v);
@@ -1172,133 +1186,136 @@
 			cx25840_write(client, 0x115, 0x00);
 			cx25840_write(client, 0x116, 0x00);
 		}
+	}
+	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:
+	case V4L2_CID_CONTRAST:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_HUE:
+		return v4l2_ctrl_query_fill_std(qc);
+	default:
 		break;
+	}
+	if (state->is_cx25836)
+		return -EINVAL;
 
-	case VIDIOC_LOG_STATUS:
-		log_video_status(client);
-		if (!state->is_cx25836)
-			log_audio_status(client);
-		break;
-
-	case VIDIOC_G_CTRL:
-		return get_v4lctrl(client, (struct v4l2_control *)arg);
-
-	case VIDIOC_S_CTRL:
-		return set_v4lctrl(client, (struct v4l2_control *)arg);
-
-	case VIDIOC_QUERYCTRL:
-	{
-		struct v4l2_queryctrl *qc = arg;
-
-		switch (qc->id) {
-			case V4L2_CID_BRIGHTNESS:
-			case V4L2_CID_CONTRAST:
-			case V4L2_CID_SATURATION:
-			case V4L2_CID_HUE:
-				return v4l2_ctrl_query_fill_std(qc);
-			default:
-				break;
-		}
-		if (state->is_cx25836)
-			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:
-			case V4L2_CID_AUDIO_BALANCE:
-			case V4L2_CID_AUDIO_BASS:
-			case V4L2_CID_AUDIO_TREBLE:
-				return v4l2_ctrl_query_fill_std(qc);
-			default:
-				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:
+	case V4L2_CID_AUDIO_BALANCE:
+	case V4L2_CID_AUDIO_BASS:
+	case V4L2_CID_AUDIO_TREBLE:
+		return v4l2_ctrl_query_fill_std(qc);
+	default:
 		return -EINVAL;
 	}
+	return -EINVAL;
+}
 
-	case VIDIOC_G_STD:
-		*(v4l2_std_id *)arg = state->std;
-		break;
+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);
 
-	case VIDIOC_S_STD:
-		if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
-			return 0;
-		state->radio = 0;
-		state->std = *(v4l2_std_id *)arg;
-		return set_v4lstd(client);
+	if (state->radio == 0 && state->std == std)
+		return 0;
+	state->radio = 0;
+	state->std = std;
+	return set_v4lstd(client);
+}
 
-	case AUDC_SET_RADIO:
-		state->radio = 1;
-		break;
+static int cx25840_s_radio(struct v4l2_subdev *sd)
+{
+	struct cx25840_state *state = to_state(sd);
 
-	case VIDIOC_INT_G_VIDEO_ROUTING:
-		route->input = state->vid_input;
-		route->output = 0;
-		break;
+	state->radio = 1;
+	return 0;
+}
 
-	case VIDIOC_INT_S_VIDEO_ROUTING:
-		return set_input(client, route->input, state->aud_input);
+static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+	struct cx25840_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-	case VIDIOC_INT_G_AUDIO_ROUTING:
-		if (state->is_cx25836)
-			return -EINVAL;
-		route->input = state->aud_input;
-		route->output = 0;
-		break;
+	return set_input(client, route->input, state->aud_input);
+}
 
-	case VIDIOC_INT_S_AUDIO_ROUTING:
-		if (state->is_cx25836)
-			return -EINVAL;
-		return set_input(client, state->vid_input, route->input);
+static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+	struct cx25840_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-	case VIDIOC_S_FREQUENCY:
-		if (!state->is_cx25836) {
-			input_change(client);
-		}
-		break;
+	if (state->is_cx25836)
+		return -EINVAL;
+	return set_input(client, state->vid_input, route->input);
+}
 
-	case VIDIOC_G_TUNER:
-	{
-		u8 vpres = cx25840_read(client, 0x40e) & 0x20;
-		u8 mode;
-		int val = 0;
+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 (state->radio)
-			break;
+	if (!state->is_cx25836)
+		input_change(client);
+	return 0;
+}
 
-		vt->signal = vpres ? 0xffff : 0x0;
-		if (state->is_cx25836)
-			break;
+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;
 
-		vt->capability |=
-		    V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
-		    V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+	if (state->radio)
+		return 0;
 
-		mode = cx25840_read(client, 0x804);
+	vt->signal = vpres ? 0xffff : 0x0;
+	if (state->is_cx25836)
+		return 0;
 
-		/* get rxsubchans and audmode */
-		if ((mode & 0xf) == 1)
-			val |= V4L2_TUNER_SUB_STEREO;
-		else
-			val |= V4L2_TUNER_SUB_MONO;
+	vt->capability |=
+		V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+		V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
 
-		if (mode == 2 || mode == 4)
-			val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+	mode = cx25840_read(client, 0x804);
 
-		if (mode & 0x10)
-			val |= V4L2_TUNER_SUB_SAP;
+	/* get rxsubchans and audmode */
+	if ((mode & 0xf) == 1)
+		val |= V4L2_TUNER_SUB_STEREO;
+	else
+		val |= V4L2_TUNER_SUB_MONO;
 
-		vt->rxsubchans = val;
-		vt->audmode = state->audmode;
-		break;
-	}
+	if (mode == 2 || mode == 4)
+		val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
 
-	case VIDIOC_S_TUNER:
-		if (state->radio || state->is_cx25836)
-			break;
+	if (mode & 0x10)
+		val |= V4L2_TUNER_SUB_SAP;
 
-		switch (vt->audmode) {
+	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 || state->is_cx25836)
+		return 0;
+
+	switch (vt->audmode) {
 		case V4L2_TUNER_MODE_MONO:
 			/* mono      -> mono
 			   stereo    -> mono
@@ -1326,41 +1343,100 @@
 			break;
 		default:
 			return -EINVAL;
-		}
-		state->audmode = vt->audmode;
-		break;
-
-	case VIDIOC_G_FMT:
-		return get_v4lfmt(client, (struct v4l2_format *)arg);
-
-	case VIDIOC_S_FMT:
-		return set_v4lfmt(client, (struct v4l2_format *)arg);
-
-	case VIDIOC_INT_RESET:
-		if (state->is_cx25836)
-			cx25836_initialize(client);
-		else if (state->is_cx23885)
-			cx23885_initialize(client);
-		else
-			cx25840_initialize(client);
-		break;
-
-	case VIDIOC_G_CHIP_IDENT:
-		return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev);
-
-	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 (state->is_cx25836)
+		cx25836_initialize(client);
+	else if (state->is_cx23885)
+		cx23885_initialize(client);
+	else
+		cx25840_initialize(client);
+	return 0;
+}
+
+static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_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 (!state->is_cx25836)
+		log_audio_status(client);
+	return 0;
+}
+
+static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+	return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+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,
+	.reset = cx25840_reset,
+	.init = cx25840_init,
+#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_std = cx25840_s_std,
+	.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,
+};
+
+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 int cx25840_probe(struct i2c_client *client,
 			 const struct i2c_device_id *did)
 {
 	struct cx25840_state *state;
+	struct v4l2_subdev *sd;
 	u32 id;
 	u16 device_id;
 
@@ -1392,10 +1468,11 @@
 	}
 
 	state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
-	if (state == NULL) {
+	if (state == NULL)
 		return -ENOMEM;
-	}
 
+	sd = &state->sd;
+	v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
 	/* Note: revision '(device_id & 0x0f) == 2' was never built. The
 	   marking skips from 0x1 == 22 to 0x3 == 23. */
 	v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
@@ -1403,7 +1480,6 @@
 		    (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f),
 		    client->addr << 1, client->adapter->name);
 
-	i2c_set_clientdata(client, state);
 	state->c = client;
 	state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
 	state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
@@ -1430,7 +1506,10 @@
 
 static int cx25840_remove(struct i2c_client *client)
 {
-	kfree(i2c_get_clientdata(client));
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+	v4l2_device_unregister_subdev(sd);
+	kfree(to_state(sd));
 	return 0;
 }