Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/Documentation/kbuild/   (Linux Kernel Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 4 kB image not shown  

Quelle  aio-cpu.c   Sprache: unbekannt

 
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA CPU DAI driver.
//
// Copyright (c) 2016-2018 Socionext Inc.

#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>

#include "aio.h"

static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id)
{
 struct device *dev = &chip->pdev->dev;

 if (pll_id < 0 || chip->num_plls <= pll_id) {
  dev_err(dev, "PLL(%d) is not supported\n", pll_id);
  return false;
 }

 return chip->plls[pll_id].enable;
}

/**
 * find_volume - find volume supported HW port by HW port number
 * @chip: the AIO chip pointer
 * @oport_hw: HW port number, one of AUD_HW_XXXX
 *
 * Find AIO device from device list by HW port number. Volume feature is
 * available only in Output and PCM ports, this limitation comes from HW
 * specifications.
 *
 * Return: The pointer of AIO substream if successful, otherwise NULL on error.
 */

static struct uniphier_aio_sub *find_volume(struct uniphier_aio_chip *chip,
         int oport_hw)
{
 int i;

 for (i = 0; i < chip->num_aios; i++) {
  struct uniphier_aio_sub *sub = &chip->aios[i].sub[0];

  if (!sub->swm)
   continue;

  if (sub->swm->oport.hw == oport_hw)
   return sub;
 }

 return NULL;
}

static bool match_spec(const struct uniphier_aio_spec *spec,
         const char *name, int dir)
{
 if (dir == SNDRV_PCM_STREAM_PLAYBACK &&
     spec->swm.dir != PORT_DIR_OUTPUT) {
  return false;
 }

 if (dir == SNDRV_PCM_STREAM_CAPTURE &&
     spec->swm.dir != PORT_DIR_INPUT) {
  return false;
 }

 if (spec->name && strcmp(spec->name, name) == 0)
  return true;

 if (spec->gname && strcmp(spec->gname, name) == 0)
  return true;

 return false;
}

/**
 * find_spec - find HW specification info by name
 * @aio: the AIO device pointer
 * @name: name of device
 * @direction: the direction of substream, SNDRV_PCM_STREAM_*
 *
 * Find hardware specification information from list by device name. This
 * information is used for telling the difference of SoCs to driver.
 *
 * Specification list is array of 'struct uniphier_aio_spec' which is defined
 * in each drivers (see: aio-i2s.c).
 *
 * Return: The pointer of hardware specification of AIO if successful,
 * otherwise NULL on error.
 */

static const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio,
       const char *name,
       int direction)
{
 const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec;
 int i;

 for (i = 0; i < chip_spec->num_specs; i++) {
  const struct uniphier_aio_spec *spec = &chip_spec->specs[i];

  if (match_spec(spec, name, direction))
   return spec;
 }

 return NULL;
}

/**
 * find_divider - find clock divider by frequency
 * @aio: the AIO device pointer
 * @pll_id: PLL ID, should be AUD_PLL_XX
 * @freq: required frequency
 *
 * Find suitable clock divider by frequency.
 *
 * Return: The ID of PLL if successful, otherwise negative error value.
 */

static int find_divider(struct uniphier_aio *aio, int pll_id, unsigned int freq)
{
 struct uniphier_aio_pll *pll;
 static const int mul[] = { 1112, };
 static const int div[] = { 2313, };
 int i;

 if (!is_valid_pll(aio->chip, pll_id))
  return -EINVAL;

 pll = &aio->chip->plls[pll_id];
 for (i = 0; i < ARRAY_SIZE(mul); i++)
  if (pll->freq * mul[i] / div[i] == freq)
   return i;

 return -ENOTSUPP;
}

