18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* cx25840 audio functions
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
78c2ecf20Sopenharmony_ci#include <linux/i2c.h>
88c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
98c2ecf20Sopenharmony_ci#include <media/drv-intf/cx25840.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "cx25840-core.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * Note: The PLL and SRC parameters are based on a reference frequency that
158c2ecf20Sopenharmony_ci * would ideally be:
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * However, it's not the exact reference frequency that matters, only that the
208c2ecf20Sopenharmony_ci * firmware and modules that comprise the driver for a particular board all
218c2ecf20Sopenharmony_ci * use the same value (close to the ideal value).
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Comments below will note which reference frequency is assumed for various
248c2ecf20Sopenharmony_ci * parameters.  They will usually be one of
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci *	ref_freq = 28.636360 MHz
278c2ecf20Sopenharmony_ci *		or
288c2ecf20Sopenharmony_ci *	ref_freq = 28.636363 MHz
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (state->aud_input != CX25840_AUDIO_SERIAL) {
368c2ecf20Sopenharmony_ci		switch (freq) {
378c2ecf20Sopenharmony_ci		case 32000:
388c2ecf20Sopenharmony_ci			/*
398c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
408c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10
418c2ecf20Sopenharmony_ci			 */
428c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x108, 0x1006040f);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci			/*
458c2ecf20Sopenharmony_ci			 * VID_PLL Fraction (register 0x10c) = 0x2be2fe
468c2ecf20Sopenharmony_ci			 * 28636360 * 0xf.15f17f0/4 = 108 MHz
478c2ecf20Sopenharmony_ci			 * 432 MHz pre-postdivide
488c2ecf20Sopenharmony_ci			 */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci			/*
518c2ecf20Sopenharmony_ci			 * AUX_PLL Fraction = 0x1bb39ee
528c2ecf20Sopenharmony_ci			 * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384
538c2ecf20Sopenharmony_ci			 * 196.6 MHz pre-postdivide
548c2ecf20Sopenharmony_ci			 * FIXME < 200 MHz is out of specified valid range
558c2ecf20Sopenharmony_ci			 * FIXME 28636363 ref_freq doesn't match VID PLL ref
568c2ecf20Sopenharmony_ci			 */
578c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x110, 0x01bb39ee);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci			/*
608c2ecf20Sopenharmony_ci			 * SA_MCLK_SEL = 1
618c2ecf20Sopenharmony_ci			 * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
628c2ecf20Sopenharmony_ci			 */
638c2ecf20Sopenharmony_ci			cx25840_write(client, 0x127, 0x50);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci			if (is_cx2583x(state))
668c2ecf20Sopenharmony_ci				break;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
698c2ecf20Sopenharmony_ci			/* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
708c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x0801f77f);
718c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x0801f77f);
728c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x0801f77f);
738c2ecf20Sopenharmony_ci			break;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		case 44100:
768c2ecf20Sopenharmony_ci			/*
778c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
788c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10
798c2ecf20Sopenharmony_ci			 */
808c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x108, 0x1009040f);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci			/*
838c2ecf20Sopenharmony_ci			 * VID_PLL Fraction (register 0x10c) = 0x2be2fe
848c2ecf20Sopenharmony_ci			 * 28636360 * 0xf.15f17f0/4 = 108 MHz
858c2ecf20Sopenharmony_ci			 * 432 MHz pre-postdivide
868c2ecf20Sopenharmony_ci			 */
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci			/*
898c2ecf20Sopenharmony_ci			 * AUX_PLL Fraction = 0x0ec6bd6
908c2ecf20Sopenharmony_ci			 * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384
918c2ecf20Sopenharmony_ci			 * 271 MHz pre-postdivide
928c2ecf20Sopenharmony_ci			 * FIXME 28636363 ref_freq doesn't match VID PLL ref
938c2ecf20Sopenharmony_ci			 */
948c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x110, 0x00ec6bd6);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci			/*
978c2ecf20Sopenharmony_ci			 * SA_MCLK_SEL = 1
988c2ecf20Sopenharmony_ci			 * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
998c2ecf20Sopenharmony_ci			 */
1008c2ecf20Sopenharmony_ci			cx25840_write(client, 0x127, 0x50);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci			if (is_cx2583x(state))
1038c2ecf20Sopenharmony_ci				break;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
1068c2ecf20Sopenharmony_ci			/* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
1078c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08016d59);
1088c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08016d59);
1098c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08016d59);
1108c2ecf20Sopenharmony_ci			break;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		case 48000:
1138c2ecf20Sopenharmony_ci			/*
1148c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
1158c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10
1168c2ecf20Sopenharmony_ci			 */
1178c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x108, 0x100a040f);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci			/*
1208c2ecf20Sopenharmony_ci			 * VID_PLL Fraction (register 0x10c) = 0x2be2fe
1218c2ecf20Sopenharmony_ci			 * 28636360 * 0xf.15f17f0/4 = 108 MHz
1228c2ecf20Sopenharmony_ci			 * 432 MHz pre-postdivide
1238c2ecf20Sopenharmony_ci			 */
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci			/*
1268c2ecf20Sopenharmony_ci			 * AUX_PLL Fraction = 0x098d6e5
1278c2ecf20Sopenharmony_ci			 * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384
1288c2ecf20Sopenharmony_ci			 * 295 MHz pre-postdivide
1298c2ecf20Sopenharmony_ci			 * FIXME 28636363 ref_freq doesn't match VID PLL ref
1308c2ecf20Sopenharmony_ci			 */
1318c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x110, 0x0098d6e5);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci			/*
1348c2ecf20Sopenharmony_ci			 * SA_MCLK_SEL = 1
1358c2ecf20Sopenharmony_ci			 * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
1368c2ecf20Sopenharmony_ci			 */
1378c2ecf20Sopenharmony_ci			cx25840_write(client, 0x127, 0x50);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci			if (is_cx2583x(state))
1408c2ecf20Sopenharmony_ci				break;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
1438c2ecf20Sopenharmony_ci			/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
1448c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08014faa);
1458c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08014faa);
1468c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08014faa);
1478c2ecf20Sopenharmony_ci			break;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci	} else {
1508c2ecf20Sopenharmony_ci		switch (freq) {
1518c2ecf20Sopenharmony_ci		case 32000:
1528c2ecf20Sopenharmony_ci			/*
1538c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
1548c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e
1558c2ecf20Sopenharmony_ci			 */
1568c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x108, 0x1e08040f);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci			/*
1598c2ecf20Sopenharmony_ci			 * VID_PLL Fraction (register 0x10c) = 0x2be2fe
1608c2ecf20Sopenharmony_ci			 * 28636360 * 0xf.15f17f0/4 = 108 MHz
1618c2ecf20Sopenharmony_ci			 * 432 MHz pre-postdivide
1628c2ecf20Sopenharmony_ci			 */
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci			/*
1658c2ecf20Sopenharmony_ci			 * AUX_PLL Fraction = 0x12a0869
1668c2ecf20Sopenharmony_ci			 * 28636363 * 0x8.9504348/0x1e = 32000 * 256
1678c2ecf20Sopenharmony_ci			 * 246 MHz pre-postdivide
1688c2ecf20Sopenharmony_ci			 * FIXME 28636363 ref_freq doesn't match VID PLL ref
1698c2ecf20Sopenharmony_ci			 */
1708c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x110, 0x012a0869);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci			/*
1738c2ecf20Sopenharmony_ci			 * SA_MCLK_SEL = 1
1748c2ecf20Sopenharmony_ci			 * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider
1758c2ecf20Sopenharmony_ci			 */
1768c2ecf20Sopenharmony_ci			cx25840_write(client, 0x127, 0x54);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			if (is_cx2583x(state))
1798c2ecf20Sopenharmony_ci				break;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci			/* src1_ctl */
1828c2ecf20Sopenharmony_ci			/* 0x1.0000 = 32000/32000 */
1838c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8f8, 0x08010000);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
1868c2ecf20Sopenharmony_ci			/* 0x2.0000 = 2 * (32000/32000) */
1878c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08020000);
1888c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08020000);
1898c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08020000);
1908c2ecf20Sopenharmony_ci			break;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		case 44100:
1938c2ecf20Sopenharmony_ci			/*
1948c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
1958c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18
1968c2ecf20Sopenharmony_ci			 */
1978c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x108, 0x1809040f);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci			/*
2008c2ecf20Sopenharmony_ci			 * VID_PLL Fraction (register 0x10c) = 0x2be2fe
2018c2ecf20Sopenharmony_ci			 * 28636360 * 0xf.15f17f0/4 = 108 MHz
2028c2ecf20Sopenharmony_ci			 * 432 MHz pre-postdivide
2038c2ecf20Sopenharmony_ci			 */
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			/*
2068c2ecf20Sopenharmony_ci			 * AUX_PLL Fraction = 0x0ec6bd6
2078c2ecf20Sopenharmony_ci			 * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256
2088c2ecf20Sopenharmony_ci			 * 271 MHz pre-postdivide
2098c2ecf20Sopenharmony_ci			 * FIXME 28636363 ref_freq doesn't match VID PLL ref
2108c2ecf20Sopenharmony_ci			 */
2118c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x110, 0x00ec6bd6);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci			/*
2148c2ecf20Sopenharmony_ci			 * SA_MCLK_SEL = 1
2158c2ecf20Sopenharmony_ci			 * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
2168c2ecf20Sopenharmony_ci			 */
2178c2ecf20Sopenharmony_ci			cx25840_write(client, 0x127, 0x50);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci			if (is_cx2583x(state))
2208c2ecf20Sopenharmony_ci				break;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci			/* src1_ctl */
2238c2ecf20Sopenharmony_ci			/* 0x1.60cd = 44100/32000 */
2248c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8f8, 0x080160cd);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
2278c2ecf20Sopenharmony_ci			/* 0x1.7385 = 2 * (32000/44100) */
2288c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08017385);
2298c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08017385);
2308c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08017385);
2318c2ecf20Sopenharmony_ci			break;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		case 48000:
2348c2ecf20Sopenharmony_ci			/*
2358c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
2368c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18
2378c2ecf20Sopenharmony_ci			 */
2388c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x108, 0x180a040f);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci			/*
2418c2ecf20Sopenharmony_ci			 * VID_PLL Fraction (register 0x10c) = 0x2be2fe
2428c2ecf20Sopenharmony_ci			 * 28636360 * 0xf.15f17f0/4 = 108 MHz
2438c2ecf20Sopenharmony_ci			 * 432 MHz pre-postdivide
2448c2ecf20Sopenharmony_ci			 */
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci			/*
2478c2ecf20Sopenharmony_ci			 * AUX_PLL Fraction = 0x098d6e5
2488c2ecf20Sopenharmony_ci			 * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256
2498c2ecf20Sopenharmony_ci			 * 295 MHz pre-postdivide
2508c2ecf20Sopenharmony_ci			 * FIXME 28636363 ref_freq doesn't match VID PLL ref
2518c2ecf20Sopenharmony_ci			 */
2528c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x110, 0x0098d6e5);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci			/*
2558c2ecf20Sopenharmony_ci			 * SA_MCLK_SEL = 1
2568c2ecf20Sopenharmony_ci			 * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
2578c2ecf20Sopenharmony_ci			 */
2588c2ecf20Sopenharmony_ci			cx25840_write(client, 0x127, 0x50);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci			if (is_cx2583x(state))
2618c2ecf20Sopenharmony_ci				break;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci			/* src1_ctl */
2648c2ecf20Sopenharmony_ci			/* 0x1.8000 = 48000/32000 */
2658c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8f8, 0x08018000);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
2688c2ecf20Sopenharmony_ci			/* 0x1.5555 = 2 * (32000/48000) */
2698c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08015555);
2708c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08015555);
2718c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08015555);
2728c2ecf20Sopenharmony_ci			break;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	state->audclk_freq = freq;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	return cx25840_set_audclk_freq(client, freq);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (state->aud_input != CX25840_AUDIO_SERIAL) {
2918c2ecf20Sopenharmony_ci		switch (freq) {
2928c2ecf20Sopenharmony_ci		case 32000:
2938c2ecf20Sopenharmony_ci		case 44100:
2948c2ecf20Sopenharmony_ci		case 48000:
2958c2ecf20Sopenharmony_ci			/* We don't have register values
2968c2ecf20Sopenharmony_ci			 * so avoid destroying registers. */
2978c2ecf20Sopenharmony_ci			/* FIXME return -EINVAL; */
2988c2ecf20Sopenharmony_ci			break;
2998c2ecf20Sopenharmony_ci		}
3008c2ecf20Sopenharmony_ci	} else {
3018c2ecf20Sopenharmony_ci		switch (freq) {
3028c2ecf20Sopenharmony_ci		case 32000:
3038c2ecf20Sopenharmony_ci		case 44100:
3048c2ecf20Sopenharmony_ci			/* We don't have register values
3058c2ecf20Sopenharmony_ci			 * so avoid destroying registers. */
3068c2ecf20Sopenharmony_ci			/* FIXME return -EINVAL; */
3078c2ecf20Sopenharmony_ci			break;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		case 48000:
3108c2ecf20Sopenharmony_ci			/* src1_ctl */
3118c2ecf20Sopenharmony_ci			/* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
3128c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8f8, 0x0801867c);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
3158c2ecf20Sopenharmony_ci			/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
3168c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08014faa);
3178c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08014faa);
3188c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08014faa);
3198c2ecf20Sopenharmony_ci			break;
3208c2ecf20Sopenharmony_ci		}
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	state->audclk_freq = freq;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return 0;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (state->aud_input != CX25840_AUDIO_SERIAL) {
3338c2ecf20Sopenharmony_ci		switch (freq) {
3348c2ecf20Sopenharmony_ci		case 32000:
3358c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
3368c2ecf20Sopenharmony_ci			/* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
3378c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x0801f77f);
3388c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x0801f77f);
3398c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x0801f77f);
3408c2ecf20Sopenharmony_ci			break;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		case 44100:
3438c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
3448c2ecf20Sopenharmony_ci			/* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
3458c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08016d59);
3468c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08016d59);
3478c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08016d59);
3488c2ecf20Sopenharmony_ci			break;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		case 48000:
3518c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
3528c2ecf20Sopenharmony_ci			/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
3538c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08014faa);
3548c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08014faa);
3558c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08014faa);
3568c2ecf20Sopenharmony_ci			break;
3578c2ecf20Sopenharmony_ci		}
3588c2ecf20Sopenharmony_ci	} else {
3598c2ecf20Sopenharmony_ci		switch (freq) {
3608c2ecf20Sopenharmony_ci		/* FIXME These cases make different assumptions about audclk */
3618c2ecf20Sopenharmony_ci		case 32000:
3628c2ecf20Sopenharmony_ci			/* src1_ctl */
3638c2ecf20Sopenharmony_ci			/* 0x1.0000 = 32000/32000 */
3648c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8f8, 0x08010000);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
3678c2ecf20Sopenharmony_ci			/* 0x2.0000 = 2 * (32000/32000) */
3688c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08020000);
3698c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08020000);
3708c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08020000);
3718c2ecf20Sopenharmony_ci			break;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		case 44100:
3748c2ecf20Sopenharmony_ci			/* src1_ctl */
3758c2ecf20Sopenharmony_ci			/* 0x1.60cd = 44100/32000 */
3768c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8f8, 0x080160cd);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
3798c2ecf20Sopenharmony_ci			/* 0x1.7385 = 2 * (32000/44100) */
3808c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08017385);
3818c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08017385);
3828c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08017385);
3838c2ecf20Sopenharmony_ci			break;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		case 48000:
3868c2ecf20Sopenharmony_ci			/* src1_ctl */
3878c2ecf20Sopenharmony_ci			/* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
3888c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8f8, 0x0801867c);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
3918c2ecf20Sopenharmony_ci			/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
3928c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x900, 0x08014faa);
3938c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x904, 0x08014faa);
3948c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x90c, 0x08014faa);
3958c2ecf20Sopenharmony_ci			break;
3968c2ecf20Sopenharmony_ci		}
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	state->audclk_freq = freq;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int set_audclk_freq(struct i2c_client *client, u32 freq)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (freq != 32000 && freq != 44100 && freq != 48000)
4098c2ecf20Sopenharmony_ci		return -EINVAL;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (is_cx231xx(state))
4128c2ecf20Sopenharmony_ci		return cx231xx_set_audclk_freq(client, freq);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (is_cx2388x(state))
4158c2ecf20Sopenharmony_ci		return cx23885_set_audclk_freq(client, freq);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (is_cx2583x(state))
4188c2ecf20Sopenharmony_ci		return cx25836_set_audclk_freq(client, freq);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return cx25840_set_audclk_freq(client, freq);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_civoid cx25840_audio_set_path(struct i2c_client *client)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (!is_cx2583x(state)) {
4288c2ecf20Sopenharmony_ci		/* assert soft reset */
4298c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x810, ~0x1, 0x01);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		/* stop microcontroller */
4328c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x803, ~0x10, 0);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		/* Mute everything to prevent the PFFT! */
4358c2ecf20Sopenharmony_ci		cx25840_write(client, 0x8d3, 0x1f);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		if (state->aud_input == CX25840_AUDIO_SERIAL) {
4388c2ecf20Sopenharmony_ci			/* Set Path1 to Serial Audio Input */
4398c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8d0, 0x01011012);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci			/* The microcontroller should not be started for the
4428c2ecf20Sopenharmony_ci			 * non-tuner inputs: autodetection is specific for
4438c2ecf20Sopenharmony_ci			 * TV audio. */
4448c2ecf20Sopenharmony_ci		} else {
4458c2ecf20Sopenharmony_ci			/* Set Path1 to Analog Demod Main Channel */
4468c2ecf20Sopenharmony_ci			cx25840_write4(client, 0x8d0, 0x1f063870);
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	set_audclk_freq(client, state->audclk_freq);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!is_cx2583x(state)) {
4538c2ecf20Sopenharmony_ci		if (state->aud_input != CX25840_AUDIO_SERIAL) {
4548c2ecf20Sopenharmony_ci			/* When the microcontroller detects the
4558c2ecf20Sopenharmony_ci			 * audio format, it will unmute the lines */
4568c2ecf20Sopenharmony_ci			cx25840_and_or(client, 0x803, ~0x10, 0x10);
4578c2ecf20Sopenharmony_ci		}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		/* deassert soft reset */
4608c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x810, ~0x1, 0x00);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		/* Ensure the controller is running when we exit */
4638c2ecf20Sopenharmony_ci		if (is_cx2388x(state) || is_cx231xx(state))
4648c2ecf20Sopenharmony_ci			cx25840_and_or(client, 0x803, ~0x10, 0x10);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void set_volume(struct i2c_client *client, int volume)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	int vol;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/* Convert the volume to msp3400 values (0-127) */
4738c2ecf20Sopenharmony_ci	vol = volume >> 9;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/* now scale it up to cx25840 values
4768c2ecf20Sopenharmony_ci	 * -114dB to -96dB maps to 0
4778c2ecf20Sopenharmony_ci	 * this should be 19, but in my testing that was 4dB too loud */
4788c2ecf20Sopenharmony_ci	if (vol <= 23) {
4798c2ecf20Sopenharmony_ci		vol = 0;
4808c2ecf20Sopenharmony_ci	} else {
4818c2ecf20Sopenharmony_ci		vol -= 23;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* PATH1_VOLUME */
4858c2ecf20Sopenharmony_ci	cx25840_write(client, 0x8d4, 228 - (vol * 2));
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic void set_balance(struct i2c_client *client, int balance)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	int bal = balance >> 8;
4918c2ecf20Sopenharmony_ci	if (bal > 0x80) {
4928c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEFT */
4938c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x8d5, 0x7f, 0x80);
4948c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEVEL */
4958c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f);
4968c2ecf20Sopenharmony_ci	} else {
4978c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEFT */
4988c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x8d5, 0x7f, 0x00);
4998c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEVEL */
5008c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal);
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ciint cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
5078c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
5088c2ecf20Sopenharmony_ci	int retval;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (!is_cx2583x(state))
5118c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x810, ~0x1, 1);
5128c2ecf20Sopenharmony_ci	if (state->aud_input != CX25840_AUDIO_SERIAL) {
5138c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x803, ~0x10, 0);
5148c2ecf20Sopenharmony_ci		cx25840_write(client, 0x8d3, 0x1f);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	retval = set_audclk_freq(client, freq);
5178c2ecf20Sopenharmony_ci	if (state->aud_input != CX25840_AUDIO_SERIAL)
5188c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x803, ~0x10, 0x10);
5198c2ecf20Sopenharmony_ci	if (!is_cx2583x(state))
5208c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x810, ~0x1, 0);
5218c2ecf20Sopenharmony_ci	return retval;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = to_sd(ctrl);
5278c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
5288c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	switch (ctrl->id) {
5318c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
5328c2ecf20Sopenharmony_ci		if (state->mute->val)
5338c2ecf20Sopenharmony_ci			set_volume(client, 0);
5348c2ecf20Sopenharmony_ci		else
5358c2ecf20Sopenharmony_ci			set_volume(client, state->volume->val);
5368c2ecf20Sopenharmony_ci		break;
5378c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_BASS:
5388c2ecf20Sopenharmony_ci		/* PATH1_EQ_BASS_VOL */
5398c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x8d9, ~0x3f,
5408c2ecf20Sopenharmony_ci					48 - (ctrl->val * 48 / 0xffff));
5418c2ecf20Sopenharmony_ci		break;
5428c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_TREBLE:
5438c2ecf20Sopenharmony_ci		/* PATH1_EQ_TREBLE_VOL */
5448c2ecf20Sopenharmony_ci		cx25840_and_or(client, 0x8db, ~0x3f,
5458c2ecf20Sopenharmony_ci					48 - (ctrl->val * 48 / 0xffff));
5468c2ecf20Sopenharmony_ci		break;
5478c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_BALANCE:
5488c2ecf20Sopenharmony_ci		set_balance(client, ctrl->val);
5498c2ecf20Sopenharmony_ci		break;
5508c2ecf20Sopenharmony_ci	default:
5518c2ecf20Sopenharmony_ci		return -EINVAL;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci	return 0;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ciconst struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = {
5578c2ecf20Sopenharmony_ci	.s_ctrl = cx25840_audio_s_ctrl,
5588c2ecf20Sopenharmony_ci};
559