18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  cx18 ADEC audio functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Derived from cx25840-audio.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
88c2ecf20Sopenharmony_ci *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "cx18-driver.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic int set_audclk_freq(struct cx18 *cx, u32 freq)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct cx18_av_state *state = &cx->av_state;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	if (freq != 32000 && freq != 44100 && freq != 48000)
188c2ecf20Sopenharmony_ci		return -EINVAL;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	/*
218c2ecf20Sopenharmony_ci	 * The PLL parameters are based on the external crystal frequency that
228c2ecf20Sopenharmony_ci	 * would ideally be:
238c2ecf20Sopenharmony_ci	 *
248c2ecf20Sopenharmony_ci	 * NTSC Color subcarrier freq * 8 =
258c2ecf20Sopenharmony_ci	 *	4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
268c2ecf20Sopenharmony_ci	 *
278c2ecf20Sopenharmony_ci	 * The accidents of history and rationale that explain from where this
288c2ecf20Sopenharmony_ci	 * combination of magic numbers originate can be found in:
298c2ecf20Sopenharmony_ci	 *
308c2ecf20Sopenharmony_ci	 * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
318c2ecf20Sopenharmony_ci	 * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
328c2ecf20Sopenharmony_ci	 *
338c2ecf20Sopenharmony_ci	 * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
348c2ecf20Sopenharmony_ci	 * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
358c2ecf20Sopenharmony_ci	 *
368c2ecf20Sopenharmony_ci	 * As Mike Bradley has rightly pointed out, it's not the exact crystal
378c2ecf20Sopenharmony_ci	 * frequency that matters, only that all parts of the driver and
388c2ecf20Sopenharmony_ci	 * firmware are using the same value (close to the ideal value).
398c2ecf20Sopenharmony_ci	 *
408c2ecf20Sopenharmony_ci	 * Since I have a strong suspicion that, if the firmware ever assumes a
418c2ecf20Sopenharmony_ci	 * crystal value at all, it will assume 28.636360 MHz, the crystal
428c2ecf20Sopenharmony_ci	 * freq used in calculations in this driver will be:
438c2ecf20Sopenharmony_ci	 *
448c2ecf20Sopenharmony_ci	 *	xtal_freq = 28.636360 MHz
458c2ecf20Sopenharmony_ci	 *
468c2ecf20Sopenharmony_ci	 * an error of less than 0.13 ppm which is way, way better than any off
478c2ecf20Sopenharmony_ci	 * the shelf crystal will have for accuracy anyway.
488c2ecf20Sopenharmony_ci	 *
498c2ecf20Sopenharmony_ci	 * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error.
508c2ecf20Sopenharmony_ci	 *
518c2ecf20Sopenharmony_ci	 * Many thanks to Jeff Campbell and Mike Bradley for their extensive
528c2ecf20Sopenharmony_ci	 * investigation, experimentation, testing, and suggested solutions of
538c2ecf20Sopenharmony_ci	 * of audio/video sync problems with SVideo and CVBS captures.
548c2ecf20Sopenharmony_ci	 */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
578c2ecf20Sopenharmony_ci		switch (freq) {
588c2ecf20Sopenharmony_ci		case 32000:
598c2ecf20Sopenharmony_ci			/*
608c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
618c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
628c2ecf20Sopenharmony_ci			 */
638c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x108, 0x200d040f);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci			/* VID_PLL Fraction = 0x2be2fe */
668c2ecf20Sopenharmony_ci			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
678c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x10c, 0x002be2fe);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci			/* AUX_PLL Fraction = 0x176740c */
708c2ecf20Sopenharmony_ci			/* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/
718c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x110, 0x0176740c);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
748c2ecf20Sopenharmony_ci			/* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */
758c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x900, 0x0801f77f);
768c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x904, 0x0801f77f);
778c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x90c, 0x0801f77f);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
808c2ecf20Sopenharmony_ci			cx18_av_write(cx, 0x127, 0x60);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci			/* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */
838c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x12c, 0x11202fff);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci			/*
868c2ecf20Sopenharmony_ci			 * EN_AV_LOCK = 0
878c2ecf20Sopenharmony_ci			 * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
888c2ecf20Sopenharmony_ci			 *  ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
898c2ecf20Sopenharmony_ci			 */
908c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x128, 0xa00d2ef8);
918c2ecf20Sopenharmony_ci			break;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		case 44100:
948c2ecf20Sopenharmony_ci			/*
958c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
968c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18
978c2ecf20Sopenharmony_ci			 */
988c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x108, 0x180e040f);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci			/* VID_PLL Fraction = 0x2be2fe */
1018c2ecf20Sopenharmony_ci			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
1028c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x10c, 0x002be2fe);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci			/* AUX_PLL Fraction = 0x062a1f2 */
1058c2ecf20Sopenharmony_ci			/* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/
1068c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x110, 0x0062a1f2);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
1098c2ecf20Sopenharmony_ci			/* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */
1108c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x900, 0x08016d59);
1118c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x904, 0x08016d59);
1128c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x90c, 0x08016d59);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */
1158c2ecf20Sopenharmony_ci			cx18_av_write(cx, 0x127, 0x58);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci			/* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */
1188c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x12c, 0x112092ff);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci			/*
1218c2ecf20Sopenharmony_ci			 * EN_AV_LOCK = 0
1228c2ecf20Sopenharmony_ci			 * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
1238c2ecf20Sopenharmony_ci			 *  ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
1248c2ecf20Sopenharmony_ci			 */
1258c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x128, 0xa01d4bf8);
1268c2ecf20Sopenharmony_ci			break;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		case 48000:
1298c2ecf20Sopenharmony_ci			/*
1308c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
1318c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16
1328c2ecf20Sopenharmony_ci			 */
1338c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x108, 0x160e040f);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci			/* VID_PLL Fraction = 0x2be2fe */
1368c2ecf20Sopenharmony_ci			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
1378c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x10c, 0x002be2fe);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci			/* AUX_PLL Fraction = 0x05227ad */
1408c2ecf20Sopenharmony_ci			/* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/
1418c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x110, 0x005227ad);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
1448c2ecf20Sopenharmony_ci			/* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */
1458c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x900, 0x08014faa);
1468c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x904, 0x08014faa);
1478c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x90c, 0x08014faa);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
1508c2ecf20Sopenharmony_ci			cx18_av_write(cx, 0x127, 0x56);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci			/* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */
1538c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x12c, 0x11205fff);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci			/*
1568c2ecf20Sopenharmony_ci			 * EN_AV_LOCK = 0
1578c2ecf20Sopenharmony_ci			 * VID_COUNT = 0x1193f8 = 143999.000 * 8 =
1588c2ecf20Sopenharmony_ci			 *  ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
1598c2ecf20Sopenharmony_ci			 */
1608c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x128, 0xa01193f8);
1618c2ecf20Sopenharmony_ci			break;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	} else {
1648c2ecf20Sopenharmony_ci		switch (freq) {
1658c2ecf20Sopenharmony_ci		case 32000:
1668c2ecf20Sopenharmony_ci			/*
1678c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
1688c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30
1698c2ecf20Sopenharmony_ci			 */
1708c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x108, 0x300d040f);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci			/* VID_PLL Fraction = 0x2be2fe */
1738c2ecf20Sopenharmony_ci			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
1748c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x10c, 0x002be2fe);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci			/* AUX_PLL Fraction = 0x176740c */
1778c2ecf20Sopenharmony_ci			/* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/
1788c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x110, 0x0176740c);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci			/* src1_ctl */
1818c2ecf20Sopenharmony_ci			/* 0x1.0000 = 32000/32000 */
1828c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x8f8, 0x08010000);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
1858c2ecf20Sopenharmony_ci			/* 0x2.0000 = 2 * (32000/32000) */
1868c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x900, 0x08020000);
1878c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x904, 0x08020000);
1888c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x90c, 0x08020000);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */
1918c2ecf20Sopenharmony_ci			cx18_av_write(cx, 0x127, 0x70);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci			/* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */
1948c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x12c, 0x11201fff);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci			/*
1978c2ecf20Sopenharmony_ci			 * EN_AV_LOCK = 0
1988c2ecf20Sopenharmony_ci			 * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
1998c2ecf20Sopenharmony_ci			 *  ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
2008c2ecf20Sopenharmony_ci			 */
2018c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x128, 0xa00d2ef8);
2028c2ecf20Sopenharmony_ci			break;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		case 44100:
2058c2ecf20Sopenharmony_ci			/*
2068c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
2078c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24
2088c2ecf20Sopenharmony_ci			 */
2098c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x108, 0x240e040f);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci			/* VID_PLL Fraction = 0x2be2fe */
2128c2ecf20Sopenharmony_ci			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
2138c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x10c, 0x002be2fe);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci			/* AUX_PLL Fraction = 0x062a1f2 */
2168c2ecf20Sopenharmony_ci			/* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/
2178c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x110, 0x0062a1f2);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci			/* src1_ctl */
2208c2ecf20Sopenharmony_ci			/* 0x1.60cd = 44100/32000 */
2218c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x8f8, 0x080160cd);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
2248c2ecf20Sopenharmony_ci			/* 0x1.7385 = 2 * (32000/44100) */
2258c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x900, 0x08017385);
2268c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x904, 0x08017385);
2278c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x90c, 0x08017385);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */
2308c2ecf20Sopenharmony_ci			cx18_av_write(cx, 0x127, 0x64);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci			/* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */
2338c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x12c, 0x112061ff);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci			/*
2368c2ecf20Sopenharmony_ci			 * EN_AV_LOCK = 0
2378c2ecf20Sopenharmony_ci			 * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
2388c2ecf20Sopenharmony_ci			 *  ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
2398c2ecf20Sopenharmony_ci			 */
2408c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x128, 0xa01d4bf8);
2418c2ecf20Sopenharmony_ci			break;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		case 48000:
2448c2ecf20Sopenharmony_ci			/*
2458c2ecf20Sopenharmony_ci			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
2468c2ecf20Sopenharmony_ci			 * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
2478c2ecf20Sopenharmony_ci			 */
2488c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x108, 0x200d040f);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci			/* VID_PLL Fraction = 0x2be2fe */
2518c2ecf20Sopenharmony_ci			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
2528c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x10c, 0x002be2fe);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci			/* AUX_PLL Fraction = 0x176740c */
2558c2ecf20Sopenharmony_ci			/* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/
2568c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x110, 0x0176740c);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci			/* src1_ctl */
2598c2ecf20Sopenharmony_ci			/* 0x1.8000 = 48000/32000 */
2608c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x8f8, 0x08018000);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci			/* src3/4/6_ctl */
2638c2ecf20Sopenharmony_ci			/* 0x1.5555 = 2 * (32000/48000) */
2648c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x900, 0x08015555);
2658c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x904, 0x08015555);
2668c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x90c, 0x08015555);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
2698c2ecf20Sopenharmony_ci			cx18_av_write(cx, 0x127, 0x60);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci			/* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */
2728c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x12c, 0x11203fff);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci			/*
2758c2ecf20Sopenharmony_ci			 * EN_AV_LOCK = 0
2768c2ecf20Sopenharmony_ci			 * VID_COUNT = 0x1193f8 = 143999.000 * 8 =
2778c2ecf20Sopenharmony_ci			 *  ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
2788c2ecf20Sopenharmony_ci			 */
2798c2ecf20Sopenharmony_ci			cx18_av_write4(cx, 0x128, 0xa01193f8);
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	state->audclk_freq = freq;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_civoid cx18_av_audio_set_path(struct cx18 *cx)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct cx18_av_state *state = &cx->av_state;
2928c2ecf20Sopenharmony_ci	u8 v;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* stop microcontroller */
2958c2ecf20Sopenharmony_ci	v = cx18_av_read(cx, 0x803) & ~0x10;
2968c2ecf20Sopenharmony_ci	cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* assert soft reset */
2998c2ecf20Sopenharmony_ci	v = cx18_av_read(cx, 0x810) | 0x01;
3008c2ecf20Sopenharmony_ci	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Mute everything to prevent the PFFT! */
3038c2ecf20Sopenharmony_ci	cx18_av_write(cx, 0x8d3, 0x1f);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) {
3068c2ecf20Sopenharmony_ci		/* Set Path1 to Serial Audio Input */
3078c2ecf20Sopenharmony_ci		cx18_av_write4(cx, 0x8d0, 0x01011012);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		/* The microcontroller should not be started for the
3108c2ecf20Sopenharmony_ci		 * non-tuner inputs: autodetection is specific for
3118c2ecf20Sopenharmony_ci		 * TV audio. */
3128c2ecf20Sopenharmony_ci	} else {
3138c2ecf20Sopenharmony_ci		/* Set Path1 to Analog Demod Main Channel */
3148c2ecf20Sopenharmony_ci		cx18_av_write4(cx, 0x8d0, 0x1f063870);
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	set_audclk_freq(cx, state->audclk_freq);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* deassert soft reset */
3208c2ecf20Sopenharmony_ci	v = cx18_av_read(cx, 0x810) & ~0x01;
3218c2ecf20Sopenharmony_ci	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
3248c2ecf20Sopenharmony_ci		/* When the microcontroller detects the
3258c2ecf20Sopenharmony_ci		 * audio format, it will unmute the lines */
3268c2ecf20Sopenharmony_ci		v = cx18_av_read(cx, 0x803) | 0x10;
3278c2ecf20Sopenharmony_ci		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void set_volume(struct cx18 *cx, int volume)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	/* First convert the volume to msp3400 values (0-127) */
3348c2ecf20Sopenharmony_ci	int vol = volume >> 9;
3358c2ecf20Sopenharmony_ci	/* now scale it up to cx18_av values
3368c2ecf20Sopenharmony_ci	 * -114dB to -96dB maps to 0
3378c2ecf20Sopenharmony_ci	 * this should be 19, but in my testing that was 4dB too loud */
3388c2ecf20Sopenharmony_ci	if (vol <= 23)
3398c2ecf20Sopenharmony_ci		vol = 0;
3408c2ecf20Sopenharmony_ci	else
3418c2ecf20Sopenharmony_ci		vol -= 23;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* PATH1_VOLUME */
3448c2ecf20Sopenharmony_ci	cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void set_bass(struct cx18 *cx, int bass)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	/* PATH1_EQ_BASS_VOL */
3508c2ecf20Sopenharmony_ci	cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic void set_treble(struct cx18 *cx, int treble)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	/* PATH1_EQ_TREBLE_VOL */
3568c2ecf20Sopenharmony_ci	cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void set_balance(struct cx18 *cx, int balance)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	int bal = balance >> 8;
3628c2ecf20Sopenharmony_ci	if (bal > 0x80) {
3638c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEFT */
3648c2ecf20Sopenharmony_ci		cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
3658c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEVEL */
3668c2ecf20Sopenharmony_ci		cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
3678c2ecf20Sopenharmony_ci	} else {
3688c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEFT */
3698c2ecf20Sopenharmony_ci		cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
3708c2ecf20Sopenharmony_ci		/* PATH1_BAL_LEVEL */
3718c2ecf20Sopenharmony_ci		cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic void set_mute(struct cx18 *cx, int mute)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct cx18_av_state *state = &cx->av_state;
3788c2ecf20Sopenharmony_ci	u8 v;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
3818c2ecf20Sopenharmony_ci		/* Must turn off microcontroller in order to mute sound.
3828c2ecf20Sopenharmony_ci		 * Not sure if this is the best method, but it does work.
3838c2ecf20Sopenharmony_ci		 * If the microcontroller is running, then it will undo any
3848c2ecf20Sopenharmony_ci		 * changes to the mute register. */
3858c2ecf20Sopenharmony_ci		v = cx18_av_read(cx, 0x803);
3868c2ecf20Sopenharmony_ci		if (mute) {
3878c2ecf20Sopenharmony_ci			/* disable microcontroller */
3888c2ecf20Sopenharmony_ci			v &= ~0x10;
3898c2ecf20Sopenharmony_ci			cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
3908c2ecf20Sopenharmony_ci			cx18_av_write(cx, 0x8d3, 0x1f);
3918c2ecf20Sopenharmony_ci		} else {
3928c2ecf20Sopenharmony_ci			/* enable microcontroller */
3938c2ecf20Sopenharmony_ci			v |= 0x10;
3948c2ecf20Sopenharmony_ci			cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
3958c2ecf20Sopenharmony_ci		}
3968c2ecf20Sopenharmony_ci	} else {
3978c2ecf20Sopenharmony_ci		/* SRC1_MUTE_EN */
3988c2ecf20Sopenharmony_ci		cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ciint cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct cx18 *cx = v4l2_get_subdevdata(sd);
4058c2ecf20Sopenharmony_ci	struct cx18_av_state *state = &cx->av_state;
4068c2ecf20Sopenharmony_ci	int retval;
4078c2ecf20Sopenharmony_ci	u8 v;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
4108c2ecf20Sopenharmony_ci		v = cx18_av_read(cx, 0x803) & ~0x10;
4118c2ecf20Sopenharmony_ci		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
4128c2ecf20Sopenharmony_ci		cx18_av_write(cx, 0x8d3, 0x1f);
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	v = cx18_av_read(cx, 0x810) | 0x1;
4158c2ecf20Sopenharmony_ci	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	retval = set_audclk_freq(cx, freq);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	v = cx18_av_read(cx, 0x810) & ~0x1;
4208c2ecf20Sopenharmony_ci	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
4218c2ecf20Sopenharmony_ci	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
4228c2ecf20Sopenharmony_ci		v = cx18_av_read(cx, 0x803) | 0x10;
4238c2ecf20Sopenharmony_ci		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci	return retval;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = to_sd(ctrl);
4318c2ecf20Sopenharmony_ci	struct cx18 *cx = v4l2_get_subdevdata(sd);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	switch (ctrl->id) {
4348c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
4358c2ecf20Sopenharmony_ci		set_volume(cx, ctrl->val);
4368c2ecf20Sopenharmony_ci		break;
4378c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_BASS:
4388c2ecf20Sopenharmony_ci		set_bass(cx, ctrl->val);
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_TREBLE:
4418c2ecf20Sopenharmony_ci		set_treble(cx, ctrl->val);
4428c2ecf20Sopenharmony_ci		break;
4438c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_BALANCE:
4448c2ecf20Sopenharmony_ci		set_balance(cx, ctrl->val);
4458c2ecf20Sopenharmony_ci		break;
4468c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
4478c2ecf20Sopenharmony_ci		set_mute(cx, ctrl->val);
4488c2ecf20Sopenharmony_ci		break;
4498c2ecf20Sopenharmony_ci	default:
4508c2ecf20Sopenharmony_ci		return -EINVAL;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci	return 0;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ciconst struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = {
4568c2ecf20Sopenharmony_ci	.s_ctrl = cx18_av_audio_s_ctrl,
4578c2ecf20Sopenharmony_ci};
458