| /* |
| * tlv320dac31.c ---- DAC3100 ALSA Soc Audio Driver |
| * |
| * History: |
| * 2012/05/17 - [ Johnson Diao ] created file |
| * |
| * Copyright (C) 2004-2012, Ambarella, Inc. |
| * |
| * All rights reserved. No Part of this file may be reproduced, stored |
| * in a retrieval system, or transmitted, in any form, or by any means, |
| * electronic, mechanical, photocopying, recording, or otherwise, |
| * without the prior consent of Ambarella, Inc. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/io.h> |
| #include <linux/init.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/pm.h> |
| #include <linux/i2c.h> |
| #include <linux/platform_device.h> |
| #include <sound/core.h> |
| #include <sound/pcm.h> |
| #include <sound/pcm_params.h> |
| #include <sound/soc.h> |
| #include <sound/soc-dapm.h> |
| #include <sound/initval.h> |
| #include <sound/tlv.h> |
| #include <linux/gpio.h> |
| #include <sound/tlv320dac31.h> |
| #include <linux/device.h> |
| |
| #include "tlv320dac31.h" |
| |
| #define DAC31_VERSION "0.1" |
| |
| #define debug_dac (0) |
| #if debug_dac |
| #define PRINTK(arg...) printk(arg) |
| #else |
| #define PRINTK(arg...) |
| #endif |
| |
| /* havn't test the headphone and mic ,so DO NOT DEFINE IT !!!! */ |
| #define HAEDPHONE_JACK 0 |
| |
| /* whenever aplay/arecord is run, dac3100_hw_params() function gets called. |
| * This function reprograms the clock dividers etc. this flag can be used to |
| * disable this when the clock dividers are programmed by pps config file |
| */ |
| |
| static int PLL_ENABLE=0; |
| |
| |
| /* |
| ***************************************************************************** |
| * Function Definitions |
| ***************************************************************************** |
| */ |
| static int dac31_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); |
| /* |
| *---------------------------------------------------------------------------- |
| * Function : snd_soc_get_volsw_2r_dac3100 |
| * Purpose : Callback to get the value of a double mixer control that spans |
| * two registers. |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| int snd_soc_get_volsw_dac3100(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; |
| struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| int reg = mc->reg; |
| int mask; |
| int shift; |
| unsigned short val; |
| |
| PRINTK("snd_soc_get_volsw_dac3100 %s\n", kcontrol->id.name); |
| |
| /* Check the id name of the kcontrol and configure the mask and shift */ |
| if (!strcmp(kcontrol->id.name,"Mic Register Volume")) { |
| mask = 0x7f; |
| shift =0; |
| } else { |
| printk(KERN_ALERT "Invalid kcontrol name\n"); |
| return -1; |
| } |
| |
| val = (snd_soc_read(codec, reg) >> shift) & mask; |
| |
| if (!strcmp(kcontrol->id.name,"Mic Register Volume")) { |
| ucontrol->value.integer.value[0] = (126-val); |
| } |
| return 0; |
| } |
| |
| |
| /* |
| *---------------------------------------------------------------------------- |
| * Function : snd_soc_put_volsw_2r_dac3100 |
| * Purpose : Callback to set the value of a double mixer control that spans |
| * two registers. |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| int snd_soc_put_volsw_dac3100(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| unsigned int reg = mc->reg; |
| unsigned int shift = mc->shift; |
| int err; |
| unsigned short val, val_mask; |
| |
| PRINTK("snd_soc_put_volsw_dac3100 (%s)\n", kcontrol->id.name); |
| |
| val = (ucontrol->value.integer.value[0] << shift); |
| |
| PRINTK("\nval= 0x%x \n",val); |
| |
| if (!strcmp(kcontrol->id.name,"Mic Register Volume")) { |
| val = 126-val; |
| val_mask = 0x7f; |
| }else { |
| printk(KERN_ALERT "Invalid control name\n"); |
| return -1; |
| } |
| |
| if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0) { |
| printk(KERN_ALERT "Error while updating bits\n"); |
| return err; |
| } |
| |
| return err; |
| } |
| |
| |
| /* |
| *---------------------------------------------------------------------------- |
| * Function : snd_soc_get_volsw_2r_dac3100 |
| * Purpose : Callback to get the value of a double mixer control that spans |
| * two registers. |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| int snd_soc_get_volsw_2r_dac3100(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| int reg = mc->reg; |
| int reg2 = mc->rreg; |
| int mask; |
| int shift; |
| unsigned short val, val2; |
| |
| PRINTK("snd_soc_get_volsw_2r_dac3100 %s\n", kcontrol->id.name); |
| |
| /* Check the id name of the kcontrol and configure the mask and shift */ |
| if (!strcmp(kcontrol->id.name, "DAC Playback Volume")) { |
| mask = dac3100_8BITS_MASK; |
| shift = 0; |
| } else if (!strcmp(kcontrol->id.name, "HP Driver Gain")) { |
| mask = 0xF; |
| shift = 3; |
| } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) { |
| mask = 0x7F; |
| shift = 0; |
| } else { |
| printk(KERN_ALERT "Invalid kcontrol name\n"); |
| return -1; |
| } |
| |
| val = (snd_soc_read(codec, reg) >> shift) & mask; |
| val2 = (snd_soc_read(codec, reg2) >> shift) & mask; |
| |
| if (!strcmp(kcontrol->id.name, "DAC Playback Volume")) { |
| ucontrol->value.integer.value[0] = |
| (val <= 48) ? (val + 127) : (val - 129); |
| ucontrol->value.integer.value[1] = |
| (val2 <= 48) ? (val2 + 127) : (val2 - 129); |
| }else if (!strcmp(kcontrol->id.name, "HP Driver Gain")){ |
| ucontrol->value.integer.value[0] = val; |
| ucontrol->value.integer.value[1] = val2; |
| } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) { |
| ucontrol->value.integer.value[0] = |
| ((val*2) <= 40) ? ((val*2 + 24)/2) : ((val*2 - 254)/2); |
| ucontrol->value.integer.value[1] = |
| ((val2*2) <= 40) ? ((val2*2 + 24)/2) : ((val2*2 - 254)/2); |
| } |
| return 0; |
| } |
| |
| /* |
| *---------------------------------------------------------------------------- |
| * Function : snd_soc_put_volsw_2r_dac3100 |
| * Purpose : Callback to set the value of a double mixer control that spans |
| * two registers. |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| int snd_soc_put_volsw_2r_dac3100(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| unsigned int reg = mc->reg; |
| unsigned int reg2 = mc->rreg; |
| unsigned int shift = mc->shift; |
| int err; |
| unsigned short val, val2, val_mask; |
| |
| PRINTK("snd_soc_put_volsw_2r_dac3100 (%s)\n", kcontrol->id.name); |
| |
| val = (ucontrol->value.integer.value[0] << shift); |
| val2 = (ucontrol->value.integer.value[1] << shift); |
| |
| PRINTK("\nval= 0x%x val2 =0x%x\n",val,val2); |
| |
| if (!strcmp(kcontrol->id.name, "DAC Playback Volume")) { |
| val = (val >= 127) ? (val - 127):(val+129); |
| val2 = (val2 >= 127) ? (val2 - 127) : (val2 + 129); |
| val_mask = dac3100_8BITS_MASK; /* 8 bits */ |
| } else if (!strcmp(kcontrol->id.name, "HP Driver Gain")) { |
| val_mask = (0xF << shift); /* 4 bits */ |
| } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) { |
| val = (val*2 >= 24) ? ((val*2 - 24)/2) : ((val*2 + 254)/2); |
| val2 = (val2*2 >= 24) ? ((val2*2 - 24)/2) : ((val2*2 + 254)/2); |
| val_mask = 0x7F; /* 7 bits */ |
| } else { |
| printk(KERN_ALERT "Invalid control name\n"); |
| return -1; |
| } |
| PRINTK("VAL=%x MASK=%x\n",val,val_mask); |
| |
| if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0) { |
| printk(KERN_ALERT "Error while updating bits\n"); |
| return err; |
| } |
| |
| err = snd_soc_update_bits(codec, reg2, val_mask, val2); |
| return err; |
| } |
| |
| struct dac31_priv { |
| unsigned int sysclk; |
| void *control_data; |
| }; |
| |
| static const int dac31_reg_enable[] = { |
| RESET, |
| AIS_REG_3, |
| CLK_REG_1, |
| CLK_REG_2, |
| NDAC_CLK_REG_6, |
| MDAC_CLK_REG_7, |
| DAC_OSR_MSB, |
| DAC_OSR_LSB, |
| INTERFACE_SET_REG_1, |
| DAC_INSTRUCTION_SET, |
| DAC31_COERAM, |
| DAC_MIXER_ROUTING, |
| SPL_DRIVER, |
| LEFT_ANALOG_SPL, |
| CLASS_D_SPK, |
| LDAC_VOL, |
| RDAC_VOL, |
| DAC_MUTE_CTRL_REG, |
| DAC_CHN_REG, |
| DAC31_REG_END, |
| }; |
| |
| static const u8 dac31_reg[DAC31_CACHEREGNUM] = { |
| 0x00,0x01,0x01,0x66, /* 0 */ |
| 0x00,0x00,0x04,0x00, /* 4 */ |
| 0x00,0x00,0x00,0x81, /* 8 */ |
| 0x82,0x00,0x80,0x80, /* 12 */ |
| 0x08,0x00,0x01,0x01, /* 16 */ |
| 0x80,0x80,0x04,0x00, /* 20 */ |
| 0x00,0x00,0x01,0x00, /* 24 */ |
| 0x00,0x09,0x01,0x00, /* 28 */ |
| 0x00,0x00,0x00,0x00, /* 32 */ |
| 0x80,0x00,0x00,0x00, /* 36 */ |
| 0x00,0x00,0x00,0x00, /* 40 */ |
| 0x00,0x00,0x00,0x00, /* 44 */ |
| 0x00,0x00,0x00,0x02, /* 48 */ |
| 0x32,0x12,0x03,0x02, /* 52 */ |
| 0x02,0x11,0x10,0x00, /* 56 */ |
| 0x17,0x04,0x00,0x14, /* 60 */ |
| 0x00,0xd8,0xd8,0x00, /* 64 */ |
| 0x6f,0x38,0x00,0x00, /* 68 */ |
| 0x00,0x00,0x00,0xee, /* 72 */ |
| 0x10,0xd8,0x7e,0xe3, /* 76 */ |
| 0x00,0x00,0x80,0x00, /* 80 */ |
| 0x00,0x00,0x00,0x00, /* 84 */ |
| 0x7f,0x00,0x00,0x00, /* 88 */ |
| 0x00,0x00,0x00,0x00, /* 92 */ |
| 0x00,0x00,0x00,0x00, /* 96 */ |
| 0x00,0x00,0x00,0x00, /* 100 */ |
| 0x00,0x00,0x00,0x00, /* 104 */ |
| 0x00,0x00,0x00,0x00, /* 108 */ |
| 0x00,0x00,0x00,0x00, /* 112 */ |
| 0x00,0x00,0x00,0x00, /* 116 */ |
| 0x00,0x00,0x00,0x00, /* 120 */ |
| 0x00,0x00,0x00,0x00, /* 124 - PAGE0 Registers(127) ends here */ |
| 0x01,0x00,0x00,0x00, /* 128, PAGE1-0 */ |
| 0x00,0x00,0x00,0x00, /* 132, PAGE1-4 */ |
| 0x00,0x00,0x00,0x00, /* 136, PAGE1-8 */ |
| 0x00,0x00,0x00,0x00, /* 140, PAGE1-12 */ |
| 0x00,0x00,0x00,0x00, /* 144, PAGE1-16 */ |
| 0x00,0x00,0x00,0x00, /* 148, PAGE1-20 */ |
| 0x00,0x00,0x00,0x00, /* 152, PAGE1-24 */ |
| 0x00,0x00,0x00,0x04, /* 156, PAGE1-28 */ |
| 0x06,0x3e,0x00,0x44, /* 160, PAGE1-32 */ |
| 0x7f,0x7f,0x92,0x7f, /* 164, PAGE1-36 */ |
| 0x02,0x02,0x1c,0x00, /* 168, PAGE1-40 */ |
| 0x20,0x86,0x00,0x80, /* 172, PAGE1-44 */ |
| 0x00, 0x00, 0x00, /* 176, PAGE1-48 */ |
| /* page 3-16 , 179 need Individual treatment*/ |
| 0x00, |
| /* page 8-1 ,180 need Individual treatment */ |
| 0x04, |
| }; |
| |
| #define PAGE3_REG16 179 |
| #define PAGE8_REG1 180 |
| |
| static inline unsigned int dac31_read_reg_cache(struct snd_soc_codec *codec,unsigned int reg) |
| { |
| u8 *cache = codec->reg_cache; |
| unsigned int ret; |
| if (TIMER_MCLK_DIV == reg){ |
| ret = cache[PAGE3_REG16]; |
| }else if (DAC31_COERAM == reg){ |
| ret = cache[PAGE8_REG1]; |
| }else if (reg >= DAC31_CACHEREGNUM){ |
| ret = 0; |
| }else{ |
| ret = cache[reg]; |
| } |
| return ret; |
| } |
| |
| static int dac31_change_reg_page(struct snd_soc_codec *codec, u8 page_no) |
| { |
| struct i2c_client *client = codec->control_data; |
| int count = 3; |
| if (i2c_smbus_read_byte_data(client, PAGE_SELECT) == page_no){ |
| PRINTK("already in page %d\n",page_no); |
| return 0; |
| } |
| while (count > 0) { |
| i2c_smbus_write_byte_data(client, PAGE_SELECT, page_no); /* switch register page, Page0 */ |
| if (i2c_smbus_read_byte_data(client, PAGE_SELECT) == page_no) { |
| break; |
| } else { |
| printk(KERN_ALERT " DAC: try to select page %d but failed ,try again \n",page_no); |
| } |
| count --; |
| } |
| if (count == 0) { |
| printk(KERN_ALERT "DAC : failed to change dac31 reg page to %d \n",page_no); |
| return -1; |
| } else { |
| return 0; |
| } |
| } |
| |
| static int dac31_write(struct snd_soc_codec *codec, unsigned int reg,unsigned int value) |
| { |
| u8 *cache = codec->reg_cache; |
| struct i2c_client *client = codec->control_data; |
| u8 page; |
| u8 rreg; |
| page = reg / 128; |
| rreg = reg % 128; |
| PRINTK("PAGE =%x\n",page); |
| if (dac31_change_reg_page(codec,page) ){ |
| printk(KERN_ALERT "ERROR IN dac31_write\n"); |
| return -EIO; |
| } |
| PRINTK("WRITE %d =0x%x\n",rreg,value); |
| if (i2c_smbus_write_byte_data(client, rreg, value)) { |
| pr_err("DAC31: I2C write failed\n"); |
| return -EIO; |
| } |
| if (reg < DAC31_CACHEREGNUM){ |
| cache[reg] = value; |
| }else if (TIMER_MCLK_DIV == reg){ |
| cache[PAGE3_REG16] = value; |
| }else if (DAC31_COERAM == reg){ |
| cache[PAGE8_REG1] = value; |
| } |
| return value; |
| } |
| #if 0 |
| static inline unsigned int dac31_read(struct snd_soc_codec *codec, unsigned int reg) |
| { |
| u8 *cache = codec->reg_cache; |
| u8 page; |
| u8 rreg; |
| u8 value; |
| //PRINTK("************in dac31_read\n"); |
| page = reg / 128; |
| rreg = reg % 128; |
| if (dac31_change_reg_page(codec,page)){ |
| PRINTK("=======================ERROR IN dac31_read\n"); |
| return -EIO; |
| } |
| value = rreg & 0xff; |
| value = i2c_smbus_read_byte_data(codec->control_data, value); |
| if (reg < DAC31_CACHEREGNUM){ |
| cache[reg] = value; |
| }else if (TIMER_MCLK_DIV == reg){ |
| cache[PAGE3_REG16] = value; |
| }else if (DAC31_COERAM == reg){ |
| cache[PAGE8_REG1] = value; |
| } |
| //PRINTK("reg_%d : %x\n",reg,value); |
| return value; |
| } |
| #endif |
| |
| static int dac31_sync(struct snd_soc_codec *codec) |
| { |
| u8 *cache = codec->reg_cache; |
| int i,r = 0; |
| |
| for (i=0; dac31_reg_enable[i] != -1;i++ ) { |
| PRINTK("IN SYNC i=%d\n",i); |
| if (TIMER_MCLK_DIV == dac31_reg_enable[i]) { |
| r |= snd_soc_write(codec,TIMER_MCLK_DIV,cache[PAGE3_REG16]); |
| }else if (DAC31_COERAM == dac31_reg_enable[i]){ |
| r |= snd_soc_write(codec,DAC31_COERAM,cache[PAGE8_REG1]); |
| }else{ |
| r |= snd_soc_write(codec,dac31_reg_enable[i],cache[dac31_reg_enable[i]]); |
| } |
| } |
| |
| return r; |
| } |
| |
| static int dac31_get_pllenable(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) |
| { |
| ucontrol->value.integer.value[0] = PLL_ENABLE; |
| return 0; |
| } |
| |
| static int dac31_SET_pllenable(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
| PLL_ENABLE = ucontrol->value.integer.value[0]; |
| if (PLL_ENABLE){ |
| snd_soc_write(codec,CLK_REG_1,0x03);//PLL generic on chip |
| snd_soc_write(codec,CLK_REG_3,0x01); // PLL j VALUE |
| snd_soc_write(codec,CLK_REG_4,0x00); // PLL D VALUE |
| snd_soc_write(codec,CLK_REG_5,0x00); //PLL D VALUE |
| snd_soc_write(codec,CLK_REG_2,0x91); // PLL PR VALUE,AND POWER UP |
| snd_soc_write(codec,NDAC_CLK_REG_6,0x81); // ndac value |
| snd_soc_write(codec,MDAC_CLK_REG_7,0x82); // mdac value |
| snd_soc_write(codec,DAC_OSR_MSB,0x00); // dosr value |
| snd_soc_write(codec,DAC_OSR_LSB,0x80); //dosr value |
| }else{ |
| snd_soc_write(codec,CLK_REG_1,0X00);//DISABLE PLL, USED MCLK |
| snd_soc_write(codec,CLK_REG_2,0x00); // PLL POWER DOWN |
| snd_soc_write(codec,NDAC_CLK_REG_6,0x81); // NDAC POWER up |
| snd_soc_write(codec,MDAC_CLK_REG_7,0x82); // mdac value |
| snd_soc_write(codec,DAC_OSR_MSB,0x00); // dosr value |
| snd_soc_write(codec,DAC_OSR_LSB,0x80); //dosr value |
| } |
| return 0; |
| } |
| |
| /******************** ALSA Controls and Widgets *********************************************/ |
| static DECLARE_TLV_DB_SCALE(output_tlv,-6350,50,0); |
| static DECLARE_TLV_DB_SCALE(hp_tlv,0,100,0); |
| static DECLARE_TLV_DB_SCALE(volume_tlv,-7830,50,0); |
| static DECLARE_TLV_DB_SCALE(micvo_tlv,-6300,50,0); |
| |
| static const char *dac31_lo_gain[] = {"+6db", "+12db","+18db","+24db"}; |
| static const char *dac_mute_control[] = {"UNMUTE" , "MUTE"}; |
| static const char *hpdriver_voltage_control[] = {"1.35V", "1.5V", "1.65V", "1.8V"}; |
| static const char *drc_status_control[] = {"DISABLED", "ENABLED"}; |
| static const char *pll_enable[]={"DISABLE","ENABLE"}; |
| static const char *process_block[]={"RESERVED","PRB_P1","PRB_P2","PRB_P3","PRB_P4","PRB_P5","PRB_P6","PRB_P7", |
| "PRB_P8","PRB_P9","PRB_P10","PRB_P11","PRB_P12","PRB_P13","PRB_P14", |
| "PRB_P15","PRB_P16","PRB_P17","PRB_P18","PRB_P19","PRB_P20","PRB_P21", |
| "PRB_P22","PRB_P23","PRB_P24","PRB_P25"}; |
| static const char *micvol_control[]={"CONTROL REGISTER","PIN"}; |
| static const char *micbiasvoltage_control[]={"POWER DOWN","2V","2.5V","AVDD"}; |
| static const char *datapath_control[]={"Both Channel Off","Left-channel=left data Right-channel=right data", |
| "Left-channel=right data Right-channel=left data", |
| "Left-channel=Right-channel=(L+R)/2 data"}; |
| |
| static const struct soc_enum dac31_enum[] = { |
| SOC_ENUM_SINGLE(SPL_DRIVER, 3, 4, dac31_lo_gain), |
| SOC_ENUM_SINGLE (DAC_MUTE_CTRL_REG, 3, 2, dac_mute_control), |
| SOC_ENUM_SINGLE (DAC_MUTE_CTRL_REG, 2, 2, dac_mute_control), |
| SOC_ENUM_SINGLE (HPHONE_DRIVERS, 3, 4, hpdriver_voltage_control), |
| SOC_ENUM_DOUBLE (DRC_CTRL_1, 6, 5, 2, drc_status_control), |
| SOC_ENUM_SINGLE_EXT(2,pll_enable), |
| SOC_ENUM_SINGLE(DAC_INSTRUCTION_SET,0,25,process_block), |
| SOC_ENUM_SINGLE(VOL_MICDECT_ADC,7,2,micvol_control), |
| SOC_ENUM_SINGLE(MICBIAS_CTRL,0,4,micbiasvoltage_control), |
| SOC_ENUM_DOUBLE(DAC_CHN_REG,4,2,4,datapath_control), |
| }; |
| |
| static const struct snd_kcontrol_new dac31_snd_controls[]={ |
| |
| SOC_DOUBLE_R_EXT_TLV("DAC Playback Volume",LDAC_VOL,RDAC_VOL,0,0xaf,0,\ |
| snd_soc_get_volsw_2r_dac3100,snd_soc_put_volsw_2r_dac3100,output_tlv), |
| /* sound new kcontrol for HP driver gain */ |
| SOC_DOUBLE_R_EXT_TLV("HP Driver Gain", HPL_DRIVER, HPR_DRIVER, 3, 9, 0,\ |
| snd_soc_get_volsw_2r_dac3100,snd_soc_put_volsw_2r_dac3100,hp_tlv), |
| /* sound new kcontrol for LO driver gain */ |
| SOC_ENUM("LO Driver Gain", dac31_enum[0]), |
| /* sound new kcontrol for HP mute */ |
| SOC_DOUBLE_R("HP DAC Playback Switch", HPL_DRIVER, HPR_DRIVER, 2, 0x01, 0), |
| /* sound new kcontrol for LO mute */ |
| SOC_SINGLE("LO DAC Playback Switch", SPL_DRIVER, 2, 0x01, 0), |
| |
| /* sound new kcontrol for Analog Volume Control for headphone and Speaker Outputs |
| * Please refer to Table 5-24 of the Codec DataSheet |
| */ |
| SOC_DOUBLE_R_TLV("HP Analog Volume",LEFT_ANALOG_HPL,RIGHT_ANALOG_HPR, 0, 117, 1, volume_tlv), |
| SOC_SINGLE_TLV("SPKR Analog Volume", LEFT_ANALOG_SPL, 0, 117, 1,volume_tlv), |
| |
| SOC_ENUM ("LEFT DAC MUTE", dac31_enum[1]), |
| SOC_ENUM ("RIGHT DAC MUTE", dac31_enum[2]), |
| SOC_ENUM ("HP Driver Voltage level", dac31_enum[3]), |
| SOC_ENUM ("DRC Status", dac31_enum[4]), |
| |
| /* Dynamic Range Compression Control */ |
| SOC_SINGLE ("DRC Hysteresis Value (0=0db 3=db)", DRC_CTRL_1, 0, 0x03, 0), |
| SOC_SINGLE ("DRC Threshold Value (0=-3db,7=-24db)", DRC_CTRL_1, 2, 0x07, 0), |
| SOC_SINGLE ("DRC Hold Time", DRC_CTRL_2, 3, 0x0F, 0), |
| SOC_SINGLE ("DRC Attack Time", DRC_CTRL_3, 4, 0x0F, 0), |
| SOC_SINGLE ("DRC Delay Rate", DRC_CTRL_3, 0, 0x0F, 0), |
| |
| /* PLL control */ |
| SOC_ENUM_EXT("PLL Enable Switch", dac31_enum[5], dac31_get_pllenable,dac31_SET_pllenable), |
| |
| /*Processing Blocks*/ |
| SOC_ENUM("Processing Blocks Switch",dac31_enum[6]), |
| |
| /* input volume control */ |
| SOC_SINGLE_EXT_TLV("Pin Volume Control Value(Read Only)",VOL_MICDECT_GAIN,0,0x7e,0,\ |
| snd_soc_get_volsw_dac3100,snd_soc_put_volsw_dac3100,micvo_tlv), |
| SOC_ENUM("Volume Control Pin Switch",dac31_enum[7]), |
| |
| /* headphone */ |
| SOC_ENUM("Micbias Voltage Switch",dac31_enum[8]), |
| |
| /*Channel Path */ |
| SOC_ENUM("Data Path Switch",dac31_enum[9]), |
| |
| }; |
| |
| /* |
| * DAPM Mixer Controls |
| */ |
| /* Left DAC_L Mixer */ |
| static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { |
| SOC_DAPM_SINGLE("L_DAC switch", DAC_MIXER_ROUTING, 6, 2, 0), |
| SOC_DAPM_SINGLE("MIC1_L switch", DAC_MIXER_ROUTING, 5, 1, 0), |
| }; |
| |
| /* Right DAC_R Mixer */ |
| static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { |
| |
| SOC_DAPM_SINGLE("R_DAC switch", DAC_MIXER_ROUTING, 2, 2, 0), |
| SOC_DAPM_SINGLE("MIC1_R switch", DAC_MIXER_ROUTING, 1, 1, 0), |
| }; |
| |
| static const struct snd_kcontrol_new lol_output_mixer_controls[] = { |
| SOC_DAPM_SINGLE("L_DAC switch", DAC_MIXER_ROUTING, 6, 2, 0), |
| SOC_DAPM_SINGLE("MIC1_L switch", DAC_MIXER_ROUTING, 5, 1, 0), |
| |
| }; |
| |
| |
| static const struct snd_soc_dapm_widget dac31_dapm_widgets[] = { |
| /* Left DAC to Left Outputs */ |
| /* dapm widget (stream domain) for left DAC */ |
| SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_CHN_REG, 7, 0), |
| |
| /* dapm widget (path domain) for left DAC_L Mixer */ |
| |
| SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, |
| &hpl_output_mixer_controls[0], |
| ARRAY_SIZE(hpl_output_mixer_controls)), |
| SND_SOC_DAPM_PGA("HPL Power", HPHONE_DRIVERS, 7, 0, NULL, 0), |
| |
| SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, |
| &lol_output_mixer_controls[0], |
| ARRAY_SIZE(lol_output_mixer_controls)), |
| SND_SOC_DAPM_PGA("LOL Power", CLASS_D_SPK, 7, 0, NULL, 0), |
| |
| /* Right DAC to Right Outputs */ |
| |
| /* dapm widget (stream domain) for right DAC */ |
| SND_SOC_DAPM_DAC("Right DAC", "Right Playback", DAC_CHN_REG, 6, 0), |
| /* dapm widget (path domain) for right DAC_R mixer */ |
| SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, |
| &hpr_output_mixer_controls[0], |
| ARRAY_SIZE(hpr_output_mixer_controls)), |
| SND_SOC_DAPM_PGA("HPR Power", HPHONE_DRIVERS, 6, 0, NULL, 0), |
| |
| /* dapm widget (platform domain) name for HPLOUT */ |
| SND_SOC_DAPM_OUTPUT("HPL"), |
| /* dapm widget (platform domain) name for HPROUT */ |
| SND_SOC_DAPM_OUTPUT("HPR"), |
| /* dapm widget (platform domain) name for LOLOUT */ |
| SND_SOC_DAPM_OUTPUT("LOL"), |
| |
| /* dapm widget (platform domain) name for MIC1LP */ |
| SND_SOC_DAPM_INPUT("MIC1LP"), |
| /* dapm widget (platform domain) name for MIC1RP*/ |
| SND_SOC_DAPM_INPUT("MIC1RP"), |
| /* dapm widget (platform domain) name for MIC1LM */ |
| SND_SOC_DAPM_INPUT("MIC1LM"), |
| }; |
| |
| static const struct snd_soc_dapm_route audio_map[] = { |
| /* ******** Right Output ******** */ |
| {"HPR Output Mixer", "R_DAC switch", "Right DAC"}, |
| {"HPR Output Mixer", "MIC1_R switch", "MIC1RP"}, |
| |
| {"HPR Power", NULL, "HPR Output Mixer"}, |
| {"HPR", NULL, "HPR Power"}, |
| |
| /* ******** Left Output ******** */ |
| {"HPL Output Mixer", "L_DAC switch", "Left DAC"}, |
| {"HPL Output Mixer", "MIC1_L switch", "MIC1LP"}, |
| |
| {"HPL Power", NULL, "HPL Output Mixer"}, |
| {"HPL", NULL, "HPL Power"}, |
| |
| |
| {"LOL Output Mixer", "L_DAC switch", "Left DAC"}, |
| {"LOL Output Mixer", "MIC1_L switch", "MIC1LP"}, |
| |
| {"LOL Power", NULL, "LOL Output Mixer"}, |
| {"LOL", NULL, "LOL Power"}, |
| |
| |
| }; |
| |
| static int dac31_add_widgets(struct snd_soc_codec *codec) |
| { |
| struct snd_soc_dapm_context *dapm = &codec->dapm; |
| snd_soc_dapm_new_controls(dapm,dac31_dapm_widgets, |
| ARRAY_SIZE(dac31_dapm_widgets)); |
| snd_soc_dapm_add_routes(dapm, audio_map,ARRAY_SIZE(audio_map)); |
| return 0; |
| } |
| |
| static int dac31_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) |
| { |
| u8 value; |
| if (level ==codec->dapm.bias_level) |
| return 0; |
| |
| switch (level) { |
| /* full On */ |
| case SND_SOC_BIAS_ON: |
| PRINTK("===========SND_SOC_BIAS_ON=============\n"); |
| /* Switch on NDAC Divider */ |
| value = snd_soc_read(codec, NDAC_CLK_REG_6); |
| snd_soc_write(codec, NDAC_CLK_REG_6, value | ENABLE_NDAC); |
| |
| /* Switch on MDAC Divider */ |
| value = snd_soc_read(codec, MDAC_CLK_REG_7); |
| snd_soc_write(codec, MDAC_CLK_REG_7, |
| value | ENABLE_MDAC); |
| |
| /* Switch on BCLK_N Divider */ |
| value = snd_soc_read(codec, CLK_REG_11); |
| snd_soc_write(codec, CLK_REG_11, value | ENABLE_BCLK); |
| |
| |
| /* Switch ON Left and Right DACs */ |
| value = snd_soc_read (codec, DAC_CHN_REG); |
| snd_soc_write (codec, DAC_CHN_REG, (value | ENABLE_DAC)); |
| |
| snd_soc_write(codec,DAC_MUTE_CTRL_REG,0x00);//unmute DAC left and right channels |
| |
| /* Switch ON the Class_D Speaker Amplifier */ |
| value = snd_soc_read (codec, CLASS_D_SPK); |
| snd_soc_write (codec, CLASS_D_SPK, (value | 0x80)); |
| |
| /* UNMUTE THE Class-D Speaker Driver */ |
| value = snd_soc_read (codec, SPL_DRIVER); |
| snd_soc_write (codec, SPL_DRIVER, (value | 0x04)); |
| break; |
| case SND_SOC_BIAS_PREPARE: |
| break; |
| case SND_SOC_BIAS_STANDBY: |
| PRINTK("===========SND_SOC_BIAS_STANDBY=============\n"); |
| |
| /* MUTE THE Class-D Speaker Driver */ |
| value = snd_soc_read (codec, SPL_DRIVER); |
| value &= ~0x04; |
| snd_soc_write (codec, SPL_DRIVER, value); |
| |
| /* Switch OFF the Class_D Speaker Amplifier */ |
| value = snd_soc_read (codec, CLASS_D_SPK); |
| snd_soc_write (codec, CLASS_D_SPK, (value & ~0x80)); |
| |
| /* Switch OFF Left and Right DACs */ |
| value = snd_soc_read (codec, DAC_CHN_REG); |
| snd_soc_write (codec, DAC_CHN_REG, (value & ~ENABLE_DAC)); |
| |
| /* Switch off NDAC Divider */ |
| value = snd_soc_read(codec, NDAC_CLK_REG_6); |
| snd_soc_write(codec, NDAC_CLK_REG_6, |
| value & ~ENABLE_NDAC); |
| |
| /* Switch off MDAC Divider */ |
| value = snd_soc_read(codec, MDAC_CLK_REG_7); |
| snd_soc_write(codec, MDAC_CLK_REG_7, |
| value & ~ENABLE_MDAC); |
| /* Switch off BCLK_N Divider */ |
| snd_soc_write(codec, CLK_REG_11, value & ~ENABLE_BCLK); |
| break; |
| |
| case SND_SOC_BIAS_OFF: |
| PRINTK("===========SND_SOC_BIAS_OFF=============\n"); |
| /* MUTE THE Class-D Speaker Driver */ |
| value = snd_soc_read (codec, SPL_DRIVER); |
| value &= ~0x04; |
| snd_soc_write (codec, SPL_DRIVER, value); |
| |
| /* Switch OFF the Class_D Speaker Amplifier */ |
| value = snd_soc_read (codec, CLASS_D_SPK); |
| snd_soc_write (codec, CLASS_D_SPK, (value & ~0x80)); |
| /* Switch OFF Left and Right DACs */ |
| value = snd_soc_read (codec, DAC_CHN_REG); |
| snd_soc_write (codec, DAC_CHN_REG, (value & ~ENABLE_DAC)); |
| /* Switch off NDAC Divider */ |
| value = snd_soc_read(codec, NDAC_CLK_REG_6); |
| snd_soc_write(codec, NDAC_CLK_REG_6, |
| value & ~ENABLE_NDAC); |
| /* Switch off MDAC Divider */ |
| value = snd_soc_read(codec, MDAC_CLK_REG_7); |
| snd_soc_write(codec, MDAC_CLK_REG_7, |
| value & ~ENABLE_MDAC); |
| /* Switch off BCLK_N Divider */ |
| snd_soc_write(codec, CLK_REG_11, value & ~ENABLE_BCLK); |
| break; |
| default: |
| break; |
| } |
| codec->dapm.bias_level = level; |
| return 0; |
| } |
| |
| /* the sturcture contains the different values for mclk */ |
| static const struct dac3100_rate_divs dac3100_divs[] = { |
| /* |
| * mclk, rate, p_val, pll_j, pll_d, dosr, ndac, mdac, aosr, nadc, madc, blck_N, |
| * codec_speficic_initializations |
| */ |
| /* 8k rate */ |
| // DDenchev (MMS) |
| { 4096000, 8000,1, 1, 0, 512, 1, 1,16, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 1}}}, |
| {12000000, 8000, 1, 7, 1680, 128, 2, 42, 4, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 1}}}, |
| {13000000, 8000, 1, 6, 3803, 128, 3, 27, 4, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 4}}}, |
| {24000000, 8000, 2, 7, 6800, 768, 15, 1, 24, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| |
| /* 11.025k rate */ |
| // DDenchev (MMS) |
| {5644800, 11025, 1, 1, 0, 256, 1, 2, 8, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {12000000, 11025, 1, 7, 560, 128, 5, 12, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {13000000, 11025, 1, 6, 1876, 128, 3, 19, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 4}}}, |
| {24000000, 11025, 2, 7, 5264, 512, 16, 1, 16, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| |
| /* 12k rate */ |
| // DDenchev (MMS) |
| {12000000, 12000, 1, 7, 1680, 128, 2, 28, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {13000000, 12000, 1, 6, 3803, 128, 3, 18, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 4}}}, |
| |
| /* 16k rate */ |
| // DDenchev (MMS) |
| { 4096000, 16000, 1, 1, 0, 256, 1, 1, 8, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {12000000, 16000, 1, 7, 1680, 128, 2, 21, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {13000000, 16000, 1, 6, 6166, 128, 3, 14, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 4}}}, |
| {24000000, 16000, 2, 7, 6800, 384, 15, 1, 12, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| |
| /* 22.05k rate */ |
| // DDenchev (MMS) |
| { 5644800, 22050, 1, 1, 0, 128, 1, 2, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {12000000, 22050, 1, 7, 560, 128, 5, 6, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {13000000, 22050, 1, 6, 5132, 128, 3, 10, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 4}}}, |
| {24000000, 22050, 2, 7, 5264, 256, 16, 1, 8, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| |
| /* 24k rate */ |
| // DDenchev (MMS) |
| { 6144000, 24000, 1, 1, 0, 128, 1, 2, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {12000000, 24000, 1, 7, 1680, 128, 2, 14, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {13000000, 24000, 1, 6, 3803, 128, 3, 9, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 4}}}, |
| |
| /* 32k rate */ |
| // DDenchev (MMS) |
| { 8192000, 32000, 1, 1, 0, 128, 1, 2, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {12000000, 32000, 1, 6, 1440, 128, 2, 9, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {13000000, 32000, 1, 6, 6166, 128, 3, 7, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 4}}}, |
| {24000000, 32000, 2, 7, 1680, 192, 7, 2, 6, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| |
| /* 44.1k rate */ |
| // DDenchev (MMS) |
| {11289600, 44100, 1, 1, 0, 128, 1, 2, 4, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 1}}}, |
| {12000000, 44100, 1, 7, 560, 128, 5, 3, 4, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 1}}}, |
| {13000000, 44100, 1, 6, 5132, 128, 3, 5, 4, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 4}}}, |
| {24000000, 44100, 2, 7, 5264, 128, 8, 2, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| |
| /* 48k rate */ |
| // DDenchev (MMS) |
| {12288000, 48000, 1, 1, 0, 64, 1, 4, 2, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {12000000, 48000, 1, 7, 1680, 128, 2, 7, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| {13000000, 48000, 1, 6, 6166, 128, 7, 2, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 4}}}, |
| {24000000, 48000, 2, 8, 1920, 128, 8, 2, 4, |
| {{DAC_INSTRUCTION_SET, 1}, {61, 1}}}, |
| |
| /*96k rate : GT 21/12/2009: NOT MODIFIED */ |
| {12000000, 96000, 1, 8, 1920, 64, 2, 8, 2, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 7}}}, |
| {13000000, 96000, 1, 6, 6166, 64, 7, 2, 2, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 10}}}, |
| {24000000, 96000, 2, 8, 1920, 64, 4, 4, 2, |
| {{DAC_INSTRUCTION_SET, 7}, {61, 7}}}, |
| |
| /*192k : GT 21/12/2009: NOT MODIFIED */ |
| {12000000, 192000, 1, 8, 1920, 32, 2, 8, 1, |
| {{DAC_INSTRUCTION_SET, 17}, {61, 13}}}, |
| {13000000, 192000, 1, 6, 6166, 32, 7, 2, 1, |
| {{DAC_INSTRUCTION_SET, 17}, {61, 13}}}, |
| {24000000, 192000, 2, 8, 1920, 32, 4, 4, 1, |
| {{DAC_INSTRUCTION_SET, 17}, {61, 13}}}, |
| }; |
| |
| /* |
| *---------------------------------------------------------------------------- |
| * Function : dac3100_get_divs |
| * Purpose : This function is to get required divisor from the "dac3100_divs" |
| * table. |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static inline int dac31_get_divs(int mclk, int rate) |
| { |
| int i; |
| |
| PRINTK("+ dac3100_get_divs mclk(%d) rate(%d)\n", mclk, rate); |
| |
| for (i = 0; i < ARRAY_SIZE(dac3100_divs); i++) { |
| if ((dac3100_divs[i].rate == rate) && (dac3100_divs[i].mclk == mclk)) { |
| PRINTK("%d %d %d %d %d %d %d\n", |
| dac3100_divs[i].p_val, |
| dac3100_divs[i].pll_j, |
| dac3100_divs[i].pll_d, |
| dac3100_divs[i].dosr, |
| dac3100_divs[i].ndac, |
| dac3100_divs[i].mdac, |
| dac3100_divs[i].blck_N); |
| |
| return i; |
| } |
| } |
| printk(KERN_ALERT "Master clock and sample rate is not supported\n"); |
| return -EINVAL; |
| } |
| |
| |
| |
| static int dac31_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
| int clk_id, unsigned int freq, int dir) |
| { |
| struct snd_soc_codec *codec = codec_dai->codec; |
| struct dac31_priv *dac31 = snd_soc_codec_get_drvdata(codec); |
| dac31->sysclk = freq; |
| return 0; |
| } |
| |
| #if debug_dac |
| void debug_hw_params(struct snd_soc_codec *codec) |
| { |
| PRINTK("CLK_REG_2=%x\n",snd_soc_read(codec, CLK_REG_2)); |
| PRINTK("CLK_REG_3=%x\n",snd_soc_read(codec, CLK_REG_3)); |
| PRINTK("CLK_REG_4=%x\n",snd_soc_read(codec, CLK_REG_4)); |
| PRINTK("CLK_REG_5=%x\n",snd_soc_read(codec, CLK_REG_5)); |
| PRINTK("NDAC_CLK_REG_6=%x\n",snd_soc_read(codec, NDAC_CLK_REG_6 )); |
| PRINTK("MDAC_CLK_REG_7=%x\n",snd_soc_read(codec, MDAC_CLK_REG_7)); |
| PRINTK("DAC_OSR_MSB=%x\n",snd_soc_read(codec, DAC_OSR_MSB)); |
| PRINTK("DAC_OSR_LSB=%x\n",snd_soc_read(codec, DAC_OSR_LSB)); |
| PRINTK("CLK_REG_11=%x\n",snd_soc_read(codec, CLK_REG_11)); |
| PRINTK("INTERFACE_SET_REG_1=%x\n",snd_soc_read(codec, INTERFACE_SET_REG_1)); |
| return; |
| } |
| |
| |
| #endif |
| |
| static int dac31_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_codec *codec =rtd->codec; |
| struct dac31_priv *dac31 = snd_soc_codec_get_drvdata(codec); |
| int i; |
| u8 data; |
| |
| dac31_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| |
| i = dac31_get_divs(dac31->sysclk, params_rate(params)); |
| PRINTK("- Sampling rate: %d, %d\n", params_rate(params), i); |
| |
| |
| if (i < 0) { |
| printk(KERN_ALERT "sampling rate not supported\n"); |
| return i; |
| } |
| |
| if (PLL_ENABLE){ |
| |
| /* We will fix R value to 1 and will make P & J=K.D as varialble */ |
| |
| /* Setting P & R values */ |
| snd_soc_write(codec, CLK_REG_2, ((dac3100_divs[i].p_val << 4) | 0x81));//cddiao fix PLL to power up |
| |
| /* J value */ |
| snd_soc_write(codec, CLK_REG_3, dac3100_divs[i].pll_j); |
| |
| /* MSB & LSB for D value */ |
| snd_soc_write(codec, CLK_REG_4, (dac3100_divs[i].pll_d >> 8)); |
| snd_soc_write(codec, CLK_REG_5, (dac3100_divs[i].pll_d & dac3100_8BITS_MASK)); |
| } |
| /* NDAC divider value */ |
| snd_soc_write(codec, NDAC_CLK_REG_6,( dac3100_divs[i].ndac) | 0x80);// cddiao fix NDAC to power up |
| |
| /* MDAC divider value */ |
| snd_soc_write(codec, MDAC_CLK_REG_7, (dac3100_divs[i].mdac) |0x80);// cddiao fix MDAC to power up |
| |
| /* DOSR MSB & LSB values */ |
| snd_soc_write(codec, DAC_OSR_MSB, dac3100_divs[i].dosr >> 8); |
| snd_soc_write(codec, DAC_OSR_LSB, dac3100_divs[i].dosr & dac3100_8BITS_MASK); |
| |
| /* BCLK N divider */ |
| snd_soc_write(codec, CLK_REG_11, (dac3100_divs[i].blck_N) | 0x80); // slave mode, no need to |
| |
| data = snd_soc_read(codec, INTERFACE_SET_REG_1); |
| |
| data = data & ~(3 << 4); |
| |
| PRINTK( "- Data length: %d\n", params_format(params)); |
| |
| switch (params_format(params)) { |
| case SNDRV_PCM_FORMAT_S16_LE: |
| break; |
| case SNDRV_PCM_FORMAT_S20_3LE: |
| data |= (dac3100_WORD_LEN_20BITS << DAC_OSR_MSB_SHIFT); |
| break; |
| case SNDRV_PCM_FORMAT_S24_LE: |
| data |= (dac3100_WORD_LEN_24BITS << DAC_OSR_MSB_SHIFT); |
| break; |
| case SNDRV_PCM_FORMAT_S32_LE: |
| data |= (dac3100_WORD_LEN_32BITS << DAC_OSR_MSB_SHIFT); |
| break; |
| } |
| /* Write to Page 0 Reg 27 for the Codec Interface control 1 Register */ |
| snd_soc_write(codec, INTERFACE_SET_REG_1, data); |
| |
| /* Switch on the Codec into ON State after all the above configuration */ |
| dac31_set_bias_level(codec, SND_SOC_BIAS_ON); |
| |
| /* The below block is not required since these are RESERVED REGISTERS |
| * in the DAc3100 Codec Chipset |
| * for (j = 0; j < NO_FEATURE_REGS; j++) { |
| * snd_soc_write(codec, |
| * dac3100_divs[i].codec_specific_regs[j].reg_offset, |
| * dac3100_divs[i].codec_specific_regs[j].reg_val); |
| *} |
| */ |
| |
| PRINTK("- SET dac3100_hw_params\n"); |
| #if debug_dac |
| debug_hw_params(codec); |
| #endif |
| |
| return 0; |
| } |
| |
| static int dac31_set_dai_fmt(struct snd_soc_dai *codec_dai,unsigned int fmt) |
| { |
| struct snd_soc_codec *codec = codec_dai->codec; |
| u8 iface_reg; |
| iface_reg = snd_soc_read(codec, INTERFACE_SET_REG_1); |
| iface_reg = iface_reg & ~(3 << 6 | 3 << 2); |
| |
| PRINTK("+ dac3100_set_dai_fmt (%x) \n", fmt); |
| |
| /* interface format */ |
| switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| case SND_SOC_DAIFMT_I2S: |
| break; |
| case SND_SOC_DAIFMT_DSP_A: |
| iface_reg |= (dac3100_DSP_MODE << CLK_REG_3_SHIFT); |
| break; |
| case SND_SOC_DAIFMT_RIGHT_J: |
| iface_reg |= (dac3100_RIGHT_JUSTIFIED_MODE << CLK_REG_3_SHIFT); |
| break; |
| case SND_SOC_DAIFMT_LEFT_J: |
| iface_reg |= (dac3100_LEFT_JUSTIFIED_MODE << CLK_REG_3_SHIFT); |
| break; |
| default: |
| printk(KERN_ALERT "Invalid DAI interface format\n"); |
| return -EINVAL; |
| } |
| |
| switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
| case SND_SOC_DAIFMT_CBM_CFM: |
| iface_reg |= 0x0c; |
| PRINTK("=======SLAVE MODE ========\n"); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| |
| PRINTK("- dac3100_set_dai_fmt (%x) \n", iface_reg); |
| snd_soc_write(codec, INTERFACE_SET_REG_1, iface_reg); |
| return 0; |
| } |
| |
| static int dac31_mute(struct snd_soc_dai *dai, int mute) |
| { |
| return 0; |
| } |
| |
| |
| #define DAC31_RATES SNDRV_PCM_RATE_8000_48000 |
| #define DAC31_FORMATS SNDRV_PCM_FMTBIT_S16_LE |
| |
| |
| static struct snd_soc_dai_ops dac31_dai_ops = { |
| .set_fmt = dac31_set_dai_fmt, |
| .hw_params = dac31_hw_params, |
| .digital_mute = dac31_mute, |
| .set_sysclk = dac31_set_dai_sysclk, |
| }; |
| |
| struct snd_soc_dai_driver dac31_dai = { |
| .name = "tlv320dac31", |
| .playback = { |
| .stream_name = "Playback", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = DAC31_RATES, |
| .formats = DAC31_FORMATS,}, |
| .capture = { |
| .stream_name = "Capture", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = DAC31_RATES, |
| .formats = DAC31_FORMATS,}, |
| .ops = &dac31_dai_ops, |
| }; |
| |
| static int dac31_suspend(struct snd_soc_codec *codec, pm_message_t state) |
| { |
| PRINTK("IN SUSPEND\n"); |
| dac31_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| return 0; |
| } |
| |
| static int dac31_resume(struct snd_soc_codec *codec) |
| { |
| PRINTK("in resume\n"); |
| dac31_sync(codec); |
| dac31_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| return 0; |
| } |
| |
| static int dac31_probe(struct snd_soc_codec *codec) |
| { |
| struct dac31_priv *dac31 = snd_soc_codec_get_drvdata(codec); |
| struct dac31_platform_data *dac31_pdata; |
| int ret; |
| PRINTK("IN THE DAC31_PROBE\n"); |
| |
| dev_info(codec->dev, "DAC31 Audio Codec %s", DAC31_VERSION); |
| |
| codec->control_data = dac31->control_data; |
| |
| dac31_pdata = codec->dev->platform_data; |
| if (!dac31_pdata) |
| return -EINVAL; |
| |
| if (gpio_is_valid(dac31_pdata->rst_pin)) { |
| ret = gpio_request(dac31_pdata->rst_pin,"TLVDAC31RESETGPIO"); |
| if (ret < 0) |
| return ret; |
| } else { |
| return -ENODEV; |
| } |
| |
| gpio_direction_output(dac31_pdata->rst_pin,GPIO_LOW); |
| msleep(dac31_pdata->rst_delay); |
| gpio_direction_output(dac31_pdata->rst_pin,GPIO_HIGH); |
| |
| /* power on device */ |
| dac31_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| snd_soc_write(codec,RESET,0x01); // reset the chip |
| snd_soc_write(codec,AIS_REG_3,0x01); // BDIV_CLKIN confied to DAC_MOD_CLK and BCLK not inverted |
| if (PLL_ENABLE){ |
| snd_soc_write(codec,CLK_REG_1,0x03);//PLL generic on chip |
| snd_soc_write(codec,CLK_REG_3,0x01); // PLL j VALUE |
| snd_soc_write(codec,CLK_REG_4,0x00); // PLL D VALUE |
| snd_soc_write(codec,CLK_REG_5,0x00); //PLL D VALUE |
| snd_soc_write(codec,CLK_REG_2,0x91); // PLL PR VALUE,AND POWER UP |
| snd_soc_write(codec,NDAC_CLK_REG_6,0x81); // ndac value |
| snd_soc_write(codec,MDAC_CLK_REG_7,0x82); // mdac value |
| snd_soc_write(codec,DAC_OSR_MSB,0x00); // dosr value |
| snd_soc_write(codec,DAC_OSR_LSB,0x80); //dosr value |
| }else{ |
| snd_soc_write(codec,CLK_REG_1,0x00);//DISABLE PLL, USED MCLK |
| snd_soc_write(codec,CLK_REG_2,0x00); // PLL POWER DOWN |
| snd_soc_write(codec,NDAC_CLK_REG_6,0x81); // NDAC POWER up |
| snd_soc_write(codec,MDAC_CLK_REG_7,0x82); // mdac value |
| snd_soc_write(codec,DAC_OSR_MSB,0x00); // dosr value |
| snd_soc_write(codec,DAC_OSR_LSB,0x80); //dosr value |
| } |
| #if HAEDPHONE_JACK |
| snd_soc_write(codec,GPIO1_CTRL,0x14);// set gpio1 to interrupt 1 output |
| snd_soc_write(codec,INT1_CTRL,0x80);// set INT1 to headphone detector |
| #endif |
| |
| snd_soc_write(codec,INTERFACE_SET_REG_1,0x00);//i2s 16bit |
| snd_soc_write(codec,DAC_INSTRUCTION_SET,0x17);// PRB_P23 |
| snd_soc_write(codec,DAC31_COERAM,0x04);// enable adaptive filtering |
| |
| //PLAYBACK PATH SETTING |
| snd_soc_write(codec,DAC_MIXER_ROUTING,0x44);// dac route to the mixer |
| |
| //CLASS-D SPEAKER DRIVER |
| snd_soc_write(codec,SPL_DRIVER,0x01c);//class-d 24db |
| snd_soc_write(codec,LEFT_ANALOG_SPL,0x92);//class-d volume -6db |
| snd_soc_write(codec,CLASS_D_SPK,0x86);//power up class-d, |
| //power up dac |
| snd_soc_write(codec,LDAC_VOL,0xd8);//dac left -22db |
| snd_soc_write(codec,RDAC_VOL,0xd8);//dac right -22db |
| snd_soc_write(codec,DAC_MUTE_CTRL_REG,0x00);//unmute DAC left and right channels |
| snd_soc_write(codec,DAC_CHN_REG,0xd4);//power up dac and data path |
| |
| snd_soc_add_controls(codec, dac31_snd_controls,ARRAY_SIZE(dac31_snd_controls)); |
| dac31_add_widgets(codec); |
| |
| return 0; |
| |
| } |
| |
| static int dac31_remove(struct snd_soc_codec *codec) |
| { |
| struct dac31_platform_data *dac31_pdata; |
| dac31_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| dac31_pdata = codec->dev->platform_data; |
| gpio_free(dac31_pdata->rst_pin); |
| return 0; |
| } |
| |
| |
| static struct snd_soc_codec_driver soc_codec_dev_dac31 = { |
| .probe = dac31_probe, |
| .remove = dac31_remove, |
| .suspend = dac31_suspend, |
| .resume = dac31_resume, |
| .read = dac31_read_reg_cache, |
| .write = dac31_write, |
| .set_bias_level = dac31_set_bias_level, |
| .reg_cache_size = ARRAY_SIZE(dac31_reg), |
| .reg_word_size = sizeof(u8), |
| .reg_cache_default = dac31_reg, |
| .reg_cache_step = 1, |
| }; |
| |
| #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) |
| |
| static int dac31_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) |
| { |
| struct dac31_priv *dac31; |
| int ret; |
| PRINTK("IN I2C PROBE\n"); |
| dac31 = kzalloc(sizeof(struct dac31_priv), GFP_KERNEL); |
| if (dac31 == NULL) |
| return -ENOMEM; |
| i2c_set_clientdata(i2c,dac31); |
| dac31->control_data = i2c; |
| |
| ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_dac31, |
| &dac31_dai, 1); |
| if (ret < 0) |
| kfree(dac31); |
| |
| PRINTK("ret = %d\n",ret); |
| return ret; |
| } |
| |
| static int dac31_i2c_remove(struct i2c_client *i2c) |
| { |
| snd_soc_unregister_codec(&i2c->dev); |
| kfree(i2c_get_clientdata(i2c)); |
| return 0; |
| } |
| |
| static int dac31_i2c_resume(struct i2c_client *i2c) |
| { |
| struct dac31_platform_data *dac31_pdata; |
| dac31_pdata = i2c->dev.platform_data; |
| if (!dac31_pdata) |
| return -EINVAL; |
| PRINTK("IN DAC31_I2C_RESUME\n"); |
| gpio_direction_output(dac31_pdata->rst_pin,GPIO_LOW); |
| msleep(dac31_pdata->rst_delay); |
| gpio_direction_output(dac31_pdata->rst_pin,GPIO_HIGH); |
| msleep(dac31_pdata->rst_delay); |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id dac31_i2c_id[] = { |
| { "tlv320dac31", 0 }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, dac31_i2c_id); |
| |
| static struct i2c_driver dac31_i2c_driver = { |
| .driver = { |
| .name = "tlv320dac31-codec", |
| .owner = THIS_MODULE, |
| }, |
| .probe = dac31_i2c_probe, |
| .remove = dac31_i2c_remove, |
| .id_table = dac31_i2c_id, |
| .resume = dac31_i2c_resume, |
| }; |
| #endif |
| |
| static int __init dac31_modinit(void) |
| { |
| int ret; |
| #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) |
| PRINTK("init dac31 modinit\n"); |
| ret = i2c_add_driver(&dac31_i2c_driver); |
| if (ret != 0) |
| pr_err("Failed to register DAC31 I2C driver: %d\n", ret); |
| #endif |
| return ret; |
| } |
| module_init(dac31_modinit); |
| |
| static void __exit dac31_exit(void) |
| { |
| #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) |
| i2c_del_driver(&dac31_i2c_driver); |
| #endif |
| } |
| module_exit(dac31_exit); |
| |
| MODULE_DESCRIPTION("Soc DAC31 Driver"); |
| MODULE_AUTHOR("Johnson Diao (cddiao@ambarella.com)"); |
| MODULE_LICENSE("GPL"); |