Re: Writing an AT91SAM9260-EK Dumb Codec Driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I think I have my driver compiling and such with the kernel. I had an error
in my last Kconfig and Makefile, which I hope I have fixed. It appears to be
making correctly. I will attach the three files if anyone would like to view
them.

I am still working on getting linux2.6.22-rc1 booting correctly. Once I have
that, I should be able to evaluate the driver a little better.

I also believe I am going to need alsa-libs. I will try to locate those and
get them cross-compiled asap.

If anyone has pointers on my Kconfig, Makefile, or driver--or on how to get
linux booting, please feel free to give them to me.

Thanks.

Paul

Attachment: Kconfig
Description: Binary data

Attachment: Makefile
Description: Binary data

/*
 * grh_ek_w6811 -- SoC audio for AT91SAM9260-EK board
 *
 * ASoC Codec and Machine driver for W6811 PCM interface.
 *
 * Author:	Paul Kavan, Project Engineer <pkavan@xxxxxxxxx>
 *		GRH Electronics, Inc. 
 * Created:	Jun 15, 2007
 *
 *
 * Borrowed heavily from:
 *
 * eti_b1_bluecore  --  SoC audio for AT91RM9200-based Endrelia ETI_B1 board
 *                      Frank Mandarino <fmandarino@xxxxxxxxxxxx>
 *                      Endrelia Technologies, Inc.
 * 
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 *
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>

#include <asm/arch/hardware.h>
#include <asm/arch/at91_pio.h>
#include <asm/arch/gpio.h>

#include "at91-pcm.h"
#include "at91-ssc.h"

#if 0
#define	DBG(x...)	printk(KERN_INFO "grh_ek_w6811: " x)
#else
#define	DBG(x...)
#endif


/*
 * W6811 PCM interface codec driver.
 */
#define W6811_VERSION "0.1"

#define W6811_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE) //TODO: Check on S16_LE

static struct snd_soc_codec_dai w6811_dai = {
	.name = "W6811",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 1,
		.rates = SNDRV_PCM_RATE_8000,
		.formats = W6811_FORMATS,},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 1,
		.rates = SNDRV_PCM_RATE_8000,
		.formats = W6811_FORMATS,},
};

static int w6811_soc_probe(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec;
	int ret = 0;

	printk(KERN_INFO "w6811: W6811 PCM SoC Audio %s\n", W6811_VERSION);

	socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
	if (socdev->codec == NULL)
		return -ENOMEM;
	codec = socdev->codec;
	mutex_init(&codec->mutex);

	codec->name = "W6811";
	codec->owner = THIS_MODULE;
	codec->dai = &w6811_dai;
	codec->num_dai = 1;
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

	/* register pcms */
	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
	if(ret < 0)
		goto err;

	ret = snd_soc_register_card(socdev);
	if (ret < 0)
		goto bus_err;
	return 0;

bus_err:
	snd_soc_free_pcms(socdev);

err:
	kfree(socdev->codec);
	socdev->codec = NULL;
	return ret;
}

static int w6811_soc_remove(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec = socdev->codec;

	if(codec == NULL)
		return 0;

	snd_soc_free_pcms(socdev);
	kfree(socdev->codec);

	return 0;
}

static struct snd_soc_codec_device soc_codec_dev_w6811 = {
	.probe = 	w6811_soc_probe,
	.remove = 	w6811_soc_remove,
};


/*
 * GRH EK W6811 Machine driver.
 */
#define AT91_PIO_TK0	(1 << (AT91_PIN_PB16 - PIN_BASE) % 32)
#define AT91_PIO_TF0	(1 << (AT91_PIN_PB17 - PIN_BASE) % 32)
#define AT91_PIO_TD0	(1 << (AT91_PIN_PB18 - PIN_BASE) % 32)
#define AT91_PIO_RD0	(1 << (AT91_PIN_PB19 - PIN_BASE) % 32)
#define AT91_PIO_RK0	(1 << (AT91_PIN_PB20 - PIN_BASE) % 32)
#define AT91_PIO_RF0	(1 << (AT91_PIN_PB21 - PIN_BASE) % 32)


/*
 * GRH EK W6811 PCM interface machine driver operations.
 *
 * Only hw_params() is required, and only the CPU_DAI needs to be
 * initiailzed, as there is no real CODEC DAI.  
 *
 * ??The CODEC DAI is controlled by BlueZ BCCMD and SCO operations.
 */
