linuxUDA134X ASoc板驅動

 

static int __init s3c24xx_uda134x_init(void)
{
 return platform_driver_register(&s3c24xx_uda134x_driver);
}

static struct platform_driver s3c24xx_uda134x_driver = {
 .probe  = s3c24xx_uda134x_probe,
 .remove = s3c24xx_uda134x_remove,
 .driver = {
  .name = "s3c24xx_uda134x",
  .owner = THIS_MODULE,
 },
};

這裏就是模塊初始化函數。我們都知道當註冊成功的時候也就是.name和平臺設備的.name匹配成功的時候將調用.probe,這裏就是s3c24xx_uda134x_probe。那麼平臺的設備是什麼時候加入的呢。看linux-2.6.32.2\arch\arm\mach-s3c2440\mach-mini2440.中的

mini2440_machine_init()

{...

  platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

  .....

}

static struct platform_device *mini2440_devices[] __initdata = {

...
 &s3c24xx_uda134x,

...
 };

static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = {
 .l3_clk = S3C2410_GPB(4),
 .l3_data = S3C2410_GPB(3),
 .l3_mode = S3C2410_GPB(2),
 .model = UDA134X_UDA1341,
};

static struct platform_device s3c24xx_uda134x = {
 .name = "s3c24xx_uda134x",
 .dev = {
  .platform_data    = &s3c24xx_uda134x_data,
 }
};

所以s3c24xx_uda134x_probe會得到運行。講這個函數之前先看看一些結構體,變量

static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins用來指向&s3c24xx_uda134x_data

tatic struct uda134x_platform_data s3c24xx_uda134x = {
 .l3 = {
  .setdat = setdat,
  .setclk = setclk,
  .setmode = setmode,
  .data_hold = 1,
  .data_setup = 1,
  .clock_high = 1,
  .mode_hold = 1,
  .mode = 1,
  .mode_setup = 1,
 },
};與L3MODE,L3CLOCK,L3DATA有關,也就是編碼相關的數據。它會賦值給static struct snd_soc_device s3c24xx_uda134x_snd_devdata

snd_soc_device s3c24xx_uda134x_snd_devdata非常重要。是對ASoc設備的整體封裝。包括封裝ASoc Codec設備用的snd_soc_codec_device(.codec_dev 成員),板和平臺設備用的snd_soc_card組件(.card成員)。

--------------------------------------------------------------------------------------

static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
 .card = &snd_soc_s3c24xx_uda134x,
 .codec_dev = &soc_codec_dev_uda134x,
 .codec_data = &s3c24xx_uda134x,
};

---------------------------------------------------------------------------------------

封裝的ASoc Codec設備用的snd_soc_codec_device(.codec_dev 成員)定義如下:

struct snd_soc_codec_device soc_codec_dev_uda134x = {
 .probe =        uda134x_soc_probe,
 .remove =       uda134x_soc_remove,
 .suspend =      uda134x_soc_suspend,
 .resume =       uda134x_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);

在snd_soc_card組件(.card成員)中封裝了板和平臺的定義如下:

static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
 .name = "S3C24XX_UDA134X",
 .platform = &s3c24xx_soc_platform,平臺,在s3c24xx-pcm.c中註冊了平臺
 .dai_link = &s3c24xx_uda134x_dai_link,板
 .num_links = 1,
};

板&s3c24xx_uda134x_dai_link起綁定codec驅動和平臺I2C驅動的作用定義:

static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
 .name = "UDA134X",
 .stream_name = "UDA134X",
 .codec_dai = &uda134x_dai,//codec驅動
 .cpu_dai = &s3c24xx_i2s_dai,//平臺I2S驅動
 .ops = &s3c24xx_uda134x_ops,//操作板驅動的函數
};

static struct snd_soc_ops s3c24xx_uda134x_ops = {
 .startup = s3c24xx_uda134x_startup,
 .shutdown = s3c24xx_uda134x_shutdown,
 .hw_params = s3c24xx_uda134x_hw_params,
};

 

最後看看s3c24xx_uda134x_probe()做了什麼:

獲得設備資源賦值給s3c24xx_uda134x_l3_pins。設置s3c24xx_uda134x的power,model.

