summaryrefslogtreecommitdiff
path: root/sound/soc/sh/rcar/rsnd.h
blob: d4093907dfd86f544d79f4b545cd41ef854ff288 (plain)
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
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
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
160
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
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
 * Renesas R-Car
 *
 * 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 RSND_H
#define RSND_H

#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/sh_dma.h>
#include <linux/workqueue.h>
#include <sound/rcar_snd.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>

/*
 *	pseudo register
 *
 * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different.
 * This driver uses pseudo register in order to hide it.
 * see gen1/gen2 for detail
 */
enum rsnd_reg {
	/* SRU/SCU/SSIU */
	RSND_REG_SRC_ROUTE_SEL,		/* for Gen1 */
	RSND_REG_SRC_TMG_SEL0,		/* for Gen1 */
	RSND_REG_SRC_TMG_SEL1,		/* for Gen1 */
	RSND_REG_SRC_TMG_SEL2,		/* for Gen1 */
	RSND_REG_SRC_ROUTE_CTRL,	/* for Gen1 */
	RSND_REG_SRC_CTRL,		/* for Gen2 */
	RSND_REG_SSI_CTRL,		/* for Gen2 */
	RSND_REG_SSI_CONTROL,
	RSND_REG_SSI_BUSIF_MODE,	/* for Gen2 */
	RSND_REG_SSI_BUSIF_ADINR,	/* for Gen2 */
	RSND_REG_SSI_MODE0,
	RSND_REG_SSI_MODE1,
	RSND_REG_INT_ENABLE,		/* for Gen2 */
	RSND_REG_SRC_BUSIF_MODE,
	RSND_REG_SRC_ROUTE_MODE0,
	RSND_REG_SRC_SWRSR,
	RSND_REG_SRC_SRCIR,
	RSND_REG_SRC_ADINR,
	RSND_REG_SRC_IFSCR,
	RSND_REG_SRC_IFSVR,
	RSND_REG_SRC_SRCCR,
	RSND_REG_SRC_MNFSR,		/* for Gen1 */
	RSND_REG_SRC_BSDSR,		/* for Gen2 */
	RSND_REG_SRC_BSISR,		/* for Gen2 */

	/* ADG */
	RSND_REG_BRRA,
	RSND_REG_BRRB,
	RSND_REG_SSICKR,
	RSND_REG_AUDIO_CLK_SEL0,
	RSND_REG_AUDIO_CLK_SEL1,
	RSND_REG_AUDIO_CLK_SEL2,
	RSND_REG_AUDIO_CLK_SEL3,	/* for Gen1 */
	RSND_REG_AUDIO_CLK_SEL4,	/* for Gen1 */
	RSND_REG_AUDIO_CLK_SEL5,	/* for Gen1 */
	RSND_REG_DIV_EN,		/* for Gen2 */
	RSND_REG_SRCIN_TIMSEL0,		/* for Gen2 */
	RSND_REG_SRCIN_TIMSEL1,		/* for Gen2 */
	RSND_REG_SRCIN_TIMSEL2,		/* for Gen2 */
	RSND_REG_SRCIN_TIMSEL3,		/* for Gen2 */
	RSND_REG_SRCIN_TIMSEL4,		/* for Gen2 */
	RSND_REG_SRCOUT_TIMSEL0,	/* for Gen2 */
	RSND_REG_SRCOUT_TIMSEL1,	/* for Gen2 */
	RSND_REG_SRCOUT_TIMSEL2,	/* for Gen2 */
	RSND_REG_SRCOUT_TIMSEL3,	/* for Gen2 */
	RSND_REG_SRCOUT_TIMSEL4,	/* for Gen2 */

	/* SSI */
	RSND_REG_SSICR,
	RSND_REG_SSISR,
	RSND_REG_SSITDR,
	RSND_REG_SSIRDR,
	RSND_REG_SSIWSR,

	RSND_REG_MAX,
};

