linux/sound/soc/sdw_utils/soc_sdw_rt_amp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2// This file incorporates work covered by the following copyright notice:
   3// Copyright (c) 2022 Intel Corporation
   4// Copyright (c) 2024 Advanced Micro Devices, Inc.
   5
   6/*
   7 *  soc_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver
   8 */
   9
  10#include <linux/device.h>
  11#include <linux/errno.h>
  12#include <sound/control.h>
  13#include <sound/soc.h>
  14#include <sound/soc-acpi.h>
  15#include <sound/soc-dapm.h>
  16#include <linux/soundwire/sdw.h>
  17#include <linux/soundwire/sdw_type.h>
  18#include <linux/dmi.h>
  19#include <sound/soc_sdw_utils.h>
  20#include "soc_sdw_rt_amp_coeff_tables.h"
  21#include "../codecs/rt1308.h"
  22
  23#define CODEC_NAME_SIZE 7
  24
  25/* choose a larger value to resolve compatibility issues */
  26#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG
  27
  28struct rt_amp_platform_data {
  29        const unsigned char *bq_params;
  30        const unsigned int bq_params_cnt;
  31};
  32
  33static const struct rt_amp_platform_data dell_0a5d_platform_data = {
  34        .bq_params = dell_0a5d_bq_params,
  35        .bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params),
  36};
  37
  38static const struct rt_amp_platform_data dell_0b00_platform_data = {
  39        .bq_params = dell_0b00_bq_params,
  40        .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
  41};
  42
  43static const struct dmi_system_id dmi_platform_data[] = {
  44        /* CometLake devices */
  45        {
  46                .matches = {
  47                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  48                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990")
  49                },
  50                .driver_data = (void *)&dell_0a5d_platform_data,
  51        },
  52        {
  53                .matches = {
  54                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  55                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F")
  56                },
  57                .driver_data = (void *)&dell_0a5d_platform_data,
  58        },
  59        /* TigerLake devices */
  60        {
  61                .matches = {
  62                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  63                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")
  64                },
  65                .driver_data = (void *)&dell_0a5d_platform_data,
  66        },
  67        {
  68                .matches = {
  69                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  70                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
  71                },
  72                .driver_data = (void *)&dell_0a5d_platform_data,
  73        },
  74        /* AlderLake devices */
  75        {
  76                .matches = {
  77                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  78                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
  79                },
  80                .driver_data = (void *)&dell_0b00_platform_data,
  81        },
  82        {
  83                .matches = {
  84                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  85                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
  86                },
  87                .driver_data = (void *)&dell_0b00_platform_data,
  88        },
  89        {
  90                .matches = {
  91                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  92                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
  93                },
  94                .driver_data = (void *)&dell_0b00_platform_data,
  95        },
  96        {
  97                .matches = {
  98                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
  99                        DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
 100                },
 101                .driver_data = (void *)&dell_0b00_platform_data,
 102        },
 103        {},
 104};
 105
 106static int rt_amp_add_device_props(struct device *sdw_dev)
 107{
 108        struct property_entry props[3] = {};
 109        struct fwnode_handle *fwnode;
 110        const struct dmi_system_id *dmi_data;
 111        const struct rt_amp_platform_data *pdata;
 112        unsigned char params[RT_AMP_MAX_BQ_REG];
 113        int ret;
 114
 115        dmi_data = dmi_first_match(dmi_platform_data);
 116        if (!dmi_data)
 117                return 0;
 118
 119        pdata = dmi_data->driver_data;
 120        memcpy(&params, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt);
 121
 122        props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params);
 123        props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt);
 124
 125        fwnode = fwnode_create_software_node(props, NULL);
 126        if (IS_ERR(fwnode))
 127                return PTR_ERR(fwnode);
 128
 129        ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
 130
 131        fwnode_handle_put(fwnode);
 132
 133        return ret;
 134}
 135
 136/*
 137 * dapm routes for rt1308/rt1316/rt1318 will be registered dynamically
 138 * according to the number of rt1308/rt1316/rt1318 used. The first two
 139 * entries will be registered for one codec case, and the last two entries
 140 * are also registered if two 1308s/1316s/1318s are used.
 141 */
 142static const struct snd_soc_dapm_route rt1308_map[] = {
 143        { "Speaker", NULL, "rt1308-1 SPOL" },
 144        { "Speaker", NULL, "rt1308-1 SPOR" },
 145        { "Speaker", NULL, "rt1308-2 SPOL" },
 146        { "Speaker", NULL, "rt1308-2 SPOR" },
 147};
 148
 149static const struct snd_soc_dapm_route rt1316_map[] = {
 150        { "Speaker", NULL, "rt1316-1 SPOL" },
 151        { "Speaker", NULL, "rt1316-1 SPOR" },
 152        { "Speaker", NULL, "rt1316-2 SPOL" },
 153        { "Speaker", NULL, "rt1316-2 SPOR" },
 154};
 155
 156static const struct snd_soc_dapm_route rt1318_map[] = {
 157        { "Speaker", NULL, "rt1318-1 SPOL" },
 158        { "Speaker", NULL, "rt1318-1 SPOR" },
 159        { "Speaker", NULL, "rt1318-2 SPOL" },
 160        { "Speaker", NULL, "rt1318-2 SPOR" },
 161};
 162
 163static const struct snd_soc_dapm_route rt1320_map[] = {
 164        { "Speaker", NULL, "rt1320-1 SPOL" },
 165        { "Speaker", NULL, "rt1320-1 SPOR" },
 166        { "Speaker", NULL, "rt1320-2 SPOL" },
 167        { "Speaker", NULL, "rt1320-2 SPOR" },
 168};
 169
 170static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_dai *dai,
 171                                                                 char *codec_name)
 172{
 173        /* get the codec name */
 174        snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name);
 175
 176        /* choose the right codec's map  */
 177        if (strcmp(codec_name, "rt1308") == 0)
 178                return rt1308_map;
 179        else if (strcmp(codec_name, "rt1316") == 0)
 180                return rt1316_map;
 181        else if (strcmp(codec_name, "rt1318") == 0)
 182                return rt1318_map;
 183        else
 184                return rt1320_map;
 185}
 186
 187int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
 188{
 189        struct snd_soc_card *card = rtd->card;
 190        const struct snd_soc_dapm_route *rt_amp_map;
 191        char codec_name[CODEC_NAME_SIZE];
 192        struct snd_soc_dai *codec_dai;
 193        int ret = -EINVAL;
 194        int i;
 195
 196        rt_amp_map = get_codec_name_and_route(dai, codec_name);
 197
 198        for_each_rtd_codec_dais(rtd, i, codec_dai) {
 199                if (strstr(codec_dai->component->name_prefix, "-1"))
 200                        ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);
 201                else if (strstr(codec_dai->component->name_prefix, "-2"))
 202                        ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2);
 203        }
 204
 205        return ret;
 206}
 207EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_spk_rtd_init, "SND_SOC_SDW_UTILS");
 208
 209static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream,
 210                                struct snd_pcm_hw_params *params)
 211{
 212        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 213        struct snd_soc_card *card = rtd->card;
 214        struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 215        int clk_id, clk_freq, pll_out;
 216        int err;
 217
 218        clk_id = RT1308_PLL_S_MCLK;
 219        clk_freq = 38400000;
 220
 221        pll_out = params_rate(params) * 512;
 222
 223        /* Set rt1308 pll */
 224        err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
 225        if (err < 0) {
 226                dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err);
 227                return err;
 228        }
 229
 230        /* Set rt1308 sysclk */
 231        err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out,
 232                                     SND_SOC_CLOCK_IN);
 233        if (err < 0) {
 234                dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err);
 235                return err;
 236        }
 237
 238        return 0;
 239}
 240
 241/* machine stream operations */
 242const struct snd_soc_ops soc_sdw_rt1308_i2s_ops = {
 243        .hw_params = rt1308_i2s_hw_params,
 244};
 245EXPORT_SYMBOL_NS(soc_sdw_rt1308_i2s_ops, "SND_SOC_SDW_UTILS");
 246
 247int asoc_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
 248{
 249        struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
 250
 251        if (ctx->amp_dev1) {
 252                device_remove_software_node(ctx->amp_dev1);
 253                put_device(ctx->amp_dev1);
 254        }
 255
 256        if (ctx->amp_dev2) {
 257                device_remove_software_node(ctx->amp_dev2);
 258                put_device(ctx->amp_dev2);
 259        }
 260
 261        return 0;
 262}
 263EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_exit, "SND_SOC_SDW_UTILS");
 264
 265int asoc_sdw_rt_amp_init(struct snd_soc_card *card,
 266                         struct snd_soc_dai_link *dai_links,
 267                         struct asoc_sdw_codec_info *info,
 268                         bool playback)
 269{
 270        struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
 271        struct device *sdw_dev1, *sdw_dev2;
 272        int ret;
 273
 274        /* Count amp number and do init on playback link only. */
 275        if (!playback)
 276                return 0;
 277
 278        info->amp_num++;
 279
 280        if (info->amp_num == 2) {
 281                sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
 282                if (!sdw_dev1)
 283                        return -EPROBE_DEFER;
 284
 285                ret = rt_amp_add_device_props(sdw_dev1);
 286                if (ret < 0) {
 287                        put_device(sdw_dev1);
 288                        return ret;
 289                }
 290                ctx->amp_dev1 = sdw_dev1;
 291
 292                sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);
 293                if (!sdw_dev2)
 294                        return -EPROBE_DEFER;
 295
 296                ret = rt_amp_add_device_props(sdw_dev2);
 297                if (ret < 0) {
 298                        put_device(sdw_dev2);
 299                        return ret;
 300                }
 301                ctx->amp_dev2 = sdw_dev2;
 302        }
 303
 304        return 0;
 305}
 306EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_init, "SND_SOC_SDW_UTILS");
 307