Skip to content

Commit

Permalink
[media] tm6000: add radio support to the driver
Browse files Browse the repository at this point in the history
Changes:
  Add function tm6000_set_reg_mask for change some bits in regs. Very usefull, simplify some code with this function.
  Add control mute
  Add control volume
  Add control audio input MUX
  Add support radio

Radio works well. TV works too

Known bugs:
  The programm gnomeradio can't set freq for radio, it use old v4l API. Audio over USB works via arecord.
  The programm mplayer can set freq but no any audio
           mplayer -v -rawaudio rate=48000 radio://105.2/capture driver=v4l2:alsa:adevice=hw.1,0:amode=1:audiorate=48000:forceaudio:immediatemode=0
  When start watch TV very shortly after radio the kernel crashed hardly. Didn't stop all USB URBs, need some time for stop.

[mchehab@redhat.com: fix merge conflicts]

Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Dmitri Belimov authored and Mauro Carvalho Chehab committed Mar 21, 2011
1 parent 6ca0004 commit 8aff8ba
Show file tree
Hide file tree
Showing 6 changed files with 566 additions and 101 deletions.
13 changes: 4 additions & 9 deletions drivers/staging/tm6000/tm6000-alsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,11 @@ MODULE_PARM_DESC(debug, "enable debug messages");
static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
{
struct tm6000_core *core = chip->core;
int val;

dprintk(1, "Starting audio DMA\n");

/* Enables audio */
val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
val |= 0x20;
tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x40, 0x40);

tm6000_set_audio_bitrate(core, 48000);

Expand All @@ -98,13 +95,11 @@ static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
{
struct tm6000_core *core = chip->core;
int val;

dprintk(1, "Stopping audio DMA\n");

/* Enables audio */
val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
val &= ~0x20;
tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
/* Disables audio */
tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x00, 0x40);

tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0);

Expand Down
11 changes: 9 additions & 2 deletions drivers/staging/tm6000/tm6000-cards.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ struct tm6000_board {
char *name;

struct tm6000_capabilities caps;
enum tm6000_inaudio aradio;
enum tm6000_inaudio avideo;

enum tm6000_devtype type; /* variant of the chipset */
int tuner_type; /* type of the tuner */
Expand Down Expand Up @@ -230,6 +232,8 @@ struct tm6000_board tm6000_boards[] = {
.tuner_addr = 0xc2 >> 1,
.demod_addr = 0x1e >> 1,
.type = TM6010,
.avideo = TM6000_AIP_SIF1,
.aradio = TM6000_AIP_LINE1,
.caps = {
.has_tuner = 1,
.has_dvb = 1,
Expand All @@ -248,6 +252,8 @@ struct tm6000_board tm6000_boards[] = {
.tuner_type = TUNER_XC5000,
.tuner_addr = 0xc2 >> 1,
.type = TM6010,
.avideo = TM6000_AIP_SIF1,
.aradio = TM6000_AIP_LINE1,
.caps = {
.has_tuner = 1,
.has_dvb = 0,
Expand Down Expand Up @@ -693,13 +699,12 @@ static void tm6000_config_tuner(struct tm6000_core *dev)
struct xc5000_config ctl = {
.i2c_address = dev->tuner_addr,
.if_khz = 4570,
.radio_input = XC5000_RADIO_FM1,
.radio_input = XC5000_RADIO_FM1_MONO,
};

xc5000_cfg.tuner = TUNER_XC5000;
xc5000_cfg.priv = &ctl;


v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
&xc5000_cfg);
}
Expand Down Expand Up @@ -732,6 +737,8 @@ static int tm6000_init_dev(struct tm6000_core *dev)

dev->caps = tm6000_boards[dev->model].caps;

dev->avideo = tm6000_boards[dev->model].avideo;
dev->aradio = tm6000_boards[dev->model].aradio;
/* initialize hardware */
rc = tm6000_init(dev);
if (rc < 0)
Expand Down
260 changes: 224 additions & 36 deletions drivers/staging/tm6000/tm6000-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,29 @@ int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index)
}
EXPORT_SYMBOL_GPL(tm6000_get_reg);

int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
u16 index, u16 mask)
{
int rc;
u8 buf[1];
u8 new_index;

rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
value, index, buf, 1);

if (rc < 0)
return rc;

new_index = (buf[0] & ~mask) | (index & mask);

if (new_index == index)
return 0;

return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
req, value, new_index, NULL, 0);
}
EXPORT_SYMBOL_GPL(tm6000_set_reg_mask);

