~vcs-imports/qemu/git

« back to all changes in this revision

Viewing changes to hw/tsc2005.c

  • Committer: bellard
  • Date: 2003-03-23 20:17:16 UTC
  • Revision ID: git-v1:3ef693a03205217a5def9318b443c8cb6de17217
distribution patches


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@44 c046a42c-6fe2-441c-8c8c-71466251a162

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * TI TSC2005 emulator.
3
 
 *
4
 
 * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
5
 
 * Copyright (C) 2008 Nokia Corporation
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
18
 
 * along with this program; if not, write to the Free Software
19
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20
 
 * MA 02111-1307 USA
21
 
 */
22
 
 
23
 
#include "hw.h"
24
 
#include "qemu-timer.h"
25
 
#include "console.h"
26
 
#include "devices.h"
27
 
 
28
 
#define TSC_CUT_RESOLUTION(value, p)    ((value) >> (16 - (p ? 12 : 10)))
29
 
 
30
 
struct tsc2005_state_s {
31
 
    qemu_irq pint;      /* Combination of the nPENIRQ and DAV signals */
32
 
    QEMUTimer *timer;
33
 
    uint16_t model;
34
 
 
35
 
    int x, y;
36
 
    int pressure;
37
 
 
38
 
    int state, reg, irq, command;
39
 
    uint16_t data, dav;
40
 
 
41
 
    int busy;
42
 
    int enabled;
43
 
    int host_mode;
44
 
    int function;
45
 
    int nextfunction;
46
 
    int precision;
47
 
    int nextprecision;
48
 
    int filter;
49
 
    int pin_func;
50
 
    int timing[2];
51
 
    int noise;
52
 
    int reset;
53
 
    int pdst;
54
 
    int pnd0;
55
 
    uint16_t temp_thr[2];
56
 
    uint16_t aux_thr[2];
57
 
 
58
 
    int tr[8];
59
 
};
60
 
 
61
 
enum {
62
 
    TSC_MODE_XYZ_SCAN   = 0x0,
63
 
    TSC_MODE_XY_SCAN,
64
 
    TSC_MODE_X,
65
 
    TSC_MODE_Y,
66
 
    TSC_MODE_Z,
67
 
    TSC_MODE_AUX,
68
 
    TSC_MODE_TEMP1,
69
 
    TSC_MODE_TEMP2,
70
 
    TSC_MODE_AUX_SCAN,
71
 
    TSC_MODE_X_TEST,
72
 
    TSC_MODE_Y_TEST,
73
 
    TSC_MODE_TS_TEST,
74
 
    TSC_MODE_RESERVED,
75
 
    TSC_MODE_XX_DRV,
76
 
    TSC_MODE_YY_DRV,
77
 
    TSC_MODE_YX_DRV,
78
 
};
79
 
 
80
 
static const uint16_t mode_regs[16] = {
81
 
    0xf000,     /* X, Y, Z scan */
82
 
    0xc000,     /* X, Y scan */
83
 
    0x8000,     /* X */
84
 
    0x4000,     /* Y */
85
 
    0x3000,     /* Z */
86
 
    0x0800,     /* AUX */
87
 
    0x0400,     /* TEMP1 */
88
 
    0x0200,     /* TEMP2 */
89
 
    0x0800,     /* AUX scan */
90
 
    0x0040,     /* X test */
91
 
    0x0020,     /* Y test */
92
 
    0x0080,     /* Short-circuit test */
93
 
    0x0000,     /* Reserved */
94
 
    0x0000,     /* X+, X- drivers */
95
 
    0x0000,     /* Y+, Y- drivers */
96
 
    0x0000,     /* Y+, X- drivers */
97
 
};
98
 
 
99
 
#define X_TRANSFORM(s)                  \
100
 
    ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
101
 
#define Y_TRANSFORM(s)                  \
102
 
    ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
103
 
#define Z1_TRANSFORM(s)                 \
104
 
    ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
105
 
#define Z2_TRANSFORM(s)                 \
106
 
    ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
107
 
 
108
 
#define AUX_VAL                         (700 << 4)      /* +/- 3 at 12-bit */
109
 
