V4L/DVB (3214): Calculate the saa7115 AMCLK regs instead of using fixed values

- Calculate the audio master clock registers from the actual
frequencies. This simplifies the code and it also prepares
for adding CGC2 support.
- VIDIOC_INT_AUDIO_CLOCK_FREQ now receives an u32 instead of
an enum. It is more generic and actually easier to implement.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 740908f..6c44bd9 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -23,11 +23,13 @@
 
 #include "cx25840.h"
 
-inline static int set_audclk_freq(struct i2c_client *client,
-				 enum v4l2_audio_clock_freq freq)
+static int set_audclk_freq(struct i2c_client *client, u32 freq)
 {
 	struct cx25840_state *state = i2c_get_clientdata(client);
 
+	if (freq != 32000 && freq != 44100 && freq != 48000)
+		return -EINVAL;
+
 	/* assert soft reset */
 	cx25840_and_or(client, 0x810, ~0x1, 0x01);
 
@@ -38,7 +40,7 @@
 	switch (state->audio_input) {
 	case AUDIO_TUNER:
 		switch (freq) {
-		case V4L2_AUDCLK_32_KHZ:
+		case 32000:
 			/* VID_PLL and AUX_PLL */
 			cx25840_write4(client, 0x108, 0x0f040610);
 
@@ -51,7 +53,7 @@
 			cx25840_write4(client, 0x90c, 0x7ff70108);
 			break;
 
-		case V4L2_AUDCLK_441_KHZ:
+		case 44100:
 			/* VID_PLL and AUX_PLL */
 			cx25840_write4(client, 0x108, 0x0f040910);
 
@@ -64,7 +66,7 @@
 			cx25840_write4(client, 0x90c, 0x596d0108);
 			break;
 
-		case V4L2_AUDCLK_48_KHZ:
+		case 48000:
 			/* VID_PLL and AUX_PLL */
 			cx25840_write4(client, 0x108, 0x0f040a10);
 
@@ -84,7 +86,7 @@
 	case AUDIO_INTERN:
 	case AUDIO_RADIO:
 		switch (freq) {
-		case V4L2_AUDCLK_32_KHZ:
+		case 32000:
 			/* VID_PLL and AUX_PLL */
 			cx25840_write4(client, 0x108, 0x0f04081e);
 
@@ -103,7 +105,7 @@
 			cx25840_write(client, 0x127, 0x54);
 			break;
 
-		case V4L2_AUDCLK_441_KHZ:
+		case 44100:
 			/* VID_PLL and AUX_PLL */
 			cx25840_write4(client, 0x108, 0x0f040918);
 
@@ -119,7 +121,7 @@
 			cx25840_write4(client, 0x90c, 0x85730108);
 			break;
 
-		case V4L2_AUDCLK_48_KHZ:
+		case 48000:
 			/* VID_PLL and AUX_PLL */
 			cx25840_write4(client, 0x108, 0x0f040a18);
 
@@ -317,7 +319,7 @@
 	case AUDC_SET_INPUT:
 		return set_input(client, *(int *)arg);
 	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
-		return set_audclk_freq(client, *(enum v4l2_audio_clock_freq *)arg);
+		return set_audclk_freq(client, *(u32 *)arg);
 	case VIDIOC_G_CTRL:
 		switch (ctrl->id) {
 		case V4L2_CID_AUDIO_VOLUME:
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 54ffae6..c2c1e85 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -802,7 +802,7 @@
 	i2c_set_clientdata(client, state);
 	memset(state, 0, sizeof(struct cx25840_state));
 	state->input = CX25840_TUNER;
-	state->audclk_freq = V4L2_AUDCLK_48_KHZ;
+	state->audclk_freq = 48000;
 	state->audio_input = AUDIO_TUNER;
 	state->cardtype = CARDTYPE_PVR150;
 
@@ -1008,13 +1008,7 @@
 	cx25840_info("Specified audio input:     %s\n",
 		    state->audio_input == 0 ? "Tuner" : "External");
 
-	switch (state->audclk_freq) {
-	case V4L2_AUDCLK_441_KHZ: p = "44.1 kHz"; break;
-	case V4L2_AUDCLK_48_KHZ: p = "48 kHz"; break;
-	case V4L2_AUDCLK_32_KHZ: p = "32 kHz"; break;
-	default: p = "undefined";
-	}
-	cx25840_info("Specified audioclock freq: %s\n", p);
+	cx25840_info("Specified audioclock freq: %d Hz\n", state->audclk_freq);
 
 	switch (pref_mode & 0xf) {
 	case 0: p = "mono/language A"; break;
diff --git a/drivers/media/video/cx25840/cx25840.h b/drivers/media/video/cx25840/cx25840.h
index 40aa59f..4731a19 100644
--- a/drivers/media/video/cx25840/cx25840.h
+++ b/drivers/media/video/cx25840/cx25840.h
@@ -65,7 +65,7 @@
 	enum cx25840_cardtype cardtype;
 	enum cx25840_input input;
 	int audio_input;
-	enum v4l2_audio_clock_freq audclk_freq;
+	u32 audclk_freq;
 };
 
 /* ----------------------------------------------------------------------- */
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index b175389..3e4e558 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -39,6 +39,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <asm/div64.h>
 
 MODULE_DESCRIPTION("Philips SAA7114/SAA7115 video decoder driver");
 MODULE_AUTHOR("Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, Hans Verkuil");
@@ -78,7 +79,7 @@
 	int hue;
 	int sat;
 	enum v4l2_chip_ident ident;
-	enum v4l2_audio_clock_freq audclk_freq;
+	u32 audclk_freq;
 };
 
 /* ----------------------------------------------------------------------- */
@@ -469,80 +470,6 @@
 	0x00, 0x00
 };
 
-/* ============== SAA7715 AUDIO settings ============= */
-
-/* 48.0 kHz */
-static const unsigned char saa7115_cfg_48_audio[] = {
-	0x34, 0xce,
-	0x35, 0xfb,
-	0x36, 0x30,
-	0x00, 0x00
-};
-
-/* 44.1 kHz */
-static const unsigned char saa7115_cfg_441_audio[] = {
-	0x34, 0xf2,
-	0x35, 0x00,
-	0x36, 0x2d,
-	0x00, 0x00
-};
-
-/* 32.0 kHz */
-static const unsigned char saa7115_cfg_32_audio[] = {
-	0x34, 0xdf,
-	0x35, 0xa7,
-	0x36, 0x20,
-	0x00, 0x00
-};
-
-/* 48.0 kHz 60hz */
-static const unsigned char saa7115_cfg_60hz_48_audio[] = {
-	0x30, 0xcd,
-	0x31, 0x20,
-	0x32, 0x03,
-	0x00, 0x00
-};
-
-/* 48.0 kHz 50hz */
-static const unsigned char saa7115_cfg_50hz_48_audio[] = {
-	0x30, 0x00,
-	0x31, 0xc0,
-	0x32, 0x03,
-	0x00, 0x00
-};
-
-/* 44.1 kHz 60hz */
-static const unsigned char saa7115_cfg_60hz_441_audio[] = {
-	0x30, 0xbc,
-	0x31, 0xdf,
-	0x32, 0x02,
-	0x00, 0x00
-};
-
-/* 44.1 kHz 50hz */
-static const unsigned char saa7115_cfg_50hz_441_audio[] = {
-	0x30, 0x00,
-	0x31, 0x72,
-	0x32, 0x03,
-	0x00, 0x00
-};
-
-/* 32.0 kHz 60hz */
-static const unsigned char saa7115_cfg_60hz_32_audio[] = {
-	0x30, 0xde,
-	0x31, 0x15,
-	0x32, 0x02,
-	0x00, 0x00
-};
-
-/* 32.0 kHz 50hz */
-static const unsigned char saa7115_cfg_50hz_32_audio[] = {
-	0x30, 0x00,
-	0x31, 0x80,
-	0x32, 0x02,
-	0x00, 0x00
-};
-
 static int saa7115_odd_parity(u8 c)
 {
 	c ^= (c >> 4);
@@ -627,40 +554,38 @@
 }
 
 
-static int saa7115_set_audio_clock_freq(struct i2c_client *client, enum v4l2_audio_clock_freq freq)
+static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq)
 {
 	struct saa7115_state *state = i2c_get_clientdata(client);
+	u32 acpf;
+	u32 acni;
+	u32 hz;
+	u64 f;
 
 	saa7115_dbg("set audio clock freq: %d\n", freq);
-	switch (freq) {
-		case V4L2_AUDCLK_32_KHZ:
-			saa7115_writeregs(client, saa7115_cfg_32_audio);
-			if (state->std & V4L2_STD_525_60) {
-				saa7115_writeregs(client, saa7115_cfg_60hz_32_audio);
-			} else {
-				saa7115_writeregs(client, saa7115_cfg_50hz_32_audio);
-			}
-			break;
-		case V4L2_AUDCLK_441_KHZ:
-			saa7115_writeregs(client, saa7115_cfg_441_audio);
-			if (state->std & V4L2_STD_525_60) {
-				saa7115_writeregs(client, saa7115_cfg_60hz_441_audio);
-			} else {
-				saa7115_writeregs(client, saa7115_cfg_50hz_441_audio);
-			}
-			break;
-		case V4L2_AUDCLK_48_KHZ:
-			saa7115_writeregs(client, saa7115_cfg_48_audio);
-			if (state->std & V4L2_STD_525_60) {
-				saa7115_writeregs(client, saa7115_cfg_60hz_48_audio);
-			} else {
-				saa7115_writeregs(client, saa7115_cfg_50hz_48_audio);
-			}
-			break;
-		default:
-			saa7115_dbg("invalid audio setting %d\n", freq);
-			return -EINVAL;
-	}
+
+	/* sanity check */
+	if (freq < 32000 || freq > 48000)
+		return -EINVAL;
+
+	/* 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 */
+	f = freq;
+	f = f << 31;
+	do_div(f, 32110000);
+	acni = f;
+
+	saa7115_write(client, 0x30, acpf & 0xff);
+	saa7115_write(client, 0x31, (acpf >> 8) & 0xff);
+	saa7115_write(client, 0x32, (acpf >> 16) & 0x03);
+	saa7115_write(client, 0x34, acni & 0xff);
+	saa7115_write(client, 0x35, (acni >> 8) & 0xff);
+	saa7115_write(client, 0x36, (acni >> 16) & 0x3f);
 	state->audclk_freq = freq;
 	return 0;
 }
@@ -773,24 +698,17 @@
 static void saa7115_log_status(struct i2c_client *client)
 {
 	struct saa7115_state *state = i2c_get_clientdata(client);
-	char *audfreq = "undefined";
 	int reg1e, reg1f;
 	int signalOk;
 	int vcr;
 
-	switch (state->audclk_freq) {
-		case V4L2_AUDCLK_32_KHZ:  audfreq = "32 kHz"; break;
-		case V4L2_AUDCLK_441_KHZ: audfreq = "44.1 kHz"; break;
-		case V4L2_AUDCLK_48_KHZ:  audfreq = "48 kHz"; break;
-	}
-
-	saa7115_info("Audio frequency: %s\n", audfreq);
+	saa7115_info("Audio frequency: %d Hz\n", state->audclk_freq);
 	if (client->name[6] == '4') {
 		/* status for the saa7114 */
 		reg1f = saa7115_read(client, 0x1f);
 		signalOk = (reg1f & 0xc1) == 0x81;
 		saa7115_info("Video signal:    %s\n", signalOk ? "ok" : "bad");
-		saa7115_info("Frequency:       %s\n", (reg1f & 0x20) ? "60Hz" : "50Hz");
+		saa7115_info("Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
 		return;
 	}
 
@@ -807,7 +725,7 @@
 		saa7115_info("Input:           Composite %d\n", state->input);
 	}
 	saa7115_info("Video signal:    %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
-	saa7115_info("Frequency:       %s\n", (reg1f & 0x20) ? "60Hz" : "50Hz");
+	saa7115_info("Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
 
 	switch (reg1e & 0x03) {
 		case 1:
@@ -1108,7 +1026,7 @@
 		return saa7115_get_v4lfmt(client, (struct v4l2_format *)arg);
 
 	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
-		return saa7115_set_audio_clock_freq(client, *(enum v4l2_audio_clock_freq *)arg);
+		return saa7115_set_audio_clock_freq(client, *(u32 *)arg);
 
 	case VIDIOC_G_TUNER:
 	{
@@ -1307,7 +1225,7 @@
 	state->hue = 0;
 	state->sat = 64;
 	state->ident = (chip_id == 4) ? V4L2_IDENT_SAA7114 : V4L2_IDENT_SAA7115;
-	state->audclk_freq = V4L2_AUDCLK_48_KHZ;
+	state->audclk_freq = 48000;
 
 	saa7115_dbg("writing init values\n");
 
@@ -1317,8 +1235,7 @@
 	saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x);
 	saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y);
 	saa7115_writeregs(client, saa7115_cfg_60hz_video);
-	saa7115_writeregs(client, saa7115_cfg_48_audio);
-	saa7115_writeregs(client, saa7115_cfg_60hz_48_audio);
+	saa7115_set_audio_clock_freq(client, state->audclk_freq);
 	saa7115_writeregs(client, saa7115_cfg_reset_scaler);
 
 	i2c_attach_client(client);