Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
0004  *
0005  * Copyright (C) 2012 Innovative Converged Devices(ICD)
0006  * Copyright (C) 2013 Andrey Smirnov
0007  *
0008  * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/slab.h>
0013 #include <sound/pcm.h>
0014 #include <sound/pcm_params.h>
0015 #include <linux/regmap.h>
0016 #include <sound/soc.h>
0017 #include <sound/initval.h>
0018 
0019 #include <linux/i2c.h>
0020 
0021 #include <linux/mfd/si476x-core.h>
0022 
0023 enum si476x_audio_registers {
0024     SI476X_DIGITAL_IO_OUTPUT_FORMAT     = 0x0203,
0025     SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE    = 0x0202,
0026 };
0027 
0028 enum si476x_digital_io_output_format {
0029     SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT   = 11,
0030     SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8,
0031 };
0032 
0033 #define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
0034                           (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
0035 #define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK    (0x7e)
0036 
0037 enum si476x_daudio_formats {
0038     SI476X_DAUDIO_MODE_I2S      = (0x0 << 1),
0039     SI476X_DAUDIO_MODE_DSP_A    = (0x6 << 1),
0040     SI476X_DAUDIO_MODE_DSP_B    = (0x7 << 1),
0041     SI476X_DAUDIO_MODE_LEFT_J   = (0x8 << 1),
0042     SI476X_DAUDIO_MODE_RIGHT_J  = (0x9 << 1),
0043 
0044     SI476X_DAUDIO_MODE_IB       = (1 << 5),
0045     SI476X_DAUDIO_MODE_IF       = (1 << 6),
0046 };
0047 
0048 enum si476x_pcm_format {
0049     SI476X_PCM_FORMAT_S8        = 2,
0050     SI476X_PCM_FORMAT_S16_LE    = 4,
0051     SI476X_PCM_FORMAT_S20_3LE   = 5,
0052     SI476X_PCM_FORMAT_S24_LE    = 6,
0053 };
0054 
0055 static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = {
0056 SND_SOC_DAPM_OUTPUT("LOUT"),
0057 SND_SOC_DAPM_OUTPUT("ROUT"),
0058 };
0059 
0060 static const struct snd_soc_dapm_route si476x_dapm_routes[] = {
0061     { "Capture", NULL, "LOUT" },
0062     { "Capture", NULL, "ROUT" },
0063 };
0064 
0065 static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
0066                     unsigned int fmt)
0067 {
0068     struct si476x_core *core = i2c_mfd_cell_to_core(codec_dai->dev);
0069     int err;
0070     u16 format = 0;
0071 
0072     if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
0073         return -EINVAL;
0074 
0075     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0076     case SND_SOC_DAIFMT_DSP_A:
0077         format |= SI476X_DAUDIO_MODE_DSP_A;
0078         break;
0079     case SND_SOC_DAIFMT_DSP_B:
0080         format |= SI476X_DAUDIO_MODE_DSP_B;
0081         break;
0082     case SND_SOC_DAIFMT_I2S:
0083         format |= SI476X_DAUDIO_MODE_I2S;
0084         break;
0085     case SND_SOC_DAIFMT_RIGHT_J:
0086         format |= SI476X_DAUDIO_MODE_RIGHT_J;
0087         break;
0088     case SND_SOC_DAIFMT_LEFT_J:
0089         format |= SI476X_DAUDIO_MODE_LEFT_J;
0090         break;
0091     default:
0092         return -EINVAL;
0093     }
0094 
0095     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0096     case SND_SOC_DAIFMT_DSP_A:
0097     case SND_SOC_DAIFMT_DSP_B:
0098         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0099         case SND_SOC_DAIFMT_NB_NF:
0100             break;
0101         case SND_SOC_DAIFMT_IB_NF:
0102             format |= SI476X_DAUDIO_MODE_IB;
0103             break;
0104         default:
0105             return -EINVAL;
0106         }
0107         break;
0108     case SND_SOC_DAIFMT_I2S:
0109     case SND_SOC_DAIFMT_RIGHT_J:
0110     case SND_SOC_DAIFMT_LEFT_J:
0111         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0112         case SND_SOC_DAIFMT_NB_NF:
0113             break;
0114         case SND_SOC_DAIFMT_IB_IF:
0115             format |= SI476X_DAUDIO_MODE_IB |
0116                 SI476X_DAUDIO_MODE_IF;
0117             break;
0118         case SND_SOC_DAIFMT_IB_NF:
0119             format |= SI476X_DAUDIO_MODE_IB;
0120             break;
0121         case SND_SOC_DAIFMT_NB_IF:
0122             format |= SI476X_DAUDIO_MODE_IF;
0123             break;
0124         default:
0125             return -EINVAL;
0126         }
0127         break;
0128     default:
0129         return -EINVAL;
0130     }
0131 
0132     si476x_core_lock(core);
0133 
0134     err = snd_soc_component_update_bits(codec_dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
0135                   SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK,
0136                   format);
0137 
0138     si476x_core_unlock(core);
0139 
0140     if (err < 0) {
0141         dev_err(codec_dai->component->dev, "Failed to set output format\n");
0142         return err;
0143     }
0144 
0145     return 0;
0146 }
0147 
0148 static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
0149                   struct snd_pcm_hw_params *params,
0150                   struct snd_soc_dai *dai)
0151 {
0152     struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev);
0153     int rate, width, err;
0154 
0155     rate = params_rate(params);
0156     if (rate < 32000 || rate > 48000) {
0157         dev_err(dai->component->dev, "Rate: %d is not supported\n", rate);
0158         return -EINVAL;
0159     }
0160 
0161     switch (params_width(params)) {
0162     case 8:
0163         width = SI476X_PCM_FORMAT_S8;
0164         break;
0165     case 16:
0166         width = SI476X_PCM_FORMAT_S16_LE;
0167         break;
0168     case 20:
0169         width = SI476X_PCM_FORMAT_S20_3LE;
0170         break;
0171     case 24:
0172         width = SI476X_PCM_FORMAT_S24_LE;
0173         break;
0174     default:
0175         return -EINVAL;
0176     }
0177 
0178     si476x_core_lock(core);
0179 
0180     err = snd_soc_component_write(dai->component, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
0181                 rate);
0182     if (err < 0) {
0183         dev_err(dai->component->dev, "Failed to set sample rate\n");
0184         goto out;
0185     }
0186 
0187     err = snd_soc_component_update_bits(dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
0188                   SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
0189                   (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
0190                   (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
0191     if (err < 0) {
0192         dev_err(dai->component->dev, "Failed to set output width\n");
0193         goto out;
0194     }
0195 
0196 out:
0197     si476x_core_unlock(core);
0198 
0199     return err;
0200 }
0201 
0202 static const struct snd_soc_dai_ops si476x_dai_ops = {
0203     .hw_params  = si476x_codec_hw_params,
0204     .set_fmt    = si476x_codec_set_dai_fmt,
0205 };
0206 
0207 static struct snd_soc_dai_driver si476x_dai = {
0208     .name       = "si476x-codec",
0209     .capture    = {
0210         .stream_name    = "Capture",
0211         .channels_min   = 2,
0212         .channels_max   = 2,
0213 
0214         .rates = SNDRV_PCM_RATE_32000 |
0215         SNDRV_PCM_RATE_44100 |
0216         SNDRV_PCM_RATE_48000,
0217         .formats = SNDRV_PCM_FMTBIT_S8 |
0218         SNDRV_PCM_FMTBIT_S16_LE |
0219         SNDRV_PCM_FMTBIT_S20_3LE |
0220         SNDRV_PCM_FMTBIT_S24_LE
0221     },
0222     .ops        = &si476x_dai_ops,
0223 };
0224 
0225 static int si476x_probe(struct snd_soc_component *component)
0226 {
0227     snd_soc_component_init_regmap(component,
0228                 dev_get_regmap(component->dev->parent, NULL));
0229 
0230     return 0;
0231 }
0232 
0233 static const struct snd_soc_component_driver soc_component_dev_si476x = {
0234     .probe          = si476x_probe,
0235     .dapm_widgets       = si476x_dapm_widgets,
0236     .num_dapm_widgets   = ARRAY_SIZE(si476x_dapm_widgets),
0237     .dapm_routes        = si476x_dapm_routes,
0238     .num_dapm_routes    = ARRAY_SIZE(si476x_dapm_routes),
0239     .idle_bias_on       = 1,
0240     .use_pmdown_time    = 1,
0241     .endianness     = 1,
0242 };
0243 
0244 static int si476x_platform_probe(struct platform_device *pdev)
0245 {
0246     return devm_snd_soc_register_component(&pdev->dev,
0247                       &soc_component_dev_si476x,
0248                       &si476x_dai, 1);
0249 }
0250 
0251 MODULE_ALIAS("platform:si476x-codec");
0252 
0253 static struct platform_driver si476x_platform_driver = {
0254     .driver     = {
0255         .name   = "si476x-codec",
0256     },
0257     .probe      = si476x_platform_probe,
0258 };
0259 module_platform_driver(si476x_platform_driver);
0260 
0261 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
0262 MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");
0263 MODULE_LICENSE("GPL");