#define TEMP1_VAL                       (1264 << 4)     /* +/- 5 at 12-bit */
110
 
#define TEMP2_VAL                       (1531 << 4)     /* +/- 5 at 12-bit */
111
 
 
112
 
static uint16_t tsc2005_read(struct tsc2005_state_s *s, int reg)
113
 
{
114
 
    uint16_t ret;
115
 
 
116
 
    switch (reg) {
117
 
    case 0x0:   /* X */
118
 
        s->dav &= ~mode_regs[TSC_MODE_X];
119
 
        return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
120
 
                (s->noise & 3);
121
 
    case 0x1:   /* Y */
122
 
        s->dav &= ~mode_regs[TSC_MODE_Y];
123
 
        s->noise ++;
124
 
        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
125
 
                (s->noise & 3);
126
 
    case 0x2:   /* Z1 */
127
 
        s->dav &= 0xdfff;
128
 
        return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
129
 
                (s->noise & 3);
130
 
    case 0x3:   /* Z2 */
131
 
        s->dav &= 0xefff;
132
 
        return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
133
 
                (s->noise & 3);
134
 
 
135
 
    case 0x4:   /* AUX */
136
 
        s->dav &= ~mode_regs[TSC_MODE_AUX];
137
 
        return TSC_CUT_RESOLUTION(AUX_VAL, s->precision);
138
 
 
139
 
    case 0x5:   /* TEMP1 */
140
 
        s->dav &= ~mode_regs[TSC_MODE_TEMP1];
141
 
        return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
142
 
                (s->noise & 5);
143
 
    case 0x6:   /* TEMP2 */
144
 
        s->dav &= 0xdfff;
145
 
        s->dav &= ~mode_regs[TSC_MODE_TEMP2];
146
 
        return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
147
 
                (s->noise & 3);
148
 
 
149
 
    case 0x7:   /* Status */
150
 
        ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
151
 
        s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
152
 
                        mode_regs[TSC_MODE_TS_TEST]);
153
 
        s->reset = 1;
154
 
        return ret;
155
 
 
156
 
    case 0x8:   /* AUX high treshold */
157
 
        return s->aux_thr[1];
158
 
    case 0x9:   /* AUX low treshold */
159
 
        return s->aux_thr[0];
160
 
 
161
 
    case 0xa:   /* TEMP high treshold */
162
 
        return s->temp_thr[1];
163
 
    case 0xb:   /* TEMP low treshold */
164
 
        return s->temp_thr[0];
165
 
 
166
 
    case 0xc:   /* CFR0 */
167
 
        return (s->pressure << 15) | ((!s->busy) << 14) |
168
 
                (s->nextprecision << 13) | s->timing[0]; 
169
 
    case 0xd:   /* CFR1 */
170
 
        return s->timing[1];
171
 
    case 0xe:   /* CFR2 */
172
 
        return (s->pin_func << 14) | s->filter;
173
 
 
174
 
    case 0xf:   /* Function select status */
175
 
        return s->function >= 0 ? 1 << s->function : 0;
176
 
    }
177
 
 
178
 
    /* Never gets here */
179
 
    return 0xffff;
180
 
}
181
 
 
182
 
static void tsc2005_write(struct tsc2005_state_s *s, int reg, uint16_t data)
183
 
