V4L/DVB (5756): Tda9887: store tuning operations in tuner_operations structure
[firefly-linux-kernel-4.4.55.git] / drivers / media / video / tda9887.c
1 #include <linux/module.h>
2 #include <linux/moduleparam.h>
3 #include <linux/kernel.h>
4 #include <linux/i2c.h>
5 #include <linux/types.h>
6 #include <linux/videodev.h>
7 #include <linux/init.h>
8 #include <linux/errno.h>
9 #include <linux/slab.h>
10 #include <linux/delay.h>
11
12 #include <media/v4l2-common.h>
13 #include <media/tuner.h>
14
15
16 /* Chips:
17    TDA9885 (PAL, NTSC)
18    TDA9886 (PAL, SECAM, NTSC)
19    TDA9887 (PAL, SECAM, NTSC, FM Radio)
20
21    Used as part of several tuners
22 */
23
24 #define tda9887_info(fmt, arg...) do {\
25         printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
26                         i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
27 #define tda9887_dbg(fmt, arg...) do {\
28         if (tuner_debug) \
29                 printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
30                         i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
31
32 struct tda9887_priv {
33         unsigned char      data[4];
34 };
35
36 /* ---------------------------------------------------------------------- */
37
38 #define UNSET       (-1U)
39
40 struct tvnorm {
41         v4l2_std_id       std;
42         char              *name;
43         unsigned char     b;
44         unsigned char     c;
45         unsigned char     e;
46 };
47
48 /* ---------------------------------------------------------------------- */
49
50 //
51 // TDA defines
52 //
53
54 //// first reg (b)
55 #define cVideoTrapBypassOFF     0x00    // bit b0
56 #define cVideoTrapBypassON      0x01    // bit b0
57
58 #define cAutoMuteFmInactive     0x00    // bit b1
59 #define cAutoMuteFmActive       0x02    // bit b1
60
61 #define cIntercarrier           0x00    // bit b2
62 #define cQSS                    0x04    // bit b2
63
64 #define cPositiveAmTV           0x00    // bit b3:4
65 #define cFmRadio                0x08    // bit b3:4
66 #define cNegativeFmTV           0x10    // bit b3:4
67
68
69 #define cForcedMuteAudioON      0x20    // bit b5
70 #define cForcedMuteAudioOFF     0x00    // bit b5
71
72 #define cOutputPort1Active      0x00    // bit b6
73 #define cOutputPort1Inactive    0x40    // bit b6
74
75 #define cOutputPort2Active      0x00    // bit b7
76 #define cOutputPort2Inactive    0x80    // bit b7
77
78
79 //// second reg (c)
80 #define cDeemphasisOFF          0x00    // bit c5
81 #define cDeemphasisON           0x20    // bit c5
82
83 #define cDeemphasis75           0x00    // bit c6
84 #define cDeemphasis50           0x40    // bit c6
85
86 #define cAudioGain0             0x00    // bit c7
87 #define cAudioGain6             0x80    // bit c7
88
89 #define cTopMask                0x1f    // bit c0:4
90 #define cTopDefault             0x10    // bit c0:4
91
92 //// third reg (e)
93 #define cAudioIF_4_5             0x00    // bit e0:1
94 #define cAudioIF_5_5             0x01    // bit e0:1
95 #define cAudioIF_6_0             0x02    // bit e0:1
96 #define cAudioIF_6_5             0x03    // bit e0:1
97
98
99 #define cVideoIF_58_75           0x00    // bit e2:4
100 #define cVideoIF_45_75           0x04    // bit e2:4
101 #define cVideoIF_38_90           0x08    // bit e2:4
102 #define cVideoIF_38_00           0x0C    // bit e2:4
103 #define cVideoIF_33_90           0x10    // bit e2:4
104 #define cVideoIF_33_40           0x14    // bit e2:4
105 #define cRadioIF_45_75           0x18    // bit e2:4
106 #define cRadioIF_38_90           0x1C    // bit e2:4
107
108
109 #define cTunerGainNormal         0x00    // bit e5
110 #define cTunerGainLow            0x20    // bit e5
111
112 #define cGating_18               0x00    // bit e6
113 #define cGating_36               0x40    // bit e6
114
115 #define cAgcOutON                0x80    // bit e7
116 #define cAgcOutOFF               0x00    // bit e7
117
118 /* ---------------------------------------------------------------------- */
119
120 static struct tvnorm tvnorms[] = {
121         {
122                 .std   = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
123                 .name  = "PAL-BGHN",
124                 .b     = ( cNegativeFmTV  |
125                            cQSS           ),
126                 .c     = ( cDeemphasisON  |
127                            cDeemphasis50  |
128                            cTopDefault),
129                 .e     = ( cGating_36     |
130                            cAudioIF_5_5   |
131                            cVideoIF_38_90 ),
132         },{
133                 .std   = V4L2_STD_PAL_I,
134                 .name  = "PAL-I",
135                 .b     = ( cNegativeFmTV  |
136                            cQSS           ),
137                 .c     = ( cDeemphasisON  |
138                            cDeemphasis50  |
139                            cTopDefault),
140                 .e     = ( cGating_36     |
141                            cAudioIF_6_0   |
142                            cVideoIF_38_90 ),
143         },{
144                 .std   = V4L2_STD_PAL_DK,
145                 .name  = "PAL-DK",
146                 .b     = ( cNegativeFmTV  |
147                            cQSS           ),
148                 .c     = ( cDeemphasisON  |
149                            cDeemphasis50  |
150                            cTopDefault),
151                 .e     = ( cGating_36     |
152                            cAudioIF_6_5   |
153                            cVideoIF_38_90 ),
154         },{
155                 .std   = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
156                 .name  = "PAL-M/Nc",
157                 .b     = ( cNegativeFmTV  |
158                            cQSS           ),
159                 .c     = ( cDeemphasisON  |
160                            cDeemphasis75  |
161                            cTopDefault),
162                 .e     = ( cGating_36     |
163                            cAudioIF_4_5   |
164                            cVideoIF_45_75 ),
165         },{
166                 .std   = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
167                 .name  = "SECAM-BGH",
168                 .b     = ( cPositiveAmTV  |
169                            cQSS           ),
170                 .c     = ( cTopDefault),
171                 .e     = ( cGating_36     |
172                            cAudioIF_5_5   |
173                            cVideoIF_38_90 ),
174         },{
175                 .std   = V4L2_STD_SECAM_L,
176                 .name  = "SECAM-L",
177                 .b     = ( cPositiveAmTV  |
178                            cQSS           ),
179                 .c     = ( cTopDefault),
180                 .e     = ( cGating_36     |
181                            cAudioIF_6_5   |
182                            cVideoIF_38_90 ),
183         },{
184                 .std   = V4L2_STD_SECAM_LC,
185                 .name  = "SECAM-L'",
186                 .b     = ( cOutputPort2Inactive |
187                            cPositiveAmTV  |
188                            cQSS           ),
189                 .c     = ( cTopDefault),
190                 .e     = ( cGating_36     |
191                            cAudioIF_6_5   |
192                            cVideoIF_33_90 ),
193         },{
194                 .std   = V4L2_STD_SECAM_DK,
195                 .name  = "SECAM-DK",
196                 .b     = ( cNegativeFmTV  |
197                            cQSS           ),
198                 .c     = ( cDeemphasisON  |
199                            cDeemphasis50  |
200                            cTopDefault),
201                 .e     = ( cGating_36     |
202                            cAudioIF_6_5   |
203                            cVideoIF_38_90 ),
204         },{
205                 .std   = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
206                 .name  = "NTSC-M",
207                 .b     = ( cNegativeFmTV  |
208                            cQSS           ),
209                 .c     = ( cDeemphasisON  |
210                            cDeemphasis75  |
211                            cTopDefault),
212                 .e     = ( cGating_36     |
213                            cAudioIF_4_5   |
214                            cVideoIF_45_75 ),
215         },{
216                 .std   = V4L2_STD_NTSC_M_JP,
217                 .name  = "NTSC-M-JP",
218                 .b     = ( cNegativeFmTV  |
219                            cQSS           ),
220                 .c     = ( cDeemphasisON  |
221                            cDeemphasis50  |
222                            cTopDefault),
223                 .e     = ( cGating_36     |
224                            cAudioIF_4_5   |
225                            cVideoIF_58_75 ),
226         }
227 };
228
229 static struct tvnorm radio_stereo = {
230         .name = "Radio Stereo",
231         .b    = ( cFmRadio       |
232                   cQSS           ),
233         .c    = ( cDeemphasisOFF |
234                   cAudioGain6    |
235                   cTopDefault),
236         .e    = ( cTunerGainLow  |
237                   cAudioIF_5_5   |
238                   cRadioIF_38_90 ),
239 };
240
241 static struct tvnorm radio_mono = {
242         .name = "Radio Mono",
243         .b    = ( cFmRadio       |
244                   cQSS           ),
245         .c    = ( cDeemphasisON  |
246                   cDeemphasis75  |
247                   cTopDefault),
248         .e    = ( cTunerGainLow  |
249                   cAudioIF_5_5   |
250                   cRadioIF_38_90 ),
251 };
252
253 /* ---------------------------------------------------------------------- */
254
255 static void dump_read_message(struct tuner *t, unsigned char *buf)
256 {
257         static char *afc[16] = {
258                 "- 12.5 kHz",
259                 "- 37.5 kHz",
260                 "- 62.5 kHz",
261                 "- 87.5 kHz",
262                 "-112.5 kHz",
263                 "-137.5 kHz",
264                 "-162.5 kHz",
265                 "-187.5 kHz [min]",
266                 "+187.5 kHz [max]",
267                 "+162.5 kHz",
268                 "+137.5 kHz",
269                 "+112.5 kHz",
270                 "+ 87.5 kHz",
271                 "+ 62.5 kHz",
272                 "+ 37.5 kHz",
273                 "+ 12.5 kHz",
274         };
275         tda9887_info("read: 0x%2x\n", buf[0]);
276         tda9887_info("  after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
277         tda9887_info("  afc            : %s\n", afc[(buf[0] >> 1) & 0x0f]);
278         tda9887_info("  fmif level     : %s\n", (buf[0] & 0x20) ? "high" : "low");
279         tda9887_info("  afc window     : %s\n", (buf[0] & 0x40) ? "in" : "out");
280         tda9887_info("  vfi level      : %s\n", (buf[0] & 0x80) ? "high" : "low");
281 }
282
283 static void dump_write_message(struct tuner *t, unsigned char *buf)
284 {
285         static char *sound[4] = {
286                 "AM/TV",
287                 "FM/radio",
288                 "FM/TV",
289                 "FM/radio"
290         };
291         static char *adjust[32] = {
292                 "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
293                 "-8",  "-7",  "-6",  "-5",  "-4",  "-3",  "-2",  "-1",
294                 "0",   "+1",  "+2",  "+3",  "+4",  "+5",  "+6",  "+7",
295                 "+8",  "+9",  "+10", "+11", "+12", "+13", "+14", "+15"
296         };
297         static char *deemph[4] = {
298                 "no", "no", "75", "50"
299         };
300         static char *carrier[4] = {
301                 "4.5 MHz",
302                 "5.5 MHz",
303                 "6.0 MHz",
304                 "6.5 MHz / AM"
305         };
306         static char *vif[8] = {
307                 "58.75 MHz",
308                 "45.75 MHz",
309                 "38.9 MHz",
310                 "38.0 MHz",
311                 "33.9 MHz",
312                 "33.4 MHz",
313                 "45.75 MHz + pin13",
314                 "38.9 MHz + pin13",
315         };
316         static char *rif[4] = {
317                 "44 MHz",
318                 "52 MHz",
319                 "52 MHz",
320                 "44 MHz",
321         };
322
323         tda9887_info("write: byte B 0x%02x\n",buf[1]);
324         tda9887_info("  B0   video mode      : %s\n",
325                (buf[1] & 0x01) ? "video trap" : "sound trap");
326         tda9887_info("  B1   auto mute fm    : %s\n",
327                (buf[1] & 0x02) ? "yes" : "no");
328         tda9887_info("  B2   carrier mode    : %s\n",
329                (buf[1] & 0x04) ? "QSS" : "Intercarrier");
330         tda9887_info("  B3-4 tv sound/radio  : %s\n",
331                sound[(buf[1] & 0x18) >> 3]);
332         tda9887_info("  B5   force mute audio: %s\n",
333                (buf[1] & 0x20) ? "yes" : "no");
334         tda9887_info("  B6   output port 1   : %s\n",
335                (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
336         tda9887_info("  B7   output port 2   : %s\n",
337                (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
338
339         tda9887_info("write: byte C 0x%02x\n",buf[2]);
340         tda9887_info("  C0-4 top adjustment  : %s dB\n", adjust[buf[2] & 0x1f]);
341         tda9887_info("  C5-6 de-emphasis     : %s\n", deemph[(buf[2] & 0x60) >> 5]);
342         tda9887_info("  C7   audio gain      : %s\n",
343                (buf[2] & 0x80) ? "-6" : "0");
344
345         tda9887_info("write: byte E 0x%02x\n",buf[3]);
346         tda9887_info("  E0-1 sound carrier   : %s\n",
347                carrier[(buf[3] & 0x03)]);
348         tda9887_info("  E6   l pll gating   : %s\n",
349                (buf[3] & 0x40) ? "36" : "13");
350
351         if (buf[1] & 0x08) {
352                 /* radio */
353                 tda9887_info("  E2-4 video if        : %s\n",
354                        rif[(buf[3] & 0x0c) >> 2]);
355                 tda9887_info("  E7   vif agc output  : %s\n",
356                        (buf[3] & 0x80)
357                        ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
358                        : "fm radio carrier afc");
359         } else {
360                 /* video */
361                 tda9887_info("  E2-4 video if        : %s\n",
362                        vif[(buf[3] & 0x1c) >> 2]);
363                 tda9887_info("  E5   tuner gain      : %s\n",
364                        (buf[3] & 0x80)
365                        ? ((buf[3] & 0x20) ? "external" : "normal")
366                        : ((buf[3] & 0x20) ? "minimum"  : "normal"));
367                 tda9887_info("  E7   vif agc output  : %s\n",
368                        (buf[3] & 0x80)
369                        ? ((buf[3] & 0x20)
370                           ? "pin3 port, pin22 vif agc out"
371                           : "pin22 port, pin3 vif acg ext in")
372                        : "pin3+pin22 port");
373         }
374         tda9887_info("--\n");
375 }
376
377 /* ---------------------------------------------------------------------- */
378
379 static int tda9887_set_tvnorm(struct tuner *t, char *buf)
380 {
381         struct tvnorm *norm = NULL;
382         int i;
383
384         if (t->mode == V4L2_TUNER_RADIO) {
385                 if (t->audmode == V4L2_TUNER_MODE_MONO)
386                         norm = &radio_mono;
387                 else
388                         norm = &radio_stereo;
389         } else {
390                 for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
391                         if (tvnorms[i].std & t->std) {
392                                 norm = tvnorms+i;
393                                 break;
394                         }
395                 }
396         }
397         if (NULL == norm) {
398                 tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
399                 return -1;
400         }
401
402         tda9887_dbg("configure for: %s\n",norm->name);
403         buf[1] = norm->b;
404         buf[2] = norm->c;
405         buf[3] = norm->e;
406         return 0;
407 }
408
409 static unsigned int port1  = UNSET;
410 static unsigned int port2  = UNSET;
411 static unsigned int qss    = UNSET;
412 static unsigned int adjust = UNSET;
413
414 module_param(port1, int, 0644);
415 module_param(port2, int, 0644);
416 module_param(qss, int, 0644);
417 module_param(adjust, int, 0644);
418
419 static int tda9887_set_insmod(struct tuner *t, char *buf)
420 {
421         if (UNSET != port1) {
422                 if (port1)
423                         buf[1] |= cOutputPort1Inactive;
424                 else
425                         buf[1] &= ~cOutputPort1Inactive;
426         }
427         if (UNSET != port2) {
428                 if (port2)
429                         buf[1] |= cOutputPort2Inactive;
430                 else
431                         buf[1] &= ~cOutputPort2Inactive;
432         }
433
434         if (UNSET != qss) {
435                 if (qss)
436                         buf[1] |= cQSS;
437                 else
438                         buf[1] &= ~cQSS;
439         }
440
441         if (adjust >= 0x00 && adjust < 0x20) {
442                 buf[2] &= ~cTopMask;
443                 buf[2] |= adjust;
444         }
445         return 0;
446 }
447
448 static int tda9887_set_config(struct tuner *t, char *buf)
449 {
450         if (t->tda9887_config & TDA9887_PORT1_ACTIVE)
451                 buf[1] &= ~cOutputPort1Inactive;
452         if (t->tda9887_config & TDA9887_PORT1_INACTIVE)
453                 buf[1] |= cOutputPort1Inactive;
454         if (t->tda9887_config & TDA9887_PORT2_ACTIVE)
455                 buf[1] &= ~cOutputPort2Inactive;
456         if (t->tda9887_config & TDA9887_PORT2_INACTIVE)
457                 buf[1] |= cOutputPort2Inactive;
458
459         if (t->tda9887_config & TDA9887_QSS)
460                 buf[1] |= cQSS;
461         if (t->tda9887_config & TDA9887_INTERCARRIER)
462                 buf[1] &= ~cQSS;
463
464         if (t->tda9887_config & TDA9887_AUTOMUTE)
465                 buf[1] |= cAutoMuteFmActive;
466         if (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
467                 buf[2] &= ~0x60;
468                 switch (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
469                 case TDA9887_DEEMPHASIS_NONE:
470                         buf[2] |= cDeemphasisOFF;
471                         break;
472                 case TDA9887_DEEMPHASIS_50:
473                         buf[2] |= cDeemphasisON | cDeemphasis50;
474                         break;
475                 case TDA9887_DEEMPHASIS_75:
476                         buf[2] |= cDeemphasisON | cDeemphasis75;
477                         break;
478                 }
479         }
480         if (t->tda9887_config & TDA9887_TOP_SET) {
481                 buf[2] &= ~cTopMask;
482                 buf[2] |= (t->tda9887_config >> 8) & cTopMask;
483         }
484         if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
485                 buf[1] &= ~cQSS;
486         if (t->tda9887_config & TDA9887_GATING_18)
487                 buf[3] &= ~cGating_36;
488
489         if (t->tda9887_config & TDA9887_GAIN_NORMAL) {
490                 radio_stereo.e &= ~cTunerGainLow;
491                 radio_mono.e &= ~cTunerGainLow;
492         }
493
494         return 0;
495 }
496
497 /* ---------------------------------------------------------------------- */
498
499 static int tda9887_status(struct tuner *t)
500 {
501         unsigned char buf[1];
502         int rc;
503
504         memset(buf,0,sizeof(buf));
505         if (1 != (rc = i2c_master_recv(&t->i2c,buf,1)))
506                 tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
507         dump_read_message(t, buf);
508         return 0;
509 }
510
511 static void tda9887_configure(struct i2c_client *client)
512 {
513         struct tuner *t = i2c_get_clientdata(client);
514         struct tda9887_priv *priv = t->priv;
515         int rc;
516
517         memset(priv->data,0,sizeof(priv->data));
518         tda9887_set_tvnorm(t,priv->data);
519
520         /* A note on the port settings:
521            These settings tend to depend on the specifics of the board.
522            By default they are set to inactive (bit value 1) by this driver,
523            overwriting any changes made by the tvnorm. This means that it
524            is the responsibility of the module using the tda9887 to set
525            these values in case of changes in the tvnorm.
526            In many cases port 2 should be made active (0) when selecting
527            SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
528
529            For the other standards the tda9887 application note says that
530            the ports should be set to active (0), but, again, that may
531            differ depending on the precise hardware configuration.
532          */
533         priv->data[1] |= cOutputPort1Inactive;
534         priv->data[1] |= cOutputPort2Inactive;
535
536         tda9887_set_config(t,priv->data);
537         tda9887_set_insmod(t,priv->data);
538
539         if (t->mode == T_STANDBY) {
540                 priv->data[1] |= cForcedMuteAudioON;
541         }
542
543         tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
544                 priv->data[1],priv->data[2],priv->data[3]);
545         if (tuner_debug > 1)
546                 dump_write_message(t, priv->data);
547
548         if (4 != (rc = i2c_master_send(&t->i2c,priv->data,4)))
549                 tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
550
551         if (tuner_debug > 2) {
552                 msleep_interruptible(1000);
553                 tda9887_status(t);
554         }
555 }
556
557 /* ---------------------------------------------------------------------- */
558
559 static void tda9887_tuner_status(struct i2c_client *client)
560 {
561         struct tuner *t = i2c_get_clientdata(client);
562         struct tda9887_priv *priv = t->priv;
563         tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", priv->data[1], priv->data[2], priv->data[3]);
564 }
565
566 static int tda9887_get_afc(struct i2c_client *client)
567 {
568         struct tuner *t = i2c_get_clientdata(client);
569         static int AFC_BITS_2_kHz[] = {
570                 -12500,  -37500,  -62500,  -97500,
571                 -112500, -137500, -162500, -187500,
572                 187500,  162500,  137500,  112500,
573                 97500 ,  62500,   37500 ,  12500
574         };
575         int afc=0;
576         __u8 reg = 0;
577
578         if (1 == i2c_master_recv(&t->i2c,&reg,1))
579                 afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
580
581         return afc;
582 }
583
584 static void tda9887_standby(struct i2c_client *client)
585 {
586         tda9887_configure(client);
587 }
588
589 static void tda9887_set_freq(struct i2c_client *client, unsigned int freq)
590 {
591         tda9887_configure(client);
592 }
593
594 static void tda9887_release(struct i2c_client *c)
595 {
596         struct tuner *t = i2c_get_clientdata(c);
597
598         kfree(t->priv);
599         t->priv = NULL;
600 }
601
602 static struct tuner_operations tda9887_tuner_ops = {
603         .set_tv_freq    = tda9887_set_freq,
604         .set_radio_freq = tda9887_set_freq,
605         .standby        = tda9887_standby,
606         .tuner_status   = tda9887_tuner_status,
607         .get_afc        = tda9887_get_afc,
608         .release        = tda9887_release,
609 };
610
611 int tda9887_tuner_init(struct i2c_client *c)
612 {
613         struct tda9887_priv *priv = NULL;
614         struct tuner *t = i2c_get_clientdata(c);
615
616         priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
617         if (priv == NULL)
618                 return -ENOMEM;
619         t->priv = priv;
620
621         strlcpy(c->name, "tda9887", sizeof(c->name));
622
623         tda9887_info("tda988[5/6/7] found @ 0x%x (%s)\n", t->i2c.addr,
624                                                 t->i2c.driver->driver.name);
625
626         memcpy(&t->ops, &tda9887_tuner_ops, sizeof(struct tuner_operations));
627
628         return 0;
629 }
630
631 /*
632  * Overrides for Emacs so that we follow Linus's tabbing style.
633  * ---------------------------------------------------------------------------
634  * Local variables:
635  * c-basic-offset: 8
636  * End:
637  */