// SPDX-License-Identifier: GPL-2.0 /* * ROHM BU26154 mono audio codec (I2C control), as found in SHARP Brain PW-SH3. * * The chip has three register maps selected by MAPCON. A register is read at * an even index and written at that index + 1. Registers are addressed here * as (map << 8) | read index. */ #include #include #include #include #include #include #include #include #include #define BU26154_REG(map, index) (((map) << 8) | (index)) #define BU26154_SR BU26154_REG(0, 0x00) #define BU26154_CLKEN BU26154_REG(0, 0x0c) #define BU26154_CLKSEL BU26154_REG(0, 0x0e) #define BU26154_SOFTRST BU26154_REG(0, 0x10) #define BU26154_RECPLAY BU26154_REG(0, 0x12) #define BU26154_MCTIME BU26154_REG(0, 0x14) #define BU26154_MAPCON 0x1c #define BU26154_AREF_PM BU26154_REG(0, 0x20) #define BU26154_AIN_PM BU26154_REG(0, 0x22) #define BU26154_DAC_PM BU26154_REG(0, 0x24) #define BU26154_SP_PM BU26154_REG(0, 0x26) #define BU26154_AVVOL BU26154_REG(0, 0x3a) #define BU26154_PDATT BU26154_REG(0, 0x3e) #define BU26154_DAC_CLK BU26154_REG(0, 0x58) #define BU26154_MIC_IF BU26154_REG(0, 0x5a) #define BU26154_SAI_TX BU26154_REG(0, 0x60) #define BU26154_SAI_RX BU26154_REG(0, 0x62) #define BU26154_SAI_MODE BU26154_REG(0, 0x64) #define BU26154_RDVOL BU26154_REG(0, 0x6c) #define BU26154_MIC_SEL BU26154_REG(0, 0xe8) #define BU26154_HP_IN BU26154_REG(1, 0x82) #define BU26154_SP_IN BU26154_REG(1, 0x84) #define BU26154_MAX_REG BU26154_REG(2, 0xfe) #define BU26154_CLKEN_MCLKEN BIT(0) /* Must also be set when the PLL is unused, or no internal clock runs. */ #define BU26154_CLKEN_PLLOE BIT(2) #define BU26154_CLKSEL_MCLK_256FS 0x4 #define BU26154_CLKSEL_MCLK_512FS 0x6 #define BU26154_CLKSEL_MCLK_1024FS 0x7 #define BU26154_RECPLAY_REC BIT(0) #define BU26154_RECPLAY_PLAY BIT(1) #define BU26154_AREF_VMID_MASK 0x03 #define BU26154_AREF_VMID_FAST 0x01 #define BU26154_AREF_VMID_NORMAL 0x02 #define BU26154_AREF_MICBEN BIT(2) #define BU26154_AREF_HPVDDEN BIT(3) #define BU26154_AIN_ADCEN BIT(1) #define BU26154_AIN_PGAEN BIT(3) #define BU26154_AIN_ALL (BU26154_AIN_ADCEN | BU26154_AIN_PGAEN) #define BU26154_DAC_LEN BIT(1) #define BU26154_DAC_REN BIT(2) #define BU26154_SP_AVLEN BIT(0) #define BU26154_SP_AVREN BIT(4) #define BU26154_SP_MDSEL_D BIT(7) #define BU26154_SAI_16BIT_I2S_32FS 0x80 #define BU26154_HP_IN_STEREO 0x21 /* DAC L -> HPL, DAC R -> HPR */ #define BU26154_SP_IN_BOTH 0x0c /* L and R volume -> speaker */ struct bu26154 { struct i2c_client *i2c; struct regmap *regmap; struct mutex lock; int map; unsigned int sysclk; unsigned int rate; unsigned int running; /* BU26154_RECPLAY_* of the active streams */ }; static int bu26154_select_map(struct bu26154 *bu, unsigned int map) { int ret; if (bu->map == map) return 0; ret = i2c_smbus_write_byte_data(bu->i2c, BU26154_MAPCON | 1, map); if (ret < 0) { bu->map = -1; return ret; } bu->map = map; return 0; } static int bu26154_reg_read(void *context, unsigned int reg, unsigned int *val) { struct bu26154 *bu = context; int ret; ret = bu26154_select_map(bu, reg >> 8); if (ret < 0) return ret; ret = i2c_smbus_read_byte_data(bu->i2c, reg & 0xfe); if (ret < 0) return ret; *val = ret; return 0; } static int bu26154_reg_write(void *context, unsigned int reg, unsigned int val) { struct bu26154 *bu = context; int ret; ret = bu26154_select_map(bu, reg >> 8); if (ret < 0) return ret; return i2c_smbus_write_byte_data(bu->i2c, (reg & 0xfe) | 1, val); } static const struct regmap_config bu26154_regmap = { .reg_bits = 16, .val_bits = 8, .reg_stride = 2, .max_register = BU26154_MAX_REG, .reg_read = bu26154_reg_read, .reg_write = bu26154_reg_write, .cache_type = REGCACHE_NONE, }; static const struct snd_kcontrol_new bu26154_controls[] = { /* 0xff is a prohibited setting; 0xfe is -0.5 dB. */ SOC_SINGLE("Master Playback Volume", BU26154_PDATT, 0, 0xfe, 0), SOC_SINGLE("Speaker Playback Volume", BU26154_AVVOL, 0, 0x19, 0), SOC_SINGLE("Speaker Boost Volume", BU26154_SP_IN, 0, 3, 0), SOC_SINGLE("Speaker Playback Switch", BU26154_SP_PM, 1, 1, 0), SOC_SINGLE("Speaker Class D Switch", BU26154_SP_PM, 7, 1, 0), SOC_DOUBLE("Headphone Playback Switch", BU26154_AREF_PM, 6, 7, 1, 0), SOC_SINGLE("Capture Volume", BU26154_RDVOL, 0, 0xff, 0), SOC_SINGLE("Mic Boost Volume", BU26154_MIC_IF, 5, 7, 0), SOC_SINGLE("Mic Differential Switch", BU26154_MIC_IF, 1, 1, 0), SOC_SINGLE("Mic1 Capture Switch", BU26154_MIC_SEL, 0, 1, 0), SOC_SINGLE("Mic2 Capture Switch", BU26154_MIC_SEL, 1, 1, 0), }; static const unsigned int bu26154_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, }; /* RECPLAY may only change through the stopped state. */ static int bu26154_set_running(struct bu26154 *bu, unsigned int running) { int ret; if (bu->running == running) return 0; ret = regmap_write(bu->regmap, BU26154_RECPLAY, 0); if (ret) return ret; bu->running = 0; if (running) { ret = regmap_write(bu->regmap, BU26154_RECPLAY, running); if (ret) return ret; bu->running = running; } return 0; } static int bu26154_set_rate(struct bu26154 *bu, unsigned int rate) { unsigned int sr, clksel, osr; int ret; for (sr = 0; sr < ARRAY_SIZE(bu26154_rates); sr++) if (bu26154_rates[sr] == rate) break; if (sr == ARRAY_SIZE(bu26154_rates)) return -EINVAL; switch (bu->sysclk / rate) { case 256: clksel = BU26154_CLKSEL_MCLK_256FS; break; case 512: clksel = BU26154_CLKSEL_MCLK_512FS; break; case 1024: clksel = BU26154_CLKSEL_MCLK_1024FS; break; default: dev_err(&bu->i2c->dev, "unsupported MCLK %u for rate %u\n", bu->sysclk, rate); return -EINVAL; } if (rate <= 12000) osr = 0; else if (rate <= 24000) osr = 1; else osr = 2; ret = regmap_write(bu->regmap, BU26154_SR, sr); if (!ret) ret = regmap_write(bu->regmap, BU26154_DAC_CLK, osr << 4); if (!ret) ret = regmap_write(bu->regmap, BU26154_CLKSEL, clksel); if (!ret) ret = regmap_write(bu->regmap, BU26154_CLKEN, BU26154_CLKEN_MCLKEN | BU26154_CLKEN_PLLOE); if (!ret) bu->rate = rate; return ret; } static int bu26154_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct bu26154 *bu = snd_soc_component_get_drvdata(dai->component); unsigned int rate = params_rate(params); unsigned int self = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? BU26154_RECPLAY_PLAY : BU26154_RECPLAY_REC; int ret = 0; mutex_lock(&bu->lock); if (bu->running & ~self) { /* The other direction is running: the rate cannot change. */ if (rate != bu->rate) ret = -EBUSY; } else { ret = bu26154_set_running(bu, 0); if (!ret) ret = bu26154_set_rate(bu, rate); } mutex_unlock(&bu->lock); return ret; } static int bu26154_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bu26154 *bu = snd_soc_component_get_drvdata(dai->component); int ret = 0; mutex_lock(&bu->lock); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = bu26154_set_running(bu, bu->running | BU26154_RECPLAY_PLAY); } else if (!(bu->running & BU26154_RECPLAY_REC)) { ret = regmap_update_bits(bu->regmap, BU26154_AREF_PM, BU26154_AREF_MICBEN, BU26154_AREF_MICBEN); if (!ret) ret = regmap_update_bits(bu->regmap, BU26154_AIN_PM, BU26154_AIN_ALL, BU26154_AIN_ALL); if (!ret) ret = bu26154_set_running(bu, bu->running | BU26154_RECPLAY_REC); } mutex_unlock(&bu->lock); return ret; } static int bu26154_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bu26154 *bu = snd_soc_component_get_drvdata(dai->component); mutex_lock(&bu->lock); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { bu26154_set_running(bu, bu->running & ~BU26154_RECPLAY_PLAY); } else { bu26154_set_running(bu, bu->running & ~BU26154_RECPLAY_REC); regmap_update_bits(bu->regmap, BU26154_AIN_PM, BU26154_AIN_ALL, 0); regmap_update_bits(bu->regmap, BU26154_AREF_PM, BU26154_AREF_MICBEN, 0); } mutex_unlock(&bu->lock); return 0; } static int bu26154_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct bu26154 *bu = snd_soc_component_get_drvdata(dai->component); bu->sysclk = freq; return 0; } static int bu26154_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { /* Only what the Brain needs: I2S, codec is clock consumer. */ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S || (fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF || (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) return -EINVAL; return 0; } static const struct snd_soc_dai_ops bu26154_dai_ops = { .hw_params = bu26154_hw_params, .prepare = bu26154_prepare, .hw_free = bu26154_hw_free, .set_sysclk = bu26154_set_sysclk, .set_fmt = bu26154_set_fmt, }; #define BU26154_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000) static struct snd_soc_dai_driver bu26154_dai = { .name = "bu26154-hifi", .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = BU26154_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "Capture", .channels_min = 2, .channels_max = 2, .rates = BU26154_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &bu26154_dai_ops, }; static int bu26154_component_probe(struct snd_soc_component *component) { struct bu26154 *bu = snd_soc_component_get_drvdata(component); struct regmap *map = bu->regmap; /* Software reset; this also returns MAPCON to map 0. */ regmap_write(map, BU26154_SOFTRST, 1); regmap_write(map, BU26154_SOFTRST, 0); bu->map = -1; bu->running = 0; regmap_write(map, BU26154_SAI_TX, BU26154_SAI_16BIT_I2S_32FS); regmap_write(map, BU26154_SAI_RX, BU26154_SAI_16BIT_I2S_32FS); regmap_write(map, BU26154_SAI_MODE, 0); regmap_write(map, BU26154_MCTIME, 0x10); /* VMID: fast charge, then normal mode (5 ms minimum each). */ regmap_update_bits(map, BU26154_AREF_PM, BU26154_AREF_VMID_MASK, BU26154_AREF_VMID_FAST); msleep(10); regmap_update_bits(map, BU26154_AREF_PM, BU26154_AREF_VMID_MASK, BU26154_AREF_VMID_NORMAL); msleep(10); /* Headphone LDO must be up 1 ms before the headphone amplifier. */ regmap_update_bits(map, BU26154_AREF_PM, BU26154_AREF_HPVDDEN, BU26154_AREF_HPVDDEN); usleep_range(2000, 3000); regmap_write(map, BU26154_DAC_PM, BU26154_DAC_LEN | BU26154_DAC_REN); /* * Class D by default: less distortion on the PW-SH3 speaker than * class AB. The mode must be chosen while the amplifier is off. */ regmap_update_bits(map, BU26154_SP_PM, BU26154_SP_AVLEN | BU26154_SP_AVREN | BU26154_SP_MDSEL_D, BU26154_SP_AVLEN | BU26154_SP_AVREN | BU26154_SP_MDSEL_D); regmap_write(map, BU26154_HP_IN, BU26154_HP_IN_STEREO); regmap_write(map, BU26154_SP_IN, BU26154_SP_IN_BOTH); return 0; } static void bu26154_component_remove(struct snd_soc_component *component) { struct bu26154 *bu = snd_soc_component_get_drvdata(component); regmap_write(bu->regmap, BU26154_SOFTRST, 1); regmap_write(bu->regmap, BU26154_SOFTRST, 0); bu->map = -1; } static const struct snd_soc_component_driver bu26154_component = { .probe = bu26154_component_probe, .remove = bu26154_component_remove, .controls = bu26154_controls, .num_controls = ARRAY_SIZE(bu26154_controls), .idle_bias_on = 1, .endianness = 1, }; static int bu26154_i2c_probe(struct i2c_client *i2c) { struct bu26154 *bu; unsigned int val; int ret; bu = devm_kzalloc(&i2c->dev, sizeof(*bu), GFP_KERNEL); if (!bu) return -ENOMEM; bu->i2c = i2c; bu->map = -1; mutex_init(&bu->lock); i2c_set_clientdata(i2c, bu); bu->regmap = devm_regmap_init(&i2c->dev, NULL, bu, &bu26154_regmap); if (IS_ERR(bu->regmap)) return PTR_ERR(bu->regmap); ret = regmap_read(bu->regmap, BU26154_SR, &val); if (ret) return dev_err_probe(&i2c->dev, ret, "no response from codec\n"); return devm_snd_soc_register_component(&i2c->dev, &bu26154_component, &bu26154_dai, 1); } static const struct i2c_device_id bu26154_i2c_id[] = { { "bu26154", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, bu26154_i2c_id); static const struct of_device_id bu26154_of_match[] = { { .compatible = "rohm,bu26154" }, { } }; MODULE_DEVICE_TABLE(of, bu26154_of_match); static struct i2c_driver bu26154_i2c_driver = { .driver = { .name = "bu26154", .of_match_table = bu26154_of_match, }, .probe_new = bu26154_i2c_probe, .id_table = bu26154_i2c_id, }; module_i2c_driver(bu26154_i2c_driver); MODULE_DESCRIPTION("ROHM BU26154 audio codec driver"); MODULE_LICENSE("GPL");