162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cx18 ADEC audio functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from cx25840-audio.c 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> 862306a36Sopenharmony_ci * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "cx18-driver.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic int set_audclk_freq(struct cx18 *cx, u32 freq) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct cx18_av_state *state = &cx->av_state; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci if (freq != 32000 && freq != 44100 && freq != 48000) 1862306a36Sopenharmony_ci return -EINVAL; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci /* 2162306a36Sopenharmony_ci * The PLL parameters are based on the external crystal frequency that 2262306a36Sopenharmony_ci * would ideally be: 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * NTSC Color subcarrier freq * 8 = 2562306a36Sopenharmony_ci * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * The accidents of history and rationale that explain from where this 2862306a36Sopenharmony_ci * combination of magic numbers originate can be found in: 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in 3162306a36Sopenharmony_ci * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the 3462306a36Sopenharmony_ci * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * As Mike Bradley has rightly pointed out, it's not the exact crystal 3762306a36Sopenharmony_ci * frequency that matters, only that all parts of the driver and 3862306a36Sopenharmony_ci * firmware are using the same value (close to the ideal value). 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Since I have a strong suspicion that, if the firmware ever assumes a 4162306a36Sopenharmony_ci * crystal value at all, it will assume 28.636360 MHz, the crystal 4262306a36Sopenharmony_ci * freq used in calculations in this driver will be: 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * xtal_freq = 28.636360 MHz 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * an error of less than 0.13 ppm which is way, way better than any off 4762306a36Sopenharmony_ci * the shelf crystal will have for accuracy anyway. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Below I aim to run the PLLs' VCOs near 400 MHz to minimize error. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Many thanks to Jeff Campbell and Mike Bradley for their extensive 5262306a36Sopenharmony_ci * investigation, experimentation, testing, and suggested solutions of 5362306a36Sopenharmony_ci * audio/video sync problems with SVideo and CVBS captures. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { 5762306a36Sopenharmony_ci switch (freq) { 5862306a36Sopenharmony_ci case 32000: 5962306a36Sopenharmony_ci /* 6062306a36Sopenharmony_ci * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 6162306a36Sopenharmony_ci * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci cx18_av_write4(cx, 0x108, 0x200d040f); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* VID_PLL Fraction = 0x2be2fe */ 6662306a36Sopenharmony_ci /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ 6762306a36Sopenharmony_ci cx18_av_write4(cx, 0x10c, 0x002be2fe); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* AUX_PLL Fraction = 0x176740c */ 7062306a36Sopenharmony_ci /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/ 7162306a36Sopenharmony_ci cx18_av_write4(cx, 0x110, 0x0176740c); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* src3/4/6_ctl */ 7462306a36Sopenharmony_ci /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */ 7562306a36Sopenharmony_ci cx18_av_write4(cx, 0x900, 0x0801f77f); 7662306a36Sopenharmony_ci cx18_av_write4(cx, 0x904, 0x0801f77f); 7762306a36Sopenharmony_ci cx18_av_write4(cx, 0x90c, 0x0801f77f); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ 8062306a36Sopenharmony_ci cx18_av_write(cx, 0x127, 0x60); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */ 8362306a36Sopenharmony_ci cx18_av_write4(cx, 0x12c, 0x11202fff); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * EN_AV_LOCK = 0 8762306a36Sopenharmony_ci * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = 8862306a36Sopenharmony_ci * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci cx18_av_write4(cx, 0x128, 0xa00d2ef8); 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci case 44100: 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 9662306a36Sopenharmony_ci * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci cx18_av_write4(cx, 0x108, 0x180e040f); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* VID_PLL Fraction = 0x2be2fe */ 10162306a36Sopenharmony_ci /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ 10262306a36Sopenharmony_ci cx18_av_write4(cx, 0x10c, 0x002be2fe); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* AUX_PLL Fraction = 0x062a1f2 */ 10562306a36Sopenharmony_ci /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/ 10662306a36Sopenharmony_ci cx18_av_write4(cx, 0x110, 0x0062a1f2); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* src3/4/6_ctl */ 10962306a36Sopenharmony_ci /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */ 11062306a36Sopenharmony_ci cx18_av_write4(cx, 0x900, 0x08016d59); 11162306a36Sopenharmony_ci cx18_av_write4(cx, 0x904, 0x08016d59); 11262306a36Sopenharmony_ci cx18_av_write4(cx, 0x90c, 0x08016d59); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */ 11562306a36Sopenharmony_ci cx18_av_write(cx, 0x127, 0x58); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */ 11862306a36Sopenharmony_ci cx18_av_write4(cx, 0x12c, 0x112092ff); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * EN_AV_LOCK = 0 12262306a36Sopenharmony_ci * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = 12362306a36Sopenharmony_ci * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci cx18_av_write4(cx, 0x128, 0xa01d4bf8); 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci case 48000: 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 13162306a36Sopenharmony_ci * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci cx18_av_write4(cx, 0x108, 0x160e040f); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* VID_PLL Fraction = 0x2be2fe */ 13662306a36Sopenharmony_ci /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ 13762306a36Sopenharmony_ci cx18_av_write4(cx, 0x10c, 0x002be2fe); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* AUX_PLL Fraction = 0x05227ad */ 14062306a36Sopenharmony_ci /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/ 14162306a36Sopenharmony_ci cx18_av_write4(cx, 0x110, 0x005227ad); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* src3/4/6_ctl */ 14462306a36Sopenharmony_ci /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */ 14562306a36Sopenharmony_ci cx18_av_write4(cx, 0x900, 0x08014faa); 14662306a36Sopenharmony_ci cx18_av_write4(cx, 0x904, 0x08014faa); 14762306a36Sopenharmony_ci cx18_av_write4(cx, 0x90c, 0x08014faa); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ 15062306a36Sopenharmony_ci cx18_av_write(cx, 0x127, 0x56); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */ 15362306a36Sopenharmony_ci cx18_av_write4(cx, 0x12c, 0x11205fff); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * EN_AV_LOCK = 0 15762306a36Sopenharmony_ci * VID_COUNT = 0x1193f8 = 143999.000 * 8 = 15862306a36Sopenharmony_ci * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci cx18_av_write4(cx, 0x128, 0xa01193f8); 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci } else { 16462306a36Sopenharmony_ci switch (freq) { 16562306a36Sopenharmony_ci case 32000: 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 16862306a36Sopenharmony_ci * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci cx18_av_write4(cx, 0x108, 0x300d040f); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* VID_PLL Fraction = 0x2be2fe */ 17362306a36Sopenharmony_ci /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ 17462306a36Sopenharmony_ci cx18_av_write4(cx, 0x10c, 0x002be2fe); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* AUX_PLL Fraction = 0x176740c */ 17762306a36Sopenharmony_ci /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/ 17862306a36Sopenharmony_ci cx18_av_write4(cx, 0x110, 0x0176740c); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* src1_ctl */ 18162306a36Sopenharmony_ci /* 0x1.0000 = 32000/32000 */ 18262306a36Sopenharmony_ci cx18_av_write4(cx, 0x8f8, 0x08010000); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* src3/4/6_ctl */ 18562306a36Sopenharmony_ci /* 0x2.0000 = 2 * (32000/32000) */ 18662306a36Sopenharmony_ci cx18_av_write4(cx, 0x900, 0x08020000); 18762306a36Sopenharmony_ci cx18_av_write4(cx, 0x904, 0x08020000); 18862306a36Sopenharmony_ci cx18_av_write4(cx, 0x90c, 0x08020000); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */ 19162306a36Sopenharmony_ci cx18_av_write(cx, 0x127, 0x70); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */ 19462306a36Sopenharmony_ci cx18_av_write4(cx, 0x12c, 0x11201fff); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * EN_AV_LOCK = 0 19862306a36Sopenharmony_ci * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = 19962306a36Sopenharmony_ci * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci cx18_av_write4(cx, 0x128, 0xa00d2ef8); 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci case 44100: 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 20762306a36Sopenharmony_ci * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci cx18_av_write4(cx, 0x108, 0x240e040f); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* VID_PLL Fraction = 0x2be2fe */ 21262306a36Sopenharmony_ci /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ 21362306a36Sopenharmony_ci cx18_av_write4(cx, 0x10c, 0x002be2fe); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* AUX_PLL Fraction = 0x062a1f2 */ 21662306a36Sopenharmony_ci /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/ 21762306a36Sopenharmony_ci cx18_av_write4(cx, 0x110, 0x0062a1f2); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* src1_ctl */ 22062306a36Sopenharmony_ci /* 0x1.60cd = 44100/32000 */ 22162306a36Sopenharmony_ci cx18_av_write4(cx, 0x8f8, 0x080160cd); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* src3/4/6_ctl */ 22462306a36Sopenharmony_ci /* 0x1.7385 = 2 * (32000/44100) */ 22562306a36Sopenharmony_ci cx18_av_write4(cx, 0x900, 0x08017385); 22662306a36Sopenharmony_ci cx18_av_write4(cx, 0x904, 0x08017385); 22762306a36Sopenharmony_ci cx18_av_write4(cx, 0x90c, 0x08017385); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */ 23062306a36Sopenharmony_ci cx18_av_write(cx, 0x127, 0x64); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */ 23362306a36Sopenharmony_ci cx18_av_write4(cx, 0x12c, 0x112061ff); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * EN_AV_LOCK = 0 23762306a36Sopenharmony_ci * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = 23862306a36Sopenharmony_ci * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci cx18_av_write4(cx, 0x128, 0xa01d4bf8); 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci case 48000: 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 24662306a36Sopenharmony_ci * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci cx18_av_write4(cx, 0x108, 0x200d040f); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* VID_PLL Fraction = 0x2be2fe */ 25162306a36Sopenharmony_ci /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ 25262306a36Sopenharmony_ci cx18_av_write4(cx, 0x10c, 0x002be2fe); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* AUX_PLL Fraction = 0x176740c */ 25562306a36Sopenharmony_ci /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/ 25662306a36Sopenharmony_ci cx18_av_write4(cx, 0x110, 0x0176740c); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* src1_ctl */ 25962306a36Sopenharmony_ci /* 0x1.8000 = 48000/32000 */ 26062306a36Sopenharmony_ci cx18_av_write4(cx, 0x8f8, 0x08018000); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* src3/4/6_ctl */ 26362306a36Sopenharmony_ci /* 0x1.5555 = 2 * (32000/48000) */ 26462306a36Sopenharmony_ci cx18_av_write4(cx, 0x900, 0x08015555); 26562306a36Sopenharmony_ci cx18_av_write4(cx, 0x904, 0x08015555); 26662306a36Sopenharmony_ci cx18_av_write4(cx, 0x90c, 0x08015555); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ 26962306a36Sopenharmony_ci cx18_av_write(cx, 0x127, 0x60); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */ 27262306a36Sopenharmony_ci cx18_av_write4(cx, 0x12c, 0x11203fff); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * EN_AV_LOCK = 0 27662306a36Sopenharmony_ci * VID_COUNT = 0x1193f8 = 143999.000 * 8 = 27762306a36Sopenharmony_ci * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci cx18_av_write4(cx, 0x128, 0xa01193f8); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci state->audclk_freq = freq; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_civoid cx18_av_audio_set_path(struct cx18 *cx) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct cx18_av_state *state = &cx->av_state; 29262306a36Sopenharmony_ci u8 v; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* stop microcontroller */ 29562306a36Sopenharmony_ci v = cx18_av_read(cx, 0x803) & ~0x10; 29662306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x803, v, v, 0x1f); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* assert soft reset */ 29962306a36Sopenharmony_ci v = cx18_av_read(cx, 0x810) | 0x01; 30062306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x810, v, v, 0x0f); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Mute everything to prevent the PFFT! */ 30362306a36Sopenharmony_ci cx18_av_write(cx, 0x8d3, 0x1f); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) { 30662306a36Sopenharmony_ci /* Set Path1 to Serial Audio Input */ 30762306a36Sopenharmony_ci cx18_av_write4(cx, 0x8d0, 0x01011012); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* The microcontroller should not be started for the 31062306a36Sopenharmony_ci * non-tuner inputs: autodetection is specific for 31162306a36Sopenharmony_ci * TV audio. */ 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci /* Set Path1 to Analog Demod Main Channel */ 31462306a36Sopenharmony_ci cx18_av_write4(cx, 0x8d0, 0x1f063870); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci set_audclk_freq(cx, state->audclk_freq); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* deassert soft reset */ 32062306a36Sopenharmony_ci v = cx18_av_read(cx, 0x810) & ~0x01; 32162306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x810, v, v, 0x0f); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { 32462306a36Sopenharmony_ci /* When the microcontroller detects the 32562306a36Sopenharmony_ci * audio format, it will unmute the lines */ 32662306a36Sopenharmony_ci v = cx18_av_read(cx, 0x803) | 0x10; 32762306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x803, v, v, 0x1f); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void set_volume(struct cx18 *cx, int volume) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci /* First convert the volume to msp3400 values (0-127) */ 33462306a36Sopenharmony_ci int vol = volume >> 9; 33562306a36Sopenharmony_ci /* now scale it up to cx18_av values 33662306a36Sopenharmony_ci * -114dB to -96dB maps to 0 33762306a36Sopenharmony_ci * this should be 19, but in my testing that was 4dB too loud */ 33862306a36Sopenharmony_ci if (vol <= 23) 33962306a36Sopenharmony_ci vol = 0; 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci vol -= 23; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* PATH1_VOLUME */ 34462306a36Sopenharmony_ci cx18_av_write(cx, 0x8d4, 228 - (vol * 2)); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void set_bass(struct cx18 *cx, int bass) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci /* PATH1_EQ_BASS_VOL */ 35062306a36Sopenharmony_ci cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void set_treble(struct cx18 *cx, int treble) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci /* PATH1_EQ_TREBLE_VOL */ 35662306a36Sopenharmony_ci cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void set_balance(struct cx18 *cx, int balance) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci int bal = balance >> 8; 36262306a36Sopenharmony_ci if (bal > 0x80) { 36362306a36Sopenharmony_ci /* PATH1_BAL_LEFT */ 36462306a36Sopenharmony_ci cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80); 36562306a36Sopenharmony_ci /* PATH1_BAL_LEVEL */ 36662306a36Sopenharmony_ci cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f); 36762306a36Sopenharmony_ci } else { 36862306a36Sopenharmony_ci /* PATH1_BAL_LEFT */ 36962306a36Sopenharmony_ci cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00); 37062306a36Sopenharmony_ci /* PATH1_BAL_LEVEL */ 37162306a36Sopenharmony_ci cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void set_mute(struct cx18 *cx, int mute) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct cx18_av_state *state = &cx->av_state; 37862306a36Sopenharmony_ci u8 v; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { 38162306a36Sopenharmony_ci /* Must turn off microcontroller in order to mute sound. 38262306a36Sopenharmony_ci * Not sure if this is the best method, but it does work. 38362306a36Sopenharmony_ci * If the microcontroller is running, then it will undo any 38462306a36Sopenharmony_ci * changes to the mute register. */ 38562306a36Sopenharmony_ci v = cx18_av_read(cx, 0x803); 38662306a36Sopenharmony_ci if (mute) { 38762306a36Sopenharmony_ci /* disable microcontroller */ 38862306a36Sopenharmony_ci v &= ~0x10; 38962306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x803, v, v, 0x1f); 39062306a36Sopenharmony_ci cx18_av_write(cx, 0x8d3, 0x1f); 39162306a36Sopenharmony_ci } else { 39262306a36Sopenharmony_ci /* enable microcontroller */ 39362306a36Sopenharmony_ci v |= 0x10; 39462306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x803, v, v, 0x1f); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } else { 39762306a36Sopenharmony_ci /* SRC1_MUTE_EN */ 39862306a36Sopenharmony_ci cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciint cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 40562306a36Sopenharmony_ci struct cx18_av_state *state = &cx->av_state; 40662306a36Sopenharmony_ci int retval; 40762306a36Sopenharmony_ci u8 v; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { 41062306a36Sopenharmony_ci v = cx18_av_read(cx, 0x803) & ~0x10; 41162306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x803, v, v, 0x1f); 41262306a36Sopenharmony_ci cx18_av_write(cx, 0x8d3, 0x1f); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci v = cx18_av_read(cx, 0x810) | 0x1; 41562306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x810, v, v, 0x0f); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci retval = set_audclk_freq(cx, freq); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci v = cx18_av_read(cx, 0x810) & ~0x1; 42062306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x810, v, v, 0x0f); 42162306a36Sopenharmony_ci if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { 42262306a36Sopenharmony_ci v = cx18_av_read(cx, 0x803) | 0x10; 42362306a36Sopenharmony_ci cx18_av_write_expect(cx, 0x803, v, v, 0x1f); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci return retval; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 43162306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci switch (ctrl->id) { 43462306a36Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: 43562306a36Sopenharmony_ci set_volume(cx, ctrl->val); 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci case V4L2_CID_AUDIO_BASS: 43862306a36Sopenharmony_ci set_bass(cx, ctrl->val); 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci case V4L2_CID_AUDIO_TREBLE: 44162306a36Sopenharmony_ci set_treble(cx, ctrl->val); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case V4L2_CID_AUDIO_BALANCE: 44462306a36Sopenharmony_ci set_balance(cx, ctrl->val); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 44762306a36Sopenharmony_ci set_mute(cx, ctrl->val); 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci default: 45062306a36Sopenharmony_ci return -EINVAL; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciconst struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = { 45662306a36Sopenharmony_ci .s_ctrl = cx18_av_audio_s_ctrl, 45762306a36Sopenharmony_ci}; 458