| 1 | |
| 2 | * linux/drivers/video/bt431.h
|
| 3 | *
|
| 4 | * Copyright 2003 Thiemo Seufer <[email protected]>
|
| 5 | * Copyright 2016 Maciej W. Rozycki <[email protected]>
|
| 6 | *
|
| 7 | * This file is subject to the terms and conditions of the GNU General
|
| 8 | * Public License. See the file COPYING in the main directory of this
|
| 9 | * archive for more details.
|
| 10 |
|
| 11 | #include <linux/types.h>
|
| 12 |
|
| 13 | #define BT431_CURSOR_SIZE 64
|
| 14 |
|
| 15 | |
| 16 | * Bt431 cursor generator registers, 32-bit aligned.
|
| 17 | * Two twin Bt431 are used on the DECstation's PMAG-AA.
|
| 18 |
|
| 19 | struct bt431_regs {
|
| 20 | volatile u16 addr_lo;
|
| 21 | u16 pad0;
|
| 22 | volatile u16 addr_hi;
|
| 23 | u16 pad1;
|
| 24 | volatile u16 addr_cmap;
|
| 25 | u16 pad2;
|
| 26 | volatile u16 addr_reg;
|
| 27 | u16 pad3;
|
| 28 | };
|
| 29 |
|
| 30 | static inline u16 bt431_set_value(u8 val)
|
| 31 | {
|
| 32 | return ((val << 8) | (val & 0xff)) & 0xffff;
|
| 33 | }
|
| 34 |
|
| 35 | static inline u8 bt431_get_value(u16 val)
|
| 36 | {
|
| 37 | return val & 0xff;
|
| 38 | }
|
| 39 |
|
| 40 | |
| 41 | * Additional registers addressed indirectly.
|
| 42 |
|
| 43 | #define BT431_REG_CMD 0x0000
|
| 44 | #define BT431_REG_CXLO 0x0001
|
| 45 | #define BT431_REG_CXHI 0x0002
|
| 46 | #define BT431_REG_CYLO 0x0003
|
| 47 | #define BT431_REG_CYHI 0x0004
|
| 48 | #define BT431_REG_WXLO 0x0005
|
| 49 | #define BT431_REG_WXHI 0x0006
|
| 50 | #define BT431_REG_WYLO 0x0007
|
| 51 | #define BT431_REG_WYHI 0x0008
|
| 52 | #define BT431_REG_WWLO 0x0009
|
| 53 | #define BT431_REG_WWHI 0x000a
|
| 54 | #define BT431_REG_WHLO 0x000b
|
| 55 | #define BT431_REG_WHHI 0x000c
|
| 56 |
|
| 57 | #define BT431_REG_CRAM_BASE 0x0000
|
| 58 | #define BT431_REG_CRAM_END 0x01ff
|
| 59 |
|
| 60 | |
| 61 | * Command register.
|
| 62 |
|
| 63 | #define BT431_CMD_CURS_ENABLE 0x40
|
| 64 | #define BT431_CMD_XHAIR_ENABLE 0x20
|
| 65 | #define BT431_CMD_OR_CURSORS 0x10
|
| 66 | #define BT431_CMD_XOR_CURSORS 0x00
|
| 67 | #define BT431_CMD_1_1_MUX 0x00
|
| 68 | #define BT431_CMD_4_1_MUX 0x04
|
| 69 | #define BT431_CMD_5_1_MUX 0x08
|
| 70 | #define BT431_CMD_xxx_MUX 0x0c
|
| 71 | #define BT431_CMD_THICK_1 0x00
|
| 72 | #define BT431_CMD_THICK_3 0x01
|
| 73 | #define BT431_CMD_THICK_5 0x02
|
| 74 | #define BT431_CMD_THICK_7 0x03
|
| 75 |
|
| 76 | static inline void bt431_select_reg(struct bt431_regs *regs, int ir)
|
| 77 | {
|
| 78 | |
| 79 | * The compiler splits the write in two bytes without these
|
| 80 | * helper variables.
|
| 81 |
|
| 82 | volatile u16 *lo = &(regs->addr_lo);
|
| 83 | volatile u16 *hi = &(regs->addr_hi);
|
| 84 |
|
| 85 | mb();
|
| 86 | *lo = bt431_set_value(ir & 0xff);
|
| 87 | wmb();
|
| 88 | *hi = bt431_set_value((ir >> 8) & 0xff);
|
| 89 | }
|
| 90 |
|
| 91 |
|
| 92 | static inline u8 bt431_read_reg_inc(struct bt431_regs *regs)
|
| 93 | {
|
| 94 | |
| 95 | * The compiler splits the write in two bytes without the
|
| 96 | * helper variable.
|
| 97 |
|
| 98 | volatile u16 *r = &(regs->addr_reg);
|
| 99 |
|
| 100 | mb();
|
| 101 | return bt431_get_value(*r);
|
| 102 | }
|
| 103 |
|
| 104 | static inline void bt431_write_reg_inc(struct bt431_regs *regs, u8 value)
|
| 105 | {
|
| 106 | |
| 107 | * The compiler splits the write in two bytes without the
|
| 108 | * helper variable.
|
| 109 |
|
| 110 | volatile u16 *r = &(regs->addr_reg);
|
| 111 |
|
| 112 | mb();
|
| 113 | *r = bt431_set_value(value);
|
| 114 | }
|
| 115 |
|
| 116 | static inline u8 bt431_read_reg(struct bt431_regs *regs, int ir)
|
| 117 | {
|
| 118 | bt431_select_reg(regs, ir);
|
| 119 | return bt431_read_reg_inc(regs);
|
| 120 | }
|
| 121 |
|
| 122 | static inline void bt431_write_reg(struct bt431_regs *regs, int ir, u8 value)
|
| 123 | {
|
| 124 | bt431_select_reg(regs, ir);
|
| 125 | bt431_write_reg_inc(regs, value);
|
| 126 | }
|
| 127 |
|
| 128 |
|
| 129 | static inline u16 bt431_read_cmap_inc(struct bt431_regs *regs)
|
| 130 | {
|
| 131 | |
| 132 | * The compiler splits the write in two bytes without the
|
| 133 | * helper variable.
|
| 134 |
|
| 135 | volatile u16 *r = &(regs->addr_cmap);
|
| 136 |
|
| 137 | mb();
|
| 138 | return *r;
|
| 139 | }
|
| 140 |
|
| 141 | static inline void bt431_write_cmap_inc(struct bt431_regs *regs, u16 value)
|
| 142 | {
|
| 143 | |
| 144 | * The compiler splits the write in two bytes without the
|
| 145 | * helper variable.
|
| 146 |
|
| 147 | volatile u16 *r = &(regs->addr_cmap);
|
| 148 |
|
| 149 | mb();
|
| 150 | *r = value;
|
| 151 | }
|
| 152 |
|
| 153 | static inline u16 bt431_read_cmap(struct bt431_regs *regs, int cr)
|
| 154 | {
|
| 155 | bt431_select_reg(regs, cr);
|
| 156 | return bt431_read_cmap_inc(regs);
|
| 157 | }
|
| 158 |
|
| 159 | static inline void bt431_write_cmap(struct bt431_regs *regs, int cr, u16 value)
|
| 160 | {
|
| 161 | bt431_select_reg(regs, cr);
|
| 162 | bt431_write_cmap_inc(regs, value);
|
| 163 | }
|
| 164 |
|
| 165 | static inline void bt431_enable_cursor(struct bt431_regs *regs)
|
| 166 | {
|
| 167 | bt431_write_reg(regs, BT431_REG_CMD,
|
| 168 | BT431_CMD_CURS_ENABLE | BT431_CMD_OR_CURSORS
|
| 169 | | BT431_CMD_4_1_MUX | BT431_CMD_THICK_1);
|
| 170 | }
|
| 171 |
|
| 172 | static inline void bt431_erase_cursor(struct bt431_regs *regs)
|
| 173 | {
|
| 174 | bt431_write_reg(regs, BT431_REG_CMD, BT431_CMD_4_1_MUX);
|
| 175 | }
|
| 176 |
|
| 177 | static inline void bt431_position_cursor(struct bt431_regs *regs, u16 x, u16 y)
|
| 178 | {
|
| 179 | |
| 180 | * Magic from the MACH sources.
|
| 181 | *
|
| 182 | * Cx = x + D + H - P
|
| 183 | * P = 37 if 1:1, 52 if 4:1, 57 if 5:1
|
| 184 | * D = pixel skew between outdata and external data
|
| 185 | * H = pixels between HSYNCH falling and active video
|
| 186 | *
|
| 187 | * Cy = y + V - 32
|
| 188 | * V = scanlines between HSYNCH falling, two or more
|
| 189 | * clocks after VSYNCH falling, and active video
|
| 190 |
|
| 191 | x += 412 - 52;
|
| 192 | y += 68 - 32;
|
| 193 |
|
| 194 |
|
| 195 | bt431_select_reg(regs, BT431_REG_CXLO);
|
| 196 | bt431_write_reg_inc(regs, x & 0xff);
|
| 197 | bt431_write_reg_inc(regs, (x >> 8) & 0x0f);
|
| 198 | bt431_write_reg_inc(regs, y & 0xff);
|
| 199 | bt431_write_reg_inc(regs, (y >> 8) & 0x0f);
|
| 200 | }
|
| 201 |
|
| 202 | static inline void bt431_set_cursor(struct bt431_regs *regs,
|
| 203 | const char *data, const char *mask,
|
| 204 | u16 rop, u16 width, u16 height)
|
| 205 | {
|
| 206 | u16 x, y;
|
| 207 | int i;
|
| 208 |
|
| 209 | i = 0;
|
| 210 | width = DIV_ROUND_UP(width, 8);
|
| 211 | bt431_select_reg(regs, BT431_REG_CRAM_BASE);
|
| 212 | for (y = 0; y < BT431_CURSOR_SIZE; y++)
|
| 213 | for (x = 0; x < BT431_CURSOR_SIZE / 8; x++) {
|
| 214 | u16 val = 0;
|
| 215 |
|
| 216 | if (y < height && x < width) {
|
| 217 | val = mask[i];
|
| 218 | if (rop == ROP_XOR)
|
| 219 | val = (val << 8) | (val ^ data[i]);
|
| 220 | else
|
| 221 | val = (val << 8) | (val & data[i]);
|
| 222 | i++;
|
| 223 | }
|
| 224 | bt431_write_cmap_inc(regs, val);
|
| 225 | }
|
| 226 | }
|
| 227 |
|
| 228 | static inline void bt431_init_cursor(struct bt431_regs *regs)
|
| 229 | {
|
| 230 |
|
| 231 | bt431_select_reg(regs, BT431_REG_WXLO);
|
| 232 | bt431_write_reg_inc(regs, 0x00);
|
| 233 | bt431_write_reg_inc(regs, 0x00);
|
| 234 | bt431_write_reg_inc(regs, 0x00);
|
| 235 | bt431_write_reg_inc(regs, 0x00);
|
| 236 | bt431_write_reg_inc(regs, 0x00);
|
| 237 | bt431_write_reg_inc(regs, 0x00);
|
| 238 | bt431_write_reg_inc(regs, 0x00);
|
| 239 | bt431_write_reg_inc(regs, 0x00);
|
| 240 | }
|