Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b008387a authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branch 'asoc/topic/rcar' into asoc-next

parents 5388d480 2460719c
Loading
Loading
Loading
Loading
+84 −0
Original line number Original line Diff line number Diff line
/*
 * Renesas R-Car SRU/SCU/SSIU/SSI support
 *
 * Copyright (C) 2013 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.
 */

#ifndef RCAR_SND_H
#define RCAR_SND_H

#include <linux/sh_clk.h>

#define RSND_GEN1_SRU	0
#define RSND_GEN1_ADG	1
#define RSND_GEN1_SSI	2

#define RSND_GEN2_SRU	0
#define RSND_GEN2_ADG	1
#define RSND_GEN2_SSIU	2
#define RSND_GEN2_SSI	3

#define RSND_BASE_MAX	4

/*
 * flags
 *
 * 0xAB000000
 *
 * A : clock sharing settings
 * B : SSI direction
 */
#define RSND_SSI_CLK_PIN_SHARE		(1 << 31)
#define RSND_SSI_CLK_FROM_ADG		(1 << 30) /* clock parent is master */
#define RSND_SSI_SYNC			(1 << 29) /* SSI34_sync etc */
#define RSND_SSI_DEPENDENT		(1 << 28) /* SSI needs SRU/SCU */

#define RSND_SSI_PLAY			(1 << 24)

#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags)	\
{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
#define RSND_SSI_UNUSED \
{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 }

struct rsnd_ssi_platform_info {
	int dai_id;
	int dma_id;
	int pio_irq;
	u32 flags;
};

/*
 * flags
 */
#define RSND_SCU_USB_HPBIF		(1 << 31) /* it needs RSND_SSI_DEPENDENT */

struct rsnd_scu_platform_info {
	u32 flags;
};

/*
 * flags
 *
 * 0x0000000A
 *
 * A : generation
 */
#define RSND_GEN1	(1 << 0) /* fixme */
#define RSND_GEN2	(2 << 0) /* fixme */

struct rcar_snd_info {
	u32 flags;
	struct rsnd_ssi_platform_info *ssi_info;
	int ssi_info_nr;
	struct rsnd_scu_platform_info *scu_info;
	int scu_info_nr;
	int (*start)(int id);
	int (*stop)(int id);
};

#endif
+7 −0
Original line number Original line Diff line number Diff line
@@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU
	select SH_DMAE
	select SH_DMAE
	select FW_LOADER
	select FW_LOADER


config SND_SOC_RCAR
	tristate "R-Car series SRU/SCU/SSIU/SSI support"
	select SND_SIMPLE_CARD
	select RCAR_CLK_ADG
	help
	  This option enables R-Car SUR/SCU/SSIU/SSI sound support

##
##
## Boards
## Boards
##
##
+3 −0
Original line number Original line Diff line number Diff line
@@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o
obj-$(CONFIG_SND_SOC_SH4_FSI)	+= snd-soc-fsi.o
obj-$(CONFIG_SND_SOC_SH4_FSI)	+= snd-soc-fsi.o
obj-$(CONFIG_SND_SOC_SH4_SIU)	+= snd-soc-siu.o
obj-$(CONFIG_SND_SOC_SH4_SIU)	+= snd-soc-siu.o


## audio units for R-Car
obj-$(CONFIG_SND_SOC_RCAR)	+= rcar/

## boards
## boards
snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
snd-soc-migor-objs		:= migor.o
snd-soc-migor-objs		:= migor.o
+2 −0
Original line number Original line Diff line number Diff line
snd-soc-rcar-objs	:= core.o gen.o scu.o adg.o ssi.o
obj-$(CONFIG_SND_SOC_RCAR)	+= snd-soc-rcar.o
 No newline at end of file
+234 −0
Original line number Original line Diff line number Diff line
/*
 * Helper routines for R-Car sound ADG.
 *
 *  Copyright (C) 2013  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/sh_clk.h>
#include <mach/clock.h>
#include "rsnd.h"

#define CLKA	0
#define CLKB	1
#define CLKC	2
#define CLKI	3
#define CLKMAX	4

struct rsnd_adg {
	struct clk *clk[CLKMAX];

	int rate_of_441khz_div_6;
	int rate_of_48khz_div_6;
};

#define for_each_rsnd_clk(pos, adg, i)		\
	for (i = 0, (pos) = adg->clk[i];	\
	     i < CLKMAX;			\
	     i++, (pos) = adg->clk[i])
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)

static enum rsnd_reg rsnd_adg_ssi_reg_get(int id)
{
	enum rsnd_reg reg;

	/*
	 * SSI 8 is not connected to ADG.
	 * it works with SSI 7
	 */
	if (id == 8)
		return RSND_REG_MAX;

	if (0 <= id && id <= 3)
		reg = RSND_REG_AUDIO_CLK_SEL0;
	else if (4 <= id && id <= 7)
		reg = RSND_REG_AUDIO_CLK_SEL1;
	else
		reg = RSND_REG_AUDIO_CLK_SEL2;

	return reg;
}

