static void twl6040_hs_jack_report(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int report) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int status, state; mutex_lock(&priv->mutex); /* Sync status */ status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS); if (status & TWL6040_PLUGCOMP) state = report; else state = 0; mutex_unlock(&priv->mutex); snd_soc_jack_report(jack, state, report); switch_set_state(&priv->hs_jack.sdev, !!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); /* 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)) { /* use AUDPWRON line */ gpio_set_value(audpwron, 0); /* power-down sequence latency */ udelay(500); /* 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->bias_level = level; return 0; }
static int twl6040_probe(struct snd_soc_codec *codec) { struct twl4030_codec_audio_data *twl_codec = codec->dev->platform_data; struct twl6040_data *priv; struct twl6040_jack_data *jack; int audpwron, naudint; struct input_dev *ip_dev; #if defined(CONFIG_MACH_LGE_COSMO_REV_A) unsigned hsjack_gpio, hsjack_irq; int err; #endif int ret = 0; u8 icrev = 0, intmr = TWL6040_ALLINT_MSK; int idx; for( idx = 0 ; idx < TWL6040_MUTE_DATA_MAX ; idx++ ) { s_mute_data[idx].dai = 0; s_mute_data[idx].mute = 0; } priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; priv->dl_active = 0; priv->ul_active = 0; twl6040_i2c_read(TWL6040_REG_ASICREV, &icrev); if (twl_codec && (icrev > 0)) audpwron = twl_codec->audpwron_gpio; else audpwron = -EINVAL; #if defined(CONFIG_MACH_LGE_COSMO_REV_A) if (twl_codec){ naudint = twl_codec->naudint_irq; hsjack_gpio = twl_codec->hsjack_gpio; hsjack_irq = twl_codec->hsjack_irq; } else { naudint = 0; hsjack_gpio = 0; hsjack_irq = 0; } #else if (twl_codec) naudint = twl_codec->naudint_irq; else naudint = 0; #endif priv->audpwron = audpwron; priv->naudint = naudint; #if defined(CONFIG_MACH_LGE_COSMO_REV_A) priv->hsjack_gpio = hsjack_gpio; priv->hsjack_irq = hsjack_irq; #endif init_completion(&priv->ready); /* Disable safe mode in SYS_NIRQ PAD */ // omap_writew(0x0118, 0x4A1001A0); INIT_DELAYED_WORK(&priv->hsdet_dwork, twl6040_hs_jack_detect_dwork); INIT_DELAYED_WORK(&priv->hook_work, twl6040_hs_hook_detect_work); #ifndef CONFIG_MACH_LGE_COSMOPOLITAN INIT_WORK(&priv->audint_work, twl6040_audint_work); #endif ip_dev = input_allocate_device(); if(!ip_dev){ dev_err(codec->dev, "failed to allocation hook input device"); goto switch_err; } __set_bit(EV_KEY, ip_dev->evbit); __set_bit(EV_SYN, ip_dev->evbit); __set_bit(KEY_HOOK, ip_dev->keybit); ip_dev->name = "headset_hook"; ip_dev->phys = "headset_hook/input0"; priv->hs_jack.headset_input = ip_dev; input_register_device(priv->hs_jack.headset_input); /* 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; } #if defined(CONFIG_MACH_LGE_COSMO_REV_A) /* GPIO request and direction set */ if(gpio_is_valid(hsjack_gpio)) { err = gpio_request(hsjack_gpio, "ear_sense"); if (err) { printk(KERN_ERR "%s: failed to request GPIO_%d\n", __func__, hsjack_gpio); goto err_hs_gpio_request; } err = gpio_direction_input(hsjack_gpio); if (err) { printk(KERN_ERR "%s: failed to set direction GPIO_%d\n", __func__, hsjack_gpio); goto err_hs_gpio_direction; } } /* IRQ request */ err = request_irq(hsjack_irq, hsjack_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "headset_detect", codec); if (err) { printk(KERN_ERR "%s: failed to request irq (%d)\n", __func__, hsjack_irq); goto err_hs_request_irq; } #endif if (gpio_is_valid(audpwron)) { ret = gpio_request(audpwron, "audpwron"); if (ret) goto gpio1_err; ret = gpio_direction_output(audpwron, 0); if (ret) goto gpio2_err; priv->codec_powered = 0; /* enable only codec ready interrupt */ intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK ); priv->intmask = intmr; /* reset interrupt status to allow correct power up sequence */ twl6040_read_reg_volatile(codec, TWL6040_REG_INTID); } twl6040_write(codec, TWL6040_REG_INTMR, intmr); if (naudint) { /* audio interrupt */ ret = request_threaded_irq(naudint, NULL, twl6040_naudint_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "twl6040_codec", codec); if (ret) goto gpio2_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); wake_lock_init(&priv->wake_lock, WAKE_LOCK_SUSPEND, "twl6040"); cdc_tcxo_set_req_int(CDC_TCXO_CLK3, 0); /* TODO: read HS jack insertion status */ return 0; bias_err: if (naudint) free_irq(naudint, codec); #if defined(CONFIG_MACH_LGE_COSMO_REV_A) err_hs_request_irq: err_hs_gpio_direction: if (gpio_is_valid(hsjack_gpio)) gpio_free(hsjack_gpio); err_hs_gpio_request: #endif gpio2_err: if (gpio_is_valid(audpwron)) gpio_free(audpwron); gpio1_err: switch_dev_unregister(&jack->sdev); switch_err: kfree(priv); return ret; }
void twl6040_hs_jack_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int report) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int status, state; priv->hs_jack.jack = jack; priv->hs_jack.report = report; #if !defined(CONFIG_MACH_LGE_COSMO_REV_A) /* Sync status */ status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS); #if defined(CONFIG_MACH_LGE_COSMOPOLITAN) if(status & TWL6040_PLUGCOMP) priv->hs_jack.state = HEADSET_NONE; else priv->hs_jack.state = WIRED_HEADSET; #else if(status & TWL6040_PLUGCOMP) state = report; else state = 0; #endif #endif// !CONFIG_MACH_LGE_COSMO_REV_A #if defined(CONFIG_MACH_LGE_COSMO_REV_A) if(gpio_get_value(priv->hsjack_gpio)) #else if(priv->hs_jack.state) #endif { hs_set_bias(codec, 1); set_hook_enable(codec, 1); mdelay(200); if(is_without_mic()){ set_hook_enable(codec, 0); hs_set_bias(codec, 0); state = WIRED_HEADPHONE;//wired headset without MIC status = SND_JACK_HEADPHONE; } else{ /* for wired_headset */ if(priv->intmask & TWL6040_HOOKMSK){ priv->intmask &= ~TWL6040_HOOKMSK; twl6040_write(codec, TWL6040_REG_INTMR, priv->intmask); } state = WIRED_HEADSET;//wired headset with MIC status = priv->hs_jack.report;//SND_JACK_HEADSET } } else{ /* when headset removed */ if(!(priv->intmask & TWL6040_HOOKMSK)) { priv->intmask |= TWL6040_HOOKMSK; twl6040_write(codec, TWL6040_REG_INTMR, priv->intmask); } set_hook_enable(codec, 0); hs_set_bias(codec, 0); state = HEADSET_NONE; status = 0; } switch_set_state(&priv->hs_jack.sdev, state); snd_soc_jack_report(jack, status, report); }
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; }