Commit | Line | Data |
---|---|---|
1c1e45d1 HV |
1 | /* |
2 | * cx18 functions for DVB support | |
3 | * | |
4 | * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> | |
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" | |
24 | #include "cx18-streams.h" | |
25 | #include "cx18-cards.h" | |
26 | #include "s5h1409.h" | |
67129471 | 27 | #include "mxl5005s.h" |
1c1e45d1 HV |
28 | |
29 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | |
30 | ||
31 | #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 | |
32 | ||
67129471 ST |
33 | static struct mxl5005s_config hauppauge_hvr1600_tuner = { |
34 | .i2c_address = 0xC6 >> 1, | |
35 | .if_freq = IF_FREQ_5380000HZ, | |
36 | .xtal_freq = CRYSTAL_FREQ_16000000HZ, | |
37 | .agc_mode = MXL_SINGLE_AGC, | |
38 | .tracking_filter = MXL_TF_C_H, | |
39 | .rssi_enable = MXL_RSSI_ENABLE, | |
40 | .cap_select = MXL_CAP_SEL_ENABLE, | |
41 | .div_out = MXL_DIV_OUT_4, | |
42 | .clock_out = MXL_CLOCK_OUT_DISABLE, | |
43 | .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, | |
44 | .top = MXL5005S_TOP_25P2, | |
45 | .mod_mode = MXL_DIGITAL_MODE, | |
46 | .if_mode = MXL_ZERO_IF, | |
47 | .AgcMasterByte = 0x00, | |
1c1e45d1 HV |
48 | }; |
49 | ||
50 | static struct s5h1409_config hauppauge_hvr1600_config = { | |
51 | .demod_address = 0x32 >> 1, | |
52 | .output_mode = S5H1409_SERIAL_OUTPUT, | |
53 | .gpio = S5H1409_GPIO_ON, | |
54 | .qam_if = 44000, | |
55 | .inversion = S5H1409_INVERSION_OFF, | |
56 | .status_mode = S5H1409_DEMODLOCKING, | |
57 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK | |
58 | ||
59 | }; | |
1c1e45d1 HV |
60 | |
61 | static int dvb_register(struct cx18_stream *stream); | |
62 | ||
63 | /* Kernel DVB framework calls this when the feed needs to start. | |
64 | * The CX18 framework should enable the transport DMA handling | |
65 | * and queue processing. | |
66 | */ | |
67 | static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) | |
68 | { | |
69 | struct dvb_demux *demux = feed->demux; | |
70 | struct cx18_stream *stream = (struct cx18_stream *) demux->priv; | |
71 | struct cx18 *cx = stream->cx; | |
08cf7b2e | 72 | int ret; |
1c1e45d1 HV |
73 | u32 v; |
74 | ||
75 | CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", | |
76 | feed->pid, feed->index); | |
08cf7b2e AW |
77 | |
78 | mutex_lock(&cx->serialize_lock); | |
79 | ret = cx18_init_on_first_open(cx); | |
80 | mutex_unlock(&cx->serialize_lock); | |
81 | if (ret) { | |
82 | CX18_ERR("Failed to initialize firmware starting DVB feed\n"); | |
83 | return ret; | |
84 | } | |
85 | ret = -EINVAL; | |
86 | ||
1c1e45d1 HV |
87 | switch (cx->card->type) { |
88 | case CX18_CARD_HVR_1600_ESMT: | |
89 | case CX18_CARD_HVR_1600_SAMSUNG: | |
90 | v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL); | |
91 | v |= 0x00400000; /* Serial Mode */ | |
92 | v |= 0x00002000; /* Data Length - Byte */ | |
93 | v |= 0x00010000; /* Error - Polarity */ | |
94 | v |= 0x00020000; /* Error - Passthru */ | |
95 | v |= 0x000c0000; /* Error - Ignore */ | |
96 | write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); | |
97 | break; | |
98 | ||
99 | default: | |
100 | /* Assumption - Parallel transport - Signalling | |
101 | * undefined or default. | |
102 | */ | |
103 | break; | |
104 | } | |
105 | ||
106 | if (!demux->dmx.frontend) | |
107 | return -EINVAL; | |
108 | ||
109 | if (stream) { | |
110 | mutex_lock(&stream->dvb.feedlock); | |
111 | if (stream->dvb.feeding++ == 0) { | |
112 | CX18_DEBUG_INFO("Starting Transport DMA\n"); | |
113 | ret = cx18_start_v4l2_encode_stream(stream); | |
08cf7b2e AW |
114 | if (ret < 0) { |
115 | CX18_DEBUG_INFO( | |
116 | "Failed to start Transport DMA\n"); | |
117 | stream->dvb.feeding--; | |
118 | } | |
1c1e45d1 HV |
119 | } else |
120 | ret = 0; | |
121 | mutex_unlock(&stream->dvb.feedlock); | |
122 | } | |
123 | ||
124 | return ret; | |
125 | } | |
126 | ||
127 | /* Kernel DVB framework calls this when the feed needs to stop. */ | |
128 | static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) | |
129 | { | |
130 | struct dvb_demux *demux = feed->demux; | |
131 | struct cx18_stream *stream = (struct cx18_stream *)demux->priv; | |
132 | struct cx18 *cx = stream->cx; | |
133 | int ret = -EINVAL; | |
134 | ||
135 | CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", | |
136 | feed->pid, feed->index); | |
137 | ||
138 | if (stream) { | |
139 | mutex_lock(&stream->dvb.feedlock); | |
140 | if (--stream->dvb.feeding == 0) { | |
141 | CX18_DEBUG_INFO("Stopping Transport DMA\n"); | |
142 | ret = cx18_stop_v4l2_encode_stream(stream, 0); | |
143 | } else | |
144 | ret = 0; | |
145 | mutex_unlock(&stream->dvb.feedlock); | |
146 | } | |
147 | ||
148 | return ret; | |
149 | } | |
150 | ||
151 | int cx18_dvb_register(struct cx18_stream *stream) | |
152 | { | |
153 | struct cx18 *cx = stream->cx; | |
154 | struct cx18_dvb *dvb = &stream->dvb; | |
155 | struct dvb_adapter *dvb_adapter; | |
156 | struct dvb_demux *dvbdemux; | |
157 | struct dmx_demux *dmx; | |
158 | int ret; | |
159 | ||
160 | if (!dvb) | |
161 | return -EINVAL; | |
162 | ||
163 | ret = dvb_register_adapter(&dvb->dvb_adapter, | |
164 | CX18_DRIVER_NAME, | |
165 | THIS_MODULE, &cx->dev->dev, adapter_nr); | |
166 | if (ret < 0) | |
167 | goto err_out; | |
168 | ||
169 | dvb_adapter = &dvb->dvb_adapter; | |
170 | ||
171 | dvbdemux = &dvb->demux; | |
172 | ||
173 | dvbdemux->priv = (void *)stream; | |
174 | ||
175 | dvbdemux->filternum = 256; | |
176 | dvbdemux->feednum = 256; | |
177 | dvbdemux->start_feed = cx18_dvb_start_feed; | |
178 | dvbdemux->stop_feed = cx18_dvb_stop_feed; | |
179 | dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | | |
180 | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); | |
181 | ret = dvb_dmx_init(dvbdemux); | |
182 | if (ret < 0) | |
183 | goto err_dvb_unregister_adapter; | |
184 | ||
185 | dmx = &dvbdemux->dmx; | |
186 | ||
187 | dvb->hw_frontend.source = DMX_FRONTEND_0; | |
188 | dvb->mem_frontend.source = DMX_MEMORY_FE; | |
189 | dvb->dmxdev.filternum = 256; | |
190 | dvb->dmxdev.demux = dmx; | |
191 | ||
192 | ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); | |
193 | if (ret < 0) | |
194 | goto err_dvb_dmx_release; | |
195 | ||
196 | ret = dmx->add_frontend(dmx, &dvb->hw_frontend); | |
197 | if (ret < 0) | |
198 | goto err_dvb_dmxdev_release; | |
199 | ||
200 | ret = dmx->add_frontend(dmx, &dvb->mem_frontend); | |
201 | if (ret < 0) | |
202 | goto err_remove_hw_frontend; | |
203 | ||
204 | ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); | |
205 | if (ret < 0) | |
206 | goto err_remove_mem_frontend; | |
207 | ||
208 | ret = dvb_register(stream); | |
209 | if (ret < 0) | |
210 | goto err_disconnect_frontend; | |
211 | ||
212 | dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); | |
213 | ||
214 | CX18_INFO("DVB Frontend registered\n"); | |
215 | mutex_init(&dvb->feedlock); | |
216 | dvb->enabled = 1; | |
217 | return ret; | |
218 | ||
219 | err_disconnect_frontend: | |
220 | dmx->disconnect_frontend(dmx); | |
221 | err_remove_mem_frontend: | |
222 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | |
223 | err_remove_hw_frontend: | |
224 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | |
225 | err_dvb_dmxdev_release: | |
226 | dvb_dmxdev_release(&dvb->dmxdev); | |
227 | err_dvb_dmx_release: | |
228 | dvb_dmx_release(dvbdemux); | |
229 | err_dvb_unregister_adapter: | |
230 | dvb_unregister_adapter(dvb_adapter); | |
231 | err_out: | |
232 | return ret; | |
233 | } | |
234 | ||
235 | void cx18_dvb_unregister(struct cx18_stream *stream) | |
236 | { | |
237 | struct cx18 *cx = stream->cx; | |
238 | struct cx18_dvb *dvb = &stream->dvb; | |
239 | struct dvb_adapter *dvb_adapter; | |
240 | struct dvb_demux *dvbdemux; | |
241 | struct dmx_demux *dmx; | |
242 | ||
243 | CX18_INFO("unregister DVB\n"); | |
244 | ||
245 | dvb_adapter = &dvb->dvb_adapter; | |
246 | dvbdemux = &dvb->demux; | |
247 | dmx = &dvbdemux->dmx; | |
248 | ||
249 | dmx->close(dmx); | |
250 | dvb_net_release(&dvb->dvbnet); | |
251 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | |
252 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | |
253 | dvb_dmxdev_release(&dvb->dmxdev); | |
254 | dvb_dmx_release(dvbdemux); | |
255 | dvb_unregister_frontend(dvb->fe); | |
256 | dvb_frontend_detach(dvb->fe); | |
257 | dvb_unregister_adapter(dvb_adapter); | |
258 | } | |
259 | ||
260 | /* All the DVB attach calls go here, this function get's modified | |
261 | * for each new card. No other function in this file needs | |
262 | * to change. | |
263 | */ | |
264 | static int dvb_register(struct cx18_stream *stream) | |
265 | { | |
266 | struct cx18_dvb *dvb = &stream->dvb; | |
267 | struct cx18 *cx = stream->cx; | |
268 | int ret = 0; | |
269 | ||
270 | switch (cx->card->type) { | |
1c1e45d1 HV |
271 | case CX18_CARD_HVR_1600_ESMT: |
272 | case CX18_CARD_HVR_1600_SAMSUNG: | |
273 | dvb->fe = dvb_attach(s5h1409_attach, | |
274 | &hauppauge_hvr1600_config, | |
275 | &cx->i2c_adap[0]); | |
276 | if (dvb->fe != NULL) { | |
67129471 ST |
277 | dvb_attach(mxl5005s_attach, dvb->fe, |
278 | &cx->i2c_adap[0], | |
279 | &hauppauge_hvr1600_tuner); | |
1c1e45d1 HV |
280 | ret = 0; |
281 | } | |
282 | break; | |
1c1e45d1 HV |
283 | default: |
284 | /* No Digital Tv Support */ | |
285 | break; | |
286 | } | |
287 | ||
288 | if (dvb->fe == NULL) { | |
289 | CX18_ERR("frontend initialization failed\n"); | |
290 | return -1; | |
291 | } | |
292 | ||
293 | ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); | |
294 | if (ret < 0) { | |
295 | if (dvb->fe->ops.release) | |
296 | dvb->fe->ops.release(dvb->fe); | |
297 | return ret; | |
298 | } | |
299 | ||
300 | return ret; | |
301 | } |