V4L/DVB (3711): Add support for VIDIOC_INT_S_CRYSTAL_FREQ internal command.

Some saa7115-based cards use a different crystal frequency and a different
audio clock generation. Add a new VIDIOC_INT_S_CRYSTAL_FREQ command to be
able to set these values.
Also change the default APLL setting to 0. It makes no sense to have the
audio clock independent from the video clock, this can lead to audio/video
synchronization problems. Setting this to 0 is also consistent with the old
saa7114.c source and the way the Hauppauge Windows driver sets it.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index dceebc0..edea9e3 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -72,6 +72,10 @@
 	int sat;
 	enum v4l2_chip_ident ident;
 	u32 audclk_freq;
+	u32 crystal_freq;
+	u8 ucgc;
+	u8 cgcdiv;
+	u8 apll;
 };
 
 /* ----------------------------------------------------------------------- */
@@ -375,10 +379,6 @@
 };
 
 static const unsigned char saa7115_init_misc[] = {
-	0x38, 0x03,		/* audio stuff */
-	0x39, 0x10,
-	0x3a, 0x08,
-
 	0x81, 0x01,		/* reg 0x15,0x16 define blanking window */
 	0x82, 0x00,
 	0x83, 0x01,		/* I port settings */
@@ -584,6 +584,7 @@
 	u32 acni;
 	u32 hz;
 	u64 f;
+	u8 acc = 0; 	/* reg 0x3a, audio clock control */
 
 	v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq);
 
@@ -591,18 +592,34 @@
 	if (freq < 32000 || freq > 48000)
 		return -EINVAL;
 
+	/* The saa7113 has no audio clock */
+	if (state->ident == V4L2_IDENT_SAA7113)
+		return 0;
+
 	/* hz is the refresh rate times 100 */
 	hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000;
 	/* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */
 	acpf = (25600 * freq) / hz;
 	/* acni = (256 * freq * 2^23) / crystal_frequency =
 		  (freq * 2^(8+23)) / crystal_frequency =
-		  (freq << 31) / 32.11 MHz */
+		  (freq << 31) / crystal_frequency */
 	f = freq;
 	f = f << 31;
-	do_div(f, 32110000);
+	do_div(f, state->crystal_freq);
 	acni = f;
+	if (state->ucgc) {
+		acpf = acpf * state->cgcdiv / 16;
+		acni = acni * state->cgcdiv / 16;
+		acc = 0x80;
+		if (state->cgcdiv == 3)
+			acc |= 0x40;
+	}
+	if (state->apll)
+		acc |= 0x08;
 
+	saa7115_write(client, 0x38, 0x03);
+	saa7115_write(client, 0x39, 0x10);
+	saa7115_write(client, 0x3a, acc);
 	saa7115_write(client, 0x30, acpf & 0xff);
 	saa7115_write(client, 0x31, (acpf >> 8) & 0xff);
 	saa7115_write(client, 0x32, (acpf >> 16) & 0x03);
@@ -1260,6 +1277,21 @@
 		}
 		break;
 
+	case VIDIOC_INT_S_CRYSTAL_FREQ:
+	{
+		struct v4l2_crystal_freq *freq = arg;
+
+		if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
+		    freq->freq != SAA7115_FREQ_24_576_MHZ)
+			return -EINVAL;
+		state->crystal_freq = freq->freq;
+		state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
+		state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
+		state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
+		saa7115_set_audio_clock_freq(client, state->audclk_freq);
+		break;
+	}
+
 	case VIDIOC_INT_DECODE_VBI_LINE:
 		saa7115_decode_vbi_line(client, arg);
 		break;
@@ -1401,10 +1433,13 @@
 	v4l_dbg(1, debug, client, "writing init values\n");
 
 	/* init to 60hz/48khz */
-	if (state->ident == V4L2_IDENT_SAA7113)
+	if (state->ident == V4L2_IDENT_SAA7113) {
+		state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
 		saa7115_writeregs(client, saa7113_init_auto_input);
-	else
+	} else {
+		state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
 		saa7115_writeregs(client, saa7115_init_auto_input);
+	}
 	saa7115_writeregs(client, saa7115_init_misc);
 	saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x);
 	saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y);
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index d330fa9..ad92e07 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -331,7 +331,8 @@
 	[_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)]  = "VIDIOC_INT_S_AUDIO_ROUTING",
 	[_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)]  = "VIDIOC_INT_G_AUDIO_ROUTING",
 	[_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)]  = "VIDIOC_INT_S_VIDEO_ROUTING",
-	[_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)]  = "VIDIOC_INT_G_VIDEO_ROUTING"
+	[_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)]  = "VIDIOC_INT_G_VIDEO_ROUTING",
+	[_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)]   = "VIDIOC_INT_S_CRYSTAL_FREQ"
 };
 #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls)
 
@@ -667,6 +668,12 @@
 		printk ("%s: input=0x%x, output=0x%x\n", s, p->input, p->output);
 		break;
 	}
+	case VIDIOC_INT_S_CRYSTAL_FREQ:
+	{
+		struct v4l2_crystal_freq *p=arg;
+		printk ("%s: freq=%u, flags=0x%x\n", s, p->freq, p->flags);
+		break;
+	}
 	case VIDIOC_G_SLICED_VBI_CAP:
 	{
 		struct v4l2_sliced_vbi_cap *p=arg;