|
三.Wm9713的codec 部分的分析
欢迎加入qq群138581843讨论,提供所有设计的代码
1. codec
再次引用上面提到的内容由于在sound\soc\samsung\smdk_wm97
13.c文件的初始化函数smdk_init 中注册一个名词为"soc-audio"的平台设备(device)
smdk_snd_wm9713_device =
platform_device_alloc("wm9713-codec", -1);//创建名字为wm9713-codec的设备
根据匹配规则相应的就应该有一个同名的平台驱动(driver),平台驱动注册在文件sound\soc\codecs\wm9713.c
static struct platform_driver wm9713_codec_driver = {
.driver = {
.name = "wm9713-codec",//这里定义名字为“wm9713-codec”平台驱动
.owner = THIS_MODULE,
},
.probe = wm9713_probe,//制定probe函数为当前文件的wm9713_probe函数
.remove = __devexit_p(wm9713_remove),
};
SSss
wm9713_probe的主体
snd_soc_register_codec(&pdev->dev,&soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)
这个函数主要设置注册wm9713的解码器,从而引出两个结构体snd_soc_dai_driver和snd_soc_codec_drive。在说明这两个结构体之前,先介绍一下snd_soc_codec,snd_soc_codec_driver,snd_soc_dai,snd_soc_dai_driver,其中的snd_soc_dai和snd_soc_dai_driver在ASoC的Platform驱动中也会使用到,Platform和Codec的DAI通过snd_soc_dai_link结构,在Machine驱动中进行绑定连接。下面详细贴出这四个结构体涉及到的成员说明
snd_soc_codec:
1. /* SoC Audio Codec device */
2. struct snd_soc_codec {
3. const char *name; /* Codec的名字*/
4. struct device *dev; /* 指向Codec设备的指针 */
5. const struct snd_soc_codec_driver *driver; /* 指向该codec的驱动的指针 */
6. struct snd_soc_card *card; /* 指向Machine驱动的card实例 */
7. int num_dai; /* 该Codec数字接口的个数,目前越来越多的Codec带有多个I2S或者是PCM接口 */
8. int (*volatile_register)(...); /* 用于判定某一寄存器是否是volatile */
9. int (*readable_register)(...); /* 用于判定某一寄存器是否可读 */
10. int (*writable_register)(...); /* 用于判定某一寄存器是否可写 */
11.
12. /* runtime */
13. ......
14. /* codec IO */
15. void *control_data; /* 该指针指向的结构用于对codec的控制,通常和read,write字段联合使用 */
16. enum snd_soc_control_type control_type;/* 可以是SND_SOC_SPI,SND_SOC_I2C,SND_SOC_REGMAP中的一种 */
17. unsigned int (*read)(struct snd_soc_codec *, unsigned int); /* 读取Codec寄存器的函数 */
18. int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); /* 写入Codec寄存器的函数 */
19. /* dapm */
20. struct snd_soc_dapm_context dapm; /* 用于DAPM控件 */
21. };
/* SoC Audio Codec device */
struct snd_soc_codec {
const char *name; /* Codec的名字*/
struct device *dev; /* 指向Codec设备的指针 */
const struct snd_soc_codec_driver *driver; /* 指向该codec的驱动的指针 */
struct snd_soc_card *card; /* 指向Machine驱动的card实例 */
int num_dai; /* 该Codec数字接口的个数,目前越来越多的Codec带有多个I2S或者是PCM接口 */
int (*volatile_register)(...); /* 用于判定某一寄存器是否是volatile */
int (*readable_register)(...); /* 用于判定某一寄存器是否可读 */
int (*writable_register)(...); /* 用于判定某一寄存器是否可写 */
/* runtime */
......
/* codec IO */
void *control_data; /* 该指针指向的结构用于对codec的控制,通常和read,write字段联合使用 */
enum snd_soc_control_type control_type;/* 可以是SND_SOC_SPI,SND_SOC_I2C,SND_SOC_REGMAP中的一种 */
unsigned int (*read)(struct snd_soc_codec *, unsigned int); /* 读取Codec寄存器的函数 */
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); /* 写入Codec寄存器的函数 */
/* dapm */
struct snd_soc_dapm_context dapm; /* 用于DAPM控件 */
};
snd_soc_codec_driver:
1. /* codec driver */
2. struct snd_soc_codec_driver {
3. /* driver ops */
4. int (*probe)(struct snd_soc_codec *); /* codec驱动的probe函数,由snd_soc_instantiate_card回调 */
5. int (*remove)(struct snd_soc_codec *);
6. int (*suspend)(struct snd_soc_codec *); /* 电源管理 */
7. int (*resume)(struct snd_soc_codec *); /* 电源管理 */
8.
9. /* Default control and setup, added after probe() is run */
10. const struct snd_kcontrol_new *controls; /* 音频控件指针 */
11. const struct snd_soc_dapm_widget *dapm_widgets; /* dapm部件指针 */
12. const struct snd_soc_dapm_route *dapm_routes; /* dapm路由指针 */
13.
14. /* codec wide operations */
15. int (*set_sysclk)(...); /* 时钟配置函数 */
16. int (*set_pll)(...); /* 锁相环配置函数 */
17.
18. /* codec IO */
19. unsigned int (*read)(...); /* 读取codec寄存器函数 */
20. int (*write)(...); /* 写入codec寄存器函数 */
21. int (*volatile_register)(...); /* 用于判定某一寄存器是否是volatile */
22. int (*readable_register)(...); /* 用于判定某一寄存器是否可读 */
23. int (*writable_register)(...); /* 用于判定某一寄存器是否可写 */
24.
25. /* codec bias level */
26. int (*set_bias_level)(...); /* 偏置电压配置函数 */
27.
28. };
/* codec driver */
struct snd_soc_codec_driver {
/* driver ops */
int (*probe)(struct snd_soc_codec *); /* codec驱动的probe函数,由snd_soc_instantiate_card回调 */
int (*remove)(struct snd_soc_codec *);
int (*suspend)(struct snd_soc_codec *); /* 电源管理 */
int (*resume)(struct snd_soc_codec *); /* 电源管理 */
/* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls; /* 音频控件指针 */
const struct snd_soc_dapm_widget *dapm_widgets; /* dapm部件指针 */
const struct snd_soc_dapm_route *dapm_routes; /* dapm路由指针 */
/* codec wide operations */
int (*set_sysclk)(...); /* 时钟配置函数 */
int (*set_pll)(...); /* 锁相环配置函数 */
/* codec IO */
unsigned int (*read)(...); /* 读取codec寄存器函数 */
int (*write)(...); /* 写入codec寄存器函数 */
int (*volatile_register)(...); /* 用于判定某一寄存器是否是volatile */
int (*readable_register)(...); /* 用于判定某一寄存器是否可读 */
int (*writable_register)(...); /* 用于判定某一寄存器是否可写 */
/* codec bias level */
int (*set_bias_level)(...); /* 偏置电压配置函数 */
};
snd_soc_dai:
1. /*
2. * Digital Audio Interface runtime data.
3. *
4. * Holds runtime data for a DAI.
5. */
6. struct snd_soc_dai {
7. const char *name; /* dai的名字 */
8. struct device *dev; /* 设备指针 */
9.
10. /* driver ops */
11. struct snd_soc_dai_driver *driver; /* 指向dai驱动结构的指针 */
12.
13. /* DAI runtime info */
14. unsigned int capture_active:1; /* stream is in use */
15. unsigned int playback_active:1; /* stream is in use */
16.
17. /* DAI DMA data */
18. void *playback_dma_data; /* 用于管理playback dma */
19. void *capture_dma_data; /* 用于管理capture dma */
20.
21. /* parent platform/codec */
22. union {
23. struct snd_soc_platform *platform; /* 如果是cpu dai,指向所绑定的平台 */
24. struct snd_soc_codec *codec; /* 如果是codec dai指向所绑定的codec */
25. };
26. struct snd_soc_card *card; /* 指向Machine驱动中的crad实例 */
27. };
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI.
*/
struct snd_soc_dai {
const char *name; /* dai的名字 */
struct device *dev; /* 设备指针 */
/* driver ops */
struct snd_soc_dai_driver *driver; /* 指向dai驱动结构的指针 */
/* DAI runtime info */
unsigned int capture_active:1; /* stream is in use */
unsigned int playback_active:1; /* stream is in use */
/* DAI DMA data */
void *playback_dma_data; /* 用于管理playback dma */
void *capture_dma_data; /* 用于管理capture dma */
/* parent platform/codec */
union {
struct snd_soc_platform *platform; /* 如果是cpu dai,指向所绑定的平台 */
struct snd_soc_codec *codec; /* 如果是codec dai指向所绑定的codec */
};
struct snd_soc_card *card; /* 指向Machine驱动中的crad实例 */
};
snd_soc_dai_driver:
1. /*
2. * Digital Audio Interface Driver.
3. *
4. * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
5. * operations and capabilities. Codec and platform drivers will register this
6. * structure for every DAI they have.
7. *
8. * This structure covers the clocking, formating and ALSA operations for each
9. * interface.
10. */
11. struct snd_soc_dai_driver {
12. /* DAI description */
13. const char *name; /* dai驱动名字 */
14.
15. /* DAI driver callbacks */
16. int (*probe)(struct snd_soc_dai *dai); /* dai驱动的probe函数,由snd_soc_instantiate_card回调 */
17. int (*remove)(struct snd_soc_dai *dai);
18. int (*suspend)(struct snd_soc_dai *dai); /* 电源管理 */
19. int (*resume)(struct snd_soc_dai *dai);
20.
21. /* ops */
22. const struct snd_soc_dai_ops *ops; /* 指向本dai的snd_soc_dai_ops结构 */
23.
24. /* DAI capabilities */
25. struct snd_soc_pcm_stream capture; /* 描述capture的能力 */
26. struct snd_soc_pcm_stream playback; /* 描述playback的能力 */
27. };
/*
* Digital Audio Interface Driver.
*
* Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
* operations and capabilities. Codec and platform drivers will register this
* structure for every DAI they have.
*
* This structure covers the clocking, formating and ALSA operations for each
* interface.
*/
struct snd_soc_dai_driver {
/* DAI description */
const char *name; /* dai驱动名字 */
/* DAI driver callbacks */
int (*probe)(struct snd_soc_dai *dai); /* dai驱动的probe函数,由snd_soc_instantiate_card回调 */
int (*remove)(struct snd_soc_dai *dai);
int (*suspend)(struct snd_soc_dai *dai); /* 电源管理 */
int (*resume)(struct snd_soc_dai *dai);
/* ops */
const struct snd_soc_dai_ops *ops; /* 指向本dai的snd_soc_dai_ops结构 */
/* DAI capabilities */
struct snd_soc_pcm_stream capture; /* 描述capture的能力 */
struct snd_soc_pcm_stream playback; /* 描述playback的能力 */
};
snd_soc_dai_ops用于实现该dai的控制和参数配置:
1. struct snd_soc_dai_ops {
2. /*
3. * DAI clocking configuration, all optional.
4. * Called by soc_card drivers, normally in their hw_params.
5. */
6. int (*set_sysclk)(...);
7. int (*set_pll)(...);
8. int (*set_clkdiv)(...);
9. /*
10. * DAI format configuration
11. * Called by soc_card drivers, normally in their hw_params.
12. */
13. int (*set_fmt)(...);
14. int (*set_tdm_slot)(...);
15. int (*set_channel_map)(...);
16. int (*set_tristate)(...);
17. /*
18. * DAI digital mute - optional.
19. * Called by soc-core to minimise any pops.
20. */
21. int (*digital_mute)(...);
22. /*
23. * ALSA PCM audio operations - all optional.
24. * Called by soc-core during audio PCM operations.
25. */
26. int (*startup)(...);
27. void (*shutdown)(...);
28. int (*hw_params)(...);
29. int (*hw_free)(...);
30. int (*prepare)(...);
31. int (*trigger)(...);
32. /*
33. * For hardware based FIFO caused delay reporting.
34. * Optional.
35. */
36. snd_pcm_sframes_t (*delay)(...);
37. };
先贴出来我们自己的snd_soc_dai_driver和snd_soc_codec_driv都是怎么配置的
snd_soc_codec_driver
static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
.probe = wm9713_soc_probe,
.remove = wm9713_soc_remove,
#ifdef CONFIG_PM
.suspend = wm9713_soc_suspend,
.resume = wm9713_soc_resume,
#endif
.read = ac97_read,
.write = ac97_write,
.set_bias_level = wm9713_set_bias_level,
.reg_cache_size = ARRAY_SIZE(wm9713_reg),
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9713_reg,
.dapm_widgets = wm9713_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
.dapm_routes = wm9713_audio_map,
.num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
};
struct snd_soc_dai_driver
static struct snd_soc_dai_driver wm9713_dai[] = {
{
.name = "wm9713-hifi",
.ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM9713_RATES,
.formats = SND_SOC_STD_AC97_FMTS,},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM9713_RATES,
.formats = SND_SOC_STD_AC97_FMTS,},
.ops = &wm9713_dai_ops_hifi,
},
……
};
struct snd_soc_dai_ops
static struct snd_soc_dai_ops wm9713_dai_ops_hifi = {
.prepare = ac97_hifi_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,
};
下面分析snd_soc_register_codec
snd_soc_register_codec首先分配给snd_soc_codec结构snd_soc_codec分配内存实例化,
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
确定codec的名字,名字很重要以后调用soc_probe_dai_link时候将绑定相应的codec,上文第二章已经提到
codec->name = fmt_single_name(dev, &codec->id);
然后根据传进来的snd_soc_codec_driver填充snd_soc_codec结构,紧接着进行相应的初始化配置,完成这一步后,就可以使用API:snd_soc_read(),snd_soc_write()对codec的寄存器进行读写了。
codec->write = codec_drv->write;
codec->read = codec_drv->read;
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
codec->writable_register = codec_drv->writable_register;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;
codec->dapm.seq_notifier = codec_drv->seq_notifier;
codec->dev = dev;
codec->driver = codec_drv;
codec->num_dai = num_dai
进行一些寄存器缓存的初始化和配置
if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {
reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
codec->reg_size = reg_size;
/* it is necessary to make a copy of the default register cache
* because in the case of using a compression type that requires
* the default register cache to be marked as __devinitconst the
* kernel might have freed the array by the time we initialize
* the cache.
*/
if (codec_drv->reg_cache_default) {
codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,
reg_size, GFP_KERNEL);
if (!codec->reg_def_copy) {
ret = -ENOMEM;
goto fail;
}
}
}
根据我们配置的snd_soc_dai_driver的playback和capture内的farmats配置找到数组codec_format_map中相应的配置
for (i = 0; i < num_dai; i++) {
fixup_codec_formats(&dai_drv[i].playback);
fixup_codec_formats(&dai_drv[i].capture);
}
之后进行一次codec dai 注册
snd_soc_register_dais(dev, dai_drv, num_dai);
snd_soc_register_dais和snd_soc_register_codec基本相似也是先实例snd_soc_dai结构,初始化,最后添加全局变量链表dai_list(list_add(&dai->list, &dai_list);)这里插入一句话其实大家不难发现codec dai和cpu dai都在一个链表(dai_list)里而cpu dai 在哪里注册呢,cpu dai 在文件linux-3.0-rc6\sound\soc\samsung\ac97.c中注册后继具体说明
dai的注册
snd_soc_register_codec然后要填充全局变量链表codec_list
list_add(&codec->list, &codec_list);
最后调用snd_soc_instantiate_cards触发Machine驱动进行一次匹配绑定操作。
至此alsa框架的codec协议相关我们终于理顺了,下面我们可以看到我们驱动的主体函数,这里我们看到snd_soc_codec_driver结构里有几个重要的驱动相关的函数,这也是我们要实现的。
2. codec 初始化
上文提到当machine调用snd_soc_instantiate_cards()进行dai,codec,platform匹配的时候,一旦匹配成功则会调用codec和dai驱动相应probe函数,而在dai,codec,platform注册的时候也都调用snd_soc_instantiate_cards()进行匹配。
而本codec的probe函数正是wm9713_soc_probe函数。
wm9713_soc_probe很简单主要做了向设备添加ac97底层接口操作函数集soc_ac97_ops和控制部件(注:这里的wm9713_soc_probe参数类似于platform设备——驱动模型,他们的设备和驱动公用同一个结构,也就是说明wm9713_soc_probe在codec里类似于全局变量)。详细如下:
● 申请一个wm8994_priv私有数据结构,并把它设为codec设备的driver_data;
struct wm9713_priv *wm9713;
……
wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
……
snd_soc_codec_set_drvdata(codec, wm9713);
● 向设备添加ac97底层接口操作函数集soc_ac97_ops(linux-3.0-rc6\sound\soc\samsung ac97.c后边将提到。);
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
● 设置合适的偏置电平;
● 添加必要的control部件;
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls));
至此,codec驱动的初始化完成。
讲到这里,大家可能有那么一点点小迷糊和疑问,可能会问到我们之前在soc_codec_dev_wm9713配置时候指定的函数
.read = ac97_read,
.write = ac97_write,
……
和刚刚提到的ac97底层接口操作函数集soc_ac97_ops是怎么注册进来的,或者初始化操作是在哪里发生的呢?下文将一一理顺。
总结一下codec部分的相关工作,本质就是填充codec_list,实现上文(第一章.基础准备)提到的alsa数据结构表中的codec部分。如下图
Codec
Codec_dai
注意这里的dai指的是codec dai与之区别的还有cpu_dai必须明确这一点
3. 声卡的通信接口
wm9713有两大接口,一是AC97,另一是PCM/I2S, 看tq2440底板原理图,不难发现WM9713的PCM/I2S的接口没接线,故wm9713的PCM|I2S没用上, wm9713采用ac97接口。
而市场上有一些芯片不采用ac97实现,比如WM1811,WM8994,WM8958,这些芯片本身具备多种功能,除了codec外,它还有作为LDO和GPIO使用,这几种功能共享一些IO和中断资源,linux为这种设备提供了一套标准的实现方法:mfd设备,其基本思想是为这些功能的公共部分实现一个父设备,以便共享某些系统资源和功能,然后每个子功能实现为它的子设备,这样既共享了资源和代码,又能实现合理的设备层次结构,主要利用到的API就是:mfd_add_devices(),mfd_remove_devices(),mfd_cell_enable(),mfd_cell_disable(),mfd_clone_cell()。为了以后学习这里只是提一下mfd设备,因为wm9713没有使用mfd设备,这里不详述,有兴趣可以参考相关文章。
我上一个开发板是tq2440,它的声卡是l3接口用做控制,i2s声音的标准接口用作传输声音数据。由于wm9713用ac97通信接口,同时实现控制和声音数据通信。个人认为这就是没有采用mfd设备的原因。下面进入主题。
1. 有关AC97 Spec与WM9713的时钟关系
24.576MHz外部晶振给WM9713供给MCLKA。再由WM9713于内部供给AC97 CLK:24.576MHz,此处于WM9713 Reg44h可配置
然后AC97 CLK:24.576MHz再供给BITCLK:12.288MHz,这个BITCLK再由WM9713提供给S3C6410用,6410再利用BITCLK:12.288MHz去产生SYNC:48KHz,这个48KHz的SYNC将反过来再供给WM9713芯片及6410的AC97 Controller,以使两者同步
Refer: WM9713 Spec P22
另外只要24.576MHz的外部晶振一定,供给WM9713的MCLKA,WM9713自动会将其分频至12.288MHz给BITCLK,送往6410 AC97 Controller
12.288MHz的BITCLK产生48KHz的SYNC的机理:
Refer: 6410手册35-5:
BITCLK:12.288MHz
SYNC:48KHz=12.288MHz/256bit
一个AC97帧由13个Slot组成,第1个 NO.0 Slot:16bit组成Tag Phase
第2--13个即:NO.1--12 Slot:每个Slot 20bit,组成Data Phase
Total Bits: 16bit+12*20=256bit
2.ac97总线设备驱动
1)、samsung-ac97设备
上面提到关于codec里 ac97_read,ac97_write等跟ac97总线函数是这么来的呢。
下面通过tq6410板级设备初始化结构数组tq6410_devices(linux-3.0-rc6\arch\arm\mach-s3c64xx\mach-tq6410.c)配置
#ifdef CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713
&samsung_asoc_dma,
&s3c64xx_device_ac97,
中&s3c64xx_device_ac97可知,设备在板级的时候就注册了s3c64xx_device_ac97设备,我们看一下s3c64xx_device_ac97都做了什么。
s3c64xx_device_ac97:
struct platform_device s3c64xx_device_ac97 = {
.name = "samsung-ac97",
.id = -1,
.num_resources = ARRAY_SIZE(s3c64xx_ac97_resource),
.resource = s3c64xx_ac97_resource,
.dev = {
.platform_data = &s3c_ac97_pdata,
.dma_mask = &s3c64xx_ac97_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
};
s3c64xx_device_ac97设备主要定义了ac97硬件资源s3c64xx_ac97_resource和分配一组gpio和dma等。
这里的重点是s3c64xx_ac97_resource
s3c64xx_ac97_resource:
static struct resource exynos4_ac97_resource[] = {
[0] = {
.start = EXYNOS4_PA_AC97,
.end = EXYNOS4_PA_AC97 + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = DMACH_AC97_PCMOUT,
.end = DMACH_AC97_PCMOUT,
.flags = IORESOURCE_DMA,
},
[2] = {
.start = DMACH_AC97_PCMIN,
.end = DMACH_AC97_PCMIN,
.flags = IORESOURCE_DMA,
},
[3] = {
.start = DMACH_AC97_MICIN,
.end = DMACH_AC97_MICIN,
.flags = IORESOURCE_DMA,
},
[4] = {
.start = IRQ_AC97,
.end = IRQ_AC97,
.flags = IORESOURCE_IRQ,
},
};
根据他们flags名称就知道IORESOURCE_MEM这是io的地址空间范围,为驱动层ioremap准备。剩下的有ac97 in ,ac97 out和ac97 mic 的dma,IORESOURCE_IRQ是要用到的中断。Start和end表示起始和结束范围。这里只是指出wm9713要用到的资源,注册在"samsung-ac97"驱动中完成。
2)、samsung-ac97驱动
根据平台(platform)设备驱动模型可知有名为"samsung-ac97"设备如果有与之同名的驱动与之匹配,就会调用相应的probe函数。而"samsung-ac97"设备定义在linux-3.0-rc6\sound\soc\samsung\ac97.c中。这里插一句为什么我们一定确定s3c_ac97_init函数一定运行呢,如果不运行"samsung-ac97"驱动也不会注册到platform驱动里面,我们的声卡也就无法运行,当然了这种情况是不可能的。因为我们知道如果一个函数__init修饰的话,编译器会把这个函数的指针统一编译到__init段,开机时候会自动调用。言回正传。下面分析"samsung-ac97"的probe函数在本文中也就是s3c_ac97_probe都做了什么
static __devinit int s3c_ac97_probe(struct platform_device *pdev)
{
……
ac97_pdata = pdev->dev.platform_data;
……
/* Check for availability of necessary resource */
dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
……
dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
……
dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
……
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
……
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
……
if (!request_mem_region(mem_res->start,
resource_size(mem_res), "ac97")) {
dev_err(&pdev->dev, "Unable to request register region\n");
return -EBUSY;
}
s3c_ac97_pcm_out.channel = dmatx_res->start;
s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
s3c_ac97_pcm_in.channel = dmarx_res->start;
s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
s3c_ac97_mic_in.channel = dmamic_res->start;
s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
……
s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
……
s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
……
clk_enable(s3c_ac97.ac97_clk);
if (ac97_pdata->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to configure gpio\n");
ret = -EINVAL;
goto err3;
}
ret = request_irq(irq_res->start, s3c_ac97_irq,
……
ret = snd_soc_register_dais(&pdev->dev, s3c_ac97_dai,
ARRAY_SIZE(s3c_ac97_dai));
……
return ret;
}
先是取出"samsung-ac97"设备里的资源,
dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
……
根据设备里IORESOURCE_MEM内存资源申请内存
if (!request_mem_region(mem_res->start,
resource_size(mem_res), "ac97")) {
dev_err(&pdev->dev, "Unable to request register region\n");
return -EBUSY;
}
填充相关全局变量结构体,为实现soc_ac97_ops里的相关函数做准备。
s3c_ac97_pcm_out.channel = dmatx_res->start;
s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
s3c_ac97_pcm_in.channel = dmarx_res->start;
s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
s3c_ac97_mic_in.channel = dmamic_res->start;
s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
根据申请的内存空间进行物理地址空间映射
s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
使能时钟
clk_enable(s3c_ac97.ac97_clk);
申请中断
ret = request_irq(irq_res->start, s3c_ac97_irq,
IRQF_DISABLED, "AC97", NULL);
最后注册cpu_dai
ret = snd_soc_register_dais(&pdev->dev, s3c_ac97_dai,
ARRAY_SIZE(s3c_ac97_dai));
至于驱动相关的方面大家可以参考代码树,这里不详述了。
Archiver|手机版|小黑屋|天嵌 嵌入式开发社区 ( 粤ICP备11094220号 )
GMT+8, 2024-5-18 12:55 , Processed in 1.031250 second(s), 15 queries .
Powered by Discuz! X3.4
Copyright © 2001-2020, Tencent Cloud.