Loading Documentation/devicetree/bindings/sound/sgtl5000.txt +3 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,12 @@ Required properties: - reg : the I2C address of the device - clocks : the clock provider of SYS_MCLK Example: codec: sgtl5000@0a { compatible = "fsl,sgtl5000"; reg = <0x0a>; clocks = <&clks 150>; }; sound/soc/codecs/sgtl5000.c +208 −59 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/clk.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/consumer.h> Loading @@ -34,30 +35,30 @@ #define SGTL5000_MAX_REG_OFFSET 0x013A /* default value of sgtl5000 registers */ static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = { [SGTL5000_CHIP_CLK_CTRL] = 0x0008, [SGTL5000_CHIP_I2S_CTRL] = 0x0010, [SGTL5000_CHIP_SSS_CTRL] = 0x0008, [SGTL5000_CHIP_DAC_VOL] = 0x3c3c, [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f, [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818, [SGTL5000_CHIP_ANA_CTRL] = 0x0111, [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404, [SGTL5000_CHIP_ANA_POWER] = 0x7060, [SGTL5000_CHIP_PLL_CTRL] = 0x5000, [SGTL5000_DAP_BASS_ENHANCE] = 0x0040, [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f, [SGTL5000_DAP_SURROUND] = 0x0040, [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f, [SGTL5000_DAP_MAIN_CHAN] = 0x8000, [SGTL5000_DAP_AVC_CTRL] = 0x0510, [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473, [SGTL5000_DAP_AVC_ATTACK] = 0x0028, [SGTL5000_DAP_AVC_DECAY] = 0x0050, static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_CHIP_CLK_CTRL, 0x0008 }, { SGTL5000_CHIP_I2S_CTRL, 0x0010 }, { SGTL5000_CHIP_SSS_CTRL, 0x0008 }, { SGTL5000_CHIP_DAC_VOL, 0x3c3c }, { SGTL5000_CHIP_PAD_STRENGTH, 0x015f }, { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 }, { SGTL5000_CHIP_ANA_CTRL, 0x0111 }, { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 }, { SGTL5000_CHIP_ANA_POWER, 0x7060 }, { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f }, { SGTL5000_DAP_SURROUND, 0x0040 }, { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f }, { SGTL5000_DAP_MAIN_CHAN, 0x8000 }, { SGTL5000_DAP_AVC_CTRL, 0x0510 }, { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 }, { SGTL5000_DAP_AVC_ATTACK, 0x0028 }, { SGTL5000_DAP_AVC_DECAY, 0x0050 }, }; /* regulator supplies for sgtl5000, VDDD is an optional external supply */ Loading Loading @@ -112,6 +113,8 @@ struct sgtl5000_priv { int fmt; /* i2s data format */ struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; struct ldo_regulator *ldo; struct regmap *regmap; struct clk *mclk; }; /* Loading Loading @@ -151,12 +154,12 @@ static int power_vag_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { switch (event) { case SND_SOC_DAPM_PRE_PMU: case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); break; case SND_SOC_DAPM_POST_PMD: case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_VAG_POWERUP, 0); msleep(400); Loading Loading @@ -217,12 +220,11 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { 0, SGTL5000_CHIP_DIG_POWER, 1, 0), SND_SOC_DAPM_SUPPLY("VAG_POWER", SGTL5000_CHIP_ANA_POWER, 7, 0, power_vag_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0), SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0), SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event), SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event), }; /* routes for sgtl5000 */ Loading @@ -230,16 +232,13 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = { {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */ {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */ {"ADC", NULL, "VAG_POWER"}, {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */ {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */ {"DAC", NULL, "VAG_POWER"}, {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */ {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */ {"LO", NULL, "DAC"}, /* dac --> line_out */ {"LINE_IN", NULL, "VAG_POWER"}, {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */ {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */ Loading Loading @@ -909,10 +908,25 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, if (ret) return ret; udelay(10); regcache_cache_only(sgtl5000->regmap, false); ret = regcache_sync(sgtl5000->regmap); if (ret != 0) { dev_err(codec->dev, "Failed to restore cache: %d\n", ret); regcache_cache_only(sgtl5000->regmap, true); regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); return ret; } } break; case SND_SOC_BIAS_OFF: regcache_cache_only(sgtl5000->regmap, true); regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); break; Loading Loading @@ -958,17 +972,76 @@ static struct snd_soc_dai_driver sgtl5000_dai = { .symmetric_rates = 1, }; static int sgtl5000_volatile_register(struct snd_soc_codec *codec, unsigned int reg) static bool sgtl5000_volatile(struct device *dev, unsigned int reg) { switch (reg) { case SGTL5000_CHIP_ID: case SGTL5000_CHIP_ADCDAC_CTRL: case SGTL5000_CHIP_ANA_STATUS: return 1; return true; } return 0; return false; } static bool sgtl5000_readable(struct device *dev, unsigned int reg) { switch (reg) { case SGTL5000_CHIP_ID: case SGTL5000_CHIP_DIG_POWER: case SGTL5000_CHIP_CLK_CTRL: case SGTL5000_CHIP_I2S_CTRL: case SGTL5000_CHIP_SSS_CTRL: case SGTL5000_CHIP_ADCDAC_CTRL: case SGTL5000_CHIP_DAC_VOL: case SGTL5000_CHIP_PAD_STRENGTH: case SGTL5000_CHIP_ANA_ADC_CTRL: case SGTL5000_CHIP_ANA_HP_CTRL: case SGTL5000_CHIP_ANA_CTRL: case SGTL5000_CHIP_LINREG_CTRL: case SGTL5000_CHIP_REF_CTRL: case SGTL5000_CHIP_MIC_CTRL: case SGTL5000_CHIP_LINE_OUT_CTRL: case SGTL5000_CHIP_LINE_OUT_VOL: case SGTL5000_CHIP_ANA_POWER: case SGTL5000_CHIP_PLL_CTRL: case SGTL5000_CHIP_CLK_TOP_CTRL: case SGTL5000_CHIP_ANA_STATUS: case SGTL5000_CHIP_SHORT_CTRL: case SGTL5000_CHIP_ANA_TEST2: case SGTL5000_DAP_CTRL: case SGTL5000_DAP_PEQ: case SGTL5000_DAP_BASS_ENHANCE: case SGTL5000_DAP_BASS_ENHANCE_CTRL: case SGTL5000_DAP_AUDIO_EQ: case SGTL5000_DAP_SURROUND: case SGTL5000_DAP_FLT_COEF_ACCESS: case SGTL5000_DAP_COEF_WR_B0_MSB: case SGTL5000_DAP_COEF_WR_B0_LSB: case SGTL5000_DAP_EQ_BASS_BAND0: case SGTL5000_DAP_EQ_BASS_BAND1: case SGTL5000_DAP_EQ_BASS_BAND2: case SGTL5000_DAP_EQ_BASS_BAND3: case SGTL5000_DAP_EQ_BASS_BAND4: case SGTL5000_DAP_MAIN_CHAN: case SGTL5000_DAP_MIX_CHAN: case SGTL5000_DAP_AVC_CTRL: case SGTL5000_DAP_AVC_THRESHOLD: case SGTL5000_DAP_AVC_ATTACK: case SGTL5000_DAP_AVC_DECAY: case SGTL5000_DAP_COEF_WR_B1_MSB: case SGTL5000_DAP_COEF_WR_B1_LSB: case SGTL5000_DAP_COEF_WR_B2_MSB: case SGTL5000_DAP_COEF_WR_B2_LSB: case SGTL5000_DAP_COEF_WR_A1_MSB: case SGTL5000_DAP_COEF_WR_A1_LSB: case SGTL5000_DAP_COEF_WR_A2_MSB: case SGTL5000_DAP_COEF_WR_A2_LSB: return true; default: return false; } } #ifdef CONFIG_SUSPEND Loading Loading @@ -1214,7 +1287,7 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec) static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) { u16 reg; int reg; int ret; int rev; int i; Loading Loading @@ -1242,23 +1315,17 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) /* wait for all power rails bring up */ udelay(10); /* read chip information */ reg = snd_soc_read(codec, SGTL5000_CHIP_ID); if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != SGTL5000_PARTID_PART_ID) { dev_err(codec->dev, "Device with ID register %x is not a sgtl5000\n", reg); ret = -ENODEV; goto err_regulator_disable; } rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; dev_info(codec->dev, "sgtl5000 revision 0x%x\n", rev); /* * workaround for revision 0x11 and later, * roll back to use internal LDO */ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); if (ret) goto err_regulator_disable; rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; if (external_vddd && rev >= 0x11) { /* disable all regulator first */ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), Loading Loading @@ -1300,7 +1367,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); /* setup i2c data ops */ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); codec->control_data = sgtl5000->regmap; ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; Loading Loading @@ -1391,11 +1459,6 @@ static struct snd_soc_codec_driver sgtl5000_driver = { .suspend = sgtl5000_suspend, .resume = sgtl5000_resume, .set_bias_level = sgtl5000_set_bias_level, .reg_cache_size = ARRAY_SIZE(sgtl5000_regs), .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = sgtl5000_regs, .volatile_register = sgtl5000_volatile_register, .controls = sgtl5000_snd_controls, .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), .dapm_widgets = sgtl5000_dapm_widgets, Loading @@ -1404,28 +1467,114 @@ static struct snd_soc_codec_driver sgtl5000_driver = { .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), }; static const struct regmap_config sgtl5000_regmap = { .reg_bits = 16, .val_bits = 16, .max_register = SGTL5000_MAX_REG_OFFSET, .volatile_reg = sgtl5000_volatile, .readable_reg = sgtl5000_readable, .cache_type = REGCACHE_RBTREE, .reg_defaults = sgtl5000_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults), }; /* * Write all the default values from sgtl5000_reg_defaults[] array into the * sgtl5000 registers, to make sure we always start with the sane registers * values as stated in the datasheet. * * Since sgtl5000 does not have a reset line, nor a reset command in software, * we follow this approach to guarantee we always start from the default values * and avoid problems like, not being able to probe after an audio playback * followed by a system reset or a 'reboot' command in Linux */ static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000) { int i, ret, val, index; for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) { val = sgtl5000_reg_defaults[i].def; index = sgtl5000_reg_defaults[i].reg; ret = regmap_write(sgtl5000->regmap, index, val); if (ret) return ret; } return 0; } static int sgtl5000_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct sgtl5000_priv *sgtl5000; int ret; int ret, reg, rev; sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), GFP_KERNEL); if (!sgtl5000) return -ENOMEM; sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap); if (IS_ERR(sgtl5000->regmap)) { ret = PTR_ERR(sgtl5000->regmap); dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); return ret; } sgtl5000->mclk = devm_clk_get(&client->dev, NULL); if (IS_ERR(sgtl5000->mclk)) { ret = PTR_ERR(sgtl5000->mclk); dev_err(&client->dev, "Failed to get mclock: %d\n", ret); return ret; } ret = clk_prepare_enable(sgtl5000->mclk); if (ret) return ret; /* read chip information */ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); if (ret) goto disable_clk; if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != SGTL5000_PARTID_PART_ID) { dev_err(&client->dev, "Device with ID register %x is not a sgtl5000\n", reg); ret = -ENODEV; goto disable_clk; } rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev); i2c_set_clientdata(client, sgtl5000); /* Ensure sgtl5000 will start with sane register values */ ret = sgtl5000_fill_defaults(sgtl5000); if (ret) goto disable_clk; ret = snd_soc_register_codec(&client->dev, &sgtl5000_driver, &sgtl5000_dai, 1); if (ret) goto disable_clk; return 0; disable_clk: clk_disable_unprepare(sgtl5000->mclk); return ret; } static int sgtl5000_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); snd_soc_unregister_codec(&client->dev); clk_disable_unprepare(sgtl5000->mclk); return 0; } Loading sound/soc/codecs/sgtl5000.h +1 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ #define _SGTL5000_H /* * Register values. * Registers addresses */ #define SGTL5000_CHIP_ID 0x0000 #define SGTL5000_CHIP_DIG_POWER 0x0002 Loading sound/soc/fsl/imx-sgtl5000.c +8 −24 Original line number Diff line number Diff line Loading @@ -128,21 +128,11 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) goto fail; } data->codec_clk = clk_get(&codec_dev->dev, NULL); if (IS_ERR(data->codec_clk)) { /* assuming clock enabled by default */ data->codec_clk = NULL; ret = of_property_read_u32(codec_np, "clock-frequency", &data->clk_frequency); if (ret) { dev_err(&codec_dev->dev, "clock-frequency missing or invalid\n"); data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); if (IS_ERR(data->codec_clk)) goto fail; } } else { data->clk_frequency = clk_get_rate(data->codec_clk); clk_prepare_enable(data->codec_clk); } data->dai.name = "HiFi"; data->dai.stream_name = "HiFi"; Loading @@ -157,10 +147,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->card.dev = &pdev->dev; ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) goto clk_fail; goto fail; ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) goto clk_fail; goto fail; data->card.num_links = 1; data->card.owner = THIS_MODULE; data->card.dai_link = &data->dai; Loading @@ -170,7 +160,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = snd_soc_register_card(&data->card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); goto clk_fail; goto fail; } platform_set_drvdata(pdev, data); Loading @@ -179,8 +169,6 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) return 0; clk_fail: clk_put(data->codec_clk); fail: if (ssi_np) of_node_put(ssi_np); Loading @@ -194,10 +182,6 @@ static int imx_sgtl5000_remove(struct platform_device *pdev) { struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); if (data->codec_clk) { clk_disable_unprepare(data->codec_clk); clk_put(data->codec_clk); } snd_soc_unregister_card(&data->card); return 0; Loading Loading
Documentation/devicetree/bindings/sound/sgtl5000.txt +3 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,12 @@ Required properties: - reg : the I2C address of the device - clocks : the clock provider of SYS_MCLK Example: codec: sgtl5000@0a { compatible = "fsl,sgtl5000"; reg = <0x0a>; clocks = <&clks 150>; };
sound/soc/codecs/sgtl5000.c +208 −59 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/clk.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/consumer.h> Loading @@ -34,30 +35,30 @@ #define SGTL5000_MAX_REG_OFFSET 0x013A /* default value of sgtl5000 registers */ static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = { [SGTL5000_CHIP_CLK_CTRL] = 0x0008, [SGTL5000_CHIP_I2S_CTRL] = 0x0010, [SGTL5000_CHIP_SSS_CTRL] = 0x0008, [SGTL5000_CHIP_DAC_VOL] = 0x3c3c, [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f, [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818, [SGTL5000_CHIP_ANA_CTRL] = 0x0111, [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404, [SGTL5000_CHIP_ANA_POWER] = 0x7060, [SGTL5000_CHIP_PLL_CTRL] = 0x5000, [SGTL5000_DAP_BASS_ENHANCE] = 0x0040, [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f, [SGTL5000_DAP_SURROUND] = 0x0040, [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f, [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f, [SGTL5000_DAP_MAIN_CHAN] = 0x8000, [SGTL5000_DAP_AVC_CTRL] = 0x0510, [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473, [SGTL5000_DAP_AVC_ATTACK] = 0x0028, [SGTL5000_DAP_AVC_DECAY] = 0x0050, static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_CHIP_CLK_CTRL, 0x0008 }, { SGTL5000_CHIP_I2S_CTRL, 0x0010 }, { SGTL5000_CHIP_SSS_CTRL, 0x0008 }, { SGTL5000_CHIP_DAC_VOL, 0x3c3c }, { SGTL5000_CHIP_PAD_STRENGTH, 0x015f }, { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 }, { SGTL5000_CHIP_ANA_CTRL, 0x0111 }, { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 }, { SGTL5000_CHIP_ANA_POWER, 0x7060 }, { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f }, { SGTL5000_DAP_SURROUND, 0x0040 }, { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f }, { SGTL5000_DAP_MAIN_CHAN, 0x8000 }, { SGTL5000_DAP_AVC_CTRL, 0x0510 }, { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 }, { SGTL5000_DAP_AVC_ATTACK, 0x0028 }, { SGTL5000_DAP_AVC_DECAY, 0x0050 }, }; /* regulator supplies for sgtl5000, VDDD is an optional external supply */ Loading Loading @@ -112,6 +113,8 @@ struct sgtl5000_priv { int fmt; /* i2s data format */ struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; struct ldo_regulator *ldo; struct regmap *regmap; struct clk *mclk; }; /* Loading Loading @@ -151,12 +154,12 @@ static int power_vag_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { switch (event) { case SND_SOC_DAPM_PRE_PMU: case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); break; case SND_SOC_DAPM_POST_PMD: case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_VAG_POWERUP, 0); msleep(400); Loading Loading @@ -217,12 +220,11 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { 0, SGTL5000_CHIP_DIG_POWER, 1, 0), SND_SOC_DAPM_SUPPLY("VAG_POWER", SGTL5000_CHIP_ANA_POWER, 7, 0, power_vag_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0), SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0), SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event), SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event), }; /* routes for sgtl5000 */ Loading @@ -230,16 +232,13 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = { {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */ {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */ {"ADC", NULL, "VAG_POWER"}, {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */ {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */ {"DAC", NULL, "VAG_POWER"}, {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */ {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */ {"LO", NULL, "DAC"}, /* dac --> line_out */ {"LINE_IN", NULL, "VAG_POWER"}, {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */ {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */ Loading Loading @@ -909,10 +908,25 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, if (ret) return ret; udelay(10); regcache_cache_only(sgtl5000->regmap, false); ret = regcache_sync(sgtl5000->regmap); if (ret != 0) { dev_err(codec->dev, "Failed to restore cache: %d\n", ret); regcache_cache_only(sgtl5000->regmap, true); regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); return ret; } } break; case SND_SOC_BIAS_OFF: regcache_cache_only(sgtl5000->regmap, true); regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); break; Loading Loading @@ -958,17 +972,76 @@ static struct snd_soc_dai_driver sgtl5000_dai = { .symmetric_rates = 1, }; static int sgtl5000_volatile_register(struct snd_soc_codec *codec, unsigned int reg) static bool sgtl5000_volatile(struct device *dev, unsigned int reg) { switch (reg) { case SGTL5000_CHIP_ID: case SGTL5000_CHIP_ADCDAC_CTRL: case SGTL5000_CHIP_ANA_STATUS: return 1; return true; } return 0; return false; } static bool sgtl5000_readable(struct device *dev, unsigned int reg) { switch (reg) { case SGTL5000_CHIP_ID: case SGTL5000_CHIP_DIG_POWER: case SGTL5000_CHIP_CLK_CTRL: case SGTL5000_CHIP_I2S_CTRL: case SGTL5000_CHIP_SSS_CTRL: case SGTL5000_CHIP_ADCDAC_CTRL: case SGTL5000_CHIP_DAC_VOL: case SGTL5000_CHIP_PAD_STRENGTH: case SGTL5000_CHIP_ANA_ADC_CTRL: case SGTL5000_CHIP_ANA_HP_CTRL: case SGTL5000_CHIP_ANA_CTRL: case SGTL5000_CHIP_LINREG_CTRL: case SGTL5000_CHIP_REF_CTRL: case SGTL5000_CHIP_MIC_CTRL: case SGTL5000_CHIP_LINE_OUT_CTRL: case SGTL5000_CHIP_LINE_OUT_VOL: case SGTL5000_CHIP_ANA_POWER: case SGTL5000_CHIP_PLL_CTRL: case SGTL5000_CHIP_CLK_TOP_CTRL: case SGTL5000_CHIP_ANA_STATUS: case SGTL5000_CHIP_SHORT_CTRL: case SGTL5000_CHIP_ANA_TEST2: case SGTL5000_DAP_CTRL: case SGTL5000_DAP_PEQ: case SGTL5000_DAP_BASS_ENHANCE: case SGTL5000_DAP_BASS_ENHANCE_CTRL: case SGTL5000_DAP_AUDIO_EQ: case SGTL5000_DAP_SURROUND: case SGTL5000_DAP_FLT_COEF_ACCESS: case SGTL5000_DAP_COEF_WR_B0_MSB: case SGTL5000_DAP_COEF_WR_B0_LSB: case SGTL5000_DAP_EQ_BASS_BAND0: case SGTL5000_DAP_EQ_BASS_BAND1: case SGTL5000_DAP_EQ_BASS_BAND2: case SGTL5000_DAP_EQ_BASS_BAND3: case SGTL5000_DAP_EQ_BASS_BAND4: case SGTL5000_DAP_MAIN_CHAN: case SGTL5000_DAP_MIX_CHAN: case SGTL5000_DAP_AVC_CTRL: case SGTL5000_DAP_AVC_THRESHOLD: case SGTL5000_DAP_AVC_ATTACK: case SGTL5000_DAP_AVC_DECAY: case SGTL5000_DAP_COEF_WR_B1_MSB: case SGTL5000_DAP_COEF_WR_B1_LSB: case SGTL5000_DAP_COEF_WR_B2_MSB: case SGTL5000_DAP_COEF_WR_B2_LSB: case SGTL5000_DAP_COEF_WR_A1_MSB: case SGTL5000_DAP_COEF_WR_A1_LSB: case SGTL5000_DAP_COEF_WR_A2_MSB: case SGTL5000_DAP_COEF_WR_A2_LSB: return true; default: return false; } } #ifdef CONFIG_SUSPEND Loading Loading @@ -1214,7 +1287,7 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec) static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) { u16 reg; int reg; int ret; int rev; int i; Loading Loading @@ -1242,23 +1315,17 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) /* wait for all power rails bring up */ udelay(10); /* read chip information */ reg = snd_soc_read(codec, SGTL5000_CHIP_ID); if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != SGTL5000_PARTID_PART_ID) { dev_err(codec->dev, "Device with ID register %x is not a sgtl5000\n", reg); ret = -ENODEV; goto err_regulator_disable; } rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; dev_info(codec->dev, "sgtl5000 revision 0x%x\n", rev); /* * workaround for revision 0x11 and later, * roll back to use internal LDO */ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); if (ret) goto err_regulator_disable; rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; if (external_vddd && rev >= 0x11) { /* disable all regulator first */ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), Loading Loading @@ -1300,7 +1367,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); /* setup i2c data ops */ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); codec->control_data = sgtl5000->regmap; ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; Loading Loading @@ -1391,11 +1459,6 @@ static struct snd_soc_codec_driver sgtl5000_driver = { .suspend = sgtl5000_suspend, .resume = sgtl5000_resume, .set_bias_level = sgtl5000_set_bias_level, .reg_cache_size = ARRAY_SIZE(sgtl5000_regs), .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = sgtl5000_regs, .volatile_register = sgtl5000_volatile_register, .controls = sgtl5000_snd_controls, .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), .dapm_widgets = sgtl5000_dapm_widgets, Loading @@ -1404,28 +1467,114 @@ static struct snd_soc_codec_driver sgtl5000_driver = { .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), }; static const struct regmap_config sgtl5000_regmap = { .reg_bits = 16, .val_bits = 16, .max_register = SGTL5000_MAX_REG_OFFSET, .volatile_reg = sgtl5000_volatile, .readable_reg = sgtl5000_readable, .cache_type = REGCACHE_RBTREE, .reg_defaults = sgtl5000_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults), }; /* * Write all the default values from sgtl5000_reg_defaults[] array into the * sgtl5000 registers, to make sure we always start with the sane registers * values as stated in the datasheet. * * Since sgtl5000 does not have a reset line, nor a reset command in software, * we follow this approach to guarantee we always start from the default values * and avoid problems like, not being able to probe after an audio playback * followed by a system reset or a 'reboot' command in Linux */ static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000) { int i, ret, val, index; for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) { val = sgtl5000_reg_defaults[i].def; index = sgtl5000_reg_defaults[i].reg; ret = regmap_write(sgtl5000->regmap, index, val); if (ret) return ret; } return 0; } static int sgtl5000_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct sgtl5000_priv *sgtl5000; int ret; int ret, reg, rev; sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), GFP_KERNEL); if (!sgtl5000) return -ENOMEM; sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap); if (IS_ERR(sgtl5000->regmap)) { ret = PTR_ERR(sgtl5000->regmap); dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); return ret; } sgtl5000->mclk = devm_clk_get(&client->dev, NULL); if (IS_ERR(sgtl5000->mclk)) { ret = PTR_ERR(sgtl5000->mclk); dev_err(&client->dev, "Failed to get mclock: %d\n", ret); return ret; } ret = clk_prepare_enable(sgtl5000->mclk); if (ret) return ret; /* read chip information */ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); if (ret) goto disable_clk; if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != SGTL5000_PARTID_PART_ID) { dev_err(&client->dev, "Device with ID register %x is not a sgtl5000\n", reg); ret = -ENODEV; goto disable_clk; } rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev); i2c_set_clientdata(client, sgtl5000); /* Ensure sgtl5000 will start with sane register values */ ret = sgtl5000_fill_defaults(sgtl5000); if (ret) goto disable_clk; ret = snd_soc_register_codec(&client->dev, &sgtl5000_driver, &sgtl5000_dai, 1); if (ret) goto disable_clk; return 0; disable_clk: clk_disable_unprepare(sgtl5000->mclk); return ret; } static int sgtl5000_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); snd_soc_unregister_codec(&client->dev); clk_disable_unprepare(sgtl5000->mclk); return 0; } Loading
sound/soc/codecs/sgtl5000.h +1 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ #define _SGTL5000_H /* * Register values. * Registers addresses */ #define SGTL5000_CHIP_ID 0x0000 #define SGTL5000_CHIP_DIG_POWER 0x0002 Loading
sound/soc/fsl/imx-sgtl5000.c +8 −24 Original line number Diff line number Diff line Loading @@ -128,21 +128,11 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) goto fail; } data->codec_clk = clk_get(&codec_dev->dev, NULL); if (IS_ERR(data->codec_clk)) { /* assuming clock enabled by default */ data->codec_clk = NULL; ret = of_property_read_u32(codec_np, "clock-frequency", &data->clk_frequency); if (ret) { dev_err(&codec_dev->dev, "clock-frequency missing or invalid\n"); data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); if (IS_ERR(data->codec_clk)) goto fail; } } else { data->clk_frequency = clk_get_rate(data->codec_clk); clk_prepare_enable(data->codec_clk); } data->dai.name = "HiFi"; data->dai.stream_name = "HiFi"; Loading @@ -157,10 +147,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->card.dev = &pdev->dev; ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) goto clk_fail; goto fail; ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) goto clk_fail; goto fail; data->card.num_links = 1; data->card.owner = THIS_MODULE; data->card.dai_link = &data->dai; Loading @@ -170,7 +160,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = snd_soc_register_card(&data->card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); goto clk_fail; goto fail; } platform_set_drvdata(pdev, data); Loading @@ -179,8 +169,6 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) return 0; clk_fail: clk_put(data->codec_clk); fail: if (ssi_np) of_node_put(ssi_np); Loading @@ -194,10 +182,6 @@ static int imx_sgtl5000_remove(struct platform_device *pdev) { struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); if (data->codec_clk) { clk_disable_unprepare(data->codec_clk); clk_put(data->codec_clk); } snd_soc_unregister_card(&data->card); return 0; Loading