/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz This file is part of the SANE package. 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 of the License, or (at your option) any later version. 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. As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. */ #define DEBUG_DECLARE_ONLY #include "gl843_registers.h" #include "gl843.h" #include "test_settings.h" #include #include namespace genesys { namespace gl843 { /** * compute the step multiplier used */ static int gl843_get_step_multiplier(Genesys_Register_Set* regs) { switch (regs->get8(REG_0x9D) & 0x0c) { case 0x04: return 2; case 0x08: return 4; default: return 1; } } /** @brief set all registers to default values . * This function is called only once at the beginning and * fills register startup values for registers reused across scans. * Those that are rarely modified or not modified are written * individually. * @param dev device structure holding register set to initialize */ static void gl843_init_registers (Genesys_Device * dev) { // Within this function SENSOR_DEF marker documents that a register is part // of the sensors definition and the actual value is set in // scanner_setup_sensor(). // 0x6c, 0x6d, 0x6e, 0x6f, 0xa6, 0xa7, 0xa8, 0xa9 are defined in the Gpo sensor struct DBG_HELPER(dbg); dev->reg.clear(); dev->reg.init_reg(0x01, 0x00); dev->reg.init_reg(0x02, 0x78); dev->reg.init_reg(0x03, 0x1f); if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x03, 0x1d); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x03, 0x1c); } dev->reg.init_reg(0x04, 0x10); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x04, 0x22); } // fine tune upon device description dev->reg.init_reg(0x05, 0x80); if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x05, 0x08); } auto initial_scan_method = dev->model->default_method; if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { initial_scan_method = ScanMethod::TRANSPARENCY; } const auto& sensor = sanei_genesys_find_sensor_any(dev); const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, initial_scan_method); sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); // TODO: on 8600F the windows driver turns off GAIN4 which is recommended dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */ } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) { dev->reg.init_reg(0x06, 0xd0); } if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x06, 0xf0); /* SCANMOD=111, PWRBIT and no GAIN4 */ } dev->reg.init_reg(0x08, 0x00); dev->reg.init_reg(0x09, 0x00); dev->reg.init_reg(0x0a, 0x00); if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x0a, 0x18); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x0a, 0x10); } // This register controls clock and RAM settings and is further modified in // gl843_boot dev->reg.init_reg(0x0b, 0x6a); if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x0b, 0x69); // 16M only } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x0b, 0x89); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) { dev->reg.init_reg(0x0b, 0x2a); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x0b, 0x4a); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x0b, 0x69); } if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x0c, 0x00); } // EXPR[0:15], EXPG[0:15], EXPB[0:15]: Exposure time settings. dev->reg.init_reg(0x10, 0x00); // SENSOR_DEF dev->reg.init_reg(0x11, 0x00); // SENSOR_DEF dev->reg.init_reg(0x12, 0x00); // SENSOR_DEF dev->reg.init_reg(0x13, 0x00); // SENSOR_DEF dev->reg.init_reg(0x14, 0x00); // SENSOR_DEF dev->reg.init_reg(0x15, 0x00); // SENSOR_DEF if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.set16(REG_EXPR, 0x9c40); dev->reg.set16(REG_EXPG, 0x9c40); dev->reg.set16(REG_EXPB, 0x9c40); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.set16(REG_EXPR, 0x2c09); dev->reg.set16(REG_EXPG, 0x22b8); dev->reg.set16(REG_EXPB, 0x10f0); } // CCD signal settings. dev->reg.init_reg(0x16, 0x33); // SENSOR_DEF dev->reg.init_reg(0x17, 0x1c); // SENSOR_DEF dev->reg.init_reg(0x18, 0x10); // SENSOR_DEF // EXPDMY[0:7]: Exposure time of dummy lines. dev->reg.init_reg(0x19, 0x2a); // SENSOR_DEF // Various CCD clock settings. dev->reg.init_reg(0x1a, 0x04); // SENSOR_DEF dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x1c, 0x20); // SENSOR_DEF dev->reg.init_reg(0x1d, 0x04); // SENSOR_DEF dev->reg.init_reg(0x1e, 0x10); if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x1e, 0x20); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x1e, 0xa0); } dev->reg.init_reg(0x1f, 0x01); if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x1f, 0xff); } dev->reg.init_reg(0x20, 0x10); dev->reg.init_reg(0x21, 0x04); dev->reg.init_reg(0x22, 0x10); dev->reg.init_reg(0x23, 0x10); if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x22, 0xc8); dev->reg.init_reg(0x23, 0xc8); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x22, 0x50); dev->reg.init_reg(0x23, 0x50); } dev->reg.init_reg(0x24, 0x04); dev->reg.init_reg(0x25, 0x00); dev->reg.init_reg(0x26, 0x00); dev->reg.init_reg(0x27, 0x00); dev->reg.init_reg(0x2c, 0x02); dev->reg.init_reg(0x2d, 0x58); // BWHI[0:7]: high level of black and white threshold dev->reg.init_reg(0x2e, 0x80); // BWLOW[0:7]: low level of black and white threshold dev->reg.init_reg(0x2f, 0x80); dev->reg.init_reg(0x30, 0x00); dev->reg.init_reg(0x31, 0x14); dev->reg.init_reg(0x32, 0x27); dev->reg.init_reg(0x33, 0xec); // DUMMY: CCD dummy and optically black pixel count dev->reg.init_reg(0x34, 0x24); if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x34, 0x14); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x34, 0x3c); } // MAXWD: If available buffer size is less than 2*MAXWD words, then // "buffer full" state will be set. dev->reg.init_reg(0x35, 0x00); dev->reg.init_reg(0x36, 0xff); dev->reg.init_reg(0x37, 0xff); // LPERIOD: Line period or exposure time for CCD or CIS. dev->reg.init_reg(0x38, 0x55); // SENSOR_DEF dev->reg.init_reg(0x39, 0xf0); // SENSOR_DEF // FEEDL[0:24]: The number of steps of motor movement. dev->reg.init_reg(0x3d, 0x00); dev->reg.init_reg(0x3e, 0x00); dev->reg.init_reg(0x3f, 0x01); // Latch points for high and low bytes of R, G and B channels of AFE. If // multiple clocks per pixel are consumed, then the setting defines during // which clock the corresponding value will be read. // RHI[0:4]: The latch point for high byte of R channel. // RLOW[0:4]: The latch point for low byte of R channel. // GHI[0:4]: The latch point for high byte of G channel. // GLOW[0:4]: The latch point for low byte of G channel. // BHI[0:4]: The latch point for high byte of B channel. // BLOW[0:4]: The latch point for low byte of B channel. dev->reg.init_reg(0x52, 0x01); // SENSOR_DEF dev->reg.init_reg(0x53, 0x04); // SENSOR_DEF dev->reg.init_reg(0x54, 0x07); // SENSOR_DEF dev->reg.init_reg(0x55, 0x0a); // SENSOR_DEF dev->reg.init_reg(0x56, 0x0d); // SENSOR_DEF dev->reg.init_reg(0x57, 0x10); // SENSOR_DEF // VSMP[0:4]: The position of the image sampling pulse for AFE in cycles. // VSMPW[0:2]: The length of the image sampling pulse for AFE in cycles. dev->reg.init_reg(0x58, 0x1b); // SENSOR_DEF dev->reg.init_reg(0x59, 0x00); // SENSOR_DEF dev->reg.init_reg(0x5a, 0x40); // SENSOR_DEF // 0x5b-0x5c: GMMADDR[0:15] address for gamma or motor tables download // SENSOR_DEF // DECSEL[0:2]: The number of deceleration steps after touching home sensor // STOPTIM[0:4]: The stop duration between change of directions in // backtracking dev->reg.init_reg(0x5e, 0x23); if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x5e, 0x3f); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x5e, 0x85); } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x5e, 0x1f); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x5e, 0x01); } //FMOVDEC: The number of deceleration steps in table 5 for auto-go-home dev->reg.init_reg(0x5f, 0x01); if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x5f, 0xf0); } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x5f, 0xf0); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x5f, 0x01); } // Z1MOD[0:20] dev->reg.init_reg(0x60, 0x00); dev->reg.init_reg(0x61, 0x00); dev->reg.init_reg(0x62, 0x00); // Z2MOD[0:20] dev->reg.init_reg(0x63, 0x00); dev->reg.init_reg(0x64, 0x00); dev->reg.init_reg(0x65, 0x00); // STEPSEL[0:1]. Motor movement step mode selection for tables 1-3 in // scanning mode. // MTRPWM[0:5]. Motor phase PWM duty cycle setting for tables 1-3 dev->reg.init_reg(0x67, 0x7f); // MOTOR_PROFILE // FSTPSEL[0:1]: Motor movement step mode selection for tables 4-5 in // command mode. // FASTPWM[5:0]: Motor phase PWM duty cycle setting for tables 4-5 dev->reg.init_reg(0x68, 0x7f); // MOTOR_PROFILE if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x67, 0x80); dev->reg.init_reg(0x68, 0x80); } // FSHDEC[0:7]: The number of deceleration steps after scanning is finished // (table 3) dev->reg.init_reg(0x69, 0x01); // MOTOR_PROFILE // FMOVNO[0:7] The number of acceleration or deceleration steps for fast // moving (table 4) dev->reg.init_reg(0x6a, 0x04); // MOTOR_PROFILE // GPIO-related register bits dev->reg.init_reg(0x6b, 0x30); if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x6b, 0x72); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x6b, 0xb1); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x6b, 0xf4); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x6b, 0x31); } // 0x6c, 0x6d, 0x6e, 0x6f are set according to gpio tables. See // gl843_init_gpio. // RSH[0:4]: The position of rising edge of CCD RS signal in cycles // RSL[0:4]: The position of falling edge of CCD RS signal in cycles // CPH[0:4]: The position of rising edge of CCD CP signal in cycles. // CPL[0:4]: The position of falling edge of CCD CP signal in cycles dev->reg.init_reg(0x70, 0x01); // SENSOR_DEF dev->reg.init_reg(0x71, 0x03); // SENSOR_DEF dev->reg.init_reg(0x72, 0x04); // SENSOR_DEF dev->reg.init_reg(0x73, 0x05); // SENSOR_DEF if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x70, 0x01); dev->reg.init_reg(0x71, 0x03); dev->reg.init_reg(0x72, 0x01); dev->reg.init_reg(0x73, 0x03); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x70, 0x01); dev->reg.init_reg(0x71, 0x03); dev->reg.init_reg(0x72, 0x03); dev->reg.init_reg(0x73, 0x04); } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x70, 0x00); dev->reg.init_reg(0x71, 0x02); dev->reg.init_reg(0x72, 0x02); dev->reg.init_reg(0x73, 0x04); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x70, 0x00); dev->reg.init_reg(0x71, 0x02); dev->reg.init_reg(0x72, 0x00); dev->reg.init_reg(0x73, 0x00); } // CK1MAP[0:17], CK3MAP[0:17], CK4MAP[0:17]: CCD clock bit mapping setting. dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF // various AFE settings dev->reg.init_reg(0x7d, 0x00); if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x7d, 0x20); } // GPOLED[x]: LED vs GPIO settings dev->reg.init_reg(0x7e, 0x00); // BSMPDLY, VSMPDLY // LEDCNT[0:1]: Controls led blinking and its period dev->reg.init_reg(0x7f, 0x00); // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for // moving in various situations. dev->reg.init_reg(0x80, 0x00); // MOTOR_PROFILE if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x80, 0x0c); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x80, 0x28); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x80, 0x50); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x80, 0x0f); } if (dev->model->model_id != ModelId::CANON_4400F) { dev->reg.init_reg(0x81, 0x00); dev->reg.init_reg(0x82, 0x00); dev->reg.init_reg(0x83, 0x00); dev->reg.init_reg(0x84, 0x00); dev->reg.init_reg(0x85, 0x00); dev->reg.init_reg(0x86, 0x00); } dev->reg.init_reg(0x87, 0x00); if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x87, 0x02); } // MTRPLS[0:7]: The width of the ADF motor trigger signal pulse. if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x94, 0xff); } // 0x95-0x97: SCANLEN[0:19]: Controls when paper jam bit is set in sheetfed // scanners. // ONDUR[0:15]: The duration of PWM ON phase for LAMP control // OFFDUR[0:15]: The duration of PWM OFF phase for LAMP control // both of the above are in system clocks if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x98, 0x00); dev->reg.init_reg(0x99, 0x00); dev->reg.init_reg(0x9a, 0x00); dev->reg.init_reg(0x9b, 0x00); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { // TODO: move to set for scan dev->reg.init_reg(0x98, 0x03); dev->reg.init_reg(0x99, 0x30); dev->reg.init_reg(0x9a, 0x01); dev->reg.init_reg(0x9b, 0x80); } // RMADLY[0:1], MOTLAG, CMODE, STEPTIM, MULDMYLN, IFRS dev->reg.init_reg(0x9d, 0x04); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x9d, 0x00); } if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8400F || dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x9d, 0x08); // sets the multiplier for slope tables } // SEL3INV, TGSTIME[0:2], TGWTIME[0:2] if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x9e, 0x00); // SENSOR_DEF } if (dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0xa2, 0x0f); } // RFHSET[0:4]: Refresh time of SDRAM in units of 2us if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0xa2, 0x1f); } // 0xa6-0xa9: controls gpio, see gl843_gpio_init // not documented if (dev->model->model_id != ModelId::CANON_4400F && dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0xaa, 0x00); } // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK. if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0xab, 0x50); } if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0xab, 0x00); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0xab, 0x40); } // VRHOME[3:2], VRMOVE[3:2], VRBACK[3:2]: Vref setting of the motor driver IC // for various situations. if (dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0xac, 0x00); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) { uint8_t data[32] = { 0x8c, 0x8f, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x73, 0x63, 0x68, 0x69, 0x65, 0x6e, 0x00, }; dev->interface->write_buffer(0x3c, 0x3ff000, data, 32); } } static void gl843_set_ad_fe(Genesys_Device* dev) { for (const auto& reg : dev->frontend.regs) { dev->interface->write_fe_register(reg.address, reg.value); } } // Set values of analog frontend void CommandSetGl843::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const { DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?"); (void) sensor; if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; } // check analog frontend type // FIXME: looks like we write to that register with initial data uint8_t fe_type = dev->interface->read_register(REG_0x04) & REG_0x04_FESET; if (fe_type == 2) { gl843_set_ad_fe(dev); return; } if (fe_type != 0) { throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type); } for (unsigned i = 1; i <= 3; i++) { dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i)); } for (const auto& reg : sensor.custom_fe_regs) { dev->interface->write_fe_register(reg.address, reg.value); } for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); } if (dev->model->sensor_id == SensorId::CCD_KVSS080) { for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i)); } } for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); } } static void gl843_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int exposure, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, ScanFlag flags) { DBG_HELPER_ARGS(dbg, "exposure=%d, scan_yres=%d, step_type=%d, scan_lines=%d, scan_dummy=%d, " "feed_steps=%d, flags=%x", exposure, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(flags)); unsigned feedl, dist; /* get step multiplier */ unsigned step_multiplier = gl843_get_step_multiplier (reg); bool use_fast_fed = false; if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) { use_fast_fed = true; } if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { use_fast_fed = false; } reg->set24(REG_LINCNT, scan_lines); reg->set8(REG_0x02, 0); sanei_genesys_set_motor_power(*reg, true); std::uint8_t reg02 = reg->get8(REG_0x02); if (use_fast_fed) { reg02 |= REG_0x02_FASTFED; } else { reg02 &= ~REG_0x02_FASTFED; } // in case of automatic go home, move until home sensor if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; } /* disable backtracking */ if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F) || (scan_yres>=sensor.full_resolution)) { reg02 |= REG_0x02_ACDCDIS; } if (has_flag(flags, ScanFlag::REVERSE)) { reg02 |= REG_0x02_MTRREV; } else { reg02 &= ~REG_0x02_MTRREV; } reg->set8(REG_0x02, reg02); // scan and backtracking slope table auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure, step_multiplier, motor_profile); scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); // fast table const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); if (fast_profile == nullptr) { fast_profile = &motor_profile; } auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, *fast_profile); scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { std::uint8_t vref = 0; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; reg->set8(REG_0x80, vref); } /* substract acceleration distance from feedl */ feedl=feed_steps; feedl <<= static_cast(motor_profile.step_type); dist = scan_table.table.size() / step_multiplier; if (use_fast_fed) { dist += (fast_table.table.size() / step_multiplier) * 2; } /* get sure when don't insane value : XXX STEF XXX in this case we should * fall back to single table move */ if (dist < feedl) { feedl -= dist; } else { feedl = 1; } reg->set24(REG_FEEDL, feedl); // doesn't seem to matter that much std::uint32_t z1, z2; sanei_genesys_calculate_zmod(use_fast_fed, exposure, scan_table.table, scan_table.table.size() / step_multiplier, feedl, scan_table.table.size() / step_multiplier, &z1, &z2); if(scan_yres>600) { z1=0; z2=0; } reg->set24(REG_Z1MOD, z1); reg->set24(REG_Z2MOD, z2); reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); reg->set8_mask(REG_0x67, static_cast(motor_profile.step_type) << REG_0x67S_STEPSEL, 0xc0); reg->set8_mask(REG_0x68, static_cast(fast_profile->step_type) << REG_0x68S_FSTPSEL, 0xc0); // steps for STOP table reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); if (dev->model->model_id == ModelId::PANASONIC_KV_SS080 || dev->model->model_id == ModelId::HP_SCANJET_4850C || dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { // FIXME: take this information from motor struct std::uint8_t reg_vref = reg->get8(0x80); reg_vref = 0x50; unsigned coeff = sensor.full_resolution / scan_yres; if (dev->model->motor_id == MotorId::KVSS080) { if (coeff >= 1) { reg_vref |= 0x05; } } else { switch (coeff) { case 4: reg_vref |= 0x0a; break; case 2: reg_vref |= 0x0f; break; case 1: reg_vref |= 0x0f; break; } } reg->set8(REG_0x80, reg_vref); } } /** @brief setup optical related registers * start and pixels are expressed in optical sensor resolution coordinate * space. * @param dev device to use * @param reg registers to set up * @param exposure exposure time to use * @param used_res scanning resolution used, may differ from * scan's one * @param start logical start pixel coordinate * @param pixels logical number of pixels to use * @param channels number of color channles used (1 or 3) * @param depth bit depth of the scan (1, 8 or 16 bits) * @param color_filter to choose the color channel used in gray scans * @param flags to drive specific settings such no calibration, XPA use ... */ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure, const ScanSession& session) { DBG_HELPER_ARGS(dbg, "exposure=%d", exposure); unsigned int tgtime; /**> exposure time multiplier */ /* tgtime */ tgtime = exposure / 65536 + 1; DBG(DBG_io2, "%s: tgtime=%d\n", __func__, tgtime); // sensor parameters scanner_setup_sensor(*dev, sensor, *reg); dev->cmd_set->set_fe(dev, sensor, AFE_SET); /* enable shading */ regs_set_optical_off(dev->model->asic_type, *reg); if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || session.use_host_side_calib) { reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; } else { reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } bool use_shdarea = false; if (dev->model->model_id == ModelId::CANON_4400F) { use_shdarea = session.params.xres <= 600; } else if (dev->model->model_id == ModelId::CANON_8400F) { use_shdarea = session.params.xres <= 400; } else if (dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { use_shdarea = true; } else { use_shdarea = session.params.xres > 600; } if (use_shdarea) { reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; } else { reg->find_reg(REG_0x01).value &= ~REG_0x01_SHDAREA; } if (dev->model->model_id == ModelId::CANON_8600F) { reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB; } else { reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; } // FIXME: we probably don't need to set exposure to registers at this point. It was this way // before a refactor. sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); /* select XPA */ reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; } reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); // BW threshold reg->set8(REG_0x2E, 0x7f); reg->set8(REG_0x2F, 0x7f); /* monochrome / color scan */ switch (session.params.depth) { case 8: reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: reg->find_reg(REG_0x04).value |= 0x14; break; case ColorFilter::BLUE: reg->find_reg(REG_0x04).value |= 0x1c; break; case ColorFilter::GREEN: reg->find_reg(REG_0x04).value |= 0x18; break; default: break; // should not happen } } else { switch (dev->frontend.layout.type) { case FrontendType::WOLFSON: reg->find_reg(REG_0x04).value |= 0x10; // pixel by pixel break; case FrontendType::ANALOG_DEVICES: reg->find_reg(REG_0x04).value |= 0x20; // slow color pixel by pixel break; default: throw SaneException("Invalid frontend type %d", static_cast(dev->frontend.layout.type)); } } const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, session.params.channels, session.params.scan_method); sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } reg->set16(REG_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); /* MAXWD is expressed in 2 words unit */ /* nousedspace = (mem_bank_range * 1024 / 256 -1 ) * 4; */ // BUG: the division by optical and full resolution factor likely does not make sense reg->set24(REG_MAXWD, (session.output_line_bytes * session.optical_resolution / session.full_resolution) >> 1); reg->set16(REG_LPERIOD, exposure / tgtime); reg->set8(REG_DUMMY, sensor.dummy_pixel); } void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); int exposure; int slope_dpi = 0; int dummy = 0; /* we enable true gray for cis scanners only, and just when doing * scan since color calibration is OK for this mode */ dummy = 0; if (dev->model->model_id == ModelId::CANON_4400F && session.params.yres == 1200) { dummy = 1; } /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ if (dev->model->is_cis) slope_dpi = session.params.yres * session.params.channels; else slope_dpi = session.params.yres; slope_dpi = slope_dpi * (1 + dummy); /* scan_step_type */ exposure = sensor.exposure_lperiod; if (exposure < 0) { throw std::runtime_error("Exposure not defined in sensor definition"); } const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session); // now _LOGICAL_ optical values used are known, setup registers gl843_init_optical_regs_scan(dev, sensor, reg, exposure, session); gl843_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi, session.optical_line_count, dummy, session.params.starty, session.params.flags); setup_image_pipeline(*dev, session); dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines; DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read); } ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { DBG_HELPER(dbg); debug_dump(DBG_info, settings); ScanFlag flags = ScanFlag::NONE; float move = 0.0f; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter if (!dev->ignore_offsets) { move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; } flags |= ScanFlag::USE_XPA; } else { if (!dev->ignore_offsets) { move = dev->model->y_offset; } } move += settings.tl_y; int move_dpi = dev->motor.base_ydpi; move = static_cast((move * move_dpi) / MM_PER_INCH); float start = 0.0f; if (settings.scan_method==ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { start = dev->model->x_offset_ta; } else { start = dev->model->x_offset; } start = start + settings.tl_x; start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; session.params.yres = settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; session.params.depth = settings.depth; session.params.channels = settings.get_channels(); session.params.scan_method = settings.scan_method; session.params.scan_mode = settings.scan_mode; session.params.color_filter = settings.color_filter; session.params.flags = flags; compute_session(dev, session, sensor); return session; } /** * for fast power saving methods only, like disabling certain amplifiers * @param dev device to use * @param enable true to set inot powersaving * */ void CommandSetGl843::save_power(Genesys_Device* dev, bool enable) const { DBG_HELPER_ARGS(dbg, "enable = %d", enable); // switch KV-SS080 lamp off if (dev->model->gpio_id == GpioId::KVSS080) { uint8_t val = dev->interface->read_register(REG_0x6C); if (enable) { val &= 0xef; } else { val |= 0x10; } dev->interface->write_register(REG_0x6C, val); } } void CommandSetGl843::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { (void) dev; DBG_HELPER_ARGS(dbg, "delay = %d", delay); } static bool gl843_get_paper_sensor(Genesys_Device* dev) { DBG_HELPER(dbg); uint8_t val = dev->interface->read_register(REG_0x6D); return (val & 0x1) == 0; } void CommandSetGl843::eject_document(Genesys_Device* dev) const { (void) dev; DBG_HELPER(dbg); } void CommandSetGl843::load_document(Genesys_Device* dev) const { DBG_HELPER(dbg); (void) dev; } /** * detects end of document and adjust current scan * to take it into account * used by sheetfed scanners */ void CommandSetGl843::detect_document_end(Genesys_Device* dev) const { DBG_HELPER(dbg); bool paper_loaded = gl843_get_paper_sensor(dev); /* sheetfed scanner uses home sensor as paper present */ if (dev->document && !paper_loaded) { DBG(DBG_info, "%s: no more document\n", __func__); dev->document = false; unsigned scanned_lines = 0; catch_all_exceptions(__func__, [&](){ sanei_genesys_read_scancnt(dev, &scanned_lines); }); std::size_t output_lines = dev->session.output_line_count; std::size_t offset_lines = static_cast( (dev->model->post_scan * dev->session.params.yres) / MM_PER_INCH); std::size_t scan_end_lines = scanned_lines + offset_lines; std::size_t remaining_lines = dev->get_pipeline_source().remaining_bytes() / dev->session.output_line_bytes_raw; DBG(DBG_io, "%s: scanned_lines=%u\n", __func__, scanned_lines); DBG(DBG_io, "%s: scan_end_lines=%zu\n", __func__, scan_end_lines); DBG(DBG_io, "%s: output_lines=%zu\n", __func__, output_lines); DBG(DBG_io, "%s: remaining_lines=%zu\n", __func__, remaining_lines); if (scan_end_lines > output_lines) { auto skip_lines = scan_end_lines - output_lines; if (remaining_lines > skip_lines) { remaining_lines -= skip_lines; dev->get_pipeline_source().set_remaining_bytes(remaining_lines * dev->session.output_line_bytes_raw); dev->total_bytes_to_read -= skip_lines * dev->session.output_line_bytes_requested; } } } } // Send the low-level scan command void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; /* set up GPIO for scan */ switch(dev->model->gpio_id) { /* KV case */ case GpioId::KVSS080: dev->interface->write_register(REG_0xA9, 0x00); dev->interface->write_register(REG_0xA6, 0xf6); // blinking led dev->interface->write_register(0x7e, 0x04); break; case GpioId::G4050: dev->interface->write_register(REG_0xA7, 0xfe); dev->interface->write_register(REG_0xA8, 0x3e); dev->interface->write_register(REG_0xA9, 0x06); if ((reg->get8(0x05) & REG_0x05_DPIHW) == REG_0x05_DPIHW_600) { dev->interface->write_register(REG_0x6C, 0x20); dev->interface->write_register(REG_0xA6, 0x44); } else { dev->interface->write_register(REG_0x6C, 0x60); dev->interface->write_register(REG_0xA6, 0x46); } if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } // blinking led dev->interface->write_register(REG_0x7E, 0x01); break; case GpioId::CANON_8400F: if (dev->session.params.xres == 3200) { GenesysRegisterSettingSet reg_settings = { { 0x6c, 0x00, 0x02 }, }; apply_reg_settings_to_device(*dev, reg_settings); } if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } break; case GpioId::CANON_8600F: if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } break; case GpioId::PLUSTEK_OPTICFILM_7200I: case GpioId::PLUSTEK_OPTICFILM_7300: case GpioId::PLUSTEK_OPTICFILM_7500I: { if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } break; } case GpioId::CANON_4400F: default: break; } scanner_clear_scan_and_feed_counts(*dev); // enable scan and motor uint8_t val = dev->interface->read_register(REG_0x01); val |= REG_0x01_SCAN; dev->interface->write_register(REG_0x01, val); scanner_start_action(*dev, start_motor); switch (reg->state.motor_mode) { case MotorMode::PRIMARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } break; } case MotorMode::PRIMARY_AND_SECONDARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); } break; } case MotorMode::SECONDARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); } break; } } } // Send the stop scan command void CommandSetGl843::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const { DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); // post scan gpio dev->interface->write_register(0x7e, 0x00); if (reg->state.is_xpa_on) { dev->cmd_set->set_xpa_lamp_power(*dev, false); } if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } } /** @brief Moves the slider to the home (top) position slowly * */ void CommandSetGl843::move_back_home(Genesys_Device* dev, bool wait_until_home) const { scanner_move_back_home(*dev, wait_until_home); } // init registers for shading calibration shading calibration is done at dpihw void CommandSetGl843::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); int move; float calib_size_mm = 0; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { calib_size_mm = dev->model->y_size_calib_ta_mm; } else { calib_size_mm = dev->model->y_size_calib_mm; } unsigned resolution = sensor.shading_resolution; unsigned channels = 3; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); unsigned calib_pixels = 0; unsigned calib_pixels_offset = 0; if (should_calibrate_only_active_area(*dev, dev->settings)) { float offset = dev->model->x_offset_ta; // FIXME: we should use resolution here offset = static_cast((offset * dev->settings.xres) / MM_PER_INCH); float size = dev->model->x_size_ta; size = static_cast((size * dev->settings.xres) / MM_PER_INCH); calib_pixels_offset = static_cast(offset); calib_pixels = static_cast(size); } else { calib_pixels_offset = 0; calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; } ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::DISABLE_BUFFER_FULL_MOVE; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter move = static_cast(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta); if (dev->model->model_id == ModelId::CANON_8600F && resolution == 2400) { move /= 2; } if (dev->model->model_id == ModelId::CANON_8600F && resolution == 4800) { move /= 4; } flags |= ScanFlag::USE_XPA; } else { move = static_cast(dev->model->y_offset_calib_white); } move = static_cast((move * resolution) / MM_PER_INCH); unsigned calib_lines = static_cast(calib_size_mm * resolution / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = calib_pixels_offset; session.params.starty = move; session.params.pixels = calib_pixels; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = dev->settings.scan_mode; session.params.color_filter = dev->settings.color_filter; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); dev->calib_session = session; } /** * This function sends gamma tables to ASIC */ void CommandSetGl843::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { DBG_HELPER(dbg); int size; int i; size = 256; /* allocate temporary gamma tables: 16 bits words, 3 channels */ std::vector gamma(size * 2 * 3); std::vector rgamma = get_gamma_table(dev, sensor, GENESYS_RED); std::vector ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN); std::vector bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE); // copy sensor specific's gamma tables for (i = 0; i < size; i++) { gamma[i * 2 + size * 0 + 0] = rgamma[i] & 0xff; gamma[i * 2 + size * 0 + 1] = (rgamma[i] >> 8) & 0xff; gamma[i * 2 + size * 2 + 0] = ggamma[i] & 0xff; gamma[i * 2 + size * 2 + 1] = (ggamma[i] >> 8) & 0xff; gamma[i * 2 + size * 4 + 0] = bgamma[i] & 0xff; gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff; } dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3); } /* this function does the led calibration by scanning one line of the calibration area below scanner's top on white strip. -needs working coarse/gain */ SensorExposure CommandSetGl843::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { return scanner_led_calibration(*dev, sensor, regs); } void CommandSetGl843::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { scanner_offset_calibration(*dev, sensor, regs); } void CommandSetGl843::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } // wait for lamp warmup by scanning the same line until difference // between 2 scans is below a threshold void CommandSetGl843::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg) const { DBG_HELPER(dbg); (void) sensor; unsigned channels = 3; unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method) .get_nearest_resolution_x(600); const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH / 2; *reg = dev->reg; auto flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { flags |= ScanFlag::USE_XPA; } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = (num_pixels / 2) * resolution / calib_sensor.full_resolution; session.params.starty = 0; session.params.pixels = num_pixels; session.params.lines = 1; session.params.depth = dev->model->bpp_color_values.front(); session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, reg, session); sanei_genesys_set_motor_power(*reg, false); } /** * set up GPIO/GPOE for idle state WRITE GPIO[17-21]= GPIO19 WRITE GPOE[17-21]= GPOE21 GPOE20 GPOE19 GPOE18 genesys_write_register(0xa8,0x3e) GPIO(0xa8)=0x3e */ static void gl843_init_gpio(Genesys_Device* dev) { DBG_HELPER(dbg); apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg) { dev->interface->write_register(reg.address, reg.value); }); } /* * * initialize ASIC from power on condition */ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const { DBG_HELPER(dbg); uint8_t val; if (cold) { dev->interface->write_register(0x0e, 0x01); dev->interface->write_register(0x0e, 0x00); } if(dev->usb_mode == 1) { val = 0x14; } else { val = 0x11; } dev->interface->write_0x8c(0x0f, val); // test CHKVER val = dev->interface->read_register(REG_0x40); if (val & REG_0x40_CHKVER) { val = dev->interface->read_register(0x00); DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val); } /* Set default values for registers */ gl843_init_registers (dev); if (dev->model->model_id == ModelId::CANON_8600F) { // turns on vref control for maximum current of the motor driver dev->interface->write_register(REG_0x6B, 0x72); } else { dev->interface->write_register(REG_0x6B, 0x02); } // Write initial registers dev->interface->write_registers(dev->reg); // Enable DRAM by setting a rising edge on bit 3 of reg 0x0b val = dev->reg.find_reg(0x0b).value & REG_0x0B_DRAMSEL; val = (val | REG_0x0B_ENBDRAM); dev->interface->write_register(REG_0x0B, val); dev->reg.find_reg(0x0b).value = val; if (dev->model->model_id == ModelId::CANON_8400F) { dev->interface->write_0x8c(0x1e, 0x01); dev->interface->write_0x8c(0x10, 0xb4); dev->interface->write_0x8c(0x0f, 0x02); } else if (dev->model->model_id == ModelId::CANON_8600F) { dev->interface->write_0x8c(0x10, 0xc8); } else if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->interface->write_0x8c(0x10, 0xd4); } else { dev->interface->write_0x8c(0x10, 0xb4); } /* CLKSET */ int clock_freq = REG_0x0B_48MHZ; switch (dev->model->model_id) { case ModelId::CANON_8600F: clock_freq = REG_0x0B_60MHZ; break; case ModelId::PLUSTEK_OPTICFILM_7200I: clock_freq = REG_0x0B_30MHZ; break; case ModelId::PLUSTEK_OPTICFILM_7300: case ModelId::PLUSTEK_OPTICFILM_7500I: clock_freq = REG_0x0B_40MHZ; break; default: break; } val = (dev->reg.find_reg(0x0b).value & ~REG_0x0B_CLKSET) | clock_freq; dev->interface->write_register(REG_0x0B, val); dev->reg.find_reg(0x0b).value = val; /* prevent further writings by bulk write register */ dev->reg.remove_reg(0x0b); // set RAM read address dev->interface->write_register(REG_0x29, 0x00); dev->interface->write_register(REG_0x2A, 0x00); dev->interface->write_register(REG_0x2B, 0x00); // setup gpio gl843_init_gpio(dev); dev->interface->sleep_ms(100); } /* * * initialize backend and ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home */ void CommandSetGl843::init(Genesys_Device* dev) const { DBG_INIT (); DBG_HELPER(dbg); sanei_genesys_asic_init(dev); } void CommandSetGl843::update_hardware_sensors(Genesys_Scanner* s) const { DBG_HELPER(dbg); /* do what is needed to get a new set of events, but try to not lose any of them. */ uint8_t val = s->dev->interface->read_register(REG_0x6D); switch (s->dev->model->gpio_id) { case GpioId::KVSS080: s->buttons[BUTTON_SCAN_SW].write((val & 0x04) == 0); break; case GpioId::G4050: s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0); break; case GpioId::CANON_4400F: case GpioId::CANON_8400F: default: break; } } void CommandSetGl843::update_home_sensor_gpio(Genesys_Device& dev) const { DBG_HELPER(dbg); (void) dev; } /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. */ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data, int size) const { DBG_HELPER(dbg); uint32_t final_size, i; uint8_t *buffer; int count; int offset = 0; unsigned length = size; if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { offset = dev->session.params.startx * sensor.shading_resolution / dev->session.params.xres; length = dev->session.output_pixels * sensor.shading_resolution / dev->session.params.xres; offset += sensor.shading_pixel_offset; // 16 bit words, 2 words per color, 3 color channels length *= 2 * 2 * 3; offset *= 2 * 2 * 3; } else { offset += sensor.shading_pixel_offset * 2 * 2 * 3; } dev->interface->record_key_value("shading_offset", std::to_string(offset)); dev->interface->record_key_value("shading_length", std::to_string(length)); /* compute and allocate size for final data */ final_size = ((length+251) / 252) * 256; DBG(DBG_io, "%s: final shading size=%04x (length=%d)\n", __func__, final_size, length); std::vector final_data(final_size, 0); /* copy regular shading data to the expected layout */ buffer = final_data.data(); count = 0; if (offset < 0) { count += (-offset); length -= (-offset); offset = 0; } if (static_cast(length) + offset > static_cast(size)) { length = size - offset; } /* loop over calibration data */ for (i = 0; i < length; i++) { buffer[count] = data[offset+i]; count++; if ((count % (256*2)) == (252*2)) { count += 4*2; } } dev->interface->write_buffer(0x3c, 0, final_data.data(), count); } bool CommandSetGl843::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { (void) dev; return true; } void CommandSetGl843::wait_for_motor_stop(Genesys_Device* dev) const { (void) dev; } } // namespace gl843 } // namespace genesys