V4L/DVB (7344): cx25840: better PAL-M and NTSC-KR handling

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 756a1ee..ae395f8 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -352,7 +352,7 @@
 static void input_change(struct i2c_client *client)
 {
 	struct cx25840_state *state = i2c_get_clientdata(client);
-	v4l2_std_id std = cx25840_get_v4lstd(client);
+	v4l2_std_id std = state->std;
 
 	/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
 	if (std & V4L2_STD_SECAM) {
@@ -523,32 +523,34 @@
 
 /* ----------------------------------------------------------------------- */
 
-static int set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+static int set_v4lstd(struct i2c_client *client)
 {
-	u8 fmt=0; 	/* zero is autodetect */
+	struct cx25840_state *state = i2c_get_clientdata(client);
+	u8 fmt = 0; 	/* zero is autodetect */
+	u8 pal_m = 0;
 
 	/* First tests should be against specific std */
-	if (std == V4L2_STD_NTSC_M_JP) {
-		fmt=0x2;
-	} else if (std == V4L2_STD_NTSC_443) {
-		fmt=0x3;
-	} else if (std == V4L2_STD_PAL_M) {
-		fmt=0x5;
-	} else if (std == V4L2_STD_PAL_N) {
-		fmt=0x6;
-	} else if (std == V4L2_STD_PAL_Nc) {
-		fmt=0x7;
-	} else if (std == V4L2_STD_PAL_60) {
-		fmt=0x8;
+	if (state->std == V4L2_STD_NTSC_M_JP) {
+		fmt = 0x2;
+	} else if (state->std == V4L2_STD_NTSC_443) {
+		fmt = 0x3;
+	} else if (state->std == V4L2_STD_PAL_M) {
+		pal_m = 1;
+		fmt = 0x5;
+	} else if (state->std == V4L2_STD_PAL_N) {
+		fmt = 0x6;
+	} else if (state->std == V4L2_STD_PAL_Nc) {
+		fmt = 0x7;
+	} else if (state->std == V4L2_STD_PAL_60) {
+		fmt = 0x8;
 	} else {
 		/* Then, test against generic ones */
-		if (std & V4L2_STD_NTSC) {
-			fmt=0x1;
-		} else if (std & V4L2_STD_PAL) {
-			fmt=0x4;
-		} else if (std & V4L2_STD_SECAM) {
-			fmt=0xc;
-		}
+		if (state->std & V4L2_STD_NTSC)
+			fmt = 0x1;
+		else if (state->std & V4L2_STD_PAL)
+			fmt = 0x4;
+		else if (state->std & V4L2_STD_SECAM)
+			fmt = 0xc;
 	}
 
 	v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt);
@@ -563,42 +565,13 @@
 		cx25840_and_or(client, 0x47b, ~6, 0);
 	}
 	cx25840_and_or(client, 0x400, ~0xf, fmt);
+	cx25840_and_or(client, 0x403, ~0x3, pal_m);
 	cx25840_vbi_setup(client);
+	if (!state->is_cx25836)
+		input_change(client);
 	return 0;
 }
 
-v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client)
-{
-	struct cx25840_state *state = i2c_get_clientdata(client);
-	/* check VID_FMT_SEL first */
-	u8 fmt = cx25840_read(client, 0x400) & 0xf;
-
-	if (!fmt) {
-		/* check AFD_FMT_STAT if set to autodetect */
-		fmt = cx25840_read(client, 0x40d) & 0xf;
-	}
-
-	switch (fmt) {
-	case 0x1:
-	{
-		/* if the audio std is A2-M, then this is the South Korean
-		   NTSC standard */
-		if (!state->is_cx25836 && cx25840_read(client, 0x805) == 2)
-			return V4L2_STD_NTSC_M_KR;
-		return V4L2_STD_NTSC_M;
-	}
-	case 0x2: return V4L2_STD_NTSC_M_JP;
-	case 0x3: return V4L2_STD_NTSC_443;
-	case 0x4: return V4L2_STD_PAL;
-	case 0x5: return V4L2_STD_PAL_M;
-	case 0x6: return V4L2_STD_PAL_N;
-	case 0x7: return V4L2_STD_PAL_Nc;
-	case 0x8: return V4L2_STD_PAL_60;
-	case 0xc: return V4L2_STD_SECAM;
-	default: return V4L2_STD_UNKNOWN;
-	}
-}
-
 /* ----------------------------------------------------------------------- */
 
 static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
@@ -718,9 +691,10 @@
 
 static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
 {
+	struct cx25840_state *state = i2c_get_clientdata(client);
 	struct v4l2_pix_format *pix;
 	int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
-	int is_50Hz = !(cx25840_get_v4lstd(client) & V4L2_STD_525_60);
+	int is_50Hz = !(state->std & V4L2_STD_525_60);
 
 	switch (fmt->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
@@ -1096,12 +1070,15 @@
 	}
 
 	case VIDIOC_G_STD:
-		*(v4l2_std_id *)arg = cx25840_get_v4lstd(client);
+		*(v4l2_std_id *)arg = state->std;
 		break;
 
 	case VIDIOC_S_STD:
+		if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
+			return 0;
 		state->radio = 0;
-		return set_v4lstd(client, *(v4l2_std_id *)arg);
+		state->std = *(v4l2_std_id *)arg;
+		return set_v4lstd(client);
 
 	case AUDC_SET_RADIO:
 		state->radio = 1;
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 95093ed..8bf797f 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -38,6 +38,7 @@
 	struct i2c_client *c;
 	int pvr150_workaround;
 	int radio;
+	v4l2_std_id std;
 	enum cx25840_video_input vid_input;
 	enum cx25840_audio_input aud_input;
 	u32 audclk_freq;
@@ -60,7 +61,6 @@
 u8 cx25840_read(struct i2c_client *client, u16 addr);
 u32 cx25840_read4(struct i2c_client *client, u16 addr);
 int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
-v4l2_std_id cx25840_get_v4lstd(struct i2c_client *client);
 
 /* ----------------------------------------------------------------------- */
 /* cx25850-firmware.c                                                      */
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
index 6828f59..c754b9d 100644
--- a/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -85,7 +85,7 @@
 void cx25840_vbi_setup(struct i2c_client *client)
 {
 	struct cx25840_state *state = i2c_get_clientdata(client);
-	v4l2_std_id std = cx25840_get_v4lstd(client);
+	v4l2_std_id std = state->std;
 	int hblank,hactive,burst,vblank,vactive,sc,vblank656,src_decimation;
 	int luma_lpf,uv_lpf, comb;
 	u32 pll_int,pll_frac,pll_post;
@@ -242,7 +242,7 @@
 			0, 0, V4L2_SLICED_VPS, 0, 0,	/* 9 */
 			0, 0, 0, 0
 		};
-		int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_525_60);
+		int is_pal = !(state->std & V4L2_STD_525_60);
 		int i;
 
 		fmt = arg;
@@ -279,7 +279,7 @@
 
 	case VIDIOC_S_FMT:
 	{
-		int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_525_60);
+		int is_pal = !(state->std & V4L2_STD_525_60);
 		int vbi_offset = is_pal ? 1 : 0;
 		int i, x;
 		u8 lcr[24];