linux/sound/soc/codecs/si476x.c
<<
>>
Prefs
   1#include <linux/module.h>
   2#include <linux/slab.h>
   3#include <sound/pcm.h>
   4#include <sound/pcm_params.h>
   5#include <sound/soc.h>
   6#include <sound/initval.h>
   7
   8#include <linux/i2c.h>
   9
  10#include <linux/mfd/si476x-core.h>
  11
  12enum si476x_audio_registers {
  13        SI476X_DIGITAL_IO_OUTPUT_FORMAT         = 0x0203,
  14        SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE    = 0x0202,
  15};
  16
  17enum si476x_digital_io_output_format {
  18        SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT       = 11,
  19        SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT     = 8,
  20};
  21
  22#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK     ((0b111 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
  23                                                  (0b111 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
  24#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK    (0b1111110)
  25
  26enum si476x_daudio_formats {
  27        SI476X_DAUDIO_MODE_I2S          = (0x0 << 1),
  28        SI476X_DAUDIO_MODE_DSP_A        = (0x6 << 1),
  29        SI476X_DAUDIO_MODE_DSP_B        = (0x7 << 1),
  30        SI476X_DAUDIO_MODE_LEFT_J       = (0x8 << 1),
  31        SI476X_DAUDIO_MODE_RIGHT_J      = (0x9 << 1),
  32
  33        SI476X_DAUDIO_MODE_IB           = (1 << 5),
  34        SI476X_DAUDIO_MODE_IF           = (1 << 6),
  35};
  36
  37enum si476x_pcm_format {
  38        SI476X_PCM_FORMAT_S8            = 2,
  39        SI476X_PCM_FORMAT_S16_LE        = 4,
  40        SI476X_PCM_FORMAT_S20_3LE       = 5,
  41        SI476X_PCM_FORMAT_S24_LE        = 6,
  42};
  43
  44static unsigned int si476x_codec_read(struct snd_soc_codec *codec,
  45                                      unsigned int reg)
  46{
  47        int err;
  48        struct si476x_core *core = codec->control_data;
  49
  50        si476x_core_lock(core);
  51        err = si476x_core_cmd_get_property(core, reg);
  52        si476x_core_unlock(core);
  53
  54        return err;
  55}
  56
  57static int si476x_codec_write(struct snd_soc_codec *codec,
  58                              unsigned int reg, unsigned int val)
  59{
  60        int err;
  61        struct si476x_core *core = codec->control_data;
  62
  63        si476x_core_lock(core);
  64        err = si476x_core_cmd_set_property(core, reg, val);
  65        si476x_core_unlock(core);
  66
  67        return err;
  68}
  69
  70static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
  71                                    unsigned int fmt)
  72{
  73        int err;
  74        u16 format = 0;
  75
  76        if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
  77                return -EINVAL;
  78
  79        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  80        case SND_SOC_DAIFMT_DSP_A:
  81                format |= SI476X_DAUDIO_MODE_DSP_A;
  82                break;
  83        case SND_SOC_DAIFMT_DSP_B:
  84                format |= SI476X_DAUDIO_MODE_DSP_B;
  85                break;
  86        case SND_SOC_DAIFMT_I2S:
  87                format |= SI476X_DAUDIO_MODE_I2S;
  88                break;
  89        case SND_SOC_DAIFMT_RIGHT_J:
  90                format |= SI476X_DAUDIO_MODE_RIGHT_J;
  91                break;
  92        case SND_SOC_DAIFMT_LEFT_J:
  93                format |= SI476X_DAUDIO_MODE_LEFT_J;
  94                break;
  95        default:
  96                return -EINVAL;
  97        }
  98
  99        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 100        case SND_SOC_DAIFMT_DSP_A:
 101        case SND_SOC_DAIFMT_DSP_B:
 102                switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 103                case SND_SOC_DAIFMT_NB_NF:
 104                        break;
 105                case SND_SOC_DAIFMT_IB_NF:
 106                        format |= SI476X_DAUDIO_MODE_IB;
 107                        break;
 108                default:
 109                        return -EINVAL;
 110                }
 111                break;
 112        case SND_SOC_DAIFMT_I2S:
 113        case SND_SOC_DAIFMT_RIGHT_J:
 114        case SND_SOC_DAIFMT_LEFT_J:
 115                switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 116                case SND_SOC_DAIFMT_NB_NF:
 117                        break;
 118                case SND_SOC_DAIFMT_IB_IF:
 119                        format |= SI476X_DAUDIO_MODE_IB |
 120                                SI476X_DAUDIO_MODE_IF;
 121                        break;
 122                case SND_SOC_DAIFMT_IB_NF:
 123                        format |= SI476X_DAUDIO_MODE_IB;
 124                        break;
 125                case SND_SOC_DAIFMT_NB_IF:
 126                        format |= SI476X_DAUDIO_MODE_IF;
 127                        break;
 128                default:
 129                        return -EINVAL;
 130                }
 131                break;
 132        default:
 133                return -EINVAL;
 134        }
 135
 136        err = snd_soc_update_bits(codec_dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
 137                                  SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK,
 138                                  format);
 139        if (err < 0) {
 140                dev_err(codec_dai->codec->dev, "Failed to set output format\n");
 141                return err;
 142        }
 143        
 144        return 0;
 145}
 146
 147static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
 148                                  struct snd_pcm_hw_params *params,
 149                                  struct snd_soc_dai *dai)
 150{
 151        int rate, width, err;
 152
 153        rate = params_rate(params);
 154        if (rate < 32000 || rate > 48000) {
 155                dev_err(dai->codec->dev, "Rate: %d is not supported\n", rate);
 156                return -EINVAL;
 157        }
 158
 159        switch (params_format(params)) {
 160        case SNDRV_PCM_FORMAT_S8:
 161                width = SI476X_PCM_FORMAT_S8;
 162                break;
 163        case SNDRV_PCM_FORMAT_S16_LE:
 164                width = SI476X_PCM_FORMAT_S16_LE;
 165                break;
 166        case SNDRV_PCM_FORMAT_S20_3LE:
 167                width = SI476X_PCM_FORMAT_S20_3LE;
 168                break;
 169        case SNDRV_PCM_FORMAT_S24_LE:
 170                width = SI476X_PCM_FORMAT_S24_LE;
 171                break;
 172        default:
 173                return -EINVAL;
 174        }
 175
 176        err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
 177                            rate);
 178        if (err < 0) {
 179                dev_err(dai->codec->dev, "Failed to set sample rate\n");
 180                return err;
 181        }
 182
 183        err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
 184                                  SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
 185                                  (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | 
 186                                  (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
 187        if (err < 0) {
 188                dev_err(dai->codec->dev, "Failed to set output width\n");
 189                return err;
 190        }
 191
 192        return 0;
 193}
 194
 195static int si476x_codec_probe(struct snd_soc_codec *codec)
 196{
 197        codec->control_data = i2c_mfd_cell_to_core(codec->dev);
 198        return 0;
 199}
 200
 201static struct snd_soc_dai_ops si476x_dai_ops = {
 202        .hw_params      = si476x_codec_hw_params,
 203        .set_fmt        = si476x_codec_set_dai_fmt,
 204};
 205
 206static struct snd_soc_dai_driver si476x_dai = {
 207        .name           = "si476x-codec",
 208        .capture        = {
 209                .stream_name    = "Capture",
 210                .channels_min   = 2,
 211                .channels_max   = 2,
 212
 213                .rates = SNDRV_PCM_RATE_32000 |
 214                SNDRV_PCM_RATE_44100 |
 215                SNDRV_PCM_RATE_48000,
 216                .formats = SNDRV_PCM_FMTBIT_S8 |
 217                SNDRV_PCM_FMTBIT_S16_LE |
 218                SNDRV_PCM_FMTBIT_S20_3LE |
 219                SNDRV_PCM_FMTBIT_S24_LE
 220        },
 221        .ops            = &si476x_dai_ops,
 222};
 223
 224static struct snd_soc_codec_driver soc_codec_dev_si476x = {
 225        .probe  = si476x_codec_probe,
 226        .read   = si476x_codec_read,
 227        .write  = si476x_codec_write,
 228};
 229
 230static int si476x_platform_probe(struct platform_device *pdev)
 231{
 232        return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_si476x,
 233                                      &si476x_dai, 1);
 234}
 235
 236static int si476x_platform_remove(struct platform_device *pdev)
 237{
 238        snd_soc_unregister_codec(&pdev->dev);
 239        return 0;
 240}
 241
 242MODULE_ALIAS("platform:si476x-codec");
 243
 244static struct platform_driver si476x_platform_driver = {
 245        .driver         = {
 246                .name   = "si476x-codec",
 247                .owner  = THIS_MODULE,
 248        },
 249        .probe          = si476x_platform_probe,
 250        .remove         = si476x_platform_remove,
 251};
 252module_platform_driver(si476x_platform_driver);
 253
 254MODULE_AUTHOR("Andrey Smirnov <andrey.smirnov@convergeddevices.net>");
 255MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");
 256MODULE_LICENSE("GPL");
 257
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.