{
184
 
    switch (reg) {
185
 
    case 0x8:   /* AUX high treshold */
186
 
        s->aux_thr[1] = data;
187
 
        break;
188
 
    case 0x9:   /* AUX low treshold */
189
 
        s->aux_thr[0] = data;
190
 
        break;
191
 
 
192
 
    case 0xa:   /* TEMP high treshold */
193
 
        s->temp_thr[1] = data;
194
 
        break;
195
 
    case 0xb:   /* TEMP low treshold */
196
 
        s->temp_thr[0] = data;
197
 
        break;
198
 
 
199
 
    case 0xc:   /* CFR0 */
200
 
        s->host_mode = data >> 15;
201
 
        if (s->enabled != !(data & 0x4000)) {
202
 
            s->enabled = !(data & 0x4000);
203
 
            fprintf(stderr, "%s: touchscreen sense %sabled\n",
204
 
                            __FUNCTION__, s->enabled ? "en" : "dis");
205
 
            if (s->busy && !s->enabled)
206
 
                qemu_del_timer(s->timer);
207
 
            s->busy &= s->enabled;
208
 
        }
209
 
        s->nextprecision = (data >> 13) & 1;
210
 
        s->timing[0] = data & 0x1fff;
211
 
        if ((s->timing[0] >> 11) == 3)
212
 
            fprintf(stderr, "%s: illegal conversion clock setting\n",
213
 
                            __FUNCTION__);
214
 
        break;
215
 
    case 0xd:   /* CFR1 */
216
 
        s->timing[1] = data & 0xf07;
217
 
        break;
218
 
    case 0xe:   /* CFR2 */
219
 
        s->pin_func = (data >> 14) & 3;
220
 
        s->filter = data & 0x3fff;
221
 
        break;
222
 
 
223
 
    default:
224
 
        fprintf(stderr, "%s: write into read-only register %x\n",
225
 
                        __FUNCTION__, reg);
226
 
    }
227
 
}
228
 
 
229
 
/* This handles most of the chip's logic.  */
230
 
static void tsc2005_pin_update(struct tsc2005_state_s *s)
231
 
{
232
 
    int64_t expires;
233
 
    int pin_state;
234
 
 
235
 
    switch (s->pin_func) {
236
 
    case 0:
237
 
        pin_state = !s->pressure && !!s->dav;
238
 
        break;
239
 
    case 1:
240
 
    case 3:
241
 
    default:
242
 
        pin_state = !s->dav;
243
 
        break;
244
 
    case 2:
245
 
        pin_state = !s->pressure;
246
 
    }
247
 
 
248
 
    if (pin_state != s->irq) {
249
 
        s->irq = pin_state;
250
 
        qemu_set_irq(s->pint, s->irq);
251
 
    }
252
 
 
253
 
    switch (s->nextfunction) {
254
 
    case TSC_MODE_XYZ_SCAN:
255
 
    case TSC_MODE_XY_SCAN:
256
 
        if (!s->host_mode && s->dav)
257
 
            s->enabled = 0;
258
 
        if (!s->pressure)
259
 
            return;
260
 
        /* Fall through */
261
 
    case TSC_MODE_AUX_SCAN:
262
 
        break;
263
 
 
264
 
    case TSC_MODE_X:
265
 
    case TSC_MODE_Y:
266
 
    case TSC_MODE_Z:
267
 
        if (!s->pressure)
268
 
            return;
269
 
        /* Fall through */
270
 
    case TSC_MODE_AUX:
271
 
    case TSC_MODE_TEMP1:
272
 
    case TSC_MODE_TEMP2:
273
 
    case TSC_MODE_X_TEST:
274
 
    case TSC_MODE_Y_TEST:
275
 
    case TSC_MODE_TS_TEST:
276
 
        if (s->dav)
277
 
            s->enabled = 0;
278
 
        break;
279
 
 
280
 
    case TSC_MODE_RESERVED:
281
 
    case TSC_MODE_XX_DRV:
282
 
    case TSC_MODE_YY_DRV:
283
 
    case TSC_MODE_YX_DRV:
284
 
    default:
285
 
        return;
286
 
    }
287
 
 
288
 
    if (!s->enabled || s->busy)
289
 
        return;
290
 
 
291
 
    s->busy = 1;
292
 
    s->precision = s->nextprecision;
293
 
    s->function = s->nextfunction;
294
 
    s->pdst = !s->pnd0; /* Synchronised on internal clock */
295
 
    expires = qemu_get_clock(vm_clock) + (ticks_per_sec >> 7);
296
 
    qemu_mod_timer(s->timer, expires);
297
 
}
298
 
 
299
 
static void tsc2005_reset(struct tsc2005_state_s *s)
300
 