struct rsnd_priv;
struct rsnd_mod;
struct rsnd_dai;
struct rsnd_dai_stream;

/*
 *	R-Car basic functions
 */
#define rsnd_mod_read(m, r) \
	rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
#define rsnd_mod_write(m, r, d) \
	rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
#define rsnd_mod_bset(m, r, s, d) \
	rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)

u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg);
void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
		enum rsnd_reg reg, u32 data);
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
		    u32 mask, u32 data);

/*
 *	R-Car DMA
 */
struct rsnd_dma {
	struct sh_dmae_slave	slave;
	struct work_struct	work;
	struct dma_chan		*chan;
	enum dma_data_direction dir;

	int submit_loop;
	int offset; /* it cares A/B plane */
};

void rsnd_dma_start(struct rsnd_dma *dma);
void rsnd_dma_stop(struct rsnd_dma *dma);
int rsnd_dma_available(struct rsnd_dma *dma);
int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
	int is_play, int id);
void  rsnd_dma_quit(struct rsnd_priv *priv,
		    struct rsnd_dma *dma);


/*
 *	R-Car sound mod
 */

struct rsnd_mod_ops {
	char *name;
	int (*init)(struct rsnd_mod *mod,
		    struct rsnd_dai *rdai,
		    struct rsnd_dai_stream *io);
	int (*quit)(struct rsnd_mod *mod,
		    struct rsnd_dai *rdai,
		    struct rsnd_dai_stream *io);
	int (*start)(struct rsnd_mod *mod,
		     struct rsnd_dai *rdai,
		     struct rsnd_dai_stream *io);
	int (*stop)(struct rsnd_mod *mod,
		    struct rsnd_dai *rdai,
		    struct rsnd_dai_stream *io);
};

struct rsnd_dai_stream;
struct rsnd_mod {
	int id;
	struct rsnd_priv *priv;
	struct rsnd_mod_ops *ops;
	struct list_head list; /* connect to rsnd_dai playback/capture */
	struct rsnd_dma dma;
	struct rsnd_dai_stream *io;
};

#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
#define rsnd_mod_to_io(mod) ((mod)->io)
#define rsnd_mod_id(mod) ((mod)->id)
#define for_each_rsnd_mod(pos, n, io)	\
	list_for_each_entry_safe(pos, n, &(io)->head, list)
#define __rsnd_mod_call(mod, func, rdai, io)			\
({								\
	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);		\
	struct device *dev = rsnd_priv_to_dev(priv);		\
	dev_dbg(dev, "%s-%d-%s\n",				\
		rsnd_mod_name(mod), rsnd_mod_id(mod), #func);	\
	(mod)->ops->func(mod, rdai, io);			\
})

#define rsnd_mod_call(mod, func, rdai, io)	\
	(!(mod) ? -ENODEV :			\
	 !((mod)->ops->func) ? 0 :		\
	 __rsnd_mod_call(mod, func, rdai, io))

void rsnd_mod_init(struct rsnd_priv *priv,
		   struct rsnd_mod *mod,
		   struct rsnd_mod_ops *ops,
		   int id);
char *rsnd_mod_name(struct rsnd_mod *mod);

/*
 *	R-Car sound DAI
 */
#define RSND_DAI_NAME_SIZE	16
struct rsnd_dai_stream {
	struct list_head head; /* head of rsnd_mod list */
	struct snd_pcm_substream *substream;
	int byte_pos;
	int period_pos;
	int byte_per_period;
	int next_period_byte;
};

struct rsnd_dai {
	char name[RSND_DAI_NAME_SIZE];
	struct rsnd_dai_platform_info *info; /* rcar_snd.h */
	struct rsnd_dai_stream playback;
	struct rsnd_dai_stream capture;

	unsigned int clk_master:1;
	unsigned int bit_clk_inv:1;
	unsigned int frm_clk_inv:1;
	unsigned int sys_delay:1;
	unsigned int data_alignment:1;
};