static int uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id,
       unsigned int freq, int dir)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 struct device *dev = &aio->chip->pdev->dev;
 bool pll_auto = false;
 int pll_id, div_id;

 switch (clk_id) {
 case AUD_CLK_IO:
  return -ENOTSUPP;
 case AUD_CLK_A1:
  pll_id = AUD_PLL_A1;
  break;
 case AUD_CLK_F1:
  pll_id = AUD_PLL_F1;
  break;
 case AUD_CLK_A2:
  pll_id = AUD_PLL_A2;
  break;
 case AUD_CLK_F2:
  pll_id = AUD_PLL_F2;
  break;
 case AUD_CLK_A:
  pll_id = AUD_PLL_A1;
  pll_auto = true;
  break;
 case AUD_CLK_F:
  pll_id = AUD_PLL_F1;
  pll_auto = true;
  break;
 case AUD_CLK_APLL:
  pll_id = AUD_PLL_APLL;
  break;
 case AUD_CLK_RX0:
  pll_id = AUD_PLL_RX0;
  break;
 case AUD_CLK_USB0:
  pll_id = AUD_PLL_USB0;
  break;
 case AUD_CLK_HSC0:
  pll_id = AUD_PLL_HSC0;
  break;
 default:
  dev_err(dev, "Sysclk(%d) is not supported\n", clk_id);
  return -EINVAL;
 }

 if (pll_auto) {
  for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) {
   div_id = find_divider(aio, pll_id, freq);
   if (div_id >= 0) {
    aio->plldiv = div_id;
    break;
   }
  }
  if (pll_id == aio->chip->num_plls) {
   dev_err(dev, "Sysclk frequency is not supported(%d)\n",
    freq);
   return -EINVAL;
  }
 }

 if (dir == SND_SOC_CLOCK_OUT)
  aio->pll_out = pll_id;
 else
  aio->pll_in = pll_id;

 return 0;
}

static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id,
    int source, unsigned int freq_in,
    unsigned int freq_out)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 int ret;

 if (!is_valid_pll(aio->chip, pll_id))
  return -EINVAL;

 ret = aio_chip_set_pll(aio->chip, pll_id, freq_out);
 if (ret < 0)
  return ret;

 return 0;
}

static int uniphier_aio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 struct device *dev = &aio->chip->pdev->dev;

 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 case SND_SOC_DAIFMT_LEFT_J:
 case SND_SOC_DAIFMT_RIGHT_J:
 case SND_SOC_DAIFMT_I2S:
  aio->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
  break;
 default:
  dev_err(dev, "Format is not supported(%d)\n",
   fmt & SND_SOC_DAIFMT_FORMAT_MASK);
  return -EINVAL;
 }

 return 0;
}

static int uniphier_aio_startup(struct snd_pcm_substream *substream,
    struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];

 sub->substream = substream;
 sub->pass_through = 0;
 sub->use_mmap = true;

 return aio_init(sub);
}

static void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
      struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];

 sub->substream = NULL;
}

static int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
      struct snd_pcm_hw_params *params,
      struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
 struct device *dev = &aio->chip->pdev->dev;
 int freq, ret;

 switch (params_rate(params)) {
 case 48000:
 case 32000:
 case 24000:
  freq = 12288000;
  break;
 case 44100:
 case 22050:
  freq = 11289600;
  break;
 default:
  dev_err(dev, "Rate is not supported(%d)\n",
   params_rate(params));
  return -EINVAL;
 }
 ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A,
         freq, SND_SOC_CLOCK_OUT);
 if (ret)
  return ret;

 sub->params = *params;
 sub->setting = 1;

 aio_port_reset(sub);
 aio_port_set_volume(sub, sub->vol);
 aio_src_reset(sub);

 return 0;
}

static int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
    struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];

 sub->setting = 0;

 return 0;
}

static int uniphier_aio_prepare(struct snd_pcm_substream *substream,
    struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
 int ret;

 ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
 if (ret)
  return ret;
 ret = aio_src_set_param(sub, &sub->params);
 if (ret)
  return ret;
 aio_port_set_enable(sub, 1);

 ret = aio_if_set_param(sub, sub->pass_through);
 if (ret)
  return ret;

 if (sub->swm->type == PORT_TYPE_CONV) {
  ret = aio_srcif_set_param(sub);
  if (ret)
   return ret;
  ret = aio_srcch_set_param(sub);
  if (ret)
   return ret;
  aio_srcch_set_enable(sub, 1);
 }

 return 0;
}

