jz4740.c 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
 *
 * 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.
 *
 *  You should have received a copy of the  GNU General Public License along
 *  with this program; if not, write  to the Free Software Foundation, Inc.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
18
#include <linux/io.h>
19
#include <linux/regmap.h>
20 21 22 23 24 25 26 27

#include <linux/delay.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
28
#include <sound/tlv.h>
29 30

#define JZ4740_REG_CODEC_1 0x0
31
#define JZ4740_REG_CODEC_2 0x4
32 33 34 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

#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
#define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18)
#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
#define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12)
#define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10))
#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9)
#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8)
#define JZ4740_CODEC_1_SUSPEND BIT(1)
#define JZ4740_CODEC_1_RESET BIT(0)

#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8

#define JZ4740_CODEC_2_INPUT_VOLUME_MASK		0x1f0000
#define JZ4740_CODEC_2_SAMPLE_RATE_MASK			0x000f00
#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK		0x000030
#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK	0x000003

#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET		16
#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET		 8
#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET	 4
#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET	 0

72 73 74
static const struct reg_default jz4740_codec_reg_defaults[] = {
	{ JZ4740_REG_CODEC_1, 0x021b2302 },
	{ JZ4740_REG_CODEC_2, 0x00170803 },
75 76 77
};

struct jz4740_codec {
78
	struct regmap *regmap;
79 80
};

81
static const DECLARE_TLV_DB_RANGE(jz4740_mic_tlv,
82
	0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
83 84
	3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0)
);
85 86 87 88

static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0);
static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0);

89
static const struct snd_kcontrol_new jz4740_codec_controls[] = {
90 91 92 93 94 95
	SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2,
			JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0,
			jz4740_out_tlv),
	SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2,
			JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0,
			jz4740_in_tlv),
96 97
	SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
98 99 100
	SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2,
			JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0,
			jz4740_mic_tlv),
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 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
};

static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
	SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
	SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
};

static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
	SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
	SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
};

static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
	SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
	SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),

	SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1,
			jz4740_codec_output_controls,
			ARRAY_SIZE(jz4740_codec_output_controls)),

	SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
			jz4740_codec_input_controls,
			ARRAY_SIZE(jz4740_codec_input_controls)),
	SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),

	SND_SOC_DAPM_OUTPUT("LOUT"),
	SND_SOC_DAPM_OUTPUT("ROUT"),

	SND_SOC_DAPM_INPUT("MIC"),
	SND_SOC_DAPM_INPUT("LIN"),
	SND_SOC_DAPM_INPUT("RIN"),
};

static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
	{"Line Input", NULL, "LIN"},
	{"Line Input", NULL, "RIN"},

	{"Input Mixer", "Line Capture Switch", "Line Input"},
	{"Input Mixer", "Mic Capture Switch", "MIC"},

	{"ADC", NULL, "Input Mixer"},

	{"Output Mixer", "Bypass Switch", "Input Mixer"},
	{"Output Mixer", "DAC Switch", "DAC"},

	{"LOUT", NULL, "Output Mixer"},
	{"ROUT", NULL, "Output Mixer"},
};

static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
160
	struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(dai->codec);
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	uint32_t val;

	switch (params_rate(params)) {
	case 8000:
		val = 0;
		break;
	case 11025:
		val = 1;
		break;
	case 12000:
		val = 2;
		break;
	case 16000:
		val = 3;
		break;
	case 22050:
		val = 4;
		break;
	case 24000:
		val = 5;
		break;
	case 32000:
		val = 6;
		break;
	case 44100:
		val = 7;
		break;
	case 48000:
		val = 8;
		break;
	default:
		return -EINVAL;
	}

	val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;

197
	regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2,
198 199 200 201 202
				JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);

	return 0;
}

203
static const struct snd_soc_dai_ops jz4740_codec_dai_ops = {
204 205 206
	.hw_params = jz4740_codec_hw_params,
};

207 208
static struct snd_soc_dai_driver jz4740_codec_dai = {
	.name = "jz4740-hifi",
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
	.playback = {
		.stream_name = "Playback",
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
	},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
	},
	.ops = &jz4740_codec_dai_ops,
	.symmetric_rates = 1,
};

227
static void jz4740_codec_wakeup(struct regmap *regmap)
228
{
229
	regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
230 231 232
		JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
	udelay(2);

233
	regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
234 235
		JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);

