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