ASoC platform bus part (platform layer)

Posted by mjm7867 on Sat, 05 Mar 2022 17:35:43 +0100

Note: This article is to analyze the platform bus used in the platform layer of ASoC (two platforms are not a concept)

ASoC concept introduction

In order to better support the Assa driver and ASCOD driver in the embedded device layer, we need to use the Assa driver framework_ card_ Create and other functions to create our sound card. At present, it has been integrated into the code tree of the kernel. sound/soc and ASoC divide the sound card driver into three parts: machine, platform and codec.

Briefly introduce three parts (driving level):

  • Machine: board related, indicating which platform is, which CPU DAI is, which DMA is, which codec is, which codec DAI is. The driver is responsible for handling some controls and audio events unique to the machine (for example, when playing audio, an amplifier needs to be turned on first). The separate platform and codec driver cannot work, It must be driven by machine and combined with them to complete the audio processing of the whole device

  • Platform: it includes the configuration and control of audio DMA and audio interface of the SoC platform

  • Codec: configure DAI interface, io control mode of codec and DAPM. Codec driver is platform independent

platform bus

background

First of all, we should know the relationship between equipment, bus and driver. The driver is platform related, and the bus is responsible for matching different devices with specific drivers. In order to remove the strong coupling between the device and the driver, that is, to prevent the situation that multiple devices of the same kind need different drivers on the same platform, the driver and the device are separated, and a core layer is abstracted in the middle. Operate through unified API interface

concept

platform bus is a kind of virtual bus, which can make some external devices hang up, and then match them with corresponding drivers

advantage

  1. Linux 2.0 compliant Device model of kernel after 6. The result is to make the supporting sysfs nodes and equipment power management possible

  2. Isolate devices and drivers to make drivers more scalable and cross platform

  3. One driver supports multiple instances

struct platform_device {                                                 
		const char		*name;                                           
		int				id;                                             
		bool			id_auto;
		struct device   dev;                                             
		u64             platform_dma_mask;                               
		u32             num_resources;                                   
		struct resource *resource;
		
		const struct platform_device_id *id_entry;                       
		char *driver_override; /* Driver name to force a match */    

		/* MFD cell pointer */
		struct mfd_cell *mfd_cell;                                               
		/* arch specific additions */ 
		struct pdev_archdata    archdata;
};
struct platform_driver {                                                 
		int (*probe)(struct platform_device *);
		int (*remove)(struct platform_device *);
		void (*shutdown)(struct platform_device *);
		int (*suspend)(struct platform_device *, pm_message_t state);
		int (*resume)(struct platform_device *);
		struct device_driver driver;
		const struct platform_device_id *id_table;
		bool prevent_deferred_probe;
}; 

probe function of ASoC platform layer

Problem finding device tree

This function needs to pass in a platform_device *pdev the function of this pointer:

  1. Find the node of the device tree

  2. Put them into the component dai queue and register them

Here we focus on the first working principle

The source code of relevant parts is as follows:

struct device_node *np = pdev->dev.of_node;
...
ret = of_property_read_u32(np, "playback_cma", &temp_val);

1. np here points to of of the device structure in pdev_ Node member, which is device_node type is mainly used to save equipment node information.

2. It means to find the "playback_cma" attribute of the device node according to np, read its 32-bit value and save it to temp_val. The same is true for reading other attributes.

platform device and driver matching problem

Before that, another important structure, platform, is introduced_ Device in driver_ Driver member

struct device_driver {
		const char			*name;
		struct bus_type		*bus;
		struct module		*owner
		const char			*mod_name;	/* used for built-in modules */
		
		bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
		
		const struct of_device_id		*of_match_table;
		const struct acpi_device_id		*acpi_match_table;
		
		int (*probe) (struct device *dev);
		int (*remove) (struct device *dev);
		void (*shutdown) (struct device *dev);
		int (*suspend) (struct device *dev, pm_message_t state);
		int (*resume) (struct device *dev);
		const struct attribute_group **group;
		
		const struct dev_pm_ops *pm;
		
		struct driver_private *p;
};

This structure mainly describes some commonalities in the driving sense of various buses. It should be noted that bus_type and of_device_id

struct bus_type platform_bus_type = {
		.name			= "platform",
		.dev_groups		= platform_dev_groups,
		.match			= platform_match,
		.uevent			= platform_uevent,
		.pm				= &platform_dev_pm_ops,
};

Focus on the match() function, which determines the platform_device and platform_ How do drivers match? The source code of this function contains four matching methods. We mainly look at the way of device tree. The source code is as follows

static int platform_match(struct device *dev, struct device_driver *drv)
{
		...
		if(of_driver_match_device(dev, drv))
				return 1;
		...
}

The source code of the above function is:

static inline int of_driver_match_device(struct device *dev, 
											const struct device_driver *drv) {
		return of_match_device(drv->of_match_table, dev) != NULL;
}

The underlying source code will not be shown later. The principle is to find of through dev_ Node to obtain the information of the nodes in the device tree, and then compare them. If they are equal, they can be matched

Now back to the driver source code of ASoC platform layer, the parts related to device and driver matching are as follows

static const struct of_device_id audio_of_match[] = {
		{ .compatible = "MTK," DRV_NAME, },
		{},
};

MODULE_DEVICE_TABLE(of, audio_of_match);

static struct platform_driver audio_driver = {    
		.driver	= {
				.name		= DRV_NAME,
				.owner		= THIS_MODULE,
				.of_match_table	= audio_of_match,
		},
		.probe	= audio_dev_probe,
		.remove	= audio_dev_remove,
};

It can be divided into three parts:

  1. Filled with the string that needs to be matched (the second one needs to be empty)

  2. Add the device to the peripheral queue

  3. Will of_match_table assignment

Supplement:

device is usually registered before driver, but it is not exactly in this order

Topics: Linux