static int uniphier_aio_dai_probe(struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 int i;

 for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
  struct uniphier_aio_sub *sub = &aio->sub[i];
  const struct uniphier_aio_spec *spec;

  spec = find_spec(aio, dai->name, i);
  if (!spec)
   continue;

  sub->swm = &spec->swm;
  sub->spec = spec;

  sub->vol = AUD_VOL_INIT;
 }

 aio_iecout_set_enable(aio->chip, true);
 aio_chip_init(aio->chip);
 aio->chip->active = 1;

 return 0;
}

static int uniphier_aio_dai_remove(struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);

 aio->chip->active = 0;

 return 0;
}

static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai)
{
 int ret;

 ret = uniphier_aio_dai_probe(dai);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0036864000);
 if (ret < 0)
  return ret;
 ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0036864000);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0033868800);
 if (ret < 0)
  return ret;
 ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0033868800);
 if (ret < 0)
  return ret;

 return 0;
}

static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai)
{
 int ret;

 ret = uniphier_aio_dai_probe(dai);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0036864000);
 if (ret < 0)
  return ret;
 ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0036864000);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0033868800);
 if (ret < 0)
  return ret;
 ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0033868800);
 if (ret < 0)
  return ret;

 return 0;
}

const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops = {
 .probe  = uniphier_aio_ld11_probe,
 .remove  = uniphier_aio_dai_remove,
 .set_sysclk = uniphier_aio_set_sysclk,
 .set_pll = uniphier_aio_set_pll,
 .set_fmt = uniphier_aio_set_fmt,
 .startup = uniphier_aio_startup,
 .shutdown = uniphier_aio_shutdown,
 .hw_params = uniphier_aio_hw_params,
 .hw_free = uniphier_aio_hw_free,
 .prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ld11_ops);

const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops = {
 .probe  = uniphier_aio_ld11_probe,
 .remove  = uniphier_aio_dai_remove,
 .set_sysclk = uniphier_aio_set_sysclk,
 .set_pll = uniphier_aio_set_pll,
 .startup = uniphier_aio_startup,
 .shutdown = uniphier_aio_shutdown,
 .hw_params = uniphier_aio_hw_params,
 .hw_free = uniphier_aio_hw_free,
 .prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops);

const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops2 = {
 .probe  = uniphier_aio_ld11_probe,
 .remove  = uniphier_aio_dai_remove,
 .set_sysclk = uniphier_aio_set_sysclk,
 .set_pll = uniphier_aio_set_pll,
 .startup = uniphier_aio_startup,
 .shutdown = uniphier_aio_shutdown,
 .hw_params = uniphier_aio_hw_params,
 .hw_free = uniphier_aio_hw_free,
 .prepare = uniphier_aio_prepare,
 .compress_new = snd_soc_new_compress,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops2);

const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops = {
 .probe  = uniphier_aio_pxs2_probe,
 .remove  = uniphier_aio_dai_remove,
 .set_sysclk = uniphier_aio_set_sysclk,
 .set_pll = uniphier_aio_set_pll,
 .set_fmt = uniphier_aio_set_fmt,
 .startup = uniphier_aio_startup,
 .shutdown = uniphier_aio_shutdown,
 .hw_params = uniphier_aio_hw_params,
 .hw_free = uniphier_aio_hw_free,
 .prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_pxs2_ops);

const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops = {
 .probe  = uniphier_aio_pxs2_probe,
 .remove  = uniphier_aio_dai_remove,
 .set_sysclk = uniphier_aio_set_sysclk,
 .set_pll = uniphier_aio_set_pll,
 .startup = uniphier_aio_startup,
 .shutdown = uniphier_aio_shutdown,
 .hw_params = uniphier_aio_hw_params,
 .hw_free = uniphier_aio_hw_free,
 .prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops);

const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops2 = {
 .probe  = uniphier_aio_pxs2_probe,
 .remove  = uniphier_aio_dai_remove,
 .set_sysclk = uniphier_aio_set_sysclk,
 .set_pll = uniphier_aio_set_pll,
 .startup = uniphier_aio_startup,
 .shutdown = uniphier_aio_shutdown,
 .hw_params = uniphier_aio_hw_params,
 .hw_free = uniphier_aio_hw_free,
 .prepare = uniphier_aio_prepare,
 .compress_new = snd_soc_new_compress,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops2);

