Soundkartentreiber schreiben (Framework)

In den beiden vorherigen Artikeln haben wir über den Prozess des Registrierens und Aufrufens der Soundkarte des eingebetteten Linux-Systems gesprochen:

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

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

Nach alledem besteht unser oberstes Ziel darin, einen Soundkartentreiber zu schreiben und ihn dann für die APP der oberen Ebene zu verwenden. Im vorherigen Artikel haben wir gesehen, dass der Teil über die Soundkarte im Kernel sehr kompliziert ist, aber tatsächlich schreiben wir den Treiber Zu diesem Zeitpunkt müssen Sie nur einige Hardware-bezogene Strukturen implementieren, z. B. cpu_dai. Verwenden Sie dann das ASOC-Framework des Kernels, um unseren Treiber zu registrieren . Hier ist der Treiber, den wir geschrieben haben:

1. Maschinenteil:

① Wir eine snd_soc_card Struktur myalsa_card konstruieren, es dai_link angeben , dass Sie diese cpu_dai verwenden möchten, codec_dai ... dann imitieren wir den Soundkartentreiber , die mit dem Kernel kommt, platform_set_drvdata die myalsa_card speichern in platform_device in ( „ASOC Registrierungsprozess“ Aufruf Maschinenteile Es gibt eine Einführung zu Punkt ①) .

② Erstellen Sie ein Plattformgerät mit dem Namen "soc-audio" und registrieren Sie es dann. Da sich im Kernel ein Plattformtreiber mit demselben Namen befindet, wird die entsprechende Prüffunktion aufgerufen , dh wir haben im ①-Punkt des Maschinenteils den "ASOC-Registrierungsprozess". Die Funktion soc_probe wurde eingeführt. Diese Funktion erledigt alle Registrierungsaufgaben unserer Soundkarte basierend auf den Informationen in der von uns erstellten myalsa_card-Struktur .

Zu diesem Zeitpunkt wurde der Treiber unseres Maschinenteils geschrieben.

#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");

Zweitens der Plattformteil

1.cpu_dai

Konstruieren Sie eine snd_soc_dai_driver-Strukturvariable s3c2440_i2s_dai. Die Funktion in s3c2440_i2s_dai ist die Operationsfunktion für Hardware, die wir selbst implementieren müssen .

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

② Erstellen Sie ein Plattformgerät und einen Plattformtreiber separat ( ihre Namen müssen identisch sein und mit den Namen übereinstimmen, die durch dai_lnk des Maschinenteils angegeben sind ), und rufen Sie dann ihre Prüffunktion auf.

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

③ Rufen Sie in dieser Testfunktion snd_soc_register_dai (& pdev-> dev, & s3c2440_i2s_dai) auf, fügen Sie die in Schritt ① erstellte Strukturvariable in die verknüpfte Liste dai_list ein und nennen Sie sie den in Schritt mentioned genannten Plattformgerätenamen. Der Maschinenteil besteht darin, s3c2440_i2s_dai in der dai_list gemäß dem durch dai_link angegebenen Namen zu finden.

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

Zu diesem Zeitpunkt wurde auch der Teil cpu_dai des Treiber-Frameworks fertiggestellt, und die spezifischen Hardware-Betriebsfunktionen müssen entsprechend der unterschiedlichen Hardware geschrieben werden . (Dma und codec_dai ähneln dem und werden unten nicht wiederholt. Geben Sie einfach den Code ein, um dies anzuzeigen.)

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);

Drei, Codec-Teil


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


 

Veröffentlicht 42 Originalartikel · Gefällt mir 10 · Besucher 10.000+

Ich denke du magst

Origin blog.csdn.net/qq_37659294/article/details/104816238
Empfohlen
Rangfolge