simple-card.c 15.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * ASoC simple sound card support
 *
 * Copyright (C) 2012 Renesas Solutions Corp.
 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 *
 * 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.
 */
11
#include <linux/clk.h>
12
#include <linux/device.h>
13
#include <linux/gpio.h>
14
#include <linux/module.h>
15
#include <linux/of.h>
16
#include <linux/of_gpio.h>
17
#include <linux/platform_device.h>
18
#include <linux/string.h>
19
#include <sound/jack.h>
20
#include <sound/simple_card.h>
21 22
#include <sound/soc-dai.h>
#include <sound/soc.h>
23

24 25
struct simple_card_data {
	struct snd_soc_card snd_card;
26 27 28 29
	struct simple_dai_props {
		struct asoc_simple_dai cpu_dai;
		struct asoc_simple_dai codec_dai;
	} *dai_props;
30
	unsigned int mclk_fs;
31
	int gpio_hp_det;
32
	int gpio_hp_det_invert;
33
	int gpio_mic_det;
34
	int gpio_mic_det_invert;
35
	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
36 37
};

38
#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
39 40
#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
#define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
static int asoc_simple_card_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 *codec_dai = rtd->codec_dai;
	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
	unsigned int mclk;
	int ret = 0;

	if (priv->mclk_fs) {
		mclk = params_rate(params) * priv->mclk_fs;
		ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
					     SND_SOC_CLOCK_IN);
	}

	return ret;
}

static struct snd_soc_ops asoc_simple_card_ops = {
	.hw_params = asoc_simple_card_hw_params,
};

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 struct snd_soc_jack simple_card_hp_jack;
static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
	{
		.pin = "Headphones",
		.mask = SND_JACK_HEADPHONE,
	},
};
static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
	.name = "Headphone detection",
	.report = SND_JACK_HEADPHONE,
	.debounce_time = 150,
};

static struct snd_soc_jack simple_card_mic_jack;
static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
	{
		.pin = "Mic Jack",
		.mask = SND_JACK_MICROPHONE,
	},
};
static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
	.name = "Mic detection",
	.report = SND_JACK_MICROPHONE,
	.debounce_time = 150,
};

90
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
91
				       struct asoc_simple_dai *set)
92
{
93
	int ret;
94

95 96
	if (set->fmt) {
		ret = snd_soc_dai_set_fmt(dai, set->fmt);
97 98 99 100
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_fmt error\n");
			goto err;
		}
101 102
	}

103
	if (set->sysclk) {
104
		ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
105 106 107 108 109 110
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_sysclk error\n");
			goto err;
		}
	}

111 112 113 114 115 116 117 118 119 120
	if (set->slots) {
		ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
						set->slots,
						set->slot_width);
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
			goto err;
		}
	}

121
	ret = 0;
122

123
err:
124 125 126
	return ret;
}

127 128
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
129
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
130 131
	struct snd_soc_dai *codec = rtd->codec_dai;
	struct snd_soc_dai *cpu = rtd->cpu_dai;
132 133
	struct simple_dai_props *dai_props;
	int num, ret;
134

135 136 137
	num = rtd - rtd->card->rtd;
	dai_props = &priv->dai_props[num];
	ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
138 139
	if (ret < 0)
		return ret;
140

141
	ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai);
142 143
	if (ret < 0)
		return ret;
144

145 146 147 148 149 150 151 152
	if (gpio_is_valid(priv->gpio_hp_det)) {
		snd_soc_jack_new(codec->codec, "Headphones", SND_JACK_HEADPHONE,
				 &simple_card_hp_jack);
		snd_soc_jack_add_pins(&simple_card_hp_jack,
				      ARRAY_SIZE(simple_card_hp_jack_pins),
				      simple_card_hp_jack_pins);

		simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
153
		simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
154 155 156 157 158 159 160 161 162 163 164
		snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
				       &simple_card_hp_jack_gpio);
	}

	if (gpio_is_valid(priv->gpio_mic_det)) {
		snd_soc_jack_new(codec->codec, "Mic Jack", SND_JACK_MICROPHONE,
				 &simple_card_mic_jack);
		snd_soc_jack_add_pins(&simple_card_mic_jack,
				      ARRAY_SIZE(simple_card_mic_jack_pins),
				      simple_card_mic_jack_pins);
		simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
165
		simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
166 167 168
		snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
				       &simple_card_mic_jack_gpio);
	}
169 170 171
	return 0;
}

172 173 174
static int
asoc_simple_card_sub_parse_of(struct device_node *np,
			      struct asoc_simple_dai *dai,
175
			      struct device_node **p_node,
176 177
			      const char **name,
			      int *args_count)
