]>
Commit | Line | Data |
---|---|---|
eea977ed MB |
1 | /* |
2 | * Fitipower FC0011 tuner driver | |
3 | * | |
4 | * Copyright (C) 2012 Michael Buesch <m@bues.ch> | |
5 | * | |
6 | * Derived from FC0012 tuner driver: | |
7 | * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
eea977ed MB |
18 | */ |
19 | ||
20 | #include "fc0011.h" | |
21 | ||
22 | ||
23 | /* Tuner registers */ | |
24 | enum { | |
25 | FC11_REG_0, | |
26 | FC11_REG_FA, /* FA */ | |
27 | FC11_REG_FP, /* FP */ | |
28 | FC11_REG_XINHI, /* XIN high 8 bit */ | |
29 | FC11_REG_XINLO, /* XIN low 8 bit */ | |
30 | FC11_REG_VCO, /* VCO */ | |
31 | FC11_REG_VCOSEL, /* VCO select */ | |
32 | FC11_REG_7, /* Unknown tuner reg 7 */ | |
33 | FC11_REG_8, /* Unknown tuner reg 8 */ | |
34 | FC11_REG_9, | |
35 | FC11_REG_10, /* Unknown tuner reg 10 */ | |
36 | FC11_REG_11, /* Unknown tuner reg 11 */ | |
37 | FC11_REG_12, | |
38 | FC11_REG_RCCAL, /* RC calibrate */ | |
39 | FC11_REG_VCOCAL, /* VCO calibrate */ | |
40 | FC11_REG_15, | |
41 | FC11_REG_16, /* Unknown tuner reg 16 */ | |
42 | FC11_REG_17, | |
43 | ||
44 | FC11_NR_REGS, /* Number of registers */ | |
45 | }; | |
46 | ||
47 | enum FC11_REG_VCOSEL_bits { | |
48 | FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ | |
49 | FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ | |
50 | FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ | |
51 | FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ | |
52 | FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ | |
53 | }; | |
54 | ||
55 | enum FC11_REG_RCCAL_bits { | |
56 | FC11_RCCAL_FORCE = 0x10, /* force */ | |
57 | }; | |
58 | ||
59 | enum FC11_REG_VCOCAL_bits { | |
60 | FC11_VCOCAL_RUN = 0, /* VCO calibration run */ | |
61 | FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ | |
62 | FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ | |
63 | FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ | |
64 | }; | |
65 | ||
66 | ||
67 | struct fc0011_priv { | |
68 | struct i2c_adapter *i2c; | |
69 | u8 addr; | |
70 | ||
71 | u32 frequency; | |
72 | u32 bandwidth; | |
73 | }; | |
74 | ||
75 | ||
76 | static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) | |
77 | { | |
78 | u8 buf[2] = { reg, val }; | |
79 | struct i2c_msg msg = { .addr = priv->addr, | |
80 | .flags = 0, .buf = buf, .len = 2 }; | |
81 | ||
82 | if (i2c_transfer(priv->i2c, &msg, 1) != 1) { | |
83 | dev_err(&priv->i2c->dev, | |
84 | "I2C write reg failed, reg: %02x, val: %02x\n", | |
85 | reg, val); | |
86 | return -EIO; | |
87 | } | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
92 | static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) | |
93 | { | |
94 | u8 dummy; | |
95 | struct i2c_msg msg[2] = { | |
96 | { .addr = priv->addr, | |
97 | .flags = 0, .buf = ®, .len = 1 }, | |
98 | { .addr = priv->addr, | |
99 | .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, | |
100 | }; | |
101 | ||
102 | if (i2c_transfer(priv->i2c, msg, 2) != 2) { | |
103 | dev_err(&priv->i2c->dev, | |
104 | "I2C read failed, reg: %02x\n", reg); | |
105 | return -EIO; | |
106 | } | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
f2709c20 MCC |
111 | static void fc0011_release(struct dvb_frontend *fe) |
112 | { | |
113 | kfree(fe->tuner_priv); | |
114 | fe->tuner_priv = NULL; | |
115 | } | |
116 | ||
eea977ed MB |
117 | static int fc0011_init(struct dvb_frontend *fe) |
118 | { | |
119 | struct fc0011_priv *priv = fe->tuner_priv; | |
120 | int err; | |
121 | ||
122 | if (WARN_ON(!fe->callback)) | |
123 | return -EINVAL; | |
124 | ||
125 | err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, | |
126 | FC0011_FE_CALLBACK_POWER, priv->addr); | |
127 | if (err) { | |
128 | dev_err(&priv->i2c->dev, "Power-on callback failed\n"); | |
129 | return err; | |
130 | } | |
131 | err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, | |
132 | FC0011_FE_CALLBACK_RESET, priv->addr); | |
133 | if (err) { | |
134 | dev_err(&priv->i2c->dev, "Reset callback failed\n"); | |
135 | return err; | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | /* Initiate VCO calibration */ | |
142 | static int fc0011_vcocal_trigger(struct fc0011_priv *priv) | |
143 | { | |
144 | int err; | |
145 | ||
146 | err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); | |
147 | if (err) | |
148 | return err; | |
149 | err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); | |
150 | if (err) | |
151 | return err; | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | /* Read VCO calibration value */ | |
157 | static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) | |
158 | { | |
159 | int err; | |
160 | ||
161 | err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); | |
162 | if (err) | |
163 | return err; | |
c421d5c9 | 164 | usleep_range(10000, 20000); |
eea977ed MB |
165 | err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); |
166 | if (err) | |
167 | return err; | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | static int fc0011_set_params(struct dvb_frontend *fe) | |
173 | { | |
174 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; | |
175 | struct fc0011_priv *priv = fe->tuner_priv; | |
176 | int err; | |
177 | unsigned int i, vco_retries; | |
178 | u32 freq = p->frequency / 1000; | |
179 | u32 bandwidth = p->bandwidth_hz / 1000; | |
03a497d4 | 180 | u32 fvco, xin, frac, xdiv, xdivr; |
eea977ed MB |
181 | u8 fa, fp, vco_sel, vco_cal; |
182 | u8 regs[FC11_NR_REGS] = { }; | |
183 | ||
184 | regs[FC11_REG_7] = 0x0F; | |
185 | regs[FC11_REG_8] = 0x3E; | |
186 | regs[FC11_REG_10] = 0xB8; | |
187 | regs[FC11_REG_11] = 0x80; | |
188 | regs[FC11_REG_RCCAL] = 0x04; | |
189 | err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); | |
190 | err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); | |
191 | err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); | |
192 | err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); | |
193 | err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); | |
194 | if (err) | |
195 | return -EIO; | |
196 | ||
197 | /* Set VCO freq and VCO div */ | |
198 | if (freq < 54000) { | |
199 | fvco = freq * 64; | |
200 | regs[FC11_REG_VCO] = 0x82; | |
201 | } else if (freq < 108000) { | |
202 | fvco = freq * 32; | |
203 | regs[FC11_REG_VCO] = 0x42; | |
204 | } else if (freq < 216000) { | |
205 | fvco = freq * 16; | |
206 | regs[FC11_REG_VCO] = 0x22; | |
207 | } else if (freq < 432000) { | |
208 | fvco = freq * 8; | |
209 | regs[FC11_REG_VCO] = 0x12; | |
210 | } else { | |
211 | fvco = freq * 4; | |
212 | regs[FC11_REG_VCO] = 0x0A; | |
213 | } | |
214 | ||
215 | /* Calc XIN. The PLL reference frequency is 18 MHz. */ | |
216 | xdiv = fvco / 18000; | |
aadb4640 | 217 | WARN_ON(xdiv > 0xFF); |
eea977ed MB |
218 | frac = fvco - xdiv * 18000; |
219 | frac = (frac << 15) / 18000; | |
220 | if (frac >= 16384) | |
221 | frac += 32786; | |
222 | if (!frac) | |
223 | xin = 0; | |
eea977ed | 224 | else |
03a497d4 | 225 | xin = clamp_t(u32, frac, 512, 65024); |
eea977ed MB |
226 | regs[FC11_REG_XINHI] = xin >> 8; |
227 | regs[FC11_REG_XINLO] = xin; | |
228 | ||
229 | /* Calc FP and FA */ | |
230 | xdivr = xdiv; | |
231 | if (fvco - xdiv * 18000 >= 9000) | |
232 | xdivr += 1; /* round */ | |
233 | fp = xdivr / 8; | |
234 | fa = xdivr - fp * 8; | |
235 | if (fa < 2) { | |
236 | fp -= 1; | |
237 | fa += 8; | |
238 | } | |
239 | if (fp > 0x1F) { | |
0917a604 MB |
240 | fp = 0x1F; |
241 | fa = 0xF; | |
eea977ed MB |
242 | } |
243 | if (fa >= fp) { | |
244 | dev_warn(&priv->i2c->dev, | |
245 | "fa %02X >= fp %02X, but trying to continue\n", | |
246 | (unsigned int)(u8)fa, (unsigned int)(u8)fp); | |
247 | } | |
248 | regs[FC11_REG_FA] = fa; | |
249 | regs[FC11_REG_FP] = fp; | |
250 | ||
251 | /* Select bandwidth */ | |
252 | switch (bandwidth) { | |
253 | case 8000: | |
254 | break; | |
255 | case 7000: | |
256 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; | |
257 | break; | |
258 | default: | |
8ca2e927 | 259 | dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. Using 6000 kHz.\n", |
eea977ed MB |
260 | bandwidth); |
261 | bandwidth = 6000; | |
262 | /* fallthrough */ | |
263 | case 6000: | |
264 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; | |
265 | break; | |
266 | } | |
267 | ||
268 | /* Pre VCO select */ | |
269 | if (fvco < 2320000) { | |
270 | vco_sel = 0; | |
271 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
272 | } else if (fvco < 3080000) { | |
273 | vco_sel = 1; | |
274 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
275 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | |
276 | } else { | |
277 | vco_sel = 2; | |
278 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
279 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; | |
280 | } | |
281 | ||
282 | /* Fix for low freqs */ | |
283 | if (freq < 45000) { | |
284 | regs[FC11_REG_FA] = 0x6; | |
285 | regs[FC11_REG_FP] = 0x11; | |
286 | } | |
287 | ||
288 | /* Clock out fix */ | |
289 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; | |
290 | ||
291 | /* Write the cached registers */ | |
292 | for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { | |
293 | err = fc0011_writereg(priv, i, regs[i]); | |
294 | if (err) | |
295 | return err; | |
296 | } | |
297 | ||
298 | /* VCO calibration */ | |
299 | err = fc0011_vcocal_trigger(priv); | |
300 | if (err) | |
301 | return err; | |
302 | err = fc0011_vcocal_read(priv, &vco_cal); | |
303 | if (err) | |
304 | return err; | |
305 | vco_retries = 0; | |
a182fd8f | 306 | while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { |
eea977ed MB |
307 | /* Reset the tuner and try again */ |
308 | err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, | |
309 | FC0011_FE_CALLBACK_RESET, priv->addr); | |
310 | if (err) { | |
311 | dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); | |
312 | return err; | |
313 | } | |
314 | /* Reinit tuner config */ | |
315 | err = 0; | |
316 | for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) | |
317 | err |= fc0011_writereg(priv, i, regs[i]); | |
318 | err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); | |
319 | err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); | |
320 | err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); | |
321 | err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); | |
322 | err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); | |
323 | if (err) | |
324 | return -EIO; | |
325 | /* VCO calibration */ | |
326 | err = fc0011_vcocal_trigger(priv); | |
327 | if (err) | |
328 | return err; | |
329 | err = fc0011_vcocal_read(priv, &vco_cal); | |
330 | if (err) | |
331 | return err; | |
332 | vco_retries++; | |
333 | } | |
334 | if (!(vco_cal & FC11_VCOCAL_OK)) { | |
335 | dev_err(&priv->i2c->dev, | |
336 | "Failed to read VCO calibration value (got %02X)\n", | |
337 | (unsigned int)vco_cal); | |
338 | return -EIO; | |
339 | } | |
340 | vco_cal &= FC11_VCOCAL_VALUEMASK; | |
341 | ||
342 | switch (vco_sel) { | |
aadb4640 MB |
343 | default: |
344 | WARN_ON(1); | |
f7d84fa7 | 345 | return -EINVAL; |
eea977ed MB |
346 | case 0: |
347 | if (vco_cal < 8) { | |
348 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
349 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | |
350 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | |
351 | regs[FC11_REG_VCOSEL]); | |
352 | if (err) | |
353 | return err; | |
354 | err = fc0011_vcocal_trigger(priv); | |
355 | if (err) | |
356 | return err; | |
357 | } else { | |
358 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
359 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | |
360 | regs[FC11_REG_VCOSEL]); | |
361 | if (err) | |
362 | return err; | |
363 | } | |
364 | break; | |
365 | case 1: | |
366 | if (vco_cal < 5) { | |
367 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
368 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; | |
369 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | |
370 | regs[FC11_REG_VCOSEL]); | |
371 | if (err) | |
372 | return err; | |
373 | err = fc0011_vcocal_trigger(priv); | |
374 | if (err) | |
375 | return err; | |
376 | } else if (vco_cal <= 48) { | |
377 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
378 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | |
379 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | |
380 | regs[FC11_REG_VCOSEL]); | |
381 | if (err) | |
382 | return err; | |
383 | } else { | |
384 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
385 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | |
386 | regs[FC11_REG_VCOSEL]); | |
387 | if (err) | |
388 | return err; | |
389 | err = fc0011_vcocal_trigger(priv); | |
390 | if (err) | |
391 | return err; | |
392 | } | |
393 | break; | |
394 | case 2: | |
395 | if (vco_cal > 53) { | |
396 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
397 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; | |
398 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | |
399 | regs[FC11_REG_VCOSEL]); | |
400 | if (err) | |
401 | return err; | |
402 | err = fc0011_vcocal_trigger(priv); | |
403 | if (err) | |
404 | return err; | |
405 | } else { | |
406 | regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); | |
407 | regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; | |
408 | err = fc0011_writereg(priv, FC11_REG_VCOSEL, | |
409 | regs[FC11_REG_VCOSEL]); | |
410 | if (err) | |
411 | return err; | |
412 | } | |
413 | break; | |
414 | } | |
415 | err = fc0011_vcocal_read(priv, NULL); | |
416 | if (err) | |
417 | return err; | |
c421d5c9 | 418 | usleep_range(10000, 50000); |
eea977ed MB |
419 | |
420 | err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); | |
421 | if (err) | |
422 | return err; | |
423 | regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; | |
424 | err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); | |
425 | if (err) | |
426 | return err; | |
aadb4640 MB |
427 | regs[FC11_REG_16] = 0xB; |
428 | err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]); | |
eea977ed MB |
429 | if (err) |
430 | return err; | |
431 | ||
8ca2e927 | 432 | dev_dbg(&priv->i2c->dev, "Tuned to fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X vcocal=%02X(%u) bw=%u\n", |
eea977ed MB |
433 | (unsigned int)regs[FC11_REG_FA], |
434 | (unsigned int)regs[FC11_REG_FP], | |
435 | (unsigned int)regs[FC11_REG_XINHI], | |
436 | (unsigned int)regs[FC11_REG_XINLO], | |
437 | (unsigned int)regs[FC11_REG_VCO], | |
438 | (unsigned int)regs[FC11_REG_VCOSEL], | |
439 | (unsigned int)vco_cal, vco_retries, | |
440 | (unsigned int)bandwidth); | |
441 | ||
442 | priv->frequency = p->frequency; | |
443 | priv->bandwidth = p->bandwidth_hz; | |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
448 | static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) | |
449 | { | |
450 | struct fc0011_priv *priv = fe->tuner_priv; | |
451 | ||
452 | *frequency = priv->frequency; | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
457 | static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) | |
458 | { | |
459 | *frequency = 0; | |
460 | ||
461 | return 0; | |
462 | } | |
463 | ||
464 | static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) | |
465 | { | |
466 | struct fc0011_priv *priv = fe->tuner_priv; | |
467 | ||
468 | *bandwidth = priv->bandwidth; | |
469 | ||
470 | return 0; | |
471 | } | |
472 | ||
473 | static const struct dvb_tuner_ops fc0011_tuner_ops = { | |
474 | .info = { | |
475 | .name = "Fitipower FC0011", | |
476 | ||
477 | .frequency_min = 45000000, | |
478 | .frequency_max = 1000000000, | |
479 | }, | |
480 | ||
f2709c20 | 481 | .release = fc0011_release, |
eea977ed MB |
482 | .init = fc0011_init, |
483 | ||
484 | .set_params = fc0011_set_params, | |
485 | ||
486 | .get_frequency = fc0011_get_frequency, | |
487 | .get_if_frequency = fc0011_get_if_frequency, | |
488 | .get_bandwidth = fc0011_get_bandwidth, | |
489 | }; | |
490 | ||
491 | struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, | |
492 | struct i2c_adapter *i2c, | |
493 | const struct fc0011_config *config) | |
494 | { | |
495 | struct fc0011_priv *priv; | |
496 | ||
497 | priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); | |
498 | if (!priv) | |
499 | return NULL; | |
500 | ||
501 | priv->i2c = i2c; | |
502 | priv->addr = config->i2c_address; | |
503 | ||
504 | fe->tuner_priv = priv; | |
505 | fe->ops.tuner_ops = fc0011_tuner_ops; | |
506 | ||
507 | dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); | |
508 | ||
509 | return fe; | |
510 | } | |
511 | EXPORT_SYMBOL(fc0011_attach); | |
512 | ||
513 | MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); | |
514 | MODULE_AUTHOR("Michael Buesch <m@bues.ch>"); | |
515 | MODULE_LICENSE("GPL"); |