1/*
2 * Copyright (c) 2006 Advanced Micro Devices, Inc.
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 shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
23 * contributors may be used to endorse or promote products derived from this
24 * software without specific prior written permission.
25 */
26
27 /*
28  * Cimarron VOP configuration routines.
29  */
30
31/*---------------------------------------------------------------------------
32 * vop_set_vbi_window
33 *
34 * This routine configures the output position and location in memory of
35 * VBI data.
36 *--------------------------------------------------------------------------*/
37
38int
39vop_set_vbi_window(VOPVBIWINDOWBUFFER * buffer)
40{
41    unsigned long unlock, temp;
42    unsigned long hstart, hstop;
43    unsigned long htotal, hsyncstart;
44
45    if (!buffer)
46        return CIM_STATUS_INVALIDPARAMS;
47
48    unlock = READ_REG32(DC3_UNLOCK);
49    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
50
51    /* PROGRAM HORIZONTAL POSITION
52     * The horizontal position is a little tricky.  The counter for the
53     * horizontal timings is reused for the VBI counter.  Consequently, the
54     * horizontal start and stop values are based off the beginning of active
55     * data.  However, the VG has a quirk.  If the counter start position is
56     * before the beginning of HSync, it applies to the previous line.  If
57     * the counter is after the beginning of HSync it applies to the current
58     * line.  So, for one line the real range can be thought of as
59     * HSync_start to (HSync_start + htotal - 1).  However, the counters
60     * must be between 0 and htotal - 1.  When placing VBI data before the
61     * start of active data, the horizontal end position will thus be *less*
62     * than the horizontal start.
63     */
64
65    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
66    hsyncstart = (READ_REG32(DC3_H_SYNC_TIMING) & 0xFFF) + 1;
67
68    if (buffer->horz_from_hsync) {
69        /* VERIFY THAT THE INPUT IS VALID */
70
71        if (buffer->horz_start < 0
72            || (buffer->horz_start + buffer->vbi_width) > htotal)
73            return CIM_STATUS_INVALIDPARAMS;
74
75        hstart = buffer->horz_start + hsyncstart;
76    }
77    else {
78        /* VERIFY THAT THE INPUT IS VALID */
79
80        if (buffer->horz_start < ((long) hsyncstart - (long) htotal) ||
81            buffer->horz_start > (long) hsyncstart ||
82            buffer->vbi_width > htotal) {
83            return CIM_STATUS_INVALIDPARAMS;
84        }
85
86        hstart = buffer->horz_start + htotal;
87    }
88
89    hstop = hstart + buffer->vbi_width;
90    if (hstart > htotal)
91        hstart -= htotal;
92    if (hstop > htotal)
93        hstop -= htotal;
94    hstart--;
95    hstop--;
96    WRITE_REG32(DC3_VBI_HOR, ((hstop << DC3_VBI_HOR_END_SHIFT) &
97                              DC3_VBI_HOR_END_MASK) | (hstart &
98                                                       DC3_VBI_HOR_START_MASK));
99
100    /* WRITE LINE CAPTURE MASKS */
101
102    WRITE_REG32(DC3_VBI_LN_ODD, ((buffer->odd_line_offset <<
103                                  DC3_VBI_ODD_LINE_SHIFT) &
104                                 DC3_VBI_ODD_LINE_MASK) |
105                (buffer->odd_line_capture_mask & DC3_VBI_ODD_ENABLE_MASK));
106
107    WRITE_REG32(DC3_VBI_LN_EVEN, ((buffer->even_line_offset <<
108                                   DC3_VBI_EVEN_LINE_SHIFT) &
109                                  DC3_VBI_EVEN_LINE_MASK) |
110                (buffer->even_line_capture_mask & DC3_VBI_EVEN_ENABLE_MASK));
111
112    /* PROGRAM SOURCE OFFSETS
113     * Start with the even offsets.  Note that we always enable 16-bit VBI,
114     * as this is the only way to get VBI data on each VOP clock.
115     */
116
117    temp = READ_REG32(DC3_VBI_EVEN_CTL) & ~DC3_VBI_EVEN_CTL_OFFSET_MASK;
118    temp |= DC3_VBI_EVEN_CTL_ENABLE_16;
119    if (buffer->enable_upscale)
120        temp |= DC3_VBI_EVEN_CTL_UPSCALE;
121    WRITE_REG32(DC3_VBI_EVEN_CTL, temp |
122                (buffer->even_address_offset & DC3_VBI_EVEN_CTL_OFFSET_MASK));
123
124    /* ODD OFFSET */
125
126    temp = READ_REG32(DC3_VBI_ODD_CTL) & ~DC3_VBI_ODD_CTL_OFFSET_MASK;
127    WRITE_REG32(DC3_VBI_ODD_CTL, temp |
128                (buffer->odd_address_offset & DC3_VBI_ODD_CTL_OFFSET_MASK));
129
130    /* PITCH */
131
132    temp = ((buffer->data_size >> 3) << 16) | ((buffer->data_pitch >> 3) &
133                                               0x0000FFFF);
134    WRITE_REG32(DC3_VBI_PITCH, temp);
135
136    WRITE_REG32(DC3_UNLOCK, unlock);
137
138    return CIM_STATUS_OK;
139}
140
141/*---------------------------------------------------------------------------
142 * vop_enable_vbi_output
143 *
144 * This routine enables/disables VBI fetching inside the video generator.
145 *--------------------------------------------------------------------------*/
146
147int
148vop_enable_vbi_output(int enable)
149{
150    unsigned long unlock, temp;
151
152    unlock = READ_REG32(DC3_UNLOCK);
153    temp = READ_REG32(DC3_VBI_EVEN_CTL);
154
155    if (enable)
156        temp |= DC3_VBI_ENABLE;
157    else
158        temp &= ~DC3_VBI_ENABLE;
159
160    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
161    WRITE_REG32(DC3_VBI_EVEN_CTL, temp);
162    WRITE_REG32(DC3_UNLOCK, unlock);
163
164    return CIM_STATUS_OK;
165}
166
167/*---------------------------------------------------------------------------
168 * vop_set_configuration
169 *
170 * This routine is passed a VOP_CONFIGURATION structure that contains all
171 * the necessary information to configure VOP output.
172 *--------------------------------------------------------------------------*/
173
174int
175vop_set_configuration(VOPCONFIGURATIONBUFFER * config)
176{
177    unsigned long vop_config = 0;
178    unsigned long alpha, control2;
179    unsigned long unlock;
180    unsigned long delta;
181    Q_WORD msr_value;
182    int rgb = 0;
183
184    if (!config)
185        return CIM_STATUS_INVALIDPARAMS;
186
187    unlock = READ_REG32(DC3_UNLOCK);
188    delta = READ_REG32(DC3_VID_DS_DELTA) & DC3_DS_DELTA_MASK;
189
190    /* OVERRIDE THE OUTPUT SETTINGS TO ENABLE VOP OUTPUT */
191
192    if (config->mode != VOP_MODE_DISABLED) {
193        msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
194        msr_value.low &= ~DF_CONFIG_OUTPUT_MASK;
195        msr_value.low |= DF_OUTPUT_VOP;
196        msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
197    }
198
199    /* SET THE UNIVERSAL VOP OPTIONS */
200
201    if (config->flags & VOP_FLAG_SWAP_UV)
202        vop_config |= VOP_CONFIG_SWAPUV;
203    if (config->flags & VOP_FLAG_SWAP_VBI)
204        vop_config |= VOP_CONFIG_SWAPVBI;
205
206    /* SET THE MODE SPECIFIC PARAMETERS */
207
208    if (config->mode == VOP_MODE_601) {
209        vop_config |= config->vop601.flags;
210        vop_config |= config->vop601.vsync_shift;
211        vop_config |= VOP_CONFIG_ENABLE_601 | VOP_CONFIG_VIP2_0;
212
213        switch (config->vop601.output_mode) {
214        case VOP_601_YUV_16BIT:
215            vop_config |= VOP_CONFIG_VIP2_16BIT;
216            break;
217        case VOP_601_YUV_4_4_4:
218            vop_config |= VOP_CONFIG_DISABLE_DECIMATE;
219            break;
220        case VOP_601_RGB_8_8_8:
221            vop_config |= VOP_CONFIG_DISABLE_DECIMATE | VOP_CONFIG_RGBMODE;
222            rgb = 1;
223            break;
224        }
225
226        if (config->vop601.vsync_shift == VOP_VSYNC_LATER_BY_X) {
227            delta |= (config->vop601.vsync_shift_count &
228                      DC3_601_VSYNC_SHIFT_MASK);
229            delta |= DC3_601_VSYNC_SHIFT_ENABLE;
230        }
231    }
232    else {
233        if (config->flags & VOP_FLAG_VBI)
234            vop_config |= VOP_CONFIG_VBI;
235        if (config->flags & VOP_FLAG_TASK)
236            vop_config |= VOP_CONFIG_TASK;
237        if (config->flags & VOP_FLAG_SINGLECHIPCOMPAT)
238            vop_config |= VOP_CONFIG_SC_COMPATIBLE;
239        if (config->flags & VOP_FLAG_EXTENDEDSAV)
240            vop_config |= VOP_CONFIG_EXTENDED_SAV;
241
242        switch (config->mode) {
243        case VOP_MODE_DISABLED:
244            vop_config |= VOP_CONFIG_DISABLED;
245            break;
246        case VOP_MODE_VIP11:
247            vop_config |= VOP_CONFIG_VIP1_1;
248            break;
249        case VOP_MODE_CCIR656:
250            vop_config |= VOP_CONFIG_CCIR656;
251            break;
252        case VOP_MODE_VIP20_8BIT:
253            vop_config |= VOP_CONFIG_VIP2_0;
254            break;
255        case VOP_MODE_VIP20_16BIT:
256            vop_config |= VOP_CONFIG_VIP2_0 | VOP_CONFIG_VIP2_16BIT;
257            break;
258        }
259    }
260
261    /* SET THE 4:4:4 TO 4:2:2 DECIMATION ALGORITHM */
262
263    vop_config |= (config->conversion_mode);
264
265    /* SET THE VSYNC OUT OPTIONS */
266
267    control2 = READ_VIP32(VIP_CONTROL2) & ~VIP_CONTROL2_SYNC2PIN_MASK;
268    control2 |= config->vsync_out;
269    WRITE_VIP32(VIP_CONTROL2, control2);
270
271    /* FORCE THE CORRECT VOP COLOR SPACE */
272    /* The output of the mixer will be either RGB or YUV.  We must enable */
273    /* or disable the VOP CSC based on the desired output format.         */
274
275    alpha = READ_VID32(DF_VID_ALPHA_CONTROL);
276    if (!(alpha & DF_CSC_GRAPHICS_RGB_TO_YUV)) {
277        /* RGB OUTPUT FROM THE MIXER */
278
279        if (!rgb)
280            alpha |= DF_CSC_VOP_RGB_TO_YUV;
281        else
282            alpha &= ~DF_CSC_VOP_RGB_TO_YUV;
283    }
284    else {
285        /* YUV OUTPUT FROM THE MIXER */
286        /* As there is no YUV->RGB VOP conversion, we simply disable the */
287        /* VOP CSC and trust that the user is competent.                 */
288
289        alpha &= ~DF_CSC_VOP_RGB_TO_YUV;
290    }
291
292    /* AND WRITE THE CONFIGURATION */
293
294    WRITE_VID32(DF_VID_ALPHA_CONTROL, alpha);
295    WRITE_VOP32(VOP_CONFIGURATION, vop_config);
296    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
297    WRITE_REG32(DC3_VID_DS_DELTA, delta);
298    WRITE_REG32(DC3_UNLOCK, unlock);
299
300    return CIM_STATUS_OK;
301}
302
303/*---------------------------------------------------------------------------
304 * vop_save_state
305 *
306 * This routine saves the necessary register contents in order to restore
307 * at a later point to the same state.  Note that the capture state is
308 * forced to OFF in this routine.
309 *--------------------------------------------------------------------------*/
310
311int
312vop_save_state(VOPSTATEBUFFER * save_buffer)
313{
314    if (!save_buffer)
315        return CIM_STATUS_INVALIDPARAMS;
316
317    save_buffer->config = READ_VOP32(VOP_CONFIGURATION);
318
319    return CIM_STATUS_OK;
320}
321
322/*---------------------------------------------------------------------------
323 * vop_restore_state
324 *
325 * This routine restores the state of the vop registers - which were
326 * previously saved using vop_save_state.
327 *--------------------------------------------------------------------------*/
328
329int
330vop_restore_state(VOPSTATEBUFFER * restore_buffer)
331{
332    if (!restore_buffer)
333        return CIM_STATUS_INVALIDPARAMS;
334
335    WRITE_VOP32(VOP_CONFIGURATION, restore_buffer->config);
336
337    return CIM_STATUS_OK;
338}
339
340/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
341 * CIMARRON VOP READ ROUTINES
342 * These routines are included for use in diagnostics or when debugging.  They
343 * can be optionally excluded from a project.
344 *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
345
346#if CIMARRON_INCLUDE_VOP_READ_ROUTINES
347
348/*---------------------------------------------------------------------------
349 * vop_get_current_mode
350 *
351 * This routine reads the current VIP operating mode and stores it in the
352 * passed VOP_CONFIGURATION structure.
353 *--------------------------------------------------------------------------*/
354
355int
356vop_get_current_mode(VOPCONFIGURATIONBUFFER * config)
357{
358    unsigned long vop_config = 0;
359    unsigned long alpha;
360
361    if (!config)
362        return CIM_STATUS_INVALIDPARAMS;
363
364    vop_config = READ_VOP32(VOP_CONFIGURATION);
365    alpha = READ_VID32(DF_VID_ALPHA_CONTROL);
366
367    /* READ THE CURRENT MODE */
368
369    switch (vop_config & VOP_CONFIG_MODE_MASK) {
370    case VOP_CONFIG_DISABLED:
371        config->mode = VOP_MODE_DISABLED;
372        break;
373    case VOP_CONFIG_VIP1_1:
374        config->mode = VOP_MODE_VIP11;
375        break;
376    case VOP_CONFIG_CCIR656:
377        config->mode = VOP_MODE_CCIR656;
378        break;
379    case VOP_CONFIG_VIP2_0:
380
381        if (vop_config & VOP_CONFIG_ENABLE_601)
382            config->mode = VOP_MODE_601;
383        else if (vop_config & VOP_CONFIG_VIP2_16BIT)
384            config->mode = VOP_MODE_VIP20_16BIT;
385        else
386            config->mode = VOP_MODE_VIP20_8BIT;
387        break;
388    }
389
390    /* READ 601 SETTINGS */
391
392    config->vop601.flags = vop_config & (VOP_CONFIG_INVERT_DISPE |
393                                         VOP_CONFIG_INVERT_HSYNC |
394                                         VOP_CONFIG_INVERT_VSYNC);
395
396    config->vop601.vsync_shift = vop_config & VOP_CONFIG_VSYNC_MASK;
397    config->vop601.vsync_shift_count =
398        READ_REG32(DC3_VID_DS_DELTA) & DC3_601_VSYNC_SHIFT_MASK;
399
400    if ((alpha & DF_CSC_GRAPHICS_RGB_TO_YUV) || (alpha & DF_CSC_VOP_RGB_TO_YUV)) {
401        /* YUV OUTPUT */
402
403        if (vop_config & VOP_CONFIG_DISABLE_DECIMATE)
404            config->vop601.output_mode = VOP_601_YUV_4_4_4;
405        else if (vop_config & VOP_CONFIG_VIP2_16BIT)
406            config->vop601.output_mode = VOP_601_YUV_16BIT;
407        else
408            config->vop601.output_mode = VOP_601_YUV_8BIT;
409    }
410    else {
411        config->vop601.output_mode = VOP_601_RGB_8_8_8;
412    }
413
414    config->flags = 0;
415
416    /* READ THE UNIVERSAL VOP OPTIONS */
417
418    if (vop_config & VOP_CONFIG_SWAPUV)
419        config->flags |= VOP_FLAG_SWAP_UV;
420    if (vop_config & VOP_CONFIG_SWAPVBI)
421        config->flags |= VOP_FLAG_SWAP_VBI;
422    if (vop_config & VOP_CONFIG_VBI)
423        config->flags |= VOP_FLAG_VBI;
424    if (vop_config & VOP_CONFIG_TASK)
425        config->flags |= VOP_FLAG_TASK;
426    if (vop_config & VOP_CONFIG_SC_COMPATIBLE)
427        config->flags |= VOP_FLAG_SINGLECHIPCOMPAT;
428    if (vop_config & VOP_CONFIG_EXTENDED_SAV)
429        config->flags |= VOP_FLAG_EXTENDEDSAV;
430
431    config->conversion_mode = vop_config & VOP_CONFIG_422_MASK;
432
433    config->vsync_out = READ_VIP32(VIP_CONTROL2) & VIP_CONTROL2_SYNC2PIN_MASK;
434
435    return CIM_STATUS_OK;
436}
437
438/*---------------------------------------------------------------------------
439 * vop_get_vbi_window
440 *
441 * This routine reads the current VBI configuration for VOP output.
442 *--------------------------------------------------------------------------*/
443
444int
445vop_get_vbi_configuration(VOPVBIWINDOWBUFFER * buffer)
446{
447    unsigned long temp;
448    unsigned long hstart, hstop;
449    unsigned long htotal, hsyncstart;
450
451    if (!buffer)
452        return CIM_STATUS_INVALIDPARAMS;
453
454    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
455    hsyncstart = (READ_REG32(DC3_H_SYNC_TIMING) & 0xFFF) + 1;
456
457    /* DECODE HORIZONTAL POSITION */
458    /* This is done according to the requested horizontal origin */
459
460    temp = READ_REG32(DC3_VBI_HOR);
461    hstart = (temp & DC3_VBI_HOR_START_MASK) + 1;
462    hstop = ((temp & DC3_VBI_HOR_END_MASK) >> DC3_VBI_HOR_END_SHIFT) + 1;
463    if (buffer->horz_from_hsync) {
464        buffer->horz_start = hstart + htotal - hsyncstart;
465        if (buffer->horz_start >= (long) htotal)
466            buffer->horz_start -= htotal;
467    }
468    else {
469        if (hstart > hsyncstart)
470            buffer->horz_start = (long) hstart - (long) htotal;
471        else
472            buffer->horz_start = hstart;
473    }
474
475    if (hstop > hstart)
476        buffer->vbi_width = hstop - hstart;
477    else
478        buffer->vbi_width = (htotal - hstart) + hstop;
479
480    /* READ LINE MASKS */
481
482    temp = READ_REG32(DC3_VBI_LN_ODD);
483    buffer->odd_line_offset = (temp & DC3_VBI_ODD_LINE_MASK) >>
484        DC3_VBI_ODD_LINE_SHIFT;
485    buffer->odd_line_capture_mask = (temp & DC3_VBI_ODD_ENABLE_MASK);
486
487    temp = READ_REG32(DC3_VBI_LN_EVEN);
488    buffer->even_line_offset = (temp & DC3_VBI_EVEN_LINE_MASK) >>
489        DC3_VBI_EVEN_LINE_SHIFT;
490    buffer->even_line_capture_mask = (temp & DC3_VBI_EVEN_ENABLE_MASK);
491
492    /* READ VBI UPSCALE SETTINGS */
493
494    buffer->enable_upscale = 0;
495    temp = READ_REG32(DC3_VBI_EVEN_CTL);
496    if (temp & DC3_VBI_EVEN_CTL_UPSCALE)
497        buffer->enable_upscale = 1;
498
499    /* READ SOURCE OFFSETS */
500
501    buffer->even_address_offset = temp & DC3_VBI_EVEN_CTL_OFFSET_MASK;
502    buffer->odd_address_offset =
503        READ_REG32(DC3_VBI_ODD_CTL) & DC3_VBI_ODD_CTL_OFFSET_MASK;
504
505    /* PITCH AND SIZE */
506
507    temp = READ_REG32(DC3_VBI_PITCH);
508    buffer->data_size = (temp >> 16) << 3;
509    buffer->data_pitch = (temp & 0xFFFF);
510
511    return CIM_STATUS_OK;
512}
513
514/*---------------------------------------------------------------------------
515 * vop_get_vbi_enable
516 *
517 * This routine reads the current enable status of VBI output.
518 *--------------------------------------------------------------------------*/
519
520int
521vop_get_vbi_enable(void)
522{
523    if (READ_REG32(DC3_VBI_EVEN_CTL) & DC3_VBI_ENABLE)
524        return 1;
525
526    return 0;
527}
528
529/*---------------------------------------------------------------------------
530 * vop_get_crc
531 *
532 * This routine returns a CRC of the current VOP data
533 --------------------------------------------------------------------------*/
534
535unsigned long
536vop_get_crc(void)
537{
538    unsigned long crc;
539    unsigned long config = READ_VOP32(VOP_CONFIGURATION);
540    unsigned long timeout = 1000;
541
542    if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN))
543        return 0xFFFFFFFF;
544
545    /* RESET CRC */
546
547    WRITE_VOP32(VOP_CONFIGURATION, config & ~VOP_CONFIG_ENABLE_SIGNATURE);
548
549    /* WAIT FOR THE RESET TO BE LATCHED */
550
551    while ((READ_VOP32(VOP_SIGNATURE) != 0x00000001) && timeout)
552        timeout--;
553
554    WRITE_VOP32(VOP_CONFIGURATION, config | VOP_CONFIG_ENABLE_SIGNATURE);
555
556    /* WAIT UNTIL NOT ACTIVE, THEN ACTIVE, NOT ACTIVE, THEN ACTIVE */
557
558    while (!(READ_VOP32(VOP_CONFIGURATION) & VOP_CONFIG_SIGVAL));
559
560    crc = READ_VOP32(VOP_SIGNATURE);
561
562    return crc;
563}
564
565/*---------------------------------------------------------------------------
566 * vop_read_vbi_crc
567 *
568 * This routine returns a CRC of the current VBI data
569 ---------------------------------------------------------------------------*/
570
571unsigned long
572vop_read_vbi_crc(void)
573{
574    unsigned long gcfg, unlock, vbi_even;
575    unsigned long crc;
576
577    if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN) ||
578        !(READ_REG32(DC3_VBI_EVEN_CTL) & DC3_VBI_ENABLE)) {
579        return 0xFFFFFFFF;
580    }
581
582    unlock = READ_REG32(DC3_UNLOCK);
583    gcfg = READ_REG32(DC3_GENERAL_CFG);
584    vbi_even = READ_REG32(DC3_VBI_EVEN_CTL);
585
586    gcfg |= DC3_GCFG_SGRE | DC3_GCFG_CRC_MODE;
587    gcfg &= ~(DC3_GCFG_SGFR | DC3_GCFG_SIG_SEL);
588    vbi_even |= DC3_VBI_EVEN_ENABLE_CRC;
589
590    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
591    WRITE_REG32(DC3_VBI_EVEN_CTL, vbi_even);
592    WRITE_REG32(DC3_GENERAL_CFG, gcfg & ~DC3_GCFG_SIGE);
593    WRITE_REG32(DC3_GENERAL_CFG, gcfg | DC3_GCFG_SIGE);
594
595    /* WAIT FOR THE CRC TO BE COMPLETED */
596
597    while (!(READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_SIGC));
598
599    /* READ THE COMPLETED CRC */
600
601    crc = READ_REG32(DC3_PAL_DATA);
602
603    /* RESTORE THE PALETTE SETTINGS */
604
605    gcfg &= ~DC3_GCFG_SGRE;
606    WRITE_REG32(DC3_GENERAL_CFG, gcfg);
607    WRITE_REG32(DC3_UNLOCK, unlock);
608
609    return crc;
610}
611
612#endif
613