178
{
179
	struct of_phandle_args args;
180
	struct clk *clk;
181
	u32 val;
182 183 184
	int ret;

	/*
185
	 * Get node via "sound-dai = <&phandle port>"
186 187
	 * it will be used as xxx_of_node on soc_bind_dai_link()
	 */
188 189 190 191 192 193 194 195 196
	ret = of_parse_phandle_with_args(np, "sound-dai",
					 "#sound-dai-cells", 0, &args);
	if (ret)
		return ret;

	*p_node = args.np;

	if (args_count)
		*args_count = args.args_count;
197

198
	/* Get dai->name */
199
	ret = snd_soc_of_get_dai_name(np, name);
200
	if (ret < 0)
201
		return ret;
202

203
	/* Parse TDM slot */
204 205
	ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
	if (ret)
206
		return ret;
207

208
	/*
209 210
	 * Parse dai->sysclk come from "clocks = <&xxx>"
	 * (if system has common clock)
211
	 *  or "system-clock-frequency = <xxx>"
212
	 *  or device's module clock.
213
	 */
214 215 216 217
	if (of_property_read_bool(np, "clocks")) {
		clk = of_clk_get(np, 0);
		if (IS_ERR(clk)) {
			ret = PTR_ERR(clk);
218
			return ret;
219 220 221
		}

		dai->sysclk = clk_get_rate(clk);
222 223
	} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
		dai->sysclk = val;
224
	} else {
225
		clk = of_clk_get(args.np, 0);
226 227
		if (!IS_ERR(clk))
			dai->sysclk = clk_get_rate(clk);
228
	}
229

230
	return 0;
231 232
}

233 234 235 236 237 238 239 240 241 242 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
static int asoc_simple_card_parse_daifmt(struct device_node *node,
					 struct simple_card_data *priv,
					 struct device_node *codec,
					 char *prefix, int idx)
{
	struct device *dev = simple_priv_to_dev(priv);
	struct device_node *bitclkmaster = NULL;
	struct device_node *framemaster = NULL;
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
	struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
	struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
	unsigned int daifmt;

	daifmt = snd_soc_of_parse_daifmt(node, prefix,
					 &bitclkmaster, &framemaster);
	daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;

	if (strlen(prefix) && !bitclkmaster && !framemaster) {
		/*
		 * No dai-link level and master setting was not found from
		 * sound node level, revert back to legacy DT parsing and
		 * take the settings from codec node.
		 */
		dev_dbg(dev, "Revert to legacy daifmt parsing\n");

		cpu_dai->fmt = codec_dai->fmt =
			snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
			(daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
	} else {
		if (codec == bitclkmaster)
			daifmt |= (codec == framemaster) ?
				SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
		else
			daifmt |= (codec == framemaster) ?
				SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;

		cpu_dai->fmt	= daifmt;
		codec_dai->fmt	= daifmt;
	}

	of_node_put(bitclkmaster);
	of_node_put(framemaster);

	return 0;
}

279
static int asoc_simple_card_dai_link_of(struct device_node *node,
280
					struct simple_card_data *priv,
281
					int idx,
282
					bool is_top_level_node)
283
{
284
	struct device *dev = simple_priv_to_dev(priv);
285 286
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
287 288
	struct device_node *cpu = NULL;
	struct device_node *codec = NULL;
289 290 291
	char *name;
	char prop[128];
	char *prefix = "";
292
	int ret, cpu_args;
293

294
	/* For single DAI link & old style of DT node */
295 296
	if (is_top_level_node)
		prefix = "simple-audio-card,";
297 298

	snprintf(prop, sizeof(prop), "%scpu", prefix);
299 300 301 302 303 304
	cpu = of_get_child_by_name(node, prop);

	snprintf(prop, sizeof(prop), "%scodec", prefix);
	codec = of_get_child_by_name(node, prop);

	if (!cpu || !codec) {
305
		ret = -EINVAL;
306
		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
307
		goto dai_link_of_err;
308
	}
309

310
	ret = asoc_simple_card_parse_daifmt(node, priv,
311
					    codec, prefix, idx);
312 313 314 315
	if (ret < 0)
		goto dai_link_of_err;

	ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
316
					    &dai_link->cpu_of_node,
317 318
					    &dai_link->cpu_dai_name,
					    &cpu_args);
319
	if (ret < 0)
320 321
		goto dai_link_of_err;

322
	ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
323
					    &dai_link->codec_of_node,
324
					    &dai_link->codec_dai_name, NULL);
325 326 327 328
	if (ret < 0)
		goto dai_link_of_err;

	if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
329 330
		ret = -EINVAL;
		goto dai_link_of_err;
331 332
	}

333
	/* Simple Card assumes platform == cpu */