通過函數s3c24xx_uda134x_setup_pin()獲得音頻口的clk,data,model引腳使用,設置輸出爲0;

申請s3c24xx_uda134x_snd_device內存並通過platform_set_drvdata()設置ASoc設備的整體封裝s3c24xx_uda134x_snd_devdata 爲其私有數據。然後設置s3c24xx_uda134x_snd_devdata的dev指向s3c24xx_uda134x_snd_device的dev.最後添加s3c24xx_uda134x_snd_device平臺設備。

添加這的這個平臺設備.name爲“soc-audio”,在soc-core.c有個平臺驅動的.name也爲“soc-audio”。

所以會引起匹配調用在soc-core.c文件中的.probe,所以s3c24xx_uda134x_snd_device會傳遞給soc-core.c文件中的.probe函數作爲參數。也就完成了ASoc設備的整體封裝s3c24xx_uda134x_snd_devdata的傳遞。

 

 

下面是板驅動的源代碼:

#include <linux/module.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/s3c24xx_uda134x.h>
#include <sound/uda134x.h>

#include <plat/regs-iis.h>

#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
#include "../codecs/uda134x.h"



static struct clk *xtal;
static struct clk *pclk;

static int clk_users;
static DEFINE_MUTEX(clk_lock);

static unsigned int rates[33 * 2];
#ifdef ENFORCE_RATES
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
 .count = ARRAY_SIZE(rates),
 .list = rates,
 .mask = 0,
};
#endif

static struct platform_device *s3c24xx_uda134x_snd_device;

static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
{
 int ret = 0;
#ifdef ENFORCE_RATES
 struct snd_pcm_runtime *runtime = substream->runtime;
#endif

 mutex_lock(&clk_lock);
 pr_debug("%s %d\n", __func__, clk_users);
 if (clk_users == 0) {
  xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
  if (!xtal) {
   printk(KERN_ERR "%s cannot get xtal\n", __func__);
   ret = -EBUSY;
  } else {
   pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
           "pclk");
   if (!pclk) {
    printk(KERN_ERR "%s cannot get pclk\n",
           __func__);
    clk_put(xtal);
    ret = -EBUSY;
   }
  }
  if (!ret) {
   int i, j;

   for (i = 0; i < 2; i++) {
    int fs = i ? 256 : 384;

    rates[i*33] = clk_get_rate(xtal) / fs;
    for (j = 1; j < 33; j++)
     rates[i*33 + j] = clk_get_rate(pclk) /
      (j * fs);
   }
  }
 }
 clk_users += 1;
 mutex_unlock(&clk_lock);
 if (!ret) {
#ifdef ENFORCE_RATES
  ret = snd_pcm_hw_constraint_list(runtime, 0,
       SNDRV_PCM_HW_PARAM_RATE,
       &hw_constraints_rates);
  if (ret < 0)
   printk(KERN_ERR "%s cannot set constraints\n",
          __func__);
#endif
 }
 return ret;
}

static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
{
 mutex_lock(&clk_lock);
 pr_debug("%s %d\n", __func__, clk_users);
 clk_users -= 1;
 if (clk_users == 0) {
  clk_put(xtal);
  xtal = NULL;
  clk_put(pclk);
  pclk = NULL;
 }
 mutex_unlock(&clk_lock);
}

static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
     struct snd_pcm_hw_params *params)
{
 struct snd_soc_pcm_runtime *rtd = substream->private_data;
 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 unsigned int clk = 0;
 int ret = 0;
 int clk_source, fs_mode;
 unsigned long rate = params_rate(params);
 long err, cerr;
 unsigned int div;
 int i, bi;