{
301
 
    s->state = 0;
302
 
    s->pin_func = 0;
303
 
    s->enabled = 0;
304
 
    s->busy = 0;
305
 
    s->nextprecision = 0;
306
 
    s->nextfunction = 0;
307
 
    s->timing[0] = 0;
308
 
    s->timing[1] = 0;
309
 
    s->irq = 0;
310
 
    s->dav = 0;
311
 
    s->reset = 0;
312
 
    s->pdst = 1;
313
 
    s->pnd0 = 0;
314
 
    s->function = -1;
315
 
    s->temp_thr[0] = 0x000;
316
 
    s->temp_thr[1] = 0xfff;
317
 
    s->aux_thr[0] = 0x000;
318
 
    s->aux_thr[1] = 0xfff;
319
 
 
320
 
    tsc2005_pin_update(s);
321
 
}
322
 
 
323
 
static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
324
 
{
325
 
    struct tsc2005_state_s *s = opaque;
326
 
    uint32_t ret = 0;
327
 
 
328
 
    switch (s->state ++) {
329
 
    case 0:
330
 
        if (value & 0x80) {
331
 
            /* Command */
332
 
            if (value & (1 << 1))
333
 
                tsc2005_reset(s);
334
 
            else {
335
 
                s->nextfunction = (value >> 3) & 0xf;
336
 
                s->nextprecision = (value >> 2) & 1;
337
 
                if (s->enabled != !(value & 1)) {
338
 
                    s->enabled = !(value & 1);
339
 
                    fprintf(stderr, "%s: touchscreen sense %sabled\n",
340
 
                                    __FUNCTION__, s->enabled ? "en" : "dis");
341
 
                    if (s->busy && !s->enabled)
342
 
                        qemu_del_timer(s->timer);
343
 
                    s->busy &= s->enabled;
344
 
                }
345
 
                tsc2005_pin_update(s);
346
 
            }
347
 
 
348
 
            s->state = 0;
349
 
        } else if (value) {
350
 
            /* Data transfer */
351
 
            s->reg = (value >> 3) & 0xf;
352
 
            s->pnd0 = (value >> 1) & 1;
353
 
            s->command = value & 1;
354
 
 
355
 
            if (s->command) {
356
 
                /* Read */
357
 
                s->data = tsc2005_read(s, s->reg);
358
 
                tsc2005_pin_update(s);
359
 
            } else
360
 
                s->data = 0;
361
 
        } else
362
 
            s->state = 0;
363
 
        break;
364
 
 
365
 
    case 1:
366
 
        if (s->command)
367
 
            ret = (s->data >> 8) & 0xff;
368
 
        else
369
 
            s->data |= value << 8;
370
 
        break;
371
 
 
372
 
    case 2:
373
 
        if (s->command)
374
 
            ret = s->data & 0xff;
375
 
        else {
376
 
            s->data |= value;
377
 
            tsc2005_write(s, s->reg, s->data);
378
 
            tsc2005_pin_update(s);
379
 
        }
380
 
 
381
 
        s->state = 0;
382
 
        break;
383
 
    }
384
 
 
385
 
    return ret;
386
 
}
387
 
 
388
 
uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len)
389
 
{
390
 
    uint32_t ret = 0;
391
 
 
392
 
    len &= ~7;
393
 
    while (len > 0) {
394
 
        len -= 8;
395
 
        ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len;
396
 
    }
397
 
 
398
 
    return ret;
399
 
}
400
 
 
401
 
static void tsc2005_timer_tick(void *opaque)
402
 
{
403
 
    struct tsc2005_state_s *s = opaque;
404
 
 
405
 
    /* Timer ticked -- a set of conversions has been finished.  */
406
 
 
407
 
    if (!s->busy)
408
 
        return;
409
 
 
410
 
    s->busy = 0;
411
 
    s->dav |= mode_regs[s->function];
412
 
    s->function = -1;
413
 
    tsc2005_pin_update(s);
414
 
}
415
 
 
416
 
static void tsc2005_touchscreen_event(void *opaque,
417
 
                int x, int y, int z, int buttons_state)
418
 
