我们在添加codec的时候,不仅需要将codec的驱动添加到内核中,还需要再machine驱动中添加codec的dai_link才能使能这个codec。
拿三星s5p4418来看:
codec为es8316,codec描述信息如下:
static struct snd_soc_dai_driver es8316_dai = {
.name = "ES8316 HiFi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = es8316_RATES,
.formats = es8316_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = es8316_RATES,
.formats = es8316_FORMATS,
},
.ops = &es8316_ops,
.symmetric_rates = 1,
};
static struct snd_soc_codec_driver soc_codec_dev_es8316 = {
.read = es8316_read_reg_cache,
.write = es8316_write,
.probe = es8316_probe,
.remove = es8316_remove,
.suspend = es8316_suspend,
.resume = es8316_resume,
.set_bias_level = es8316_set_bias_level,
.reg_cache_size = ARRAY_SIZE(es8316_reg),
.reg_word_size = sizeof(u16),
};
static struct i2c_driver es8316_i2c_driver = {
.driver = {
.name = "ES8316",
.owner = THIS_MODULE,
.of_match_table = es8316_of_match,
},
.shutdown = es8316_i2c_shutdown,
.probe = es8316_i2c_probe,
.remove = es8316_i2c_remove,
.id_table = es8316_i2c_id,
};
设备树中的I2C定义为
i2c_0:i2c@c00a4000 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
es8316: es8316@11 {
compatible = "everest,es8316";
reg = <0x11>;
};
};
而在machine驱动中的dai_link则是:
static struct snd_soc_dai_link es8316_dai_link = {
.name = "ASOC-ES8316",
.stream_name = "es8316 HiFi",
.cpu_dai_name = str_dai_name, /* nx_snd_i2s_driver name */
.platform_name = "nexell-pcm", /* nx_snd_pcm_driver name */
.codec_dai_name = "ES8316 HiFi", /* es8316_dai's name */
.codec_name = "ES8316.0-0011", /* es8316_i2c_driver name
+ '.' + bus + '-'
+ address(7bit) */
.ops = &es8316_ops,
.symmetric_rates = 1,
.init = es8316_dai_init,
};
我们发现在machine驱动中的dai_link结构体中codec的名字发生了变化,并不是ES8316而是在其后加上了I2C总线以及I2C地址。
这是因为在初始化codec的component时,会对codec的名字进行格式化处理
static int snd_soc_component_initialize(struct snd_soc_component *component,
const struct snd_soc_component_driver *driver, struct device *dev) //driver=null
{
struct snd_soc_dapm_context *dapm;
component->name = fmt_single_name(dev, &component->id); //格式化component的名称
if (!component->name) {
dev_err(dev, "ASoC: Failed to allocate name\n");
return -ENOMEM;
}
component->dev = dev;
component->driver = driver;
component->probe = component->driver->probe;
component->remove = component->driver->remove;
dapm = &component->dapm;
dapm->dev = dev;
dapm->component = component;
dapm->bias_level = SND_SOC_BIAS_OFF;
dapm->idle_bias_off = true;
if (driver->seq_notifier)
dapm->seq_notifier = snd_soc_component_seq_notifier;
if (driver->stream_event)
dapm->stream_event = snd_soc_component_stream_event;
component->controls = driver->controls;
component->num_controls = driver->num_controls;
component->dapm_widgets = driver->dapm_widgets;
component->num_dapm_widgets = driver->num_dapm_widgets;
component->dapm_routes = driver->dapm_routes;
component->num_dapm_routes = driver->num_dapm_routes;
INIT_LIST_HEAD(&component->dai_list);
mutex_init(&component->io_mutex);
return 0;
}
/*
* Simplify DAI link configuration by removing ".-1" from device names
* and sanitizing names.
*/
static char *fmt_single_name(struct device *dev, int *id) //id = null
{
char *found, name[NAME_SIZE];
int id1, id2;
if (dev_name(dev) == NULL)
return NULL;
strlcpy(name, dev_name(dev), NAME_SIZE); //获取codec的驱动结构中的device-init_name
/* are we a "%s.%d" name (platform and SPI components) */
found = strstr(name, dev->driver->name); //判断init_name和driver中定义的name是不是有同样的字符串
if (found) {
/* get ID */
if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) {
/* discard ID from name if ID == -1 */
if (*id == -1)
found[strlen(dev->driver->name)] = '\0';
}
} else {
/* I2C component devices are named "bus-addr" */
if (sscanf(name, "%x-%x", &id1, &id2) == 2) {
char tmp[NAME_SIZE];
/* create unique ID number from I2C addr and bus */
*id = ((id1 & 0xffff) << 16) + id2;
/* sanitize component name for DAI link creation */
snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, name); //在这里把driver的name和init_name拼接起来成了machine中用到的codec name
strlcpy(name, tmp, NAME_SIZE);
} else
*id = 0;
}
return kstrdup(name, GFP_KERNEL);
}
driver的name来自于codec驱动中的i2c结构体中定义的name,那么init_name是从哪里来的呢?
init_name在添加i2c device的时候被定义,I2C的adapter在注册时,会扫描当前adapter下的所有设备树节点,并根据节点信息,注册i2c device
static int i2c_register_adapter(struct i2c_adapter *adap)
{
.........................
exit_recovery:
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
}
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
................................
result = i2c_new_device(adap, &info); //创建新的i2c设备
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
node->full_name);
of_node_put(node);
return ERR_PTR(-EINVAL);
}
return result;
}
static void i2c_dev_set_name(struct i2c_adapter *adap,
struct i2c_client *client)
{
struct acpi_device *adev = ACPI_COMPANION(&client->dev);
if (adev) {
dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev));
return;
}
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), //device的name变成了 "总线-地址" 的格式
i2c_encode_flags_to_addr(client));
}
所以最后在machine驱动中定义dai_link时的codec_name就变成了 codec驱动名字.总线-地址