 err = 999999;
 bi = 0;
 for (i = 0; i < 2*33; i++) {
  cerr = rates[i] - rate;
  if (cerr < 0)
   cerr = -cerr;
  if (cerr < err) {
   err = cerr;
   bi = i;
  }
 }
 if (bi / 33 == 1)
  fs_mode = S3C2410_IISMOD_256FS;
 else
  fs_mode = S3C2410_IISMOD_384FS;
 if (bi % 33 == 0) {
  clk_source = S3C24XX_CLKSRC_MPLL;
  div = 1;
 } else {
  clk_source = S3C24XX_CLKSRC_PCLK;
  div = bi % 33;
 }
 pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);

 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
   fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
   clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
   div, clk, err);

 if ((err * 100 / rate) > 5) {
  printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
         "too different from desired (%ld%%)\n",
         err * 100 / rate);
  return -EINVAL;
 }

 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
   SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
   SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
   SND_SOC_CLOCK_IN);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
   S3C2410_IISMOD_32FS);
 if (ret < 0)
  return ret;

 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
   S3C24XX_PRESCALE(div, div));
 if (ret < 0)
  return ret;

 
 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
   SND_SOC_CLOCK_OUT);
 if (ret < 0)
  return ret;

 return 0;
}

static struct snd_soc_ops s3c24xx_uda134x_ops = {
 .startup = s3c24xx_uda134x_startup,
 .shutdown = s3c24xx_uda134x_shutdown,
 .hw_params = s3c24xx_uda134x_hw_params,
};

static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
 .name = "UDA134X",
 .stream_name = "UDA134X",
 .codec_dai = &uda134x_dai,
 .cpu_dai = &s3c24xx_i2s_dai,
 .ops = &s3c24xx_uda134x_ops,
};

static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
 .name = "S3C24XX_UDA134X",
 .platform = &s3c24xx_soc_platform,
 .dai_link = &s3c24xx_uda134x_dai_link,
 .num_links = 1,
};

static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;

static void setdat(int v)
{
 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
}

static void setclk(int v)
{
 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
}

static void setmode(int v)
{
 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
}

static struct uda134x_platform_data s3c24xx_uda134x = {
 .l3 = {
  .setdat = setdat,
  .setclk = setclk,
  .setmode = setmode,
  .data_hold = 1,
  .data_setup = 1,
  .clock_high = 1,
  .mode_hold = 1,
  .mode = 1,
  .mode_setup = 1,
 },
};

static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
 .card = &snd_soc_s3c24xx_uda134x,
 .codec_dev = &soc_codec_dev_uda134x,
 .codec_data = &s3c24xx_uda134x,
};

static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
{
 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
  printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
         "l3 %s pin already in use", fun);
  return -EBUSY;
 }
 gpio_direction_output(pin, 0);
 return 0;
}

static int s3c24xx_uda134x_probe(struct platform_device *pdev)
{
 int ret;

 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");

 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
 if (s3c24xx_uda134x_l3_pins == NULL) {
  printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
         "unable to find platform data\n");
  return -ENODEV;
 }
 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;

 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
          "data") < 0)
  return -EBUSY;
 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
          "clk") < 0) {
  gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
  return -EBUSY;
 }
 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
          "mode") < 0) {
  gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
  gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
  return -EBUSY;
 }

 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
 if (!s3c24xx_uda134x_snd_device) {
  printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
         "Unable to register\n");
  return -ENOMEM;
 }

 platform_set_drvdata(s3c24xx_uda134x_snd_device,
        &s3c24xx_uda134x_snd_devdata);
 s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
 ret = platform_device_add(s3c24xx_uda134x_snd_device);
 if (ret) {
  printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
  platform_device_put(s3c24xx_uda134x_snd_device);
 }

 return ret;
}

static int s3c24xx_uda134x_remove(struct platform_device *pdev)
{
 platform_device_unregister(s3c24xx_uda134x_snd_device);
 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
 return 0;
}

static struct platform_driver s3c24xx_uda134x_driver = {
 .probe  = s3c24xx_uda134x_probe,
 .remove = s3c24xx_uda134x_remove,
 .driver = {
  .name = "s3c24xx_uda134x",
  .owner = THIS_MODULE,
 },
};

static int __init s3c24xx_uda134x_init(void)
{
 return platform_driver_register(&s3c24xx_uda134x_driver);
}

static void __exit s3c24xx_uda134x_exit(void)
{
 platform_driver_unregister(&s3c24xx_uda134x_driver);
}


module_init(s3c24xx_uda134x_init);
module_exit(s3c24xx_uda134x_exit);

MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <[email protected]>");
MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
MODULE_LICENSE("GPL");

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章