qemu/hw/display/blizzard.c
<<
>>
Prefs
   1/*
   2 * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller.
   3 *
   4 * Copyright (C) 2008 Nokia Corporation
   5 * Written by Andrzej Zaborowski <andrew@openedhand.com>
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License as
   9 * published by the Free Software Foundation; either version 2 or
  10 * (at your option) version 3 of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along
  18 * with this program; if not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "qemu/bitops.h"
  23#include "ui/console.h"
  24#include "hw/display/blizzard.h"
  25#include "ui/pixel_ops.h"
  26
  27typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int);
  28
  29typedef struct {
  30    uint8_t reg;
  31    uint32_t addr;
  32    int swallow;
  33
  34    int pll;
  35    int pll_range;
  36    int pll_ctrl;
  37    uint8_t pll_mode;
  38    uint8_t clksel;
  39    int memenable;
  40    int memrefresh;
  41    uint8_t timing[3];
  42    int priority;
  43
  44    uint8_t lcd_config;
  45    int x;
  46    int y;
  47    int skipx;
  48    int skipy;
  49    uint8_t hndp;
  50    uint8_t vndp;
  51    uint8_t hsync;
  52    uint8_t vsync;
  53    uint8_t pclk;
  54    uint8_t u;
  55    uint8_t v;
  56    uint8_t yrc[2];
  57    int ix[2];
  58    int iy[2];
  59    int ox[2];
  60    int oy[2];
  61
  62    int enable;
  63    int blank;
  64    int bpp;
  65    int invalidate;
  66    int mx[2];
  67    int my[2];
  68    uint8_t mode;
  69    uint8_t effect;
  70    uint8_t iformat;
  71    uint8_t source;
  72    QemuConsole *con;
  73    blizzard_fn_t *line_fn_tab[2];
  74    void *fb;
  75
  76    uint8_t hssi_config[3];
  77    uint8_t tv_config;
  78    uint8_t tv_timing[4];
  79    uint8_t vbi;
  80    uint8_t tv_x;
  81    uint8_t tv_y;
  82    uint8_t tv_test;
  83    uint8_t tv_filter_config;
  84    uint8_t tv_filter_idx;
  85    uint8_t tv_filter_coeff[0x20];
  86    uint8_t border_r;
  87    uint8_t border_g;
  88    uint8_t border_b;
  89    uint8_t gamma_config;
  90    uint8_t gamma_idx;
  91    uint8_t gamma_lut[0x100];
  92    uint8_t matrix_ena;
  93    uint8_t matrix_coeff[0x12];
  94    uint8_t matrix_r;
  95    uint8_t matrix_g;
  96    uint8_t matrix_b;
  97    uint8_t pm;
  98    uint8_t status;
  99    uint8_t rgbgpio_dir;
 100    uint8_t rgbgpio;
 101    uint8_t gpio_dir;
 102    uint8_t gpio;
 103    uint8_t gpio_edge[2];
 104    uint8_t gpio_irq;
 105    uint8_t gpio_pdown;
 106
 107    struct {
 108        int x;
 109        int y;
 110        int dx;
 111        int dy;
 112        int len;
 113        int buflen;
 114        void *buf;
 115        void *data;
 116        uint16_t *ptr;
 117        int angle;
 118        int pitch;
 119        blizzard_fn_t line_fn;
 120    } data;
 121} BlizzardState;
 122
 123/* Bytes(!) per pixel */
 124static const int blizzard_iformat_bpp[0x10] = {
 125    0,
 126    2,  /* RGB 5:6:5*/
 127    3,  /* RGB 6:6:6 mode 1 */
 128    3,  /* RGB 8:8:8 mode 1 */
 129    0, 0,
 130    4,  /* RGB 6:6:6 mode 2 */
 131    4,  /* RGB 8:8:8 mode 2 */
 132    0,  /* YUV 4:2:2 */
 133    0,  /* YUV 4:2:0 */
 134    0, 0, 0, 0, 0, 0,
 135};
 136
 137static void blizzard_window(BlizzardState *s)
 138{
 139    DisplaySurface *surface = qemu_console_surface(s->con);
 140    uint8_t *src, *dst;
 141    int bypp[2];
 142    int bypl[3];
 143    int y;
 144    blizzard_fn_t fn = s->data.line_fn;
 145
 146    if (!fn)
 147        return;
 148    if (s->mx[0] > s->data.x)
 149        s->mx[0] = s->data.x;
 150    if (s->my[0] > s->data.y)
 151        s->my[0] = s->data.y;
 152    if (s->mx[1] < s->data.x + s->data.dx)
 153        s->mx[1] = s->data.x + s->data.dx;
 154    if (s->my[1] < s->data.y + s->data.dy)
 155        s->my[1] = s->data.y + s->data.dy;
 156
 157    bypp[0] = s->bpp;
 158    bypp[1] = surface_bytes_per_pixel(surface);
 159    bypl[0] = bypp[0] * s->data.pitch;
 160    bypl[1] = bypp[1] * s->x;
 161    bypl[2] = bypp[0] * s->data.dx;
 162
 163    src = s->data.data;
 164    dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x;
 165    for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1])
 166        fn(dst, src, bypl[2]);
 167}
 168
 169static int blizzard_transfer_setup(BlizzardState *s)
 170{
 171    if (s->source > 3 || !s->bpp ||
 172                    s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0])
 173        return 0;
 174
 175    s->data.angle = s->effect & 3;
 176    s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat];
 177    s->data.x = s->ix[0];
 178    s->data.y = s->iy[0];
 179    s->data.dx = s->ix[1] - s->ix[0] + 1;
 180    s->data.dy = s->iy[1] - s->iy[0] + 1;
 181    s->data.len = s->bpp * s->data.dx * s->data.dy;
 182    s->data.pitch = s->data.dx;
 183    if (s->data.len > s->data.buflen) {
 184        s->data.buf = g_realloc(s->data.buf, s->data.len);
 185        s->data.buflen = s->data.len;
 186    }
 187    s->data.ptr = s->data.buf;
 188    s->data.data = s->data.buf;
 189    s->data.len /= 2;
 190    return 1;
 191}
 192
 193static void blizzard_reset(BlizzardState *s)
 194{
 195    s->reg = 0;
 196    s->swallow = 0;
 197
 198    s->pll = 9;
 199    s->pll_range = 1;
 200    s->pll_ctrl = 0x14;
 201    s->pll_mode = 0x32;
 202    s->clksel = 0x00;
 203    s->memenable = 0;
 204    s->memrefresh = 0x25c;
 205    s->timing[0] = 0x3f;
 206    s->timing[1] = 0x13;
 207    s->timing[2] = 0x21;
 208    s->priority = 0;
 209
 210    s->lcd_config = 0x74;
 211    s->x = 8;
 212    s->y = 1;
 213    s->skipx = 0;
 214    s->skipy = 0;
 215    s->hndp = 3;
 216    s->vndp = 2;
 217    s->hsync = 1;
 218    s->vsync = 1;
 219    s->pclk = 0x80;
 220
 221    s->ix[0] = 0;
 222    s->ix[1] = 0;
 223    s->iy[0] = 0;
 224    s->iy[1] = 0;
 225    s->ox[0] = 0;
 226    s->ox[1] = 0;
 227    s->oy[0] = 0;
 228    s->oy[1] = 0;
 229
 230    s->yrc[0] = 0x00;
 231    s->yrc[1] = 0x30;
 232    s->u = 0;
 233    s->v = 0;
 234
 235    s->iformat = 3;
 236    s->source = 0;
 237    s->bpp = blizzard_iformat_bpp[s->iformat];
 238
 239    s->hssi_config[0] = 0x00;
 240    s->hssi_config[1] = 0x00;
 241    s->hssi_config[2] = 0x01;
 242    s->tv_config = 0x00;
 243    s->tv_timing[0] = 0x00;
 244    s->tv_timing[1] = 0x00;
 245    s->tv_timing[2] = 0x00;
 246    s->tv_timing[3] = 0x00;
 247    s->vbi = 0x10;
 248    s->tv_x = 0x14;
 249    s->tv_y = 0x03;
 250    s->tv_test = 0x00;
 251    s->tv_filter_config = 0x80;
 252    s->tv_filter_idx = 0x00;
 253    s->border_r = 0x10;
 254    s->border_g = 0x80;
 255    s->border_b = 0x80;
 256    s->gamma_config = 0x00;
 257    s->gamma_idx = 0x00;
 258    s->matrix_ena = 0x00;
 259    memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff));
 260    s->matrix_r = 0x00;
 261    s->matrix_g = 0x00;
 262    s->matrix_b = 0x00;
 263    s->pm = 0x02;
 264    s->status = 0x00;
 265    s->rgbgpio_dir = 0x00;
 266    s->gpio_dir = 0x00;
 267    s->gpio_edge[0] = 0x00;
 268    s->gpio_edge[1] = 0x00;
 269    s->gpio_irq = 0x00;
 270    s->gpio_pdown = 0xff;
 271}
 272
 273static inline void blizzard_invalidate_display(void *opaque) {
 274    BlizzardState *s = (BlizzardState *) opaque;
 275
 276    s->invalidate = 1;
 277}
 278
 279static uint16_t blizzard_reg_read(void *opaque, uint8_t reg)
 280{
 281    BlizzardState *s = (BlizzardState *) opaque;
 282
 283    switch (reg) {
 284    case 0x00:  /* Revision Code */
 285        return 0xa5;
 286
 287    case 0x02:  /* Configuration Readback */
 288        return 0x83;    /* Macrovision OK, CNF[2:0] = 3 */
 289
 290    case 0x04:  /* PLL M-Divider */
 291        return (s->pll - 1) | (1 << 7);
 292    case 0x06:  /* PLL Lock Range Control */
 293        return s->pll_range;
 294    case 0x08:  /* PLL Lock Synthesis Control 0 */
 295        return s->pll_ctrl & 0xff;
 296    case 0x0a:  /* PLL Lock Synthesis Control 1 */
 297        return s->pll_ctrl >> 8;
 298    case 0x0c:  /* PLL Mode Control 0 */
 299        return s->pll_mode;
 300
 301    case 0x0e:  /* Clock-Source Select */
 302        return s->clksel;
 303
 304    case 0x10:  /* Memory Controller Activate */
 305    case 0x14:  /* Memory Controller Bank 0 Status Flag */
 306        return s->memenable;
 307
 308    case 0x18:  /* Auto-Refresh Interval Setting 0 */
 309        return s->memrefresh & 0xff;
 310    case 0x1a:  /* Auto-Refresh Interval Setting 1 */
 311        return s->memrefresh >> 8;
 312
 313    case 0x1c:  /* Power-On Sequence Timing Control */
 314        return s->timing[0];
 315    case 0x1e:  /* Timing Control 0 */
 316        return s->timing[1];
 317    case 0x20:  /* Timing Control 1 */
 318        return s->timing[2];
 319
 320    case 0x24:  /* Arbitration Priority Control */
 321        return s->priority;
 322
 323    case 0x28:  /* LCD Panel Configuration */
 324        return s->lcd_config;
 325
 326    case 0x2a:  /* LCD Horizontal Display Width */
 327        return s->x >> 3;
 328    case 0x2c:  /* LCD Horizontal Non-display Period */
 329        return s->hndp;
 330    case 0x2e:  /* LCD Vertical Display Height 0 */
 331        return s->y & 0xff;
 332    case 0x30:  /* LCD Vertical Display Height 1 */
 333        return s->y >> 8;
 334    case 0x32:  /* LCD Vertical Non-display Period */
 335        return s->vndp;
 336    case 0x34:  /* LCD HS Pulse-width */
 337        return s->hsync;
 338    case 0x36:  /* LCd HS Pulse Start Position */
 339        return s->skipx >> 3;
 340    case 0x38:  /* LCD VS Pulse-width */
 341        return s->vsync;
 342    case 0x3a:  /* LCD VS Pulse Start Position */
 343        return s->skipy;
 344
 345    case 0x3c:  /* PCLK Polarity */
 346        return s->pclk;
 347
 348    case 0x3e:  /* High-speed Serial Interface Tx Configuration Port 0 */
 349        return s->hssi_config[0];
 350    case 0x40:  /* High-speed Serial Interface Tx Configuration Port 1 */
 351        return s->hssi_config[1];
 352    case 0x42:  /* High-speed Serial Interface Tx Mode */
 353        return s->hssi_config[2];
 354    case 0x44:  /* TV Display Configuration */
 355        return s->tv_config;
 356    case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */
 357        return s->tv_timing[(reg - 0x46) >> 1];
 358    case 0x4e:  /* VBI: Closed Caption / XDS Control / Status */
 359        return s->vbi;
 360    case 0x50:  /* TV Horizontal Start Position */
 361        return s->tv_x;
 362    case 0x52:  /* TV Vertical Start Position */
 363        return s->tv_y;
 364    case 0x54:  /* TV Test Pattern Setting */
 365        return s->tv_test;
 366    case 0x56:  /* TV Filter Setting */
 367        return s->tv_filter_config;
 368    case 0x58:  /* TV Filter Coefficient Index */
 369        return s->tv_filter_idx;
 370    case 0x5a:  /* TV Filter Coefficient Data */
 371        if (s->tv_filter_idx < 0x20)
 372            return s->tv_filter_coeff[s->tv_filter_idx ++];
 373        return 0;
 374
 375    case 0x60:  /* Input YUV/RGB Translate Mode 0 */
 376        return s->yrc[0];
 377    case 0x62:  /* Input YUV/RGB Translate Mode 1 */
 378        return s->yrc[1];
 379    case 0x64:  /* U Data Fix */
 380        return s->u;
 381    case 0x66:  /* V Data Fix */
 382        return s->v;
 383
 384    case 0x68:  /* Display Mode */
 385        return s->mode;
 386
 387    case 0x6a:  /* Special Effects */
 388        return s->effect;
 389
 390    case 0x6c:  /* Input Window X Start Position 0 */
 391        return s->ix[0] & 0xff;
 392    case 0x6e:  /* Input Window X Start Position 1 */
 393        return s->ix[0] >> 3;
 394    case 0x70:  /* Input Window Y Start Position 0 */
 395        return s->ix[0] & 0xff;
 396    case 0x72:  /* Input Window Y Start Position 1 */
 397        return s->ix[0] >> 3;
 398    case 0x74:  /* Input Window X End Position 0 */
 399        return s->ix[1] & 0xff;
 400    case 0x76:  /* Input Window X End Position 1 */
 401        return s->ix[1] >> 3;
 402    case 0x78:  /* Input Window Y End Position 0 */
 403        return s->ix[1] & 0xff;
 404    case 0x7a:  /* Input Window Y End Position 1 */
 405        return s->ix[1] >> 3;
 406    case 0x7c:  /* Output Window X Start Position 0 */
 407        return s->ox[0] & 0xff;
 408    case 0x7e:  /* Output Window X Start Position 1 */
 409        return s->ox[0] >> 3;
 410    case 0x80:  /* Output Window Y Start Position 0 */
 411        return s->oy[0] & 0xff;
 412    case 0x82:  /* Output Window Y Start Position 1 */
 413        return s->oy[0] >> 3;
 414    case 0x84:  /* Output Window X End Position 0 */
 415        return s->ox[1] & 0xff;
 416    case 0x86:  /* Output Window X End Position 1 */
 417        return s->ox[1] >> 3;
 418    case 0x88:  /* Output Window Y End Position 0 */
 419        return s->oy[1] & 0xff;
 420    case 0x8a:  /* Output Window Y End Position 1 */
 421        return s->oy[1] >> 3;
 422
 423    case 0x8c:  /* Input Data Format */
 424        return s->iformat;
 425    case 0x8e:  /* Data Source Select */
 426        return s->source;
 427    case 0x90:  /* Display Memory Data Port */
 428        return 0;
 429
 430    case 0xa8:  /* Border Color 0 */
 431        return s->border_r;
 432    case 0xaa:  /* Border Color 1 */
 433        return s->border_g;
 434    case 0xac:  /* Border Color 2 */
 435        return s->border_b;
 436
 437    case 0xb4:  /* Gamma Correction Enable */
 438        return s->gamma_config;
 439    case 0xb6:  /* Gamma Correction Table Index */
 440        return s->gamma_idx;
 441    case 0xb8:  /* Gamma Correction Table Data */
 442        return s->gamma_lut[s->gamma_idx ++];
 443
 444    case 0xba:  /* 3x3 Matrix Enable */
 445        return s->matrix_ena;
 446    case 0xbc ... 0xde: /* Coefficient Registers */
 447        return s->matrix_coeff[(reg - 0xbc) >> 1];
 448    case 0xe0:  /* 3x3 Matrix Red Offset */
 449        return s->matrix_r;
 450    case 0xe2:  /* 3x3 Matrix Green Offset */
 451        return s->matrix_g;
 452    case 0xe4:  /* 3x3 Matrix Blue Offset */
 453        return s->matrix_b;
 454
 455    case 0xe6:  /* Power-save */
 456        return s->pm;
 457    case 0xe8:  /* Non-display Period Control / Status */
 458        return s->status | (1 << 5);
 459    case 0xea:  /* RGB Interface Control */
 460        return s->rgbgpio_dir;
 461    case 0xec:  /* RGB Interface Status */
 462        return s->rgbgpio;
 463    case 0xee:  /* General-purpose IO Pins Configuration */
 464        return s->gpio_dir;
 465    case 0xf0:  /* General-purpose IO Pins Status / Control */
 466        return s->gpio;
 467    case 0xf2:  /* GPIO Positive Edge Interrupt Trigger */
 468        return s->gpio_edge[0];
 469    case 0xf4:  /* GPIO Negative Edge Interrupt Trigger */
 470        return s->gpio_edge[1];
 471    case 0xf6:  /* GPIO Interrupt Status */
 472        return s->gpio_irq;
 473    case 0xf8:  /* GPIO Pull-down Control */
 474        return s->gpio_pdown;
 475
 476    default:
 477        fprintf(stderr, "%s: unknown register %02x\n", __func__, reg);
 478        return 0;
 479    }
 480}
 481
 482static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value)
 483{
 484    BlizzardState *s = (BlizzardState *) opaque;
 485
 486    switch (reg) {
 487    case 0x04:  /* PLL M-Divider */
 488        s->pll = (value & 0x3f) + 1;
 489        break;
 490    case 0x06:  /* PLL Lock Range Control */
 491        s->pll_range = value & 3;
 492        break;
 493    case 0x08:  /* PLL Lock Synthesis Control 0 */
 494        s->pll_ctrl &= 0xf00;
 495        s->pll_ctrl |= (value << 0) & 0x0ff;
 496        break;
 497    case 0x0a:  /* PLL Lock Synthesis Control 1 */
 498        s->pll_ctrl &= 0x0ff;
 499        s->pll_ctrl |= (value << 8) & 0xf00;
 500        break;
 501    case 0x0c:  /* PLL Mode Control 0 */
 502        s->pll_mode = value & 0x77;
 503        if ((value & 3) == 0 || (value & 3) == 3)
 504            fprintf(stderr, "%s: wrong PLL Control bits (%i)\n",
 505                    __func__, value & 3);
 506        break;
 507
 508    case 0x0e:  /* Clock-Source Select */
 509        s->clksel = value & 0xff;
 510        break;
 511
 512    case 0x10:  /* Memory Controller Activate */
 513        s->memenable = value & 1;
 514        break;
 515    case 0x14:  /* Memory Controller Bank 0 Status Flag */
 516        break;
 517
 518    case 0x18:  /* Auto-Refresh Interval Setting 0 */
 519        s->memrefresh &= 0xf00;
 520        s->memrefresh |= (value << 0) & 0x0ff;
 521        break;
 522    case 0x1a:  /* Auto-Refresh Interval Setting 1 */
 523        s->memrefresh &= 0x0ff;
 524        s->memrefresh |= (value << 8) & 0xf00;
 525        break;
 526
 527    case 0x1c:  /* Power-On Sequence Timing Control */
 528        s->timing[0] = value & 0x7f;
 529        break;
 530    case 0x1e:  /* Timing Control 0 */
 531        s->timing[1] = value & 0x17;
 532        break;
 533    case 0x20:  /* Timing Control 1 */
 534        s->timing[2] = value & 0x35;
 535        break;
 536
 537    case 0x24:  /* Arbitration Priority Control */
 538        s->priority = value & 1;
 539        break;
 540
 541    case 0x28:  /* LCD Panel Configuration */
 542        s->lcd_config = value & 0xff;
 543        if (value & (1 << 7))
 544            fprintf(stderr, "%s: data swap not supported!\n", __func__);
 545        break;
 546
 547    case 0x2a:  /* LCD Horizontal Display Width */
 548        s->x = value << 3;
 549        break;
 550    case 0x2c:  /* LCD Horizontal Non-display Period */
 551        s->hndp = value & 0xff;
 552        break;
 553    case 0x2e:  /* LCD Vertical Display Height 0 */
 554        s->y &= 0x300;
 555        s->y |= (value << 0) & 0x0ff;
 556        break;
 557    case 0x30:  /* LCD Vertical Display Height 1 */
 558        s->y &= 0x0ff;
 559        s->y |= (value << 8) & 0x300;
 560        break;
 561    case 0x32:  /* LCD Vertical Non-display Period */
 562        s->vndp = value & 0xff;
 563        break;
 564    case 0x34:  /* LCD HS Pulse-width */
 565        s->hsync = value & 0xff;
 566        break;
 567    case 0x36:  /* LCD HS Pulse Start Position */
 568        s->skipx = value & 0xff;
 569        break;
 570    case 0x38:  /* LCD VS Pulse-width */
 571        s->vsync = value & 0xbf;
 572        break;
 573    case 0x3a:  /* LCD VS Pulse Start Position */
 574        s->skipy = value & 0xff;
 575        break;
 576
 577    case 0x3c:  /* PCLK Polarity */
 578        s->pclk = value & 0x82;
 579        /* Affects calculation of s->hndp, s->hsync and s->skipx.  */
 580        break;
 581
 582    case 0x3e:  /* High-speed Serial Interface Tx Configuration Port 0 */
 583        s->hssi_config[0] = value;
 584        break;
 585    case 0x40:  /* High-speed Serial Interface Tx Configuration Port 1 */
 586        s->hssi_config[1] = value;
 587        if (((value >> 4) & 3) == 3)
 588            fprintf(stderr, "%s: Illegal active-data-links value\n",
 589                            __func__);
 590        break;
 591    case 0x42:  /* High-speed Serial Interface Tx Mode */
 592        s->hssi_config[2] = value & 0xbd;
 593        break;
 594
 595    case 0x44:  /* TV Display Configuration */
 596        s->tv_config = value & 0xfe;
 597        break;
 598    case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */
 599        s->tv_timing[(reg - 0x46) >> 1] = value;
 600        break;
 601    case 0x4e:  /* VBI: Closed Caption / XDS Control / Status */
 602        s->vbi = value;
 603        break;
 604    case 0x50:  /* TV Horizontal Start Position */
 605        s->tv_x = value;
 606        break;
 607    case 0x52:  /* TV Vertical Start Position */
 608        s->tv_y = value & 0x7f;
 609        break;
 610    case 0x54:  /* TV Test Pattern Setting */
 611        s->tv_test = value;
 612        break;
 613    case 0x56:  /* TV Filter Setting */
 614        s->tv_filter_config = value & 0xbf;
 615        break;
 616    case 0x58:  /* TV Filter Coefficient Index */
 617        s->tv_filter_idx = value & 0x1f;
 618        break;
 619    case 0x5a:  /* TV Filter Coefficient Data */
 620        if (s->tv_filter_idx < 0x20)
 621            s->tv_filter_coeff[s->tv_filter_idx ++] = value;
 622        break;
 623
 624    case 0x60:  /* Input YUV/RGB Translate Mode 0 */
 625        s->yrc[0] = value & 0xb0;
 626        break;
 627    case 0x62:  /* Input YUV/RGB Translate Mode 1 */
 628        s->yrc[1] = value & 0x30;
 629        break;
 630    case 0x64:  /* U Data Fix */
 631        s->u = value & 0xff;
 632        break;
 633    case 0x66:  /* V Data Fix */
 634        s->v = value & 0xff;
 635        break;
 636
 637    case 0x68:  /* Display Mode */
 638        if ((s->mode ^ value) & 3)
 639            s->invalidate = 1;
 640        s->mode = value & 0xb7;
 641        s->enable = value & 1;
 642        s->blank = (value >> 1) & 1;
 643        if (value & (1 << 4))
 644            fprintf(stderr, "%s: Macrovision enable attempt!\n", __func__);
 645        break;
 646
 647    case 0x6a:  /* Special Effects */
 648        s->effect = value & 0xfb;
 649        break;
 650
 651    case 0x6c:  /* Input Window X Start Position 0 */
 652        s->ix[0] &= 0x300;
 653        s->ix[0] |= (value << 0) & 0x0ff;
 654        break;
 655    case 0x6e:  /* Input Window X Start Position 1 */
 656        s->ix[0] &= 0x0ff;
 657        s->ix[0] |= (value << 8) & 0x300;
 658        break;
 659    case 0x70:  /* Input Window Y Start Position 0 */
 660        s->iy[0] &= 0x300;
 661        s->iy[0] |= (value << 0) & 0x0ff;
 662        break;
 663    case 0x72:  /* Input Window Y Start Position 1 */
 664        s->iy[0] &= 0x0ff;
 665        s->iy[0] |= (value << 8) & 0x300;
 666        break;
 667    case 0x74:  /* Input Window X End Position 0 */
 668        s->ix[1] &= 0x300;
 669        s->ix[1] |= (value << 0) & 0x0ff;
 670        break;
 671    case 0x76:  /* Input Window X End Position 1 */
 672        s->ix[1] &= 0x0ff;
 673        s->ix[1] |= (value << 8) & 0x300;
 674        break;
 675    case 0x78:  /* Input Window Y End Position 0 */
 676        s->iy[1] &= 0x300;
 677        s->iy[1] |= (value << 0) & 0x0ff;
 678        break;
 679    case 0x7a:  /* Input Window Y End Position 1 */
 680        s->iy[1] &= 0x0ff;
 681        s->iy[1] |= (value << 8) & 0x300;
 682        break;
 683    case 0x7c:  /* Output Window X Start Position 0 */
 684        s->ox[0] &= 0x300;
 685        s->ox[0] |= (value << 0) & 0x0ff;
 686        break;
 687    case 0x7e:  /* Output Window X Start Position 1 */
 688        s->ox[0] &= 0x0ff;
 689        s->ox[0] |= (value << 8) & 0x300;
 690        break;
 691    case 0x80:  /* Output Window Y Start Position 0 */
 692        s->oy[0] &= 0x300;
 693        s->oy[0] |= (value << 0) & 0x0ff;
 694        break;
 695    case 0x82:  /* Output Window Y Start Position 1 */
 696        s->oy[0] &= 0x0ff;
 697        s->oy[0] |= (value << 8) & 0x300;
 698        break;
 699    case 0x84:  /* Output Window X End Position 0 */
 700        s->ox[1] &= 0x300;
 701        s->ox[1] |= (value << 0) & 0x0ff;
 702        break;
 703    case 0x86:  /* Output Window X End Position 1 */
 704        s->ox[1] &= 0x0ff;
 705        s->ox[1] |= (value << 8) & 0x300;
 706        break;
 707    case 0x88:  /* Output Window Y End Position 0 */
 708        s->oy[1] &= 0x300;
 709        s->oy[1] |= (value << 0) & 0x0ff;
 710        break;
 711    case 0x8a:  /* Output Window Y End Position 1 */
 712        s->oy[1] &= 0x0ff;
 713        s->oy[1] |= (value << 8) & 0x300;
 714        break;
 715
 716    case 0x8c:  /* Input Data Format */
 717        s->iformat = value & 0xf;
 718        s->bpp = blizzard_iformat_bpp[s->iformat];
 719        if (!s->bpp)
 720            fprintf(stderr, "%s: Illegal or unsupported input format %x\n",
 721                            __func__, s->iformat);
 722        break;
 723    case 0x8e:  /* Data Source Select */
 724        s->source = value & 7;
 725        /* Currently all windows will be "destructive overlays".  */
 726        if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] ||
 727                                        s->iy[0] != s->oy[0] ||
 728                                        s->ix[1] != s->ox[1] ||
 729                                        s->iy[1] != s->oy[1])) ||
 730                        !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) &
 731                          (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1))
 732            fprintf(stderr, "%s: Illegal input/output window positions\n",
 733                            __func__);
 734
 735        blizzard_transfer_setup(s);
 736        break;
 737
 738    case 0x90:  /* Display Memory Data Port */
 739        if (!s->data.len && !blizzard_transfer_setup(s))
 740            break;
 741
 742        *s->data.ptr ++ = value;
 743        if (-- s->data.len == 0)
 744            blizzard_window(s);
 745        break;
 746
 747    case 0xa8:  /* Border Color 0 */
 748        s->border_r = value;
 749        break;
 750    case 0xaa:  /* Border Color 1 */
 751        s->border_g = value;
 752        break;
 753    case 0xac:  /* Border Color 2 */
 754        s->border_b = value;
 755        break;
 756
 757    case 0xb4:  /* Gamma Correction Enable */
 758        s->gamma_config = value & 0x87;
 759        break;
 760    case 0xb6:  /* Gamma Correction Table Index */
 761        s->gamma_idx = value;
 762        break;
 763    case 0xb8:  /* Gamma Correction Table Data */
 764        s->gamma_lut[s->gamma_idx ++] = value;
 765        break;
 766
 767    case 0xba:  /* 3x3 Matrix Enable */
 768        s->matrix_ena = value & 1;
 769        break;
 770    case 0xbc ... 0xde: /* Coefficient Registers */
 771        s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff);
 772        break;
 773    case 0xe0:  /* 3x3 Matrix Red Offset */
 774        s->matrix_r = value;
 775        break;
 776    case 0xe2:  /* 3x3 Matrix Green Offset */
 777        s->matrix_g = value;
 778        break;
 779    case 0xe4:  /* 3x3 Matrix Blue Offset */
 780        s->matrix_b = value;
 781        break;
 782
 783    case 0xe6:  /* Power-save */
 784        s->pm = value & 0x83;
 785        if (value & s->mode & 1)
 786            fprintf(stderr, "%s: The display must be disabled before entering "
 787                            "Standby Mode\n", __func__);
 788        break;
 789    case 0xe8:  /* Non-display Period Control / Status */
 790        s->status = value & 0x1b;
 791        break;
 792    case 0xea:  /* RGB Interface Control */
 793        s->rgbgpio_dir = value & 0x8f;
 794        break;
 795    case 0xec:  /* RGB Interface Status */
 796        s->rgbgpio = value & 0xcf;
 797        break;
 798    case 0xee:  /* General-purpose IO Pins Configuration */
 799        s->gpio_dir = value;
 800        break;
 801    case 0xf0:  /* General-purpose IO Pins Status / Control */
 802        s->gpio = value;
 803        break;
 804    case 0xf2:  /* GPIO Positive Edge Interrupt Trigger */
 805        s->gpio_edge[0] = value;
 806        break;
 807    case 0xf4:  /* GPIO Negative Edge Interrupt Trigger */
 808        s->gpio_edge[1] = value;
 809        break;
 810    case 0xf6:  /* GPIO Interrupt Status */
 811        s->gpio_irq &= value;
 812        break;
 813    case 0xf8:  /* GPIO Pull-down Control */
 814        s->gpio_pdown = value;
 815        break;
 816
 817    default:
 818        fprintf(stderr, "%s: unknown register %02x\n", __func__, reg);
 819        break;
 820    }
 821}
 822
 823uint16_t s1d13745_read(void *opaque, int dc)
 824{
 825    BlizzardState *s = (BlizzardState *) opaque;
 826    uint16_t value = blizzard_reg_read(s, s->reg);
 827
 828    if (s->swallow -- > 0)
 829        return 0;
 830    if (dc)
 831        s->reg ++;
 832
 833    return value;
 834}
 835
 836void s1d13745_write(void *opaque, int dc, uint16_t value)
 837{
 838    BlizzardState *s = (BlizzardState *) opaque;
 839
 840    if (s->swallow -- > 0)
 841        return;
 842    if (dc) {
 843        blizzard_reg_write(s, s->reg, value);
 844
 845        if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8)
 846            s->reg += 2;
 847    } else
 848        s->reg = value & 0xff;
 849}
 850
 851void s1d13745_write_block(void *opaque, int dc,
 852                void *buf, size_t len, int pitch)
 853{
 854    BlizzardState *s = (BlizzardState *) opaque;
 855
 856    while (len > 0) {
 857        if (s->reg == 0x90 && dc &&
 858                        (s->data.len || blizzard_transfer_setup(s)) &&
 859                        len >= (s->data.len << 1)) {
 860            len -= s->data.len << 1;
 861            s->data.len = 0;
 862            s->data.data = buf;
 863            if (pitch)
 864                s->data.pitch = pitch;
 865            blizzard_window(s);
 866            s->data.data = s->data.buf;
 867            continue;
 868        }
 869
 870        s1d13745_write(opaque, dc, *(uint16_t *) buf);
 871        len -= 2;
 872        buf += 2;
 873    }
 874}
 875
 876static void blizzard_update_display(void *opaque)
 877{
 878    BlizzardState *s = (BlizzardState *) opaque;
 879    DisplaySurface *surface = qemu_console_surface(s->con);
 880    int y, bypp, bypl, bwidth;
 881    uint8_t *src, *dst;
 882
 883    if (!s->enable)
 884        return;
 885
 886    if (s->x != surface_width(surface) || s->y != surface_height(surface)) {
 887        s->invalidate = 1;
 888        qemu_console_resize(s->con, s->x, s->y);
 889        surface = qemu_console_surface(s->con);
 890    }
 891
 892    if (s->invalidate) {
 893        s->invalidate = 0;
 894
 895        if (s->blank) {
 896            bypp = surface_bytes_per_pixel(surface);
 897            memset(surface_data(surface), 0, bypp * s->x * s->y);
 898            return;
 899        }
 900
 901        s->mx[0] = 0;
 902        s->mx[1] = s->x;
 903        s->my[0] = 0;
 904        s->my[1] = s->y;
 905    }
 906
 907    if (s->mx[1] <= s->mx[0])
 908        return;
 909
 910    bypp = surface_bytes_per_pixel(surface);
 911    bypl = bypp * s->x;
 912    bwidth = bypp * (s->mx[1] - s->mx[0]);
 913    y = s->my[0];
 914    src = s->fb + bypl * y + bypp * s->mx[0];
 915    dst = surface_data(surface) + bypl * y + bypp * s->mx[0];
 916    for (; y < s->my[1]; y ++, src += bypl, dst += bypl)
 917        memcpy(dst, src, bwidth);
 918
 919    dpy_gfx_update(s->con, s->mx[0], s->my[0],
 920                   s->mx[1] - s->mx[0], y - s->my[0]);
 921
 922    s->mx[0] = s->x;
 923    s->mx[1] = 0;
 924    s->my[0] = s->y;
 925    s->my[1] = 0;
 926}
 927
 928static void blizzard_draw_line16_32(uint32_t *dest,
 929                                    const uint16_t *src, unsigned int width)
 930{
 931    uint16_t data;
 932    unsigned int r, g, b;
 933    const uint16_t *end = (const void *) src + width;
 934    while (src < end) {
 935        data = *src ++;
 936        b = extract16(data, 0, 5) << 3;
 937        g = extract16(data, 5, 6) << 2;
 938        r = extract16(data, 11, 5) << 3;
 939        *dest++ = rgb_to_pixel32(r, g, b);
 940    }
 941}
 942
 943static void blizzard_draw_line24mode1_32(uint32_t *dest,
 944                                         const uint8_t *src, unsigned int width)
 945{
 946    /* TODO: check if SDL 24-bit planes are not in the same format and
 947     * if so, use memcpy */
 948    unsigned int r[2], g[2], b[2];
 949    const uint8_t *end = src + width;
 950    while (src < end) {
 951        g[0] = *src ++;
 952        r[0] = *src ++;
 953        r[1] = *src ++;
 954        b[0] = *src ++;
 955        *dest++ = rgb_to_pixel32(r[0], g[0], b[0]);
 956        b[1] = *src ++;
 957        g[1] = *src ++;
 958        *dest++ = rgb_to_pixel32(r[1], g[1], b[1]);
 959    }
 960}
 961
 962static void blizzard_draw_line24mode2_32(uint32_t *dest,
 963                                         const uint8_t *src, unsigned int width)
 964{
 965    unsigned int r, g, b;
 966    const uint8_t *end = src + width;
 967    while (src < end) {
 968        r = *src ++;
 969        src ++;
 970        b = *src ++;
 971        g = *src ++;
 972        *dest++ = rgb_to_pixel32(r, g, b);
 973    }
 974}
 975
 976/* No rotation */
 977static blizzard_fn_t blizzard_draw_fn_32[0x10] = {
 978    NULL,
 979    /* RGB 5:6:5*/
 980    (blizzard_fn_t) blizzard_draw_line16_32,
 981    /* RGB 6:6:6 mode 1 */
 982    (blizzard_fn_t) blizzard_draw_line24mode1_32,
 983    /* RGB 8:8:8 mode 1 */
 984    (blizzard_fn_t) blizzard_draw_line24mode1_32,
 985    NULL, NULL,
 986    /* RGB 6:6:6 mode 2 */
 987    (blizzard_fn_t) blizzard_draw_line24mode2_32,
 988    /* RGB 8:8:8 mode 2 */
 989    (blizzard_fn_t) blizzard_draw_line24mode2_32,
 990    /* YUV 4:2:2 */
 991    NULL,
 992    /* YUV 4:2:0 */
 993    NULL,
 994    NULL, NULL, NULL, NULL, NULL, NULL,
 995};
 996
 997/* 90deg, 180deg and 270deg rotation */
 998static blizzard_fn_t blizzard_draw_fn_r_32[0x10] = {
 999    /* TODO */
1000    [0 ... 0xf] = NULL,
1001};
1002
1003static const GraphicHwOps blizzard_ops = {
1004    .invalidate  = blizzard_invalidate_display,
1005    .gfx_update  = blizzard_update_display,
1006};
1007
1008void *s1d13745_init(qemu_irq gpio_int)
1009{
1010    BlizzardState *s = (BlizzardState *) g_malloc0(sizeof(*s));
1011    DisplaySurface *surface;
1012
1013    s->fb = g_malloc(0x180000);
1014
1015    s->con = graphic_console_init(NULL, 0, &blizzard_ops, s);
1016    surface = qemu_console_surface(s->con);
1017
1018    assert(surface_bits_per_pixel(surface) == 32);
1019
1020    s->line_fn_tab[0] = blizzard_draw_fn_32;
1021    s->line_fn_tab[1] = blizzard_draw_fn_r_32;
1022
1023    blizzard_reset(s);
1024
1025    return s;
1026}
1027