xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * DesignWare HDMI audio driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Written and tested against the Designware HDMI Tx found in iMX6.
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun #include <linux/io.h>
8*4882a593Smuzhiyun #include <linux/interrupt.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/platform_device.h>
11*4882a593Smuzhiyun #include <drm/bridge/dw_hdmi.h>
12*4882a593Smuzhiyun #include <drm/drm_edid.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <sound/asoundef.h>
15*4882a593Smuzhiyun #include <sound/core.h>
16*4882a593Smuzhiyun #include <sound/initval.h>
17*4882a593Smuzhiyun #include <sound/pcm.h>
18*4882a593Smuzhiyun #include <sound/pcm_drm_eld.h>
19*4882a593Smuzhiyun #include <sound/pcm_iec958.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "dw-hdmi-audio.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #define DRIVER_NAME "dw-hdmi-ahb-audio"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* Provide some bits rather than bit offsets */
26*4882a593Smuzhiyun enum {
27*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
28*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
29*4882a593Smuzhiyun 	HDMI_AHB_DMA_START_START = BIT(0),
30*4882a593Smuzhiyun 	HDMI_AHB_DMA_STOP_STOP = BIT(0),
31*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
32*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
33*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
34*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
35*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
36*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
37*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
38*4882a593Smuzhiyun 		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
39*4882a593Smuzhiyun 		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
40*4882a593Smuzhiyun 		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
41*4882a593Smuzhiyun 		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
42*4882a593Smuzhiyun 		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
43*4882a593Smuzhiyun 		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
44*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
45*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
46*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
47*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
48*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
49*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
50*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0_ALL =
51*4882a593Smuzhiyun 		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
52*4882a593Smuzhiyun 		HDMI_IH_AHBDMAAUD_STAT0_LOST |
53*4882a593Smuzhiyun 		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
54*4882a593Smuzhiyun 		HDMI_IH_AHBDMAAUD_STAT0_DONE |
55*4882a593Smuzhiyun 		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
56*4882a593Smuzhiyun 		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
57*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
58*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
59*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF0_INCR4 = 0,
60*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
61*4882a593Smuzhiyun 	HDMI_AHB_DMA_MASK_DONE = BIT(7),
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	HDMI_REVISION_ID = 0x0001,
64*4882a593Smuzhiyun 	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
65*4882a593Smuzhiyun 	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
66*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF0 = 0x3600,
67*4882a593Smuzhiyun 	HDMI_AHB_DMA_START = 0x3601,
68*4882a593Smuzhiyun 	HDMI_AHB_DMA_STOP = 0x3602,
69*4882a593Smuzhiyun 	HDMI_AHB_DMA_THRSLD = 0x3603,
70*4882a593Smuzhiyun 	HDMI_AHB_DMA_STRADDR0 = 0x3604,
71*4882a593Smuzhiyun 	HDMI_AHB_DMA_STPADDR0 = 0x3608,
72*4882a593Smuzhiyun 	HDMI_AHB_DMA_MASK = 0x3614,
73*4882a593Smuzhiyun 	HDMI_AHB_DMA_POL = 0x3615,
74*4882a593Smuzhiyun 	HDMI_AHB_DMA_CONF1 = 0x3616,
75*4882a593Smuzhiyun 	HDMI_AHB_DMA_BUFFPOL = 0x361a,
76*4882a593Smuzhiyun };
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun struct dw_hdmi_channel_conf {
79*4882a593Smuzhiyun 	u8 conf1;
80*4882a593Smuzhiyun 	u8 ca;
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun /*
84*4882a593Smuzhiyun  * The default mapping of ALSA channels to HDMI channels and speaker
85*4882a593Smuzhiyun  * allocation bits.  Note that we can't do channel remapping here -
86*4882a593Smuzhiyun  * channels must be in the same order.
87*4882a593Smuzhiyun  *
88*4882a593Smuzhiyun  * Mappings for alsa-lib pcm/surround*.conf files:
89*4882a593Smuzhiyun  *
90*4882a593Smuzhiyun  *		Front	Sur4.0	Sur4.1	Sur5.0	Sur5.1	Sur7.1
91*4882a593Smuzhiyun  * Channels	2	4	6	6	6	8
92*4882a593Smuzhiyun  *
93*4882a593Smuzhiyun  * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
94*4882a593Smuzhiyun  *
95*4882a593Smuzhiyun  *				Number of ALSA channels
96*4882a593Smuzhiyun  * ALSA Channel	2	3	4	5	6	7	8
97*4882a593Smuzhiyun  * 0		FL:0	=	=	=	=	=	=
98*4882a593Smuzhiyun  * 1		FR:1	=	=	=	=	=	=
99*4882a593Smuzhiyun  * 2			FC:3	RL:4	LFE:2	=	=	=
100*4882a593Smuzhiyun  * 3				RR:5	RL:4	FC:3	=	=
101*4882a593Smuzhiyun  * 4					RR:5	RL:4	=	=
102*4882a593Smuzhiyun  * 5						RR:5	=	=
103*4882a593Smuzhiyun  * 6							RC:6	=
104*4882a593Smuzhiyun  * 7							RLC/FRC	RLC/FRC
105*4882a593Smuzhiyun  */
106*4882a593Smuzhiyun static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
107*4882a593Smuzhiyun 	{ 0x03, 0x00 },	/* FL,FR */
108*4882a593Smuzhiyun 	{ 0x0b, 0x02 },	/* FL,FR,FC */
109*4882a593Smuzhiyun 	{ 0x33, 0x08 },	/* FL,FR,RL,RR */
110*4882a593Smuzhiyun 	{ 0x37, 0x09 },	/* FL,FR,LFE,RL,RR */
111*4882a593Smuzhiyun 	{ 0x3f, 0x0b },	/* FL,FR,LFE,FC,RL,RR */
112*4882a593Smuzhiyun 	{ 0x7f, 0x0f },	/* FL,FR,LFE,FC,RL,RR,RC */
113*4882a593Smuzhiyun 	{ 0xff, 0x13 },	/* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun struct snd_dw_hdmi {
117*4882a593Smuzhiyun 	struct snd_card *card;
118*4882a593Smuzhiyun 	struct snd_pcm *pcm;
119*4882a593Smuzhiyun 	spinlock_t lock;
120*4882a593Smuzhiyun 	struct dw_hdmi_audio_data data;
121*4882a593Smuzhiyun 	struct snd_pcm_substream *substream;
122*4882a593Smuzhiyun 	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
123*4882a593Smuzhiyun 	void *buf_src;
124*4882a593Smuzhiyun 	void *buf_dst;
125*4882a593Smuzhiyun 	dma_addr_t buf_addr;
126*4882a593Smuzhiyun 	unsigned buf_offset;
127*4882a593Smuzhiyun 	unsigned buf_period;
128*4882a593Smuzhiyun 	unsigned buf_size;
129*4882a593Smuzhiyun 	unsigned channels;
130*4882a593Smuzhiyun 	u8 revision;
131*4882a593Smuzhiyun 	u8 iec_offset;
132*4882a593Smuzhiyun 	u8 cs[192][8];
133*4882a593Smuzhiyun };
134*4882a593Smuzhiyun 
dw_hdmi_writel(u32 val,void __iomem * ptr)135*4882a593Smuzhiyun static void dw_hdmi_writel(u32 val, void __iomem *ptr)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	writeb_relaxed(val, ptr);
138*4882a593Smuzhiyun 	writeb_relaxed(val >> 8, ptr + 1);
139*4882a593Smuzhiyun 	writeb_relaxed(val >> 16, ptr + 2);
140*4882a593Smuzhiyun 	writeb_relaxed(val >> 24, ptr + 3);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun /*
144*4882a593Smuzhiyun  * Convert to hardware format: The userspace buffer contains IEC958 samples,
145*4882a593Smuzhiyun  * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
146*4882a593Smuzhiyun  * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
147*4882a593Smuzhiyun  * samples in 23..0.
148*4882a593Smuzhiyun  *
149*4882a593Smuzhiyun  * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
150*4882a593Smuzhiyun  *
151*4882a593Smuzhiyun  * Ideally, we could do with having the data properly formatted in userspace.
152*4882a593Smuzhiyun  */
dw_hdmi_reformat_iec958(struct snd_dw_hdmi * dw,size_t offset,size_t bytes)153*4882a593Smuzhiyun static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
154*4882a593Smuzhiyun 	size_t offset, size_t bytes)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	u32 *src = dw->buf_src + offset;
157*4882a593Smuzhiyun 	u32 *dst = dw->buf_dst + offset;
158*4882a593Smuzhiyun 	u32 *end = dw->buf_src + offset + bytes;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	do {
161*4882a593Smuzhiyun 		u32 b, sample = *src++;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 		b = (sample & 8) << (28 - 3);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 		sample >>= 4;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 		*dst++ = sample | b;
168*4882a593Smuzhiyun 	} while (src < end);
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun 
parity(u32 sample)171*4882a593Smuzhiyun static u32 parity(u32 sample)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	sample ^= sample >> 16;
174*4882a593Smuzhiyun 	sample ^= sample >> 8;
175*4882a593Smuzhiyun 	sample ^= sample >> 4;
176*4882a593Smuzhiyun 	sample ^= sample >> 2;
177*4882a593Smuzhiyun 	sample ^= sample >> 1;
178*4882a593Smuzhiyun 	return (sample & 1) << 27;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
dw_hdmi_reformat_s24(struct snd_dw_hdmi * dw,size_t offset,size_t bytes)181*4882a593Smuzhiyun static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
182*4882a593Smuzhiyun 	size_t offset, size_t bytes)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	u32 *src = dw->buf_src + offset;
185*4882a593Smuzhiyun 	u32 *dst = dw->buf_dst + offset;
186*4882a593Smuzhiyun 	u32 *end = dw->buf_src + offset + bytes;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	do {
189*4882a593Smuzhiyun 		unsigned i;
190*4882a593Smuzhiyun 		u8 *cs;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 		cs = dw->cs[dw->iec_offset++];
193*4882a593Smuzhiyun 		if (dw->iec_offset >= 192)
194*4882a593Smuzhiyun 			dw->iec_offset = 0;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 		i = dw->channels;
197*4882a593Smuzhiyun 		do {
198*4882a593Smuzhiyun 			u32 sample = *src++;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 			sample &= ~0xff000000;
201*4882a593Smuzhiyun 			sample |= *cs++ << 24;
202*4882a593Smuzhiyun 			sample |= parity(sample & ~0xf8000000);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 			*dst++ = sample;
205*4882a593Smuzhiyun 		} while (--i);
206*4882a593Smuzhiyun 	} while (src < end);
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
dw_hdmi_create_cs(struct snd_dw_hdmi * dw,struct snd_pcm_runtime * runtime)209*4882a593Smuzhiyun static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
210*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	u8 cs[4];
213*4882a593Smuzhiyun 	unsigned ch, i, j;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	memset(dw->cs, 0, sizeof(dw->cs));
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	for (ch = 0; ch < 8; ch++) {
220*4882a593Smuzhiyun 		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
221*4882a593Smuzhiyun 		cs[2] |= (ch + 1) << 4;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(cs); i++) {
224*4882a593Smuzhiyun 			unsigned c = cs[i];
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 			for (j = 0; j < 8; j++, c >>= 1)
227*4882a593Smuzhiyun 				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
228*4882a593Smuzhiyun 		}
229*4882a593Smuzhiyun 	}
230*4882a593Smuzhiyun 	dw->cs[0][0] |= BIT(4);
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun 
dw_hdmi_start_dma(struct snd_dw_hdmi * dw)233*4882a593Smuzhiyun static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	void __iomem *base = dw->data.base;
236*4882a593Smuzhiyun 	unsigned offset = dw->buf_offset;
237*4882a593Smuzhiyun 	unsigned period = dw->buf_period;
238*4882a593Smuzhiyun 	u32 start, stop;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	dw->reformat(dw, offset, period);
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	/* Clear all irqs before enabling irqs and starting DMA */
243*4882a593Smuzhiyun 	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
244*4882a593Smuzhiyun 		       base + HDMI_IH_AHBDMAAUD_STAT0);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	start = dw->buf_addr + offset;
247*4882a593Smuzhiyun 	stop = start + period - 1;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	/* Setup the hardware start/stop addresses */
250*4882a593Smuzhiyun 	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
251*4882a593Smuzhiyun 	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
254*4882a593Smuzhiyun 	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	offset += period;
257*4882a593Smuzhiyun 	if (offset >= dw->buf_size)
258*4882a593Smuzhiyun 		offset = 0;
259*4882a593Smuzhiyun 	dw->buf_offset = offset;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
dw_hdmi_stop_dma(struct snd_dw_hdmi * dw)262*4882a593Smuzhiyun static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	/* Disable interrupts before disabling DMA */
265*4882a593Smuzhiyun 	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
266*4882a593Smuzhiyun 	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
snd_dw_hdmi_irq(int irq,void * data)269*4882a593Smuzhiyun static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = data;
272*4882a593Smuzhiyun 	struct snd_pcm_substream *substream;
273*4882a593Smuzhiyun 	unsigned stat;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
276*4882a593Smuzhiyun 	if (!stat)
277*4882a593Smuzhiyun 		return IRQ_NONE;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	substream = dw->substream;
282*4882a593Smuzhiyun 	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
283*4882a593Smuzhiyun 		snd_pcm_period_elapsed(substream);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 		spin_lock(&dw->lock);
286*4882a593Smuzhiyun 		if (dw->substream)
287*4882a593Smuzhiyun 			dw_hdmi_start_dma(dw);
288*4882a593Smuzhiyun 		spin_unlock(&dw->lock);
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	return IRQ_HANDLED;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun static const struct snd_pcm_hardware dw_hdmi_hw = {
295*4882a593Smuzhiyun 	.info = SNDRV_PCM_INFO_INTERLEAVED |
296*4882a593Smuzhiyun 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
297*4882a593Smuzhiyun 		SNDRV_PCM_INFO_MMAP |
298*4882a593Smuzhiyun 		SNDRV_PCM_INFO_MMAP_VALID,
299*4882a593Smuzhiyun 	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
300*4882a593Smuzhiyun 		   SNDRV_PCM_FMTBIT_S24_LE,
301*4882a593Smuzhiyun 	.rates = SNDRV_PCM_RATE_32000 |
302*4882a593Smuzhiyun 		 SNDRV_PCM_RATE_44100 |
303*4882a593Smuzhiyun 		 SNDRV_PCM_RATE_48000 |
304*4882a593Smuzhiyun 		 SNDRV_PCM_RATE_88200 |
305*4882a593Smuzhiyun 		 SNDRV_PCM_RATE_96000 |
306*4882a593Smuzhiyun 		 SNDRV_PCM_RATE_176400 |
307*4882a593Smuzhiyun 		 SNDRV_PCM_RATE_192000,
308*4882a593Smuzhiyun 	.channels_min = 2,
309*4882a593Smuzhiyun 	.channels_max = 8,
310*4882a593Smuzhiyun 	.buffer_bytes_max = 1024 * 1024,
311*4882a593Smuzhiyun 	.period_bytes_min = 256,
312*4882a593Smuzhiyun 	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
313*4882a593Smuzhiyun 	.periods_min = 2,
314*4882a593Smuzhiyun 	.periods_max = 16,
315*4882a593Smuzhiyun 	.fifo_size = 0,
316*4882a593Smuzhiyun };
317*4882a593Smuzhiyun 
dw_hdmi_open(struct snd_pcm_substream * substream)318*4882a593Smuzhiyun static int dw_hdmi_open(struct snd_pcm_substream *substream)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
321*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = substream->private_data;
322*4882a593Smuzhiyun 	void __iomem *base = dw->data.base;
323*4882a593Smuzhiyun 	u8 *eld;
324*4882a593Smuzhiyun 	int ret;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	runtime->hw = dw_hdmi_hw;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	eld = dw->data.get_eld(dw->data.hdmi);
329*4882a593Smuzhiyun 	if (eld) {
330*4882a593Smuzhiyun 		ret = snd_pcm_hw_constraint_eld(runtime, eld);
331*4882a593Smuzhiyun 		if (ret < 0)
332*4882a593Smuzhiyun 			return ret;
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	ret = snd_pcm_limit_hw_rates(runtime);
336*4882a593Smuzhiyun 	if (ret < 0)
337*4882a593Smuzhiyun 		return ret;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	ret = snd_pcm_hw_constraint_integer(runtime,
340*4882a593Smuzhiyun 					    SNDRV_PCM_HW_PARAM_PERIODS);
341*4882a593Smuzhiyun 	if (ret < 0)
342*4882a593Smuzhiyun 		return ret;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	/* Limit the buffer size to the size of the preallocated buffer */
345*4882a593Smuzhiyun 	ret = snd_pcm_hw_constraint_minmax(runtime,
346*4882a593Smuzhiyun 					   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
347*4882a593Smuzhiyun 					   0, substream->dma_buffer.bytes);
348*4882a593Smuzhiyun 	if (ret < 0)
349*4882a593Smuzhiyun 		return ret;
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	/* Clear FIFO */
352*4882a593Smuzhiyun 	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
353*4882a593Smuzhiyun 		       base + HDMI_AHB_DMA_CONF0);
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	/* Configure interrupt polarities */
356*4882a593Smuzhiyun 	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
357*4882a593Smuzhiyun 	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	/* Keep interrupts masked, and clear any pending */
360*4882a593Smuzhiyun 	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
361*4882a593Smuzhiyun 	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
364*4882a593Smuzhiyun 			  "dw-hdmi-audio", dw);
365*4882a593Smuzhiyun 	if (ret)
366*4882a593Smuzhiyun 		return ret;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	/* Un-mute done interrupt */
369*4882a593Smuzhiyun 	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
370*4882a593Smuzhiyun 		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
371*4882a593Smuzhiyun 		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	return 0;
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun 
dw_hdmi_close(struct snd_pcm_substream * substream)376*4882a593Smuzhiyun static int dw_hdmi_close(struct snd_pcm_substream *substream)
377*4882a593Smuzhiyun {
378*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = substream->private_data;
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	/* Mute all interrupts */
381*4882a593Smuzhiyun 	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
382*4882a593Smuzhiyun 		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	free_irq(dw->data.irq, dw);
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	return 0;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun 
dw_hdmi_hw_free(struct snd_pcm_substream * substream)389*4882a593Smuzhiyun static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	return snd_pcm_lib_free_vmalloc_buffer(substream);
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun 
dw_hdmi_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)394*4882a593Smuzhiyun static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
395*4882a593Smuzhiyun 	struct snd_pcm_hw_params *params)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	/* Allocate the PCM runtime buffer, which is exposed to userspace. */
398*4882a593Smuzhiyun 	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
399*4882a593Smuzhiyun 						params_buffer_bytes(params));
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun 
dw_hdmi_prepare(struct snd_pcm_substream * substream)402*4882a593Smuzhiyun static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
405*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = substream->private_data;
406*4882a593Smuzhiyun 	u8 threshold, conf0, conf1, ca;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
409*4882a593Smuzhiyun 	switch (dw->revision) {
410*4882a593Smuzhiyun 	case 0x0a:
411*4882a593Smuzhiyun 		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
412*4882a593Smuzhiyun 			HDMI_AHB_DMA_CONF0_INCR4;
413*4882a593Smuzhiyun 		if (runtime->channels == 2)
414*4882a593Smuzhiyun 			threshold = 126;
415*4882a593Smuzhiyun 		else
416*4882a593Smuzhiyun 			threshold = 124;
417*4882a593Smuzhiyun 		break;
418*4882a593Smuzhiyun 	case 0x1a:
419*4882a593Smuzhiyun 		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
420*4882a593Smuzhiyun 			HDMI_AHB_DMA_CONF0_INCR8;
421*4882a593Smuzhiyun 		threshold = 128;
422*4882a593Smuzhiyun 		break;
423*4882a593Smuzhiyun 	default:
424*4882a593Smuzhiyun 		/* NOTREACHED */
425*4882a593Smuzhiyun 		return -EINVAL;
426*4882a593Smuzhiyun 	}
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	/* Minimum number of bytes in the fifo. */
431*4882a593Smuzhiyun 	runtime->hw.fifo_size = threshold * 32;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
434*4882a593Smuzhiyun 	conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
435*4882a593Smuzhiyun 	ca = default_hdmi_channel_config[runtime->channels - 2].ca;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
438*4882a593Smuzhiyun 	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
439*4882a593Smuzhiyun 	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	dw_hdmi_set_channel_count(dw->data.hdmi, runtime->channels);
442*4882a593Smuzhiyun 	dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	switch (runtime->format) {
445*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
446*4882a593Smuzhiyun 		dw->reformat = dw_hdmi_reformat_iec958;
447*4882a593Smuzhiyun 		break;
448*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S24_LE:
449*4882a593Smuzhiyun 		dw_hdmi_create_cs(dw, runtime);
450*4882a593Smuzhiyun 		dw->reformat = dw_hdmi_reformat_s24;
451*4882a593Smuzhiyun 		break;
452*4882a593Smuzhiyun 	}
453*4882a593Smuzhiyun 	dw->iec_offset = 0;
454*4882a593Smuzhiyun 	dw->channels = runtime->channels;
455*4882a593Smuzhiyun 	dw->buf_src  = runtime->dma_area;
456*4882a593Smuzhiyun 	dw->buf_dst  = substream->dma_buffer.area;
457*4882a593Smuzhiyun 	dw->buf_addr = substream->dma_buffer.addr;
458*4882a593Smuzhiyun 	dw->buf_period = snd_pcm_lib_period_bytes(substream);
459*4882a593Smuzhiyun 	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	return 0;
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun 
dw_hdmi_trigger(struct snd_pcm_substream * substream,int cmd)464*4882a593Smuzhiyun static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
465*4882a593Smuzhiyun {
466*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = substream->private_data;
467*4882a593Smuzhiyun 	unsigned long flags;
468*4882a593Smuzhiyun 	int ret = 0;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	switch (cmd) {
471*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
472*4882a593Smuzhiyun 		spin_lock_irqsave(&dw->lock, flags);
473*4882a593Smuzhiyun 		dw->buf_offset = 0;
474*4882a593Smuzhiyun 		dw->substream = substream;
475*4882a593Smuzhiyun 		dw_hdmi_start_dma(dw);
476*4882a593Smuzhiyun 		dw_hdmi_audio_enable(dw->data.hdmi);
477*4882a593Smuzhiyun 		spin_unlock_irqrestore(&dw->lock, flags);
478*4882a593Smuzhiyun 		substream->runtime->delay = substream->runtime->period_size;
479*4882a593Smuzhiyun 		break;
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
482*4882a593Smuzhiyun 		spin_lock_irqsave(&dw->lock, flags);
483*4882a593Smuzhiyun 		dw->substream = NULL;
484*4882a593Smuzhiyun 		dw_hdmi_stop_dma(dw);
485*4882a593Smuzhiyun 		dw_hdmi_audio_disable(dw->data.hdmi);
486*4882a593Smuzhiyun 		spin_unlock_irqrestore(&dw->lock, flags);
487*4882a593Smuzhiyun 		break;
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	default:
490*4882a593Smuzhiyun 		ret = -EINVAL;
491*4882a593Smuzhiyun 		break;
492*4882a593Smuzhiyun 	}
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	return ret;
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun 
dw_hdmi_pointer(struct snd_pcm_substream * substream)497*4882a593Smuzhiyun static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
500*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = substream->private_data;
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	/*
503*4882a593Smuzhiyun 	 * We are unable to report the exact hardware position as
504*4882a593Smuzhiyun 	 * reading the 32-bit DMA position using 8-bit reads is racy.
505*4882a593Smuzhiyun 	 */
506*4882a593Smuzhiyun 	return bytes_to_frames(runtime, dw->buf_offset);
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun static const struct snd_pcm_ops snd_dw_hdmi_ops = {
510*4882a593Smuzhiyun 	.open = dw_hdmi_open,
511*4882a593Smuzhiyun 	.close = dw_hdmi_close,
512*4882a593Smuzhiyun 	.ioctl = snd_pcm_lib_ioctl,
513*4882a593Smuzhiyun 	.hw_params = dw_hdmi_hw_params,
514*4882a593Smuzhiyun 	.hw_free = dw_hdmi_hw_free,
515*4882a593Smuzhiyun 	.prepare = dw_hdmi_prepare,
516*4882a593Smuzhiyun 	.trigger = dw_hdmi_trigger,
517*4882a593Smuzhiyun 	.pointer = dw_hdmi_pointer,
518*4882a593Smuzhiyun 	.page = snd_pcm_lib_get_vmalloc_page,
519*4882a593Smuzhiyun };
520*4882a593Smuzhiyun 
snd_dw_hdmi_probe(struct platform_device * pdev)521*4882a593Smuzhiyun static int snd_dw_hdmi_probe(struct platform_device *pdev)
522*4882a593Smuzhiyun {
523*4882a593Smuzhiyun 	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
524*4882a593Smuzhiyun 	struct device *dev = pdev->dev.parent;
525*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw;
526*4882a593Smuzhiyun 	struct snd_card *card;
527*4882a593Smuzhiyun 	struct snd_pcm *pcm;
528*4882a593Smuzhiyun 	unsigned revision;
529*4882a593Smuzhiyun 	int ret;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
532*4882a593Smuzhiyun 		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
533*4882a593Smuzhiyun 	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
534*4882a593Smuzhiyun 	if (revision != 0x0a && revision != 0x1a) {
535*4882a593Smuzhiyun 		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
536*4882a593Smuzhiyun 			revision);
537*4882a593Smuzhiyun 		return -ENXIO;
538*4882a593Smuzhiyun 	}
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
541*4882a593Smuzhiyun 			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
542*4882a593Smuzhiyun 	if (ret < 0)
543*4882a593Smuzhiyun 		return ret;
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
546*4882a593Smuzhiyun 	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
547*4882a593Smuzhiyun 	snprintf(card->longname, sizeof(card->longname),
548*4882a593Smuzhiyun 		 "%s rev 0x%02x, irq %d", card->shortname, revision,
549*4882a593Smuzhiyun 		 data->irq);
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	dw = card->private_data;
552*4882a593Smuzhiyun 	dw->card = card;
553*4882a593Smuzhiyun 	dw->data = *data;
554*4882a593Smuzhiyun 	dw->revision = revision;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	spin_lock_init(&dw->lock);
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
559*4882a593Smuzhiyun 	if (ret < 0)
560*4882a593Smuzhiyun 		goto err;
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	dw->pcm = pcm;
563*4882a593Smuzhiyun 	pcm->private_data = dw;
564*4882a593Smuzhiyun 	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
565*4882a593Smuzhiyun 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	/*
568*4882a593Smuzhiyun 	 * To support 8-channel 96kHz audio reliably, we need 512k
569*4882a593Smuzhiyun 	 * to satisfy alsa with our restricted period (ERR004323).
570*4882a593Smuzhiyun 	 */
571*4882a593Smuzhiyun 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
572*4882a593Smuzhiyun 			dev, 128 * 1024, 1024 * 1024);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	ret = snd_card_register(card);
575*4882a593Smuzhiyun 	if (ret < 0)
576*4882a593Smuzhiyun 		goto err;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	platform_set_drvdata(pdev, dw);
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	return 0;
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun err:
583*4882a593Smuzhiyun 	snd_card_free(card);
584*4882a593Smuzhiyun 	return ret;
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun 
snd_dw_hdmi_remove(struct platform_device * pdev)587*4882a593Smuzhiyun static int snd_dw_hdmi_remove(struct platform_device *pdev)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	snd_card_free(dw->card);
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	return 0;
594*4882a593Smuzhiyun }
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun #if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
597*4882a593Smuzhiyun /*
598*4882a593Smuzhiyun  * This code is fine, but requires implementation in the dw_hdmi_trigger()
599*4882a593Smuzhiyun  * method which is currently missing as I have no way to test this.
600*4882a593Smuzhiyun  */
snd_dw_hdmi_suspend(struct device * dev)601*4882a593Smuzhiyun static int snd_dw_hdmi_suspend(struct device *dev)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	return 0;
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun 
snd_dw_hdmi_resume(struct device * dev)610*4882a593Smuzhiyun static int snd_dw_hdmi_resume(struct device *dev)
611*4882a593Smuzhiyun {
612*4882a593Smuzhiyun 	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun 	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	return 0;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
620*4882a593Smuzhiyun 			 snd_dw_hdmi_resume);
621*4882a593Smuzhiyun #define PM_OPS &snd_dw_hdmi_pm
622*4882a593Smuzhiyun #else
623*4882a593Smuzhiyun #define PM_OPS NULL
624*4882a593Smuzhiyun #endif
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun static struct platform_driver snd_dw_hdmi_driver = {
627*4882a593Smuzhiyun 	.probe	= snd_dw_hdmi_probe,
628*4882a593Smuzhiyun 	.remove	= snd_dw_hdmi_remove,
629*4882a593Smuzhiyun 	.driver	= {
630*4882a593Smuzhiyun 		.name = DRIVER_NAME,
631*4882a593Smuzhiyun 		.pm = PM_OPS,
632*4882a593Smuzhiyun 	},
633*4882a593Smuzhiyun };
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun module_platform_driver(snd_dw_hdmi_driver);
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
638*4882a593Smuzhiyun MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
639*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
640*4882a593Smuzhiyun MODULE_ALIAS("platform:" DRIVER_NAME);
641