334 335
	dai_link->platform_of_node = dai_link->cpu_of_node;

336
	/* DAI link name is created from CPU/CODEC dai name */
337 338 339 340 341 342 343
	name = devm_kzalloc(dev,
			    strlen(dai_link->cpu_dai_name)   +
			    strlen(dai_link->codec_dai_name) + 2,
			    GFP_KERNEL);
	sprintf(name, "%s-%s", dai_link->cpu_dai_name,
				dai_link->codec_dai_name);
	dai_link->name = dai_link->stream_name = name;
344
	dai_link->ops = &asoc_simple_card_ops;
345
	dai_link->init = asoc_simple_card_dai_init;
346 347 348 349 350 351 352 353 354 355 356

	dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
	dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
		dai_link->cpu_dai_name,
		dai_props->cpu_dai.fmt,
		dai_props->cpu_dai.sysclk);
	dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
		dai_link->codec_dai_name,
		dai_props->codec_dai.fmt,
		dai_props->codec_dai.sysclk);

357
	/*
358 359 360 361 362
	 * In soc_bind_dai_link() will check cpu name after
	 * of_node matching if dai_link has cpu_dai_name.
	 * but, it will never match if name was created by
	 * fmt_single_name() remove cpu_dai_name if cpu_args
	 * was 0. See:
363 364 365
	 *	fmt_single_name()
	 *	fmt_multiple_name()
	 */
366 367
	if (!cpu_args)
		dai_link->cpu_dai_name = NULL;
368

369
dai_link_of_err:
370 371 372
	of_node_put(cpu);
	of_node_put(codec);

373 374 375
	return ret;
}

376
static int asoc_simple_card_parse_of(struct device_node *node,
377
				     struct simple_card_data *priv)
378
{
379
	struct device *dev = simple_priv_to_dev(priv);
380
	enum of_gpio_flags flags;
381
	u32 val;
382
	int ret;
383

384 385 386
	if (!node)
		return -EINVAL;

387
	/* Parse the card name from DT */
388 389
	snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");

390
	/* The off-codec widgets */
391 392 393 394 395 396 397
	if (of_property_read_bool(node, "simple-audio-card,widgets")) {
		ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
					"simple-audio-card,widgets");
		if (ret)
			return ret;
	}

398
	/* DAPM routes */
399
	if (of_property_read_bool(node, "simple-audio-card,routing")) {
400
		ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
401
					"simple-audio-card,routing");
402 403 404
		if (ret)
			return ret;
	}
405

406
	/* Factor to mclk, used in hw_params() */
407 408 409
	ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val);
	if (ret == 0)
		priv->mclk_fs = val;
410

411 412 413
	dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
		priv->snd_card.name : "");

414 415
	/* Single/Muti DAI link(s) & New style of DT node */
	if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
416
		struct device_node *np = NULL;
417 418 419
		int i = 0;

		for_each_child_of_node(node, np) {
420
			dev_dbg(dev, "\tlink %d:\n", i);
421
			ret = asoc_simple_card_dai_link_of(np, priv,
422
							   i, false);
423 424 425 426
			if (ret < 0) {
				of_node_put(np);
				return ret;
			}
427
			i++;
428
		}
429
	} else {
430
		/* For single DAI link & old style of DT node */
431
		ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
432
		if (ret < 0)
433
			return ret;
434
	}
435

436 437 438
	priv->gpio_hp_det = of_get_named_gpio_flags(node,
				"simple-audio-card,hp-det-gpio", 0, &flags);
	priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
439 440 441
	if (priv->gpio_hp_det == -EPROBE_DEFER)
		return -EPROBE_DEFER;

442 443 444
	priv->gpio_mic_det = of_get_named_gpio_flags(node,
				"simple-audio-card,mic-det-gpio", 0, &flags);
	priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
445 446 447
	if (priv->gpio_mic_det == -EPROBE_DEFER)
		return -EPROBE_DEFER;

448
	if (!priv->snd_card.name)
449
		priv->snd_card.name = priv->snd_card.dai_link->name;
450

451 452 453
	return 0;
}

454
/* Decrease the reference count of the device nodes */
455 456 457 458 459 460 461 462 463
static int asoc_simple_card_unref(struct platform_device *pdev)
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);
	struct snd_soc_dai_link *dai_link;
	int num_links;

	for (num_links = 0, dai_link = card->dai_link;
	     num_links < card->num_links;
	     num_links++, dai_link++) {
464 465
		of_node_put(dai_link->cpu_of_node);
		of_node_put(dai_link->codec_of_node);
466 467 468 469
	}
	return 0;
}

