gen.c 6.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/*
 * Renesas R-Car Gen1 SRU/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.
 */
#include "rsnd.h"

struct rsnd_gen_ops {
	int (*path_init)(struct rsnd_priv *priv,
			 struct rsnd_dai *rdai,
			 struct rsnd_dai_stream *io);
	int (*path_exit)(struct rsnd_priv *priv,
			 struct rsnd_dai *rdai,
			 struct rsnd_dai_stream *io);
};

struct rsnd_gen_reg_map {
	int index;	/* -1 : not supported */
	u32 offset_id;	/* offset of ssi0, ssi1, ssi2... */
	u32 offset_adr;	/* offset of SSICR, SSISR, ... */
};

struct rsnd_gen {
	void __iomem *base[RSND_BASE_MAX];

	struct rsnd_gen_reg_map reg_map[RSND_REG_MAX];
	struct rsnd_gen_ops *ops;
};

#define rsnd_priv_to_gen(p)	((struct rsnd_gen *)(p)->gen)

#define rsnd_is_gen1(s)		((s)->info->flags & RSND_GEN1)
#define rsnd_is_gen2(s)		((s)->info->flags & RSND_GEN2)

/*
 *		Gen2
 *		will be filled in the future
 */

/*
 *		Gen1
 */
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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
static int rsnd_gen1_path_init(struct rsnd_priv *priv,
			       struct rsnd_dai *rdai,
			       struct rsnd_dai_stream *io)
{
	struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai);
	struct rsnd_mod *mod;
	int ret;
	int id;

	/*
	 * Gen1 is created by SRU/SSI, and this SRU is base module of
	 * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
	 *
	 * Easy image is..
	 *	Gen1 SRU = Gen2 SCU + SSIU + etc
	 *
	 * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
	 * using fixed path.
	 *
	 * Then, SSI id = SCU id here
	 */

	if (rsnd_dai_is_play(rdai, io))
		id = info->ssi_id_playback;
	else
		id = info->ssi_id_capture;

	/* SCU */
	mod = rsnd_scu_mod_get(priv, id);
	ret = rsnd_dai_connect(rdai, mod, io);

	return ret;
}

static int rsnd_gen1_path_exit(struct rsnd_priv *priv,
			       struct rsnd_dai *rdai,
			       struct rsnd_dai_stream *io)
{
	struct rsnd_mod *mod, *n;
	int ret = 0;

	/*
	 * remove all mod from rdai
	 */
	for_each_rsnd_mod(mod, n, io)
		ret |= rsnd_dai_disconnect(mod);

	return ret;
}

static struct rsnd_gen_ops rsnd_gen1_ops = {
	.path_init	= rsnd_gen1_path_init,
	.path_exit	= rsnd_gen1_path_exit,
};

#define RSND_GEN1_REG_MAP(g, s, i, oi, oa)				\
	do {								\
		(g)->reg_map[RSND_REG_##i].index  = RSND_GEN1_##s;	\
		(g)->reg_map[RSND_REG_##i].offset_id = oi;		\
		(g)->reg_map[RSND_REG_##i].offset_adr = oa;		\
	} while (0)

static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen)
{
	RSND_GEN1_REG_MAP(gen, SRU,	SSI_MODE0,	0x0,	0xD0);
	RSND_GEN1_REG_MAP(gen, SRU,	SSI_MODE1,	0x0,	0xD4);
114 115 116 117 118 119 120 121 122

	RSND_GEN1_REG_MAP(gen, ADG,	BRRA,		0x0,	0x00);
	RSND_GEN1_REG_MAP(gen, ADG,	BRRB,		0x0,	0x04);
	RSND_GEN1_REG_MAP(gen, ADG,	SSICKR,		0x0,	0x08);
	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL0,	0x0,	0x0c);
	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL1,	0x0,	0x10);
	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL3,	0x0,	0x18);
	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL4,	0x0,	0x1c);
	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL5,	0x0,	0x20);
