rx1950_uda1380.c 6.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * rx1950.c  --  ALSA Soc Audio Layer
 *
 * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
 *
 * Based on smdk2440.c and magician.c
 *
 * Authors: Graeme Gregory graeme.gregory@wolfsonmicro.com
 *          Philipp Zabel <philipp.zabel@gmail.com>
 *          Denis Grigoriev <dgreenday@gmail.com>
 *          Vasily Khoruzhick <anarsoul@gmail.com>
 *
 *  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.
 *
 */

20
#include <linux/types.h>
21
#include <linux/gpio.h>
22
#include <linux/module.h>
23 24 25 26

#include <sound/soc.h>
#include <sound/jack.h>

27
#include <mach/gpio-samsung.h>
28
#include "regs-iis.h"
29 30
#include <asm/mach-types.h>

31 32 33
#include "s3c24xx-i2s.h"

static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
34
static int rx1950_uda1380_card_remove(struct snd_soc_card *card);
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
static int rx1950_startup(struct snd_pcm_substream *substream);
static int rx1950_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params);
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
				struct snd_kcontrol *kcontrol, int event);

static unsigned int rates[] = {
	16000,
	44100,
	48000,
};

static struct snd_pcm_hw_constraint_list hw_rates = {
	.count = ARRAY_SIZE(rates),
	.list = rates,
	.mask = 0,
};

static struct snd_soc_jack hp_jack;

static struct snd_soc_jack_pin hp_jack_pins[] = {
	{
		.pin	= "Headphone Jack",
		.mask	= SND_JACK_HEADPHONE,
	},
	{
		.pin	= "Speaker",
		.mask	= SND_JACK_HEADPHONE,
		.invert	= 1,
	},
};

static struct snd_soc_jack_gpio hp_jack_gpios[] = {
	[0] = {
		.gpio			= S3C2410_GPG(12),
		.name			= "hp-gpio",
		.report			= SND_JACK_HEADPHONE,
		.invert			= 1,
		.debounce_time		= 200,
	},
};

static struct snd_soc_ops rx1950_ops = {
	.startup	= rx1950_startup,
	.hw_params	= rx1950_hw_params,
};

/* s3c24xx digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link rx1950_uda1380_dai[] = {
	{
		.name		= "uda1380",
		.stream_name	= "UDA1380 Duplex",
		.cpu_dai_name	= "s3c24xx-iis",
		.codec_dai_name	= "uda1380-hifi",
		.init		= rx1950_uda1380_init,
90
		.platform_name	= "s3c24xx-iis",
91
		.codec_name	= "uda1380-codec.0-001a",
92 93
		.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
				  SND_SOC_DAIFMT_CBS_CFS,
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
		.ops		= &rx1950_ops,
	},
};

/* rx1950 machine dapm widgets */
static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
	SND_SOC_DAPM_HP("Headphone Jack", NULL),
	SND_SOC_DAPM_MIC("Mic Jack", NULL),
	SND_SOC_DAPM_SPK("Speaker", rx1950_spk_power),
};

/* rx1950 machine audio_map */
static const struct snd_soc_dapm_route audio_map[] = {
	/* headphone connected to VOUTLHP, VOUTRHP */
	{"Headphone Jack", NULL, "VOUTLHP"},
	{"Headphone Jack", NULL, "VOUTRHP"},

	/* ext speaker connected to VOUTL, VOUTR  */
	{"Speaker", NULL, "VOUTL"},
	{"Speaker", NULL, "VOUTR"},

	/* mic is connected to VINM */
	{"VINM", NULL, "Mic Jack"},
};

119 120
static struct snd_soc_card rx1950_asoc = {
	.name = "rx1950",
121
	.owner = THIS_MODULE,
122
	.remove = rx1950_uda1380_card_remove,
123 124 125 126 127 128 129 130 131
	.dai_link = rx1950_uda1380_dai,
	.num_links = ARRAY_SIZE(rx1950_uda1380_dai),

