intel_infoframes: add VLV support
[platform/upstream/intel-gpu-tools.git] / tools / intel_infoframes.c
1 /*
2  * Copyright © 2012 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *      Paulo Zanoni <paulo.r.zanoni@intel.com>
25  *
26  */
27
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <getopt.h>
33 #include "intel_io.h"
34 #include "intel_chipset.h"
35 #include "drmtest.h"
36
37 typedef enum {
38         TRANSC_A = 0,
39         TRANSC_B = 1,
40         TRANSC_C = 2,
41         TRANSC_INVALID
42 } Transcoder;
43
44 typedef enum {
45         REG_HDMIB_GEN4    = 0x61140,
46         REG_HDMIC_GEN4    = 0x61160,
47         REG_HDMIB_VLV     = 0x1e1140,
48         REG_HDMIC_VLV     = 0x1e1160,
49         REG_HDMIB_PCH     = 0xe1140,
50         REG_HDMIC_PCH     = 0xe1150,
51         REG_HDMID_PCH     = 0xe1160,
52         REG_DIP_CTL_GEN4  = 0x61170,
53         REG_DIP_CTL_A_VLV   = 0x1e0200,
54         REG_DIP_CTL_B_VLV   = 0x1e1170,
55         REG_DIP_CTL_A     = 0xe0200,
56         REG_DIP_CTL_B     = 0xe1200,
57         REG_DIP_CTL_C     = 0xe2200,
58         REG_DIP_DATA_GEN4 = 0x61178,
59         REG_DIP_DATA_A_VLV  = 0x1e0208,
60         REG_DIP_DATA_B_VLV  = 0x1e1174,
61         REG_DIP_DATA_A    = 0xe0208,
62         REG_DIP_DATA_B    = 0xe1208,
63         REG_DIP_DATA_C    = 0xe2208,
64 } Register;
65
66 typedef enum {
67         DIP_AVI    = 0,
68         DIP_VENDOR = 1,
69         DIP_GAMUT  = 2,
70         DIP_SPD    = 3,
71         DIP_INVALID,
72 } DipType;
73
74 typedef enum {
75         DIP_FREQ_ONCE              = 0,
76         DIP_FREQ_EVERY_VSYNC       = 1,
77         DIP_FREQ_EVERY_OTHER_VSYNC = 2,
78         DIP_FREQ_RESERVED          = 3,
79 } DipFrequency;
80
81 typedef enum {
82         SOURCE_DEVICE_UNKNOWN           = 0x00,
83         SOURCE_DEVICE_DIGITAL_STB       = 0x01,
84         SOURCE_DEVICE_DVD_PLAYER        = 0x02,
85         SOURCE_DEVICE_D_VHS             = 0x03,
86         SOURCE_DEVICE_HDD_VIDEORECORDER = 0x04,
87         SOURCE_DEVICE_DVC               = 0x05,
88         SOURCE_DEVICE_DSC               = 0x06,
89         SOURCE_DEVICE_VIDEO_CD          = 0x07,
90         SOURCE_DEVICE_GAME              = 0x08,
91         SOURCE_DEVICE_PC_GENERAL        = 0x09,
92         SOURCE_DEVICE_BLU_RAY_DISK      = 0x0a,
93         SOURCE_DEVICE_SUPER_AUDIO_CD    = 0x0b,
94         SOURCE_DEVICE_RESERVED          = 0x0c
95 } SourceDevice;
96
97 #define HDMI_PORT_ENABLE          (1 << 31)
98 #define HDMI_PORT_TRANSCODER_GEN4 (1 << 30)
99 #define HDMI_PORT_TRANSCODER_IBX  (1 << 30)
100 #define HDMI_PORT_TRANSCODER_CPT  (3 << 29)
101 #define HDMI_PORT_ENCODING        (3 << 10)
102 #define HDMI_PORT_MODE            (1 << 9)
103 #define HDMI_PORT_AUDIO           (1 << 6)
104 #define HDMI_PORT_DETECTED        (1 << 2)
105
106 #define DIP_CTL_ENABLE           (1 << 31)
107 #define DIP_CTL_GCP_ENABLE       (1 << 25)
108 #define DIP_CTL_SPD_ENABLE       (1 << 24)
109 #define DIP_CTL_GAMUT_ENABLE     (1 << 23)
110 #define DIP_CTL_VENDOR_ENABLE    (1 << 22)
111 #define DIP_CTL_AVI_ENABLE       (1 << 21)
112 #define DIP_CTL_BUFFER_INDEX     (3 << 19)
113 #define DIP_CTL_BUFFER_AVI       (0 << 19)
114 #define DIP_CTL_BUFFER_VENDOR    (1 << 19)
115 #define DIP_CTL_BUFFER_GAMUT     (2 << 19)
116 #define DIP_CTL_BUFFER_SPD       (3 << 19)
117 #define DIP_CTL_FREQUENCY        (3 << 16)
118 #define DIP_CTL_FREQ_ONCE        (0 << 16)
119 #define DIP_CTL_FREQ_EVERY       (1 << 16)
120 #define DIP_CTL_FREQ_EVERY_OTHER (2 << 16)
121 #define DIP_CTL_BUFFER_SIZE      (15 << 8)
122 #define DIP_CTL_ACCESS_ADDR      (15 << 0)
123
124 #define DIP_CTL_PORT_SEL_MASK_GEN4       (3 << 29)
125 #define DIP_CTL_PORT_SEL_B_GEN4          (1 << 29)
126 #define DIP_CTL_PORT_SEL_C_GEN4          (2 << 29)
127 #define DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 (1 << 28)
128
129 #define AVI_INFOFRAME_TYPE    0x82
130 #define AVI_INFOFRAME_VERSION 0x02
131 #define AVI_INFOFRAME_LENGTH  0x0d
132 #define SPD_INFOFRAME_TYPE    0x83
133 #define SPD_INFOFRAME_VERSION 0x01
134 #define SPD_INFOFRAME_LENGTH  0x19
135
136 #define VENDOR_ID_HDMI  0x000c03
137
138 typedef struct {
139         uint8_t type;
140         uint8_t version;
141         uint8_t length;
142         uint8_t ecc;
143 } DipInfoFrameHeader;
144
145 typedef union {
146         struct {
147                 DipInfoFrameHeader header;
148                 uint8_t checksum;
149
150                 uint8_t S     :2;
151                 uint8_t B     :2;
152                 uint8_t A     :1;
153                 uint8_t Y     :2;
154                 uint8_t Rsvd0 :1;
155
156                 uint8_t R :4;
157                 uint8_t M :2;
158                 uint8_t C :2;
159
160                 uint8_t SC  :2;
161                 uint8_t Q   :2;
162                 uint8_t EC  :3;
163                 uint8_t ITC :1;
164
165                 uint8_t VIC   :7;
166                 uint8_t Rsvd1 :1;
167
168                 uint8_t PR    :4;
169                 uint8_t Rsvd2 :4;
170
171                 uint16_t top;
172                 uint16_t bottom;
173                 uint16_t left;
174                 uint16_t right;
175
176                 uint16_t Rsvd3;
177                 uint32_t Rsvd4[3];
178         } avi;
179         struct {
180                 DipInfoFrameHeader header;
181                 uint8_t checksum;
182                 uint8_t vendor[8];
183                 uint8_t description[16];
184                 uint8_t source;
185         } __attribute__((packed)) spd;
186         struct {
187                 DipInfoFrameHeader header;
188                 uint8_t checksum;
189
190                 uint8_t id[3];
191
192                 uint8_t Rsvd0        :5;
193                 uint8_t video_format :3;
194
195                 union {
196                         uint8_t vic;
197                         struct {
198                                 uint8_t Rsvd1         :4;
199                                 uint8_t s3d_structure :4;
200                         } s3d;
201                 } pb5;
202
203                 uint8_t Rsvd2        :4;
204                 uint8_t s3d_ext_data :4;
205         } __attribute__((packed)) vendor;
206         struct {
207                 DipInfoFrameHeader header;
208                 uint8_t body[27];
209         } generic;
210         uint8_t data8[128];
211         uint32_t data32[16];
212 } DipInfoFrame;
213
214 Register vlv_hdmi_ports[] = {
215         REG_HDMIB_VLV,
216         REG_HDMIC_VLV,
217 };
218
219 Register vlv_dip_ctl_regs[] = {
220         REG_DIP_CTL_A_VLV,
221         REG_DIP_CTL_B_VLV,
222 };
223
224 Register vlv_dip_data_regs[] = {
225         REG_DIP_DATA_A_VLV,
226         REG_DIP_DATA_B_VLV,
227 };
228
229 Register gen4_hdmi_ports[] = {
230         REG_HDMIB_GEN4,
231         REG_HDMIC_GEN4,
232 };
233 Register pch_hdmi_ports[] = {
234         REG_HDMIB_PCH,
235         REG_HDMIC_PCH,
236         REG_HDMID_PCH
237 };
238 Register pch_dip_ctl_regs[] = {
239         REG_DIP_CTL_A,
240         REG_DIP_CTL_B,
241         REG_DIP_CTL_C
242 };
243 Register pch_dip_data_regs[] = {
244         REG_DIP_DATA_A,
245         REG_DIP_DATA_B,
246         REG_DIP_DATA_C
247 };
248 const char *hdmi_port_names[] = {
249         "HDMIB",
250         "HDMIC",
251         "HDMID"
252 };
253 const char *transcoder_names[] = {
254         "A",
255         "B",
256         "C"
257 };
258 const char *dip_frequency_names[] = {
259         "once",
260         "every vsync",
261         "every other vsync",
262         "reserved (invalid)"
263 };
264
265 struct pci_device *pci_dev;
266 int gen = 0;
267
268 static const char *spd_source_to_string(SourceDevice source)
269 {
270         switch (source) {
271         case SOURCE_DEVICE_UNKNOWN:
272                 return "unknown";
273         case SOURCE_DEVICE_DIGITAL_STB:
274                 return "digital stb";
275         case SOURCE_DEVICE_DVD_PLAYER:
276                 return "dvd player";
277         case SOURCE_DEVICE_D_VHS:
278                 return "d vhs";
279         case SOURCE_DEVICE_HDD_VIDEORECORDER:
280                 return "hdd videorecorder";
281         case SOURCE_DEVICE_DVC:
282                 return "dvc";
283         case SOURCE_DEVICE_DSC:
284                 return "dsc";
285         case SOURCE_DEVICE_VIDEO_CD:
286                 return "video cd";
287         case SOURCE_DEVICE_GAME:
288                 return "game";
289         case SOURCE_DEVICE_PC_GENERAL:
290                 return "pc general";
291         case SOURCE_DEVICE_BLU_RAY_DISK:
292                 return "blu-ray disk";
293         case SOURCE_DEVICE_SUPER_AUDIO_CD:
294                 return "super audio cd";
295         default:
296                 return "reserved";
297         }
298 }
299
300 static Register get_dip_ctl_reg(Transcoder transcoder)
301 {
302         if (IS_VALLEYVIEW(pci_dev->device_id))
303                 return vlv_dip_ctl_regs[transcoder];
304         else if (gen == 4)
305                 return REG_DIP_CTL_GEN4;
306         else
307                 return pch_dip_ctl_regs[transcoder];
308 }
309
310 static Register get_dip_data_reg(Transcoder transcoder)
311 {
312         if (IS_VALLEYVIEW(pci_dev->device_id))
313                 return vlv_dip_data_regs[transcoder];
314         else if (gen == 4)
315                 return REG_DIP_DATA_GEN4;
316         else
317                 return pch_dip_data_regs[transcoder];
318 }
319
320 static Register get_hdmi_port(int hdmi_port_index)
321 {
322         if (IS_VALLEYVIEW(pci_dev->device_id))
323                 return vlv_hdmi_ports[hdmi_port_index];
324         else if (gen == 4) {
325                 assert(hdmi_port_index < 2);
326                 return gen4_hdmi_ports[hdmi_port_index];
327         } else {
328                 return pch_hdmi_ports[hdmi_port_index];
329         }
330 }
331
332 static void load_infoframe(Transcoder transcoder, DipInfoFrame *frame,
333                            DipType type)
334 {
335         Register ctl_reg = get_dip_ctl_reg(transcoder);
336         Register data_reg = get_dip_data_reg(transcoder);
337         uint32_t ctl_val;
338         uint32_t i;
339
340         ctl_val = INREG(ctl_reg);
341
342         ctl_val &= ~DIP_CTL_BUFFER_INDEX;
343         ctl_val |= type << 19;
344         OUTREG(ctl_reg, ctl_val);
345         ctl_val = INREG(ctl_reg);
346
347         ctl_val &= ~DIP_CTL_ACCESS_ADDR;
348         OUTREG(ctl_reg, ctl_val);
349
350         for (i = 0; i < 16; i++) {
351                 ctl_val = INREG(ctl_reg);
352                 assert((ctl_val & DIP_CTL_ACCESS_ADDR) == i);
353                 frame->data32[i] = INREG(data_reg);
354         }
355 }
356
357 static int infoframe_valid_checksum(DipInfoFrame *frame)
358 {
359         int i;
360         int length = frame->generic.header.length;
361         uint8_t csum;
362
363         csum = frame->generic.header.type + frame->generic.header.version +
364                frame->generic.header.length; /* no ecc */
365         for (i = 0; i < length + 1; i++)
366                 csum += frame->generic.body[i];
367
368         return (csum == 0);
369 }
370
371 static void infoframe_fix_checksum(DipInfoFrame *frame)
372 {
373         int i;
374         int length = frame->generic.header.length;
375         uint8_t csum;
376
377         csum = frame->generic.header.type + frame->generic.header.version +
378                frame->generic.header.length; /* no ecc */
379         /* Length does not include the header field nor the checksum */
380         for (i = 1; i < length + 1; i++)
381                 csum += frame->generic.body[i];
382         frame->generic.body[0] = 0x100 - csum;
383 }
384
385 static void dump_port_info(int hdmi_port_index)
386 {
387         Register port = get_hdmi_port(hdmi_port_index);
388         uint32_t val = INREG(port);
389         Transcoder transcoder;
390
391         printf("\nPort %s:\n", hdmi_port_names[hdmi_port_index]);
392         printf("- %sdetected\n", val & HDMI_PORT_DETECTED ? "" : "not ");
393         printf("- %s\n", val & HDMI_PORT_ENABLE ? "enabled" : "disabled");
394
395         if (!(val & HDMI_PORT_ENABLE))
396                 return;
397
398         if (gen == 4 || IS_VALLEYVIEW(pci_dev->device_id))
399                 transcoder = (val & HDMI_PORT_TRANSCODER_GEN4) >> 30;
400         else if (intel_pch >= PCH_CPT)
401                 transcoder = (val & HDMI_PORT_TRANSCODER_CPT) >> 29;
402         else
403                 transcoder = (val & HDMI_PORT_TRANSCODER_IBX) >> 30;
404         printf("- transcoder: %s\n", transcoder_names[transcoder]);
405
406         switch ((val & HDMI_PORT_ENCODING) >> 10) {
407         case 0:
408                 printf("- mode: SDVO\n");
409                 break;
410         case 2:
411                 printf("- mode: TMDS\n");
412                 break;
413         default:
414                 printf("- mode: INVALID!\n");
415         }
416
417         printf("- mode: %s\n", val & HDMI_PORT_MODE ? "HDMI" : "DVI");
418         printf("- audio: %s\n", val & HDMI_PORT_AUDIO ? "enabled" : "disabled");
419 }
420
421 static void dump_raw_infoframe(DipInfoFrame *frame)
422 {
423         unsigned int i;
424         printf("- raw:");
425         for (i = 0; i < 16; i++) {
426                 if (i % 4 == 0)
427                         printf("\n ");
428                 printf(" %08x", frame->data32[i]);
429         }
430         printf("\n");
431 }
432
433 static void dump_avi_info(Transcoder transcoder)
434 {
435         Register reg = get_dip_ctl_reg(transcoder);
436         uint32_t val;
437         DipFrequency freq;
438         DipInfoFrame frame;
439
440         load_infoframe(transcoder, &frame, DIP_AVI);
441         val = INREG(reg);
442
443         printf("AVI InfoFrame:\n");
444
445         if (gen == 4 || IS_VALLEYVIEW(pci_dev->device_id)) {
446                 printf("- %sbeing transmitted\n",
447                        val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not ");
448         }
449
450         freq = (val & DIP_CTL_FREQUENCY) >> 16;
451         printf("- frequency: %s\n", dip_frequency_names[freq]);
452
453         dump_raw_infoframe(&frame);
454
455         printf("- type: %x, version: %x, length: %x, ecc: %x, checksum: %x\n",
456                frame.avi.header.type, frame.avi.header.version,
457                frame.avi.header.length, frame.avi.header.ecc,
458                frame.avi.checksum);
459         printf("- S: %x, B: %x, A: %x, Y: %x, Rsvd0: %x\n",
460                frame.avi.S, frame.avi.B, frame.avi.A, frame.avi.Y,
461                frame.avi.Rsvd0);
462         printf("- R: %x, M: %x, C: %x\n",
463                frame.avi.R, frame.avi.M, frame.avi.C);
464         printf("- SC: %x, Q: %x, EC: %x, ITC: %x\n",
465                frame.avi.SC, frame.avi.Q, frame.avi.EC, frame.avi.ITC);
466         printf("- VIC: %d, Rsvd1: %x\n", frame.avi.VIC, frame.avi.Rsvd1);
467         printf("- PR: %x, Rsvd2: %x\n", frame.avi.PR, frame.avi.Rsvd2);
468         printf("- top: %x, bottom: %x, left: %x, right: %x\n",
469                frame.avi.top, frame.avi.bottom, frame.avi.left,
470                frame.avi.right);
471         printf("- Rsvd3: %x, Rsvd4[0]: %x, Rsvd4[1]: %x, Rsvd4[2]: %x\n",
472                frame.avi.Rsvd3, frame.avi.Rsvd4[0], frame.avi.Rsvd4[1],
473                frame.avi.Rsvd4[2]);
474
475         if (!infoframe_valid_checksum(&frame))
476                 printf("Invalid InfoFrame checksum!\n");
477 }
478
479 static const char *vendor_id_to_string(uint32_t id)
480 {
481         switch (id) {
482         case VENDOR_ID_HDMI:
483                 return "HDMI";
484         default:
485                 return "Unknown";
486         }
487 }
488
489 static const char *s3d_structure_to_string(int format)
490 {
491         switch (format) {
492         case 0:
493                 return "Frame Packing";
494         case 6:
495                 return "Top Bottom";
496         case 8:
497                 return "Side By Side (half)";
498         default:
499                 return "Reserved";
500         }
501 }
502
503 static void dump_vendor_hdmi(DipInfoFrame *frame)
504 {
505         int vic_present = frame->vendor.video_format & 0x1;
506         int s3d_present = frame->vendor.video_format & 0x2;
507
508         printf("- video format: 0x%03x %s\n", frame->vendor.video_format,
509                s3d_present ? "(3D)" : "");
510
511         if (vic_present && s3d_present) {
512                 printf("Error: HDMI VIC and S3D bits set. Only one of those "
513                        " at a time is valid\n");
514                 return;
515         }
516
517         if (vic_present)
518                 printf("- HDMI VIC: %d\n", frame->vendor.pb5.vic);
519         else if (s3d_present) {
520                 int s3d_structure = frame->vendor.pb5.s3d.s3d_structure;
521
522                 printf("- 3D Format: %s\n",
523                        s3d_structure_to_string(s3d_structure));
524
525                 /* Side-by-side (half) */
526                 if (s3d_structure >= 8)
527                         printf("- 3D Ext Data 0x%x\n",
528                                frame->vendor.s3d_ext_data);
529         }
530 }
531
532 static void dump_vendor_info(Transcoder transcoder)
533 {
534         Register reg = get_dip_ctl_reg(transcoder);
535         uint32_t val, vendor_id;
536         DipFrequency freq;
537         DipInfoFrame frame;
538
539         load_infoframe(transcoder, &frame, DIP_VENDOR);
540         val = INREG(reg);
541
542         printf("Vendor InfoFrame:\n");
543
544         if (gen == 4 || IS_VALLEYVIEW(pci_dev->device_id)) {
545                 printf("- %sbeing transmitted\n",
546                        val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not ");
547         }
548
549         freq = (val & DIP_CTL_FREQUENCY) >> 16;
550         printf("- frequency: %s\n", dip_frequency_names[freq]);
551
552         dump_raw_infoframe(&frame);
553
554         vendor_id = frame.vendor.id[2] << 16 | frame.vendor.id[1] << 8 |
555                     frame.vendor.id[0];
556
557         printf("- vendor Id: 0x%06x (%s)\n", vendor_id,
558                vendor_id_to_string(vendor_id));
559
560         if (vendor_id == VENDOR_ID_HDMI)
561                 dump_vendor_hdmi(&frame);
562
563         if (!infoframe_valid_checksum(&frame))
564                 printf("Invalid InfoFrame checksum!\n");
565 }
566
567 static void dump_gamut_info(Transcoder transcoder)
568 {
569         Register reg = get_dip_ctl_reg(transcoder);
570         uint32_t val;
571         DipFrequency freq;
572         DipInfoFrame frame;
573
574         load_infoframe(transcoder, &frame, DIP_GAMUT);
575         val = INREG(reg);
576
577         printf("Gamut InfoFrame:\n");
578
579         if (gen == 4 || IS_VALLEYVIEW(pci_dev->device_id)) {
580                 printf("- %sbeing transmitted\n",
581                        val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not ");
582         }
583
584         freq = (val & DIP_CTL_FREQUENCY) >> 16;
585         printf("- frequency: %s\n", dip_frequency_names[freq]);
586
587         dump_raw_infoframe(&frame);
588
589         if (!infoframe_valid_checksum(&frame))
590                 printf("Invalid InfoFrame checksum!\n");
591 }
592
593 static void dump_spd_info(Transcoder transcoder)
594 {
595         Register reg = get_dip_ctl_reg(transcoder);
596         uint32_t val;
597         DipFrequency freq;
598         DipInfoFrame frame;
599         char vendor[9];
600         char description[17];
601
602         load_infoframe(transcoder, &frame, DIP_SPD);
603         val = INREG(reg);
604
605         printf("SPD InfoFrame:\n");
606
607         if (gen == 4 || IS_VALLEYVIEW(pci_dev->device_id)) {
608                 printf("- %sbeing transmitted\n",
609                        val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not ");
610         }
611
612         freq = (val & DIP_CTL_FREQUENCY) >> 16;
613         printf("- frequency: %s\n", dip_frequency_names[freq]);
614
615         dump_raw_infoframe(&frame);
616
617         printf("- type: %x, version: %x, length: %x, ecc: %x, checksum: %x\n",
618                frame.spd.header.type, frame.spd.header.version,
619                frame.spd.header.length, frame.spd.header.ecc,
620                frame.spd.checksum);
621
622         memcpy(vendor, frame.spd.vendor, 8);
623         vendor[8] = '\0';
624         memcpy(description, frame.spd.description, 16);
625         description[16] = '\0';
626
627         printf("- vendor: %s\n", vendor);
628         printf("- description: %s\n", description);
629         printf("- source: %s\n", spd_source_to_string(frame.spd.source));
630
631         if (!infoframe_valid_checksum(&frame))
632                 printf("Invalid InfoFrame checksum!\n");
633 }
634
635 static void dump_transcoder_info(Transcoder transcoder)
636 {
637         Register reg = get_dip_ctl_reg(transcoder);
638         uint32_t val = INREG(reg);
639
640         if (gen == 4) {
641                 printf("\nDIP information:\n");
642                 switch (val & DIP_CTL_PORT_SEL_MASK_GEN4) {
643                 case DIP_CTL_PORT_SEL_B_GEN4:
644                         printf("- port B\n");
645                         break;
646                 case DIP_CTL_PORT_SEL_C_GEN4:
647                         printf("- port C\n");
648                         break;
649                 default:
650                         printf("- INVALID port!\n");
651                 }
652         } else {
653                 printf("\nTranscoder %s:\n", transcoder_names[transcoder]);
654         }
655         printf("- %s\n", val & DIP_CTL_ENABLE ? "enabled" : "disabled");
656         if (!(val & DIP_CTL_ENABLE))
657                 return;
658
659         printf("- GCP: %s\n", val & DIP_CTL_GCP_ENABLE ?
660                "enabled" : "disabled");
661
662         if (val & DIP_CTL_AVI_ENABLE)
663                 dump_avi_info(transcoder);
664         if (val & DIP_CTL_VENDOR_ENABLE)
665                 dump_vendor_info(transcoder);
666         if (val & DIP_CTL_GAMUT_ENABLE)
667                 dump_gamut_info(transcoder);
668         if (val & DIP_CTL_SPD_ENABLE)
669                 dump_spd_info(transcoder);
670 }
671
672 static void dump_all_info(void)
673 {
674         unsigned int i;
675
676         if (IS_VALLEYVIEW(pci_dev->device_id)) {
677                 for (i = 0; i < ARRAY_SIZE(vlv_hdmi_ports); i++)
678                         dump_port_info(i);
679                 for (i = 0; i < ARRAY_SIZE(vlv_dip_ctl_regs); i++)
680                         dump_transcoder_info(i);
681         } else if (gen == 4) {
682                 for (i = 0; i < ARRAY_SIZE(gen4_hdmi_ports); i++)
683                         dump_port_info(i);
684                 dump_transcoder_info(0);
685         } else {
686                 for (i = 0; i < ARRAY_SIZE(pch_hdmi_ports); i++)
687                         dump_port_info(i);
688                 for (i = 0; i < ARRAY_SIZE(pch_dip_ctl_regs); i++)
689                         dump_transcoder_info(i);
690         }
691 }
692
693 static void write_infoframe(Transcoder transcoder, DipType type,
694                             DipInfoFrame *frame)
695 {
696         Register ctl_reg = get_dip_ctl_reg(transcoder);
697         Register data_reg = get_dip_data_reg(transcoder);
698         uint32_t ctl_val;
699         unsigned int i;
700
701         ctl_val = INREG(ctl_reg);
702         ctl_val &= ~DIP_CTL_BUFFER_INDEX;
703         ctl_val |= (type << 19);
704         ctl_val &= ~DIP_CTL_ACCESS_ADDR;
705         OUTREG(ctl_reg, ctl_val);
706
707         for (i = 0; i < 8; i++) {
708                 ctl_val = INREG(ctl_reg);
709                 assert((ctl_val & DIP_CTL_ACCESS_ADDR) == i);
710                 OUTREG(data_reg, frame->data32[i]);
711         }
712 }
713
714 static void disable_infoframe(Transcoder transcoder, DipType type)
715 {
716         Register reg = get_dip_ctl_reg(transcoder);
717         uint32_t val = INREG(reg);
718         if (gen != 4 && type == DIP_AVI)
719                 val &= ~DIP_CTL_ENABLE;
720         val &= ~(1 << (21 + type));
721         OUTREG(reg, val);
722 }
723
724 static void enable_infoframe(Transcoder transcoder, DipType type)
725 {
726         Register reg = get_dip_ctl_reg(transcoder);
727         uint32_t val = INREG(reg);
728         if (gen != 4 && type == DIP_AVI)
729                 val |= DIP_CTL_ENABLE;
730         val |= (1 << (21 + type));
731         OUTREG(reg, val);
732 }
733
734 static int parse_infoframe_option_u(const char *name, const char *s,
735                                     uint32_t min, uint32_t max,
736                                     uint32_t *value, char **commands)
737 {
738         int read, rc;
739         if (!strcmp(name, s)) {
740                 rc = sscanf(*commands, "%x%n", value, &read);
741                 *commands = &(*commands)[read];
742                 if (rc != 1) {
743                         printf("Invalid value.\n");
744                         return 0;
745                 }
746
747                 if (*value < min || *value > max) {
748                         printf("Value outside allowed range.\n");
749                         return 0;
750                 }
751                 return 1;
752         }
753         return 0;
754 }
755
756 static int parse_infoframe_option_s(const char *name, const char *s,
757                                     int min_size, int max_size,
758                                     char *value, char **commands)
759 {
760         int size, read, rc;
761         if (!strcmp(name, s)) {
762                 rc = sscanf(*commands, "%31s%n", value, &read);
763                 *commands = &(*commands)[read];
764                 if (rc != 1) {
765                         printf("Invalid value.\n");
766                         return 0;
767                 }
768
769                 size = strlen(value);
770                 if (size < min_size || size > max_size) {
771                         printf("String either too big or too small.\n");
772                         return 0;
773                 }
774                 return 1;
775         }
776         return 0;
777 }
778
779 static void change_avi_infoframe(Transcoder transcoder, char *commands)
780 {
781         Register reg = get_dip_ctl_reg(transcoder);
782         uint32_t val;
783         DipInfoFrame frame;
784         char option[32];
785         uint32_t option_val;
786         int rc, read;
787         char *current = commands;
788
789         load_infoframe(transcoder, &frame, DIP_AVI);
790         val = INREG(reg);
791
792         while (1) {
793                 rc = sscanf(current, "%31s%n", option, &read);
794                 current = &current[read];
795                 if (rc == EOF) {
796                         break;
797                 } else if (rc != 1) {
798                         printf("Invalid option: %s\n", option);
799                         continue;
800                 }
801
802                 if (parse_infoframe_option_u("S", option, 0, 2, &option_val,
803                                              &current))
804                         frame.avi.S = option_val;
805                 else if (parse_infoframe_option_u("B", option, 0, 3,
806                                                   &option_val, &current))
807                         frame.avi.B = option_val;
808                 else if (parse_infoframe_option_u("A", option, 0, 1,
809                                                   &option_val, &current))
810                         frame.avi.A = option_val;
811                 else if (parse_infoframe_option_u("Y", option, 0, 2,
812                                                   &option_val, &current))
813                         frame.avi.Y = option_val;
814                 else if (parse_infoframe_option_u("R", option, 0, 15,
815                                                   &option_val, &current))
816                         frame.avi.R = option_val;
817                 else if (parse_infoframe_option_u("M", option, 0, 2,
818                                                   &option_val, &current))
819                         frame.avi.M = option_val;
820                 else if (parse_infoframe_option_u("C", option, 0, 3,
821                                                   &option_val, &current))
822                         frame.avi.C = option_val;
823                 else if (parse_infoframe_option_u("SC", option, 0, 3,
824                                                   &option_val, &current))
825                         frame.avi.SC = option_val;
826                 else if (parse_infoframe_option_u("Q", option, 0, 2,
827                                                   &option_val, &current))
828                         frame.avi.Q = option_val;
829                 else if (parse_infoframe_option_u("EC", option, 0, 1,
830                                                   &option_val,&current))
831                         frame.avi.EC = option_val;
832                 else if (parse_infoframe_option_u("ITC", option, 0, 1,
833                                                   &option_val, &current))
834                         frame.avi.ITC = option_val;
835                 else if (parse_infoframe_option_u("VIC", option, 0, 127,
836                                                   &option_val, &current))
837                         frame.avi.VIC = option_val;
838                 else if (parse_infoframe_option_u("PR", option, 0, 15,
839                                                   &option_val, &current))
840                         frame.avi.PR = option_val;
841                 else if (parse_infoframe_option_u("top", option, 0, 65535,
842                                                   &option_val, &current))
843                         frame.avi.top = option_val;
844                 else if (parse_infoframe_option_u("bottom", option, 0, 65535,
845                                                   &option_val, &current))
846                         frame.avi.bottom = option_val;
847                 else if (parse_infoframe_option_u("left", option, 0, 65535,
848                                                   &option_val, &current))
849                         frame.avi.left = option_val;
850                 else if (parse_infoframe_option_u("right", option, 0, 65535,
851                                                   &option_val, &current))
852                         frame.avi.right = option_val;
853                 else
854                         printf("Unrecognized option: %s\n", option);
855         }
856
857         val &= ~DIP_CTL_FREQUENCY;
858         val |= DIP_CTL_FREQ_EVERY;
859         OUTREG(reg, val);
860
861         frame.avi.header.type = AVI_INFOFRAME_TYPE;
862         frame.avi.header.version = AVI_INFOFRAME_VERSION;
863         frame.avi.header.length = AVI_INFOFRAME_LENGTH;
864         frame.avi.Rsvd0 = 0;
865         frame.avi.Rsvd1 = 0;
866         frame.avi.Rsvd2 = 0;
867         frame.avi.Rsvd3 = 0;
868         frame.avi.Rsvd4[0] = 0;
869         frame.avi.Rsvd4[1] = 0;
870         frame.avi.Rsvd4[2] = 0;
871
872         infoframe_fix_checksum(&frame);
873
874         disable_infoframe(transcoder, DIP_AVI);
875         write_infoframe(transcoder, DIP_AVI, &frame);
876         enable_infoframe(transcoder, DIP_AVI);
877 }
878
879 static void change_spd_infoframe(Transcoder transcoder, char *commands)
880 {
881         Register reg = get_dip_ctl_reg(transcoder);
882         uint32_t val;
883         DipInfoFrame frame;
884         char option[16];
885         char option_val_s[32];
886         uint32_t option_val_i;
887         int rc, read;
888         char *current = commands;
889
890         load_infoframe(transcoder, &frame, DIP_SPD);
891         val = INREG(reg);
892
893         while (1) {
894                 rc = sscanf(current, "%15s%n", option, &read);
895                 current = &current[read];
896                 if (rc == EOF) {
897                         break;
898                 } else if (rc != 1) {
899                         printf("Invalid option: %s\n", option);
900                         continue;
901                 }
902
903                 memset(option_val_s, 0, 32);
904
905                 if (parse_infoframe_option_s("vendor", option, 0, 8,
906                                              option_val_s, &current))
907                         memcpy(frame.spd.vendor, option_val_s, 8);
908                 else if (parse_infoframe_option_s("description", option, 0, 16,
909                                                   option_val_s, &current))
910                         memcpy(frame.spd.description, option_val_s, 16);
911                 else if (parse_infoframe_option_u("source", option, 0, 0x0c,
912                                                   &option_val_i, &current))
913                         frame.spd.source = option_val_i;
914                 else
915                         printf("Unrecognized option: %s\n", option);
916         }
917
918         val &= ~DIP_CTL_FREQUENCY;
919         val |= DIP_CTL_FREQ_EVERY_OTHER;
920         OUTREG(reg, val);
921
922         frame.spd.header.type = SPD_INFOFRAME_TYPE;
923         frame.spd.header.version = SPD_INFOFRAME_VERSION;
924         frame.spd.header.length = SPD_INFOFRAME_LENGTH;
925
926         infoframe_fix_checksum(&frame);
927
928         disable_infoframe(transcoder, DIP_SPD);
929         write_infoframe(transcoder, DIP_SPD, &frame);
930         enable_infoframe(transcoder, DIP_SPD);
931 }
932
933 static void change_infoframe_checksum(Transcoder transcoder, DipType type,
934                                       uint32_t selected_csum)
935 {
936         DipInfoFrame frame;
937
938         load_infoframe(transcoder, &frame, type);
939         frame.generic.body[0] = selected_csum;
940         disable_infoframe(transcoder, type);
941         write_infoframe(transcoder, type, &frame);
942         enable_infoframe(transcoder, type);
943 }
944
945 static void change_infoframe_frequency(Transcoder transcoder, DipType type,
946                                        DipFrequency frequency)
947 {
948         Register reg = get_dip_ctl_reg(transcoder);
949         uint32_t val = INREG(reg);
950
951         if (type == DIP_AVI && frequency != DIP_FREQ_EVERY_VSYNC) {
952                 printf("Error: AVI infoframe must be sent every VSync!\n");
953                 frequency = DIP_FREQ_EVERY_VSYNC;
954         }
955
956         val &= ~DIP_CTL_FREQUENCY;
957         val |= (frequency << 16);
958         OUTREG(reg, val);
959 }
960
961 static void disable_dip(Transcoder transcoder)
962 {
963         Register reg = get_dip_ctl_reg(transcoder);
964         uint32_t val = INREG(reg);
965         val &= ~DIP_CTL_ENABLE;
966         OUTREG(reg, val);
967 }
968
969 static void enable_dip(Transcoder transcoder)
970 {
971         Register reg = get_dip_ctl_reg(transcoder);
972         uint32_t val = INREG(reg);
973         val |= DIP_CTL_ENABLE;
974         OUTREG(reg, val);
975 }
976
977 static void disable_hdmi_port(Register reg)
978 {
979         uint32_t val = INREG(reg);
980         val &= ~HDMI_PORT_ENABLE;
981         OUTREG(reg, val);
982 }
983
984 static void enable_hdmi_port(Register reg)
985 {
986         uint32_t val = INREG(reg);
987         val |= HDMI_PORT_ENABLE;
988         OUTREG(reg, val);
989 }
990
991 static void print_usage(void)
992 {
993 printf("Options:\n"
994 "  -d, --dump\n"
995 "          dump information about all transcoders\n"
996 "  -c, --change-fields [fields]\n"
997 "          change infoframe fields from selected transcoder\n"
998 "  -k, --change-checksum [checksum]\n"
999 "          change infoframe checksum (value in hex)\n"
1000 "  -q, --change-frequency [frequency]\n"
1001 "          change infoframe frequency (once, everyvsync or everyothervsync)\n"
1002 "  -n, --disable\n"
1003 "          disable the selected infoframe from the selected transcoder\n"
1004 "  -N, --enable\n"
1005 "          enable the selected infoframe from the selected transcoder\n"
1006 "  -x, --disable-infoframes\n"
1007 "          disable all infoframes from selected transcoder\n"
1008 "  -X, --enable-infoframes\n"
1009 "          enable sending infoframes on the selected transcoder\n"
1010 "  -p, --disable-hdmi-port [port]\n"
1011 "          disable hdmi port on the selected transcoder (B, C or D)\n"
1012 "  -P, --enable-hdmi-port [port]\n"
1013 "          enable hdmi port on the selected transcoder (B, C or D)\n"
1014 "  -t, --transcoder\n"
1015 "          select transcoder (A, B or C)\n"
1016 "  -f, --infoframe\n"
1017 "          select infoframe (AVI, Vendor, Gamut or SPD)\n"
1018 "  -h, --help\n"
1019 "          prints this message\n"
1020 "\n"
1021 "Examples:\n"
1022 "\n"
1023 "  Dump information:\n"
1024 "          intel_infoframes\n"
1025 "\n"
1026 "  Disable overscan and set ITC on transcoder B:\n"
1027 "          intel_infoframes -t B -f AVI -c 'S 2 ITC 1'\n"
1028 "\n"
1029 "  Many actions on the same command:\n"
1030 "  - enable overscan on transcoder A\n"
1031 "  - enable overscan and change description on transcoder B\n"
1032 "  - disable all infoframes on transcoder C\n"
1033 "  - dump the resulting state:\n"
1034 "          intel_infoframes -t A -f AVI -c 'S 1' \\\n"
1035 "                           -t B -f AVI -c 'S 2' \\\n"
1036 "                                -f SPD -c 'description Linux' \\\n"
1037 "                           -t C --disable-infoframes \\\n"
1038 "                           -d\n"
1039 "\n"
1040 "  Even more:\n"
1041 "  - print the help message\n"
1042 "  - completely disable all infoframes on all transcoders\n"
1043 "  - dump the state"
1044 "  - enable sending infoframes on transcoder B, but disable all infoframes\n"
1045 "  - enable AVI infoframes transcoder B, use underscan and declare ITC\n"
1046 "  - also enable SPD infoframes on the same transcoder, change frequency to\n"
1047 "    every vsync and change vendor, description and source\n"
1048 "  - dump the state again\n"
1049 "          intel_infoframes -h \\\n"
1050 "                           -t A -x -t B -x -t C -x \\\n"
1051 "                           -d \\\n"
1052 "                           -t A -X -f AVI -n -f Vendor -n \\\n"
1053 "                           -f Gamut -n -f SPD -n \\\n"
1054 "                           -f AVI -N -c 'S 2 ITC 1'\\\n"
1055 "                           -f SPD -q everyvsync \\\n"
1056 "                           -c 'vendor me description mine source 0x09' \\\n"
1057 "                           -d\n"
1058 "\n"
1059 "Infoframe fields used by the --change-fields option:\n"
1060 "  - AVI infoframe fields:\n"
1061 "          S B A Y R M C SC Q EC ITC VIC PR top bottom left right\n"
1062 "  - SPD infoframe fields:\n"
1063 "          vendor description source\n"
1064 "  - Other infoframe fields are not implemented yet.\n");
1065 }
1066
1067 #define CHECK_TRANSCODER(transcoder)                  \
1068         if (transcoder == TRANSC_INVALID) {           \
1069                 printf("Transcoder not selected.\n"); \
1070                 ret = 1;                              \
1071                 goto out;                             \
1072         }
1073
1074 #define CHECK_DIP(dip)                                \
1075         if (dip == DIP_INVALID) {                     \
1076                 printf("Infoframe not selected.\n");  \
1077                 ret = 1;                              \
1078                 goto out;                             \
1079         }
1080
1081 int main(int argc, char *argv[])
1082 {
1083         int opt;
1084         int ret = 0;
1085         Transcoder transcoder = TRANSC_INVALID;
1086         DipType dip = DIP_INVALID;
1087         Register hdmi_port;
1088
1089         char short_opts[] = "dc:k:q:nNxXp:P:t:f:h";
1090         struct option long_opts[] = {
1091                 { "dump",               no_argument,       NULL, 'd' },
1092                 { "change-fields",      required_argument, NULL, 'c' },
1093                 { "change-checksum",    required_argument, NULL, 'k' },
1094                 { "change-frequency",   required_argument, NULL, 'q' },
1095                 { "disable",            no_argument,       NULL, 'n' },
1096                 { "enable",             no_argument,       NULL, 'N' },
1097                 { "disable-infoframes", no_argument,       NULL, 'x' },
1098                 { "enable-infoframes",  no_argument,       NULL, 'X' },
1099                 { "disable-hdmi-port",  required_argument, NULL, 'p' },
1100                 { "enable-hdmi-port",   required_argument, NULL, 'P' },
1101                 { "transcoder" ,        required_argument, NULL, 't' },
1102                 { "infoframe",          required_argument, NULL, 'f' },
1103                 { "help",               no_argument,       NULL, 'h' },
1104         };
1105
1106         printf("WARNING: This is just a debugging tool! Don't expect it to work"
1107                " perfectly: the Kernel might undo our changes.\n");
1108
1109         pci_dev = intel_get_pci_device();
1110         intel_register_access_init(pci_dev, 0);
1111         intel_check_pch();
1112
1113         if (IS_GEN4(pci_dev->device_id))
1114                 gen = 4;
1115         else if (IS_GEN5(pci_dev->device_id))
1116                 gen = 5;
1117         else if (IS_GEN6(pci_dev->device_id))
1118                 gen = 6;
1119         else if (IS_GEN7(pci_dev->device_id))
1120                 gen = 7;
1121         else {
1122                 printf("This program does not support your hardware yet.\n");
1123                 ret = 1;
1124                 goto out;
1125         }
1126
1127         while (1) {
1128                 opt = getopt_long(argc, argv, short_opts, long_opts, NULL);
1129                 if (opt == -1)
1130                         break;
1131
1132                 switch (opt) {
1133                 case 'd':
1134                         dump_all_info();
1135                         break;
1136                 case 'c':
1137                         if (transcoder == TRANSC_INVALID) {
1138                                 printf("Transcoder not selected.\n");
1139                                 ret = 1;
1140                                 goto out;
1141                         }
1142                         switch (dip) {
1143                         case DIP_AVI:
1144                                 change_avi_infoframe(transcoder, optarg);
1145                                 break;
1146                         case DIP_VENDOR:
1147                         case DIP_GAMUT:
1148                                 printf("Option not implemented yet.\n");
1149                                 ret = 1;
1150                                 goto out;
1151                         case DIP_SPD:
1152                                 change_spd_infoframe(transcoder, optarg);
1153                                 break;
1154                         case DIP_INVALID:
1155                                 printf("Infoframe not selected.\n");
1156                                 ret = 1;
1157                                 goto out;
1158                         }
1159                         break;
1160                 case 'k':
1161                         CHECK_TRANSCODER(transcoder);
1162                         CHECK_DIP(dip);
1163                         change_infoframe_checksum(transcoder, dip, atoi(optarg));
1164                         break;
1165                 case 'q':
1166                         CHECK_TRANSCODER(transcoder);
1167                         CHECK_DIP(dip);
1168                         if (!strcmp(optarg, "once"))
1169                                 change_infoframe_frequency(transcoder, dip,
1170                                                 DIP_FREQ_ONCE);
1171                         else if (!strcmp(optarg, "everyvsync"))
1172                                 change_infoframe_frequency(transcoder, dip,
1173                                                 DIP_FREQ_EVERY_VSYNC);
1174                         else if (!strcmp(optarg, "everyothervsync"))
1175                                 change_infoframe_frequency(transcoder, dip,
1176                                                 DIP_FREQ_EVERY_OTHER_VSYNC);
1177                         else {
1178                                 printf("Invalid frequency.\n");
1179                                 ret = 1;
1180                                 goto out;
1181                         }
1182                         break;
1183                 case 'n':
1184                         CHECK_TRANSCODER(transcoder);
1185                         CHECK_DIP(dip);
1186                         disable_infoframe(transcoder, dip);
1187                         break;
1188                 case 'N':
1189                         CHECK_TRANSCODER(transcoder);
1190                         CHECK_DIP(dip);
1191                         enable_infoframe(transcoder, dip);
1192                         break;
1193                 case 'x':
1194                         CHECK_TRANSCODER(transcoder);
1195                         disable_dip(transcoder);
1196                         break;
1197                 case 'X':
1198                         CHECK_TRANSCODER(transcoder);
1199                         enable_dip(transcoder);
1200                         break;
1201                 case 'p':
1202                 case 'P':
1203                         if (!strcmp(optarg, "B"))
1204                                 hdmi_port = get_hdmi_port(0);
1205                         else if (!strcmp(optarg, "C"))
1206                                 hdmi_port = get_hdmi_port(1);
1207                         else if (!strcmp(optarg, "D"))
1208                                 hdmi_port = get_hdmi_port(2);
1209                         else {
1210                                 printf("Invalid HDMI port.\n");
1211                                 ret = 1;
1212                                 goto out;
1213                         }
1214                         if (opt == 'p')
1215                                 disable_hdmi_port(hdmi_port);
1216                         else
1217                                 enable_hdmi_port(hdmi_port);
1218                         break;
1219                 case 't':
1220                         if (!strcmp(optarg, "A"))
1221                                 transcoder = TRANSC_A;
1222                         else if (!strcmp(optarg, "B"))
1223                                 transcoder = TRANSC_B;
1224                         else if (intel_pch >= PCH_CPT && !strcmp(optarg, "C")) {
1225                                 transcoder = TRANSC_C;
1226                         } else {
1227                                 printf("Invalid transcoder.\n");
1228                                 ret = 1;
1229                                 goto out;
1230                         }
1231                         break;
1232                 case 'f':
1233                         if (!strcmp(optarg, "AVI"))
1234                                 dip = DIP_AVI;
1235                         else if (!strcmp(optarg, "Vendor"))
1236                                 dip = DIP_VENDOR;
1237                         else if (!strcmp(optarg, "Gamut"))
1238                                 dip = DIP_GAMUT;
1239                         else if (!strcmp(optarg, "SPD"))
1240                                 dip = DIP_SPD;
1241                         else {
1242                                 printf("Invalid infoframe.\n");
1243                                 ret = 1;
1244                                 goto out;
1245                         }
1246                         break;
1247                 case 'h':
1248                         print_usage();
1249                         break;
1250                 default:
1251                         print_usage();
1252                         ret = 1;
1253                         goto out;
1254                 }
1255         }
1256
1257 out:
1258         intel_register_access_fini();
1259         return ret;
1260 }