Escribir controlador de tarjeta de sonido (marco)

En los dos artículos anteriores, hablamos sobre el proceso de registrar y llamar a la tarjeta de sonido del sistema Linux incorporado:

https://blog.csdn.net/qq_37659294/article/details/104748747

https://blog.csdn.net/qq_37659294/article/details/104802868

Dicho esto, nuestro objetivo final no es más que escribir un controlador de tarjeta de sonido, y luego usarlo para la aplicación de nivel superior. En el artículo anterior, podemos ver que la parte sobre la tarjeta de sonido en el núcleo es muy complicada, pero de hecho escribimos el controlador En ese momento, solo necesita implementar algunas estructuras relacionadas con el hardware, como cpu_dai. Luego use el marco ASOC del núcleo para registrar nuestro controlador . Aquí está el controlador que escribimos:

1. parte de la máquina:

① Nosotros construimos un myalsa_card estructura snd_soc_card, que dai_link especificar que desea utilizar los cpu_dai, codec_dai ... entonces imitar el controlador de la tarjeta de sonido que viene con el kernel, llamando platform_set_drvdata la myalsa_card salvamento en platform_device en ( "proceso de registro ASOC" Piezas de máquinas Hay una introducción en el punto ①) .

constructo llamado plataforma de dispositivo "soc-audio", y luego registrarlo, ya que los controladores del núcleo tienen el mismo nombre que la plataforma, así que llame a la función sonda correspondiente , es decir, tenemos el primer punto de "proceso de registro ASOC" piezas de la máquina ① Se introdujo la función soc_probe. Esta función completará todas las tareas de registro de nuestra tarjeta de sonido en función de la información en la estructura myalsa_card que construimos .

En este punto, el controlador de nuestra pieza de máquina ha sido escrito.

#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/module.h>

#include <sound/soc.h>


/* 参考sound\soc\samsung\s3c24xx_uda134x.c
 */




/*
 * 1. 分配注册一个名为soc-audio的平台设备
 * 2. 这个平台设备有一个私有数据 snd_soc_card
 *    snd_soc_card里有一项snd_soc_dai_link
 *    snd_soc_dai_link被用来决定ASOC各部分的驱动
 */

static struct snd_soc_ops s3c2440_uda1341_ops = {
	//.hw_params = s3c24xx_uda134x_hw_params,
};

static struct snd_soc_dai_link s3c2440_uda1341_dai_link = {
	.name = "100ask_UDA1341",
	.stream_name = "100ask_UDA1341",
	.codec_name = "uda1341-codec",
	.codec_dai_name = "uda1341-iis",
	.cpu_dai_name = "s3c2440-iis",
	.ops = &s3c2440_uda1341_ops,
	.platform_name	= "s3c2440-dma",
};


static struct snd_soc_card myalsa_card = {
	.name = "S3C2440_UDA1341",
	.owner = THIS_MODULE,
	.dai_link = &s3c2440_uda1341_dai_link,
	.num_links = 1,
};

static void asoc_release(struct device * dev)
{
}

static struct platform_device asoc_dev = {
    .name         = "soc-audio",
    .id       = -1,
    .dev = { 
    	.release = asoc_release, 
	},
};

static int s3c2440_uda1341_init(void)
{
	platform_set_drvdata(&asoc_dev, &myalsa_card);
    platform_device_register(&asoc_dev);    
    return 0;
}

static void s3c2440_uda1341_exit(void)
{
    platform_device_unregister(&asoc_dev);
}

module_init(s3c2440_uda1341_init);
module_exit(s3c2440_uda1341_exit);

MODULE_LICENSE("GPL");

Segundo, la parte de la plataforma.

1.cpu_dai

Construya una variable de estructura snd_soc_dai_driver s3c2440_i2s_dai, la función en s3c2440_i2s_dai es la función de operación sobre el hardware que necesitamos implementar nosotros mismos .

static int s3c2440_i2s_hw_params(struct snd_pcm_substream *substream,
				 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *dai)
{
    /* 根据params设置IIS控制器 */

    /* 配置GPIO用于IIS */
    ...

    return 0;
}


static int s3c2440_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
			       struct snd_soc_dai *dai)
{  
    /* 硬件相关的操作 */
}

static const struct snd_soc_dai_ops s3c2440_i2s_dai_ops = {
	.hw_params	= s3c2440_i2s_hw_params,
	.trigger	= s3c2440_i2s_trigger,
};

#define S3C24XX_I2S_RATES \
	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)

