17ed4877bSJerome Brunet // SPDX-License-Identifier: (GPL-2.0 OR MIT) 27ed4877bSJerome Brunet // 37ed4877bSJerome Brunet // Copyright (c) 2018 BayLibre, SAS. 47ed4877bSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com> 57ed4877bSJerome Brunet 67ed4877bSJerome Brunet /* This driver implements the frontend capture DAI of AXG based SoCs */ 77ed4877bSJerome Brunet 87ed4877bSJerome Brunet #include <linux/clk.h> 97ed4877bSJerome Brunet #include <linux/regmap.h> 107ed4877bSJerome Brunet #include <linux/module.h> 117ed4877bSJerome Brunet #include <linux/of_platform.h> 127ed4877bSJerome Brunet #include <sound/pcm_params.h> 137ed4877bSJerome Brunet #include <sound/soc.h> 147ed4877bSJerome Brunet #include <sound/soc-dai.h> 157ed4877bSJerome Brunet 167ed4877bSJerome Brunet #include "axg-fifo.h" 177ed4877bSJerome Brunet 187ed4877bSJerome Brunet #define CTRL0_TODDR_SEL_RESAMPLE BIT(30) 197ed4877bSJerome Brunet #define CTRL0_TODDR_EXT_SIGNED BIT(29) 207ed4877bSJerome Brunet #define CTRL0_TODDR_PP_MODE BIT(28) 217ed4877bSJerome Brunet #define CTRL0_TODDR_TYPE_MASK GENMASK(15, 13) 227ed4877bSJerome Brunet #define CTRL0_TODDR_TYPE(x) ((x) << 13) 237ed4877bSJerome Brunet #define CTRL0_TODDR_MSB_POS_MASK GENMASK(12, 8) 247ed4877bSJerome Brunet #define CTRL0_TODDR_MSB_POS(x) ((x) << 8) 257ed4877bSJerome Brunet #define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) 267ed4877bSJerome Brunet #define CTRL0_TODDR_LSB_POS(x) ((x) << 3) 277ed4877bSJerome Brunet 28984463a9SJerome Brunet #define TODDR_MSB_POS 31 29984463a9SJerome Brunet 307ed4877bSJerome Brunet static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, 317ed4877bSJerome Brunet struct snd_soc_dai *dai) 327ed4877bSJerome Brunet { 337ed4877bSJerome Brunet return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE); 347ed4877bSJerome Brunet } 357ed4877bSJerome Brunet 367ed4877bSJerome Brunet static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, 377ed4877bSJerome Brunet struct snd_pcm_hw_params *params, 387ed4877bSJerome Brunet struct snd_soc_dai *dai) 397ed4877bSJerome Brunet { 407ed4877bSJerome Brunet struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 41984463a9SJerome Brunet unsigned int type, width; 427ed4877bSJerome Brunet 437ed4877bSJerome Brunet switch (params_physical_width(params)) { 447ed4877bSJerome Brunet case 8: 457ed4877bSJerome Brunet type = 0; /* 8 samples of 8 bits */ 467ed4877bSJerome Brunet break; 477ed4877bSJerome Brunet case 16: 487ed4877bSJerome Brunet type = 2; /* 4 samples of 16 bits - right justified */ 497ed4877bSJerome Brunet break; 507ed4877bSJerome Brunet case 32: 517ed4877bSJerome Brunet type = 4; /* 2 samples of 32 bits - right justified */ 527ed4877bSJerome Brunet break; 537ed4877bSJerome Brunet default: 547ed4877bSJerome Brunet return -EINVAL; 557ed4877bSJerome Brunet } 567ed4877bSJerome Brunet 577ed4877bSJerome Brunet width = params_width(params); 587ed4877bSJerome Brunet 597ed4877bSJerome Brunet regmap_update_bits(fifo->map, FIFO_CTRL0, 607ed4877bSJerome Brunet CTRL0_TODDR_TYPE_MASK | 617ed4877bSJerome Brunet CTRL0_TODDR_MSB_POS_MASK | 627ed4877bSJerome Brunet CTRL0_TODDR_LSB_POS_MASK, 637ed4877bSJerome Brunet CTRL0_TODDR_TYPE(type) | 64984463a9SJerome Brunet CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | 65984463a9SJerome Brunet CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); 667ed4877bSJerome Brunet 677ed4877bSJerome Brunet return 0; 687ed4877bSJerome Brunet } 697ed4877bSJerome Brunet 707ed4877bSJerome Brunet static int axg_toddr_dai_startup(struct snd_pcm_substream *substream, 717ed4877bSJerome Brunet struct snd_soc_dai *dai) 727ed4877bSJerome Brunet { 737ed4877bSJerome Brunet struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 747ed4877bSJerome Brunet unsigned int fifo_threshold; 757ed4877bSJerome Brunet int ret; 767ed4877bSJerome Brunet 777ed4877bSJerome Brunet /* Enable pclk to access registers and clock the fifo ip */ 787ed4877bSJerome Brunet ret = clk_prepare_enable(fifo->pclk); 797ed4877bSJerome Brunet if (ret) 807ed4877bSJerome Brunet return ret; 817ed4877bSJerome Brunet 827ed4877bSJerome Brunet /* Select orginal data - resampling not supported ATM */ 837ed4877bSJerome Brunet regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0); 847ed4877bSJerome Brunet 857ed4877bSJerome Brunet /* Only signed format are supported ATM */ 867ed4877bSJerome Brunet regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED, 877ed4877bSJerome Brunet CTRL0_TODDR_EXT_SIGNED); 887ed4877bSJerome Brunet 897ed4877bSJerome Brunet /* Apply single buffer mode to the interface */ 907ed4877bSJerome Brunet regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0); 917ed4877bSJerome Brunet 927ed4877bSJerome Brunet /* TODDR does not have a configurable fifo depth */ 937ed4877bSJerome Brunet fifo_threshold = AXG_FIFO_MIN_CNT - 1; 947ed4877bSJerome Brunet regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_THRESHOLD_MASK, 957ed4877bSJerome Brunet CTRL1_THRESHOLD(fifo_threshold)); 967ed4877bSJerome Brunet 977ed4877bSJerome Brunet return 0; 987ed4877bSJerome Brunet } 997ed4877bSJerome Brunet 1007ed4877bSJerome Brunet static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream, 1017ed4877bSJerome Brunet struct snd_soc_dai *dai) 1027ed4877bSJerome Brunet { 1037ed4877bSJerome Brunet struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); 1047ed4877bSJerome Brunet 1057ed4877bSJerome Brunet clk_disable_unprepare(fifo->pclk); 1067ed4877bSJerome Brunet } 1077ed4877bSJerome Brunet 1087ed4877bSJerome Brunet static const struct snd_soc_dai_ops axg_toddr_ops = { 1097ed4877bSJerome Brunet .hw_params = axg_toddr_dai_hw_params, 1107ed4877bSJerome Brunet .startup = axg_toddr_dai_startup, 1117ed4877bSJerome Brunet .shutdown = axg_toddr_dai_shutdown, 1127ed4877bSJerome Brunet }; 1137ed4877bSJerome Brunet 1147ed4877bSJerome Brunet static struct snd_soc_dai_driver axg_toddr_dai_drv = { 1157ed4877bSJerome Brunet .name = "TODDR", 1167ed4877bSJerome Brunet .capture = { 1177ed4877bSJerome Brunet .stream_name = "Capture", 1187ed4877bSJerome Brunet .channels_min = 1, 1197ed4877bSJerome Brunet .channels_max = AXG_FIFO_CH_MAX, 1207ed4877bSJerome Brunet .rates = AXG_FIFO_RATES, 1217ed4877bSJerome Brunet .formats = AXG_FIFO_FORMATS, 1227ed4877bSJerome Brunet }, 1237ed4877bSJerome Brunet .ops = &axg_toddr_ops, 1247ed4877bSJerome Brunet .pcm_new = axg_toddr_pcm_new, 1257ed4877bSJerome Brunet }; 1267ed4877bSJerome Brunet 1277ed4877bSJerome Brunet static const char * const axg_toddr_sel_texts[] = { 1287ed4877bSJerome Brunet "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 6" 1297ed4877bSJerome Brunet }; 1307ed4877bSJerome Brunet 1317ed4877bSJerome Brunet static const unsigned int axg_toddr_sel_values[] = { 1327ed4877bSJerome Brunet 0, 1, 2, 3, 4, 6 1337ed4877bSJerome Brunet }; 1347ed4877bSJerome Brunet 1357ed4877bSJerome Brunet static SOC_VALUE_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0, 1367ed4877bSJerome Brunet CTRL0_SEL_SHIFT, CTRL0_SEL_MASK, 1377ed4877bSJerome Brunet axg_toddr_sel_texts, axg_toddr_sel_values); 1387ed4877bSJerome Brunet 1397ed4877bSJerome Brunet static const struct snd_kcontrol_new axg_toddr_in_mux = 1407ed4877bSJerome Brunet SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum); 1417ed4877bSJerome Brunet 1427ed4877bSJerome Brunet static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = { 1437ed4877bSJerome Brunet SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux), 1447ed4877bSJerome Brunet SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), 1457ed4877bSJerome Brunet SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), 1467ed4877bSJerome Brunet SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), 1477ed4877bSJerome Brunet SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), 1487ed4877bSJerome Brunet SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), 1497ed4877bSJerome Brunet SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0), 1507ed4877bSJerome Brunet }; 1517ed4877bSJerome Brunet 1527ed4877bSJerome Brunet static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = { 1537ed4877bSJerome Brunet { "Capture", NULL, "SRC SEL" }, 1547ed4877bSJerome Brunet { "SRC SEL", "IN 0", "IN 0" }, 1557ed4877bSJerome Brunet { "SRC SEL", "IN 1", "IN 1" }, 1567ed4877bSJerome Brunet { "SRC SEL", "IN 2", "IN 2" }, 1577ed4877bSJerome Brunet { "SRC SEL", "IN 3", "IN 3" }, 1587ed4877bSJerome Brunet { "SRC SEL", "IN 4", "IN 4" }, 1597ed4877bSJerome Brunet { "SRC SEL", "IN 6", "IN 6" }, 1607ed4877bSJerome Brunet }; 1617ed4877bSJerome Brunet 1627ed4877bSJerome Brunet static const struct snd_soc_component_driver axg_toddr_component_drv = { 1637ed4877bSJerome Brunet .dapm_widgets = axg_toddr_dapm_widgets, 1647ed4877bSJerome Brunet .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), 1657ed4877bSJerome Brunet .dapm_routes = axg_toddr_dapm_routes, 1667ed4877bSJerome Brunet .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), 1677ed4877bSJerome Brunet .ops = &axg_fifo_pcm_ops 1687ed4877bSJerome Brunet }; 1697ed4877bSJerome Brunet 1707ed4877bSJerome Brunet static const struct axg_fifo_match_data axg_toddr_match_data = { 1717ed4877bSJerome Brunet .component_drv = &axg_toddr_component_drv, 1727ed4877bSJerome Brunet .dai_drv = &axg_toddr_dai_drv 1737ed4877bSJerome Brunet }; 1747ed4877bSJerome Brunet 1757ed4877bSJerome Brunet static const struct of_device_id axg_toddr_of_match[] = { 1767ed4877bSJerome Brunet { 1777ed4877bSJerome Brunet .compatible = "amlogic,axg-toddr", 1787ed4877bSJerome Brunet .data = &axg_toddr_match_data, 1797ed4877bSJerome Brunet }, {} 1807ed4877bSJerome Brunet }; 1817ed4877bSJerome Brunet MODULE_DEVICE_TABLE(of, axg_toddr_of_match); 1827ed4877bSJerome Brunet 1837ed4877bSJerome Brunet static struct platform_driver axg_toddr_pdrv = { 1847ed4877bSJerome Brunet .probe = axg_fifo_probe, 1857ed4877bSJerome Brunet .driver = { 1867ed4877bSJerome Brunet .name = "axg-toddr", 1877ed4877bSJerome Brunet .of_match_table = axg_toddr_of_match, 1887ed4877bSJerome Brunet }, 1897ed4877bSJerome Brunet }; 1907ed4877bSJerome Brunet module_platform_driver(axg_toddr_pdrv); 1917ed4877bSJerome Brunet 1927ed4877bSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG capture fifo driver"); 1937ed4877bSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 1947ed4877bSJerome Brunet MODULE_LICENSE("GPL v2"); 195