2005-11-13 16:07:56 -08:00
/* cx25840 audio functions
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include <linux/videodev2.h>
# include <linux/i2c.h>
# include <media/v4l2-common.h>
2006-03-25 10:26:09 -03:00
# include <media/cx25840.h>
2005-11-13 16:07:56 -08:00
2006-03-25 10:26:09 -03:00
# include "cx25840-core.h"
2005-11-13 16:07:56 -08:00
2006-01-09 15:25:41 -02:00
static int set_audclk_freq ( struct i2c_client * client , u32 freq )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2005-11-13 16:07:56 -08:00
2006-01-09 15:25:41 -02:00
if ( freq ! = 32000 & & freq ! = 44100 & & freq ! = 48000 )
return - EINVAL ;
2005-11-13 16:07:56 -08:00
/* common for all inputs and rates */
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx23885 & & ! state - > is_cx231xx )
2008-01-10 01:22:39 -03:00
cx25840_write ( client , 0x127 , 0x50 ) ;
2005-11-13 16:07:56 -08:00
2006-01-09 15:25:42 -02:00
if ( state - > aud_input ! = CX25840_AUDIO_SERIAL ) {
2005-11-13 16:07:56 -08:00
switch ( freq ) {
2006-01-09 15:25:41 -02:00
case 32000 :
2008-01-10 01:22:39 -03:00
if ( state - > is_cx23885 ) {
/* We don't have register values
* so avoid destroying registers . */
break ;
}
2005-11-13 16:07:56 -08:00
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx231xx ) {
2009-03-03 14:36:55 -03:00
/* VID_PLL and AUX_PLL */
cx25840_write4 ( client , 0x108 , 0x1006040f ) ;
2009-03-03 06:07:42 -03:00
2009-03-03 14:36:55 -03:00
/* AUX_PLL_FRAC */
cx25840_write4 ( client , 0x110 , 0x01bb39ee ) ;
2009-03-03 06:07:42 -03:00
}
2005-11-13 16:07:56 -08:00
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
break ;
2005-11-13 16:07:56 -08:00
/* src3/4/6_ctl = 0x0801f77f */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x900 , 0x0801f77f ) ;
cx25840_write4 ( client , 0x904 , 0x0801f77f ) ;
cx25840_write4 ( client , 0x90c , 0x0801f77f ) ;
2005-11-13 16:07:56 -08:00
break ;
2006-01-09 15:25:41 -02:00
case 44100 :
2008-01-10 01:22:39 -03:00
if ( state - > is_cx23885 ) {
/* We don't have register values
* so avoid destroying registers . */
break ;
}
2005-11-13 16:07:56 -08:00
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx231xx ) {
/* VID_PLL and AUX_PLL */
cx25840_write4 ( client , 0x108 , 0x1009040f ) ;
/* AUX_PLL_FRAC */
cx25840_write4 ( client , 0x110 , 0x00ec6bd6 ) ;
}
2005-11-13 16:07:56 -08:00
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
break ;
2005-11-13 16:07:56 -08:00
/* src3/4/6_ctl = 0x08016d59 */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x900 , 0x08016d59 ) ;
cx25840_write4 ( client , 0x904 , 0x08016d59 ) ;
cx25840_write4 ( client , 0x90c , 0x08016d59 ) ;
2005-11-13 16:07:56 -08:00
break ;
2006-01-09 15:25:41 -02:00
case 48000 :
2008-01-10 01:22:39 -03:00
if ( state - > is_cx23885 ) {
/* We don't have register values
* so avoid destroying registers . */
break ;
}
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx231xx ) {
2009-03-03 14:36:55 -03:00
/* VID_PLL and AUX_PLL */
cx25840_write4 ( client , 0x108 , 0x100a040f ) ;
2009-03-03 06:07:42 -03:00
2009-03-03 14:36:55 -03:00
/* AUX_PLL_FRAC */
cx25840_write4 ( client , 0x110 , 0x0098d6e5 ) ;
2009-03-03 06:07:42 -03:00
}
2005-11-13 16:07:56 -08:00
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
break ;
2005-11-13 16:07:56 -08:00
/* src3/4/6_ctl = 0x08014faa */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x900 , 0x08014faa ) ;
cx25840_write4 ( client , 0x904 , 0x08014faa ) ;
cx25840_write4 ( client , 0x90c , 0x08014faa ) ;
2005-11-13 16:07:56 -08:00
break ;
}
2006-01-09 15:25:42 -02:00
} else {
2005-11-13 16:07:56 -08:00
switch ( freq ) {
2006-01-09 15:25:41 -02:00
case 32000 :
2008-01-10 01:22:39 -03:00
if ( state - > is_cx23885 ) {
/* We don't have register values
* so avoid destroying registers . */
break ;
}
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx231xx ) {
2009-03-03 14:36:55 -03:00
/* VID_PLL and AUX_PLL */
cx25840_write4 ( client , 0x108 , 0x1e08040f ) ;
2009-03-03 06:07:42 -03:00
2009-03-03 14:36:55 -03:00
/* AUX_PLL_FRAC */
cx25840_write4 ( client , 0x110 , 0x012a0869 ) ;
2009-03-03 06:07:42 -03:00
}
2005-11-13 16:07:56 -08:00
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
break ;
2005-11-13 16:07:56 -08:00
/* src1_ctl = 0x08010000 */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x8f8 , 0x08010000 ) ;
2005-11-13 16:07:56 -08:00
/* src3/4/6_ctl = 0x08020000 */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x900 , 0x08020000 ) ;
cx25840_write4 ( client , 0x904 , 0x08020000 ) ;
cx25840_write4 ( client , 0x90c , 0x08020000 ) ;
2005-11-13 16:07:56 -08:00
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
cx25840_write ( client , 0x127 , 0x54 ) ;
break ;
2006-01-09 15:25:41 -02:00
case 44100 :
2008-01-10 01:22:39 -03:00
if ( state - > is_cx23885 ) {
/* We don't have register values
* so avoid destroying registers . */
break ;
}
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx231xx ) {
2009-03-03 14:36:55 -03:00
/* VID_PLL and AUX_PLL */
cx25840_write4 ( client , 0x108 , 0x1809040f ) ;
2009-03-03 06:07:42 -03:00
2009-03-03 14:36:55 -03:00
/* AUX_PLL_FRAC */
cx25840_write4 ( client , 0x110 , 0x00ec6bd6 ) ;
2009-03-03 06:07:42 -03:00
}
2005-11-13 16:07:56 -08:00
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
break ;
2005-11-13 16:07:56 -08:00
/* src1_ctl = 0x08010000 */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x8f8 , 0x080160cd ) ;
2005-11-13 16:07:56 -08:00
/* src3/4/6_ctl = 0x08020000 */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x900 , 0x08017385 ) ;
cx25840_write4 ( client , 0x904 , 0x08017385 ) ;
cx25840_write4 ( client , 0x90c , 0x08017385 ) ;
2005-11-13 16:07:56 -08:00
break ;
2006-01-09 15:25:41 -02:00
case 48000 :
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx23885 & & ! state - > is_cx231xx ) {
2008-01-10 01:22:39 -03:00
/* VID_PLL and AUX_PLL */
cx25840_write4 ( client , 0x108 , 0x180a040f ) ;
2005-11-13 16:07:56 -08:00
2008-01-10 01:22:39 -03:00
/* AUX_PLL_FRAC */
cx25840_write4 ( client , 0x110 , 0x0098d6e5 ) ;
}
2005-11-13 16:07:56 -08:00
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
break ;
2009-03-03 06:07:42 -03:00
if ( ! state - > is_cx23885 & & ! state - > is_cx231xx ) {
2008-01-10 01:22:39 -03:00
/* src1_ctl */
cx25840_write4 ( client , 0x8f8 , 0x08018000 ) ;
2005-11-13 16:07:56 -08:00
2008-01-10 01:22:39 -03:00
/* src3/4/6_ctl */
cx25840_write4 ( client , 0x900 , 0x08015555 ) ;
cx25840_write4 ( client , 0x904 , 0x08015555 ) ;
cx25840_write4 ( client , 0x90c , 0x08015555 ) ;
} else {
cx25840_write4 ( client , 0x8f8 , 0x0801867c ) ;
cx25840_write4 ( client , 0x900 , 0x08014faa ) ;
cx25840_write4 ( client , 0x904 , 0x08014faa ) ;
cx25840_write4 ( client , 0x90c , 0x08014faa ) ;
}
2005-11-13 16:07:56 -08:00
break ;
}
}
state - > audclk_freq = freq ;
return 0 ;
}
2006-01-09 15:25:42 -02:00
void cx25840_audio_set_path ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2005-11-13 16:07:56 -08:00
2007-08-07 07:16:07 -03:00
/* assert soft reset */
cx25840_and_or ( client , 0x810 , ~ 0x1 , 0x01 ) ;
2005-11-13 16:07:56 -08:00
/* stop microcontroller */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0 ) ;
/* Mute everything to prevent the PFFT! */
cx25840_write ( client , 0x8d3 , 0x1f ) ;
2006-01-09 15:25:42 -02:00
if ( state - > aud_input = = CX25840_AUDIO_SERIAL ) {
2005-11-13 16:07:56 -08:00
/* Set Path1 to Serial Audio Input */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x8d0 , 0x01011012 ) ;
2005-11-13 16:07:56 -08:00
/* The microcontroller should not be started for the
* non - tuner inputs : autodetection is specific for
* TV audio . */
2006-01-09 15:25:42 -02:00
} else {
/* Set Path1 to Analog Demod Main Channel */
2007-12-02 07:03:45 -03:00
cx25840_write4 ( client , 0x8d0 , 0x1f063870 ) ;
2006-04-29 12:11:18 -03:00
}
set_audclk_freq ( client , state - > audclk_freq ) ;
2005-11-13 16:07:56 -08:00
2006-04-29 12:11:18 -03:00
if ( state - > aud_input ! = CX25840_AUDIO_SERIAL ) {
2006-01-09 15:25:42 -02:00
/* When the microcontroller detects the
* audio format , it will unmute the lines */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x10 ) ;
2005-11-13 16:07:56 -08:00
}
2007-08-07 07:16:07 -03:00
/* deassert soft reset */
cx25840_and_or ( client , 0x810 , ~ 0x1 , 0x00 ) ;
2008-01-10 01:22:39 -03:00
2009-03-03 14:36:55 -03:00
/* Ensure the controller is running when we exit */
if ( state - > is_cx23885 | | state - > is_cx231xx )
2008-01-10 01:22:39 -03:00
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x10 ) ;
2005-11-13 16:07:56 -08:00
}
2006-01-09 15:32:41 -02:00
static int get_volume ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2007-08-05 08:00:36 -03:00
int vol ;
if ( state - > unmute_volume > = 0 )
return state - > unmute_volume ;
2005-11-13 16:07:56 -08:00
/* Volume runs +18dB to -96dB in 1/2dB steps
* change to fit the msp3400 - 114 dB to + 12 dB range */
/* check PATH1_VOLUME */
2007-08-05 08:00:36 -03:00
vol = 228 - cx25840_read ( client , 0x8d4 ) ;
2005-11-13 16:07:56 -08:00
vol = ( vol / 2 ) + 23 ;
return vol < < 9 ;
}
2006-01-09 15:32:41 -02:00
static void set_volume ( struct i2c_client * client , int volume )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2007-08-05 08:00:36 -03:00
int vol ;
if ( state - > unmute_volume > = 0 ) {
state - > unmute_volume = volume ;
return ;
}
/* Convert the volume to msp3400 values (0-127) */
vol = volume > > 9 ;
2005-11-13 16:07:56 -08:00
/* now scale it up to cx25840 values
* - 114 dB to - 96 dB maps to 0
* this should be 19 , but in my testing that was 4 dB too loud */
if ( vol < = 23 ) {
vol = 0 ;
} else {
vol - = 23 ;
}
/* PATH1_VOLUME */
cx25840_write ( client , 0x8d4 , 228 - ( vol * 2 ) ) ;
}
2006-01-09 15:32:41 -02:00
static int get_bass ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
/* bass is 49 steps +12dB to -12dB */
/* check PATH1_EQ_BASS_VOL */
int bass = cx25840_read ( client , 0x8d9 ) & 0x3f ;
bass = ( ( ( 48 - bass ) * 0xffff ) + 47 ) / 48 ;
return bass ;
}
2006-01-09 15:32:41 -02:00
static void set_bass ( struct i2c_client * client , int bass )
2005-11-13 16:07:56 -08:00
{
/* PATH1_EQ_BASS_VOL */
cx25840_and_or ( client , 0x8d9 , ~ 0x3f , 48 - ( bass * 48 / 0xffff ) ) ;
}
2006-01-09 15:32:41 -02:00
static int get_treble ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
/* treble is 49 steps +12dB to -12dB */
/* check PATH1_EQ_TREBLE_VOL */
int treble = cx25840_read ( client , 0x8db ) & 0x3f ;
treble = ( ( ( 48 - treble ) * 0xffff ) + 47 ) / 48 ;
return treble ;
}
2006-01-09 15:32:41 -02:00
static void set_treble ( struct i2c_client * client , int treble )
2005-11-13 16:07:56 -08:00
{
/* PATH1_EQ_TREBLE_VOL */
cx25840_and_or ( client , 0x8db , ~ 0x3f , 48 - ( treble * 48 / 0xffff ) ) ;
}
2006-01-09 15:32:41 -02:00
static int get_balance ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
/* balance is 7 bit, 0 to -96dB */
/* check PATH1_BAL_LEVEL */
int balance = cx25840_read ( client , 0x8d5 ) & 0x7f ;
/* check PATH1_BAL_LEFT */
if ( ( cx25840_read ( client , 0x8d5 ) & 0x80 ) = = 0 )
balance = 0x80 - balance ;
else
balance = 0x80 + balance ;
return balance < < 8 ;
}
2006-01-09 15:32:41 -02:00
static void set_balance ( struct i2c_client * client , int balance )
2005-11-13 16:07:56 -08:00
{
int bal = balance > > 8 ;
if ( bal > 0x80 ) {
/* PATH1_BAL_LEFT */
cx25840_and_or ( client , 0x8d5 , 0x7f , 0x80 ) ;
/* PATH1_BAL_LEVEL */
cx25840_and_or ( client , 0x8d5 , ~ 0x7f , bal & 0x7f ) ;
} else {
/* PATH1_BAL_LEFT */
cx25840_and_or ( client , 0x8d5 , 0x7f , 0x00 ) ;
/* PATH1_BAL_LEVEL */
cx25840_and_or ( client , 0x8d5 , ~ 0x7f , 0x80 - bal ) ;
}
}
2006-01-09 15:32:41 -02:00
static int get_mute ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2007-08-05 08:00:36 -03:00
return state - > unmute_volume > = 0 ;
2005-11-13 16:07:56 -08:00
}
2006-01-09 15:32:41 -02:00
static void set_mute ( struct i2c_client * client , int mute )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2005-11-13 16:07:56 -08:00
2007-08-05 08:00:36 -03:00
if ( mute & & state - > unmute_volume = = - 1 ) {
int vol = get_volume ( client ) ;
set_volume ( client , 0 ) ;
state - > unmute_volume = vol ;
}
else if ( ! mute & & state - > unmute_volume ! = - 1 ) {
int vol = state - > unmute_volume ;
state - > unmute_volume = - 1 ;
set_volume ( client , vol ) ;
2005-11-13 16:07:56 -08:00
}
}
2009-03-30 06:26:40 -03:00
int cx25840_s_clock_freq ( struct v4l2_subdev * sd , u32 freq )
2005-11-13 16:07:56 -08:00
{
2009-03-30 06:26:40 -03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct cx25840_state * state = to_state ( sd ) ;
2006-04-29 12:11:18 -03:00
int retval ;
2005-11-13 16:07:56 -08:00
2009-03-30 06:26:40 -03:00
if ( ! state - > is_cx25836 )
cx25840_and_or ( client , 0x810 , ~ 0x1 , 1 ) ;
if ( state - > aud_input ! = CX25840_AUDIO_SERIAL ) {
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0 ) ;
cx25840_write ( client , 0x8d3 , 0x1f ) ;
}
retval = set_audclk_freq ( client , freq ) ;
if ( state - > aud_input ! = CX25840_AUDIO_SERIAL )
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x10 ) ;
if ( ! state - > is_cx25836 )
cx25840_and_or ( client , 0x810 , ~ 0x1 , 0 ) ;
return retval ;
}
2006-01-09 15:25:42 -02:00
2009-03-30 06:26:40 -03:00
int cx25840_audio_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 15:25:42 -02:00
2009-03-30 06:26:40 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_VOLUME :
ctrl - > value = get_volume ( client ) ;
break ;
case V4L2_CID_AUDIO_BASS :
ctrl - > value = get_bass ( client ) ;
break ;
case V4L2_CID_AUDIO_TREBLE :
ctrl - > value = get_treble ( client ) ;
break ;
case V4L2_CID_AUDIO_BALANCE :
ctrl - > value = get_balance ( client ) ;
break ;
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = get_mute ( client ) ;
break ;
2005-11-13 16:07:56 -08:00
default :
return - EINVAL ;
}
2009-03-30 06:26:40 -03:00
return 0 ;
}
2005-11-13 16:07:56 -08:00
2009-03-30 06:26:40 -03:00
int cx25840_audio_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_VOLUME :
set_volume ( client , ctrl - > value ) ;
break ;
case V4L2_CID_AUDIO_BASS :
set_bass ( client , ctrl - > value ) ;
break ;
case V4L2_CID_AUDIO_TREBLE :
set_treble ( client , ctrl - > value ) ;
break ;
case V4L2_CID_AUDIO_BALANCE :
set_balance ( client , ctrl - > value ) ;
break ;
case V4L2_CID_AUDIO_MUTE :
set_mute ( client , ctrl - > value ) ;
break ;
default :
return - EINVAL ;
}
2005-11-13 16:07:56 -08:00
return 0 ;
}