Re: ASoC: at91sam9260-PCM1808/TAS5709-based board driver

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

 



On Sat, Aug 8, 2009 at 1:39 PM, Pedro Sanchez<psanchez@xxxxxxxxxxx> wrote:
> Hello,
>
> I have to develop a driver for a custom board based on the at91sam9260 with
> a PCM1808/TAS5709 chipset combo. I am addressing the PCM1808 first because
> it is a simpler device. Since I'm new to ASoC I'm looking for some help in
> this list on how to go about writing this driver.

I attached a PCM1690 driver I'm currently working on.


>
> So far I have identified two files that I need to work on (other than the
> needed kconfig modifications). First is the codec driver at
> sound/soc/codecs/PCM1808.[c|h] which is based on the existent pcm3008
> driver; I believe I'm fine with this.
>
> The second file is the machine driver at sound/soc/atmel/myBoard.c. I'm
> using sam9g20_wm8731.c as reference, on which I believe I found a typo which
> I reported in a separated e-mail.
>
> So my first question is, is this two-files step the right approach? am I in
> the right track? I'm using the 2.6.30 kernel.
>
> Other questions will come and I hope I will get some cycles from you to help
> me direct my work. Of course I'll be glad to submit my driver work for
> inclusion in the kernel if it proves useful.
>
> Regards,
>
> --
> Pedro
>
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@xxxxxxxxxxxxxxxx
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>
>



-- 
Jon Smirl
jonsmirl@xxxxxxxxx
/*
 * Texas Instruments PCM1690 low power audio CODEC
 * ALSA SoC CODEC driver
 *
 * Copyright (C) Jon Smirl <jonsmirl@xxxxxxxxx>
 */
#define DEBUG

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/soc-of-simple.h>
#include <sound/tlv.h>
#include <sound/initval.h>

#include "pcm1690.h"

#define PCM1690_REG_MAX 0x10

/* pcm1690 driver private data */
struct pcm1690_priv {
	struct i2c_client *client;
	struct snd_soc_codec codec;

	/* performance shadow of i2c registers */
	u8 reg_cache[PCM1690_REG_MAX];
};

/* ---------------------------------------------------------------------
 * Register access routines
 */

static unsigned int pcm1690_reg_read(struct snd_soc_codec *codec, unsigned int reg)
{
	struct pcm1690_priv *priv = codec->private_data;
	u8 value, r = reg + 0x40; /* HW regs start at 0x40 */
	int rc;
	struct i2c_msg msgs[2] = {
		{.addr = priv->client->addr, .flags = 0, .len = 1, .buf = &r},
		{.addr = priv->client->addr, .flags = I2C_M_RD, .len = 1, .buf = &value}
	};

	rc = i2c_transfer(priv->client->adapter, msgs, ARRAY_SIZE(msgs));
	if (rc != 2) {
		dev_err(&priv->client->dev, "%s: rc=%d reg=%02x rc != size\n",
			__func__, rc, r);
		return -1;
	}
	priv->reg_cache[reg] = value;
	return value;
}

static int pcm1690_reg_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
{
	struct pcm1690_priv *priv = codec->private_data;
	u8 buf[2];
	int rc;

	priv->reg_cache[reg] = value;

	buf[0] = reg + 0x40;
	buf[1] = value;

	rc = i2c_master_send(priv->client, buf, sizeof buf);
	if (rc != sizeof buf) {
		dev_err(&priv->client->dev,
			"%s: rc=%d reg=%02x size %02x rc != size\n",
			__func__, rc, reg, sizeof buf);
		return -EIO;
	}
	return 0;
}

/* ---------------------------------------------------------------------
 * Digital Audio Interface Operations
 */