int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index)
{
int rc;
Expand Down Expand Up @@ -245,17 +268,12 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
struct v4l2_frequency f;

if (dev->dev_type == TM6010) {
int val;

/* Enable video */
val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
val |= 0x60;
tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
val = tm6000_get_reg(dev,
TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
val &= ~0x40;
tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);

tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF,
0x60, 0x60);
tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
0x00, 0x40);
tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);

} else {
Expand Down Expand Up @@ -471,6 +489,14 @@ struct reg_init tm6010_init_tab[] = {
{ TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 },
{ TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 },
{ TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 },
{ TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00},
{ TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80},
{ TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a},
{ TM6010_REQ08_R0D_A_AMD_THRES, 0x40},
{ TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64},
{ TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20},
{ TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe},
{ TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01},
{ TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc },

{ TM6010_REQ07_R3F_RESET, 0x01 },
Expand Down Expand Up @@ -591,51 +617,213 @@ int tm6000_init(struct tm6000_core *dev)

int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
{
int val;
int val = 0;
u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
u8 areg_0a = 0x91; /* SIF 48KHz */

switch (bitrate) {
case 48000:
areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
areg_0a = 0x91; /* SIF 48KHz */
dev->audio_bitrate = bitrate;
break;
case 32000:
areg_f0 = 0x00; /* ADC MCLK = 375 Fs */
areg_0a = 0x90; /* SIF 32KHz */
dev->audio_bitrate = bitrate;
break;
default:
return -EINVAL;
}


/* enable I2S, if we use sif or external I2S device */
if (dev->dev_type == TM6010) {
val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0);
if (val < 0)
return val;
val = (val & 0xf0) | 0x1; /* 48 kHz, not muted */
val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, val);
val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a);
if (val < 0)
return val;
}

/* different reg's to set audio bitrate */
if (dev->dev_type == TM6010) {
val = tm6000_get_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
0x0);
val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
areg_f0, 0xf0);
if (val < 0)
return val;
} else {
val = tm6000_get_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x0);
val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
areg_f0, 0xf0);
if (val < 0)
return val;
}
return 0;
}
EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);

val &= 0x0f; /* Preserve the audio input control bits */
switch (bitrate) {
case 44100:
val |= 0xd0;
dev->audio_bitrate = bitrate;
int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp)
{
if (dev->dev_type == TM6010) {
/* Audio crossbar setting, default SIF1 */
u8 areg_f0 = 0x03;

switch (ainp) {
case TM6000_AIP_SIF1:
case TM6000_AIP_SIF2:
areg_f0 = 0x03;
break;
case TM6000_AIP_LINE1:
areg_f0 = 0x00;
break;
case TM6000_AIP_LINE2:
areg_f0 = 0x08;
break;
default:
return 0;
break;
}
/* Set audio input crossbar */
tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
areg_f0, 0x0f);
} else {
/* Audio setting, default LINE1 */
u8 areg_eb = 0x00;

switch (ainp) {
case TM6000_AIP_LINE1:
areg_eb = 0x00;
break;
case TM6000_AIP_LINE2:
areg_eb = 0x04;
break;
default:
return 0;
break;
}
/* Set audio input */
tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
areg_eb, 0x0f);
}
return 0;
}
EXPORT_SYMBOL_GPL(tm6000_set_audio_input);

void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
{
u8 mute_reg = 0;

if (mute)
mute_reg = 0x08;

tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08);
}

void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
{
u8 mute_reg = 0;

if (mute)
mute_reg = 0x20;

if (dev->dev_type == TM6010) {
tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL,
mute_reg, 0x20);
tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL,
mute_reg, 0x20);
} else {
tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL,
mute_reg, 0x20);
tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL,
mute_reg, 0x20);
}
}

int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute)
{
enum tm6000_inaudio ainp;

if (dev->radio)
ainp = dev->aradio;
else
ainp = dev->avideo;

switch (ainp) {
case TM6000_AIP_SIF1:
case TM6000_AIP_SIF2:
if (dev->dev_type == TM6010)
tm6010_set_mute_sif(dev, mute);
else {
printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
" SIF audio inputs. Please check the %s"
" configuration.\n", dev->name);
return -EINVAL;
}
break;
case 48000:
val |= 0x60;
dev->audio_bitrate = bitrate;
case TM6000_AIP_LINE1:
case TM6000_AIP_LINE2:
tm6010_set_mute_adc(dev, mute);
break;
default:
return -EINVAL;
break;
}
if (dev->dev_type == TM6010)
val = tm6000_set_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
val);
else
val = tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, val);
return 0;
}
EXPORT_SYMBOL_GPL(tm6000_tvaudio_set_mute);

void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
{
u8 vol_reg;

return val;
vol_reg = vol & 0x0F;

if (vol < 0)
vol_reg |= 0x40;

tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg);
tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg);
}
EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);

void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
{
u8 vol_reg;

vol_reg = (vol + 0x10) & 0x1f;

if (dev->dev_type == TM6010) {
tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg);
tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg);
} else {
tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg);
tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg);
}
}

void tm6000_set_volume(struct tm6000_core *dev, int vol)
{
enum tm6000_inaudio ainp;

if (dev->radio) {
ainp = dev->aradio;
vol += 8; /* Offset to 0 dB */
} else
ainp = dev->avideo;

switch (ainp) {
case TM6000_AIP_SIF1:
case TM6000_AIP_SIF2:
if (dev->dev_type == TM6010)
tm6010_set_volume_sif(dev, vol);
else
printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
" SIF audio inputs. Please check the %s"
" configuration.\n", dev->name);
break;
case TM6000_AIP_LINE1:
case TM6000_AIP_LINE2:
tm6010_set_volume_adc(dev, vol);
break;
default:
break;
}
}
EXPORT_SYMBOL_GPL(tm6000_set_volume);

static LIST_HEAD(tm6000_devlist);
static DEFINE_MUTEX(tm6000_devlist_mutex);
Expand Down
Loading

0 comments on commit 8aff8ba

Please sign in to comment.