static int grh_ek_w6811_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
	int ret;
	int cmr_div, period;

	/* set cpu DAI configuration */
	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
	if (ret < 0)
		return ret;

	/*
	 * The W6811 PCM interface sync operates at 8kHz, and
	 * the PCM_CLK signal must be greater than 256kHz.
	 */
	cmr_div = 187;	/* PCM_CLK = ~96MHz/(2*187) = 256kHz */
	period = 15;	/* PCM_SYNC = PCM_CLK/(2*(15+1)) = 8000Hz */

	/* set the MCK divider for PCM_CLK */
	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
	if (ret < 0)
		return ret;

	/* set the BCLK divider for DACLRC */
	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_TCMR_PERIOD, period);
	if (ret < 0)
		return ret;

	return 0;
}

static struct snd_soc_ops grh_ek_w6811_ops = {
	.hw_params = grh_ek_w6811_hw_params,
};

static struct snd_soc_dai_link grh_ek_w6811_dai = {
	.name = "W6811 PCM",
	.stream_name = "W6811",
	.cpu_dai = &at91_ssc_dai[2], //TODO: Determine what the 2 is indexing
	.codec_dai = &w6811_dai,
	.ops = &grh_ek_w6811_ops,
};

static struct snd_soc_machine snd_soc_machine_grh_ek_w6811 = {
	.name = "GRH_EK_W6811",
	.dai_link = &grh_ek_w6811_dai,
	.num_links = 1,
};

static struct snd_soc_device grh_ek_w6811_snd_devdata = {
	.machine = &snd_soc_machine_grh_ek_w6811,
	.platform = &at91_soc_platform,
	.codec_dev = &soc_codec_dev_w6811,
};

static struct platform_device *grh_ek_w6811_snd_device;

static int __init grh_ek_w6811_init(void)
{
	int ret;
	u32 ssc_pio_lines;
	struct at91_ssc_periph *ssc = grh_ek_w6811_dai.cpu_dai->private_data;

	if (!request_mem_region(AT91SAM9260_BASE_SSC, SZ_16K, "soc-audio")) {
		DBG("SSC memory region is busy\n");
		return -EBUSY;
	}

	ssc->base = ioremap(AT91SAM9260_BASE_SSC, SZ_16K);
	if (!ssc->base) {
		DBG("SSC memory ioremap failed\n");
		ret = -ENOMEM;
		goto fail_release_mem;
	}

	ssc->pid = AT91SAM9260_ID_SSC;

	grh_ek_w6811_snd_device = platform_device_alloc("soc-audio", 2); //TODO: Determine why the 2
	if (!grh_ek_w6811_snd_device) {
		DBG("platform device allocation failed\n");
		ret = -ENOMEM;
		goto fail_io_unmap;
	}

	platform_set_drvdata(grh_ek_w6811_snd_device, &grh_ek_w6811_snd_devdata);
	grh_ek_w6811_snd_devdata.dev = &grh_ek_w6811_snd_device->dev;

	ret = platform_device_add(grh_ek_w6811_snd_device);
	if (ret) {
		DBG("platform device add failed\n");
		platform_device_put(grh_ek_w6811_snd_device);
		goto fail_io_unmap;
	}

        //TODO: Ask Frank to make sure what is happening is what I think is happening        
 	ssc_pio_lines = AT91_PIO_TF0 | AT91_PIO_TK0 | AT91_PIO_TD0
			| AT91_PIO_RD0 /* | AT91_PIO_RK0 | AT91_PIO_RF0 */;

        //TODO: Check on this.
	/* Reset all PIO registers and assign lines to peripheral A */
 	at91_sys_write(AT91_PIOB + PIO_PDR,  ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_ODR,  ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_IFDR, ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_CODR, ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_IDR,  ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_MDDR, ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_PUDR, ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_ASR,  ssc_pio_lines);
 	at91_sys_write(AT91_PIOB + PIO_OWDR, ssc_pio_lines);

	printk(KERN_INFO "grh_ek_w6811: W6811 PCM in Slave Mode\n");
	return ret;

fail_io_unmap:
	iounmap(ssc->base);
fail_release_mem:
	release_mem_region(AT91SAM9260_BASE_SSC, SZ_16K);
	return ret;
}

static void __exit grh_ek_w6811_exit(void)
{
	struct at91_ssc_periph *ssc = grh_ek_w6811_dai.cpu_dai->private_data;

	platform_device_unregister(grh_ek_w6811_snd_device);

	iounmap(ssc->base);
	release_mem_region(AT91SAM9260_BASE_SSC, SZ_16K);
}

module_init(grh_ek_w6811_init);
module_exit(grh_ek_w6811_exit);

/* Module information */
MODULE_AUTHOR("Paul Kavan <pkavan@xxxxxxxxx>");
MODULE_DESCRIPTION("ALSA SoC GRH-EK-W6811");
MODULE_LICENSE("GPL");
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux