static int twl6040_mute(struct snd_soc_dai *codec_dai, int mute) { #if 0 struct snd_soc_codec *codec = codec_dai->codec; int hs_gain, hfl_gain, hfr_gain; struct snd_soc_card *card = codec_dai->card; int idx; int mute_all; hfl_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN); hfr_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN); hs_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); if (mute) { mute_all = 1; for( idx = 0 ; idx < TWL6040_MUTE_DATA_MAX ; idx++ ) { if( s_mute_data[idx].dai == codec_dai ) { s_mute_data[idx].mute = mute; } if( s_mute_data[idx].dai && s_mute_data[idx].mute == 0 ) { mute_all = 0; break; } } if( mute_all ) { twl6040_i2c_write(TWL6040_REG_HFLGAIN, 0x1D); twl6040_i2c_write(TWL6040_REG_HFRGAIN, 0x1D); twl6040_i2c_write(TWL6040_REG_HSGAIN, 0xFF); } } else { mute_all = 0; for( idx = 0 ; idx < TWL6040_MUTE_DATA_MAX ; idx++ ) { if( s_mute_data[idx].dai == codec_dai ) { s_mute_data[idx].mute = mute; } if( s_mute_data[idx].dai && s_mute_data[idx].mute ) { mute_all = 1; break; } } if( mute_all == 0 ){ twl6040_write(codec, TWL6040_REG_HFLGAIN, hfl_gain); twl6040_write(codec, TWL6040_REG_HFRGAIN, hfr_gain); twl6040_write(codec, TWL6040_REG_HSGAIN, hs_gain); } } #endif return 0; }
/* set headset dac and driver power mode */ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { int hslctl, hsrctl; int mask = TWL6040_HSDRVMODEL | TWL6040_HSDACMODEL; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); /* Earphone doesn't support low power mode */ high_perf |= priv->earpiece_used; hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); if (high_perf) { hslctl &= ~mask; hsrctl &= ~mask; } else { hslctl |= mask; hsrctl |= mask; } twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl); twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl); return 0; }
/* twl6040 codec manual power-up sequence */ static void twl6040_power_up(struct snd_soc_codec *codec) { u8 ncpctl, ldoctl, lppllctl, accctl; ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); /* enable reference system */ ldoctl |= TWL6040_REFENA; twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); msleep(10); /* enable internal oscillator */ ldoctl |= TWL6040_OSCENA; twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); udelay(10); /* enable high-side ldo */ ldoctl |= TWL6040_HSLDOENA; twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); udelay(244); /* enable negative charge pump */ ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN; twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); udelay(488); /* enable low-side ldo */ ldoctl |= TWL6040_LSLDOENA; twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); udelay(244); /* enable low-power pll */ lppllctl |= TWL6040_LPLLENA; twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); /* reset state machine */ accctl |= TWL6040_RESETSPLIT; twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); mdelay(5); accctl &= ~TWL6040_RESETSPLIT; twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); /* disable internal oscillator */ ldoctl &= ~TWL6040_OSCENA; twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); }
/* set headset dac and driver power mode */ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { int hslctl, hsrctl; int mask = TWL6040_HSDRVMODEL | TWL6040_HSDACMODEL; hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); if (high_perf) { hslctl &= ~mask; hsrctl &= ~mask; } else { hslctl |= mask; hsrctl |= mask; } twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl); twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl); return 0; }
void set_hook_enable(struct snd_soc_codec *codec, int on) { u8 hkctl1; hkctl1 = twl6040_read_reg_cache(codec, TWL6040_REG_HKCTL1); if(on) hkctl1 |= TWL6040_HKEN; else hkctl1 &= ~TWL6040_HKEN; twl6040_write(codec, TWL6040_REG_HKCTL1, hkctl1); }
void hs_set_bias(struct snd_soc_codec *codec, int on) { u8 hsbias; hsbias = twl6040_read_reg_cache(codec, TWL6040_REG_AMICBCTL); if(on) hsbias |= TWL6040_HMICENA; else hsbias &= ~TWL6040_HMICENA; twl6040_write(codec, TWL6040_REG_AMICBCTL, hsbias); }
static int twl6040_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); if (mute) { priv->hfl_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN); priv->hfr_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN); priv->hs_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D); twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D); twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF); } else { twl6040_write(codec, TWL6040_REG_HFLGAIN, priv->hfl_gain); twl6040_write(codec, TWL6040_REG_HFRGAIN, priv->hfr_gain); twl6040_write(codec, TWL6040_REG_HSGAIN, priv->hs_gain); } return 0; }
static int twl6040_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 twl6040_data *priv = snd_soc_codec_get_drvdata(codec); u8 lppllctl; int rate; /* nothing to do for high-perf pll, it supports only 48 kHz */ if (priv->pll == TWL6040_HPPLL_ID) return 0; lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); rate = params_rate(params); switch (rate) { case 11250: case 22500: case 44100: case 88200: lppllctl |= TWL6040_LPLLFIN; priv->sysclk = 17640000; break; case 8000: case 16000: case 32000: case 48000: case 96000: lppllctl &= ~TWL6040_LPLLFIN; priv->sysclk = 19200000; break; default: dev_err(codec->dev, "unsupported rate %d\n", rate); return -EINVAL; } twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); return 0; }
static int twl6040_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 twl6040_data *priv = snd_soc_codec_get_drvdata(codec); u8 hppllctl, lppllctl; hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL); lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); switch (clk_id) { case TWL6040_SYSCLK_SEL_LPPLL: switch (freq) { case 32768: /* headset dac and driver must be in low-power mode */ headset_power_mode(codec, 0); /* clk32k input requires low-power pll */ lppllctl |= TWL6040_LPLLENA; twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); mdelay(5); lppllctl &= ~TWL6040_HPLLSEL; twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); hppllctl &= ~TWL6040_HPLLENA; twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); break; default: dev_err(codec->dev, "unknown mclk freq %d\n", freq); return -EINVAL; } /* lppll divider */ switch (priv->sysclk) { case 17640000: lppllctl |= TWL6040_LPLLFIN; break; case 19200000: lppllctl &= ~TWL6040_LPLLFIN; break; default: /* sysclk not yet configured */ lppllctl &= ~TWL6040_LPLLFIN; priv->sysclk = 19200000; break; } twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); priv->pll = TWL6040_LPPLL_ID; priv->sysclk_constraints = &lp_constraints; break; case TWL6040_SYSCLK_SEL_HPPLL: hppllctl &= ~TWL6040_MCLK_MSK; switch (freq) { case 12000000: /* mclk input, pll enabled */ hppllctl |= TWL6040_MCLK_12000KHZ | TWL6040_HPLLSQRBP | TWL6040_HPLLENA; break; case 19200000: /* mclk input, pll disabled */ hppllctl |= TWL6040_MCLK_19200KHZ | TWL6040_HPLLSQRENA | TWL6040_HPLLBP; break; case 26000000: /* mclk input, pll enabled */ hppllctl |= TWL6040_MCLK_26000KHZ | TWL6040_HPLLSQRBP | TWL6040_HPLLENA; break; case 38400000: /* clk slicer, pll disabled */ hppllctl |= TWL6040_MCLK_38400KHZ | TWL6040_HPLLSQRENA | TWL6040_HPLLBP; break; default: dev_err(codec->dev, "unknown mclk freq %d\n", freq); return -EINVAL; } /* headset dac and driver must be in high-performance mode */ headset_power_mode(codec, 1); twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); udelay(500); lppllctl |= TWL6040_HPLLSEL; twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); lppllctl &= ~TWL6040_LPLLENA; twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); /* high-performance pll can provide only 19.2 MHz */ priv->pll = TWL6040_HPPLL_ID; priv->sysclk = 19200000; priv->sysclk_constraints = &hp_constraints; break; default: dev_err(codec->dev, "unknown clk_id %d\n", clk_id); return -EINVAL; } return 0; }
static int twl6040_probe(struct snd_soc_codec *codec) { struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev); struct twl6040_data *priv; struct twl6040_jack_data *jack; int ret = 0; codec->control_data = dev_get_drvdata(codec->dev->parent); priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); if (!priv->workqueue) goto work_err; #if TWL6040_DEBUG /////////////////////////////////////////////////////////////////////////////////////////////////// { codec_twl6040 = codec->control_data; ret = device_create_file(codec->dev, &dev_attr_reg); if (ret < 0) printk(KERN_WARNING "twl6040: failed to add entries\n"); ret = device_create_file(codec->dev, &dev_attr_val); if (ret < 0) printk(KERN_WARNING "twl6040: failed to add entries\n"); ret = device_create_file(codec->dev, &dev_attr_prt); if (ret < 0) printk(KERN_WARNING "twl6040: failed to add entries\n"); } /////////////////////////////////////////////////////////////////////////////////////////////////// #endif priv->hfl_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN); priv->hfr_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN); priv->hs_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); INIT_DELAYED_WORK(&priv->delayed_work, twl6040_accessory_work); mutex_init(&priv->mutex); priv->vddhf_reg = regulator_get(codec->dev, "vddhf"); if (IS_ERR(priv->vddhf_reg)) { ret = PTR_ERR(priv->vddhf_reg); dev_warn(codec->dev, "couldn't get VDDHF regulator %d\n", ret); priv->vddhf_reg = NULL; } if (priv->vddhf_reg) { ret = regulator_set_voltage(priv->vddhf_reg, pdata->vddhf_uV, pdata->vddhf_uV); if (ret) { dev_warn(codec->dev, "failed to set VDDHF voltage %d\n", ret); goto reg_err; } } /* switch-class based headset detection */ jack = &priv->hs_jack; jack->sdev.name = "h2w"; ret = switch_dev_register(&jack->sdev); if (ret) { dev_err(codec->dev, "error registering switch device %d\n", ret); goto reg_err; } wake_lock_init(&priv->wake_lock, WAKE_LOCK_SUSPEND, "twl6040"); ret = twl6040_request_irq(codec->control_data, TWL6040_IRQ_PLUG, twl6040_audio_handler, IRQF_NO_SUSPEND, "twl6040_irq_plug", codec); if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); goto irq_err; } /* init vio registers */ twl6040_init_vio_regs(codec); /* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret) goto bias_err; snd_soc_add_controls(codec, twl6040_snd_controls, ARRAY_SIZE(twl6040_snd_controls)); twl6040_add_widgets(codec); /* TODO: read HS jack insertion status */ return 0; bias_err: twl6040_free_irq(codec->control_data, TWL6040_IRQ_PLUG, codec); irq_err: wake_lock_destroy(&priv->wake_lock); switch_dev_unregister(&jack->sdev); reg_err: if (priv->vddhf_reg) regulator_put(priv->vddhf_reg); destroy_workqueue(priv->workqueue); work_err: kfree(priv); return ret; }
static void twl6040_hs_jack_detect_dwork(struct work_struct *dwork) { struct twl6040_data *priv; struct snd_soc_codec *codec; struct twl6040_jack_data *jack; int status = 0; priv = container_of(dwork, struct twl6040_data, hsdet_dwork.work); codec = priv->codec; jack = &priv->hs_jack; /* * Early interrupt, CODEC driver cannot report jack status * since jack is not registered yet. MACHINE driver will * register jack and report status through twl6040_hs_jack_detect */ if (jack->jack) { #if defined(CONFIG_MACH_LGE_COSMO_REV_A) if(gpio_get_value(priv->hsjack_gpio)) #else if(jack->state) #endif { u8 val = 0; if( twl6040_i2c_read(TWL6040_REG_AMICBCTL, &val) == 0 ){ if( val != twl6040_read_reg_cache(codec, TWL6040_REG_AMICBCTL) ) twl6040_i2c_write(TWL6040_REG_AMICBCTL, twl6040_read_reg_cache(codec, TWL6040_REG_AMICBCTL)); } hs_set_bias(codec, 1); set_hook_enable(codec, 1); mdelay(200); if( twl6040_i2c_read(TWL6040_REG_MICLCTL, &val) == 0 ){ if( val != twl6040_read_reg_cache(codec, TWL6040_REG_MICLCTL) ) twl6040_i2c_write(TWL6040_REG_MICLCTL, twl6040_read_reg_cache(codec, TWL6040_REG_MICLCTL)); } if( twl6040_i2c_read(TWL6040_REG_MICRCTL, &val) == 0 ){ if( val != twl6040_read_reg_cache(codec, TWL6040_REG_MICRCTL) ) twl6040_i2c_write(TWL6040_REG_MICRCTL, twl6040_read_reg_cache(codec, TWL6040_REG_MICRCTL)); } if(is_without_mic()){ set_hook_enable(codec, 0); hs_set_bias(codec, 0); jack->state = WIRED_HEADPHONE;//wired headset without MIC status = SND_JACK_HEADPHONE; } else{ if(priv->intmask & TWL6040_HOOKMSK){ priv->intmask &= ~TWL6040_HOOKMSK; twl6040_write(codec, TWL6040_REG_INTMR, priv->intmask); } status = jack->report;//SND_JACK_HEADSET } } else{ if(!(priv->intmask & TWL6040_HOOKMSK)) { priv->intmask |= TWL6040_HOOKMSK; twl6040_write(codec, TWL6040_REG_INTMR, priv->intmask); } hs_set_bias(codec, 0); status = 0; } snd_soc_jack_report(jack->jack, status, jack->report); } switch_set_state(&jack->sdev, jack->state); }
static int twl6040_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int audpwron = priv->audpwron; int naudint = priv->naudint; int ret; switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (priv->codec_powered) break; if (gpio_is_valid(audpwron)) { /* use AUDPWRON line */ gpio_set_value(audpwron, 1); #if defined(CONFIG_MACH_LGE_COSMO_REV_C) mdelay(50); #endif /* wait for power-up completion */ ret = twl6040_power_up_completion(codec, naudint); if (ret) return ret; /* sync registers updated during power-up sequence */ twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); } else { /* use manual power-up sequence */ twl6040_power_up(codec); priv->codec_powered = 1; } /* initialize vdd/vss registers with reg_cache */ twl6040_init_vdd_regs(codec); break; case SND_SOC_BIAS_OFF: if (!priv->codec_powered) break; if (gpio_is_valid(audpwron)) { // Headset Left Driver make pop-noise when twl6040 enter into sleep mode. u8 val; val = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); if( val & 0x04 ) // temp disableed. It will be recovered when power-on. { twl6040_i2c_write(TWL6040_REG_HSLCTL, (val & (~0x04))); msleep(1); // delay;; } /* use AUDPWRON line */ gpio_set_value(audpwron, 0); /* power-down sequence latency */ mdelay(1); // more more... /* sync registers updated during power-down sequence */ twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, 0x00); } else { /* use manual power-down sequence */ twl6040_power_down(codec); } priv->codec_powered = 0; break; } codec->dapm->bias_level = level; return 0; }
static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; struct twl6040_jack_data *jack; int ret = 0; codec->control_data = dev_get_drvdata(codec->dev->parent); priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); if (!priv->workqueue) goto work_err; priv->hfl_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN); priv->hfr_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN); priv->hs_gain = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); INIT_DELAYED_WORK(&priv->delayed_work, twl6040_accessory_work); mutex_init(&priv->mutex); /* switch-class based headset detection */ jack = &priv->hs_jack; jack->sdev.name = "h2w"; ret = switch_dev_register(&jack->sdev); if (ret) { dev_err(codec->dev, "error registering switch device %d\n", ret); goto switch_err; } //--[[ LGE_UBIQUIX_MODIFIED_START : [email protected] : add from DCM_GB wake_lock_init(&priv->wake_lock, WAKE_LOCK_SUSPEND, "twl6040"); ret = twl6040_request_irq(codec->control_data, TWL6040_IRQ_PLUG, twl6040_audio_handler, IRQF_NO_SUSPEND, "twl6040_irq_plug", codec); //--[[ LGE_UBIQUIX_MODIFIED_END : [email protected] : add from DCM_GB if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); goto irq_err; } /* init vio registers */ twl6040_init_vio_regs(codec); /* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret) goto bias_err; snd_soc_add_controls(codec, twl6040_snd_controls, ARRAY_SIZE(twl6040_snd_controls)); twl6040_add_widgets(codec); /* TODO: read HS jack insertion status */ return 0; bias_err: twl6040_free_irq(codec->control_data, TWL6040_IRQ_PLUG, codec); irq_err: //--[[ LGE_UBIQUIX_MODIFIED_START : [email protected] : add from DCM_GB wake_lock_destroy(&priv->wake_lock); //--[[ LGE_UBIQUIX_MODIFIED_END : [email protected] : add from DCM_GB switch_dev_unregister(&jack->sdev); switch_err: destroy_workqueue(priv->workqueue); work_err: kfree(priv); return ret; }