int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)
{
	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
	enum rsnd_reg reg;
	int id;

	/*
	 * "mod" = "ssi" here.
	 * we can get "ssi id" from mod
	 */
	id  = rsnd_mod_id(mod);
	reg = rsnd_adg_ssi_reg_get(id);

	rsnd_write(priv, mod, reg, 0);

	return 0;
}

int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
{
	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
	struct device *dev = rsnd_priv_to_dev(priv);
	struct clk *clk;
	enum rsnd_reg reg;
	int id, shift, i;
	u32 data;
	int sel_table[] = {
		[CLKA] = 0x1,
		[CLKB] = 0x2,
		[CLKC] = 0x3,
		[CLKI] = 0x0,
	};

	dev_dbg(dev, "request clock = %d\n", rate);

	/*
	 * find suitable clock from
	 * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
	 */
	data = 0;
	for_each_rsnd_clk(clk, adg, i) {
		if (rate == clk_get_rate(clk)) {
			data = sel_table[i];
			goto found_clock;
		}
	}

	/*
	 * find 1/6 clock from BRGA/BRGB
	 */
	if (rate == adg->rate_of_441khz_div_6) {
		data = 0x10;
		goto found_clock;
	}

	if (rate == adg->rate_of_48khz_div_6) {
		data = 0x20;
		goto found_clock;
	}

	return -EIO;

found_clock:

	/*
	 * This "mod" = "ssi" here.
	 * we can get "ssi id" from mod
	 */
	id  = rsnd_mod_id(mod);
	reg = rsnd_adg_ssi_reg_get(id);

	dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate);

	/*
	 * Enable SSIx clock
	 */
	shift = (id % 4) * 8;

	rsnd_bset(priv, mod, reg,
		   0xFF << shift,
		   data << shift);

	return 0;
}

static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
{
	struct clk *clk;
	unsigned long rate;
	u32 ckr;
	int i;
	int brg_table[] = {
		[CLKA] = 0x0,
		[CLKB] = 0x1,
		[CLKC] = 0x4,
		[CLKI] = 0x2,
	};

	/*
	 * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
	 * have 44.1kHz or 48kHz base clocks for now.
	 *
	 * SSI itself can divide parent clock by 1/1 - 1/16
	 * So,  BRGA outputs 44.1kHz base parent clock 1/32,
	 * and, BRGB outputs 48.0kHz base parent clock 1/32 here.
	 * see
	 *	rsnd_adg_ssi_clk_try_start()
	 */
	ckr = 0;
	adg->rate_of_441khz_div_6 = 0;
	adg->rate_of_48khz_div_6  = 0;
	for_each_rsnd_clk(clk, adg, i) {
		rate = clk_get_rate(clk);

		if (0 == rate) /* not used */
			continue;

		/* RBGA */
		if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) {
			adg->rate_of_441khz_div_6 = rate / 6;
			ckr |= brg_table[i] << 20;
		}

		/* RBGB */
		if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) {
			adg->rate_of_48khz_div_6 = rate / 6;
			ckr |= brg_table[i] << 16;
		}
	}

	rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr);
	rsnd_priv_write(priv, BRRA,  0x00000002); /* 1/6 */
	rsnd_priv_write(priv, BRRB,  0x00000002); /* 1/6 */
}

int rsnd_adg_probe(struct platform_device *pdev,
		   struct rcar_snd_info *info,
		   struct rsnd_priv *priv)
{
	struct rsnd_adg *adg;
	struct device *dev = rsnd_priv_to_dev(priv);
	struct clk *clk;
	int i;

	adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
	if (!adg) {
		dev_err(dev, "ADG allocate failed\n");
		return -ENOMEM;
	}

	adg->clk[CLKA] = clk_get(NULL, "audio_clk_a");
	adg->clk[CLKB] = clk_get(NULL, "audio_clk_b");
	adg->clk[CLKC] = clk_get(NULL, "audio_clk_c");
	adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal");
	for_each_rsnd_clk(clk, adg, i) {
		if (IS_ERR(clk)) {
			dev_err(dev, "Audio clock failed\n");
			return -EIO;
		}
	}

	rsnd_adg_ssi_clk_init(priv, adg);

	priv->adg = adg;

	dev_dbg(dev, "adg probed\n");

	return 0;
}

void rsnd_adg_remove(struct platform_device *pdev,
		     struct rsnd_priv *priv)
{
	struct rsnd_adg *adg = priv->adg;
	struct clk *clk;
	int i;

	for_each_rsnd_clk(clk, adg, i)
		clk_put(clk);
}
Loading