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