#define rsnd_dai_nr(priv) ((priv)->dai_nr)
#define for_each_rsnd_dai(rdai, priv, i)		\
	for (i = 0;					\
	     (i < rsnd_dai_nr(priv)) &&			\
	     ((rdai) = rsnd_dai_get(priv, i));		\
	     i++)

struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
int rsnd_dai_disconnect(struct rsnd_mod *mod);
int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod,
		     struct rsnd_dai_stream *io);
int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
#define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
#define rsnd_io_to_runtime(io) ((io)->substream->runtime)

void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
#define rsnd_dai_is_clk_master(rdai) ((rdai)->clk_master)

/*
 *	R-Car Gen1/Gen2
 */
int rsnd_gen_probe(struct platform_device *pdev,
		   struct rcar_snd_info *info,
		   struct rsnd_priv *priv);
void rsnd_gen_remove(struct platform_device *pdev,
		     struct rsnd_priv *priv);
int rsnd_gen_path_init(struct rsnd_priv *priv,
		       struct rsnd_dai *rdai,
		       struct rsnd_dai_stream *io);
int rsnd_gen_path_exit(struct rsnd_priv *priv,
		       struct rsnd_dai *rdai,
		       struct rsnd_dai_stream *io);
void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
			       struct rsnd_mod *mod,
			       enum rsnd_reg reg);
#define rsnd_is_gen1(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
#define rsnd_is_gen2(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)

/*
 *	R-Car ADG
 */
int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
int rsnd_adg_probe(struct platform_device *pdev,
		   struct rcar_snd_info *info,
		   struct rsnd_priv *priv);
void rsnd_adg_remove(struct platform_device *pdev,
		   struct rsnd_priv *priv);
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
				  struct rsnd_mod *mod,
				  unsigned int src_rate,
				  unsigned int dst_rate);
int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
				  struct rsnd_dai *rdai,
				  struct rsnd_dai_stream *io,
				  unsigned int src_rate,
				  unsigned int dst_rate);
int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
				     struct rsnd_dai *rdai,
				     struct rsnd_dai_stream *io);

/*
 *	R-Car sound priv
 */
struct rsnd_priv {

	struct device *dev;
	struct rcar_snd_info *info;
	spinlock_t lock;

	/*
	 * below value will be filled on rsnd_gen_probe()
	 */
	void *gen;

	/*
	 * below value will be filled on rsnd_scu_probe()
	 */
	void *scu;
	int scu_nr;

	/*
	 * below value will be filled on rsnd_adg_probe()
	 */
	void *adg;

	/*
	 * below value will be filled on rsnd_ssi_probe()
	 */
	void *ssi;
	int ssi_nr;

	/*
	 * below value will be filled on rsnd_dai_probe()
	 */
	struct snd_soc_dai_driver *daidrv;
	struct rsnd_dai *rdai;
	int dai_nr;
};

#define rsnd_priv_to_dev(priv)	((priv)->dev)
#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)

/*
 *	R-Car SCU
 */
int rsnd_scu_probe(struct platform_device *pdev,
		   struct rcar_snd_info *info,
		   struct rsnd_priv *priv);
void rsnd_scu_remove(struct platform_device *pdev,
		     struct rsnd_priv *priv);
struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id);
unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv,
				   struct rsnd_mod *ssi_mod,
				   struct snd_pcm_runtime *runtime);

#define rsnd_scu_nr(priv) ((priv)->scu_nr)

/*
 *	R-Car SSI
 */
int rsnd_ssi_probe(struct platform_device *pdev,
		   struct rcar_snd_info *info,
		   struct rsnd_priv *priv);
void rsnd_ssi_remove(struct platform_device *pdev,
		   struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
					  int dai_id, int is_play);
int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
int rsnd_ssi_is_play(struct rsnd_mod *mod);

#endif