Gravar driver da placa de som (estrutura)

Nos dois artigos anteriores, falamos sobre o processo de registro e chamada da placa de som do sistema Linux incorporado:

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

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

Dito isso, nosso objetivo final nada mais é do que escrever um driver da placa de som e usá-lo no aplicativo de nível superior.No artigo anterior, podemos ver que a parte da placa de som no kernel é muito complicada, mas na verdade escrevemos o driver No momento, você só precisa implementar algumas estruturas relacionadas ao hardware, como cpu_dai. Em seguida, use a estrutura ASOC do kernel para registrar nosso driver . Aqui está o driver que escrevemos:

1. peça da máquina:

① Nós construir um myalsa_card estrutura snd_soc_card, ele dai_link especificar que você quer usar aqueles cpu_dai, codec_dai ... então nós imitar o driver da placa de som que vem com o kernel, chamando platform_set_drvdata o myalsa_card salvo em platform_device em ( "processo de registro ASOC" peças de máquinas Há uma introdução no ponto ①) .

Construa um dispositivo de plataforma chamado "soc-audio" e registre-o. Como existe um driver de plataforma com o mesmo nome no kernel, a função de teste correspondente é chamada , ou seja, temos no ponto of da parte da máquina do "Processo de registro ASOC" A função soc_probe introduzida. Esta função concluirá todas as tarefas de registro da nossa placa de som com base nas informações na estrutura myalsa_card que construímos .

Neste ponto, o driver da nossa parte da máquina foi gravado.

#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, a parte da plataforma

1.cpu_dai

Construa uma variável de estrutura snd_soc_dai_driver s3c2440_i2s_dai, a função em s3c2440_i2s_dai é a função de operação sobre hardware que precisamos implementar sozinhos .

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,
};

Construa um dispositivo de plataforma e um driver de plataforma separadamente ( seus nomes devem ser os mesmos e devem ser os mesmos que os especificados pelo dai_lnk da peça da máquina ) e, em seguida, chamar sua função 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",
	}
};

Function Nesta função de análise, chame snd_soc_register_dai (& pdev-> dev, & s3c2440_i2s_dai); coloque a variável de estrutura construída na etapa ① na lista vinculada dai_list e nomeie-a como o nome do dispositivo de plataforma mencionado na etapa ②. A parte da máquina é encontrar s3c2440_i2s_dai na dai_list de acordo com o nome 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;
}

Nesse ponto, a parte cpu_dai da estrutura do driver também foi concluída e as funções específicas de operação de hardware precisam ser gravadas de acordo com o hardware diferente . (Dma e codec_dai são semelhantes a isso e não serão repetidos abaixo, basta postar o 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);

Três, parte codec


/* 参考 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);


 

Publicado 42 artigos originais · Gosto 10 · Visitantes 10.000 ou mais

Acho que você gosta

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