📄 tsc210x.c
字号:
/* * TI TSC2102 (touchscreen/sensors/audio controller) emulator. * TI TSC2301 (touchscreen/sensors/keypad). * * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org> * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */#include "hw.h"#include "audio/audio.h"#include "qemu-timer.h"#include "console.h"#include "omap.h" /* For struct i2s_codec_s and struct uwire_slave_s */#define TSC_DATA_REGISTERS_PAGE 0x0#define TSC_CONTROL_REGISTERS_PAGE 0x1#define TSC_AUDIO_REGISTERS_PAGE 0x2#define TSC_VERBOSE#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p]))struct tsc210x_state_s { qemu_irq pint; qemu_irq kbint; qemu_irq davint; QEMUTimer *timer; QEMUSoundCard card; struct uwire_slave_s chip; struct i2s_codec_s codec; uint8_t in_fifo[16384]; uint8_t out_fifo[16384]; uint16_t model; int x, y; int pressure; int state, page, offset, irq; uint16_t command, dav; int busy; int enabled; int host_mode; int function; int nextfunction; int precision; int nextprecision; int filter; int pin_func; int ref; int timing; int noise; uint16_t audio_ctrl1; uint16_t audio_ctrl2; uint16_t audio_ctrl3; uint16_t pll[3]; uint16_t volume; int64_t volume_change; int softstep; uint16_t dac_power; int64_t powerdown; uint16_t filter_data[0x14]; const char *name; SWVoiceIn *adc_voice[1]; SWVoiceOut *dac_voice[1]; int i2s_rx_rate; int i2s_tx_rate; AudioState *audio; int tr[8]; struct { uint16_t down; uint16_t mask; int scan; int debounce; int mode; int intr; } kb;};static const int resolution[4] = { 12, 8, 10, 12 };#define TSC_MODE_NO_SCAN 0x0#define TSC_MODE_XY_SCAN 0x1#define TSC_MODE_XYZ_SCAN 0x2#define TSC_MODE_X 0x3#define TSC_MODE_Y 0x4#define TSC_MODE_Z 0x5#define TSC_MODE_BAT1 0x6#define TSC_MODE_BAT2 0x7#define TSC_MODE_AUX 0x8#define TSC_MODE_AUX_SCAN 0x9#define TSC_MODE_TEMP1 0xa#define TSC_MODE_PORT_SCAN 0xb#define TSC_MODE_TEMP2 0xc#define TSC_MODE_XX_DRV 0xd#define TSC_MODE_YY_DRV 0xe#define TSC_MODE_YX_DRV 0xfstatic const uint16_t mode_regs[16] = { 0x0000, /* No scan */ 0x0600, /* X, Y scan */ 0x0780, /* X, Y, Z scan */ 0x0400, /* X */ 0x0200, /* Y */ 0x0180, /* Z */ 0x0040, /* BAT1 */ 0x0030, /* BAT2 */ 0x0010, /* AUX */ 0x0010, /* AUX scan */ 0x0004, /* TEMP1 */ 0x0070, /* Port scan */ 0x0002, /* TEMP2 */ 0x0000, /* X+, X- drivers */ 0x0000, /* Y+, Y- drivers */ 0x0000, /* Y+, X- drivers */};#define X_TRANSFORM(s) \ ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])#define Y_TRANSFORM(s) \ ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])#define Z1_TRANSFORM(s) \ ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)#define Z2_TRANSFORM(s) \ ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)#define BAT1_VAL 0x8660#define BAT2_VAL 0x0000#define AUX1_VAL 0x35c0#define AUX2_VAL 0xffff#define TEMP1_VAL 0x8c70#define TEMP2_VAL 0xa5b0#define TSC_POWEROFF_DELAY 50#define TSC_SOFTSTEP_DELAY 50static void tsc210x_reset(struct tsc210x_state_s *s){ s->state = 0; s->pin_func = 2; s->enabled = 0; s->busy = 0; s->nextfunction = 0; s->ref = 0; s->timing = 0; s->irq = 0; s->dav = 0; s->audio_ctrl1 = 0x0000; s->audio_ctrl2 = 0x4410; s->audio_ctrl3 = 0x0000; s->pll[0] = 0x1004; s->pll[1] = 0x0000; s->pll[2] = 0x1fff; s->volume = 0xffff; s->dac_power = 0x8540; s->softstep = 1; s->volume_change = 0; s->powerdown = 0; s->filter_data[0x00] = 0x6be3; s->filter_data[0x01] = 0x9666; s->filter_data[0x02] = 0x675d; s->filter_data[0x03] = 0x6be3; s->filter_data[0x04] = 0x9666; s->filter_data[0x05] = 0x675d; s->filter_data[0x06] = 0x7d83; s->filter_data[0x07] = 0x84ee; s->filter_data[0x08] = 0x7d83; s->filter_data[0x09] = 0x84ee; s->filter_data[0x0a] = 0x6be3; s->filter_data[0x0b] = 0x9666; s->filter_data[0x0c] = 0x675d; s->filter_data[0x0d] = 0x6be3; s->filter_data[0x0e] = 0x9666; s->filter_data[0x0f] = 0x675d; s->filter_data[0x10] = 0x7d83; s->filter_data[0x11] = 0x84ee; s->filter_data[0x12] = 0x7d83; s->filter_data[0x13] = 0x84ee; s->i2s_tx_rate = 0; s->i2s_rx_rate = 0; s->kb.scan = 1; s->kb.debounce = 0; s->kb.mask = 0x0000; s->kb.mode = 3; s->kb.intr = 0; qemu_set_irq(s->pint, !s->irq); qemu_set_irq(s->davint, !s->dav); qemu_irq_raise(s->kbint);}struct tsc210x_rate_info_s { int rate; int dsor; int fsref;};/* { rate, dsor, fsref } */static const struct tsc210x_rate_info_s tsc2101_rates[] = { /* Fsref / 6.0 */ { 7350, 7, 1 }, { 8000, 7, 0 }, /* Fsref / 5.5 */ { 8018, 6, 1 }, { 8727, 6, 0 }, /* Fsref / 5.0 */ { 8820, 5, 1 }, { 9600, 5, 0 }, /* Fsref / 4.0 */ { 11025, 4, 1 }, { 12000, 4, 0 }, /* Fsref / 3.0 */ { 14700, 3, 1 }, { 16000, 3, 0 }, /* Fsref / 2.0 */ { 22050, 2, 1 }, { 24000, 2, 0 }, /* Fsref / 1.5 */ { 29400, 1, 1 }, { 32000, 1, 0 }, /* Fsref */ { 44100, 0, 1 }, { 48000, 0, 0 }, { 0, 0, 0 },};/* { rate, dsor, fsref } */static const struct tsc210x_rate_info_s tsc2102_rates[] = { /* Fsref / 6.0 */ { 7350, 63, 1 }, { 8000, 63, 0 }, /* Fsref / 6.0 */ { 7350, 54, 1 }, { 8000, 54, 0 }, /* Fsref / 5.0 */ { 8820, 45, 1 }, { 9600, 45, 0 }, /* Fsref / 4.0 */ { 11025, 36, 1 }, { 12000, 36, 0 }, /* Fsref / 3.0 */ { 14700, 27, 1 }, { 16000, 27, 0 }, /* Fsref / 2.0 */ { 22050, 18, 1 }, { 24000, 18, 0 }, /* Fsref / 1.5 */ { 29400, 9, 1 }, { 32000, 9, 0 }, /* Fsref */ { 44100, 0, 1 }, { 48000, 0, 0 }, { 0, 0, 0 },};static inline void tsc210x_out_flush(struct tsc210x_state_s *s, int len){ uint8_t *data = s->codec.out.fifo + s->codec.out.start; uint8_t *end = data + len; while (data < end) data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data); s->codec.out.len -= len; if (s->codec.out.len) memmove(s->codec.out.fifo, end, s->codec.out.len); s->codec.out.start = 0;}static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b){ if (s->codec.out.len >= free_b) { tsc210x_out_flush(s, free_b); return; } s->codec.out.size = MIN(free_b, 16384); qemu_irq_raise(s->codec.tx_start);}static void tsc2102_audio_rate_update(struct tsc210x_state_s *s){ const struct tsc210x_rate_info_s *rate; s->codec.tx_rate = 0; s->codec.rx_rate = 0; if (s->dac_power & (1 << 15)) /* PWDNC */ return; for (rate = tsc2102_rates; rate->rate; rate ++) if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ break; if (!rate->rate) { printf("%s: unknown sampling rate configured\n", __FUNCTION__); return; } s->codec.tx_rate = rate->rate;}static void tsc2102_audio_output_update(struct tsc210x_state_s *s){ int enable; audsettings_t fmt; if (s->dac_voice[0]) { tsc210x_out_flush(s, s->codec.out.len); s->codec.out.size = 0; AUD_set_active_out(s->dac_voice[0], 0); AUD_close_out(&s->card, s->dac_voice[0]); s->dac_voice[0] = 0; } s->codec.cts = 0; enable = (~s->dac_power & (1 << 15)) && /* PWDNC */ (~s->dac_power & (1 << 10)); /* DAPWDN */ if (!enable || !s->codec.tx_rate) return; /* Force our own sampling rate even in slave DAC mode */ fmt.endianness = 0; fmt.nchannels = 2; fmt.freq = s->codec.tx_rate; fmt.fmt = AUD_FMT_S16; s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); if (s->dac_voice[0]) { s->codec.cts = 1; AUD_set_active_out(s->dac_voice[0], 1); }}static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg){ switch (reg) { case 0x00: /* X */ s->dav &= 0xfbff; return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + (s->noise & 3); case 0x01: /* Y */ s->noise ++; s->dav &= 0xfdff; return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ (s->noise & 3); case 0x02: /* Z1 */ s->dav &= 0xfeff; return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - (s->noise & 3); case 0x03: /* Z2 */ s->dav &= 0xff7f; return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | (s->noise & 3); case 0x04: /* KPData */ if ((s->model & 0xff00) == 0x2300) { if (s->kb.intr && (s->kb.mode & 2)) { s->kb.intr = 0; qemu_irq_raise(s->kbint); } return s->kb.down; } return 0xffff; case 0x05: /* BAT1 */ s->dav &= 0xffbf; return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) + (s->noise & 6); case 0x06: /* BAT2 */ s->dav &= 0xffdf; return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision); case 0x07: /* AUX1 */ s->dav &= 0xffef; return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision); case 0x08: /* AUX2 */ s->dav &= 0xfff7; return 0xffff; case 0x09: /* TEMP1 */ s->dav &= 0xfffb; return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - (s->noise & 5); case 0x0a: /* TEMP2 */ s->dav &= 0xfffd; return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ (s->noise & 3); case 0x0b: /* DAC */ s->dav &= 0xfffe; return 0xffff; default:#ifdef TSC_VERBOSE fprintf(stderr, "tsc2102_data_register_read: " "no such register: 0x%02x\n", reg);#endif return 0xffff; }}static uint16_t tsc2102_control_register_read( struct tsc210x_state_s *s, int reg){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -