]>
Commit | Line | Data |
---|---|---|
1c1e45d1 HV |
1 | /* |
2 | * cx18 functions for DVB support | |
3 | * | |
6d897616 | 4 | * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> |
1c1e45d1 HV |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | */ | |
21 | ||
22 | #include "cx18-version.h" | |
23 | #include "cx18-dvb.h" | |
b1526421 | 24 | #include "cx18-io.h" |
1c1e45d1 | 25 | #include "cx18-streams.h" |
1d6782bd AW |
26 | #include "cx18-queue.h" |
27 | #include "cx18-scb.h" | |
1c1e45d1 HV |
28 | #include "cx18-cards.h" |
29 | #include "s5h1409.h" | |
67129471 | 30 | #include "mxl5005s.h" |
1c1e45d1 HV |
31 | |
32 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | |
33 | ||
34 | #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 | |
35 | ||
67129471 ST |
36 | static struct mxl5005s_config hauppauge_hvr1600_tuner = { |
37 | .i2c_address = 0xC6 >> 1, | |
38 | .if_freq = IF_FREQ_5380000HZ, | |
39 | .xtal_freq = CRYSTAL_FREQ_16000000HZ, | |
40 | .agc_mode = MXL_SINGLE_AGC, | |
41 | .tracking_filter = MXL_TF_C_H, | |
42 | .rssi_enable = MXL_RSSI_ENABLE, | |
43 | .cap_select = MXL_CAP_SEL_ENABLE, | |
44 | .div_out = MXL_DIV_OUT_4, | |
45 | .clock_out = MXL_CLOCK_OUT_DISABLE, | |
46 | .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, | |
47 | .top = MXL5005S_TOP_25P2, | |
48 | .mod_mode = MXL_DIGITAL_MODE, | |
49 | .if_mode = MXL_ZERO_IF, | |
50 | .AgcMasterByte = 0x00, | |
1c1e45d1 HV |
51 | }; |
52 | ||
53 | static struct s5h1409_config hauppauge_hvr1600_config = { | |
54 | .demod_address = 0x32 >> 1, | |
55 | .output_mode = S5H1409_SERIAL_OUTPUT, | |
56 | .gpio = S5H1409_GPIO_ON, | |
57 | .qam_if = 44000, | |
58 | .inversion = S5H1409_INVERSION_OFF, | |
59 | .status_mode = S5H1409_DEMODLOCKING, | |
60 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK | |
61 | ||
62 | }; | |
1c1e45d1 HV |
63 | |
64 | static int dvb_register(struct cx18_stream *stream); | |
65 | ||
66 | /* Kernel DVB framework calls this when the feed needs to start. | |
67 | * The CX18 framework should enable the transport DMA handling | |
68 | * and queue processing. | |
69 | */ | |
70 | static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) | |
71 | { | |
72 | struct dvb_demux *demux = feed->demux; | |
73 | struct cx18_stream *stream = (struct cx18_stream *) demux->priv; | |
74 | struct cx18 *cx = stream->cx; | |
08cf7b2e | 75 | int ret; |
1c1e45d1 HV |
76 | u32 v; |
77 | ||
78 | CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", | |
79 | feed->pid, feed->index); | |
08cf7b2e AW |
80 | |
81 | mutex_lock(&cx->serialize_lock); | |
82 | ret = cx18_init_on_first_open(cx); | |
83 | mutex_unlock(&cx->serialize_lock); | |
84 | if (ret) { | |
85 | CX18_ERR("Failed to initialize firmware starting DVB feed\n"); | |
86 | return ret; | |
87 | } | |
88 | ret = -EINVAL; | |
89 | ||
1c1e45d1 HV |
90 | switch (cx->card->type) { |
91 | case CX18_CARD_HVR_1600_ESMT: | |
92 | case CX18_CARD_HVR_1600_SAMSUNG: | |
b1526421 | 93 | v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL); |
1c1e45d1 HV |
94 | v |= 0x00400000; /* Serial Mode */ |
95 | v |= 0x00002000; /* Data Length - Byte */ | |
96 | v |= 0x00010000; /* Error - Polarity */ | |
97 | v |= 0x00020000; /* Error - Passthru */ | |
98 | v |= 0x000c0000; /* Error - Ignore */ | |
b1526421 | 99 | cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); |
1c1e45d1 HV |
100 | break; |
101 | ||
102 | default: | |
103 | /* Assumption - Parallel transport - Signalling | |
104 | * undefined or default. | |
105 | */ | |
106 | break; | |
107 | } | |
108 | ||
109 | if (!demux->dmx.frontend) | |
110 | return -EINVAL; | |
111 | ||
112 | if (stream) { | |
113 | mutex_lock(&stream->dvb.feedlock); | |
114 | if (stream->dvb.feeding++ == 0) { | |
115 | CX18_DEBUG_INFO("Starting Transport DMA\n"); | |
116 | ret = cx18_start_v4l2_encode_stream(stream); | |
08cf7b2e AW |
117 | if (ret < 0) { |
118 | CX18_DEBUG_INFO( | |
119 | "Failed to start Transport DMA\n"); | |
120 | stream->dvb.feeding--; | |
121 | } | |
1c1e45d1 HV |
122 | } else |
123 | ret = 0; | |
124 | mutex_unlock(&stream->dvb.feedlock); | |
125 | } | |
126 | ||
127 | return ret; | |
128 | } | |
129 | ||
130 | /* Kernel DVB framework calls this when the feed needs to stop. */ | |
131 | static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) | |
132 | { | |
133 | struct dvb_demux *demux = feed->demux; | |
134 | struct cx18_stream *stream = (struct cx18_stream *)demux->priv; | |
135 | struct cx18 *cx = stream->cx; | |
136 | int ret = -EINVAL; | |
137 | ||
138 | CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", | |
139 | feed->pid, feed->index); | |
140 | ||
141 | if (stream) { | |
142 | mutex_lock(&stream->dvb.feedlock); | |
143 | if (--stream->dvb.feeding == 0) { | |
144 | CX18_DEBUG_INFO("Stopping Transport DMA\n"); | |
145 | ret = cx18_stop_v4l2_encode_stream(stream, 0); | |
146 | } else | |
147 | ret = 0; | |
148 | mutex_unlock(&stream->dvb.feedlock); | |
149 | } | |
150 | ||
151 | return ret; | |
152 | } | |
153 | ||
154 | int cx18_dvb_register(struct cx18_stream *stream) | |
155 | { | |
156 | struct cx18 *cx = stream->cx; | |
157 | struct cx18_dvb *dvb = &stream->dvb; | |
158 | struct dvb_adapter *dvb_adapter; | |
159 | struct dvb_demux *dvbdemux; | |
160 | struct dmx_demux *dmx; | |
161 | int ret; | |
162 | ||
163 | if (!dvb) | |
164 | return -EINVAL; | |
165 | ||
166 | ret = dvb_register_adapter(&dvb->dvb_adapter, | |
167 | CX18_DRIVER_NAME, | |
168 | THIS_MODULE, &cx->dev->dev, adapter_nr); | |
169 | if (ret < 0) | |
170 | goto err_out; | |
171 | ||
172 | dvb_adapter = &dvb->dvb_adapter; | |
173 | ||
174 | dvbdemux = &dvb->demux; | |
175 | ||
176 | dvbdemux->priv = (void *)stream; | |
177 | ||
178 | dvbdemux->filternum = 256; | |
179 | dvbdemux->feednum = 256; | |
180 | dvbdemux->start_feed = cx18_dvb_start_feed; | |
181 | dvbdemux->stop_feed = cx18_dvb_stop_feed; | |
182 | dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | | |
183 | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); | |
184 | ret = dvb_dmx_init(dvbdemux); | |
185 | if (ret < 0) | |
186 | goto err_dvb_unregister_adapter; | |
187 | ||
188 | dmx = &dvbdemux->dmx; | |
189 | ||
190 | dvb->hw_frontend.source = DMX_FRONTEND_0; | |
191 | dvb->mem_frontend.source = DMX_MEMORY_FE; | |
192 | dvb->dmxdev.filternum = 256; | |
193 | dvb->dmxdev.demux = dmx; | |
194 | ||
195 | ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); | |
196 | if (ret < 0) | |
197 | goto err_dvb_dmx_release; | |
198 | ||
199 | ret = dmx->add_frontend(dmx, &dvb->hw_frontend); | |
200 | if (ret < 0) | |
201 | goto err_dvb_dmxdev_release; | |
202 | ||
203 | ret = dmx->add_frontend(dmx, &dvb->mem_frontend); | |
204 | if (ret < 0) | |
205 | goto err_remove_hw_frontend; | |
206 | ||
207 | ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); | |
208 | if (ret < 0) | |
209 | goto err_remove_mem_frontend; | |
210 | ||
211 | ret = dvb_register(stream); | |
212 | if (ret < 0) | |
213 | goto err_disconnect_frontend; | |
214 | ||
215 | dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); | |
216 | ||
217 | CX18_INFO("DVB Frontend registered\n"); | |
218 | mutex_init(&dvb->feedlock); | |
219 | dvb->enabled = 1; | |
220 | return ret; | |
221 | ||
222 | err_disconnect_frontend: | |
223 | dmx->disconnect_frontend(dmx); | |
224 | err_remove_mem_frontend: | |
225 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | |
226 | err_remove_hw_frontend: | |
227 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | |
228 | err_dvb_dmxdev_release: | |
229 | dvb_dmxdev_release(&dvb->dmxdev); | |
230 | err_dvb_dmx_release: | |
231 | dvb_dmx_release(dvbdemux); | |
232 | err_dvb_unregister_adapter: | |
233 | dvb_unregister_adapter(dvb_adapter); | |
234 | err_out: | |
235 | return ret; | |
236 | } | |
237 | ||
238 | void cx18_dvb_unregister(struct cx18_stream *stream) | |
239 | { | |
240 | struct cx18 *cx = stream->cx; | |
241 | struct cx18_dvb *dvb = &stream->dvb; | |
242 | struct dvb_adapter *dvb_adapter; | |
243 | struct dvb_demux *dvbdemux; | |
244 | struct dmx_demux *dmx; | |
245 | ||
246 | CX18_INFO("unregister DVB\n"); | |
247 | ||
248 | dvb_adapter = &dvb->dvb_adapter; | |
249 | dvbdemux = &dvb->demux; | |
250 | dmx = &dvbdemux->dmx; | |
251 | ||
252 | dmx->close(dmx); | |
253 | dvb_net_release(&dvb->dvbnet); | |
254 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | |
255 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | |
256 | dvb_dmxdev_release(&dvb->dmxdev); | |
257 | dvb_dmx_release(dvbdemux); | |
258 | dvb_unregister_frontend(dvb->fe); | |
259 | dvb_frontend_detach(dvb->fe); | |
260 | dvb_unregister_adapter(dvb_adapter); | |
261 | } | |
262 | ||
263 | /* All the DVB attach calls go here, this function get's modified | |
264 | * for each new card. No other function in this file needs | |
265 | * to change. | |
266 | */ | |
267 | static int dvb_register(struct cx18_stream *stream) | |
268 | { | |
269 | struct cx18_dvb *dvb = &stream->dvb; | |
270 | struct cx18 *cx = stream->cx; | |
271 | int ret = 0; | |
272 | ||
273 | switch (cx->card->type) { | |
1c1e45d1 HV |
274 | case CX18_CARD_HVR_1600_ESMT: |
275 | case CX18_CARD_HVR_1600_SAMSUNG: | |
276 | dvb->fe = dvb_attach(s5h1409_attach, | |
277 | &hauppauge_hvr1600_config, | |
278 | &cx->i2c_adap[0]); | |
279 | if (dvb->fe != NULL) { | |
67129471 ST |
280 | dvb_attach(mxl5005s_attach, dvb->fe, |
281 | &cx->i2c_adap[0], | |
282 | &hauppauge_hvr1600_tuner); | |
1c1e45d1 HV |
283 | ret = 0; |
284 | } | |
285 | break; | |
1c1e45d1 HV |
286 | default: |
287 | /* No Digital Tv Support */ | |
288 | break; | |
289 | } | |
290 | ||
291 | if (dvb->fe == NULL) { | |
292 | CX18_ERR("frontend initialization failed\n"); | |
293 | return -1; | |
294 | } | |
295 | ||
296 | ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); | |
297 | if (ret < 0) { | |
298 | if (dvb->fe->ops.release) | |
299 | dvb->fe->ops.release(dvb->fe); | |
300 | return ret; | |
301 | } | |
302 | ||
303 | return ret; | |
304 | } | |
1d6782bd AW |
305 | |
306 | void cx18_dvb_work_handler(struct cx18 *cx) | |
307 | { | |
308 | struct cx18_buffer *buf; | |
309 | struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS]; | |
310 | ||
311 | while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) { | |
312 | if (s->dvb.enabled) | |
313 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | |
314 | buf->bytesused); | |
315 | ||
316 | cx18_enqueue(s, buf, &s->q_free); | |
317 | cx18_buf_sync_for_device(s, buf); | |
318 | if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */ | |
319 | continue; | |
320 | ||
321 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, | |
322 | (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, | |
323 | 1, buf->id, s->buf_size); | |
324 | } | |
325 | } |