static void uniphier_aio_dai_suspend(struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);

 if (!snd_soc_dai_active(dai))
  return;

 aio->chip->num_wup_aios--;
 if (!aio->chip->num_wup_aios) {
  reset_control_assert(aio->chip->rst);
  clk_disable_unprepare(aio->chip->clk);
 }
}

static int uniphier_aio_suspend(struct snd_soc_component *component)
{
 struct snd_soc_dai *dai;

 for_each_component_dais(component, dai)
  uniphier_aio_dai_suspend(dai);
 return 0;
}

static int uniphier_aio_dai_resume(struct snd_soc_dai *dai)
{
 struct uniphier_aio *aio = uniphier_priv(dai);
 int ret, i;

 if (!snd_soc_dai_active(dai))
  return 0;

 if (!aio->chip->active)
  return 0;

 if (!aio->chip->num_wup_aios) {
  ret = clk_prepare_enable(aio->chip->clk);
  if (ret)
   return ret;

  ret = reset_control_deassert(aio->chip->rst);
  if (ret)
   goto err_out_clock;
 }

 aio_iecout_set_enable(aio->chip, true);
 aio_chip_init(aio->chip);

 for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
  struct uniphier_aio_sub *sub = &aio->sub[i];

  if (!sub->spec || !sub->substream)
   continue;

  ret = aio_init(sub);
  if (ret)
   goto err_out_reset;

  if (!sub->setting)
   continue;

  aio_port_reset(sub);
  aio_src_reset(sub);
 }
 aio->chip->num_wup_aios++;

 return 0;

err_out_reset:
 if (!aio->chip->num_wup_aios)
  reset_control_assert(aio->chip->rst);
err_out_clock:
 if (!aio->chip->num_wup_aios)
  clk_disable_unprepare(aio->chip->clk);

 return ret;
}

static int uniphier_aio_resume(struct snd_soc_component *component)
{
 struct snd_soc_dai *dai;
 int ret = 0;

 for_each_component_dais(component, dai)
  ret |= uniphier_aio_dai_resume(dai);
 return ret;
}

static int uniphier_aio_vol_info(struct snd_kcontrol *kcontrol,
     struct snd_ctl_elem_info *uinfo)
{
 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 uinfo->count = 1;
 uinfo->value.integer.min = 0;
 uinfo->value.integer.max = AUD_VOL_MAX;

 return 0;
}

static int uniphier_aio_vol_get(struct snd_kcontrol *kcontrol,
    struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
 struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp);
 struct uniphier_aio_sub *sub;
 int oport_hw = kcontrol->private_value;

 sub = find_volume(chip, oport_hw);
 if (!sub)
  return 0;

 ucontrol->value.integer.value[0] = sub->vol;

 return 0;
}

static int uniphier_aio_vol_put(struct snd_kcontrol *kcontrol,
    struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
 struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp);
 struct uniphier_aio_sub *sub;
 int oport_hw = kcontrol->private_value;

 sub = find_volume(chip, oport_hw);
 if (!sub)
  return 0;

 if (sub->vol == ucontrol->value.integer.value[0])
  return 0;
 sub->vol = ucontrol->value.integer.value[0];

 aio_port_set_volume(sub, sub->vol);

 return 0;
}

static const struct snd_kcontrol_new uniphier_aio_controls[] = {
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  .name = "HPCMOUT1 Volume",
  .info = uniphier_aio_vol_info,
  .get = uniphier_aio_vol_get,
  .put = uniphier_aio_vol_put,
  .private_value = AUD_HW_HPCMOUT1,
 },
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  .name = "PCMOUT1 Volume",
  .info = uniphier_aio_vol_info,
  .get = uniphier_aio_vol_get,
  .put = uniphier_aio_vol_put,
  .private_value = AUD_HW_PCMOUT1,
 },
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  .name = "PCMOUT2 Volume",
  .info = uniphier_aio_vol_info,
  .get = uniphier_aio_vol_get,
  .put = uniphier_aio_vol_put,
  .private_value = AUD_HW_PCMOUT2,
 },
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  .name = "PCMOUT3 Volume",
  .info = uniphier_aio_vol_info,
  .get = uniphier_aio_vol_get,
  .put = uniphier_aio_vol_put,
  .private_value = AUD_HW_PCMOUT3,
 },
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  .name = "HIECOUT1 Volume",
  .info = uniphier_aio_vol_info,
  .get = uniphier_aio_vol_get,
  .put = uniphier_aio_vol_put,
  .private_value = AUD_HW_HIECOUT1,
 },
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  .name = "IECOUT1 Volume",
  .info = uniphier_aio_vol_info,
  .get = uniphier_aio_vol_get,
  .put = uniphier_aio_vol_put,
  .private_value = AUD_HW_IECOUT1,
 },
};