123 124
}

125 126 127 128
static int rsnd_gen1_probe(struct platform_device *pdev,
			   struct rcar_snd_info *info,
			   struct rsnd_priv *priv)
{
129 130 131
	struct device *dev = rsnd_priv_to_dev(priv);
	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
	struct resource *sru_res;
132
	struct resource *adg_res;
133 134 135 136 137

	/*
	 * map address
	 */
	sru_res	= platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU);
138 139 140
	adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG);
	if (!sru_res ||
	    !adg_res) {
141 142 143 144 145 146 147
		dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n");
		return -ENODEV;
	}

	gen->ops = &rsnd_gen1_ops;

	gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res);
148 149 150
	gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res);
	if (!gen->base[RSND_GEN1_SRU] ||
	    !gen->base[RSND_GEN1_ADG]) {
151 152 153 154 155 156 157 158 159
		dev_err(dev, "SRU/SSI/ADG ioremap failed\n");
		return -ENODEV;
	}

	rsnd_gen1_reg_map_init(gen);

	dev_dbg(dev, "Gen1 device probed\n");
	dev_dbg(dev, "SRU : %08x => %p\n",	sru_res->start,
						gen->base[RSND_GEN1_SRU]);
160 161
	dev_dbg(dev, "ADG : %08x => %p\n",	adg_res->start,
						gen->base[RSND_GEN1_ADG]);
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 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 228 229 230 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
	return 0;
}

static void rsnd_gen1_remove(struct platform_device *pdev,
			     struct rsnd_priv *priv)
{
}

/*
 *		Gen
 */
int rsnd_gen_path_init(struct rsnd_priv *priv,
		       struct rsnd_dai *rdai,
		       struct rsnd_dai_stream *io)
{
	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);

	return gen->ops->path_init(priv, rdai, io);
}

int rsnd_gen_path_exit(struct rsnd_priv *priv,
		       struct rsnd_dai *rdai,
		       struct rsnd_dai_stream *io)
{
	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);

	return gen->ops->path_exit(priv, rdai, io);
}

void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
			       struct rsnd_mod *mod,
			       enum rsnd_reg reg)
{
	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
	struct device *dev = rsnd_priv_to_dev(priv);
	int index;
	u32 offset_id, offset_adr;

	if (reg >= RSND_REG_MAX) {
		dev_err(dev, "rsnd_reg reg error\n");
		return NULL;
	}

	index		= gen->reg_map[reg].index;
	offset_id	= gen->reg_map[reg].offset_id;
	offset_adr	= gen->reg_map[reg].offset_adr;

	if (index < 0) {
		dev_err(dev, "unsupported reg access %d\n", reg);
		return NULL;
	}

	if (offset_id && mod)
		offset_id *= rsnd_mod_id(mod);

	/*
	 * index/offset were set on gen1/gen2
	 */

	return gen->base[index] + offset_id + offset_adr;
}

int rsnd_gen_probe(struct platform_device *pdev,
		   struct rcar_snd_info *info,
		   struct rsnd_priv *priv)
{
	struct device *dev = rsnd_priv_to_dev(priv);
	struct rsnd_gen *gen;
	int i;

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

	priv->gen = gen;

	/*
	 * see
	 *	rsnd_reg_get()
	 *	rsnd_gen_probe()
	 */
	for (i = 0; i < RSND_REG_MAX; i++)
		gen->reg_map[i].index = -1;

	/*
	 *	init each module
	 */
	if (rsnd_is_gen1(priv))
		return rsnd_gen1_probe(pdev, info, priv);

	dev_err(dev, "unknown generation R-Car sound device\n");

	return -ENODEV;
}

void rsnd_gen_remove(struct platform_device *pdev,
		     struct rsnd_priv *priv)
{
	if (rsnd_is_gen1(priv))
		rsnd_gen1_remove(pdev, priv);
}