/*
 * Glue audio driver for the HP iPAQ H5400
 *
 * 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 HP iPAQ (aka Bitsy) support.
 * This driver makes use of the AK4535 and the pxa-audio modules.
 *
 * History:
 *
 * 2002-12-18   Jamey Hicks     Adapted from h3600-uda1341.c
 */

#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/slab.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/i2c.h>
#include <linux/kmod.h>

#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include <asm/arch-sa1100/h3600_hal.h>
#include <asm/arch/h5400-asic.h>

#include "ak4535.h"
#include "pxa-i2s.h"


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


#define AUDIO_NAME		"H5400"

#define AUDIO_RATE_DEFAULT	44100

/* Put this inside audio_state_t some day.  */
static struct i2c_client *ak4535;

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 i2c_command(file->private_data, cmd, (void *)arg);
}

static int mixer_open(struct inode *inode, struct file *file)
{
	struct i2c_client *ak4535;

	ak4535 = i2c_get_client(I2C_DRIVERID_AK4535, I2C_ALGO_PXA, NULL);
	if (ak4535 == NULL) {
		printk("No AK4535 detected.  Is i2c-adap-pxa loaded?\n");
		return -ENODEV;
	}

	file->private_data = ak4535;
	i2c_inc_use_client(ak4535);

	return 0;
}

static int mixer_release(struct inode *inode, struct file *file)
{
	i2c_dec_use_client(file->private_data);
	return 0;
}


static struct file_operations h5400_mixer_fops = {
	open:		mixer_open,
	release:	mixer_release,
	ioctl:		mixer_ioctl,
	owner:		THIS_MODULE
};


/*
 * Audio interface
 */

static long audio_samplerate = AUDIO_RATE_DEFAULT;

static void h5400_set_samplerate(long val)
{
	struct ak4535_cfg cfg;

	cfg.fs = pxa_i2s_set_samplerate (val);
	cfg.format = FMT_I2S;
	i2c_command(ak4535, I2C_AK4535_CONFIGURE, &cfg);

	audio_samplerate = val;
}

static void h5400_audio_init(void *dummy)
{
	h3600_audio_power(0);

	pxa_i2s_init ();
	mdelay(1);

	/* Enable the audio power */
	if (h3600_audio_power(audio_samplerate))
		printk("Unable to enable audio power\n");

	/* Wait for the AK4535 to wake up */
	mdelay(1);

	/* Initialize the AK4535 internal state */
	i2c_command(ak4535, I2C_AK4535_OPEN, 0);

	h3600_audio_mute(0);
}

static void h5400_audio_shutdown(void *dummy)
{
	printk("h5400_audio_shutdown\n");

	/* disable the audio power and all signals leading to the audio chip */
	h3600_audio_mute(1);
	i2c_command(ak4535, I2C_AK4535_CLOSE, 0);

	pxa_i2s_shutdown ();

	h3600_audio_power(0);

	i2c_dec_use_client(ak4535);
	ak4535 = NULL;
}

static int h5400_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 pxa-audio module.
	 */
	switch (cmd) {
	case SNDCTL_DSP_STEREO:
		ret = get_user(val, (int *) arg);
		if (ret)
			return ret;
		/* the AK4535 is stereo only */
		ret = (val == 0) ? -EINVAL : 1;
		return put_user(ret, (int *) arg);

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

	case SNDCTL_DSP_SPEED:
		ret = get_user(val, (long *) arg);
		if (ret) break;
		h5400_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_state_t audio_state = {
	output_stream:		&pxa_i2s_output_stream,
	input_stream:		&pxa_i2s_input_stream,
	hw_init:		h5400_audio_init,
	hw_shutdown:		h5400_audio_shutdown,
	client_ioctl:		h5400_audio_ioctl,
	sem:			__MUTEX_INITIALIZER(audio_state.sem),
};

static int h5400_audio_open(struct inode *inode, struct file *file)
{
	int err;

	ak4535 = i2c_get_client(I2C_DRIVERID_AK4535, I2C_ALGO_PXA, NULL);
	if (ak4535 == NULL) {
		printk("No AK4535 detected.  Is i2c-adap-pxa loaded?\n");
		return -ENODEV;
	}

	err = pxa_audio_attach(inode, file, &audio_state);
	if (err == 0)
		i2c_inc_use_client(ak4535);

	return err;
}

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


static int audio_dev_id, mixer_dev_id;

static int __init h5400_module_init(void)
{
	int ret;

	if (!machine_is_h5400 ())
		return -ENODEV;

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

	/* try to bring in i2c support */
	request_module("i2c-adap-pxa");
	request_module("ak4535");

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

static void __exit h5400_module_exit(void)
{
	unregister_sound_dsp(audio_dev_id);
	unregister_sound_mixer(mixer_dev_id);
}

module_init(h5400_module_init);
module_exit(h5400_module_exit);

MODULE_AUTHOR("Nicolas Pitre, George France, Jamey Hicks, Phil Blundell");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Glue audio driver for the HP iPAQ H5400");

EXPORT_NO_SYMBOLS;