{
419
 
    struct tsc2005_state_s *s = opaque;
420
 
    int p = s->pressure;
421
 
 
422
 
    if (buttons_state) {
423
 
        s->x = x;
424
 
        s->y = y;
425
 
    }
426
 
    s->pressure = !!buttons_state;
427
 
 
428
 
    /*
429
 
     * Note: We would get better responsiveness in the guest by
430
 
     * signaling TS events immediately, but for now we simulate
431
 
     * the first conversion delay for sake of correctness.
432
 
     */
433
 
    if (p != s->pressure)
434
 
        tsc2005_pin_update(s);
435
 
}
436
 
 
437
 
static void tsc2005_save(QEMUFile *f, void *opaque)
438
 
{
439
 
    struct tsc2005_state_s *s = (struct tsc2005_state_s *) opaque;
440
 
    int i;
441
 
 
442
 
    qemu_put_be16(f, s->x);
443
 
    qemu_put_be16(f, s->y);
444
 
    qemu_put_byte(f, s->pressure);
445
 
 
446
 
    qemu_put_byte(f, s->state);
447
 
    qemu_put_byte(f, s->reg);
448
 
    qemu_put_byte(f, s->command);
449
 
 
450
 
    qemu_put_byte(f, s->irq);
451
 
    qemu_put_be16s(f, &s->dav);
452
 
    qemu_put_be16s(f, &s->data);
453
 
 
454
 
    qemu_put_timer(f, s->timer);
455
 
    qemu_put_byte(f, s->enabled);
456
 
    qemu_put_byte(f, s->host_mode);
457
 
    qemu_put_byte(f, s->function);
458
 
    qemu_put_byte(f, s->nextfunction);
459
 
    qemu_put_byte(f, s->precision);
460
 
    qemu_put_byte(f, s->nextprecision);
461
 
    qemu_put_be16(f, s->filter);
462
 
    qemu_put_byte(f, s->pin_func);
463
 
    qemu_put_be16(f, s->timing[0]);
464
 
    qemu_put_be16(f, s->timing[1]);
465
 
    qemu_put_be16s(f, &s->temp_thr[0]);
466
 
    qemu_put_be16s(f, &s->temp_thr[1]);
467
 
    qemu_put_be16s(f, &s->aux_thr[0]);
468
 
    qemu_put_be16s(f, &s->aux_thr[1]);
469
 
    qemu_put_be32(f, s->noise);
470
 
    qemu_put_byte(f, s->reset);
471
 
    qemu_put_byte(f, s->pdst);
472
 
    qemu_put_byte(f, s->pnd0);
473
 
 
474
 
    for (i = 0; i < 8; i ++)
475
 
        qemu_put_be32(f, s->tr[i]);
476
 
}
477
 
 
478
 
static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
479
 
{
480
 
    struct tsc2005_state_s *s = (struct tsc2005_state_s *) opaque;
481
 
    int i;
482
 
 
483
 
    s->x = qemu_get_be16(f);
484
 
    s->y = qemu_get_be16(f);
485
 
    s->pressure = qemu_get_byte(f);
486
 
 
487
 
    s->state = qemu_get_byte(f);
488
 
    s->reg = qemu_get_byte(f);
489
 
    s->command = qemu_get_byte(f);
490
 
 
491
 
    s->irq = qemu_get_byte(f);
492
 
    qemu_get_be16s(f, &s->dav);
493
 
    qemu_get_be16s(f, &s->data);
494
 
 
495
 
    qemu_get_timer(f, s->timer);
496
 
    s->enabled = qemu_get_byte(f);
497
 
    s->host_mode = qemu_get_byte(f);
498
 
    s->function = qemu_get_byte(f);
499
 
    s->nextfunction = qemu_get_byte(f);
500
 
    s->precision = qemu_get_byte(f);
501
 
    s->nextprecision = qemu_get_byte(f);
502
 
    s->filter = qemu_get_be16(f);
503
 
    s->pin_func = qemu_get_byte(f);
504
 
    s->timing[0] = qemu_get_be16(f);
505
 
    s->timing[1] = qemu_get_be16(f);
506
 
    qemu_get_be16s(f, &s->temp_thr[0]);
507
 
    qemu_get_be16s(f, &s->temp_thr[1]);
508
 
    qemu_get_be16s(f, &s->aux_thr[0]);
509
 
    qemu_get_be16s(f, &s->aux_thr[1]);
510
 
    s->noise = qemu_get_be32(f);
511
 
    s->reset = qemu_get_byte(f);
512
 
    s->pdst = qemu_get_byte(f);
513
 
    s->pnd0 = qemu_get_byte(f);
514
 
 
515
 
    for (i = 0; i < 8; i ++)
516
 
        s->tr[i] = qemu_get_be32(f);
517
 
 
518
 
    s->busy = qemu_timer_pending(s->timer);
519
 
    tsc2005_pin_update(s);
520
 
 
521
 
    return 0;
522
 
}
523
 
 
524
 
