/*--------------------------------------------------------------------------- * acc_i2c_set_base_address * * This routine sets the base address of the I2C bus *--------------------------------------------------------------------------- */ unsigned short acc_i2c_set_base_address(unsigned char busnum, short adr) { unsigned short ab_base_addr; /* Get Super I/O Index and Data registers */ if (!sio_set_index_data_reg()) return (0); /* Configure LDN to current ACB */ if (busnum == 1) sio_write_reg(LDN, ACB1_LDN); if (busnum == 2) sio_write_reg(LDN, ACB2_LDN); if (adr == -1) { /* Get ACCESS.bus base address */ ab_base_addr = sio_read_reg(BASE_ADR_MSB_REG); ab_base_addr = ab_base_addr << 8; ab_base_addr |= sio_read_reg(BASE_ADR_LSB_REG); if (ab_base_addr != 0) return ab_base_addr; else adr = (busnum == 1 ? ACB1_BASE : ACB2_BASE); } /* Set ACCESS.bus base address */ sio_write_reg(BASE_ADR_LSB_REG, (unsigned char) (adr & 0xFF)); sio_write_reg(BASE_ADR_MSB_REG, (unsigned char) (adr >> 8)); return adr; }
static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) { sio_write_reg(pdata, LDN, pdata->index); switch (pdata->pid) { case CHIP_ID_F81866: sio_write_mask_reg(pdata, F81866_FIFO_CTRL, F81866_IRQ_MODE1, 0); /* fall through */ case CHIP_ID_F81865: sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_SHARE, F81866_IRQ_SHARE); sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_MODE0, is_level ? 0 : F81866_IRQ_MODE0); break; case CHIP_ID_F81216AD: case CHIP_ID_F81216H: case CHIP_ID_F81216: sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, IRQ_SHARE); sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, is_level ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); break; } }
static int fintek_8250_rs485_config(struct uart_port *port, struct serial_rs485 *rs485) { uint8_t config = 0; struct fintek_8250 *pdata = port->private_data; if (!pdata) return -EINVAL; if (rs485->flags & SER_RS485_ENABLED) memset(rs485->padding, 0, sizeof(rs485->padding)); else memset(rs485, 0, sizeof(*rs485)); rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND; if (rs485->delay_rts_before_send) { rs485->delay_rts_before_send = 1; config |= TXW4C_IRA; } if (rs485->delay_rts_after_send) { rs485->delay_rts_after_send = 1; config |= RXW4C_IRA; } if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) == (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND))) rs485->flags &= SER_RS485_ENABLED; else config |= RS485_URA; if (rs485->flags & SER_RS485_RTS_ON_SEND) config |= RTS_INVERT; if (fintek_8250_enter_key(pdata->base_port, pdata->key)) return -EBUSY; sio_write_reg(pdata, LDN, pdata->index); sio_write_reg(pdata, RS485, config); fintek_8250_exit_key(pdata->base_port); port->rs485 = *rs485; return 0; }
static void sio_write_mask_reg(struct fintek_8250 *pdata, u8 reg, u8 mask, u8 data) { u8 tmp; tmp = (sio_read_reg(pdata, reg) & ~mask) | (mask & data); sio_write_reg(pdata, reg, tmp); }
static int probe_setup_port(struct fintek_8250 *pdata, struct uart_8250_port *uart) { static const u16 addr[] = {0x4e, 0x2e}; static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; struct irq_data *irq_data; bool level_mode = false; int i, j, k, min, max; for (i = 0; i < ARRAY_SIZE(addr); i++) { for (j = 0; j < ARRAY_SIZE(keys); j++) { pdata->base_port = addr[i]; pdata->key = keys[j]; if (fintek_8250_enter_key(addr[i], keys[j])) continue; if (fintek_8250_check_id(pdata) || fintek_8250_get_ldn_range(pdata, &min, &max)) { fintek_8250_exit_key(addr[i]); continue; } for (k = min; k < max; k++) { u16 aux; sio_write_reg(pdata, LDN, k); aux = sio_read_reg(pdata, IO_ADDR1); aux |= sio_read_reg(pdata, IO_ADDR2) << 8; if (aux != uart->port.iobase) continue; pdata->index = k; irq_data = irq_get_irq_data(uart->port.irq); if (irq_data) level_mode = irqd_is_level_type(irq_data); fintek_8250_set_irq_mode(pdata, level_mode); fintek_8250_set_max_fifo(pdata); fintek_8250_goto_highspeed(uart, pdata); fintek_8250_exit_key(addr[i]); return 0; } fintek_8250_exit_key(addr[i]); } } return -ENODEV; }
static void fintek_8250_goto_highspeed(struct uart_8250_port *uart, struct fintek_8250 *pdata) { sio_write_reg(pdata, LDN, pdata->index); switch (pdata->pid) { case CHIP_ID_F81866: /* set uart clock for high speed serial mode */ sio_write_mask_reg(pdata, F81866_UART_CLK, F81866_UART_CLK_MASK, F81866_UART_CLK_14_769MHZ); uart->port.uartclk = 921600 * 16; break; default: /* leave clock speed untouched */ break; } }
void vgatv_tvout_mode(unsigned long tvout_mode) { unsigned long cr; sio_read_reg(SIO_CR, &cr); // set requested mode switch (tvout_mode) { case FS460_TVOUT_MODE_CVBS_YC: cr &= ~SIO_CR_OFMT; break; case FS460_TVOUT_MODE_RGB: cr |= SIO_CR_OFMT; break; } sio_write_reg(SIO_CR, cr); }
void vgatv_nco(unsigned long tv_std, unsigned long vga_mode, int use_nco) { unsigned long cr, misc; int m, n; unsigned long ncon, ncod; int k; k = map_tvstd_to_index(tv_std); // initialize m, n to make compiler happy m = 512; n = 128; // if M/N mode is selected, make sure it's attainable if (!use_nco) { if ((g_specs.vga_htotal <= 0) || (g_specs.vga_vtotal <= 0)) use_nco = 1; else { m = g_specs.vga_vtotal; if ((m < 500) || (m > 1200)) use_nco = 1; n = g_specs.tv_htotal * g_specs.tv_vtotal / g_specs.vga_htotal; if (g_specs.tv_htotal * g_specs.tv_vtotal != (n * g_specs.vga_htotal)) use_nco = 1; } } // read and store CR. sio_read_reg(SIO_CR, &cr); // make sure NCO_EN (enable latch) bit is clear cr &= ~SIO_CR_NCO_EN; sio_write_reg(SIO_CR, cr); // clear NCO_LOADX. sio_read_reg(SIO_MISC, &misc); misc &= ~(SIO_MISC_NCO_LOAD1 + SIO_MISC_NCO_LOAD0); sio_write_reg(SIO_MISC, misc); if (use_nco) { if (FS460_VGA_MODE_1024X768 == vga_mode) { // setup for M and N load (Nco_load=1). misc |= (SIO_MISC_NCO_LOAD0); sio_write_reg(SIO_MISC, misc); // M and N. sio_write_reg(SIO_NCONL, 1024-2); sio_write_reg(SIO_NCODL, 128-1); // latch M/N in. cr |= SIO_CR_NCO_EN; sio_write_reg(SIO_CR, cr); cr &= ~SIO_CR_NCO_EN; sio_write_reg(SIO_CR, cr); // setup ncon and ncod load (Nco_load=0). misc &= ~(SIO_MISC_NCO_LOAD1 + SIO_MISC_NCO_LOAD0); sio_write_reg(SIO_MISC, misc); // NCON ncon = (unsigned long)g_specs.vga_vtotal * g_specs.vga_htotal / 2; sio_write_reg(SIO_NCONH, ncon >> 16); sio_write_reg(SIO_NCONL, ncon & 0xffff); // NCOD ncod = (unsigned long)g_specs.tv_vtotal * g_specs.vga_htotal * 4; sio_write_reg(SIO_NCODH, ncod >> 16); sio_write_reg(SIO_NCODL, ncod & 0xffff); } else {
void vgatv_position( unsigned long tv_std, unsigned long vga_mode, int left, int top, int width, int height) { int k; unsigned int vga_index; unsigned long vsc; unsigned short ffolat; int vga_pixels,pre_pixels; int hscale_256ths; int hsc; int iho, ivo, ihw; int limit; // basic minimums if (width < 100) width = 100; if (height < 100) height = 100; // tv_std is valid. k = map_tvstd_to_index(tv_std); // store tv width and lines g_specs.tv_htotal = tvsetup.tv_width[k]; g_specs.tv_vtotal = tvsetup.tv_lines[k]; // determine vga mode index for (vga_index = 0; vga_index < sizeof(scantable) / sizeof(*scantable); vga_index++) { if (scantable[vga_index].mode == vga_mode) break; } if (vga_index >= sizeof(scantable) / sizeof(*scantable)) return; // vga pixels is vga width, except in 1024x768, where it's half that. vga_pixels = g_specs.vga_hactive; if (1024 == vga_pixels) vga_pixels /= 2; if (g_specs.vga_vtotal_specified) { // use the provided vtotal g_specs.vga_vtotal = g_specs.vga_vtotal_specified; } else { // calculate vga vtotal based on requested height // vga v_total is (vga vactive) * (tv vtotal) / (user-selected height) // this also sets vertical scaling g_specs.vga_vtotal = ((2 * g_specs.vga_vactive * g_specs.tv_vtotal / height) + 1) / 2; } // limit vga_vtotal to slightly less than twice tv_vtotal, to limit scaling to slightly more than 1/2 if (g_specs.vga_vtotal > 2 * g_specs.tv_vtotal - 10) g_specs.vga_vtotal = 2 * g_specs.tv_vtotal - 10; // minimum ten lines of vertical blank in VGA if (g_specs.vga_vtotal < g_specs.vga_vactive + 10) g_specs.vga_vtotal = g_specs.vga_vactive + 10; // vertical upscaling not supported if (g_specs.vga_vtotal < g_specs.tv_vtotal) g_specs.vga_vtotal = g_specs.tv_vtotal; TRACE(( "vga hactive is %u, vactive is %u, htotal is %u, vtotal is %u\n", g_specs.vga_hactive, g_specs.vga_vactive, g_specs.vga_htotal, g_specs.vga_vtotal)) // place hsync halfway from vga_hactive to htotal, width 64 // also make it even // (so that hsync is never within ten pixels of active video, htotal must always exceed hactive by at least 84.) g_specs.vga_hsyncw = 64; g_specs.vga_hsync = (g_specs.vga_htotal + g_specs.vga_hactive - g_specs.vga_hsyncw) / 2; g_specs.vga_hsync &= ~1; // center v_sync halfway from vga_vactive to vga vtotal, height 2 lines g_specs.vga_vsyncw = 2; g_specs.vga_vsync = (g_specs.vga_vtotal + g_specs.vga_vactive - g_specs.vga_vsyncw) / 2; TRACE(( "vga hsync is %u to %u, vsync is %u to %u.\n", g_specs.vga_hsync, g_specs.vga_hsync + g_specs.vga_hsyncw, g_specs.vga_vsync, g_specs.vga_vsync + g_specs.vga_vsyncw)) // calculate vertical scaling based on ratio of vtotals vsc = 0xFFFF & ((0x10000 * g_specs.tv_vtotal / g_specs.vga_vtotal) - 0x10000); TRACE(("vsc = 0x%04x, tv_vtotal = %d, vga_vtotal = %d\n", vsc, g_specs.tv_vtotal, g_specs.vga_vtotal)) sio_write_reg(SIO_VSC, (int)vsc); // calculate ivo ivo = calc_ivo(top, vsc); // range check if (ivo < 0) ivo = 0; // maximum ivo limit = (g_specs.vga_vtotal - g_specs.vga_vsync) + (g_specs.tv_vtotal - tvsetup.tv_active_lines[k]) * g_specs.vga_vtotal / g_specs.tv_vtotal; if (ivo > limit) ivo = limit; // program IVO sio_write_reg(SIO_IVO, ivo); // hscale hscale_256ths = (((2 * 256 * width) / vga_pixels) + 1) / 2; TRACE(("width is %u, hscale_256ths = %u\n",width, hscale_256ths)) // determine hsc where hscale = (1 + hsc/128) hsc = ((hscale_256ths + 1) / 2) - 128; TRACE(("hsc = %d\n",hsc)) if (hsc >= 0) { // maximum upscale is 0x7F, which is about double if (hsc > 0x7F) hsc = 0x7F; sio_write_reg(SIO_HSC, hsc << 8); } else { // minimum downscale is -63, which is just over 1/2 if (hsc < -63) hsc = -63; sio_write_reg(SIO_HSC, 0xFF & hsc); } // recalculate hscale for future formulas hscale_256ths = 256 + (hsc * 2); TRACE(("recalculated hscale_256ths = %u\n",hscale_256ths)) // iho is the number of VGA pixels to skip before starting active tv video // iho starts counting at the start of hsync // account for negative rounding iho = 2 * left * 256 / hscale_256ths; if (iho < 0) iho = (iho - 1) / 2; else iho = (iho + 1) / 2; iho = (g_specs.vga_htotal - g_specs.vga_hsync) * vga_pixels / g_specs.vga_hactive - iho; // range check if (iho < 0) iho = 0; if (iho > g_specs.vga_htotal - g_specs.vga_hsync + vga_pixels) iho = g_specs.vga_htotal - g_specs.vga_hsync + vga_pixels; // program IHO TRACE(("iho = %u\n",iho)) sio_write_reg(SIO_IHO, iho); // input horizontal width. // // pre_pixels = (htotal - hsync) * (vga_pixels / vga_hactive) // input horizontal width is vga pixels + pre_pixels - iho // additionally, ihw cannot exceed tv width / hscale // and if hsc is negative, (ihw)(-hsc/128) cannot exceed ~250. // and ihw should be even. pre_pixels = (int)((long)(g_specs.vga_htotal - g_specs.vga_hsync) * vga_pixels / g_specs.vga_hactive); ihw = min((vga_pixels + pre_pixels - iho),(720 * 256 / hscale_256ths)); if (hsc < 0) ihw = (int)min(ihw,252L * 128 / (-hsc)); ihw &= ~1; TRACE(("ihw = %u\n",ihw)) sio_write_reg(SIO_IHW, ihw); // FIFO latency must advance with IVO in order to keep up with the scaler. // The amount to advance with each line is proportional to the inverse of the fraction // of the line taken by the scaler. Offsetting ivo by 2 makes the line drop logic work out // right. // If the next higher IVO was promoted because it would be skipped, then the // FIFO latency for this IVO should be calculated as IVO+1. I don't know why, // it just does. // There appears to be a fudge factor proportional to the scaling. Again, I don't know why. // There is also an adjustment for horizontal position, related to IHO and HSC. The equation // uses left instead of IHO because it was already tuned for left=40. Probably some of // the fudge factor could be explained away by using IHO directly. // If VSC is 0, this equation blows up. At this condition FIFO latency is not really // relevant anyway, so just program an intermediate value like 0x40. { int use_ivo; if (vsc) { if ((0xFFFF & ((ivo + 1) * (0x10000 - vsc))) > (0xFFFF & ((ivo + 2) * (0x10000 - vsc)))) use_ivo = ivo + 1; else use_ivo = ivo; ffolat = (unsigned short)(62 - ((0x10000 - vsc) / 305) + (720 * (0x7F & ((use_ivo + 2) * (0x10000 - vsc) / 512)) / 858) - ((left - 40) * 256 / hscale_256ths / 4)); } else { ffolat = 0x40; } TRACE(("FFOLAT = %u\n", ffolat)) sio_write_reg(SIO_FFO_LAT, ffolat); } TRACE(("freq=%ukHz\n",g_specs.vga_htotal * g_specs.vga_vtotal * 27000 / g_specs.tv_htotal / g_specs.tv_vtotal)) }
void vgatv_tv_std(unsigned long tv_std, unsigned int cp_trigger_bits) { int k; unsigned short reg34; unsigned long cr, w; unsigned long l; // verify supported standard. k = map_tvstd_to_index(tv_std); if (k < 0) return; // store tv width and lines g_specs.tv_htotal = tvsetup.tv_width[k]; g_specs.tv_vtotal = tvsetup.tv_lines[k]; // set PAL or NTSC in CR register sio_read_reg(SIO_CR, &cr); cr &= ~SIO_CR_656_PAL_NTSC; cr |= tvsetup.cr[k]; sio_write_reg(SIO_CR, cr); // setup the encoder. l = tvsetup.chroma_freq[k]; enc_write_reg(ENC_CHROMA_FREQ, (int)(l & 0x00ff)); enc_write_reg(ENC_CHROMA_FREQ+1, (int)((l>>8) & 0x00ff)); enc_write_reg(ENC_CHROMA_FREQ+2, (int)((l>>16) & 0x00ff)); enc_write_reg(ENC_CHROMA_FREQ+3, (int)((l>>24) & 0x00ff)); enc_write_reg(ENC_CHROMA_PHASE, tvsetup.chroma_phase[k]); enc_write_reg(ENC_REG05, 0x00); // reg 0x05 enc_write_reg(ENC_REG06, 0x89); // reg 0x06 enc_write_reg(ENC_REG07, 0x00); // reg 0x07 enc_write_reg(ENC_HSYNCWIDTH, tvsetup.hsync_width[k]); enc_write_reg(ENC_BURSTWIDTH, tvsetup.burst_width[k]); enc_write_reg(ENC_BACKPORCH, tvsetup.back_porch[k]); enc_write_reg(ENC_CB_BURSTLEVEL, tvsetup.cb_burst_level[k]); enc_write_reg(ENC_CR_BURSTLEVEL, tvsetup.cr_burst_level[k]); enc_write_reg(ENC_SLAVEMODE, 0x01); // slave mode if (cp_trigger_bits == 0) w = w10bit2z(tvsetup.blank_level[k]); else w = w10bit2z((unsigned short)(tvsetup.blank_level[k]-tvsetup.hamp_offset[k])); enc_write_reg(ENC_BLANKLEVEL, w & 0x00ff); enc_write_reg(ENC_BLANKLEVEL+1, w >> 8); enc_write_reg(ENC_TINT, 0x00); // tint enc_write_reg(ENC_BREEZEWAY, tvsetup.breeze_way[k]); enc_write_reg(ENC_FRONTPORCH, tvsetup.front_porch[k]); enc_write_reg(ENC_FIRSTVIDEOLINE, tvsetup.firstline[k]); // firstvideoline reg34 = (tvsetup.pal_mode[k] << 6) | (tvsetup.sys625_50[k] << 4) | (tvsetup.sys625_50[k] << 3) | (tvsetup.cphase_rst[k] << 1) | (tvsetup.vsync5[k]); enc_write_reg(ENC_REG34, reg34); // reg 0x34 enc_write_reg(ENC_SYNCLEVEL, tvsetup.sync_level[k]); if (cp_trigger_bits == 0) w = w10bit2z(tvsetup.vbi_blank_level[k]); else w = w10bit2z((unsigned short)(tvsetup.vbi_blank_level[k]-1)); enc_write_reg(ENC_VBI_BLANKLEVEL, w & 0x00ff); enc_write_reg(ENC_VBI_BLANKLEVEL+1, w >> 8); }
void vgatv_vga_mode(unsigned long vga_mode, unsigned long tv_std, int htotal, int vtotal) { static struct { unsigned long mode; int width; int lines; } vgaparams[] = { {FS460_VGA_MODE_640X480, 640, 480}, {FS460_VGA_MODE_720X487, 720, 480}, {FS460_VGA_MODE_720X576, 720, 576}, {FS460_VGA_MODE_800X600, 800, 600}, {FS460_VGA_MODE_1024X768, 1024, 768}, }; unsigned long cr, misc, byp; unsigned int i; g_specs.vga_hactive = 0; g_specs.vga_vactive = 0; g_specs.vga_htotal = 0; g_specs.vga_hdiv = 1; for (i = 0; i < sizeof(vgaparams) / sizeof(*vgaparams); i++) { if (vga_mode == vgaparams[i].mode) { g_specs.vga_hactive = vgaparams[i].width; g_specs.vga_vactive = vgaparams[i].lines; get_vga_htotal(vga_mode, tv_std); // override if specified if (htotal) g_specs.vga_htotal = htotal; g_specs.vga_vtotal_specified = vtotal; if (vtotal) g_specs.vga_vtotal = vtotal; // set divisor for div2 mode switch(vga_mode) { case FS460_VGA_MODE_1024X768: g_specs.vga_hdiv = 2; break; } break; } } if (!g_specs.vga_hactive) return; // clock mux decimator and vga dual. sio_read_reg(SIO_CR, &cr); sio_read_reg(SIO_MISC, &misc); sio_read_reg(SIO_BYP, &byp); if (vga_mode == FS460_VGA_MODE_1024X768) { // XGA cr |= SIO_CR_UIM_DEC; misc |= SIO_MISC_VGACKDIV; byp |= (SIO_BYP_HDS | SIO_BYP_CAC); } else { // VGA,SVGA cr &= ~SIO_CR_UIM_DEC; misc &= ~SIO_MISC_VGACKDIV; byp &= ~(SIO_BYP_HDS | SIO_BYP_CAC); } sio_write_reg(SIO_CR, cr); sio_write_reg(SIO_MISC, misc); sio_write_reg(SIO_BYP, byp); }