static const struct snd_soc_component_driver uniphier_aio_component = {
 .name = "uniphier-aio",
 .controls = uniphier_aio_controls,
 .num_controls = ARRAY_SIZE(uniphier_aio_controls),
 .suspend = uniphier_aio_suspend,
 .resume  = uniphier_aio_resume,
};

int uniphier_aio_probe(struct platform_device *pdev)
{
 struct uniphier_aio_chip *chip;
 struct device *dev = &pdev->dev;
 int ret, i, j;

 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 if (!chip)
  return -ENOMEM;

 chip->chip_spec = of_device_get_match_data(dev);
 if (!chip->chip_spec)
  return -EINVAL;

 chip->regmap_sg = syscon_regmap_lookup_by_phandle(dev->of_node,
         "socionext,syscon");
 if (IS_ERR(chip->regmap_sg)) {
  if (PTR_ERR(chip->regmap_sg) == -EPROBE_DEFER)
   return -EPROBE_DEFER;
  chip->regmap_sg = NULL;
 }

 chip->clk = devm_clk_get(dev, "aio");
 if (IS_ERR(chip->clk))
  return PTR_ERR(chip->clk);

 chip->rst = devm_reset_control_get_shared(dev, "aio");
 if (IS_ERR(chip->rst))
  return PTR_ERR(chip->rst);

 chip->num_aios = chip->chip_spec->num_dais;
 chip->num_wup_aios = chip->num_aios;
 chip->aios = devm_kcalloc(dev,
      chip->num_aios, sizeof(struct uniphier_aio),
      GFP_KERNEL);
 if (!chip->aios)
  return -ENOMEM;

 chip->num_plls = chip->chip_spec->num_plls;
 chip->plls = devm_kmemdup_array(dev, chip->chip_spec->plls, chip->num_plls,
     sizeof(*chip->chip_spec->plls), GFP_KERNEL);
 if (!chip->plls)
  return -ENOMEM;

 for (i = 0; i < chip->num_aios; i++) {
  struct uniphier_aio *aio = &chip->aios[i];

  aio->chip = chip;
  aio->fmt = SND_SOC_DAIFMT_I2S;

  for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
   struct uniphier_aio_sub *sub = &aio->sub[j];

   sub->aio = aio;
   spin_lock_init(&sub->lock);
  }
 }

 chip->pdev = pdev;
 platform_set_drvdata(pdev, chip);

 ret = clk_prepare_enable(chip->clk);
 if (ret)
  return ret;

 ret = reset_control_deassert(chip->rst);
 if (ret)
  goto err_out_clock;

 ret = devm_snd_soc_register_component(dev, &uniphier_aio_component,
           chip->chip_spec->dais,
           chip->chip_spec->num_dais);
 if (ret) {
  dev_err(dev, "Register component failed.\n");
  goto err_out_reset;
 }

 ret = uniphier_aiodma_soc_register_platform(pdev);
 if (ret) {
  dev_err(dev, "Register platform failed.\n");
  goto err_out_reset;
 }

 return 0;

err_out_reset:
 reset_control_assert(chip->rst);

err_out_clock:
 clk_disable_unprepare(chip->clk);

 return ret;
}
EXPORT_SYMBOL_GPL(uniphier_aio_probe);

void uniphier_aio_remove(struct platform_device *pdev)
{
 struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);

 reset_control_assert(chip->rst);
 clk_disable_unprepare(chip->clk);
}
EXPORT_SYMBOL_GPL(uniphier_aio_remove);

MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier AIO CPU DAI driver.");
MODULE_LICENSE("GPL v2");

Messung V0.5 in Prozent
C=92 H=92 G=91

[0.14QuellennavigatorsProjekt 2026-06-07]