static int wm8350_codec_io_probe(struct snd_soc_codec *codec, struct snd_soc_machine *machine) { struct wm8350* wm8350 = codec->control_data; struct wm8350_out_ramp *or = codec->private_data; struct wm8350_output *out1 = &or->out1, *out2 = &or->out2; snd_assert(wm8350 != NULL, return -EINVAL); /* reset codec */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); /* enable clock gen - lg need to move in new silicon */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_SYSCLK_ENA); /* charge output caps */ codec->dapm_state = SNDRV_CTL_POWER_D3cold; wm8350_dapm_event(codec, SNDRV_CTL_POWER_D3hot); wm8350_add_controls(codec, machine->card); wm8350_add_widgets(codec, machine); /* read OUT1 & OUT2 volumes */ out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0); wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU); wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0); wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU); return 0; }
int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp, u16 drive) { switch (isink) { case WM8350_ISINK_A: wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL, (mode ? WM8350_CS1_FLASH_MODE : 0) | (trigger ? WM8350_CS1_TRIGSRC : 0) | duration | on_ramp | off_ramp | drive); break; case WM8350_ISINK_B: wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL, (mode ? WM8350_CS2_FLASH_MODE : 0) | (trigger ? WM8350_CS2_TRIGSRC : 0) | duration | on_ramp | off_ramp | drive); break; default: return -EINVAL; } return 0; }
static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); int dcdc = rdev_get_id(rdev); u16 val; switch (dcdc) { case WM8350_DCDC_1: val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_3: val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_4: val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_6: val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, val | WM8350_DCDC_HIB_MODE_DIS); break; case WM8350_DCDC_2: case WM8350_DCDC_5: default: return -EINVAL; } return 0; }
static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); int volt_reg, ldo = rdev_get_id(rdev), mV, min_mV = min_uV / 1000, max_mV = max_uV / 1000; u16 val; if (min_mV < 900 || min_mV > 3300) return -EINVAL; if (max_mV < 900 || max_mV > 3300) return -EINVAL; if (min_mV < 1800) { /* step size is 50mV < 1800mV */ mV = (min_mV - 851) / 50; if (wm8350_ldo_val_to_mvolts(mV) > max_mV) return -EINVAL; BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); } else { /* step size is 100mV > 1800mV */ mV = ((min_mV - 1701) / 100) + 16; if (wm8350_ldo_val_to_mvolts(mV) > max_mV) return -EINVAL; BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); } switch (ldo) { case WM8350_LDO_1: volt_reg = WM8350_LDO1_CONTROL; break; case WM8350_LDO_2: volt_reg = WM8350_LDO2_CONTROL; break; case WM8350_LDO_3: volt_reg = WM8350_LDO3_CONTROL; break; case WM8350_LDO_4: volt_reg = WM8350_LDO4_CONTROL; break; default: return -EINVAL; } *selector = mV; /* all LDOs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; wm8350_reg_write(wm8350, volt_reg, val | mV); return 0; }
static int wm8350_wdt_kick(struct wm8350 *wm8350) { int ret; u16 reg; mutex_lock(&wdt_mutex); reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); mutex_unlock(&wdt_mutex); return ret; }
static int wm8350_wdt_ping(struct watchdog_device *wdt_dev) { struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); int ret; u16 reg; mutex_lock(&wdt_mutex); reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); mutex_unlock(&wdt_mutex); return ret; }
static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); int dcdc = rdev_get_id(rdev); u16 val; switch (dcdc) { case WM8350_DCDC_2: val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) & ~WM8350_DC2_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | (WM8350_DC2_HIB_MODE_DISABLE << WM8350_DC2_HIB_MODE_SHIFT)); break; case WM8350_DCDC_5: val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) & ~WM8350_DC5_HIB_MODE_MASK; wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | (WM8350_DC5_HIB_MODE_DISABLE << WM8350_DC5_HIB_MODE_SHIFT)); break; default: return -EINVAL; } return 0; }
static int wm8350_rtc_set_pie(struct wm8350_rtc *wm_rtc, unsigned long freq) { struct wm8350 *wm8350 = to_wm8350_from_rtc(wm_rtc); int ret, power; u16 reg; power = ffs(freq); if ((power == fls(freq)) && freq < 1025) { wm_rtc->pie_freq = freq; reg = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); reg &= ~WM8350_RTC_DSW_MASK; reg |= (power + 1) << WM8350_RTC_DSW_SHIFT; ret = wm8350_reg_write(wm8350, WM8350_RTC_TIME_CONTROL, reg); return ret; } return -EINVAL; }
int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, u16 stop, u16 fault) { int slot_reg; u16 val; dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", __func__, dcdc, start, stop); /* slot valid ? */ if (start > 15 || stop > 15) return -EINVAL; switch (dcdc) { case WM8350_DCDC_1: slot_reg = WM8350_DCDC1_TIMEOUTS; break; case WM8350_DCDC_2: slot_reg = WM8350_DCDC2_TIMEOUTS; break; case WM8350_DCDC_3: slot_reg = WM8350_DCDC3_TIMEOUTS; break; case WM8350_DCDC_4: slot_reg = WM8350_DCDC4_TIMEOUTS; break; case WM8350_DCDC_5: slot_reg = WM8350_DCDC5_TIMEOUTS; break; case WM8350_DCDC_6: slot_reg = WM8350_DCDC6_TIMEOUTS; break; default: return -EINVAL; } val = wm8350_reg_read(wm8350, slot_reg) & ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK | WM8350_DC1_ERRACT_MASK); wm8350_reg_write(wm8350, slot_reg, val | (start << WM8350_DC1_ENSLOT_SHIFT) | (stop << WM8350_DC1_SDSLOT_SHIFT) | (fault << WM8350_DC1_ERRACT_SHIFT)); return 0; }
static int wm8350_wdt_stop(struct wm8350 *wm8350) { int ret; u16 reg; mutex_lock(&wdt_mutex); wm8350_reg_unlock(wm8350); reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); reg &= ~WM8350_WDOG_MODE_MASK; ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); wm8350_reg_lock(wm8350); mutex_unlock(&wdt_mutex); return ret; }
static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); int volt_reg, dcdc = rdev_get_id(rdev), mV, min_mV = min_uV / 1000, max_mV = max_uV / 1000; u16 val; if (min_mV < 850 || min_mV > 4025) return -EINVAL; if (max_mV < 850 || max_mV > 4025) return -EINVAL; /* step size is 25mV */ mV = (min_mV - 826) / 25; if (wm8350_dcdc_val_to_mvolts(mV) > max_mV) return -EINVAL; BUG_ON(wm8350_dcdc_val_to_mvolts(mV) < min_mV); switch (dcdc) { case WM8350_DCDC_1: volt_reg = WM8350_DCDC1_CONTROL; break; case WM8350_DCDC_3: volt_reg = WM8350_DCDC3_CONTROL; break; case WM8350_DCDC_4: volt_reg = WM8350_DCDC4_CONTROL; break; case WM8350_DCDC_6: volt_reg = WM8350_DCDC6_CONTROL; break; case WM8350_DCDC_2: case WM8350_DCDC_5: default: return -EINVAL; } *selector = mV; /* all DCDCs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; wm8350_reg_write(wm8350, volt_reg, val | mV); return 0; }
static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value) { int ret; u16 reg; mutex_lock(&wdt_mutex); wm8350_reg_unlock(wm8350); reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); reg &= ~WM8350_WDOG_TO_MASK; reg |= value; ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); wm8350_reg_lock(wm8350); mutex_unlock(&wdt_mutex); return ret; }
static int wm8350_wdt_stop(struct watchdog_device *wdt_dev) { struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); int ret; u16 reg; mutex_lock(&wdt_mutex); wm8350_reg_unlock(wm8350); reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); reg &= ~WM8350_WDOG_MODE_MASK; ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); wm8350_reg_lock(wm8350); mutex_unlock(&wdt_mutex); return ret; }
static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); int volt_reg, mV = uV / 1000, dcdc = rdev_get_id(rdev); u16 val; dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, mV); if (mV && (mV < 850 || mV > 4025)) { dev_err(wm8350->dev, "DCDC%d suspend voltage %d mV out of range\n", dcdc, mV); return -EINVAL; } if (mV == 0) mV = 850; switch (dcdc) { case WM8350_DCDC_1: volt_reg = WM8350_DCDC1_LOW_POWER; break; case WM8350_DCDC_3: volt_reg = WM8350_DCDC3_LOW_POWER; break; case WM8350_DCDC_4: volt_reg = WM8350_DCDC4_LOW_POWER; break; case WM8350_DCDC_6: volt_reg = WM8350_DCDC6_LOW_POWER; break; case WM8350_DCDC_2: case WM8350_DCDC_5: default: return -EINVAL; } /* all DCDCs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; wm8350_reg_write(wm8350, volt_reg, val | wm8350_dcdc_mvolts_to_val(mV)); return 0; }
static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); int volt_reg, mV = uV / 1000, ldo = rdev_get_id(rdev); u16 val; dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, mV); if (mV < 900 || mV > 3300) { dev_err(wm8350->dev, "LDO%d voltage %d mV out of range\n", ldo, mV); return -EINVAL; } switch (ldo) { case WM8350_LDO_1: volt_reg = WM8350_LDO1_LOW_POWER; break; case WM8350_LDO_2: volt_reg = WM8350_LDO2_LOW_POWER; break; case WM8350_LDO_3: volt_reg = WM8350_LDO3_LOW_POWER; break; case WM8350_LDO_4: volt_reg = WM8350_LDO4_LOW_POWER; break; default: return -EINVAL; } /* all LDOs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; wm8350_reg_write(wm8350, volt_reg, val | wm8350_ldo_mvolts_to_val(mV)); return 0; }
static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); int sel, volt_reg, dcdc = rdev_get_id(rdev); u16 val; dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, uV / 1000); switch (dcdc) { case WM8350_DCDC_1: volt_reg = WM8350_DCDC1_LOW_POWER; break; case WM8350_DCDC_3: volt_reg = WM8350_DCDC3_LOW_POWER; break; case WM8350_DCDC_4: volt_reg = WM8350_DCDC4_LOW_POWER; break; case WM8350_DCDC_6: volt_reg = WM8350_DCDC6_LOW_POWER; break; case WM8350_DCDC_2: case WM8350_DCDC_5: default: return -EINVAL; } sel = regulator_map_voltage_linear(rdev, uV, uV); if (sel < 0) return sel; /* all DCDCs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; wm8350_reg_write(wm8350, volt_reg, val | sel); return 0; }
static int wm8350_codec_probe(struct snd_soc_codec *codec) { struct wm8350 *wm8350 = dev_get_platdata(codec->dev); struct wm8350_data *priv; struct wm8350_output *out1; struct wm8350_output *out2; int ret, i; if (wm8350->codec.platform_data == NULL) { dev_err(codec->dev, "No audio platform data supplied\n"); return -EINVAL; } priv = kzalloc(sizeof(struct wm8350_data), GFP_KERNEL); if (priv == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); for (i = 0; i < ARRAY_SIZE(supply_names); i++) priv->supplies[i].supply = supply_names[i]; ret = regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies), priv->supplies); if (ret != 0) goto err_priv; wm8350->codec.codec = codec; codec->control_data = wm8350; /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work); /* Enable the codec */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); /* Enable robust clocking mode in ADC */ wm8350_codec_write(codec, WM8350_SECURITY, 0xa7); wm8350_codec_write(codec, 0xde, 0x13); wm8350_codec_write(codec, WM8350_SECURITY, 0); /* read OUT1 & OUT2 volumes */ out1 = &priv->out1; out2 = &priv->out2; out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0); /* Latch VU bits & mute */ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1L_MUTE); wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2L_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1R_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2R_MUTE); /* Make sure jack detect is disabled to start off with */ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, WM8350_JDL_ENA | WM8350_JDR_ENA); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, wm8350_hp_jack_handler, 0, "Left jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, wm8350_hp_jack_handler, 0, "Right jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, wm8350_mic_handler, 0, "Microphone short", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD, wm8350_mic_handler, 0, "Microphone detect", priv); snd_soc_add_controls(codec, wm8350_snd_controls, ARRAY_SIZE(wm8350_snd_controls)); wm8350_add_widgets(codec); wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; err_priv: kfree(priv); return ret; }
static int gpio_set_func(struct wm8350 *wm8350, int gpio, int func) { u16 reg; wm8350_reg_unlock(wm8350); switch (gpio) { case 0: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) & ~WM8350_GP0_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, reg | ((func & 0xf) << 0)); break; case 1: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) & ~WM8350_GP1_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, reg | ((func & 0xf) << 4)); break; case 2: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) & ~WM8350_GP2_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, reg | ((func & 0xf) << 8)); break; case 3: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) & ~WM8350_GP3_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, reg | ((func & 0xf) << 12)); break; case 4: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) & ~WM8350_GP4_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, reg | ((func & 0xf) << 0)); break; case 5: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) & ~WM8350_GP5_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, reg | ((func & 0xf) << 4)); break; case 6: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) & ~WM8350_GP6_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, reg | ((func & 0xf) << 8)); break; case 7: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) & ~WM8350_GP7_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, reg | ((func & 0xf) << 12)); break; case 8: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) & ~WM8350_GP8_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, reg | ((func & 0xf) << 0)); break; case 9: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) & ~WM8350_GP9_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, reg | ((func & 0xf) << 4)); break; case 10: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) & ~WM8350_GP10_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, reg | ((func & 0xf) << 8)); break; case 11: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) & ~WM8350_GP11_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, reg | ((func & 0xf) << 12)); break; case 12: reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_4) & ~WM8350_GP12_FN_MASK; wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_4, reg | ((func & 0xf) << 0)); break; default: wm8350_reg_lock(wm8350); return -EINVAL; } wm8350_reg_lock(wm8350); return 0; }
//static int wm8350_init(struct wm8350 *wm8350) int wm8350_dev_init(struct wm8350 *wm8350) { int i, ret; u16 data; #if 0 /* dont assert RTS when hibernating */ wm8350_set_bits(wm8350, WM8350_SYSTEM_HIBERNATE, WM8350_RST_HIB_MODE); #endif wm8350_reg_unlock(wm8350); wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1, WM8350_IRQ_POL); wm8350_reg_lock(wm8350); s3c2410_gpio_pullup(S3C2410_GPF1, 0); s3c2410_gpio_cfgpin(S3C2410_GPF1, S3C2410_GPF1_EINT1); // set_irq_type(IRQ_EINT1, IRQT_BOTHEDGE); s3c2410_gpio_pullup(S3C2410_GPF2, 0); s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2); /* Shutdown threshold value 3.1v off , 3.2v on */ wm8350_reg_unlock(wm8350); data = wm8350_reg_read(wm8350, WM8350_POWER_CHECK_COMPARATOR) & ~(WM8350_PCCMP_OFF_THR_MASK | WM8350_PCCMP_ON_THR_MASK); wm8350_reg_write(wm8350, WM8350_POWER_CHECK_COMPARATOR, data | 0x23); wm8350_reg_lock(wm8350); data = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_2); wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_2, data | WM8350_AUXADC_CAL); config_s3c_wm8350_gpio(wm8350); #if 0 /* Sw1 --> PWR_ON */ wm8350_register_irq(wm8350, WM8350_IRQ_WKUP_ONKEY, imx32ads_switch_handler, NULL); wm8350_unmask_irq(wm8350, WM8350_IRQ_WKUP_ONKEY); #endif #ifndef CONFIG_MACH_CANOPUS for (i = 0; i < ARRAY_SIZE(wm8350_regulator_devices); i++) { platform_set_drvdata(&wm8350_regulator_devices[i], wm8350); ret = platform_device_register(&wm8350_regulator_devices[i]); if (ret < 0) goto unwind; } #else // CONFIG_MACH_CANOPUS struct regulator_init_data *reg_data = NULL; for (i = 0; i < ARRAY_SIZE(wm8350_regulator_devices); i++) { if (wm8350_regulator_devices[i].id == WM8350_DCDC_4) { // for LCD if (q_hw_ver(7800_ES2) || q_hw_ver(7800_TP) || q_hw_ver(7800_MP)) { reg_data = (struct regulator_init_data *)wm8350_regulator_devices[i].dev.platform_data; reg_data->constraints.min_uV = 3400000; reg_data->constraints.max_uV = 3400000; reg_data->constraints.state_mem.uV = 3400000; } } else if (wm8350_regulator_devices[i].id == WM8350_LDO_3) { // for PMIC LDO if (q_hw_ver(SWP2000) || q_hw_ver(7800_MP2) || q_hw_ver(KTQOOK_TP2) || q_hw_ver(KTQOOK_MP) || q_hw_ver(SKATM)) { reg_data = (struct regulator_init_data *)wm8350_regulator_devices[i].dev.platform_data; reg_data->constraints.min_uV = 1200000; reg_data->constraints.max_uV = 1200000; reg_data->num_consumer_supplies = ARRAY_SIZE(ldo4_consumers); reg_data->consumer_supplies = ldo4_consumers; } } else if (wm8350_regulator_devices[i].id == WM8350_LDO_4) { // for PMIC LDO if (q_hw_ver(SWP2000) || q_hw_ver(7800_MP2) || q_hw_ver(KTQOOK_TP2) || q_hw_ver(KTQOOK_MP) || q_hw_ver(SKATM)) { reg_data = (struct regulator_init_data *)wm8350_regulator_devices[i].dev.platform_data; reg_data->constraints.min_uV = 3300000; reg_data->constraints.max_uV = 3300000; reg_data->num_consumer_supplies = ARRAY_SIZE(ldo3_consumers); reg_data->consumer_supplies = ldo3_consumers; } } platform_set_drvdata(&wm8350_regulator_devices[i], wm8350); ret = platform_device_register(&wm8350_regulator_devices[i]); if (ret < 0) goto unwind; } #endif // CONFIG_MACH_CANOPUS /* now register other clients */ return s3c_wm8350_device_register(wm8350); unwind: for (i--; i >= 0; i--) platform_device_unregister(&wm8350_regulator_devices[i]); return ret; }
int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { int ret; u16 id1, id2, mask_rev; u16 cust_id, mode, chip_rev; /* get WM8350 revision and config mode */ ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); goto err; } ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); goto err; } ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), &mask_rev); if (ret != 0) { dev_err(wm8350->dev, "Failed to read revision: %d\n", ret); goto err; } id1 = be16_to_cpu(id1); id2 = be16_to_cpu(id2); mask_rev = be16_to_cpu(mask_rev); if (id1 != 0x6143) { dev_err(wm8350->dev, "Device with ID %x is not a WM8350\n", id1); ret = -ENODEV; goto err; } mode = (id2 & WM8350_CONF_STS_MASK) >> 10; cust_id = id2 & WM8350_CUST_ID_MASK; chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12; dev_info(wm8350->dev, "CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d\n", mode, cust_id, mask_rev, chip_rev); if (cust_id != 0) { dev_err(wm8350->dev, "Unsupported CUST_ID\n"); ret = -ENODEV; goto err; } switch (mask_rev) { case 0: wm8350->pmic.max_dcdc = WM8350_DCDC_6; wm8350->pmic.max_isink = WM8350_ISINK_B; switch (chip_rev) { case WM8350_REV_E: dev_info(wm8350->dev, "WM8350 Rev E\n"); break; case WM8350_REV_F: dev_info(wm8350->dev, "WM8350 Rev F\n"); break; case WM8350_REV_G: dev_info(wm8350->dev, "WM8350 Rev G\n"); wm8350->power.rev_g_coeff = 1; break; case WM8350_REV_H: dev_info(wm8350->dev, "WM8350 Rev H\n"); wm8350->power.rev_g_coeff = 1; wm8350->power.policy = &pavo_charger_policy; break; default: /* For safety we refuse to run on unknown hardware */ dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV\n"); ret = -ENODEV; goto err; } break; case 1: wm8350->pmic.max_dcdc = WM8350_DCDC_4; wm8350->pmic.max_isink = WM8350_ISINK_A; switch (chip_rev) { case 0: dev_info(wm8350->dev, "WM8351 Rev A\n"); wm8350->power.rev_g_coeff = 1; break; case 1: dev_info(wm8350->dev, "WM8351 Rev B\n"); wm8350->power.rev_g_coeff = 1; break; default: dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV\n"); ret = -ENODEV; goto err; } break; case 2: wm8350->pmic.max_dcdc = WM8350_DCDC_6; wm8350->pmic.max_isink = WM8350_ISINK_B; switch (chip_rev) { case 0: dev_info(wm8350->dev, "WM8352 Rev A\n"); wm8350->power.rev_g_coeff = 1; break; default: dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV\n"); ret = -ENODEV; goto err; } break; default: dev_err(wm8350->dev, "Unknown MASK_REV\n"); ret = -ENODEV; goto err; } ret = wm8350_create_cache(wm8350, mask_rev, mode); if (ret < 0) { dev_err(wm8350->dev, "Failed to create register cache\n"); return ret; } wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF); wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF); wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF); wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF); wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF); wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF); wm8350_set_bits(wm8350,0xb0,0x1<<0x1); wm8350_set_bits(wm8350,0xb8,0x1<<10); mutex_init(&wm8350->auxadc_mutex); mutex_init(&wm8350->irq_mutex); INIT_WORK(&wm8350->irq_work, wm8350_irq_worker); if (irq) { ret = request_irq(irq, wm8350_irq, 0, "wm8350", wm8350); if (ret != 0) { dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret); goto err; } } else { dev_err(wm8350->dev, "No IRQ configured\n"); goto err; } wm8350->chip_irq = irq; if (pdata && pdata->init) { ret = pdata->init(wm8350); if (ret != 0) { dev_err(wm8350->dev, "Platform init() failed: %d\n", ret); goto err; } } wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); wm8350_client_dev_register(wm8350, "wm8350-codec", &(wm8350->codec.pdev)); wm8350_client_dev_register(wm8350, "wm8350-gpio", &(wm8350->gpio.pdev)); wm8350_client_dev_register(wm8350, "wm8350-power", &(wm8350->power.pdev)); wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev)); wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev)); return 0; err: kfree(wm8350->reg_cache); return ret; }
static int wm8350_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec; struct wm8350 *wm8350; struct wm8350_data *priv; int ret; struct wm8350_output *out1; struct wm8350_output *out2; BUG_ON(!wm8350_codec); socdev->card->codec = wm8350_codec; codec = socdev->card->codec; wm8350 = codec->control_data; priv = codec->private_data; /* Enable the codec */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); /* Enable robust clocking mode in ADC */ wm8350_codec_write(codec, WM8350_SECURITY, 0xa7); wm8350_codec_write(codec, 0xde, 0x13); wm8350_codec_write(codec, WM8350_SECURITY, 0); /* read OUT1 & OUT2 volumes */ out1 = &priv->out1; out2 = &priv->out2; out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0); wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0); /* Latch VU bits & mute */ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1L_MUTE); wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2L_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME, WM8350_OUT1_VU | WM8350_OUT1R_MUTE); wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2R_MUTE); wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, wm8350_hp_jack_handler, priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, wm8350_hp_jack_handler, priv); ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { dev_err(&pdev->dev, "failed to create pcms\n"); return ret; } snd_soc_add_controls(codec, wm8350_snd_controls, ARRAY_SIZE(wm8350_snd_controls)); wm8350_add_widgets(codec); wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; }
static int wm8350_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8350 *wm8350 = codec->control_data; struct wm8350_data *priv = codec->private_data; struct wm8350_audio_platform_data *platform = wm8350->codec.platform_data; u16 pm1; int ret; switch (level) { case SND_SOC_BIAS_ON: pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_VMID_50K | platform->codec_current_on << 14); break; case SND_SOC_BIAS_PREPARE: pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1); pm1 &= ~WM8350_VMID_MASK; wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_VMID_50K); break; case SND_SOC_BIAS_STANDBY: if (codec->bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); if (ret != 0) return ret; /* Enable the system clock */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_SYSCLK_ENA); /* mute DAC & outputs */ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); /* discharge cap memory */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, platform->dis_out1 | (platform->dis_out2 << 2) | (platform->dis_out3 << 4) | (platform->dis_out4 << 6)); /* wait for discharge */ schedule_timeout_interruptible(msecs_to_jiffies (platform-> cap_discharge_msecs)); /* enable antipop */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, (platform->vmid_s_curve << 8)); /* ramp up vmid */ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, (platform-> codec_current_charge << 14) | WM8350_VMID_5K | WM8350_VMIDEN | WM8350_VBUFEN); /* wait for vmid */ schedule_timeout_interruptible(msecs_to_jiffies (platform-> vmid_charge_msecs)); /* turn on vmid 300k */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); pm1 |= WM8350_VMID_300K | (platform->codec_current_standby << 14); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); /* enable analogue bias */ pm1 |= WM8350_BIASEN; wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); /* disable antipop */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0); } else { /* turn on vmid 300k and reduce current */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_VMID_300K | (platform-> codec_current_standby << 14)); } break; case SND_SOC_BIAS_OFF: /* mute DAC & enable outputs */ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3, WM8350_OUT1L_ENA | WM8350_OUT1R_ENA | WM8350_OUT2L_ENA | WM8350_OUT2R_ENA); /* enable anti pop S curve */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, (platform->vmid_s_curve << 8)); /* turn off vmid */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~WM8350_VMIDEN; wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); /* wait */ schedule_timeout_interruptible(msecs_to_jiffies (platform-> vmid_discharge_msecs)); wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, (platform->vmid_s_curve << 8) | platform->dis_out1 | (platform->dis_out2 << 2) | (platform->dis_out3 << 4) | (platform->dis_out4 << 6)); /* turn off VBuf and drain */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VBUFEN | WM8350_VMID_MASK); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_OUTPUT_DRAIN_EN); /* wait */ schedule_timeout_interruptible(msecs_to_jiffies (platform->drain_msecs)); pm1 &= ~WM8350_BIASEN; wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); /* disable anti-pop */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0); wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1L_ENA); wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME, WM8350_OUT1R_ENA); wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2L_ENA); wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME, WM8350_OUT2R_ENA); /* disable clock gen */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_SYSCLK_ENA); regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); break; } codec->bias_level = level; return 0; }
/* * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown. */ static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec) { struct wm8350_out_ramp *or = codec->private_data; struct wm8350_output *output = &or->out1; struct wm8350 *wm8350 = codec->control_data; int left_complete = 0, right_complete = 0; u16 reg, val; /* left channel */ reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME); val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; if (output->ramp == WM8350_RAMP_UP) { /* ramp step up */ if (val < output->left_vol) { val++; reg &= ~WM8350_OUT1L_VOL_MASK; wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, reg | (val << WM8350_OUT1L_VOL_SHIFT)); } else left_complete = 1; } else if (output->ramp == WM8350_RAMP_DOWN) { /* ramp step down */ if (val > 0) { val--; reg &= ~WM8350_OUT1L_VOL_MASK; wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, reg | (val << WM8350_OUT1L_VOL_SHIFT)); } else left_complete = 1; } else return 1; /* right channel */ reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME); val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; if (output->ramp == WM8350_RAMP_UP) { /* ramp step up */ if (val < output->right_vol) { val++; reg &= ~WM8350_OUT1R_VOL_MASK; wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, reg | (val << WM8350_OUT1R_VOL_SHIFT)); } else right_complete = 1; } else if (output->ramp == WM8350_RAMP_DOWN) { /* ramp step down */ if (val > 0) { val--; reg &= ~WM8350_OUT1R_VOL_MASK; wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, reg | (val << WM8350_OUT1R_VOL_SHIFT)); } else right_complete = 1; } /* only hit the update bit if either volume has changed this step */ if (!left_complete || !right_complete) wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU); return left_complete & right_complete; }
static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { struct wm8350* wm8350 = codec->control_data; return wm8350_reg_write(wm8350, reg, value); }
int wm8350_init(struct wm8350 *wm8350) { int ret = 0; /* register regulator and set constraints */ wm8350_device_register_pmic(wm8350); set_regulator_constraints(wm8350); #ifdef NOT_PORTED_TO_IMX37 wm8350_device_register_rtc(wm8350); wm8350_device_register_wdg(wm8350); wm8350_device_register_power(wm8350); #endif mxc_init_wm8350(); /*Note: Needs to be moved into a regulator function. */ /* Configuring -- GPIO 7 pin */ if (wm8350_gpio_config(wm8350, 7, WM8350_GPIO_DIR_OUT, 0, WM8350_GPIO_ACTIVE_LOW, WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, WM8350_GPIO_DEBOUNCE_OFF) == 0) wm8350_gpio_set_status(wm8350, 7, 1); else printk(KERN_ERR "Error in setting Wolfson GPIO pin 7 \n"); /* enable gpio4:USB_VBUS_EN */ ret = wm8350_gpio_config(wm8350, 4, WM8350_GPIO_DIR_IN, WM8350_GPIO4_MR_IN, WM8350_GPIO_ACTIVE_HIGH, WM8350_GPIO_PULL_UP, WM8350_GPIO_INVERT_OFF, WM8350_GPIO_DEBOUNCE_OFF); if (ret) printk(KERN_ERR "Error in setting USB VBUS enable pin\n"); /*PMIC RDY*/ if (wm8350_gpio_config(wm8350, 9, WM8350_GPIO_DIR_OUT, WM8350_GPIO9_GPIO_OUT, WM8350_GPIO_ACTIVE_LOW, WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, WM8350_GPIO_DEBOUNCE_OFF) == 0) wm8350_gpio_set_status(wm8350, 9, 1); else printk(KERN_ERR "Error in setting Wolfson GPIO pin 9 \n"); /* register sound */ printk("Registering imx37_snd_device"); imx_snd_device = platform_device_alloc("wm8350-imx-3stack-audio", -1); if (!imx_snd_device) { ret = -ENOMEM; goto err; } imx_snd_device->dev.platform_data = &imx_3stack_audio_platform_data; platform_set_drvdata(imx_snd_device, &wm8350->audio); ret = platform_device_add(imx_snd_device); if (ret) goto snd_err; /* set up PMIC IRQ (active high) to i.MX32ADS */ printk("Registering PMIC INT"); INIT_WORK(&wm8350->work, wm8350_irq_work); wm8350_reg_unlock(wm8350); wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1, WM8350_IRQ_POL); wm8350_reg_lock(wm8350); set_irq_type(MXC_PMIC_INT_LINE, IRQT_RISING); ret = request_irq(MXC_PMIC_INT_LINE, wm8350_irq_handler, IRQF_DISABLED, "wm8350-pmic", wm8350); if (ret != 0) { printk(KERN_ERR "wm8350: cant request irq %d\n", MXC_PMIC_INT_LINE); goto err; } wm8350->nirq = MXC_PMIC_INT_LINE; set_irq_wake(MXC_PMIC_INT_LINE, 1); #ifdef NOT_PORTED_TO_IMX37 printk("Configuring WM8350 GPIOS"); config_gpios(wm8350); config_hibernate(wm8350); #endif /* Sw1 --> PWR_ON */ printk("Registering and unmasking the WM8350 wakeup key\n"); wm8350_register_irq(wm8350, WM8350_IRQ_WKUP_ONKEY, imx37_3stack_switch_handler, NULL); wm8350_unmask_irq(wm8350, WM8350_IRQ_WKUP_ONKEY); /* unmask all & clear sticky */ printk("Unmasking WM8350 local interrupts\n"); wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x3ffe); schedule_work(&wm8350->work); #if BATTERY /* not much use without a battery atm */ wm8350_init_battery(wm8350); #endif printk("Exiting normally from wm8350_init()"); return ret; snd_err: platform_device_put(imx_snd_device); err: printk("wm8350_init() FAILED"); kfree(wm8350->reg_cache); return ret; }
static int wm8350_dapm_event(struct snd_soc_codec *codec, int event) { struct wm8350 *wm8350 = codec->control_data; struct wm8350_audio_platform_data *platform = codec->platform_data; u16 pm1; snd_assert(wm8350 != NULL, return -EINVAL); snd_assert(platform != NULL, return -EINVAL); switch (event) { case SNDRV_CTL_POWER_D0: /* full On */ /* set vmid to 10k and current to 1.0x */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_VMID_10K | platform->codec_current_d0 << 14); break; case SNDRV_CTL_POWER_D1: /* partial On */ case SNDRV_CTL_POWER_D2: /* partial On */ /* set vmid to 40k for quick power up */ if (codec->dapm_state == SNDRV_CTL_POWER_D3hot) { /* D3hot --> D0 */ /* enable bias */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_BIASEN); } break; case SNDRV_CTL_POWER_D3hot: /* Off, with power */ if (codec->dapm_state == SNDRV_CTL_POWER_D3cold) { /* D3cold --> D3hot */ /* mute DAC & outputs */ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); /* discharge cap memory */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, platform->dis_out1 | (platform->dis_out2 << 2) | (platform->dis_out3 << 4) | (platform->dis_out4 << 6)); /* wait for discharge */ schedule_timeout_interruptible(msecs_to_jiffies( platform->cap_discharge_msecs)); /* enable antipop */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, (platform->vmid_s_curve << 8)); /* ramp up vmid */ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, (platform->codec_current_charge << 14) | WM8350_VMID_10K | WM8350_VMIDEN | WM8350_VBUFEN); /* wait for vmid */ schedule_timeout_interruptible(msecs_to_jiffies( platform->vmid_charge_msecs)); /* turn on vmid 500k */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_VMID_500K | (platform->codec_current_d3 << 14)); /* disable antipop */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0); } else { /* D1,D2 --> D3hot */ /* turn on vmid 500k and reduce current */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_VMID_500K | (platform->codec_current_d3 << 14)); /* disable bias */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~WM8350_BIASEN; wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); } break; case SNDRV_CTL_POWER_D3cold: /* Off, without power */ /* mute DAC & enable outputs */ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3, WM8350_OUT1L_ENA | WM8350_OUT1R_ENA | WM8350_OUT2L_ENA | WM8350_OUT2R_ENA); /* enable anti pop S curve */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, (platform->vmid_s_curve << 8)); /* turn off vmid */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~WM8350_VMIDEN; wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); /* wait */ schedule_timeout_interruptible(msecs_to_jiffies( platform->vmid_discharge_msecs)); wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, (platform->vmid_s_curve << 8) | platform->dis_out1 | (platform->dis_out2 << 2) | (platform->dis_out3 << 4) | (platform->dis_out4 << 6)); /* turn off VBuf and drain */ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & ~(WM8350_VBUFEN | WM8350_VMID_MASK); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1 | WM8350_OUTPUT_DRAIN_EN); /* wait */ schedule_timeout_interruptible(msecs_to_jiffies( platform->drain_msecs)); /* disable anti-pop */ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0); wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1L_ENA); wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME, WM8350_OUT1R_ENA); wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2L_ENA); wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME, WM8350_OUT2R_ENA); /* disable clock gen */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_SYSCLK_ENA); break; } codec->dapm_state = event; return 0; }