--- linux-2.6.11-rc5/sound/arm/s3c24xx-tlv320aic23.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.11-rc5-audio9/sound/arm/s3c24xx-tlv320aic23.c 2005-03-15 12:11:48.000000000 +0000 @@ -0,0 +1,273 @@ +/* sound/arm/s3c24xx-tlv320aic23.c + * + * (c) 2004-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C24XX TLV320AIC23 Audio CODEC interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include "tlv320aic23.h" +#include "s3c24xx-iis.h" + +/* todo list: + * + * ensure that we can operate as an clock slave + * +*/ + +struct s3c24xx_tlv { + struct s3c24xx_iis_ops ops; + s3c24xx_card_t *card; + struct clk *clk; + struct device *dev; + struct tlv320aic23 *tlv; + struct tlv320aic23_cfg cfg; +}; + + +static struct tlv320aic23 *ops_to_tlv(struct s3c24xx_iis_ops *ops) +{ + struct s3c24xx_tlv *tlv; + + tlv = container_of(ops, struct s3c24xx_tlv, ops); + return tlv->tlv; +} + +static int s3c24xx_tlv320aic32_open(struct s3c24xx_iis_ops *ops, + snd_pcm_substream_t *stream) +{ + return tlv320aic23_open(ops_to_tlv(ops), stream); +} + +static int s3c24xx_tlv320aic23_close(struct s3c24xx_iis_ops *ops, + snd_pcm_substream_t *stream) +{ + return tlv320aic23_close(ops_to_tlv(ops), stream); +} + +static int s3c24xx_tlv320aic23_prepare(struct s3c24xx_iis_ops *ops, + snd_pcm_substream_t *stream, + snd_pcm_runtime_t *run) +{ + return tlv320aic23_prepare(ops_to_tlv(ops), stream, run); +} + +static int s3c24xx_tlv320aic32_startup(struct s3c24xx_iis_ops *ops) +{ + return tlv320aic23_startup(ops_to_tlv(ops)); +} + +/* sound card operations */ + +struct s3c24xx_iis_ops s3c24xx_tlv320aic23_ops = { + .owner = THIS_MODULE, + .startup = s3c24xx_tlv320aic32_startup, + .open = s3c24xx_tlv320aic32_open, + .close = s3c24xx_tlv320aic23_close, + .prepare = s3c24xx_tlv320aic23_prepare, +}; + +static void s3c24xx_tlv320aic23_free(struct s3c24xx_tlv *tlv) +{ + kfree(tlv); +} + +static struct device *audio_dev; + +static int s3c24xx_tlv320aic23_remove(struct device *dev) +{ + /* shouldn't happen */ + return 0; +} + +static int s3c24xx_tlv320aic23_probe(struct device *dev) +{ + struct s3c24xx_platdata_iis *pdata = dev->platform_data; + + if (pdata == NULL) { + dev_err(dev, "no platform data\n"); + return -EINVAL; + } + + audio_dev = dev; + return 0; +} + +static struct device_driver s3c24xx_tlv320aic23_driver = { + .name = "s3c24xx-tlv320aic23", + .bus = &platform_bus_type, + .probe = s3c24xx_tlv320aic23_probe, + .remove = s3c24xx_tlv320aic23_remove, +}; + +static int s3c24xx_tlv320aic23_codec_probe(struct device *dev) +{ + struct s3c24xx_platdata_iis *pdata; + struct s3c24xx_tlv *tlv; + s3c24xx_card_t *card; + int err; + + dev_dbg(dev, "codec probe\n"); + + if (dev->platform_data == NULL) { + dev_err(dev, "no platform data\n"); + return -EINVAL; + } + + if (audio_dev == NULL) + return -EINVAL; + + pdata = audio_dev->platform_data; + if (pdata == NULL) + return -EINVAL; + + if (pdata->match_dev != NULL && !pdata->match_dev(dev)) { + dev_dbg(dev, "not attaching\n"); + return 0; + } + + dev_info(dev, "attached tlv320aic23 driver\n"); + + /* allocate a sound device */ + + tlv = kmalloc(sizeof(struct s3c24xx_tlv), GFP_KERNEL); + if (tlv == NULL) { + err = -ENOMEM; + goto exit_err; + } + + memset(tlv, 0, sizeof(struct s3c24xx_tlv)); + + tlv->clk = clk_get(dev, pdata->codec_clk); + if (IS_ERR(tlv->clk)) { + err = -EINVAL; + goto exit_err; + } + + tlv->dev = dev; + tlv->cfg.clkrate = clk_get_rate(tlv->clk); + tlv->cfg.master = 1; + + dev_info(dev, "clock %s, rate %ldHz\n", + pdata->codec_clk, tlv->cfg.clkrate); + + card = s3c24xx_iis_probe(audio_dev); + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto exit_err; + } + + tlv->ops = s3c24xx_tlv320aic23_ops; + card->chip_ops = &tlv->ops; + card->client = tlv; + + strcpy(card->card->driver, "tlv320aic23"); + strcpy(card->card->shortname, "tlv"); + strcpy(card->card->longname, "S3C24XX TLV320AIC23"); + + snd_card_set_dev(card->card, audio_dev); + + tlv->tlv = tlv320aic23_attach(card->card, dev, &tlv->cfg); + if (IS_ERR(tlv->tlv)) { + err = PTR_ERR(tlv->tlv); + goto exit_err; + } + + /* default configuration for IIS */ + + s3c24xx_iismod_cfg(card, S3C2410_IISMOD_16BIT, 0x0); + s3c24xx_iismod_cfg(card, S3C2410_IISMOD_32FS, 0x0); + s3c24xx_iismod_cfg(card, 0x0, S3C2410_IISMOD_384FS); + + err = s3c24xx_iis_cfgclksrc(card, pdata->codec_clk); + if (err) + goto exit_err; + + err = snd_card_register(card->card); + if (err) + goto exit_err; + + return 0; + + exit_err: + s3c24xx_tlv320aic23_free(tlv); + s3c24xx_tlv320aic23_remove(dev); + + return err; +} + +static int s3c24xx_tlv320aic23_codec_remove(struct device *dev) +{ + s3c24xx_card_t *card; + struct s3c24xx_tlv *client; + + card = dev_get_drvdata(dev); + if (card != NULL) { + client = card->client; + + s3c24xx_iis_remove(dev); + s3c24xx_tlv320aic23_free(client); + } + + return 0; +} + + +struct device_driver s3c24xx_tlv320aic23_codecdrv = { + .name = "tlv320aic23", + .bus = &platform_bus_type, + .probe = s3c24xx_tlv320aic23_codec_probe, + .remove = s3c24xx_tlv320aic23_codec_remove, +}; + +static int __init s3c24xx_tlv320aic23_init(void) +{ + driver_register(&s3c24xx_tlv320aic23_driver); + return driver_register(&s3c24xx_tlv320aic23_codecdrv); +} + +static void __exit s3c24xx_tlv320aic23_exit(void) +{ + driver_unregister(&s3c24xx_tlv320aic23_codecdrv); + driver_unregister(&s3c24xx_tlv320aic23_driver); +} + +module_init(s3c24xx_tlv320aic23_init); +module_exit(s3c24xx_tlv320aic23_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("S3C24XX / TLV320AIC23 Audio driver"); +MODULE_AUTHOR("Ben Dooks ");