470 471
static int asoc_simple_card_probe(struct platform_device *pdev)
{
472
	struct simple_card_data *priv;
473
	struct snd_soc_dai_link *dai_link;
474
	struct device_node *np = pdev->dev.of_node;
475
	struct device *dev = &pdev->dev;
476
	int num_links, ret;
477

478
	/* Get the number of DAI links */
479
	if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
480
		num_links = of_get_child_count(np);
481
	else
482
		num_links = 1;
483

484
	/* Allocate the private data and the DAI link array */
485
	priv = devm_kzalloc(dev,
486
			sizeof(*priv) + sizeof(*dai_link) * num_links,
487
			GFP_KERNEL);
488
	if (!priv)
489 490
		return -ENOMEM;

491
	/* Init snd_soc_card */
492 493
	priv->snd_card.owner = THIS_MODULE;
	priv->snd_card.dev = dev;
494
	dai_link = priv->dai_link;
495
	priv->snd_card.dai_link = dai_link;
496
	priv->snd_card.num_links = num_links;
497

498 499 500
	priv->gpio_hp_det = -ENOENT;
	priv->gpio_mic_det = -ENOENT;

501
	/* Get room for the other properties */
502
	priv->dai_props = devm_kzalloc(dev,
503
			sizeof(*priv->dai_props) * num_links,
504 505 506 507
			GFP_KERNEL);
	if (!priv->dai_props)
		return -ENOMEM;

508
	if (np && of_device_is_available(np)) {
509

510
		ret = asoc_simple_card_parse_of(np, priv);
511 512 513
		if (ret < 0) {
			if (ret != -EPROBE_DEFER)
				dev_err(dev, "parse error %d\n", ret);
514
			goto err;
515
		}
516

517
	} else {
518 519 520 521
		struct asoc_simple_card_info *cinfo;

		cinfo = dev->platform_data;
		if (!cinfo) {
522 523 524
			dev_err(dev, "no info for asoc-simple-card\n");
			return -EINVAL;
		}
525

526 527 528 529
		if (!cinfo->name ||
		    !cinfo->codec_dai.name ||
		    !cinfo->codec ||
		    !cinfo->platform ||
530 531 532 533
		    !cinfo->cpu_dai.name) {
			dev_err(dev, "insufficient asoc_simple_card_info settings\n");
			return -EINVAL;
		}
534

535
		priv->snd_card.name	= (cinfo->card) ? cinfo->card : cinfo->name;
536 537 538 539
		dai_link->name		= cinfo->name;
		dai_link->stream_name	= cinfo->name;
		dai_link->platform_name	= cinfo->platform;
		dai_link->codec_name	= cinfo->codec;
540 541
		dai_link->cpu_dai_name	= cinfo->cpu_dai.name;
		dai_link->codec_dai_name = cinfo->codec_dai.name;
542
		dai_link->init		= asoc_simple_card_dai_init;
543 544 545 546
		memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
					sizeof(priv->dai_props->cpu_dai));
		memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
					sizeof(priv->dai_props->codec_dai));
547

548 549
		priv->dai_props->cpu_dai.fmt	|= cinfo->daifmt;
		priv->dai_props->codec_dai.fmt	|= cinfo->daifmt;
550 551
	}

552
	snd_soc_card_set_drvdata(&priv->snd_card, priv);
553

554
	ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
555 556
	if (ret >= 0)
		return ret;
557 558 559 560

err:
	asoc_simple_card_unref(pdev);
	return ret;
561 562
}

563 564
static int asoc_simple_card_remove(struct platform_device *pdev)
{
565 566 567 568 569 570 571 572 573 574
	struct snd_soc_card *card = platform_get_drvdata(pdev);
	struct simple_card_data *priv = snd_soc_card_get_drvdata(card);

	if (gpio_is_valid(priv->gpio_hp_det))
		snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
					&simple_card_hp_jack_gpio);
	if (gpio_is_valid(priv->gpio_mic_det))
		snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
					&simple_card_mic_jack_gpio);

575 576 577
	return asoc_simple_card_unref(pdev);
}

578 579 580 581 582 583
static const struct of_device_id asoc_simple_of_match[] = {
	{ .compatible = "simple-audio-card", },
	{},
};
MODULE_DEVICE_TABLE(of, asoc_simple_of_match);

584 585
static struct platform_driver asoc_simple_card = {
	.driver = {
586
		.name = "asoc-simple-card",
587
		.of_match_table = asoc_simple_of_match,
588
	},
589
	.probe = asoc_simple_card_probe,
590
	.remove = asoc_simple_card_remove,
591 592 593 594
};

module_platform_driver(asoc_simple_card);

595
MODULE_ALIAS("platform:asoc-simple-card");
596 597 598
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ASoC Simple Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");