static int pcm1690_hw_params(struct snd_pcm_substream *substream,
			   struct snd_pcm_hw_params *params,
			   struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_device *socdev = rtd->socdev;
	struct snd_soc_codec *codec = socdev->card->codec;
	struct pcm1690_priv *priv = codec->private_data;

	dev_dbg(&priv->client->dev, "pcm1690_hw_params(substream=%p, params=%p)\n",
		substream, params);
	dev_dbg(&priv->client->dev, "rate=%i format=%i\n", params_rate(params),
		params_format(params));

	return 0;
}

/**
 * pcm1690_mute - Mute control to reduce noise when changing audio format
 */
static int pcm1690_mute(struct snd_soc_dai *dai, int mute)
{
	struct snd_soc_codec *codec = dai->codec;
	struct pcm1690_priv *priv = codec->private_data;

	dev_dbg(&priv->client->dev, "pcm1690_mute(dai=%p, mute=%i)\n",
		dai, mute);

	return 0;
}

/* ---------------------------------------------------------------------
 * Digital Audio Interface Definition
 */

static struct snd_soc_dai_ops pcm1690_dai_ops = {
	.hw_params = pcm1690_hw_params,
	.digital_mute = pcm1690_mute,
};

struct snd_soc_dai pcm1690_dai = {
	.name = "pcm1690",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 2,
		.channels_max = 2,
		.rates = PCM1690_RATES,
		.formats = PCM1690_FORMATS,
	},
	.ops = &pcm1690_dai_ops,
};
EXPORT_SYMBOL_GPL(pcm1690_dai);

/* ---------------------------------------------------------------------
 * ALSA controls
 */

static const DECLARE_TLV_DB_LINEAR(master_tlv, -6300, 0);


static const struct snd_kcontrol_new pcm1690_snd_controls[] = {

SOC_SINGLE_TLV("Ch 1 Volume", 0x8, 0, 0x7f, 0, master_tlv),
SOC_SINGLE_TLV("Ch 2 Volume", 0x9, 0, 0x7f, 0, master_tlv),
SOC_SINGLE_TLV("Ch 3 Volume", 0xa, 0, 0x7f, 0, master_tlv),
SOC_SINGLE_TLV("Ch 4 Volume", 0xb, 0, 0x7f, 0, master_tlv),
SOC_SINGLE_TLV("Ch 5 Volume", 0xc, 0, 0x7f, 0, master_tlv),
SOC_SINGLE_TLV("Ch 6 Volume", 0xd, 0, 0x7f, 0, master_tlv),
SOC_SINGLE_TLV("Ch 7 Volume", 0xe, 0, 0x7f, 0, master_tlv),
SOC_SINGLE_TLV("Ch 8 Volume", 0xf, 0, 0x7f, 0, master_tlv),

};


/* ---------------------------------------------------------------------
 * SoC CODEC portion of driver: probe and release routines
 */
static int pcm1690_probe(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec;
	struct snd_kcontrol *kcontrol;
	struct pcm1690_priv *priv;
	int i, ret, err;

	dev_info(&pdev->dev, "Probing pcm1690 SoC CODEC driver\n");
	dev_dbg(&pdev->dev, "socdev=%p\n", socdev);
	dev_dbg(&pdev->dev, "codec_data=%p\n", socdev->codec_data);

	/* Fetch the relevant pcm1690 private data here (it's already been
	 * stored in the .codec pointer) */
	priv = socdev->codec_data;
	if (priv == NULL) {
		dev_err(&pdev->dev, "pcm1690: missing codec pointer\n");
		return -ENODEV;
	}
	codec = &priv->codec;
	socdev->card->codec = codec;

	dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n",
		&pdev->dev, socdev->dev);
	/* register pcms */
	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
	if (ret < 0) {
		dev_err(&pdev->dev, "pcm1690: failed to create pcms\n");
		return -ENODEV;
	}

	/* register controls */
	dev_dbg(&pdev->dev, "Registering controls\n");
	for (i = 0; i < ARRAY_SIZE(pcm1690_snd_controls); i++) {
		kcontrol = snd_ctl_new1(&pcm1690_snd_controls[i], codec);
		err = snd_ctl_add(codec->card, kcontrol);
		if (err < 0)
			dev_err(&pdev->dev, "pcm1690: failed to create control %x\n", err);
	}

	/* CODEC is setup, we can register the card now */
	dev_dbg(&pdev->dev, "Registering card\n");
	ret = snd_soc_init_card(socdev);
	if (ret < 0) {
		dev_err(&pdev->dev, "pcm1690: failed to register card\n");
		goto card_err;
	}
	return 0;

 card_err:
	snd_soc_free_pcms(socdev);
	return ret;
}

