/*
 * Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec.
 *
 * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License.
 *
 * This is the machine specific part of the Compaq iPAQ (aka Bitsy) support.
 * This driver makes use of the UDA1341 and the sa1100-audio modules.
 *
 * History:
 *
 * 2000-05-21	Nicolas Pitre	Initial UDA1341 driver release.
 *
 * 2000-07-??	George France	Bitsy support.
 *
 * 2000-12-13	Deborah Wallach	Fixed power handling for iPAQ/h3600
 *
 * 2001-06-03	Nicolas Pitre	Made this file a separate module, based on
 * 				the former sa1100-uda1341.c driver.
 *
 * 2001-07-13	Nicolas Pitre	Fixes for all supported samplerates.
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/l3/l3.h>
#include <linux/l3/uda1341.h>

#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include <asm/arch/h3600_hal.h>

#include "sa1100-audio.h"


#undef DEBUG
#ifdef DEBUG
#define DPRINTK( x... )  printk( ##x )
#else
#define DPRINTK( x... )
#endif


#define AUDIO_NAME		"Bitsy_UDA1341"

#define AUDIO_RATE_DEFAULT	44100


static struct l3_client uda1341;

static int
mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{
	/*
	 * We only accept mixer (type 'M') ioctls.
	 */
	if (_IOC_TYPE(cmd) != 'M')
		return -EINVAL;

	return l3_command(&uda1341, cmd, (void *)arg);
}

static struct file_operations h3600_mixer_fops = {
	ioctl:		mixer_ioctl,
	owner:		THIS_MODULE
};


/*
 * Audio interface
 */

static long audio_samplerate = AUDIO_RATE_DEFAULT;

static void h3600_set_samplerate(long val)
{
	struct uda1341_cfg cfg;
	int clk_div = 0;

	/* We don't want to mess with clocks when frames are in flight */
	Ser4SSCR0 &= ~SSCR0_SSE;
	/* wait for any frame to complete */
	udelay(125);

	/*
	 * We have the following clock sources:
	 * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz
	 * Those can be divided either by 256, 384 or 512.
	 * This makes up 12 combinations for the following samplerates...
	 */
	if (val >= 48000)
		val = 48000;
	else if (val >= 44100)
		val = 44100;
	else if (val >= 32000)
		val = 32000;
	else if (val >= 29400)
		val = 29400;
	else if (val >= 24000)
		val = 24000;
	else if (val >= 22050)
		val = 22050;
	else if (val >= 21970)
		val = 21970;
	else if (val >= 16000)
		val = 16000;
	else if (val >= 14647)
		val = 14647;
	else if (val >= 10985)
		val = 10985;
	else if (val >= 10666)
		val = 10666;
	else
		val = 8000;

	/* Set the external clock generator */
	h3600_set_audio_clock(val);

	/* Select the clock divisor */
	switch (val) {
	case 8000:
	case 10985:
	case 22050:
	case 24000:
		cfg.fs = 512;
		clk_div = SSCR0_SerClkDiv(16);
		break;
	case 16000:
	case 21970:
	case 44100:
	case 48000:
		cfg.fs = 256;
		clk_div = SSCR0_SerClkDiv(8);
		break;
	case 10666:
	case 14647:
	case 29400:
	case 32000:
		cfg.fs = 384;
		clk_div = SSCR0_SerClkDiv(12);
		break;
	}

	cfg.format = FMT_LSB16;
	l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg);
	Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
	audio_samplerate = val;
}

static void h3600_audio_init(void *dummy)
{
	unsigned long flags;

	/* Setup the uarts */
	local_irq_save(flags);
	GAFR |= (GPIO_SSP_CLK);
	GPDR &= ~(GPIO_SSP_CLK);
	Ser4SSCR0 = 0;
	Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8);
	Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
	Ser4SSCR0 |= SSCR0_SSE;

	/* Enable the audio power */
	
	clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET);
	set_h3600_egpio(IPAQ_EGPIO_AUDIO_ON);
	set_h3600_egpio(IPAQ_EGPIO_QMUTE);
 	local_irq_restore(flags);

	/* external clock configuration */
	h3600_set_samplerate(audio_samplerate);

	/* Wait for the UDA1341 to wake up */
	set_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET);
	mdelay(1);

	/* make the left and right channels unswapped (flip the WS latch ) */
	Ser4SSDR = 0;

	/* Initialize the UDA1341 internal state */
	l3_open(&uda1341);

	clr_h3600_egpio(IPAQ_EGPIO_QMUTE);
}