236
	regcache_sync(regmap);
237 238 239 240 241
}

static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
	enum snd_soc_bias_level level)
{
242 243
	struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
	struct regmap *regmap = jz4740_codec->regmap;
244 245 246 247 248 249 250 251 252 253 254 255
	unsigned int mask;
	unsigned int value;

	switch (level) {
	case SND_SOC_BIAS_ON:
		break;
	case SND_SOC_BIAS_PREPARE:
		mask = JZ4740_CODEC_1_VREF_DISABLE |
				JZ4740_CODEC_1_VREF_AMP_DISABLE |
				JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
		value = 0;

256
		regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
257 258 259
		break;
	case SND_SOC_BIAS_STANDBY:
		/* The only way to clear the suspend flag is to reset the codec */
260
		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
261
			jz4740_codec_wakeup(regmap);
262 263 264 265 266 267 268 269

		mask = JZ4740_CODEC_1_VREF_DISABLE |
			JZ4740_CODEC_1_VREF_AMP_DISABLE |
			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
		value = JZ4740_CODEC_1_VREF_DISABLE |
			JZ4740_CODEC_1_VREF_AMP_DISABLE |
			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;

270
		regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
271 272 273 274 275
		break;
	case SND_SOC_BIAS_OFF:
		mask = JZ4740_CODEC_1_SUSPEND;
		value = JZ4740_CODEC_1_SUSPEND;

276 277
		regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
		regcache_mark_dirty(regmap);
278 279 280 281 282 283 284 285
		break;
	default:
		break;
	}

	return 0;
}

286
static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
287
{
288 289 290
	struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);

	regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
291
			JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
292 293 294 295

	return 0;
}

296
static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
297
	.probe = jz4740_codec_dev_probe,
298
	.set_bias_level = jz4740_codec_set_bias_level,
299
	.suspend_bias_off = true,
300

301 302 303 304 305 306 307 308
	.component_driver = {
		.controls		= jz4740_codec_controls,
		.num_controls		= ARRAY_SIZE(jz4740_codec_controls),
		.dapm_widgets		= jz4740_codec_dapm_widgets,
		.num_dapm_widgets	= ARRAY_SIZE(jz4740_codec_dapm_widgets),
		.dapm_routes		= jz4740_codec_dapm_routes,
		.num_dapm_routes	= ARRAY_SIZE(jz4740_codec_dapm_routes),
	},
309 310
};

311 312 313 314 315 316 317 318 319 320 321
static const struct regmap_config jz4740_codec_regmap_config = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.max_register = JZ4740_REG_CODEC_2,

	.reg_defaults = jz4740_codec_reg_defaults,
	.num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
	.cache_type = REGCACHE_RBTREE,
};

322
static int jz4740_codec_probe(struct platform_device *pdev)
323 324 325 326
{
	int ret;
	struct jz4740_codec *jz4740_codec;
	struct resource *mem;
327
	void __iomem *base;
328

A
Axel Lin 已提交
329 330
	jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
				    GFP_KERNEL);
331 332 333 334
	if (!jz4740_codec)
		return -ENOMEM;

	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
335 336 337
	base = devm_ioremap_resource(&pdev->dev, mem);
	if (IS_ERR(base))
		return PTR_ERR(base);
338

339 340 341 342 343
	jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
					    &jz4740_codec_regmap_config);
	if (IS_ERR(jz4740_codec->regmap))
		return PTR_ERR(jz4740_codec->regmap);

344 345
	platform_set_drvdata(pdev, jz4740_codec);

346 347
	ret = snd_soc_register_codec(&pdev->dev,
			&soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1);
348
	if (ret)
349 350 351 352 353
		dev_err(&pdev->dev, "Failed to register codec\n");

	return ret;
}

354
static int jz4740_codec_remove(struct platform_device *pdev)
355
{
356
	snd_soc_unregister_codec(&pdev->dev);
357 358 359 360 361 362

	return 0;
}

static struct platform_driver jz4740_codec_driver = {
	.probe = jz4740_codec_probe,
363
	.remove = jz4740_codec_remove,
364 365 366 367 368
	.driver = {
		.name = "jz4740-codec",
	},
};

369
module_platform_driver(jz4740_codec_driver);
370 371 372 373 374

MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:jz4740-codec");