static int pcm1690_remove(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	snd_soc_free_pcms(socdev);
	return 0;
}

struct snd_soc_codec_device pcm1690_soc_codec_dev = {
	.probe = pcm1690_probe,
	.remove = pcm1690_remove,
};
EXPORT_SYMBOL_GPL(pcm1690_soc_codec_dev);

/* ---------------------------------------------------------------------
 * i2c device portion of driver: probe and release routines and i2c
 * 				 driver registration.
 */
static int pcm1690_i2c_probe(struct i2c_client *client,
				const struct i2c_device_id *id)
{
	struct pcm1690_priv *priv;

	dev_dbg(&client->dev, "probing pcm1690 i2c device\n");

	/* Allocate driver data */
	priv = kzalloc(sizeof *priv, GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	/* Initialize the driver data */
	priv->client = client;
	i2c_set_clientdata(client, priv);

	/* Setup what we can in the codec structure so that the register
	 * access functions will work as expected.  More will be filled
	 * out when it is probed by the SoC CODEC part of this driver */
	priv->codec.private_data = priv;
	priv->codec.name = "pcm1690";
	priv->codec.owner = THIS_MODULE;
	priv->codec.dai = &pcm1690_dai;
	priv->codec.num_dai = 1;
	priv->codec.read = pcm1690_reg_read;
	priv->codec.write = pcm1690_reg_write;
	priv->codec.reg_cache_size = PCM1690_REG_MAX;

	mutex_init(&priv->codec.mutex);
	INIT_LIST_HEAD(&priv->codec.dapm_widgets);
	INIT_LIST_HEAD(&priv->codec.dapm_paths);

	dev_dbg(&client->dev, "I2C device initialized\n");
	return 0;
}

static int pcm1690_i2c_remove(struct i2c_client *client)
{
	struct pcm1690_priv *priv = dev_get_drvdata(&client->dev);

	kfree(priv);

	return 0;
}

static const struct i2c_device_id pcm1690_device_id[] = {
	{ "pcm1690", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, pcm1690_device_id);

static struct i2c_driver pcm1690_driver = {
	.driver		= {
		.name	= "pcm1690",
		.owner	= THIS_MODULE,
	},
	.probe		= pcm1690_i2c_probe,
	.remove		= __devexit_p(pcm1690_i2c_remove),
	.id_table	= pcm1690_device_id,
};

static __init int pcm1690_driver_init(void)
{
	snd_soc_register_dai(&pcm1690_dai);
	return i2c_add_driver(&pcm1690_driver);
}

static __exit void pcm1690_driver_exit(void)
{
	i2c_del_driver(&pcm1690_driver);
}

module_init(pcm1690_driver_init);
module_exit(pcm1690_driver_exit);

MODULE_AUTHOR("Jon Smirl");
MODULE_DESCRIPTION("PCM1690 codec module");
MODULE_LICENSE("GPL");
/*
 * Texas Instruments PCM1690 low power audio CODEC
 * ALSA SoC CODEC driver
 *
 * Copyright (C) Jon Smirl <jonsmirl@xxxxxxxxx>
 */


#define PCM1690_RATES (SNDRV_PCM_RATE_32000 |\
				SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
				SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
				SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
#define PCM1690_FORMATS SNDRV_PCM_FMTBIT_S32

extern struct snd_soc_dai pcm1690_dai;
extern struct snd_soc_codec_device pcm1690_soc_codec_dev;
_______________________________________________
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