static void h3600_audio_shutdown(void *dummy)
{
	/* disable the audio power and all signals leading to the audio chip */
	l3_close(&uda1341);
	Ser4SSCR0 = 0;
	clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET);
	clr_h3600_egpio(IPAQ_EGPIO_AUDIO_ON);
	clr_h3600_egpio(IPAQ_EGPIO_QMUTE);
}

static int h3600_audio_ioctl(struct inode *inode, struct file *file,
			     uint cmd, ulong arg)
{
	long val;
	int ret = 0;

	/*
	 * These are platform dependent ioctls which are not handled by the
	 * generic sa1100-audio module.
	 */
	switch (cmd) {
	case SNDCTL_DSP_STEREO:
		ret = get_user(val, (int *) arg);
		if (ret)
			return ret;
		/* the UDA1341 is stereo only */
		ret = (val == 0) ? -EINVAL : 1;
		return put_user(ret, (int *) arg);

	case SNDCTL_DSP_CHANNELS:
	case SOUND_PCM_READ_CHANNELS:
		/* the UDA1341 is stereo only */
		return put_user(2, (long *) arg);

	case SNDCTL_DSP_SPEED:
		ret = get_user(val, (long *) arg);
		if (ret) break;
		h3600_set_samplerate(val);
		/* fall through */

	case SOUND_PCM_READ_RATE:
		return put_user(audio_samplerate, (long *) arg);

	case SNDCTL_DSP_SETFMT:
	case SNDCTL_DSP_GETFMTS:
		/* we can do 16-bit only */
		return put_user(AFMT_S16_LE, (long *) arg);

	default:
		/* Maybe this is meant for the mixer (As per OSS Docs) */
		return mixer_ioctl(inode, file, cmd, arg);
	}

	return ret;
}

static audio_stream_t output_stream, input_stream;

static audio_state_t audio_state = {
	output_stream:	&output_stream,
	output_dma:	DMA_Ser4SSPWr,
	output_id:	"UDA1341 out",
	input_stream:	&input_stream,
	input_dma:	DMA_Ser4SSPRd,
	input_id:	"UDA1341 in",
	need_tx_for_rx:	1,
	hw_init:	h3600_audio_init,
	hw_shutdown:	h3600_audio_shutdown,
	client_ioctl:	h3600_audio_ioctl,
	sem:		__MUTEX_INITIALIZER(audio_state.sem),
};

static int h3600_audio_open(struct inode *inode, struct file *file)
{
	return sa1100_audio_attach(inode, file, &audio_state);
}

/*
 * Missing fields of this structure will be patched with the call
 * to sa1100_audio_attach().
 */
static struct file_operations h3600_audio_fops = {
	open:		h3600_audio_open,
	owner:		THIS_MODULE
};


static int audio_dev_id, mixer_dev_id;

static int __init h3600_uda1341_init(void)
{
	int ret;

	if (!machine_is_h3xxx())
		return -ENODEV;

	ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341");
	if (ret)
		goto out;

	/* register devices */
	audio_dev_id = register_sound_dsp(&h3600_audio_fops, -1);
	mixer_dev_id = register_sound_mixer(&h3600_mixer_fops, -1);

	printk( KERN_INFO "iPAQ audio support initialized\n" );
	return 0;

release_l3:
	l3_detach_client(&uda1341);
out:
	return ret;
}

static void __exit h3600_uda1341_exit(void)
{
	unregister_sound_dsp(audio_dev_id);
	unregister_sound_mixer(mixer_dev_id);
	l3_detach_client(&uda1341);
}

module_init(h3600_uda1341_init);
module_exit(h3600_uda1341_exit);

MODULE_AUTHOR("Nicolas Pitre, George France");
MODULE_DESCRIPTION("Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec.");

EXPORT_NO_SYMBOLS;
