linux/sound/soc/meson/axg-toddr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2//
   3// Copyright (c) 2018 BayLibre, SAS.
   4// Author: Jerome Brunet <jbrunet@baylibre.com>
   5
   6/* This driver implements the frontend capture DAI of AXG based SoCs */
   7
   8#include <linux/clk.h>
   9#include <linux/regmap.h>
  10#include <linux/module.h>
  11#include <linux/of_platform.h>
  12#include <sound/pcm_params.h>
  13#include <sound/soc.h>
  14#include <sound/soc-dai.h>
  15
  16#include "axg-fifo.h"
  17
  18#define CTRL0_TODDR_SEL_RESAMPLE        BIT(30)
  19#define CTRL0_TODDR_EXT_SIGNED          BIT(29)
  20#define CTRL0_TODDR_PP_MODE             BIT(28)
  21#define CTRL0_TODDR_TYPE_MASK           GENMASK(15, 13)
  22#define CTRL0_TODDR_TYPE(x)             ((x) << 13)
  23#define CTRL0_TODDR_MSB_POS_MASK        GENMASK(12, 8)
  24#define CTRL0_TODDR_MSB_POS(x)          ((x) << 8)
  25#define CTRL0_TODDR_LSB_POS_MASK        GENMASK(7, 3)
  26#define CTRL0_TODDR_LSB_POS(x)          ((x) << 3)
  27
  28#define TODDR_MSB_POS   31
  29
  30static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd,
  31                             struct snd_soc_dai *dai)
  32{
  33        return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE);
  34}
  35
  36static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream,
  37                                   struct snd_pcm_hw_params *params,
  38                                   struct snd_soc_dai *dai)
  39{
  40        struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
  41        unsigned int type, width;
  42
  43        switch (params_physical_width(params)) {
  44        case 8:
  45                type = 0; /* 8 samples of 8 bits */
  46                break;
  47        case 16:
  48                type = 2; /* 4 samples of 16 bits - right justified */
  49                break;
  50        case 32:
  51                type = 4; /* 2 samples of 32 bits - right justified */
  52                break;
  53        default:
  54                return -EINVAL;
  55        }
  56
  57        width = params_width(params);
  58
  59        regmap_update_bits(fifo->map, FIFO_CTRL0,
  60                           CTRL0_TODDR_TYPE_MASK |
  61                           CTRL0_TODDR_MSB_POS_MASK |
  62                           CTRL0_TODDR_LSB_POS_MASK,
  63                           CTRL0_TODDR_TYPE(type) |
  64                           CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) |
  65                           CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1)));
  66
  67        return 0;
  68}
  69
  70static int axg_toddr_dai_startup(struct snd_pcm_substream *substream,
  71                                 struct snd_soc_dai *dai)
  72{
  73        struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
  74        unsigned int fifo_threshold;
  75        int ret;
  76
  77        /* Enable pclk to access registers and clock the fifo ip */
  78        ret = clk_prepare_enable(fifo->pclk);
  79        if (ret)
  80                return ret;
  81
  82        /* Select orginal data - resampling not supported ATM */
  83        regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0);
  84
  85        /* Only signed format are supported ATM */
  86        regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED,
  87                           CTRL0_TODDR_EXT_SIGNED);
  88
  89        /* Apply single buffer mode to the interface */
  90        regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0);
  91
  92        /* TODDR does not have a configurable fifo depth */
  93        fifo_threshold = AXG_FIFO_MIN_CNT - 1;
  94        regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_THRESHOLD_MASK,
  95                           CTRL1_THRESHOLD(fifo_threshold));
  96
  97        return 0;
  98}
  99
 100static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream,
 101                                   struct snd_soc_dai *dai)
 102{
 103        struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
 104
 105        clk_disable_unprepare(fifo->pclk);
 106}
 107
 108static const struct snd_soc_dai_ops axg_toddr_ops = {
 109        .hw_params      = axg_toddr_dai_hw_params,
 110        .startup        = axg_toddr_dai_startup,
 111        .shutdown       = axg_toddr_dai_shutdown,
 112};
 113
 114static struct snd_soc_dai_driver axg_toddr_dai_drv = {
 115        .name = "TODDR",
 116        .capture = {
 117                .stream_name    = "Capture",
 118                .channels_min   = 1,
 119                .channels_max   = AXG_FIFO_CH_MAX,
 120                .rates          = AXG_FIFO_RATES,
 121                .formats        = AXG_FIFO_FORMATS,
 122        },
 123        .ops            = &axg_toddr_ops,
 124        .pcm_new        = axg_toddr_pcm_new,
 125};
 126
 127static const char * const axg_toddr_sel_texts[] = {
 128        "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 6"
 129};
 130
 131static const unsigned int axg_toddr_sel_values[] = {
 132        0, 1, 2, 3, 4, 6
 133};
 134
 135static SOC_VALUE_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0,
 136                                  CTRL0_SEL_SHIFT, CTRL0_SEL_MASK,
 137                                  axg_toddr_sel_texts, axg_toddr_sel_values);
 138
 139static const struct snd_kcontrol_new axg_toddr_in_mux =
 140        SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum);
 141
 142static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = {
 143        SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux),
 144        SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
 145        SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
 146        SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
 147        SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0),
 148        SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0),
 149        SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0),
 150};
 151
 152static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = {
 153        { "Capture", NULL, "SRC SEL" },
 154        { "SRC SEL", "IN 0", "IN 0" },
 155        { "SRC SEL", "IN 1", "IN 1" },
 156        { "SRC SEL", "IN 2", "IN 2" },
 157        { "SRC SEL", "IN 3", "IN 3" },
 158        { "SRC SEL", "IN 4", "IN 4" },
 159        { "SRC SEL", "IN 6", "IN 6" },
 160};
 161
 162static const struct snd_soc_component_driver axg_toddr_component_drv = {
 163        .dapm_widgets           = axg_toddr_dapm_widgets,
 164        .num_dapm_widgets       = ARRAY_SIZE(axg_toddr_dapm_widgets),
 165        .dapm_routes            = axg_toddr_dapm_routes,
 166        .num_dapm_routes        = ARRAY_SIZE(axg_toddr_dapm_routes),
 167        .ops                    = &axg_fifo_pcm_ops
 168};
 169
 170static const struct axg_fifo_match_data axg_toddr_match_data = {
 171        .component_drv  = &axg_toddr_component_drv,
 172        .dai_drv        = &axg_toddr_dai_drv
 173};
 174
 175static const struct of_device_id axg_toddr_of_match[] = {
 176        {
 177                .compatible = "amlogic,axg-toddr",
 178                .data = &axg_toddr_match_data,
 179        }, {}
 180};
 181MODULE_DEVICE_TABLE(of, axg_toddr_of_match);
 182
 183static struct platform_driver axg_toddr_pdrv = {
 184        .probe = axg_fifo_probe,
 185        .driver = {
 186                .name = "axg-toddr",
 187                .of_match_table = axg_toddr_of_match,
 188        },
 189};
 190module_platform_driver(axg_toddr_pdrv);
 191
 192MODULE_DESCRIPTION("Amlogic AXG capture fifo driver");
 193MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 194MODULE_LICENSE("GPL v2");
 195