void *tsc2005_init(qemu_irq pintdav)
525
 
{
526
 
    struct tsc2005_state_s *s;
527
 
 
528
 
    s = (struct tsc2005_state_s *)
529
 
            qemu_mallocz(sizeof(struct tsc2005_state_s));
530
 
    s->x = 400;
531
 
    s->y = 240;
532
 
    s->pressure = 0;
533
 
    s->precision = s->nextprecision = 0;
534
 
    s->timer = qemu_new_timer(vm_clock, tsc2005_timer_tick, s);
535
 
    s->pint = pintdav;
536
 
    s->model = 0x2005;
537
 
 
538
 
    s->tr[0] = 0;
539
 
    s->tr[1] = 1;
540
 
    s->tr[2] = 1;
541
 
    s->tr[3] = 0;
542
 
    s->tr[4] = 1;
543
 
    s->tr[5] = 0;
544
 
    s->tr[6] = 1;
545
 
    s->tr[7] = 0;
546
 
 
547
 
    tsc2005_reset(s);
548
 
 
549
 
    qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1,
550
 
                    "QEMU TSC2005-driven Touchscreen");
551
 
 
552
 
    qemu_register_reset((void *) tsc2005_reset, s);
553
 
    register_savevm("tsc2005", -1, 0, tsc2005_save, tsc2005_load, s);
554
 
 
555
 
    return s;
556
 
}
557
 
 
558
 
/*
559
 
 * Use tslib generated calibration data to generate ADC input values
560
 
 * from the touchscreen.  Assuming 12-bit precision was used during
561
 
 * tslib calibration.
562
 
 */
563
 
void tsc2005_set_transform(void *opaque, struct mouse_transform_info_s *info)
564
 
{
565
 
    struct tsc2005_state_s *s = (struct tsc2005_state_s *) opaque;
566
 
 
567
 
    /* This version assumes touchscreen X & Y axis are parallel or
568
 
     * perpendicular to LCD's  X & Y axis in some way.  */
569
 
    if (abs(info->a[0]) > abs(info->a[1])) {
570
 
        s->tr[0] = 0;
571
 
        s->tr[1] = -info->a[6] * info->x;
572
 
        s->tr[2] = info->a[0];
573
 
        s->tr[3] = -info->a[2] / info->a[0];
574
 
        s->tr[4] = info->a[6] * info->y;
575
 
        s->tr[5] = 0;
576
 
        s->tr[6] = info->a[4];
577
 
        s->tr[7] = -info->a[5] / info->a[4];
578
 
    } else {
579
 
        s->tr[0] = info->a[6] * info->y;
580
 
        s->tr[1] = 0;
581
 
        s->tr[2] = info->a[1];
582
 
        s->tr[3] = -info->a[2] / info->a[1];
583
 
        s->tr[4] = 0;
584
 
        s->tr[5] = -info->a[6] * info->x;
585
 
        s->tr[6] = info->a[3];
586
 
        s->tr[7] = -info->a[5] / info->a[3];
587
 
    }
588
 
 
589
 
    s->tr[0] >>= 11;
590
 
    s->tr[1] >>= 11;
591
 
    s->tr[3] <<= 4;
592
 
    s->tr[4] >>= 11;
593
 
    s->tr[5] >>= 11;
594
 
    s->tr[7] <<= 4;
595
 
}