Merge ../from-linus
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / media / dvb / frontends / lgdt330x.c
1 /*
2  *    Support for LGDT3302 & LGDT3303 (DViCO FusionHDTV Gold) - VSB/QAM
3  *
4  *    Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
5  *
6  *    Based on code from  Kirk Lapray <kirk_lapray@bigfoot.com>
7  *                           Copyright (C) 2005
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.
18  *
19  *    You should have received a copy of the GNU General Public License
20  *    along with this program; if not, write to the Free Software
21  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  */
24
25 /*
26  *                      NOTES ABOUT THIS DRIVER
27  *
28  * This driver supports DViCO FusionHDTV Gold under Linux.
29  *
30  * TODO:
31  * BER and signal strength always return 0.
32  * Include support for LGDT3303
33  *
34  */
35
36 #include <linux/kernel.h>
37 #include <linux/module.h>
38 #include <linux/moduleparam.h>
39 #include <linux/init.h>
40 #include <linux/delay.h>
41 #include <asm/byteorder.h>
42
43 #include "dvb_frontend.h"
44 #include "dvb-pll.h"
45 #include "lgdt330x_priv.h"
46 #include "lgdt330x.h"
47
48 static int debug = 0;
49 module_param(debug, int, 0644);
50 MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
51 #define dprintk(args...) \
52 do { \
53 if (debug) printk(KERN_DEBUG "lgdt330x: " args); \
54 } while (0)
55
56 struct lgdt330x_state
57 {
58         struct i2c_adapter* i2c;
59         struct dvb_frontend_ops ops;
60
61         /* Configuration settings */
62         const struct lgdt330x_config* config;
63
64         struct dvb_frontend frontend;
65
66         /* Demodulator private data */
67         fe_modulation_t current_modulation;
68
69         /* Tuner private data */
70         u32 current_frequency;
71 };
72
73 static int i2c_writebytes (struct lgdt330x_state* state,
74                            u8 addr, /* demod_address or pll_address */
75                            u8 *buf, /* data bytes to send */
76                            int len  /* number of bytes to send */ )
77 {
78         u8 tmp[] = { buf[0], buf[1] };
79         struct i2c_msg msg =
80                 { .addr = addr, .flags = 0, .buf = tmp,  .len = 2 };
81         int err;
82         int i;
83
84         for (i=1; i<len; i++) {
85                 tmp[1] = buf[i];
86                 if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
87                         printk(KERN_WARNING "lgdt330x: %s error (addr %02x <- %02x, err == %i)\n", __FUNCTION__, addr, buf[0], err);
88                         if (err < 0)
89                                 return err;
90                         else
91                                 return -EREMOTEIO;
92                 }
93                 tmp[0]++;
94         }
95         return 0;
96 }
97
98 #if 0
99 static int i2c_readbytes (struct lgdt330x_state* state,
100                           u8 addr, /* demod_address or pll_address */
101                           u8 *buf, /* holds data bytes read */
102                           int len  /* number of bytes to read */ )
103 {
104         struct i2c_msg msg =
105                 { .addr = addr, .flags = I2C_M_RD, .buf = buf,  .len = len };
106         int err;
107
108         if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
109                 printk(KERN_WARNING "lgdt330x: %s error (addr %02x, err == %i)\n", __FUNCTION__, addr, err);
110                 return -EREMOTEIO;
111         }
112         return 0;
113 }
114 #endif
115
116 /*
117  * This routine writes the register (reg) to the demod bus
118  * then reads the data returned for (len) bytes.
119  */
120
121 static u8 i2c_selectreadbytes (struct lgdt330x_state* state,
122                                enum I2C_REG reg, u8* buf, int len)
123 {
124         u8 wr [] = { reg };
125         struct i2c_msg msg [] = {
126                 { .addr = state->config->demod_address,
127                   .flags = 0, .buf = wr,  .len = 1 },
128                 { .addr = state->config->demod_address,
129                   .flags = I2C_M_RD, .buf = buf, .len = len },
130         };
131         int ret;
132         ret = i2c_transfer(state->i2c, msg, 2);
133         if (ret != 2) {
134                 printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __FUNCTION__, state->config->demod_address, reg, ret);
135         } else {
136                 ret = 0;
137         }
138         return ret;
139 }
140
141 /* Software reset */
142 int lgdt330x_SwReset(struct lgdt330x_state* state)
143 {
144         u8 ret;
145         u8 reset[] = {
146                 IRQ_MASK,
147                 0x00 /* bit 6 is active low software reset
148                       * bits 5-0 are 1 to mask interrupts */
149         };
150
151         ret = i2c_writebytes(state,
152                              state->config->demod_address,
153                              reset, sizeof(reset));
154         if (ret == 0) {
155                 /* spec says reset takes 100 ns why wait */
156                 /* mdelay(100);    */ /* keep low for 100mS */
157                 reset[1] = 0x7f;      /* force reset high (inactive)
158                                        * and unmask interrupts */
159                 ret = i2c_writebytes(state,
160                                      state->config->demod_address,
161                                      reset, sizeof(reset));
162         }
163         /* Spec does not indicate a need for this either */
164         /*mdelay(5); */               /* wait 5 msec before doing more */
165         return ret;
166 }
167
168 static int lgdt330x_init(struct dvb_frontend* fe)
169 {
170         /* Hardware reset is done using gpio[0] of cx23880x chip.
171          * I'd like to do it here, but don't know how to find chip address.
172          * cx88-cards.c arranges for the reset bit to be inactive (high).
173          * Maybe there needs to be a callable function in cx88-core or
174          * the caller of this function needs to do it. */
175
176         dprintk("%s entered\n", __FUNCTION__);
177         return lgdt330x_SwReset((struct lgdt330x_state*) fe->demodulator_priv);
178 }
179
180 static int lgdt330x_read_ber(struct dvb_frontend* fe, u32* ber)
181 {
182         *ber = 0; /* Dummy out for now */
183         return 0;
184 }
185
186 static int lgdt330x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
187 {
188         struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
189         u8 buf[2];
190
191         i2c_selectreadbytes(state, PACKET_ERR_COUNTER1, buf, sizeof(buf));
192
193         *ucblocks = (buf[0] << 8) | buf[1];
194         return 0;
195 }
196
197 static int lgdt330x_set_parameters(struct dvb_frontend* fe,
198                                    struct dvb_frontend_parameters *param)
199 {
200         struct lgdt330x_state* state =
201                 (struct lgdt330x_state*) fe->demodulator_priv;
202
203         /* Use 50MHz parameter values from spec sheet since xtal is 50 */
204         static u8 top_ctrl_cfg[]   = { TOP_CONTROL, 0x03 };
205         static u8 vsb_freq_cfg[]   = { VSB_CARRIER_FREQ0, 0x00, 0x87, 0x8e, 0x01 };
206         static u8 demux_ctrl_cfg[] = { DEMUX_CONTROL, 0xfb };
207         static u8 agc_rf_cfg[]     = { AGC_RF_BANDWIDTH0, 0x40, 0x93, 0x00 };
208         static u8 agc_ctrl_cfg[]   = { AGC_FUNC_CTRL2, 0xc6, 0x40 };
209         static u8 agc_delay_cfg[]  = { AGC_DELAY0, 0x07, 0x00, 0xfe };
210         static u8 agc_loop_cfg[]   = { AGC_LOOP_BANDWIDTH0, 0x08, 0x9a };
211
212         /* Change only if we are actually changing the modulation */
213         if (state->current_modulation != param->u.vsb.modulation) {
214                 switch(param->u.vsb.modulation) {
215                 case VSB_8:
216                         dprintk("%s: VSB_8 MODE\n", __FUNCTION__);
217
218                         /* Select VSB mode and serial MPEG interface */
219                         top_ctrl_cfg[1] = 0x07;
220
221                         /* Select ANT connector if supported by card */
222                         if (state->config->pll_rf_set)
223                                 state->config->pll_rf_set(fe, 1);
224                         break;
225
226                 case QAM_64:
227                         dprintk("%s: QAM_64 MODE\n", __FUNCTION__);
228
229                         /* Select QAM_64 mode and serial MPEG interface */
230                         top_ctrl_cfg[1] = 0x04;
231
232                         /* Select CABLE connector if supported by card */
233                         if (state->config->pll_rf_set)
234                                 state->config->pll_rf_set(fe, 0);
235                         break;
236
237                 case QAM_256:
238                         dprintk("%s: QAM_256 MODE\n", __FUNCTION__);
239
240                         /* Select QAM_256 mode and serial MPEG interface */
241                         top_ctrl_cfg[1] = 0x05;
242
243                         /* Select CABLE connector if supported by card */
244                         if (state->config->pll_rf_set)
245                                 state->config->pll_rf_set(fe, 0);
246                         break;
247                 default:
248                         printk(KERN_WARNING "lgdt330x: %s: Modulation type(%d) UNSUPPORTED\n", __FUNCTION__, param->u.vsb.modulation);
249                         return -1;
250                 }
251                 /* Initializations common to all modes */
252
253                 /* Select the requested mode */
254                 i2c_writebytes(state, state->config->demod_address,
255                                top_ctrl_cfg, sizeof(top_ctrl_cfg));
256
257                 /* Change the value of IFBW[11:0]
258                    of AGC IF/RF loop filter bandwidth register */
259                 i2c_writebytes(state, state->config->demod_address,
260                                agc_rf_cfg, sizeof(agc_rf_cfg));
261
262                 /* Change the value of bit 6, 'nINAGCBY' and
263                    'NSSEL[1:0] of ACG function control register 2 */
264                 /* Change the value of bit 6 'RFFIX'
265                    of AGC function control register 3 */
266                 i2c_writebytes(state, state->config->demod_address,
267                                agc_ctrl_cfg, sizeof(agc_ctrl_cfg));
268
269                 /* Change the TPCLK pin polarity
270                    data is valid on falling clock */
271                 i2c_writebytes(state, state->config->demod_address,
272                                demux_ctrl_cfg, sizeof(demux_ctrl_cfg));
273
274                 /* Change the value of NCOCTFV[25:0] of carrier
275                    recovery center frequency register */
276                 i2c_writebytes(state, state->config->demod_address,
277                                        vsb_freq_cfg, sizeof(vsb_freq_cfg));
278
279                 /* Set the value of 'INLVTHD' register 0x2a/0x2c to 0x7fe */
280                 i2c_writebytes(state, state->config->demod_address,
281                                agc_delay_cfg, sizeof(agc_delay_cfg));
282
283                 /* Change the value of IAGCBW[15:8]
284                    of inner AGC loop filter bandwith */
285                 i2c_writebytes(state, state->config->demod_address,
286                                agc_loop_cfg, sizeof(agc_loop_cfg));
287
288                 state->config->set_ts_params(fe, 0);
289                 state->current_modulation = param->u.vsb.modulation;
290         }
291
292         /* Change only if we are actually changing the channel */
293         if (state->current_frequency != param->frequency) {
294                 u8 buf[5];
295                 struct i2c_msg msg = { .flags = 0, .buf = &buf[1], .len = 4 };
296                 int err;
297
298                 state->config->pll_set(fe, param, buf);
299                 msg.addr = buf[0];
300
301                 dprintk("%s: tuner at 0x%02x bytes: 0x%02x 0x%02x "
302                         "0x%02x 0x%02x\n", __FUNCTION__,
303                         buf[0],buf[1],buf[2],buf[3],buf[4]);
304                 if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
305                         printk(KERN_WARNING "lgdt330x: %s error (addr %02x <- %02x, err = %i)\n", __FUNCTION__, buf[0], buf[1], err);
306                         if (err < 0)
307                                 return err;
308                         else
309                                 return -EREMOTEIO;
310                 }
311 #if 0
312                 /* Check the status of the tuner pll */
313                 i2c_readbytes(state, buf[0], &buf[1], 1);
314                 dprintk("%s: tuner status byte = 0x%02x\n", __FUNCTION__, buf[1]);
315 #endif
316                 /* Update current frequency */
317                 state->current_frequency = param->frequency;
318         }
319         lgdt330x_SwReset(state);
320         return 0;
321 }
322
323 static int lgdt330x_get_frontend(struct dvb_frontend* fe,
324                                  struct dvb_frontend_parameters* param)
325 {
326         struct lgdt330x_state *state = fe->demodulator_priv;
327         param->frequency = state->current_frequency;
328         return 0;
329 }
330
331 static int lgdt330x_read_status(struct dvb_frontend* fe, fe_status_t* status)
332 {
333         struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
334         u8 buf[3];
335
336         *status = 0; /* Reset status result */
337
338         /*
339          * You must set the Mask bits to 1 in the IRQ_MASK in order
340          * to see that status bit in the IRQ_STATUS register.
341          * This is done in SwReset();
342          */
343
344         /* AGC status register */
345         i2c_selectreadbytes(state, AGC_STATUS, buf, 1);
346         dprintk("%s: AGC_STATUS = 0x%02x\n", __FUNCTION__, buf[0]);
347         if ((buf[0] & 0x0c) == 0x8){
348                 /* Test signal does not exist flag */
349                 /* as well as the AGC lock flag.   */
350                 *status |= FE_HAS_SIGNAL;
351         } else {
352                 /* Without a signal all other status bits are meaningless */
353                 return 0;
354         }
355
356         /* signal status */
357         i2c_selectreadbytes(state, TOP_CONTROL, buf, sizeof(buf));
358         dprintk("%s: TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", __FUNCTION__, buf[0], buf[1], buf[2]);
359
360 #if 0
361         /* Alternative method to check for a signal */
362         /* using the SNR good/bad interrupts.   */
363         if ((buf[2] & 0x30) == 0x10)
364                 *status |= FE_HAS_SIGNAL;
365 #endif
366
367         /* sync status */
368         if ((buf[2] & 0x03) == 0x01) {
369                 *status |= FE_HAS_SYNC;
370         }
371
372         /* FEC error status */
373         if ((buf[2] & 0x0c) == 0x08) {
374                 *status |= FE_HAS_LOCK;
375                 *status |= FE_HAS_VITERBI;
376         }
377
378         /* Carrier Recovery Lock Status Register */
379         i2c_selectreadbytes(state, CARRIER_LOCK, buf, 1);
380         dprintk("%s: CARRIER_LOCK = 0x%02x\n", __FUNCTION__, buf[0]);
381         switch (state->current_modulation) {
382         case QAM_256:
383         case QAM_64:
384                 /* Need to undestand why there are 3 lock levels here */
385                 if ((buf[0] & 0x07) == 0x07)
386                         *status |= FE_HAS_CARRIER;
387                 break;
388         case VSB_8:
389                 if ((buf[0] & 0x80) == 0x80)
390                         *status |= FE_HAS_CARRIER;
391                 break;
392         default:
393                 printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
394         }
395
396         return 0;
397 }
398
399 static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
400 {
401         /* not directly available. */
402         return 0;
403 }
404
405 static int lgdt330x_read_snr(struct dvb_frontend* fe, u16* snr)
406 {
407 #ifdef SNR_IN_DB
408         /*
409          * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise)
410          * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker
411          * respectively. The following tables are built on these formulas.
412          * The usual definition is SNR = 20 log10(signal/noise)
413          * If the specification is wrong the value retuned is 1/2 the actual SNR in db.
414          *
415          * This table is a an ordered list of noise values computed by the
416          * formula from the spec sheet such that the index into the table
417          * starting at 43 or 45 is the SNR value in db. There are duplicate noise
418          * value entries at the beginning because the SNR varies more than
419          * 1 db for a change of 1 digit in noise at very small values of noise.
420          *
421          * Examples from SNR_EQ table:
422          * noise SNR
423          *   0    43
424          *   1    42
425          *   2    39
426          *   3    37
427          *   4    36
428          *   5    35
429          *   6    34
430          *   7    33
431          *   8    33
432          *   9    32
433          *   10   32
434          *   11   31
435          *   12   31
436          *   13   30
437          */
438
439         static const u32 SNR_EQ[] =
440                 { 1,     2,      2,      2, 3,      3,      4,     4,     5,     7,
441                   9,     11,     13,     17, 21,     26,     33,    41,    52,    65,
442                   81,    102,    129,    162, 204,    257,    323,   406,   511,   644,
443                   810,   1020,   1284,   1616, 2035,   2561,   3224,  4059,  5110,  6433,
444                   8098,  10195,  12835,  16158, 20341,  25608,  32238, 40585, 51094, 64323,
445                   80978, 101945, 128341, 161571, 203406, 256073, 0x40000
446                 };
447
448         static const u32 SNR_PH[] =
449                 { 1,     2,      2,      2,      3,      3,     4,     5,     6,     8,
450                   10,    12,     15,     19,     23,     29, 37,    46,    58,    73,
451                   91,    115,    144,    182,    229,    288, 362,   456,   574,   722,
452                   909,   1144,   1440,   1813,   2282,   2873, 3617,  4553,  5732,  7216,
453                   9084,  11436,  14396,  18124,  22817,  28724,  36161, 45524, 57312, 72151,
454                   90833, 114351, 143960, 181235, 228161, 0x040000
455                 };
456
457         static u8 buf[5];/* read data buffer */
458         static u32 noise;   /* noise value */
459         static u32 snr_db;  /* index into SNR_EQ[] */
460         struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
461
462         /* read both equalizer and pase tracker noise data */
463         i2c_selectreadbytes(state, EQPH_ERR0, buf, sizeof(buf));
464
465         if (state->current_modulation == VSB_8) {
466                 /* Equalizer Mean-Square Error Register for VSB */
467                 noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
468
469                 /*
470                  * Look up noise value in table.
471                  * A better search algorithm could be used...
472                  * watch out there are duplicate entries.
473                  */
474                 for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) {
475                         if (noise < SNR_EQ[snr_db]) {
476                                 *snr = 43 - snr_db;
477                                 break;
478                         }
479                 }
480         } else {
481                 /* Phase Tracker Mean-Square Error Register for QAM */
482                 noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
483
484                 /* Look up noise value in table. */
485                 for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) {
486                         if (noise < SNR_PH[snr_db]) {
487                                 *snr = 45 - snr_db;
488                                 break;
489                         }
490                 }
491         }
492 #else
493         /* Return the raw noise value */
494         static u8 buf[5];/* read data buffer */
495         static u32 noise;   /* noise value */
496         struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
497
498         /* read both equalizer and pase tracker noise data */
499         i2c_selectreadbytes(state, EQPH_ERR0, buf, sizeof(buf));
500
501         if (state->current_modulation == VSB_8) {
502                 /* Equalizer Mean-Square Error Register for VSB */
503                 noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
504         } else {
505                 /* Phase Tracker Mean-Square Error Register for QAM */
506                 noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
507         }
508
509         /* Small values for noise mean signal is better so invert noise */
510         /* Noise is 19 bit value so discard 3 LSB*/
511         *snr = ~noise>>3;
512 #endif
513
514         dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
515
516         return 0;
517 }
518
519 static int lgdt330x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
520 {
521         /* I have no idea about this - it may not be needed */
522         fe_tune_settings->min_delay_ms = 500;
523         fe_tune_settings->step_size = 0;
524         fe_tune_settings->max_drift = 0;
525         return 0;
526 }
527
528 static void lgdt330x_release(struct dvb_frontend* fe)
529 {
530         struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
531         kfree(state);
532 }
533
534 static struct dvb_frontend_ops lgdt330x_ops;
535
536 struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
537                                      struct i2c_adapter* i2c)
538 {
539         struct lgdt330x_state* state = NULL;
540         u8 buf[1];
541
542         /* Allocate memory for the internal state */
543         state = (struct lgdt330x_state*) kmalloc(sizeof(struct lgdt330x_state), GFP_KERNEL);
544         if (state == NULL)
545                 goto error;
546         memset(state,0,sizeof(*state));
547
548         /* Setup the state */
549         state->config = config;
550         state->i2c = i2c;
551         memcpy(&state->ops, &lgdt330x_ops, sizeof(struct dvb_frontend_ops));
552         /* Verify communication with demod chip */
553         if (i2c_selectreadbytes(state, 2, buf, 1))
554                 goto error;
555
556         state->current_frequency = -1;
557         state->current_modulation = -1;
558
559         /* Create dvb_frontend */
560         state->frontend.ops = &state->ops;
561         state->frontend.demodulator_priv = state;
562         return &state->frontend;
563
564 error:
565         if (state)
566                 kfree(state);
567         dprintk("%s: ERROR\n",__FUNCTION__);
568         return NULL;
569 }
570
571 static struct dvb_frontend_ops lgdt330x_ops = {
572         .info = {
573                 .name= "LG Electronics lgdt330x VSB/QAM Frontend",
574                 .type = FE_ATSC,
575                 .frequency_min= 54000000,
576                 .frequency_max= 858000000,
577                 .frequency_stepsize= 62500,
578                 /* Symbol rate is for all VSB modes need to check QAM */
579                 .symbol_rate_min    = 10762000,
580                 .symbol_rate_max    = 10762000,
581                 .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
582         },
583         .init                 = lgdt330x_init,
584         .set_frontend         = lgdt330x_set_parameters,
585         .get_frontend         = lgdt330x_get_frontend,
586         .get_tune_settings    = lgdt330x_get_tune_settings,
587         .read_status          = lgdt330x_read_status,
588         .read_ber             = lgdt330x_read_ber,
589         .read_signal_strength = lgdt330x_read_signal_strength,
590         .read_snr             = lgdt330x_read_snr,
591         .read_ucblocks        = lgdt330x_read_ucblocks,
592         .release              = lgdt330x_release,
593 };
594
595 MODULE_DESCRIPTION("lgdt330x [DViCO FusionHDTV 3 Gold] (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver");
596 MODULE_AUTHOR("Wilson Michaels");
597 MODULE_LICENSE("GPL");
598
599 EXPORT_SYMBOL(lgdt330x_attach);
600
601 /*
602  * Local variables:
603  * c-basic-offset: 8
604  * compile-command: "make DVB=1"
605  * End:
606  */