1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2005-2006 Micronas USA Inc.
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/moduleparam.h>
9 #include <linux/spinlock.h>
10 #include <linux/delay.h>
11 #include <linux/sched.h>
12 #include <linux/time.h>
14 #include <linux/i2c.h>
15 #include <linux/mutex.h>
16 #include <linux/uaccess.h>
17 #include <linux/slab.h>
18 #include <sound/core.h>
19 #include <sound/pcm.h>
20 #include <sound/initval.h>
22 #include "go7007-priv.h"
24 static int index
[SNDRV_CARDS
] = SNDRV_DEFAULT_IDX
;
25 static char *id
[SNDRV_CARDS
] = SNDRV_DEFAULT_STR
;
26 static bool enable
[SNDRV_CARDS
] = SNDRV_DEFAULT_ENABLE_PNP
;
28 module_param_array(index
, int, NULL
, 0444);
29 module_param_array(id
, charp
, NULL
, 0444);
30 module_param_array(enable
, bool, NULL
, 0444);
31 MODULE_PARM_DESC(index
, "Index value for the go7007 audio driver");
32 MODULE_PARM_DESC(id
, "ID string for the go7007 audio driver");
33 MODULE_PARM_DESC(enable
, "Enable for the go7007 audio driver");
36 struct snd_card
*card
;
38 struct snd_pcm_substream
*substream
;
46 static const struct snd_pcm_hardware go7007_snd_capture_hw
= {
47 .info
= (SNDRV_PCM_INFO_MMAP
|
48 SNDRV_PCM_INFO_INTERLEAVED
|
49 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
50 SNDRV_PCM_INFO_MMAP_VALID
),
51 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
52 .rates
= SNDRV_PCM_RATE_48000
,
57 .buffer_bytes_max
= (128*1024),
58 .period_bytes_min
= 4096,
59 .period_bytes_max
= (128*1024),
64 static void parse_audio_stream_data(struct go7007
*go
, u8
*buf
, int length
)
66 struct go7007_snd
*gosnd
= go
->snd_context
;
67 struct snd_pcm_runtime
*runtime
= gosnd
->substream
->runtime
;
68 int frames
= bytes_to_frames(runtime
, length
);
71 spin_lock_irqsave(&gosnd
->lock
, flags
);
72 gosnd
->hw_ptr
+= frames
;
73 if (gosnd
->hw_ptr
>= runtime
->buffer_size
)
74 gosnd
->hw_ptr
-= runtime
->buffer_size
;
75 gosnd
->avail
+= frames
;
76 spin_unlock_irqrestore(&gosnd
->lock
, flags
);
77 if (gosnd
->w_idx
+ length
> runtime
->dma_bytes
) {
78 int cpy
= runtime
->dma_bytes
- gosnd
->w_idx
;
80 memcpy(runtime
->dma_area
+ gosnd
->w_idx
, buf
, cpy
);
85 memcpy(runtime
->dma_area
+ gosnd
->w_idx
, buf
, length
);
86 gosnd
->w_idx
+= length
;
87 spin_lock_irqsave(&gosnd
->lock
, flags
);
88 if (gosnd
->avail
< runtime
->period_size
) {
89 spin_unlock_irqrestore(&gosnd
->lock
, flags
);
92 gosnd
->avail
-= runtime
->period_size
;
93 spin_unlock_irqrestore(&gosnd
->lock
, flags
);
95 snd_pcm_period_elapsed(gosnd
->substream
);
98 static int go7007_snd_hw_params(struct snd_pcm_substream
*substream
,
99 struct snd_pcm_hw_params
*hw_params
)
101 struct go7007
*go
= snd_pcm_substream_chip(substream
);
103 go
->audio_deliver
= parse_audio_stream_data
;
107 static int go7007_snd_hw_free(struct snd_pcm_substream
*substream
)
109 struct go7007
*go
= snd_pcm_substream_chip(substream
);
111 go
->audio_deliver
= NULL
;
115 static int go7007_snd_capture_open(struct snd_pcm_substream
*substream
)
117 struct go7007
*go
= snd_pcm_substream_chip(substream
);
118 struct go7007_snd
*gosnd
= go
->snd_context
;
122 spin_lock_irqsave(&gosnd
->lock
, flags
);
123 if (gosnd
->substream
== NULL
) {
124 gosnd
->substream
= substream
;
125 substream
->runtime
->hw
= go7007_snd_capture_hw
;
129 spin_unlock_irqrestore(&gosnd
->lock
, flags
);
133 static int go7007_snd_capture_close(struct snd_pcm_substream
*substream
)
135 struct go7007
*go
= snd_pcm_substream_chip(substream
);
136 struct go7007_snd
*gosnd
= go
->snd_context
;
138 gosnd
->substream
= NULL
;
142 static int go7007_snd_pcm_prepare(struct snd_pcm_substream
*substream
)
147 static int go7007_snd_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
149 struct go7007
*go
= snd_pcm_substream_chip(substream
);
150 struct go7007_snd
*gosnd
= go
->snd_context
;
153 case SNDRV_PCM_TRIGGER_START
:
154 /* Just set a flag to indicate we should signal ALSA when
156 gosnd
->capturing
= 1;
158 case SNDRV_PCM_TRIGGER_STOP
:
159 gosnd
->hw_ptr
= gosnd
->w_idx
= gosnd
->avail
= 0;
160 gosnd
->capturing
= 0;
167 static snd_pcm_uframes_t
go7007_snd_pcm_pointer(struct snd_pcm_substream
*substream
)
169 struct go7007
*go
= snd_pcm_substream_chip(substream
);
170 struct go7007_snd
*gosnd
= go
->snd_context
;
172 return gosnd
->hw_ptr
;
175 static const struct snd_pcm_ops go7007_snd_capture_ops
= {
176 .open
= go7007_snd_capture_open
,
177 .close
= go7007_snd_capture_close
,
178 .hw_params
= go7007_snd_hw_params
,
179 .hw_free
= go7007_snd_hw_free
,
180 .prepare
= go7007_snd_pcm_prepare
,
181 .trigger
= go7007_snd_pcm_trigger
,
182 .pointer
= go7007_snd_pcm_pointer
,
185 static int go7007_snd_free(struct snd_device
*device
)
187 struct go7007
*go
= device
->device_data
;
189 kfree(go
->snd_context
);
190 go
->snd_context
= NULL
;
194 static struct snd_device_ops go7007_snd_device_ops
= {
195 .dev_free
= go7007_snd_free
,
198 int go7007_snd_init(struct go7007
*go
)
201 struct go7007_snd
*gosnd
;
204 if (dev
>= SNDRV_CARDS
)
210 gosnd
= kmalloc(sizeof(struct go7007_snd
), GFP_KERNEL
);
213 spin_lock_init(&gosnd
->lock
);
214 gosnd
->hw_ptr
= gosnd
->w_idx
= gosnd
->avail
= 0;
215 gosnd
->capturing
= 0;
216 ret
= snd_card_new(go
->dev
, index
[dev
], id
[dev
], THIS_MODULE
, 0,
221 ret
= snd_device_new(gosnd
->card
, SNDRV_DEV_LOWLEVEL
, go
,
222 &go7007_snd_device_ops
);
226 ret
= snd_pcm_new(gosnd
->card
, "go7007", 0, 0, 1, &gosnd
->pcm
);
230 strscpy(gosnd
->card
->driver
, "go7007", sizeof(gosnd
->card
->driver
));
231 strscpy(gosnd
->card
->shortname
, go
->name
, sizeof(gosnd
->card
->shortname
));
232 strscpy(gosnd
->card
->longname
, gosnd
->card
->shortname
,
233 sizeof(gosnd
->card
->longname
));
235 gosnd
->pcm
->private_data
= go
;
236 snd_pcm_set_ops(gosnd
->pcm
, SNDRV_PCM_STREAM_CAPTURE
,
237 &go7007_snd_capture_ops
);
238 snd_pcm_set_managed_buffer_all(gosnd
->pcm
, SNDRV_DMA_TYPE_VMALLOC
,
241 ret
= snd_card_register(gosnd
->card
);
245 gosnd
->substream
= NULL
;
246 go
->snd_context
= gosnd
;
247 v4l2_device_get(&go
->v4l2_dev
);
253 snd_card_free(gosnd
->card
);
258 EXPORT_SYMBOL(go7007_snd_init
);
260 int go7007_snd_remove(struct go7007
*go
)
262 struct go7007_snd
*gosnd
= go
->snd_context
;
264 snd_card_disconnect(gosnd
->card
);
265 snd_card_free_when_closed(gosnd
->card
);
266 v4l2_device_put(&go
->v4l2_dev
);
269 EXPORT_SYMBOL(go7007_snd_remove
);
271 MODULE_LICENSE("GPL v2");