	.dapm_widgets = uda1380_dapm_widgets,
	.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
	.dapm_routes = audio_map,
	.num_dapm_routes = ARRAY_SIZE(audio_map),
};

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
static struct platform_device *s3c24xx_snd_device;

static int rx1950_startup(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;

	return snd_pcm_hw_constraint_list(runtime, 0,
					SNDRV_PCM_HW_PARAM_RATE,
					&hw_rates);
}

static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
				struct snd_kcontrol *kcontrol, int event)
{
	if (SND_SOC_DAPM_EVENT_ON(event))
		gpio_set_value(S3C2410_GPA(1), 1);
	else
		gpio_set_value(S3C2410_GPA(1), 0);

	return 0;
}

static int rx1950_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_dai *cpu_dai = rtd->cpu_dai;
	int div;
	int ret;
	unsigned int rate = params_rate(params);
	int clk_source, fs_mode;

	switch (rate) {
	case 16000:
	case 48000:
		clk_source = S3C24XX_CLKSRC_PCLK;
168 169 170
		fs_mode = S3C2410_IISMOD_256FS;
		div = s3c24xx_i2s_get_clockrate() / (256 * rate);
		if (s3c24xx_i2s_get_clockrate() % (256 * rate) > (128 * rate))
171 172 173 174 175
			div++;
		break;
	case 44100:
	case 88200:
		clk_source = S3C24XX_CLKSRC_MPLL;
176 177
		fs_mode = S3C2410_IISMOD_384FS;
		div = 1;
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
		break;
	default:
		printk(KERN_ERR "%s: rate %d is not supported\n",
			__func__, rate);
		return -EINVAL;
	}

	/* select clock source */
	ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source, rate,
			SND_SOC_CLOCK_OUT);
	if (ret < 0)
		return ret;

	/* set MCLK division for sample rate */
	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
193
		fs_mode);
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	if (ret < 0)
		return ret;

	/* set BCLK division for sample rate */
	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
		S3C2410_IISMOD_32FS);
	if (ret < 0)
		return ret;

	/* set prescaler division for sample rate */
	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
		S3C24XX_PRESCALE(div, div));
	if (ret < 0)
		return ret;

	return 0;
}

static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_soc_codec *codec = rtd->codec;

	snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
		&hp_jack);

	snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
		hp_jack_pins);

	snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
		hp_jack_gpios);

	return 0;
}

228
static int rx1950_uda1380_card_remove(struct snd_soc_card *card)
229 230 231 232 233 234 235
{
	snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
		hp_jack_gpios);

	return 0;
}

236 237 238 239
static int __init rx1950_init(void)
{
	int ret;

240 241 242
	if (!machine_is_rx1950())
		return -ENODEV;

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	/* configure some gpios */
	ret = gpio_request(S3C2410_GPA(1), "speaker-power");
	if (ret)
		goto err_gpio;

	ret = gpio_direction_output(S3C2410_GPA(1), 0);
	if (ret)
		goto err_gpio_conf;

	s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
	if (!s3c24xx_snd_device) {
		ret = -ENOMEM;
		goto err_plat_alloc;
	}

	platform_set_drvdata(s3c24xx_snd_device, &rx1950_asoc);
	ret = platform_device_add(s3c24xx_snd_device);

	if (ret) {
		platform_device_put(s3c24xx_snd_device);
		goto err_plat_add;
	}

	return 0;

err_plat_add:
err_plat_alloc:
err_gpio_conf:
	gpio_free(S3C2410_GPA(1));

err_gpio:
	return ret;
}

static void __exit rx1950_exit(void)
{
	platform_device_unregister(s3c24xx_snd_device);
	gpio_free(S3C2410_GPA(1));
}

module_init(rx1950_init);
module_exit(rx1950_exit);

/* Module information */
MODULE_AUTHOR("Vasily Khoruzhick");
MODULE_DESCRIPTION("ALSA SoC RX1950");
MODULE_LICENSE("GPL");