函数snd_soc_register_codec()的主要作用是注册codec到ASoC,另外呼叫函数snd_soc_register_dais()注册DAI(Digital Audio Interface).
用到了多个主要的数据结构,
1. codec相关的结构体,包括设备的结构体"snd_soc_codec" 以及设备操作的驱动 "snd_soc_codec_driver";
2. platform相关的结构体,包括设备的结构体"snd_soc_platform" 以及设备操作的驱动 "snd_soc_platform_driver";
3. DAI (Digital Audio Interface)相关的结构体,包括设备的结构体“snd_soc_dai“ 以及设备操作的驱动 "snd_soc_dai_driver";
4. 另外,包括"snd_soc_card", "snd_pcm_runtime", "snd_soc_dai_link";
对这些结构体进行详细的分析和梳理,可以比较全面地明白ALSA是如何工作的。
下面看一看函数里面做了些什么动作,
1. 为codec申请内存空间
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
2. 为codec创建名称和指定类型
/* create CODEC component name */
codec->name = fmt_single_name(dev, &codec->id);
if (codec->name == NULL) {
ret = -ENOMEM;
goto fail_codec;
}
if (codec_drv->compress_type)
codec->compress_type = codec_drv->compress_type;
else
codec->compress_type = SND_SOC_FLAT_COMPRESSION;
3. codec的初始化,包括读写相关的操作函数(实际上是由codec_drv来打理);还包括dev和driver指针的链接。
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->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
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->dapm.stream_event = codec_drv->stream_event;
codec->dev = dev;
codec->driver = codec_drv;
codec->num_dai = num_dai;
mutex_init(&codec->mutex);
4. 下面是关于codec寄存器cache相关的操作,包括数据和操作函数(为什么不直接从codec读取而要在codec中做备份?很是奇怪)。
/* allocate CODEC register cache */
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 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_codec_name;
}
}
}
if (codec_drv->reg_access_size && codec_drv->reg_access_default) {
if (!codec->volatile_register)
codec->volatile_register = snd_soc_default_volatile_register;
if (!codec->readable_register)
codec->readable_register = snd_soc_default_readable_register;
if (!codec->writable_register)
codec->writable_register = snd_soc_default_writable_register;
}
5. 设置数据的格式(big and little endian)。
for (i = 0; i < num_dai; i++) {
fixup_codec_formats(&dai_drv[i].playback);
fixup_codec_formats(&dai_drv[i].capture);
}
6. 加入到codec的list中。
mutex_lock(&client_mutex);
list_add(&codec->list, &codec_list);
mutex_unlock(&client_mutex);
7. 注册DAIs (Digital Audio Interface)
关于DAIs(Digital Audio Interface)注册的分析,请参考博文http://blog.csdn.net/jgw2008/article/details/52808044
/* register any DAIs */
ret = snd_soc_register_dais(dev, dai_drv, num_dai);
if (ret < 0) {
dev_err(codec->dev, "ASoC: Failed to regster DAIs: %d\n", ret);
goto fail_codec_name;
}
snd_soc_codec结构体定义如下,
/* SoC Audio Codec device */
struct snd_soc_codec {
const char *name;
const char *name_prefix;
int id;
struct device *dev;
const struct snd_soc_codec_driver *driver;
struct mutex mutex;
struct snd_soc_card *card;
struct list_head list;
struct list_head card_list;
int num_dai;
enum snd_soc_compress_type compress_type;
size_t reg_size; /* reg_cache_size * reg_word_size */
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
int (*writable_register)(struct snd_soc_codec *, unsigned int);
/* runtime */
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
unsigned int active;
unsigned int cache_bypass:1; /* Suppress access to the cache */
unsigned int suspended:1; /* Codec is in suspend PM state */
unsigned int probed:1; /* Codec has been probed */
unsigned int ac97_registered:1; /* Codec has been AC97 registered */
unsigned int ac97_created:1; /* Codec has been created by SoC */
unsigned int sysfs_registered:1; /* codec has been sysfs registered */
unsigned int cache_init:1; /* codec cache has been initialized */
unsigned int using_regmap:1; /* using regmap access */
u32 cache_only; /* Suppress writes to hardware */
u32 cache_sync; /* Cache needs to be synced to hardware */
/* codec IO */
void *control_data; /* codec control (i2c/3wire) data */
enum snd_soc_control_type control_type;
hw_write_t hw_write;
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t);
void *reg_cache;
const void *reg_def_copy;
const struct snd_soc_cache_ops *cache_ops;
struct mutex cache_rw_mutex;
int val_bytes;
/* dapm */
struct snd_soc_dapm_context dapm;
unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_codec_root;
struct dentry *debugfs_reg;
struct dentry *debugfs_dapm;
#endif
};
snd_soc_codec_driver定义如下,
/* codec driver */
struct snd_soc_codec_driver {
/* driver ops */
int (*probe)(struct snd_soc_codec *);
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;
int num_controls;
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
/* codec wide operations */
int (*set_sysclk)(struct snd_soc_codec *codec,
int clk_id, int source, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out);
/* codec IO */
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*display_register)(struct snd_soc_codec *, char *,
size_t, unsigned int);
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
int (*writable_register)(struct snd_soc_codec *, unsigned int);
unsigned int reg_cache_size;
short reg_cache_step;
short reg_word_size;
const void *reg_cache_default;
short reg_access_size;
const struct snd_soc_reg_access *reg_access_default;
enum snd_soc_compress_type compress_type;
/* codec bias level */
int (*set_bias_level)(struct snd_soc_codec *,
enum snd_soc_bias_level level);
bool idle_bias_off;
void (*seq_notifier)(struct snd_soc_dapm_context *,
enum snd_soc_dapm_type, int);
/* codec stream completion event */
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
bool ignore_pmdown_time; /* Doesn't benefit from pmdown delay */
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
};