static struct snd_soc_dai_driver s3c2440_i2s_dai = {
	.playback = {
		.channels_min = 2,
		.channels_max = 2,
		.rates = S3C24XX_I2S_RATES,
		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
	.capture = {
		.channels_min = 2,
		.channels_max = 2,
		.rates = S3C24XX_I2S_RATES,
		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
	.ops = &s3c2440_i2s_dai_ops,
};

Construya un dispositivo de plataforma y un controlador de plataforma por separado ( sus nombres deben ser los mismos y deben ser los mismos que los especificados por el dai_lnk de la parte de la máquina ), y luego llamar a su función de sonda,

static struct platform_device s3c2440_iis_dev = {
    .name         = "s3c2440-iis",
    .id       = -1,
    .dev = { 
    	.release = s3c2440_iis_release, 
	},
};
struct platform_driver s3c2440_iis_drv = {
	.probe		= s3c2440_iis_probe,
	.remove		= s3c2440_iis_remove,
	.driver		= {
		.name	= "s3c2440-iis",
	}
};

③ En esta función de sonda, llame a snd_soc_register_dai (& pdev-> dev, & s3c2440_i2s_dai); coloque la variable de estructura construida en el paso ① en la lista vinculada dai_list, y asígnele el nombre del dispositivo de plataforma mencionado en el paso ②. La parte de la máquina es encontrar s3c2440_i2s_dai en dai_list según el nombre especificado por dai_link.

static int s3c2440_iis_probe(struct platform_device *pdev)
{
	return snd_soc_register_dai(&pdev->dev, &s3c2440_i2s_dai);
}
static int s3c2440_iis_remove(struct platform_device *pdev)
{
	snd_soc_unregister_dai(&pdev->dev);
    return 0;
}

En este punto, la parte cpu_dai del marco del controlador también se ha completado, y las funciones específicas de operación del hardware deben escribirse de acuerdo con diferentes hardware . (Dma y codec_dai son similares a esto, y no se repetirán a continuación, solo publique el código para indicar)

2.dma

/* 参考 sound\soc\samsung\dma.c
 */

static struct snd_pcm_ops s3c2440_dma_ops = {
    /* 需要我们自己编写的,关于硬件的函数 */
	.open		= s3c2440_dma_open,
	.close		= s3c2440_dma_close,
	.ioctl		= snd_pcm_lib_ioctl,
	.hw_params	= s3c2440_dma_hw_params,
	.prepare    = s3c2440_dma_prepare,
	.trigger	= s3c2440_dma_trigger,
	.pointer	= s3c2440_dma_pointer,	
};

static struct snd_soc_platform_driver s3c2440_dma_platform = {
    /* 需要我们自己编写的,关于硬件的函数 */
	.ops		= &s3c2440_dma_ops,
	.pcm_new	= dma_new,
	.pcm_free	= dma_free_dma_buffers,
};

static int s3c2440_dma_probe(struct platform_device *pdev)
{
	return snd_soc_register_platform(&pdev->dev, &s3c2440_dma_platform);
}
static int s3c2440_dma_remove(struct platform_device *pdev)
{
	return snd_soc_unregister_platform(&pdev->dev);
}

static void s3c2440_dma_release(struct device * dev)
{
}

static struct platform_device s3c2440_dma_dev = {
    .name         = "s3c2440-dma",
    .id       = -1,
    .dev = { 
    	.release = s3c2440_dma_release, 
	},
};
struct platform_driver s3c2440_dma_drv = {
	.probe		= s3c2440_dma_probe,
	.remove		= s3c2440_dma_remove,
	.driver		= {
		.name	= "s3c2440-dma",    //必须和dai_link里面的platform_name相同
	}
};

static int s3c2440_dma_init(void)
{
    platform_device_register(&s3c2440_dma_dev);
    platform_driver_register(&s3c2440_dma_drv);
    return 0;
}

static void s3c2440_dma_exit(void)
{
    platform_device_unregister(&s3c2440_dma_dev);
    platform_driver_unregister(&s3c2440_dma_drv);
}

module_init(s3c2440_dma_init);
module_exit(s3c2440_dma_exit);

Tres, parte del códec


/* 参考 sound\soc\codecs\uda134x.c
 */

/* 1. 构造一个snd_soc_dai_driver
 * 2. 构造一个snd_soc_codec_driver
 * 3. 注册它们
 */

static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {
	/* 硬件相关的函数 */
	.probe = uda1341_soc_probe,

	.reg_cache_size = sizeof(uda1341_reg),
	.reg_word_size = sizeof(u8),
	.reg_cache_default = uda1341_reg,
	.reg_cache_step = 1,
	.read  = uda1341_read_reg_cache,
	.write = uda1341_write_reg,  /* 写寄存器 */
};

static const struct snd_soc_dai_ops uda1341_dai_ops = {
    /* 硬件相关的操作 */
	.hw_params	= uda1341_hw_params,
};

static struct snd_soc_dai_driver uda1341_dai = {
	.name = "uda1341-iis",        //必须和dai_link里面的codec_dai_name相同
	/* playback capabilities */
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* capture capabilities */
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* pcm operations */
	.ops = &uda1341_dai_ops,
};


/* 通过注册平台设备、平台驱动来实现对snd_soc_register_codec的调用
 *
 */

static void uda1341_dev_release(struct device * dev)
{
}

static int uda1341_probe(struct platform_device *pdev)
{
	return snd_soc_register_codec(&pdev->dev,
			&soc_codec_dev_uda1341, &uda1341_dai, 1);
}

static int uda1341_remove(struct platform_device *pdev)
{
    return snd_soc_unregister_codec(&pdev->dev);
}

static struct platform_device uda1341_dev = {
    .name         = "uda1341-codec",        //必须和dai_link里面的codec_name相同
    .id       = -1,
    .dev = { 
    	.release = uda1341_dev_release, 
	},
};
struct platform_driver uda1341_drv = {
	.probe		= uda1341_probe,
	.remove		= uda1341_remove,
	.driver		= {
		.name	= "uda1341-codec",
	}
};

static int uda1341_init(void)
{
    platform_device_register(&uda1341_dev);
    platform_driver_register(&uda1341_drv);
    return 0;
}

static void uda1341_exit(void)
{
    platform_device_unregister(&uda1341_dev);
    platform_driver_unregister(&uda1341_drv);
}

module_init(uda1341_init);
module_exit(uda1341_exit);


 

42 artículos originales publicados · Me gusta 10 · Visitantes más de 10,000

Supongo que te gusta

Origin blog.csdn.net/qq_37659294/article/details/104816238
Recomendado
Clasificación