kopia lustrzana https://gitlab.com/sane-project/backends
Merge branch 'genesys-duplication' into 'master'
genesys: Reduce code duplication See merge request sane-project/backends!357pixma-axis-driver
commit
a1888ae3bd
|
@ -546,6 +546,7 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \
|
|||
genesys/status.h genesys/status.cpp \
|
||||
genesys/tables_frontend.cpp \
|
||||
genesys/tables_gpo.cpp \
|
||||
genesys/tables_memory_layout.cpp \
|
||||
genesys/tables_model.cpp \
|
||||
genesys/tables_motor.cpp \
|
||||
genesys/tables_sensor.cpp \
|
||||
|
@ -554,6 +555,7 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \
|
|||
genesys/test_usb_device.h genesys/test_usb_device.cpp \
|
||||
genesys/usb_device.h genesys/usb_device.cpp \
|
||||
genesys/low.cpp genesys/low.h \
|
||||
genesys/value_filter.h \
|
||||
genesys/utilities.h
|
||||
|
||||
libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "command_set_common.h"
|
||||
#include "low.h"
|
||||
#include "value_filter.h"
|
||||
|
||||
namespace genesys {
|
||||
|
||||
|
@ -150,7 +151,7 @@ void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set&
|
|||
|
||||
struct MotorSettings {
|
||||
ModelId model_id;
|
||||
ResolutionFilter resolutions;
|
||||
ValueFilterAny<unsigned> resolutions;
|
||||
GenesysRegisterSettingSet regs_primary_and_secondary;
|
||||
GenesysRegisterSettingSet regs_primary;
|
||||
GenesysRegisterSettingSet regs_secondary;
|
||||
|
@ -187,7 +188,7 @@ void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set&
|
|||
{ 0xa6, 0x01, 0x41 },
|
||||
}
|
||||
},
|
||||
{ ModelId::HP_SCANJET_G4050, ResolutionFilter::ANY, {
|
||||
{ ModelId::HP_SCANJET_G4050, VALUE_FILTER_ANY, {
|
||||
{ 0x6b, 0x81, 0x81 }, // set MULTFILM and GPOADF
|
||||
{ 0x6c, 0x00, 0x40 }, // note that reverse change is not applied on off
|
||||
// 0xa6 register 0x08 bit likely sets motor power. No move at all without that one
|
||||
|
@ -200,9 +201,9 @@ void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set&
|
|||
{ 0xa9, 0x00, 0x10 }, // note that 0x20 bit is not reset
|
||||
}, {}
|
||||
},
|
||||
{ ModelId::PLUSTEK_OPTICFILM_7200I, ResolutionFilter::ANY, {}, {}, {} },
|
||||
{ ModelId::PLUSTEK_OPTICFILM_7300, ResolutionFilter::ANY, {}, {}, {} },
|
||||
{ ModelId::PLUSTEK_OPTICFILM_7500I, ResolutionFilter::ANY, {}, {}, {} },
|
||||
{ ModelId::PLUSTEK_OPTICFILM_7200I, VALUE_FILTER_ANY, {}, {}, {} },
|
||||
{ ModelId::PLUSTEK_OPTICFILM_7300, VALUE_FILTER_ANY, {}, {}, {} },
|
||||
{ ModelId::PLUSTEK_OPTICFILM_7500I, VALUE_FILTER_ANY, {}, {}, {} },
|
||||
};
|
||||
|
||||
for (const auto& setting : settings) {
|
||||
|
|
|
@ -227,8 +227,12 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)
|
|||
<< " initial_regs: " << format_indent_braced_list(4, dev.initial_regs) << '\n'
|
||||
<< " settings: " << format_indent_braced_list(4, dev.settings) << '\n'
|
||||
<< " frontend: " << format_indent_braced_list(4, dev.frontend) << '\n'
|
||||
<< " frontend_initial: " << format_indent_braced_list(4, dev.frontend_initial) << '\n'
|
||||
<< " gpo.regs: " << format_indent_braced_list(4, dev.gpo.regs) << '\n'
|
||||
<< " frontend_initial: " << format_indent_braced_list(4, dev.frontend_initial) << '\n';
|
||||
if (!dev.memory_layout.regs.empty()) {
|
||||
out << " memory_layout.regs: "
|
||||
<< format_indent_braced_list(4, dev.memory_layout.regs) << '\n';
|
||||
}
|
||||
out << " gpo.regs: " << format_indent_braced_list(4, dev.gpo.regs) << '\n'
|
||||
<< " motor: " << format_indent_braced_list(4, dev.motor) << '\n'
|
||||
<< " control[0..6]: " << std::hex
|
||||
<< static_cast<unsigned>(dev.control[0]) << ' '
|
||||
|
@ -272,6 +276,15 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)
|
|||
return out;
|
||||
}
|
||||
|
||||
void apply_reg_settings_to_device_write_only(Genesys_Device& dev,
|
||||
const GenesysRegisterSettingSet& regs)
|
||||
{
|
||||
GenesysRegisterSettingSet backup;
|
||||
for (const auto& reg : regs) {
|
||||
dev.interface->write_register(reg.address, reg.value);
|
||||
}
|
||||
}
|
||||
|
||||
void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs)
|
||||
{
|
||||
apply_reg_settings_to_device_with_backup(dev, regs);
|
||||
|
|
|
@ -78,6 +78,17 @@ struct Genesys_Gpo
|
|||
GenesysRegisterSettingSet regs;
|
||||
};
|
||||
|
||||
struct MemoryLayout
|
||||
{
|
||||
// This is used on GL845, GL846, GL847 and GL124 which have special registers to define the
|
||||
// memory layout
|
||||
MemoryLayout() = default;
|
||||
|
||||
ValueFilter<ModelId> models;
|
||||
|
||||
GenesysRegisterSettingSet regs;
|
||||
};
|
||||
|
||||
struct MethodResolutions
|
||||
{
|
||||
std::vector<ScanMethod> methods;
|
||||
|
@ -272,6 +283,7 @@ struct Genesys_Device
|
|||
Genesys_Settings settings;
|
||||
Genesys_Frontend frontend, frontend_initial;
|
||||
Genesys_Gpo gpo;
|
||||
MemoryLayout memory_layout;
|
||||
Genesys_Motor motor;
|
||||
std::uint8_t control[6] = {};
|
||||
|
||||
|
@ -365,6 +377,8 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev);
|
|||
|
||||
void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs);
|
||||
|
||||
void apply_reg_settings_to_device_write_only(Genesys_Device& dev,
|
||||
const GenesysRegisterSettingSet& regs);
|
||||
GenesysRegisterSettingSet
|
||||
apply_reg_settings_to_device_with_backup(Genesys_Device& dev,
|
||||
const GenesysRegisterSettingSet& regs);
|
||||
|
|
|
@ -109,6 +109,63 @@ std::ostream& operator<<(std::ostream& out, ColorFilter mode)
|
|||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, ModelId id)
|
||||
{
|
||||
switch (id) {
|
||||
case ModelId::UNKNOWN: out << "UNKNOWN"; break;
|
||||
case ModelId::CANON_4400F: out << "CANON_4400F"; break;
|
||||
case ModelId::CANON_5600F: out << "CANON_5600F"; break;
|
||||
case ModelId::CANON_8400F: out << "CANON_8400F"; break;
|
||||
case ModelId::CANON_8600F: out << "CANON_8600F"; break;
|
||||
case ModelId::CANON_IMAGE_FORMULA_101: out << "CANON_IMAGE_FORMULA_101"; break;
|
||||
case ModelId::CANON_LIDE_50: out << "CANON_LIDE_50"; break;
|
||||
case ModelId::CANON_LIDE_60: out << "CANON_LIDE_60"; break;
|
||||
case ModelId::CANON_LIDE_80: out << "CANON_LIDE_80"; break;
|
||||
case ModelId::CANON_LIDE_100: out << "CANON_LIDE_100"; break;
|
||||
case ModelId::CANON_LIDE_110: out << "CANON_LIDE_110"; break;
|
||||
case ModelId::CANON_LIDE_120: out << "CANON_LIDE_120"; break;
|
||||
case ModelId::CANON_LIDE_200: out << "CANON_LIDE_200"; break;
|
||||
case ModelId::CANON_LIDE_210: out << "CANON_LIDE_210"; break;
|
||||
case ModelId::CANON_LIDE_220: out << "CANON_LIDE_220"; break;
|
||||
case ModelId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break;
|
||||
case ModelId::DCT_DOCKETPORT_487: out << "DCT_DOCKETPORT_487"; break;
|
||||
case ModelId::HP_SCANJET_2300C: out << "HP_SCANJET_2300C"; break;
|
||||
case ModelId::HP_SCANJET_2400C: out << "HP_SCANJET_2400C"; break;
|
||||
case ModelId::HP_SCANJET_3670: out << "HP_SCANJET_3670"; break;
|
||||
case ModelId::HP_SCANJET_4850C: out << "HP_SCANJET_4850C"; break;
|
||||
case ModelId::HP_SCANJET_G4010: out << "HP_SCANJET_G4010"; break;
|
||||
case ModelId::HP_SCANJET_G4050: out << "HP_SCANJET_G4050"; break;
|
||||
case ModelId::HP_SCANJET_N6310: out << "HP_SCANJET_N6310"; break;
|
||||
case ModelId::MEDION_MD5345: out << "MEDION_MD5345"; break;
|
||||
case ModelId::PANASONIC_KV_SS080: out << "PANASONIC_KV_SS080"; break;
|
||||
case ModelId::PENTAX_DSMOBILE_600: out << "PENTAX_DSMOBILE_600"; break;
|
||||
case ModelId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break;
|
||||
case ModelId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break;
|
||||
case ModelId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break;
|
||||
case ModelId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break;
|
||||
case ModelId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break;
|
||||
case ModelId::PLUSTEK_OPTICPRO_ST12: out << "PLUSTEK_OPTICPRO_ST12"; break;
|
||||
case ModelId::PLUSTEK_OPTICPRO_ST24: out << "PLUSTEK_OPTICPRO_ST24"; break;
|
||||
case ModelId::SYSCAN_DOCKETPORT_465: out << "SYSCAN_DOCKETPORT_465"; break;
|
||||
case ModelId::SYSCAN_DOCKETPORT_467: out << "SYSCAN_DOCKETPORT_467"; break;
|
||||
case ModelId::SYSCAN_DOCKETPORT_485: out << "SYSCAN_DOCKETPORT_485"; break;
|
||||
case ModelId::SYSCAN_DOCKETPORT_665: out << "SYSCAN_DOCKETPORT_665"; break;
|
||||
case ModelId::SYSCAN_DOCKETPORT_685: out << "SYSCAN_DOCKETPORT_685"; break;
|
||||
case ModelId::UMAX_ASTRA_4500: out << "UMAX_ASTRA_4500"; break;
|
||||
case ModelId::VISIONEER_7100: out << "VISIONEER_7100"; break;
|
||||
case ModelId::VISIONEER_ROADWARRIOR: out << "VISIONEER_ROADWARRIOR"; break;
|
||||
case ModelId::VISIONEER_STROBE_XP100_REVISION3:
|
||||
out << "VISIONEER_STROBE_XP100_REVISION3"; break;
|
||||
case ModelId::VISIONEER_STROBE_XP200: out << "VISIONEER_STROBE_XP200"; break;
|
||||
case ModelId::VISIONEER_STROBE_XP300: out << "VISIONEER_STROBE_XP300"; break;
|
||||
case ModelId::XEROX_2400: out << "XEROX_2400"; break;
|
||||
case ModelId::XEROX_TRAVELSCANNER_100: out << "XEROX_TRAVELSCANNER_100"; break;
|
||||
default:
|
||||
out << static_cast<unsigned>(id); break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, StepType type)
|
||||
{
|
||||
switch (type) {
|
||||
|
|
|
@ -222,6 +222,21 @@ enum class ModelId : unsigned
|
|||
XEROX_TRAVELSCANNER_100,
|
||||
};
|
||||
|
||||
inline void serialize(std::istream& str, ModelId& x)
|
||||
{
|
||||
unsigned value;
|
||||
serialize(str, value);
|
||||
x = static_cast<ModelId>(value);
|
||||
}
|
||||
|
||||
inline void serialize(std::ostream& str, ModelId& x)
|
||||
{
|
||||
unsigned value = static_cast<unsigned>(x);
|
||||
serialize(str, value);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, ModelId id);
|
||||
|
||||
enum class SensorId : unsigned
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
|
|
|
@ -111,8 +111,6 @@ class ScannerInterfaceUsb;
|
|||
class TestScannerInterface;
|
||||
|
||||
// sensor.h
|
||||
class ScanMethodFilter;
|
||||
class ResolutionFilter;
|
||||
struct GenesysFrontendLayout;
|
||||
struct Genesys_Frontend;
|
||||
struct SensorExposure;
|
||||
|
@ -123,6 +121,10 @@ struct Genesys_Settings;
|
|||
struct SetupParams;
|
||||
struct ScanSession;
|
||||
|
||||
// value_filter.h
|
||||
template<class T> class ValueFilter;
|
||||
template<class T> class ValueFilterAny;
|
||||
|
||||
// test_usb_device.h
|
||||
class TestUsbDevice;
|
||||
|
||||
|
|
|
@ -308,6 +308,24 @@ void sanei_genesys_init_structs (Genesys_Device * dev)
|
|||
}
|
||||
}
|
||||
|
||||
if (dev->model->asic_type == AsicType::GL845 ||
|
||||
dev->model->asic_type == AsicType::GL846 ||
|
||||
dev->model->asic_type == AsicType::GL847 ||
|
||||
dev->model->asic_type == AsicType::GL124)
|
||||
{
|
||||
bool memory_layout_found = false;
|
||||
for (const auto& memory_layout : *s_memory_layout) {
|
||||
if (memory_layout.models.matches(dev->model->model_id)) {
|
||||
dev->memory_layout = memory_layout;
|
||||
memory_layout_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!memory_layout_found) {
|
||||
throw SaneException("Could not find memory layout");
|
||||
}
|
||||
}
|
||||
|
||||
if (!motor_ok || !gpo_ok || !fe_ok) {
|
||||
throw SaneException("bad description(s) for fe/gpo/motor=%d/%d/%d\n",
|
||||
static_cast<unsigned>(dev->model->sensor_id),
|
||||
|
@ -1272,6 +1290,577 @@ void scanner_search_strip(Genesys_Device& dev, bool forward, bool black)
|
|||
}
|
||||
}
|
||||
|
||||
static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
|
||||
{
|
||||
auto channels = get_pixel_channels(image.get_format());
|
||||
|
||||
unsigned avg[3];
|
||||
|
||||
// computes average values on black margin
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
avg[ch] = 0;
|
||||
unsigned count = 0;
|
||||
// FIXME: start with the second line because the black pixels often have noise on the first
|
||||
// line; the cause is probably incorrectly cleaned up previous scan
|
||||
for (std::size_t y = 1; y < image.get_height(); y++) {
|
||||
for (unsigned j = 0; j < black; j++) {
|
||||
avg[ch] += image.get_raw_channel(j, y, ch);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
avg[ch] /= count;
|
||||
}
|
||||
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
|
||||
}
|
||||
DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
|
||||
return avg[channel];
|
||||
}
|
||||
|
||||
bool should_calibrate_only_active_area(const Genesys_Device& dev,
|
||||
const Genesys_Settings& settings)
|
||||
{
|
||||
if (settings.scan_method == ScanMethod::TRANSPARENCY ||
|
||||
settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
|
||||
{
|
||||
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) {
|
||||
return true;
|
||||
}
|
||||
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float get_model_x_offset_ta(const Genesys_Device& dev, const Genesys_Settings& settings)
|
||||
{
|
||||
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
|
||||
return 85.0f;
|
||||
}
|
||||
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres == 4800) {
|
||||
return dev.model->x_offset_ta - 10.0;
|
||||
}
|
||||
return dev.model->x_offset_ta;
|
||||
}
|
||||
|
||||
void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL843 &&
|
||||
dev.frontend.layout.type != FrontendType::WOLFSON)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL845 ||
|
||||
dev.model->asic_type == AsicType::GL846)
|
||||
{
|
||||
// no gain nor offset for AKM AFE
|
||||
std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
|
||||
if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dev.model->asic_type == AsicType::GL847) {
|
||||
// no gain nor offset for AKM AFE
|
||||
std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
|
||||
if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL124) {
|
||||
std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
|
||||
if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned target_pixels = dev.model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
unsigned start_pixel = 0;
|
||||
unsigned black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
|
||||
|
||||
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
|
||||
|
||||
unsigned channels = 3;
|
||||
unsigned lines = 1;
|
||||
unsigned resolution = sensor.optical_res;
|
||||
|
||||
const Genesys_Sensor* calib_sensor = &sensor;
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
lines = 8;
|
||||
|
||||
// compute divider factor to compute final pixels number
|
||||
resolution = sensor.get_register_hwdpi(dev.settings.xres);
|
||||
unsigned factor = sensor.optical_res / resolution;
|
||||
|
||||
calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
|
||||
dev.settings.scan_method);
|
||||
|
||||
target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
black_pixels = calib_sensor->black_pixels / factor;
|
||||
|
||||
if (should_calibrate_only_active_area(dev, dev.settings)) {
|
||||
float offset = get_model_x_offset_ta(dev, dev.settings);
|
||||
offset /= calib_sensor->get_ccd_size_divisor_for_dpi(resolution);
|
||||
start_pixel = static_cast<int>((offset * resolution) / MM_PER_INCH);
|
||||
|
||||
float size = dev.model->x_size_ta;
|
||||
size /= calib_sensor->get_ccd_size_divisor_for_dpi(resolution);
|
||||
target_pixels = static_cast<int>((size * resolution) / MM_PER_INCH);
|
||||
}
|
||||
|
||||
if (dev.model->model_id == ModelId::CANON_4400F &&
|
||||
dev.settings.scan_method == ScanMethod::FLATBED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ScanFlag 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 = start_pixel;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = target_pixels;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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.model->asic_type == AsicType::GL843 ? ColorFilter::RED
|
||||
: dev.settings.color_filter;
|
||||
session.params.flags = flags;
|
||||
compute_session(&dev, session, *calib_sensor);
|
||||
|
||||
dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session);
|
||||
|
||||
unsigned output_pixels = session.output_pixels;
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
int top[3], bottom[3];
|
||||
int topavg[3], bottomavg[3], avg[3];
|
||||
|
||||
// init gain and offset
|
||||
for (unsigned ch = 0; ch < 3; ch++)
|
||||
{
|
||||
bottom[ch] = 10;
|
||||
dev.frontend.set_offset(ch, bottom[ch]);
|
||||
dev.frontend.set_gain(ch, 0);
|
||||
}
|
||||
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
|
||||
|
||||
// scan with bottom AFE settings
|
||||
dev.interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting first line reading\n", __func__);
|
||||
|
||||
dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev.interface->test_checkpoint("offset_calibration");
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
scanner_stop_action_no_move(dev, regs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Image first_line;
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
first_line = read_unshuffled_image_from_scanner(&dev, session,
|
||||
session.output_total_bytes_raw);
|
||||
scanner_stop_action_no_move(dev, regs);
|
||||
} else {
|
||||
first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
|
||||
}
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
char fn[40];
|
||||
std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.pnm",
|
||||
bottom[0], bottom[1], bottom[2]);
|
||||
sanei_genesys_write_pnm_file(fn, first_line);
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
|
||||
DBG(DBG_io2, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
|
||||
}
|
||||
|
||||
// now top value
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
top[ch] = 255;
|
||||
dev.frontend.set_offset(ch, top[ch]);
|
||||
}
|
||||
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
|
||||
|
||||
// scan with top AFE values
|
||||
dev.interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
|
||||
dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
|
||||
|
||||
Image second_line;
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
second_line = read_unshuffled_image_from_scanner(&dev, session,
|
||||
session.output_total_bytes_raw);
|
||||
scanner_stop_action_no_move(dev, regs);
|
||||
} else {
|
||||
second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < 3; ch++){
|
||||
topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
|
||||
DBG(DBG_io2, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
|
||||
}
|
||||
|
||||
unsigned pass = 0;
|
||||
|
||||
std::vector<std::uint8_t> debug_image;
|
||||
std::size_t debug_image_lines = 0;
|
||||
std::string debug_image_info;
|
||||
|
||||
// loop until acceptable level
|
||||
while ((pass < 32) && ((top[0] - bottom[0] > 1) ||
|
||||
(top[1] - bottom[1] > 1) ||
|
||||
(top[2] - bottom[2] > 1)))
|
||||
{
|
||||
pass++;
|
||||
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
if (top[ch] - bottom[ch] > 1) {
|
||||
dev.frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
|
||||
}
|
||||
}
|
||||
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
|
||||
|
||||
// scan with no move
|
||||
dev.interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
second_line = read_unshuffled_image_from_scanner(&dev, session,
|
||||
session.output_total_bytes_raw);
|
||||
scanner_stop_action_no_move(dev, regs);
|
||||
} else {
|
||||
second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
|
||||
}
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
char title[100];
|
||||
std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
|
||||
lines, output_pixels,
|
||||
dev.frontend.get_offset(0),
|
||||
dev.frontend.get_offset(1),
|
||||
dev.frontend.get_offset(2));
|
||||
debug_image_info += title;
|
||||
std::copy(second_line.get_row_ptr(0),
|
||||
second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
|
||||
std::back_inserter(debug_image));
|
||||
debug_image_lines += lines;
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
avg[ch] = dark_average_channel(second_line, black_pixels, ch);
|
||||
DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
|
||||
dev.frontend.get_offset(ch));
|
||||
}
|
||||
|
||||
// compute new boundaries
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
if (topavg[ch] >= avg[ch]) {
|
||||
topavg[ch] = avg[ch];
|
||||
top[ch] = dev.frontend.get_offset(ch);
|
||||
} else {
|
||||
bottomavg[ch] = avg[ch];
|
||||
bottom[ch] = dev.frontend.get_offset(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
sanei_genesys_write_file("gl_offset_all_desc.txt",
|
||||
reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
|
||||
debug_image_info.size());
|
||||
sanei_genesys_write_pnm_file("gl_offset_all.pnm",
|
||||
debug_image.data(), session.params.depth, channels, output_pixels,
|
||||
debug_image_lines);
|
||||
}
|
||||
|
||||
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
|
||||
dev.frontend.get_offset(0),
|
||||
dev.frontend.get_offset(1),
|
||||
dev.frontend.get_offset(2));
|
||||
}
|
||||
|
||||
/* With offset and coarse calibration we only want to get our input range into
|
||||
a reasonable shape. the fine calibration of the upper and lower bounds will
|
||||
be done with shading.
|
||||
*/
|
||||
void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs, unsigned dpi)
|
||||
{
|
||||
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL843 &&
|
||||
dev.frontend.layout.type != FrontendType::WOLFSON)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL845 ||
|
||||
dev.model->asic_type == AsicType::GL846)
|
||||
{
|
||||
// no gain nor offset for AKM AFE
|
||||
std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
|
||||
if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL847) {
|
||||
// no gain nor offset for AKM AFE
|
||||
std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
|
||||
if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL124) {
|
||||
// no gain nor offset for TI AFE
|
||||
std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
|
||||
if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL841) {
|
||||
// feed to white strip if needed
|
||||
if (dev.model->y_offset_calib_white > 0) {
|
||||
unsigned move = static_cast<unsigned>(
|
||||
(dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH);
|
||||
scanner_move(dev, dev.model->default_method, move, Direction::FORWARD);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned resolution = sensor.optical_res;
|
||||
if (dev.model->asic_type == AsicType::GL841) {
|
||||
resolution = sensor.get_register_hwdpi(dev.settings.xres);
|
||||
}
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
resolution = sensor.get_register_hwdpi(dpi);
|
||||
}
|
||||
|
||||
// coarse gain calibration is always done in color mode
|
||||
unsigned channels = 3;
|
||||
|
||||
float coeff = 1;
|
||||
|
||||
// Follow CKSEL
|
||||
if (dev.model->sensor_id == SensorId::CCD_KVSS080 ||
|
||||
dev.model->asic_type == AsicType::GL845 ||
|
||||
dev.model->asic_type == AsicType::GL846 ||
|
||||
dev.model->asic_type == AsicType::GL847 ||
|
||||
dev.model->asic_type == AsicType::GL124)
|
||||
{
|
||||
if (dev.settings.xres < sensor.optical_res) {
|
||||
coeff = 0.9f;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned lines = 10;
|
||||
if (dev.model->asic_type == AsicType::GL841) {
|
||||
lines = 1;
|
||||
}
|
||||
|
||||
const Genesys_Sensor* calib_sensor = &sensor;
|
||||
if (dev.model->asic_type == AsicType::GL841 ||
|
||||
dev.model->asic_type == AsicType::GL843)
|
||||
{
|
||||
calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
|
||||
dev.settings.scan_method);
|
||||
}
|
||||
|
||||
ScanFlag 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 = dev.model->asic_type == AsicType::GL841 ? dev.settings.yres : resolution;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = dev.model->asic_type == AsicType::GL841 ? 16 : 8;
|
||||
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);
|
||||
|
||||
std::size_t pixels = session.output_pixels;
|
||||
|
||||
try {
|
||||
dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session);
|
||||
} catch (...) {
|
||||
if (dev.model->asic_type != AsicType::GL841) {
|
||||
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
if (dev.model->asic_type != AsicType::GL841) {
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
}
|
||||
|
||||
dev.interface->write_registers(regs);
|
||||
|
||||
if (dev.model->asic_type != AsicType::GL841) {
|
||||
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
|
||||
}
|
||||
dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev.interface->test_checkpoint("coarse_gain_calibration");
|
||||
if (dev.model->asic_type == AsicType::GL841) {
|
||||
gl841::gl841_stop_action(&dev);
|
||||
} else {
|
||||
scanner_stop_action(dev);
|
||||
}
|
||||
dev.cmd_set->move_back_home(&dev, true);
|
||||
return;
|
||||
}
|
||||
|
||||
Image image;
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
|
||||
} else if (dev.model->asic_type == AsicType::GL124) {
|
||||
// BUG: we probably want to read whole image, not just first line
|
||||
image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
|
||||
} else {
|
||||
image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
|
||||
}
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
scanner_stop_action_no_move(dev, regs);
|
||||
}
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
sanei_genesys_write_pnm_file("gl_coarse_gain.pnm", image);
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
float curr_output = 0;
|
||||
float target_value = 0;
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL843) {
|
||||
std::vector<uint16_t> values;
|
||||
// FIXME: start from the second line because the first line often has artifacts. Probably
|
||||
// caused by unclean cleanup of previous scan
|
||||
for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
|
||||
values.push_back(image.get_raw_channel(x, 1, ch));
|
||||
}
|
||||
|
||||
// pick target value at 95th percentile of all values. There may be a lot of black values
|
||||
// in transparency scans for example
|
||||
std::sort(values.begin(), values.end());
|
||||
curr_output = static_cast<float>(values[unsigned((values.size() - 1) * 0.95)]);
|
||||
target_value = calib_sensor->gain_white_ref * coeff;
|
||||
|
||||
} else if (dev.model->asic_type == AsicType::GL841) {
|
||||
// FIXME: use the GL843 approach
|
||||
unsigned max = 0;
|
||||
for (std::size_t x = 0; x < image.get_width(); x++) {
|
||||
auto value = image.get_raw_channel(x, 0, ch);
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
|
||||
curr_output = max;
|
||||
target_value = 65535.0f;
|
||||
} else {
|
||||
// FIXME: use the GL843 approach
|
||||
auto width = image.get_width();
|
||||
|
||||
std::uint64_t total = 0;
|
||||
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
|
||||
total += image.get_raw_channel(x, 0, ch);
|
||||
}
|
||||
|
||||
curr_output = total / (width / 2);
|
||||
target_value = calib_sensor->gain_white_ref * coeff;
|
||||
}
|
||||
|
||||
std::uint8_t out_gain = compute_frontend_gain(curr_output, target_value,
|
||||
dev.frontend.layout.type);
|
||||
dev.frontend.set_gain(ch, out_gain);
|
||||
|
||||
DBG(DBG_proc, "%s: channel %d, curr=%f, target=%f, out_gain:%d\n", __func__, ch,
|
||||
curr_output, target_value, out_gain);
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL841 &&
|
||||
target_value / curr_output > 30)
|
||||
{
|
||||
DBG(DBG_error0, "****************************************\n");
|
||||
DBG(DBG_error0, "* *\n");
|
||||
DBG(DBG_error0, "* Extremely low Brightness detected. *\n");
|
||||
DBG(DBG_error0, "* Check the scanning head is *\n");
|
||||
DBG(DBG_error0, "* unlocked and moving. *\n");
|
||||
DBG(DBG_error0, "* *\n");
|
||||
DBG(DBG_error0, "****************************************\n");
|
||||
throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
|
||||
}
|
||||
}
|
||||
|
||||
if (dev.model->is_cis) {
|
||||
std::uint8_t min_gain = std::min({dev.frontend.get_gain(0),
|
||||
dev.frontend.get_gain(1),
|
||||
dev.frontend.get_gain(2)});
|
||||
|
||||
dev.frontend.set_gain(0, min_gain);
|
||||
dev.frontend.set_gain(1, min_gain);
|
||||
dev.frontend.set_gain(2, min_gain);
|
||||
}
|
||||
|
||||
DBG(DBG_info, "%s: gain=(%d,%d,%d)\n", __func__,
|
||||
dev.frontend.get_gain(0),
|
||||
dev.frontend.get_gain(1),
|
||||
dev.frontend.get_gain(2));
|
||||
|
||||
if (dev.model->asic_type == AsicType::GL841) {
|
||||
gl841::gl841_stop_action(&dev);
|
||||
} else {
|
||||
scanner_stop_action(dev);
|
||||
}
|
||||
|
||||
dev.cmd_set->move_back_home(&dev, true);
|
||||
}
|
||||
|
||||
void sanei_genesys_calculate_zmod(bool two_table,
|
||||
uint32_t exposure_time,
|
||||
|
@ -4534,6 +5123,7 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
|
|||
genesys_init_sensor_tables();
|
||||
genesys_init_frontend_tables();
|
||||
genesys_init_gpo_tables();
|
||||
genesys_init_memory_layout_tables();
|
||||
genesys_init_motor_tables();
|
||||
genesys_init_usb_device_tables();
|
||||
|
||||
|
|
|
@ -1460,309 +1460,16 @@ SensorExposure CommandSetGl124::led_calibration(Genesys_Device* dev, const Genes
|
|||
return { exp[0], exp[1], exp[2] };
|
||||
}
|
||||
|
||||
/**
|
||||
* average dark pixels of a 8 bits scan
|
||||
*/
|
||||
static int
|
||||
dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
|
||||
unsigned int channels, unsigned int black)
|
||||
{
|
||||
unsigned int i, j, k, average, count;
|
||||
unsigned int avg[3];
|
||||
uint8_t val;
|
||||
|
||||
/* computes average value on black margin */
|
||||
for (k = 0; k < channels; k++)
|
||||
{
|
||||
avg[k] = 0;
|
||||
count = 0;
|
||||
for (i = 0; i < lines; i++)
|
||||
{
|
||||
for (j = 0; j < black; j++)
|
||||
{
|
||||
val = data[i * channels * pixels + j + k];
|
||||
avg[k] += val;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
avg[k] /= count;
|
||||
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
|
||||
}
|
||||
average = 0;
|
||||
for (i = 0; i < channels; i++)
|
||||
average += avg[i];
|
||||
average /= channels;
|
||||
DBG(DBG_info, "%s: average = %d\n", __func__, average);
|
||||
return average;
|
||||
}
|
||||
|
||||
|
||||
void CommandSetGl124::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs) const
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned channels;
|
||||
int pass = 0, avg;
|
||||
int topavg, bottomavg, lines;
|
||||
int top, bottom, black_pixels;
|
||||
|
||||
// no gain nor offset for TI AFE
|
||||
uint8_t reg0a = dev->interface->read_register(REG_0x0A);
|
||||
if (((reg0a & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) == 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* offset calibration is always done in color mode */
|
||||
channels = 3;
|
||||
lines=1;
|
||||
unsigned pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
|
||||
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = sensor.optical_res;
|
||||
session.params.yres = sensor.optical_res;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = pixels;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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 = ScanFlag::DISABLE_SHADING |
|
||||
ScanFlag::DISABLE_GAMMA |
|
||||
ScanFlag::SINGLE_LINE |
|
||||
ScanFlag::IGNORE_STAGGER_OFFSET |
|
||||
ScanFlag::IGNORE_COLOR_OFFSET;
|
||||
compute_session(dev, session, sensor);
|
||||
|
||||
init_regs_for_scan_session(dev, sensor, ®s, session);
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
/* init gain */
|
||||
dev->frontend.set_gain(0, 0);
|
||||
dev->frontend.set_gain(1, 0);
|
||||
dev->frontend.set_gain(2, 0);
|
||||
|
||||
/* scan with no move */
|
||||
bottom = 10;
|
||||
dev->frontend.set_offset(0, bottom);
|
||||
dev->frontend.set_offset(1, bottom);
|
||||
dev->frontend.set_offset(2, bottom);
|
||||
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting first line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("offset_calibration");
|
||||
return;
|
||||
}
|
||||
|
||||
auto first_line = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data)
|
||||
{
|
||||
char title[30];
|
||||
std::snprintf(title, 30, "gl124_offset%03d.pnm", bottom);
|
||||
sanei_genesys_write_pnm_file(title, first_line);
|
||||
}
|
||||
|
||||
bottomavg = dark_average(first_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
|
||||
|
||||
/* now top value */
|
||||
top = 255;
|
||||
dev->frontend.set_offset(0, top);
|
||||
dev->frontend.set_offset(1, top);
|
||||
dev->frontend.set_offset(2, top);
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
|
||||
auto second_line = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
|
||||
|
||||
topavg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
|
||||
|
||||
/* loop until acceptable level */
|
||||
while ((pass < 32) && (top - bottom > 1))
|
||||
{
|
||||
pass++;
|
||||
|
||||
/* settings for new scan */
|
||||
dev->frontend.set_offset(0, (top + bottom) / 2);
|
||||
dev->frontend.set_offset(1, (top + bottom) / 2);
|
||||
dev->frontend.set_offset(2, (top + bottom) / 2);
|
||||
|
||||
// scan with no move
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
second_line = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
char title[30];
|
||||
std::snprintf(title, 30, "gl124_offset%03d.pnm", dev->frontend.get_offset(1));
|
||||
sanei_genesys_write_pnm_file(title, second_line);
|
||||
}
|
||||
|
||||
avg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
|
||||
|
||||
/* compute new boundaries */
|
||||
if (topavg == avg)
|
||||
{
|
||||
topavg = avg;
|
||||
top = dev->frontend.get_offset(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomavg = avg;
|
||||
bottom = dev->frontend.get_offset(1);
|
||||
}
|
||||
}
|
||||
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
|
||||
dev->frontend.get_offset(0),
|
||||
dev->frontend.get_offset(1),
|
||||
dev->frontend.get_offset(2));
|
||||
scanner_offset_calibration(*dev, sensor, regs);
|
||||
}
|
||||
|
||||
|
||||
/* alternative coarse gain calibration
|
||||
this on uses the settings from offset_calibration and
|
||||
uses only one scanline
|
||||
*/
|
||||
/*
|
||||
with offset and coarse calibration we only want to get our input range into
|
||||
a reasonable shape. the fine calibration of the upper and lower bounds will
|
||||
be done with shading.
|
||||
*/
|
||||
void CommandSetGl124::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs, int dpi) const
|
||||
{
|
||||
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
|
||||
float gain[3],coeff;
|
||||
int code, lines;
|
||||
|
||||
// no gain nor offset for TI AFE
|
||||
uint8_t reg0a = dev->interface->read_register(REG_0x0A);
|
||||
if (((reg0a & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) == 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* coarse gain calibration is always done in color mode */
|
||||
unsigned channels = 3;
|
||||
|
||||
if(dev->settings.xres<sensor.optical_res)
|
||||
{
|
||||
coeff = 0.9f;
|
||||
} else {
|
||||
coeff = 1.0f;
|
||||
}
|
||||
lines=10;
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = sensor.optical_res;
|
||||
session.params.yres = sensor.optical_res;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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 = ScanFlag::DISABLE_SHADING |
|
||||
ScanFlag::DISABLE_GAMMA |
|
||||
ScanFlag::SINGLE_LINE |
|
||||
ScanFlag::IGNORE_STAGGER_OFFSET |
|
||||
ScanFlag::IGNORE_COLOR_OFFSET;
|
||||
compute_session(dev, session, sensor);
|
||||
|
||||
try {
|
||||
init_regs_for_scan_session(dev, sensor, ®s, session);
|
||||
} catch (...) {
|
||||
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
|
||||
throw;
|
||||
}
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
dev->interface->write_registers(regs);
|
||||
|
||||
std::vector<uint8_t> line(session.output_line_bytes);
|
||||
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("coarse_gain_calibration");
|
||||
scanner_stop_action(*dev);
|
||||
move_back_home(dev, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// BUG: we probably want to read whole image, not just first line
|
||||
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
sanei_genesys_write_pnm_file("gl124_gain.pnm", image);
|
||||
}
|
||||
|
||||
/* average value on each channel */
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
|
||||
auto width = image.get_width();
|
||||
|
||||
std::uint64_t total = 0;
|
||||
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
|
||||
total += image.get_raw_channel(x, 0, ch);
|
||||
}
|
||||
|
||||
total /= width / 2;
|
||||
|
||||
gain[ch] = (static_cast<float>(sensor.gain_white_ref) * coeff) / total;
|
||||
|
||||
/* turn logical gain value into gain code, checking for overflow */
|
||||
code = static_cast<int>(283 - 208 / gain[ch]);
|
||||
code = clamp(code, 0, 255);
|
||||
dev->frontend.set_gain(ch, code);
|
||||
|
||||
DBG(DBG_proc, "%s: channel %d, total=%d, gain = %f, setting:%d\n", __func__, ch,
|
||||
static_cast<unsigned>(total),
|
||||
gain[ch], dev->frontend.get_gain(ch));
|
||||
}
|
||||
|
||||
if (dev->model->is_cis) {
|
||||
uint8_t gain0 = dev->frontend.get_gain(0);
|
||||
if (gain0 > dev->frontend.get_gain(1)) {
|
||||
gain0 = dev->frontend.get_gain(1);
|
||||
}
|
||||
if (gain0 > dev->frontend.get_gain(2)) {
|
||||
gain0 = dev->frontend.get_gain(2);
|
||||
}
|
||||
dev->frontend.set_gain(0, gain0);
|
||||
dev->frontend.set_gain(1, gain0);
|
||||
dev->frontend.set_gain(2, gain0);
|
||||
}
|
||||
|
||||
if (channels == 1) {
|
||||
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
|
||||
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
|
||||
}
|
||||
|
||||
scanner_stop_action(*dev);
|
||||
|
||||
move_back_home(dev, true);
|
||||
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
|
||||
}
|
||||
|
||||
// wait for lamp warmup by scanning the same line until difference
|
||||
|
@ -1841,64 +1548,8 @@ static void gl124_init_gpio(Genesys_Device* dev)
|
|||
static void gl124_init_memory_layout(Genesys_Device* dev)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
int idx = 0;
|
||||
|
||||
/* point to per model memory layout */
|
||||
if (dev->model->model_id == ModelId::CANON_LIDE_110 ||
|
||||
dev->model->model_id == ModelId::CANON_LIDE_120)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
else
|
||||
{ /* canon LiDE 210 and 220 case */
|
||||
idx = 1;
|
||||
}
|
||||
|
||||
/* setup base address for shading data. */
|
||||
/* values must be multiplied by 8192=0x4000 to give address on AHB */
|
||||
/* R-Channel shading bank0 address setting for CIS */
|
||||
dev->interface->write_register(0xd0, layouts[idx].rd0);
|
||||
/* G-Channel shading bank0 address setting for CIS */
|
||||
dev->interface->write_register(0xd1, layouts[idx].rd1);
|
||||
/* B-Channel shading bank0 address setting for CIS */
|
||||
dev->interface->write_register(0xd2, layouts[idx].rd2);
|
||||
|
||||
/* setup base address for scanned data. */
|
||||
/* values must be multiplied by 1024*2=0x0800 to give address on AHB */
|
||||
/* R-Channel ODD image buffer 0x0124->0x92000 */
|
||||
/* size for each buffer is 0x16d*1k word */
|
||||
dev->interface->write_register(0xe0, layouts[idx].re0);
|
||||
dev->interface->write_register(0xe1, layouts[idx].re1);
|
||||
/* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
|
||||
dev->interface->write_register(0xe2, layouts[idx].re2);
|
||||
dev->interface->write_register(0xe3, layouts[idx].re3);
|
||||
|
||||
/* R-Channel EVEN image buffer 0x0292 */
|
||||
dev->interface->write_register(0xe4, layouts[idx].re4);
|
||||
dev->interface->write_register(0xe5, layouts[idx].re5);
|
||||
/* R-Channel EVEN image buffer end-address 0x03ff*/
|
||||
dev->interface->write_register(0xe6, layouts[idx].re6);
|
||||
dev->interface->write_register(0xe7, layouts[idx].re7);
|
||||
|
||||
/* same for green, since CIS, same addresses */
|
||||
dev->interface->write_register(0xe8, layouts[idx].re0);
|
||||
dev->interface->write_register(0xe9, layouts[idx].re1);
|
||||
dev->interface->write_register(0xea, layouts[idx].re2);
|
||||
dev->interface->write_register(0xeb, layouts[idx].re3);
|
||||
dev->interface->write_register(0xec, layouts[idx].re4);
|
||||
dev->interface->write_register(0xed, layouts[idx].re5);
|
||||
dev->interface->write_register(0xee, layouts[idx].re6);
|
||||
dev->interface->write_register(0xef, layouts[idx].re7);
|
||||
|
||||
/* same for blue, since CIS, same addresses */
|
||||
dev->interface->write_register(0xf0, layouts[idx].re0);
|
||||
dev->interface->write_register(0xf1, layouts[idx].re1);
|
||||
dev->interface->write_register(0xf2, layouts[idx].re2);
|
||||
dev->interface->write_register(0xf3, layouts[idx].re3);
|
||||
dev->interface->write_register(0xf4, layouts[idx].re4);
|
||||
dev->interface->write_register(0xf5, layouts[idx].re5);
|
||||
dev->interface->write_register(0xf6, layouts[idx].re6);
|
||||
dev->interface->write_register(0xf7, layouts[idx].re7);
|
||||
apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -80,35 +80,6 @@ static Gpio_layout gpios[]={
|
|||
},
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t rd0;
|
||||
uint8_t rd1;
|
||||
uint8_t rd2;
|
||||
uint8_t re0;
|
||||
uint8_t re1;
|
||||
uint8_t re2;
|
||||
uint8_t re3;
|
||||
uint8_t re4;
|
||||
uint8_t re5;
|
||||
uint8_t re6;
|
||||
uint8_t re7;
|
||||
} Memory_layout;
|
||||
|
||||
static Memory_layout layouts[]={
|
||||
/* LIDE 110, 120 */
|
||||
{ /* 0xd0 0xd1 0xd2 */
|
||||
0x0a, 0x15, 0x20,
|
||||
/* 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 */
|
||||
0x00, 0xac, 0x08, 0x55, 0x08, 0x56, 0x0f, 0xff
|
||||
},
|
||||
/* LIDE 210, 220 */
|
||||
{
|
||||
0x0a, 0x1f, 0x34,
|
||||
0x01, 0x24, 0x08, 0x91, 0x08, 0x92, 0x0f, 0xff
|
||||
}
|
||||
};
|
||||
|
||||
static void gl124_send_slope_table(Genesys_Device* dev, int table_nr,
|
||||
const std::vector<uint16_t>& slope_table, int steps);
|
||||
|
||||
|
|
|
@ -104,44 +104,6 @@ static void gl646_stop_motor(Genesys_Device* dev)
|
|||
dev->interface->write_register(0x0f, 0x00);
|
||||
}
|
||||
|
||||
/**
|
||||
* find the closest match in mode tables for the given resolution and scan mode.
|
||||
* @param sensor id of the sensor
|
||||
* @param required required resolution
|
||||
* @param color true is color mode
|
||||
* @return the closest resolution for the sensor and mode
|
||||
*/
|
||||
static unsigned get_closest_resolution(SensorId sensor_id, int required, unsigned channels)
|
||||
{
|
||||
unsigned best_res = 0;
|
||||
unsigned best_diff = 9600;
|
||||
|
||||
for (const auto& sensor : *s_sensors) {
|
||||
if (sensor_id != sensor.sensor_id)
|
||||
continue;
|
||||
|
||||
// exit on perfect match
|
||||
if (sensor.resolutions.matches(required) && sensor.matches_channel_count(channels)) {
|
||||
DBG(DBG_info, "%s: match found for %d\n", __func__, required);
|
||||
return required;
|
||||
}
|
||||
|
||||
// computes distance and keep mode if it is closer than previous
|
||||
if (sensor.matches_channel_count(channels)) {
|
||||
for (auto res : sensor.resolutions.resolutions()) {
|
||||
unsigned curr_diff = std::abs(static_cast<int>(res) - static_cast<int>(required));
|
||||
if (curr_diff < best_diff) {
|
||||
best_res = res;
|
||||
best_diff = curr_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBG(DBG_info, "%s: closest match for %d is %d\n", __func__, required, best_res);
|
||||
return best_res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cksel values used by the required scan mode.
|
||||
* @param sensor id of the sensor
|
||||
|
@ -1820,7 +1782,6 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes
|
|||
int turn;
|
||||
uint16_t expr, expg, expb;
|
||||
Genesys_Settings settings;
|
||||
SANE_Int resolution;
|
||||
|
||||
unsigned channels = dev->settings.get_channels();
|
||||
|
||||
|
@ -1833,15 +1794,14 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes
|
|||
{
|
||||
settings.scan_mode = ScanColorMode::GRAY;
|
||||
}
|
||||
resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels);
|
||||
|
||||
/* offset calibration is always done in color mode */
|
||||
// offset calibration is always done in color mode
|
||||
settings.scan_method = dev->model->default_method;
|
||||
settings.xres = resolution;
|
||||
settings.yres = resolution;
|
||||
settings.xres = sensor.optical_res;
|
||||
settings.yres = sensor.optical_res;
|
||||
settings.tl_x = 0;
|
||||
settings.tl_y = 0;
|
||||
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
settings.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
settings.requested_pixels = settings.pixels;
|
||||
settings.lines = 1;
|
||||
settings.depth = 16;
|
||||
|
@ -1998,24 +1958,25 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&
|
|||
|
||||
unsigned int channels;
|
||||
int pass = 0;
|
||||
SANE_Int resolution;
|
||||
Genesys_Settings settings;
|
||||
unsigned int x, y, adr, min;
|
||||
unsigned int bottom, black_pixels;
|
||||
|
||||
channels = 3;
|
||||
resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels);
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
|
||||
black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res;
|
||||
|
||||
// FIXME: maybe reuse `sensor`
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, sensor.optical_res, 3,
|
||||
ScanMethod::FLATBED);
|
||||
black_pixels = (calib_sensor.black_pixels * sensor.optical_res) / calib_sensor.optical_res;
|
||||
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
|
||||
|
||||
settings.scan_method = dev->model->default_method;
|
||||
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
|
||||
settings.xres = resolution;
|
||||
settings.yres = resolution;
|
||||
settings.xres = sensor.optical_res;
|
||||
settings.yres = sensor.optical_res;
|
||||
settings.tl_x = 0;
|
||||
settings.tl_y = 0;
|
||||
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
settings.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
settings.requested_pixels = settings.pixels;
|
||||
settings.lines = CALIBRATION_LINES;
|
||||
settings.depth = 8;
|
||||
|
@ -2097,7 +2058,6 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens
|
|||
DBG_HELPER(dbg);
|
||||
(void) regs;
|
||||
|
||||
unsigned int channels;
|
||||
int pass = 0, avg;
|
||||
Genesys_Settings settings;
|
||||
int topavg, bottomavg;
|
||||
|
@ -2112,10 +2072,11 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens
|
|||
|
||||
/* setup for a RGB scan, one full sensor's width line */
|
||||
/* resolution is the one from the final scan */
|
||||
channels = 3;
|
||||
int resolution = get_closest_resolution(dev->model->sensor_id, dev->settings.xres, channels);
|
||||
unsigned resolution = dev->settings.xres;
|
||||
unsigned channels = 3;
|
||||
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
|
||||
ScanMethod::FLATBED);
|
||||
black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res;
|
||||
|
||||
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
|
||||
|
@ -2243,7 +2204,7 @@ static void ad_fe_coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sen
|
|||
(void) regs;
|
||||
|
||||
unsigned int i, channels, val;
|
||||
unsigned int size, count, resolution, pass;
|
||||
unsigned int size, count, pass;
|
||||
float average;
|
||||
Genesys_Settings settings;
|
||||
char title[32];
|
||||
|
@ -2251,18 +2212,16 @@ static void ad_fe_coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sen
|
|||
/* setup for a RGB scan, one full sensor's width line */
|
||||
/* resolution is the one from the final scan */
|
||||
channels = 3;
|
||||
resolution = get_closest_resolution(dev->model->sensor_id, dpi, channels);
|
||||
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, dpi, 3, ScanMethod::FLATBED);
|
||||
|
||||
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
|
||||
|
||||
settings.scan_method = dev->model->default_method;
|
||||
settings.xres = resolution;
|
||||
settings.yres = resolution;
|
||||
settings.xres = dpi;
|
||||
settings.yres = dpi;
|
||||
settings.tl_x = 0;
|
||||
settings.tl_y = 0;
|
||||
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
settings.pixels = dev->model->x_size_calib_mm * dpi / MM_PER_INCH;
|
||||
settings.requested_pixels = settings.pixels;
|
||||
settings.lines = CALIBRATION_LINES;
|
||||
settings.depth = 8;
|
||||
|
@ -2342,7 +2301,7 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys
|
|||
(void) dpi;
|
||||
|
||||
unsigned int i, j, k, channels, val, maximum, idx;
|
||||
unsigned int count, resolution, pass;
|
||||
unsigned count, pass;
|
||||
float average[3];
|
||||
Genesys_Settings settings;
|
||||
char title[32];
|
||||
|
@ -2355,26 +2314,26 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys
|
|||
/* resolution is the one from the final scan */
|
||||
channels = 3;
|
||||
|
||||
/* we are searching a sensor resolution */
|
||||
resolution = get_closest_resolution(dev->model->sensor_id, dev->settings.xres, channels);
|
||||
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
|
||||
// BUG: the following comment is incorrect
|
||||
// we are searching a sensor resolution */
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,
|
||||
ScanMethod::FLATBED);
|
||||
|
||||
settings.scan_method = dev->settings.scan_method;
|
||||
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
|
||||
settings.xres = resolution;
|
||||
settings.yres = resolution;
|
||||
settings.xres = dev->settings.xres;
|
||||
settings.yres = dev->settings.xres;
|
||||
settings.tl_y = 0;
|
||||
if (settings.scan_method == ScanMethod::FLATBED)
|
||||
{
|
||||
settings.tl_x = 0;
|
||||
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
settings.pixels = dev->model->x_size_calib_mm * dev->settings.xres / MM_PER_INCH;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.tl_x = dev->model->x_offset_ta;
|
||||
settings.pixels = static_cast<unsigned>((dev->model->x_size_ta * resolution) / MM_PER_INCH);
|
||||
settings.pixels = static_cast<unsigned>(
|
||||
(dev->model->x_size_ta * dev->settings.xres) / MM_PER_INCH);
|
||||
}
|
||||
settings.requested_pixels = settings.pixels;
|
||||
settings.lines = CALIBRATION_LINES;
|
||||
|
@ -2502,12 +2461,11 @@ void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Se
|
|||
(void) sensor;
|
||||
|
||||
Genesys_Settings settings;
|
||||
int resolution, lines;
|
||||
int lines;
|
||||
|
||||
dev->frontend = dev->frontend_initial;
|
||||
|
||||
resolution = get_closest_resolution(dev->model->sensor_id, 300, 1);
|
||||
|
||||
unsigned resolution = 300;
|
||||
const auto& local_sensor = sanei_genesys_find_sensor(dev, resolution, 1,
|
||||
dev->settings.scan_method);
|
||||
|
||||
|
@ -2565,7 +2523,8 @@ static void gl646_repark_head(Genesys_Device* dev)
|
|||
|
||||
settings.scan_method = dev->model->default_method;
|
||||
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
|
||||
settings.xres = get_closest_resolution(dev->model->sensor_id, 75, 1);
|
||||
settings.xres = dev->model->get_resolution_settings(dev->model->default_method)
|
||||
.get_min_resolution_y();
|
||||
settings.yres = settings.xres;
|
||||
settings.tl_x = 0;
|
||||
settings.tl_y = 5;
|
||||
|
|
|
@ -1821,6 +1821,8 @@ void CommandSetGl841::set_powersaving(Genesys_Device* dev, int delay /* in minut
|
|||
|
||||
void gl841_stop_action(Genesys_Device* dev)
|
||||
{
|
||||
// FIXME: figure out a way to merge this to scanner_stop_action()
|
||||
|
||||
DBG_HELPER(dbg);
|
||||
Genesys_Register_Set local_reg;
|
||||
unsigned int loop;
|
||||
|
@ -2949,146 +2951,7 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens
|
|||
void CommandSetGl841::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs, int dpi) const
|
||||
{
|
||||
DBG_HELPER_ARGS(dbg, "dpi=%d", dpi);
|
||||
float gain[3];
|
||||
int lines=1;
|
||||
|
||||
// feed to white strip if needed
|
||||
if (dev->model->y_offset_calib_white > 0) {
|
||||
unsigned move = static_cast<unsigned>(
|
||||
(dev->model->y_offset_calib_white * (dev->motor.base_ydpi)) / MM_PER_INCH);
|
||||
scanner_move(*dev, dev->model->default_method, move, Direction::FORWARD);
|
||||
}
|
||||
|
||||
/* coarse gain calibration is allways done in color mode */
|
||||
unsigned channels = 3;
|
||||
|
||||
unsigned resolution = sensor.get_register_hwdpi(dev->settings.xres);
|
||||
|
||||
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;
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = resolution;
|
||||
session.params.yres = dev->settings.yres;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = num_pixels;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 16;
|
||||
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 = ScanFlag::DISABLE_SHADING |
|
||||
ScanFlag::DISABLE_GAMMA |
|
||||
ScanFlag::SINGLE_LINE |
|
||||
ScanFlag::IGNORE_STAGGER_OFFSET |
|
||||
ScanFlag::IGNORE_COLOR_OFFSET;
|
||||
compute_session(dev, session, calib_sensor);
|
||||
|
||||
init_regs_for_scan_session(dev, calib_sensor, ®s, session);
|
||||
|
||||
dev->interface->write_registers(regs);
|
||||
|
||||
std::vector<uint8_t> line(session.output_total_bytes);
|
||||
|
||||
dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("coarse_gain_calibration");
|
||||
gl841_stop_action(dev);
|
||||
move_back_home(dev, true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data)
|
||||
sanei_genesys_write_pnm_file("gl841_gain.pnm", line.data(), 16, channels, num_pixels, lines);
|
||||
|
||||
/* average high level for each channel and compute gain
|
||||
to reach the target code
|
||||
we only use the central half of the CCD data */
|
||||
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
unsigned max = 0;
|
||||
for (std::size_t x = 0; x < image.get_width(); x++) {
|
||||
auto value = image.get_raw_channel(x, 0, ch);
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
|
||||
gain[ch] = 65535.0f / max;
|
||||
|
||||
uint8_t out_gain = 0;
|
||||
|
||||
if (dev->model->adc_id == AdcId::CANON_LIDE_35 ||
|
||||
dev->model->adc_id == AdcId::WOLFSON_XP300 ||
|
||||
dev->model->adc_id == AdcId::WOLFSON_DSM600)
|
||||
{
|
||||
gain[ch] *= 0.69f; // seems we don't get the real maximum. empirically derived
|
||||
if (283 - 208 / gain[ch] > 255) {
|
||||
out_gain = 255;
|
||||
} else if (283 - 208 / gain[ch] < 0) {
|
||||
out_gain = 0;
|
||||
} else {
|
||||
out_gain = static_cast<std::uint8_t>(283 - 208 / gain[ch]);
|
||||
}
|
||||
} else if (dev->model->adc_id == AdcId::CANON_LIDE_80) {
|
||||
out_gain = static_cast<std::uint8_t>(gain[ch] * 12);
|
||||
}
|
||||
dev->frontend.set_gain(ch, out_gain);
|
||||
|
||||
DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, ch, max,
|
||||
gain[ch], out_gain);
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < channels; j++) {
|
||||
if (gain[j] > 30) {
|
||||
DBG (DBG_error0, "**********************************************\n");
|
||||
DBG (DBG_error0, "**********************************************\n");
|
||||
DBG (DBG_error0, "**** ****\n");
|
||||
DBG (DBG_error0, "**** Extremely low Brightness detected. ****\n");
|
||||
DBG (DBG_error0, "**** Check the scanning head is ****\n");
|
||||
DBG (DBG_error0, "**** unlocked and moving. ****\n");
|
||||
DBG (DBG_error0, "**** ****\n");
|
||||
DBG (DBG_error0, "**********************************************\n");
|
||||
DBG (DBG_error0, "**********************************************\n");
|
||||
throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (dev->model->is_cis) {
|
||||
uint8_t gain0 = dev->frontend.get_gain(0);
|
||||
if (gain0 > dev->frontend.get_gain(1)) {
|
||||
gain0 = dev->frontend.get_gain(1);
|
||||
}
|
||||
if (gain0 > dev->frontend.get_gain(2)) {
|
||||
gain0 = dev->frontend.get_gain(2);
|
||||
}
|
||||
dev->frontend.set_gain(0, gain0);
|
||||
dev->frontend.set_gain(1, gain0);
|
||||
dev->frontend.set_gain(2, gain0);
|
||||
}
|
||||
|
||||
if (channels == 1) {
|
||||
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
|
||||
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
|
||||
}
|
||||
|
||||
DBG(DBG_info, "%s: gain=(%d,%d,%d)\n", __func__,
|
||||
dev->frontend.get_gain(0),
|
||||
dev->frontend.get_gain(1),
|
||||
dev->frontend.get_gain(2));
|
||||
|
||||
gl841_stop_action(dev);
|
||||
|
||||
dev->cmd_set->move_back_home(dev, true);
|
||||
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
|
||||
}
|
||||
|
||||
// wait for lamp warmup by scanning the same line until difference
|
||||
|
|
|
@ -755,39 +755,33 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
|
|||
exposure, scan_yres, static_cast<unsigned>(motor_profile.step_type),
|
||||
scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
|
||||
|
||||
int use_fast_fed, coeff;
|
||||
unsigned int lincnt;
|
||||
unsigned feedl, dist;
|
||||
GenesysRegister *r;
|
||||
uint32_t z1, z2;
|
||||
|
||||
/* get step multiplier */
|
||||
unsigned step_multiplier = gl843_get_step_multiplier (reg);
|
||||
|
||||
use_fast_fed = 0;
|
||||
bool use_fast_fed = false;
|
||||
|
||||
if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, MotorFlag::FEED))) {
|
||||
use_fast_fed = 1;
|
||||
use_fast_fed = true;
|
||||
}
|
||||
|
||||
lincnt=scan_lines;
|
||||
reg->set24(REG_LINCNT, lincnt);
|
||||
DBG(DBG_io, "%s: lincnt=%d\n", __func__, lincnt);
|
||||
reg->set24(REG_LINCNT, scan_lines);
|
||||
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
|
||||
|
||||
/* compute register 02 value */
|
||||
r = sanei_genesys_get_address(reg, REG_0x02);
|
||||
r->value = 0x00;
|
||||
sanei_genesys_set_motor_power(*reg, true);
|
||||
reg->set8(REG_0x02, 0);
|
||||
sanei_genesys_set_motor_power(*reg, true);
|
||||
|
||||
std::uint8_t reg02 = reg->get8(REG_0x02);
|
||||
if (use_fast_fed) {
|
||||
r->value |= REG_0x02_FASTFED;
|
||||
reg02 |= REG_0x02_FASTFED;
|
||||
} else {
|
||||
r->value &= ~REG_0x02_FASTFED;
|
||||
reg02 &= ~REG_0x02_FASTFED;
|
||||
}
|
||||
|
||||
/* in case of automatic go home, move until home sensor */
|
||||
// in case of automatic go home, move until home sensor
|
||||
if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
|
||||
r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
|
||||
reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
|
||||
}
|
||||
|
||||
/* disable backtracking */
|
||||
|
@ -795,16 +789,15 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
|
|||
||(scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F)
|
||||
||(scan_yres>=sensor.optical_res))
|
||||
{
|
||||
r->value |= REG_0x02_ACDCDIS;
|
||||
reg02 |= REG_0x02_ACDCDIS;
|
||||
}
|
||||
|
||||
if (has_flag(flags, MotorFlag::REVERSE)) {
|
||||
r->value |= REG_0x02_MTRREV;
|
||||
reg02 |= REG_0x02_MTRREV;
|
||||
} else {
|
||||
r->value &= ~REG_0x02_MTRREV;
|
||||
reg02 &= ~REG_0x02_MTRREV;
|
||||
}
|
||||
|
||||
|
||||
reg->set8(REG_0x02, reg02);
|
||||
|
||||
// scan and backtracking slope table
|
||||
auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres, exposure,
|
||||
|
@ -847,11 +840,11 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
|
|||
feedl <<= static_cast<unsigned>(motor_profile.step_type);
|
||||
|
||||
dist = scan_table.steps_count / step_multiplier;
|
||||
if (use_fast_fed)
|
||||
{
|
||||
|
||||
if (use_fast_fed) {
|
||||
dist += (fast_table.steps_count / step_multiplier) * 2;
|
||||
}
|
||||
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
|
||||
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
|
||||
|
||||
/* get sure when don't insane value : XXX STEF XXX in this case we should
|
||||
* fall back to single table move */
|
||||
|
@ -864,12 +857,13 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
|
|||
reg->set24(REG_FEEDL, feedl);
|
||||
DBG(DBG_io, "%s: feedl=%d\n", __func__, feedl);
|
||||
|
||||
/* doesn't seem to matter that much */
|
||||
// doesn't seem to matter that much
|
||||
std::uint32_t z1, z2;
|
||||
sanei_genesys_calculate_zmod(use_fast_fed,
|
||||
exposure,
|
||||
exposure,
|
||||
scan_table.table,
|
||||
scan_table.steps_count / step_multiplier,
|
||||
feedl,
|
||||
feedl,
|
||||
scan_table.steps_count / step_multiplier,
|
||||
&z1,
|
||||
&z2);
|
||||
|
@ -885,9 +879,7 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
|
|||
reg->set24(REG_Z2MOD, z2);
|
||||
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x1E);
|
||||
r->value &= 0xf0; /* 0 dummy lines */
|
||||
r->value |= scan_dummy; /* dummy lines */
|
||||
reg->set8_mask(REG_0x1E, scan_dummy, 0x0f);
|
||||
|
||||
reg->set8_mask(REG_0x67, static_cast<unsigned>(motor_profile.step_type) << REG_0x67S_STEPSEL, 0xc0);
|
||||
reg->set8_mask(REG_0x68, static_cast<unsigned>(fast_profile->step_type) << REG_0x68S_FSTPSEL, 0xc0);
|
||||
|
@ -895,32 +887,29 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
|
|||
// steps for STOP table
|
||||
reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier);
|
||||
|
||||
/* Vref XXX STEF XXX : optical divider or step type ? */
|
||||
r = sanei_genesys_get_address (reg, 0x80);
|
||||
if (!has_flag(dev->model->flags, ModelFlag::FULL_HWDPI_MODE))
|
||||
{
|
||||
r->value = 0x50;
|
||||
coeff = sensor.get_hwdpi_divisor_for_dpi(scan_yres);
|
||||
if (!has_flag(dev->model->flags, ModelFlag::FULL_HWDPI_MODE)) {
|
||||
// FIXME: take this information from motor struct
|
||||
std::uint8_t reg_vref = reg->get8(0x80);
|
||||
reg_vref = 0x50;
|
||||
unsigned coeff = sensor.get_hwdpi_divisor_for_dpi(scan_yres);
|
||||
if (dev->model->motor_id == MotorId::KVSS080) {
|
||||
if(coeff>=1)
|
||||
{
|
||||
r->value |= 0x05;
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(coeff)
|
||||
{
|
||||
case 4:
|
||||
r->value |= 0x0a;
|
||||
break;
|
||||
case 2:
|
||||
r->value |= 0x0f;
|
||||
break;
|
||||
case 1:
|
||||
r->value |= 0x0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
reg->set8(REG_0x80, reg_vref);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1170,18 +1159,6 @@ void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Gene
|
|||
DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read);
|
||||
}
|
||||
|
||||
static float get_model_x_offset_ta(const Genesys_Device& dev,
|
||||
const Genesys_Settings& settings)
|
||||
{
|
||||
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
|
||||
return 85.0f;
|
||||
}
|
||||
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres == 4800) {
|
||||
return dev.model->x_offset_ta - 10.0;
|
||||
}
|
||||
return dev.model->x_offset_ta;
|
||||
}
|
||||
|
||||
ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev,
|
||||
const Genesys_Sensor& sensor,
|
||||
const Genesys_Settings& settings) const
|
||||
|
@ -1482,22 +1459,6 @@ void CommandSetGl843::move_back_home(Genesys_Device* dev, bool wait_until_home)
|
|||
scanner_move_back_home(*dev, wait_until_home);
|
||||
}
|
||||
|
||||
static bool should_calibrate_only_active_area(const Genesys_Device& dev,
|
||||
const Genesys_Settings& settings)
|
||||
{
|
||||
if (settings.scan_method == ScanMethod::TRANSPARENCY ||
|
||||
settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
|
||||
{
|
||||
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) {
|
||||
return true;
|
||||
}
|
||||
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -1774,410 +1735,16 @@ SensorExposure CommandSetGl843::led_calibration(Genesys_Device* dev, const Genes
|
|||
return calib_sensor.exposure;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* average dark pixels of a 8 bits scan of a given channel
|
||||
*/
|
||||
static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
|
||||
{
|
||||
auto channels = get_pixel_channels(image.get_format());
|
||||
|
||||
unsigned avg[3];
|
||||
|
||||
// computes average values on black margin
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
avg[ch] = 0;
|
||||
unsigned count = 0;
|
||||
// FIXME: start with the second line because the black pixels often have noise on the first
|
||||
// line; the cause is probably incorrectly cleaned up previous scan
|
||||
for (std::size_t y = 1; y < image.get_height(); y++) {
|
||||
for (unsigned j = 0; j < black; j++) {
|
||||
avg[ch] += image.get_raw_channel(j, y, ch);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
avg[ch] /= count;
|
||||
}
|
||||
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
|
||||
}
|
||||
DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
|
||||
return avg[channel];
|
||||
}
|
||||
|
||||
/** @brief calibrate AFE offset
|
||||
* Iterate doing scans at target dpi until AFE offset if correct. One
|
||||
* color line is scanned at a time. Scanning head doesn't move.
|
||||
* @param dev device to calibrate
|
||||
*/
|
||||
void CommandSetGl843::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs) const
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
|
||||
if (dev->frontend.layout.type != FrontendType::WOLFSON)
|
||||
return;
|
||||
|
||||
unsigned channels;
|
||||
int pass, resolution, lines;
|
||||
int topavg[3], bottomavg[3], avg[3];
|
||||
int top[3], bottom[3], black_pixels, pixels, factor, dpihw;
|
||||
|
||||
/* offset calibration is always done in color mode */
|
||||
channels = 3;
|
||||
lines = 8;
|
||||
|
||||
// compute divider factor to compute final pixels number
|
||||
dpihw = sensor.get_register_hwdpi(dev->settings.xres);
|
||||
factor = sensor.optical_res / dpihw;
|
||||
resolution = dpihw;
|
||||
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
|
||||
dev->settings.scan_method);
|
||||
|
||||
int target_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
int start_pixel = 0;
|
||||
black_pixels = calib_sensor.black_pixels / factor;
|
||||
|
||||
if (should_calibrate_only_active_area(*dev, dev->settings)) {
|
||||
float offset = get_model_x_offset_ta(*dev, dev->settings);
|
||||
offset /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
|
||||
start_pixel = static_cast<int>((offset * resolution) / MM_PER_INCH);
|
||||
|
||||
float size = dev->model->x_size_ta;
|
||||
size /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
|
||||
target_pixels = static_cast<int>((size * resolution) / MM_PER_INCH);
|
||||
}
|
||||
|
||||
if (dev->model->model_id == ModelId::CANON_4400F &&
|
||||
dev->settings.scan_method == ScanMethod::FLATBED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScanFlag 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 = start_pixel;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = target_pixels;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
session.params.channels = channels;
|
||||
session.params.scan_method = dev->settings.scan_method;
|
||||
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
|
||||
session.params.color_filter = ColorFilter::RED;
|
||||
session.params.flags = flags;
|
||||
compute_session(dev, session, calib_sensor);
|
||||
pixels = session.output_pixels;
|
||||
|
||||
DBG(DBG_io, "%s: dpihw =%d\n", __func__, dpihw);
|
||||
DBG(DBG_io, "%s: factor =%d\n", __func__, factor);
|
||||
DBG(DBG_io, "%s: resolution =%d\n", __func__, resolution);
|
||||
DBG(DBG_io, "%s: pixels =%d\n", __func__, pixels);
|
||||
DBG(DBG_io, "%s: black_pixels=%d\n", __func__, black_pixels);
|
||||
init_regs_for_scan_session(dev, calib_sensor, ®s, session);
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
// init gain and offset
|
||||
for (unsigned ch = 0; ch < 3; ch++)
|
||||
{
|
||||
bottom[ch] = 10;
|
||||
dev->frontend.set_offset(ch, bottom[ch]);
|
||||
dev->frontend.set_gain(ch, 0);
|
||||
}
|
||||
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
|
||||
|
||||
// scan with bottom AFE settings
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting first line reading\n", __func__);
|
||||
|
||||
dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("offset_calibration");
|
||||
scanner_stop_action_no_move(*dev, regs);
|
||||
return;
|
||||
}
|
||||
|
||||
auto first_line = read_unshuffled_image_from_scanner(dev, session,
|
||||
session.output_total_bytes_raw);
|
||||
scanner_stop_action_no_move(*dev, regs);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data)
|
||||
{
|
||||
char fn[40];
|
||||
std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.pnm",
|
||||
bottom[0], bottom[1], bottom[2]);
|
||||
sanei_genesys_write_pnm_file(fn, first_line);
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
|
||||
DBG(DBG_io2, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
|
||||
}
|
||||
|
||||
// now top value
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
top[ch] = 255;
|
||||
dev->frontend.set_offset(ch, top[ch]);
|
||||
}
|
||||
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
|
||||
|
||||
// scan with top AFE values
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
|
||||
dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true);
|
||||
auto second_line = read_unshuffled_image_from_scanner(dev, session,
|
||||
session.output_total_bytes_raw);
|
||||
scanner_stop_action_no_move(*dev, regs);
|
||||
|
||||
for (unsigned ch = 0; ch < 3; ch++){
|
||||
topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
|
||||
DBG(DBG_io2, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
|
||||
}
|
||||
|
||||
pass = 0;
|
||||
|
||||
std::vector<uint8_t> debug_image;
|
||||
size_t debug_image_lines = 0;
|
||||
std::string debug_image_info;
|
||||
|
||||
/* loop until acceptable level */
|
||||
while ((pass < 32)
|
||||
&& ((top[0] - bottom[0] > 1)
|
||||
|| (top[1] - bottom[1] > 1) || (top[2] - bottom[2] > 1)))
|
||||
{
|
||||
pass++;
|
||||
|
||||
// settings for new scan
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
if (top[ch] - bottom[ch] > 1) {
|
||||
dev->frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
|
||||
}
|
||||
}
|
||||
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
|
||||
|
||||
// scan with no move
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true);
|
||||
second_line = read_unshuffled_image_from_scanner(dev, session,
|
||||
session.output_total_bytes_raw);
|
||||
scanner_stop_action_no_move(*dev, regs);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data)
|
||||
{
|
||||
char title[100];
|
||||
std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
|
||||
lines, pixels,
|
||||
dev->frontend.get_offset(0),
|
||||
dev->frontend.get_offset(1),
|
||||
dev->frontend.get_offset(2));
|
||||
debug_image_info += title;
|
||||
std::copy(second_line.get_row_ptr(0),
|
||||
second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
|
||||
std::back_inserter(debug_image));
|
||||
debug_image_lines += lines;
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
avg[ch] = dark_average_channel(second_line, black_pixels, ch);
|
||||
DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
|
||||
dev->frontend.get_offset(ch));
|
||||
}
|
||||
|
||||
// compute new boundaries
|
||||
for (unsigned ch = 0; ch < 3; ch++) {
|
||||
if (topavg[ch] >= avg[ch]) {
|
||||
topavg[ch] = avg[ch];
|
||||
top[ch] = dev->frontend.get_offset(ch);
|
||||
} else {
|
||||
bottomavg[ch] = avg[ch];
|
||||
bottom[ch] = dev->frontend.get_offset(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DBG_LEVEL >= DBG_data)
|
||||
{
|
||||
sanei_genesys_write_file("gl843_offset_all_desc.txt",
|
||||
reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
|
||||
debug_image_info.size());
|
||||
sanei_genesys_write_pnm_file("gl843_offset_all.pnm",
|
||||
debug_image.data(), session.params.depth, channels, pixels,
|
||||
debug_image_lines);
|
||||
}
|
||||
|
||||
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
|
||||
dev->frontend.get_offset(0),
|
||||
dev->frontend.get_offset(1),
|
||||
dev->frontend.get_offset(2));
|
||||
scanner_offset_calibration(*dev, sensor, regs);
|
||||
}
|
||||
|
||||
|
||||
/* alternative coarse gain calibration
|
||||
this on uses the settings from offset_calibration and
|
||||
uses only one scanline
|
||||
*/
|
||||
/*
|
||||
with offset and coarse calibration we only want to get our input range into
|
||||
a reasonable shape. the fine calibration of the upper and lower bounds will
|
||||
be done with shading.
|
||||
*/
|
||||
void CommandSetGl843::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs, int dpi) const
|
||||
{
|
||||
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
|
||||
int dpihw;
|
||||
float coeff;
|
||||
int lines;
|
||||
int resolution;
|
||||
|
||||
if (dev->frontend.layout.type != FrontendType::WOLFSON)
|
||||
return;
|
||||
|
||||
dpihw = sensor.get_register_hwdpi(dpi);
|
||||
|
||||
// coarse gain calibration is always done in color mode
|
||||
unsigned channels = 3;
|
||||
|
||||
/* follow CKSEL */
|
||||
if (dev->model->sensor_id == SensorId::CCD_KVSS080) {
|
||||
if(dev->settings.xres<sensor.optical_res)
|
||||
{
|
||||
coeff = 0.9f;
|
||||
}
|
||||
else
|
||||
{
|
||||
coeff=1.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
coeff=1.0;
|
||||
}
|
||||
resolution=dpihw;
|
||||
lines=10;
|
||||
|
||||
ScanFlag 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;
|
||||
}
|
||||
|
||||
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
|
||||
dev->settings.scan_method);
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = resolution;
|
||||
session.params.yres = resolution;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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);
|
||||
std::size_t pixels = session.output_pixels;
|
||||
|
||||
try {
|
||||
init_regs_for_scan_session(dev, calib_sensor, ®s, session);
|
||||
} catch (...) {
|
||||
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
|
||||
throw;
|
||||
}
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
dev->interface->write_registers(regs);
|
||||
|
||||
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
|
||||
dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("coarse_gain_calibration");
|
||||
scanner_stop_action(*dev);
|
||||
move_back_home(dev, true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw);
|
||||
scanner_stop_action_no_move(*dev, regs);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
sanei_genesys_write_pnm_file("gl843_gain.pnm", line);
|
||||
}
|
||||
|
||||
// average value on each channel
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
|
||||
std::vector<uint16_t> values;
|
||||
// FIXME: start from the second line because the first line often has artifacts. Probably
|
||||
// caused by unclean cleanup of previous scan
|
||||
for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
|
||||
values.push_back(line.get_raw_channel(x, 1, ch));
|
||||
}
|
||||
|
||||
// pick target value at 95th percentile of all values. There may be a lot of black values
|
||||
// in transparency scans for example
|
||||
std::sort(values.begin(), values.end());
|
||||
uint16_t curr_output = values[unsigned((values.size() - 1) * 0.95)];
|
||||
float target_value = calib_sensor.gain_white_ref * coeff;
|
||||
|
||||
int code = compute_frontend_gain(curr_output, target_value, dev->frontend.layout.type);
|
||||
dev->frontend.set_gain(ch, code);
|
||||
|
||||
DBG(DBG_proc, "%s: channel %d, max=%d, target=%d, setting:%d\n", __func__, ch, curr_output,
|
||||
static_cast<int>(target_value), code);
|
||||
}
|
||||
|
||||
if (dev->model->is_cis) {
|
||||
uint8_t gain0 = dev->frontend.get_gain(0);
|
||||
if (gain0 > dev->frontend.get_gain(1)) {
|
||||
gain0 = dev->frontend.get_gain(1);
|
||||
}
|
||||
if (gain0 > dev->frontend.get_gain(2)) {
|
||||
gain0 = dev->frontend.get_gain(2);
|
||||
}
|
||||
dev->frontend.set_gain(0, gain0);
|
||||
dev->frontend.set_gain(1, gain0);
|
||||
dev->frontend.set_gain(2, gain0);
|
||||
}
|
||||
|
||||
if (channels == 1) {
|
||||
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
|
||||
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
|
||||
}
|
||||
|
||||
scanner_stop_action(*dev);
|
||||
|
||||
move_back_home(dev, true);
|
||||
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
|
||||
}
|
||||
|
||||
// wait for lamp warmup by scanning the same line until difference
|
||||
|
|
|
@ -117,6 +117,9 @@ gl846_init_registers (Genesys_Device * dev)
|
|||
dev->reg.init_reg(0x09, 0x00);
|
||||
dev->reg.init_reg(0x0a, 0x00);
|
||||
dev->reg.init_reg(0x0b, 0x8b);
|
||||
if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) {
|
||||
dev->reg.init_reg(0x0b, 0x2a);
|
||||
}
|
||||
dev->reg.init_reg(0x0c, 0x00);
|
||||
dev->reg.init_reg(0x0d, 0x00);
|
||||
dev->reg.init_reg(0x10, 0x00);
|
||||
|
@ -325,51 +328,42 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
|
|||
"scan_dummy=%d, feed_steps=%d, flags=%x",
|
||||
scan_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type),
|
||||
scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
|
||||
int use_fast_fed;
|
||||
unsigned int fast_dpi;
|
||||
unsigned int feedl, dist;
|
||||
GenesysRegister *r;
|
||||
uint32_t z1, z2;
|
||||
unsigned int min_restep = 0x20;
|
||||
uint8_t val;
|
||||
unsigned int ccdlmt,tgtime;
|
||||
|
||||
unsigned step_multiplier = gl846_get_step_multiplier(reg);
|
||||
|
||||
use_fast_fed=0;
|
||||
/* no fast fed since feed works well */
|
||||
bool use_fast_fed = false;
|
||||
if (dev->settings.yres == 4444 && feed_steps > 100 && !has_flag(flags, MotorFlag::FEED)) {
|
||||
use_fast_fed = 1;
|
||||
use_fast_fed = true;
|
||||
}
|
||||
DBG (DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
|
||||
|
||||
reg->set24(REG_LINCNT, scan_lines);
|
||||
DBG (DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
|
||||
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
|
||||
|
||||
/* compute register 02 value */
|
||||
r = sanei_genesys_get_address(reg, REG_0x02);
|
||||
r->value = 0x00;
|
||||
sanei_genesys_set_motor_power(*reg, true);
|
||||
reg->set8(REG_0x02, 0);
|
||||
sanei_genesys_set_motor_power(*reg, true);
|
||||
|
||||
if (use_fast_fed)
|
||||
r->value |= REG_0x02_FASTFED;
|
||||
else
|
||||
r->value &= ~REG_0x02_FASTFED;
|
||||
std::uint8_t reg02 = reg->get8(REG_0x02);
|
||||
if (use_fast_fed) {
|
||||
reg02 |= REG_0x02_FASTFED;
|
||||
} else {
|
||||
reg02 &= ~REG_0x02_FASTFED;
|
||||
}
|
||||
|
||||
if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
|
||||
r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
|
||||
reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
|
||||
}
|
||||
|
||||
if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) ||(scan_yres>=sensor.optical_res)) {
|
||||
r->value |= REG_0x02_ACDCDIS;
|
||||
reg02 |= REG_0x02_ACDCDIS;
|
||||
}
|
||||
if (has_flag(flags, MotorFlag::REVERSE)) {
|
||||
r->value |= REG_0x02_MTRREV;
|
||||
reg02 |= REG_0x02_MTRREV;
|
||||
} else {
|
||||
r->value &= ~REG_0x02_MTRREV;
|
||||
reg02 &= ~REG_0x02_MTRREV;
|
||||
}
|
||||
reg->set8(REG_0x02, reg02);
|
||||
|
||||
/* scan and backtracking slope table */
|
||||
// scan and backtracking slope table
|
||||
auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres,
|
||||
scan_exposure_time, dev->motor.base_ydpi,
|
||||
step_multiplier, motor_profile);
|
||||
|
@ -377,8 +371,8 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
|
|||
gl846_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
|
||||
gl846_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
|
||||
|
||||
/* fast table */
|
||||
fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
|
||||
// fast table
|
||||
unsigned fast_dpi = sanei_genesys_get_lowest_ydpi(dev);
|
||||
|
||||
// BUG: looks like for fast moves we use inconsistent step type
|
||||
StepType fast_step_type = motor_profile.step_type;
|
||||
|
@ -397,30 +391,25 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
|
|||
gl846_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
|
||||
gl846_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count);
|
||||
|
||||
/* correct move distance by acceleration and deceleration amounts */
|
||||
feedl=feed_steps;
|
||||
if (use_fast_fed)
|
||||
{
|
||||
// correct move distance by acceleration and deceleration amounts
|
||||
unsigned feedl = feed_steps;
|
||||
unsigned dist = 0;
|
||||
if (use_fast_fed) {
|
||||
feedl <<= static_cast<unsigned>(fast_step_type);
|
||||
dist = (scan_table.steps_count + 2 * fast_table.steps_count);
|
||||
/* TODO read and decode REG_0xAB */
|
||||
r = sanei_genesys_get_address (reg, 0x5e);
|
||||
dist += (r->value & 31);
|
||||
/* FEDCNT */
|
||||
r = sanei_genesys_get_address(reg, REG_FEDCNT);
|
||||
dist += r->value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO read and decode REG_0xAB
|
||||
dist += (reg->get8(0x5e) & 31);
|
||||
dist += reg->get8(REG_FEDCNT);
|
||||
} else {
|
||||
feedl <<= static_cast<unsigned>(motor_profile.step_type);
|
||||
dist = scan_table.steps_count;
|
||||
if (has_flag(flags, MotorFlag::FEED)) {
|
||||
dist *= 2;
|
||||
}
|
||||
}
|
||||
DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
|
||||
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
|
||||
|
||||
/* check for overflow */
|
||||
// check for overflow
|
||||
if (dist < feedl) {
|
||||
feedl -= dist;
|
||||
} else {
|
||||
|
@ -428,13 +417,10 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
|
|||
}
|
||||
|
||||
reg->set24(REG_FEEDL, feedl);
|
||||
DBG (DBG_io ,"%s: feedl=%d\n",__func__,feedl);
|
||||
DBG(DBG_io, "%s: feedl=%d\n", __func__, feedl);
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x0C);
|
||||
ccdlmt = (r->value & REG_0x0C_CCDLMT) + 1;
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x1C);
|
||||
tgtime = 1 << (r->value & REG_0x1C_TGTIME);
|
||||
unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1;
|
||||
unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME);
|
||||
|
||||
/* hi res motor speed GPIO */
|
||||
/*
|
||||
|
@ -468,28 +454,25 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
|
|||
dev->interface->write_register(REG_0x6C, val);
|
||||
*/
|
||||
|
||||
if(dev->model->gpio_id == GpioId::IMG101) {
|
||||
if (dev->model->gpio_id == GpioId::IMG101) {
|
||||
if (scan_yres == sensor.get_register_hwdpi(scan_yres)) {
|
||||
val=1;
|
||||
dev->interface->write_register(REG_0x7E, 1);
|
||||
} else {
|
||||
dev->interface->write_register(REG_0x7E, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
val=0;
|
||||
}
|
||||
dev->interface->write_register(REG_0x7E, val);
|
||||
}
|
||||
|
||||
min_restep = (scan_table.steps_count / step_multiplier) / 2 - 1;
|
||||
unsigned min_restep = (scan_table.steps_count / step_multiplier) / 2 - 1;
|
||||
if (min_restep < 1) {
|
||||
min_restep = 1;
|
||||
}
|
||||
r = sanei_genesys_get_address(reg, REG_FWDSTEP);
|
||||
r->value = min_restep;
|
||||
r = sanei_genesys_get_address(reg, REG_BWDSTEP);
|
||||
r->value = min_restep;
|
||||
|
||||
reg->set8(REG_FWDSTEP, min_restep);
|
||||
reg->set8(REG_BWDSTEP, min_restep);
|
||||
|
||||
std::uint32_t z1, z2;
|
||||
sanei_genesys_calculate_zmod(use_fast_fed,
|
||||
scan_exposure_time*ccdlmt*tgtime,
|
||||
scan_exposure_time * ccdlmt * tgtime,
|
||||
scan_table.table,
|
||||
scan_table.steps_count,
|
||||
feedl,
|
||||
|
@ -497,21 +480,16 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
|
|||
&z1,
|
||||
&z2);
|
||||
|
||||
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
|
||||
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
|
||||
reg->set24(REG_0x60, z1 | (static_cast<unsigned>(motor_profile.step_type) << (16 + REG_0x60S_STEPSEL)));
|
||||
|
||||
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
|
||||
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
|
||||
reg->set24(REG_0x63, z2 | (static_cast<unsigned>(motor_profile.step_type) << (16 + REG_0x63S_FSTPSEL)));
|
||||
|
||||
r = sanei_genesys_get_address (reg, 0x1e);
|
||||
r->value &= 0xf0; /* 0 dummy lines */
|
||||
r->value |= scan_dummy; /* dummy lines */
|
||||
reg->set8_mask(REG_0x1E, scan_dummy, 0x0f);
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x67);
|
||||
r->value = 0x7f;
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x68);
|
||||
r->value = 0x7f;
|
||||
reg->set8(REG_0x67, 0x7f);
|
||||
reg->set8(REG_0x68, 0x7f);
|
||||
|
||||
reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier);
|
||||
reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier);
|
||||
|
@ -1153,29 +1131,10 @@ SensorExposure CommandSetGl846::led_calibration(Genesys_Device* dev, const Genes
|
|||
static void gl846_init_gpio(Genesys_Device* dev)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
int idx=0;
|
||||
|
||||
/* search GPIO profile */
|
||||
while (gpios[idx].gpio_id != GpioId::UNKNOWN && dev->model->gpio_id != gpios[idx].gpio_id) {
|
||||
idx++;
|
||||
}
|
||||
if (gpios[idx].gpio_id == GpioId::UNKNOWN)
|
||||
apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg)
|
||||
{
|
||||
throw SaneException("failed to find GPIO profile for sensor_id=%d",
|
||||
static_cast<unsigned>(dev->model->sensor_id));
|
||||
}
|
||||
|
||||
dev->interface->write_register(REG_0xA7, gpios[idx].ra7);
|
||||
dev->interface->write_register(REG_0xA6, gpios[idx].ra6);
|
||||
|
||||
dev->interface->write_register(REG_0x6B, gpios[idx].r6b);
|
||||
dev->interface->write_register(REG_0x6C, gpios[idx].r6c);
|
||||
dev->interface->write_register(REG_0x6D, gpios[idx].r6d);
|
||||
dev->interface->write_register(REG_0x6E, gpios[idx].r6e);
|
||||
dev->interface->write_register(REG_0x6F, gpios[idx].r6f);
|
||||
|
||||
dev->interface->write_register(REG_0xA8, gpios[idx].ra8);
|
||||
dev->interface->write_register(REG_0xA9, gpios[idx].ra9);
|
||||
dev->interface->write_register(reg.address, reg.value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1184,32 +1143,11 @@ static void gl846_init_gpio(Genesys_Device* dev)
|
|||
static void gl846_init_memory_layout(Genesys_Device* dev)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
int idx = 0, i;
|
||||
uint8_t val;
|
||||
|
||||
/* point to per model memory layout */
|
||||
idx = 0;
|
||||
while (layouts[idx].model != nullptr && strcmp(dev->model->name,layouts[idx].model)!=0) {
|
||||
if(strcmp(dev->model->name,layouts[idx].model)!=0)
|
||||
idx++;
|
||||
}
|
||||
if (layouts[idx].model == nullptr) {
|
||||
throw SaneException("failed to find memory layout for model %s", dev->model->name);
|
||||
}
|
||||
// prevent further writings by bulk write register
|
||||
dev->reg.remove_reg(0x0b);
|
||||
|
||||
/* CLKSET and DRAMSEL */
|
||||
val = layouts[idx].dramsel;
|
||||
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);
|
||||
|
||||
/* setup base address for shading and scanned data. */
|
||||
for(i=0;i<10;i++)
|
||||
{
|
||||
dev->interface->write_register(0xe0+i, layouts[idx].rx[i]);
|
||||
}
|
||||
apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs);
|
||||
}
|
||||
|
||||
/* *
|
||||
|
@ -1243,18 +1181,11 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const
|
|||
DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
|
||||
}
|
||||
|
||||
/* Set default values for registers */
|
||||
gl846_init_registers (dev);
|
||||
gl846_init_registers (dev);
|
||||
|
||||
// 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;
|
||||
|
||||
/* CIS_LINE */
|
||||
if (dev->model->is_cis)
|
||||
{
|
||||
|
@ -1322,292 +1253,16 @@ void CommandSetGl846::update_home_sensor_gpio(Genesys_Device& dev) const
|
|||
dev.interface->write_register(REG_0x6C, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* average dark pixels of a 8 bits scan
|
||||
*/
|
||||
static int
|
||||
dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
|
||||
unsigned int channels, unsigned int black)
|
||||
{
|
||||
unsigned int i, j, k, average, count;
|
||||
unsigned int avg[3];
|
||||
uint8_t val;
|
||||
|
||||
/* computes average value on black margin */
|
||||
for (k = 0; k < channels; k++)
|
||||
{
|
||||
avg[k] = 0;
|
||||
count = 0;
|
||||
for (i = 0; i < lines; i++)
|
||||
{
|
||||
for (j = 0; j < black; j++)
|
||||
{
|
||||
val = data[i * channels * pixels + j + k];
|
||||
avg[k] += val;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
avg[k] /= count;
|
||||
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
|
||||
}
|
||||
average = 0;
|
||||
for (i = 0; i < channels; i++)
|
||||
average += avg[i];
|
||||
average /= channels;
|
||||
DBG(DBG_info, "%s: average = %d\n", __func__, average);
|
||||
return average;
|
||||
}
|
||||
|
||||
void CommandSetGl846::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs) const
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned channels;
|
||||
int pass = 0, avg;
|
||||
int topavg, bottomavg, lines;
|
||||
int top, bottom, black_pixels;
|
||||
|
||||
// no gain nor offset for AKM AFE
|
||||
uint8_t reg04 = dev->interface->read_register(REG_0x04);
|
||||
if ((reg04 & REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* offset calibration is always done in color mode */
|
||||
channels = 3;
|
||||
lines=1;
|
||||
unsigned pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
|
||||
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = sensor.optical_res;
|
||||
session.params.yres = sensor.optical_res;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = pixels;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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 = ScanFlag::DISABLE_SHADING |
|
||||
ScanFlag::DISABLE_GAMMA |
|
||||
ScanFlag::SINGLE_LINE |
|
||||
ScanFlag::IGNORE_STAGGER_OFFSET |
|
||||
ScanFlag::IGNORE_COLOR_OFFSET;
|
||||
compute_session(dev, session, sensor);
|
||||
|
||||
init_regs_for_scan_session(dev, sensor, ®s, session);
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
/* init gain */
|
||||
dev->frontend.set_gain(0, 0);
|
||||
dev->frontend.set_gain(1, 0);
|
||||
dev->frontend.set_gain(2, 0);
|
||||
|
||||
/* scan with no move */
|
||||
bottom = 10;
|
||||
dev->frontend.set_offset(0, bottom);
|
||||
dev->frontend.set_offset(1, bottom);
|
||||
dev->frontend.set_offset(2, bottom);
|
||||
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting first line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("offset_calibration");
|
||||
return;
|
||||
}
|
||||
|
||||
auto first_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
if (DBG_LEVEL >= DBG_data)
|
||||
{
|
||||
char fn[30];
|
||||
std::snprintf(fn, 30, "gl846_offset%03d.pnm", bottom);
|
||||
sanei_genesys_write_pnm_file(fn, first_line);
|
||||
}
|
||||
|
||||
bottomavg = dark_average(first_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
|
||||
|
||||
/* now top value */
|
||||
top = 255;
|
||||
dev->frontend.set_offset(0, top);
|
||||
dev->frontend.set_offset(1, top);
|
||||
dev->frontend.set_offset(2, top);
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
auto second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
|
||||
topavg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
|
||||
|
||||
/* loop until acceptable level */
|
||||
while ((pass < 32) && (top - bottom > 1))
|
||||
{
|
||||
pass++;
|
||||
|
||||
/* settings for new scan */
|
||||
dev->frontend.set_offset(0, (top + bottom) / 2);
|
||||
dev->frontend.set_offset(1, (top + bottom) / 2);
|
||||
dev->frontend.set_offset(2, (top + bottom) / 2);
|
||||
|
||||
// scan with no move
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
char fn[30];
|
||||
std::snprintf(fn, 30, "gl846_offset%03d.pnm", dev->frontend.get_offset(1));
|
||||
sanei_genesys_write_pnm_file(fn, second_line);
|
||||
}
|
||||
|
||||
avg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
|
||||
|
||||
/* compute new boundaries */
|
||||
if (topavg == avg)
|
||||
{
|
||||
topavg = avg;
|
||||
top = dev->frontend.get_offset(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomavg = avg;
|
||||
bottom = dev->frontend.get_offset(1);
|
||||
}
|
||||
}
|
||||
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
|
||||
dev->frontend.get_offset(0),
|
||||
dev->frontend.get_offset(1),
|
||||
dev->frontend.get_offset(2));
|
||||
scanner_offset_calibration(*dev, sensor, regs);
|
||||
}
|
||||
|
||||
void CommandSetGl846::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs, int dpi) const
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
float gain[3],coeff;
|
||||
int code, lines;
|
||||
|
||||
DBG(DBG_proc, "%s: dpi = %d\n", __func__, dpi);
|
||||
|
||||
// no gain nor offset for AKM AFE
|
||||
uint8_t reg04 = dev->interface->read_register(REG_0x04);
|
||||
if ((reg04 & REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* coarse gain calibration is always done in color mode */
|
||||
unsigned channels = 3;
|
||||
|
||||
/* follow CKSEL */
|
||||
if(dev->settings.xres<sensor.optical_res)
|
||||
{
|
||||
coeff = 0.9f;
|
||||
}
|
||||
else
|
||||
{
|
||||
coeff=1.0;
|
||||
}
|
||||
lines=10;
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = sensor.optical_res;
|
||||
session.params.yres = sensor.optical_res;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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 = ScanFlag::DISABLE_SHADING |
|
||||
ScanFlag::DISABLE_GAMMA |
|
||||
ScanFlag::SINGLE_LINE |
|
||||
ScanFlag::IGNORE_STAGGER_OFFSET |
|
||||
ScanFlag::IGNORE_COLOR_OFFSET;
|
||||
compute_session(dev, session, sensor);
|
||||
|
||||
try {
|
||||
init_regs_for_scan_session(dev, sensor, ®s, session);
|
||||
} catch (...) {
|
||||
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
|
||||
throw;
|
||||
}
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
dev->interface->write_registers(regs);
|
||||
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("coarse_gain_calibration");
|
||||
scanner_stop_action(*dev);
|
||||
move_back_home(dev, true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
sanei_genesys_write_pnm_file("gl846_gain.pnm", image);
|
||||
}
|
||||
|
||||
/* average value on each channel */
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
|
||||
auto width = image.get_width();
|
||||
|
||||
std::uint64_t total = 0;
|
||||
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
|
||||
total += image.get_raw_channel(x, 0, ch);
|
||||
}
|
||||
|
||||
total /= width / 2;
|
||||
|
||||
gain[ch] = (static_cast<float>(sensor.gain_white_ref) * coeff) / total;
|
||||
|
||||
/* turn logical gain value into gain code, checking for overflow */
|
||||
code = static_cast<int>(283 - 208 / gain[ch]);
|
||||
code = clamp(code, 0, 255);
|
||||
dev->frontend.set_gain(ch, code);
|
||||
|
||||
DBG(DBG_proc, "%s: channel %d, total=%d, gain = %f, setting:%d\n", __func__, ch,
|
||||
static_cast<unsigned>(total), gain[ch], dev->frontend.get_gain(ch));
|
||||
}
|
||||
|
||||
if (dev->model->is_cis) {
|
||||
uint8_t gain0 = dev->frontend.get_gain(0);
|
||||
if (gain0 > dev->frontend.get_gain(1)) {
|
||||
gain0 = dev->frontend.get_gain(1);
|
||||
}
|
||||
if (gain0 > dev->frontend.get_gain(2)) {
|
||||
gain0 = dev->frontend.get_gain(2);
|
||||
}
|
||||
dev->frontend.set_gain(0, gain0);
|
||||
dev->frontend.set_gain(1, gain0);
|
||||
dev->frontend.set_gain(2, gain0);
|
||||
}
|
||||
|
||||
scanner_stop_action(*dev);
|
||||
|
||||
move_back_home(dev, true);
|
||||
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
|
||||
}
|
||||
|
||||
bool CommandSetGl846::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
|
||||
|
|
|
@ -50,81 +50,6 @@
|
|||
namespace genesys {
|
||||
namespace gl846 {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GpioId gpio_id;
|
||||
uint8_t r6b;
|
||||
uint8_t r6c;
|
||||
uint8_t r6d;
|
||||
uint8_t r6e;
|
||||
uint8_t r6f;
|
||||
uint8_t ra6;
|
||||
uint8_t ra7;
|
||||
uint8_t ra8;
|
||||
uint8_t ra9;
|
||||
} Gpio_Profile;
|
||||
|
||||
static Gpio_Profile gpios[]={
|
||||
{ GpioId::IMG101, 0x72, 0x1f, 0xa4, 0x13, 0xa7, 0x11, 0xff, 0x19, 0x05},
|
||||
{ GpioId::PLUSTEK_OPTICBOOK_3800, 0x30, 0x01, 0x80, 0x2d, 0x80, 0x0c, 0x8f, 0x08, 0x04},
|
||||
{ GpioId::UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *model;
|
||||
uint8_t dramsel;
|
||||
/* shading data address */
|
||||
uint8_t rd0;
|
||||
uint8_t rd1;
|
||||
uint8_t rd2;
|
||||
/* scanned data address */
|
||||
uint8_t rx[24];
|
||||
} Memory_layout;
|
||||
|
||||
static Memory_layout layouts[]={
|
||||
/* Image formula 101 */
|
||||
{
|
||||
"canon-image-formula-101",
|
||||
0x8b,
|
||||
0x0a, 0x1b, 0x00,
|
||||
{ /* RED ODD START / RED ODD END */
|
||||
0x00, 0xb0, 0x05, 0xe7, /* [0x00b0, 0x05e7] 1336*4000w */
|
||||
/* RED EVEN START / RED EVEN END */
|
||||
0x05, 0xe8, 0x0b, 0x1f, /* [0x05e8, 0x0b1f] */
|
||||
/* GREEN ODD START / GREEN ODD END */
|
||||
0x0b, 0x20, 0x10, 0x57, /* [0x0b20, 0x1057] */
|
||||
/* GREEN EVEN START / GREEN EVEN END */
|
||||
0x10, 0x58, 0x15, 0x8f, /* [0x1058, 0x158f] */
|
||||
/* BLUE ODD START / BLUE ODD END */
|
||||
0x15, 0x90, 0x1a, 0xc7, /* [0x1590,0x1ac7] */
|
||||
/* BLUE EVEN START / BLUE EVEN END */
|
||||
0x1a, 0xc8, 0x1f, 0xff /* [0x1ac8,0x1fff] */
|
||||
}
|
||||
},
|
||||
/* OpticBook 3800 */
|
||||
{
|
||||
"plustek-opticbook-3800",
|
||||
0x2a,
|
||||
0x0a, 0x0a, 0x0a,
|
||||
{ /* RED ODD START / RED ODD END */
|
||||
0x00, 0x68, 0x03, 0x00,
|
||||
/* RED EVEN START / RED EVEN END */
|
||||
0x03, 0x01, 0x05, 0x99,
|
||||
/* GREEN ODD START / GREEN ODD END */
|
||||
0x05, 0x9a, 0x08, 0x32,
|
||||
/* GREEN EVEN START / GREEN EVEN END */
|
||||
0x08, 0x33, 0x0a, 0xcb,
|
||||
/* BLUE ODD START / BLUE ODD END */
|
||||
0x0a, 0xcc, 0x0d, 0x64,
|
||||
/* BLUE EVEN START / BLUE EVEN END */
|
||||
0x0d, 0x65, 0x0f, 0xfd
|
||||
}
|
||||
},
|
||||
/* list terminating entry */
|
||||
{ nullptr, 0, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }
|
||||
};
|
||||
|
||||
class CommandSetGl846 : public CommandSetCommon
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -194,7 +194,7 @@ static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
|
|||
static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
|
||||
static constexpr RegShift REG_0x1DS_TGSHLD = 0;
|
||||
|
||||
|
||||
static constexpr RegAddr REG_0x1E = 0x1e;
|
||||
static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
|
||||
static constexpr RegShift REG_0x1ES_WDTIME = 4;
|
||||
static constexpr RegMask REG_0x1E_LINESEL = 0x0f;
|
||||
|
|
|
@ -352,64 +352,52 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
|
|||
"scan_dummy=%d, feed_steps=%d, flags=%x",
|
||||
scan_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type),
|
||||
scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
|
||||
int use_fast_fed;
|
||||
unsigned int fast_dpi;
|
||||
unsigned int feedl, dist;
|
||||
GenesysRegister *r;
|
||||
uint32_t z1, z2;
|
||||
unsigned int min_restep = 0x20;
|
||||
uint8_t val;
|
||||
unsigned int ccdlmt,tgtime;
|
||||
|
||||
unsigned step_multiplier = gl847_get_step_multiplier (reg);
|
||||
|
||||
use_fast_fed=0;
|
||||
/* no fast fed since feed works well */
|
||||
if (dev->settings.yres==4444 && feed_steps > 100 && (!has_flag(flags, MotorFlag::FEED)))
|
||||
{
|
||||
use_fast_fed=1;
|
||||
bool use_fast_fed = false;
|
||||
if (dev->settings.yres == 4444 && feed_steps > 100 && !has_flag(flags, MotorFlag::FEED)) {
|
||||
use_fast_fed = true;
|
||||
}
|
||||
DBG(DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
|
||||
|
||||
reg->set24(REG_LINCNT, scan_lines);
|
||||
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
|
||||
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
|
||||
|
||||
/* compute register 02 value */
|
||||
r = sanei_genesys_get_address(reg, REG_0x02);
|
||||
r->value = 0x00;
|
||||
sanei_genesys_set_motor_power(*reg, true);
|
||||
reg->set8(REG_0x02, 0);
|
||||
sanei_genesys_set_motor_power(*reg, true);
|
||||
|
||||
std::uint8_t reg02 = reg->get8(REG_0x02);
|
||||
if (use_fast_fed) {
|
||||
r->value |= REG_0x02_FASTFED;
|
||||
reg02 |= REG_0x02_FASTFED;
|
||||
} else {
|
||||
r->value &= ~REG_0x02_FASTFED;
|
||||
reg02 &= ~REG_0x02_FASTFED;
|
||||
}
|
||||
|
||||
if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
|
||||
r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
|
||||
reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
|
||||
}
|
||||
|
||||
if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE)
|
||||
||(scan_yres>=sensor.optical_res))
|
||||
{
|
||||
r->value |= REG_0x02_ACDCDIS;
|
||||
if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) ||(scan_yres>=sensor.optical_res)) {
|
||||
reg02 |= REG_0x02_ACDCDIS;
|
||||
}
|
||||
|
||||
if (has_flag(flags, MotorFlag::REVERSE)) {
|
||||
r->value |= REG_0x02_MTRREV;
|
||||
reg02 |= REG_0x02_MTRREV;
|
||||
} else {
|
||||
r->value &= ~REG_0x02_MTRREV;
|
||||
reg02 &= ~REG_0x02_MTRREV;
|
||||
}
|
||||
reg->set8(REG_0x02, reg02);
|
||||
|
||||
/* scan and backtracking slope table */
|
||||
// scan and backtracking slope table
|
||||
auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres,
|
||||
scan_exposure_time, dev->motor.base_ydpi,
|
||||
step_multiplier, motor_profile);
|
||||
gl847_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
|
||||
gl847_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
|
||||
|
||||
/* fast table */
|
||||
fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
|
||||
// fast table
|
||||
unsigned fast_dpi = sanei_genesys_get_lowest_ydpi(dev);
|
||||
|
||||
// BUG: looks like for fast moves we use inconsistent step type
|
||||
StepType fast_step_type = motor_profile.step_type;
|
||||
if (static_cast<unsigned>(motor_profile.step_type) >= static_cast<unsigned>(StepType::QUARTER)) {
|
||||
fast_step_type = StepType::QUARTER;
|
||||
|
@ -426,30 +414,26 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
|
|||
gl847_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
|
||||
gl847_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count);
|
||||
|
||||
/* correct move distance by acceleration and deceleration amounts */
|
||||
feedl=feed_steps;
|
||||
if (use_fast_fed)
|
||||
// correct move distance by acceleration and deceleration amounts
|
||||
unsigned feedl = feed_steps;
|
||||
unsigned dist = 0;
|
||||
if (use_fast_fed)
|
||||
{
|
||||
feedl <<= static_cast<unsigned>(fast_step_type);
|
||||
dist = (scan_table.steps_count + 2 * fast_table.steps_count);
|
||||
/* TODO read and decode REG_0xAB */
|
||||
r = sanei_genesys_get_address (reg, 0x5e);
|
||||
dist += (r->value & 31);
|
||||
/* FEDCNT */
|
||||
r = sanei_genesys_get_address (reg, REG_FEDCNT);
|
||||
dist += r->value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO read and decode REG_0xAB
|
||||
dist += (reg->get8(0x5e) & 31);
|
||||
dist += reg->get8(REG_FEDCNT);
|
||||
} else {
|
||||
feedl <<= static_cast<unsigned>(motor_profile.step_type);
|
||||
dist = scan_table.steps_count;
|
||||
if (has_flag(flags, MotorFlag::FEED)) {
|
||||
dist *= 2;
|
||||
}
|
||||
}
|
||||
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
|
||||
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
|
||||
|
||||
/* check for overflow */
|
||||
// check for overflow
|
||||
if (dist < feedl) {
|
||||
feedl -= dist;
|
||||
} else {
|
||||
|
@ -457,25 +441,21 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
|
|||
}
|
||||
|
||||
reg->set24(REG_FEEDL, feedl);
|
||||
DBG(DBG_io ,"%s: feedl=%d\n", __func__, feedl);
|
||||
DBG(DBG_io ,"%s: feedl=%d\n", __func__, feedl);
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x0C);
|
||||
ccdlmt = (r->value & REG_0x0C_CCDLMT) + 1;
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x1C);
|
||||
tgtime = 1<<(r->value & REG_0x1C_TGTIME);
|
||||
unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1;
|
||||
unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME);
|
||||
|
||||
// hi res motor speed GPIO
|
||||
uint8_t effective = dev->interface->read_register(REG_0x6C);
|
||||
|
||||
// if quarter step, bipolar Vref2
|
||||
|
||||
std::uint8_t val = effective;
|
||||
if (motor_profile.step_type == StepType::QUARTER) {
|
||||
val = effective & ~REG_0x6C_GPIO13;
|
||||
} else if (static_cast<unsigned>(motor_profile.step_type) > static_cast<unsigned>(StepType::QUARTER)) {
|
||||
val = effective | REG_0x6C_GPIO13;
|
||||
} else {
|
||||
val = effective;
|
||||
}
|
||||
dev->interface->write_register(REG_0x6C, val);
|
||||
|
||||
|
@ -484,39 +464,34 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
|
|||
val = effective | REG_0x6C_GPIO10;
|
||||
dev->interface->write_register(REG_0x6C, val);
|
||||
|
||||
min_restep = scan_table.steps_count / (2 * step_multiplier) - 1;
|
||||
unsigned min_restep = scan_table.steps_count / (2 * step_multiplier) - 1;
|
||||
if (min_restep < 1) {
|
||||
min_restep = 1;
|
||||
}
|
||||
r = sanei_genesys_get_address(reg, REG_FWDSTEP);
|
||||
r->value = min_restep;
|
||||
r = sanei_genesys_get_address(reg, REG_BWDSTEP);
|
||||
r->value = min_restep;
|
||||
|
||||
reg->set8(REG_FWDSTEP, min_restep);
|
||||
reg->set8(REG_BWDSTEP, min_restep);
|
||||
|
||||
std::uint32_t z1, z2;
|
||||
sanei_genesys_calculate_zmod(use_fast_fed,
|
||||
scan_exposure_time*ccdlmt*tgtime,
|
||||
scan_exposure_time * ccdlmt * tgtime,
|
||||
scan_table.table,
|
||||
scan_table.steps_count,
|
||||
feedl,
|
||||
feedl,
|
||||
min_restep * step_multiplier,
|
||||
&z1,
|
||||
&z2);
|
||||
|
||||
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
|
||||
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
|
||||
reg->set24(REG_0x60, z1 | (static_cast<unsigned>(motor_profile.step_type) << (16+REG_0x60S_STEPSEL)));
|
||||
|
||||
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
|
||||
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
|
||||
reg->set24(REG_0x63, z2 | (static_cast<unsigned>(motor_profile.step_type) << (16+REG_0x63S_FSTPSEL)));
|
||||
|
||||
r = sanei_genesys_get_address (reg, 0x1e);
|
||||
r->value &= 0xf0; /* 0 dummy lines */
|
||||
r->value |= scan_dummy; /* dummy lines */
|
||||
reg->set8_mask(REG_0x1E, scan_dummy, 0x0f);
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x67);
|
||||
r->value = REG_0x67_MTRPWM;
|
||||
|
||||
r = sanei_genesys_get_address(reg, REG_0x68);
|
||||
r->value = REG_0x68_FASTPWM;
|
||||
reg->set8(REG_0x67, REG_0x67_MTRPWM);
|
||||
reg->set8(REG_0x68, REG_0x68_FASTPWM);
|
||||
|
||||
reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier);
|
||||
reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier);
|
||||
|
@ -1184,77 +1159,25 @@ static void gl847_init_gpio(Genesys_Device* dev)
|
|||
static void gl847_init_memory_layout(Genesys_Device* dev)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
int idx = 0;
|
||||
uint8_t val;
|
||||
|
||||
/* point to per model memory layout */
|
||||
idx = 0;
|
||||
if (dev->model->model_id == ModelId::CANON_LIDE_100) {
|
||||
idx = 0;
|
||||
}
|
||||
if (dev->model->model_id == ModelId::CANON_LIDE_200) {
|
||||
idx = 1;
|
||||
}
|
||||
if (dev->model->model_id == ModelId::CANON_5600F) {
|
||||
idx = 2;
|
||||
}
|
||||
if (dev->model->model_id == ModelId::CANON_LIDE_700F) {
|
||||
idx = 3;
|
||||
// TODO: move to initial register list
|
||||
switch (dev->model->model_id) {
|
||||
case ModelId::CANON_LIDE_100:
|
||||
case ModelId::CANON_LIDE_200:
|
||||
case ModelId::CANON_5600F:
|
||||
dev->interface->write_register(REG_0x0B, 0x29);
|
||||
break;
|
||||
case ModelId::CANON_LIDE_700F:
|
||||
dev->interface->write_register(REG_0x0B, 0x2a);
|
||||
break;
|
||||
default:
|
||||
throw SaneException("Unknown device");
|
||||
}
|
||||
|
||||
/* CLKSET nd DRAMSEL */
|
||||
val = layouts[idx].dramsel;
|
||||
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);
|
||||
|
||||
/* prevent further writings by bulk write register */
|
||||
dev->reg.remove_reg(0x0b);
|
||||
|
||||
/* setup base address for shading data. */
|
||||
/* values must be multiplied by 8192=0x4000 to give address on AHB */
|
||||
/* R-Channel shading bank0 address setting for CIS */
|
||||
dev->interface->write_register(0xd0, layouts[idx].rd0);
|
||||
/* G-Channel shading bank0 address setting for CIS */
|
||||
dev->interface->write_register(0xd1, layouts[idx].rd1);
|
||||
/* B-Channel shading bank0 address setting for CIS */
|
||||
dev->interface->write_register(0xd2, layouts[idx].rd2);
|
||||
|
||||
/* setup base address for scanned data. */
|
||||
/* values must be multiplied by 1024*2=0x0800 to give address on AHB */
|
||||
/* R-Channel ODD image buffer 0x0124->0x92000 */
|
||||
/* size for each buffer is 0x16d*1k word */
|
||||
dev->interface->write_register(0xe0, layouts[idx].re0);
|
||||
dev->interface->write_register(0xe1, layouts[idx].re1);
|
||||
/* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
|
||||
dev->interface->write_register(0xe2, layouts[idx].re2);
|
||||
dev->interface->write_register(0xe3, layouts[idx].re3);
|
||||
|
||||
/* R-Channel EVEN image buffer 0x0292 */
|
||||
dev->interface->write_register(0xe4, layouts[idx].re4);
|
||||
dev->interface->write_register(0xe5, layouts[idx].re5);
|
||||
/* R-Channel EVEN image buffer end-address 0x03ff*/
|
||||
dev->interface->write_register(0xe6, layouts[idx].re6);
|
||||
dev->interface->write_register(0xe7, layouts[idx].re7);
|
||||
|
||||
/* same for green, since CIS, same addresses */
|
||||
dev->interface->write_register(0xe8, layouts[idx].re0);
|
||||
dev->interface->write_register(0xe9, layouts[idx].re1);
|
||||
dev->interface->write_register(0xea, layouts[idx].re2);
|
||||
dev->interface->write_register(0xeb, layouts[idx].re3);
|
||||
dev->interface->write_register(0xec, layouts[idx].re4);
|
||||
dev->interface->write_register(0xed, layouts[idx].re5);
|
||||
dev->interface->write_register(0xee, layouts[idx].re6);
|
||||
dev->interface->write_register(0xef, layouts[idx].re7);
|
||||
|
||||
/* same for blue, since CIS, same addresses */
|
||||
dev->interface->write_register(0xf0, layouts[idx].re0);
|
||||
dev->interface->write_register(0xf1, layouts[idx].re1);
|
||||
dev->interface->write_register(0xf2, layouts[idx].re2);
|
||||
dev->interface->write_register(0xf3, layouts[idx].re3);
|
||||
dev->interface->write_register(0xf4, layouts[idx].re4);
|
||||
dev->interface->write_register(0xf5, layouts[idx].re5);
|
||||
dev->interface->write_register(0xf6, layouts[idx].re6);
|
||||
dev->interface->write_register(0xf7, layouts[idx].re7);
|
||||
apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs);
|
||||
}
|
||||
|
||||
/* *
|
||||
|
@ -1363,294 +1286,16 @@ void CommandSetGl847::update_home_sensor_gpio(Genesys_Device& dev) const
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* average dark pixels of a 8 bits scan
|
||||
*/
|
||||
static int
|
||||
dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
|
||||
unsigned int channels, unsigned int black)
|
||||
{
|
||||
unsigned int i, j, k, average, count;
|
||||
unsigned int avg[3];
|
||||
uint8_t val;
|
||||
|
||||
/* computes average value on black margin */
|
||||
for (k = 0; k < channels; k++)
|
||||
{
|
||||
avg[k] = 0;
|
||||
count = 0;
|
||||
for (i = 0; i < lines; i++)
|
||||
{
|
||||
for (j = 0; j < black; j++)
|
||||
{
|
||||
val = data[i * channels * pixels + j + k];
|
||||
avg[k] += val;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
avg[k] /= count;
|
||||
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
|
||||
}
|
||||
average = 0;
|
||||
for (i = 0; i < channels; i++)
|
||||
average += avg[i];
|
||||
average /= channels;
|
||||
DBG(DBG_info, "%s: average = %d\n", __func__, average);
|
||||
return average;
|
||||
}
|
||||
|
||||
void CommandSetGl847::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs) const
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned channels;
|
||||
int pass = 0, avg;
|
||||
int topavg, bottomavg, lines;
|
||||
int top, bottom, black_pixels;
|
||||
|
||||
// no gain nor offset for AKM AFE
|
||||
uint8_t reg04 = dev->interface->read_register(REG_0x04);
|
||||
if ((reg04 & REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* offset calibration is always done in color mode */
|
||||
channels = 3;
|
||||
lines=1;
|
||||
unsigned pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
|
||||
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = sensor.optical_res;
|
||||
session.params.yres = sensor.optical_res;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = pixels;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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 = ScanFlag::DISABLE_SHADING |
|
||||
ScanFlag::DISABLE_GAMMA |
|
||||
ScanFlag::SINGLE_LINE |
|
||||
ScanFlag::IGNORE_STAGGER_OFFSET |
|
||||
ScanFlag::IGNORE_COLOR_OFFSET;
|
||||
compute_session(dev, session, sensor);
|
||||
|
||||
init_regs_for_scan_session(dev, sensor, ®s, session);
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
/* init gain */
|
||||
dev->frontend.set_gain(0, 0);
|
||||
dev->frontend.set_gain(1, 0);
|
||||
dev->frontend.set_gain(2, 0);
|
||||
|
||||
/* scan with no move */
|
||||
bottom = 10;
|
||||
dev->frontend.set_offset(0, bottom);
|
||||
dev->frontend.set_offset(1, bottom);
|
||||
dev->frontend.set_offset(2, bottom);
|
||||
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting first line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("offset_calibration");
|
||||
return;
|
||||
}
|
||||
|
||||
auto first_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
char fn[30];
|
||||
std::snprintf(fn, 30, "gl847_offset%03d.pnm", bottom);
|
||||
sanei_genesys_write_pnm_file(fn, first_line);
|
||||
}
|
||||
|
||||
bottomavg = dark_average(first_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
|
||||
|
||||
/* now top value */
|
||||
top = 255;
|
||||
dev->frontend.set_offset(0, top);
|
||||
dev->frontend.set_offset(1, top);
|
||||
dev->frontend.set_offset(2, top);
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
auto second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
|
||||
topavg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
|
||||
|
||||
/* loop until acceptable level */
|
||||
while ((pass < 32) && (top - bottom > 1))
|
||||
{
|
||||
pass++;
|
||||
|
||||
/* settings for new scan */
|
||||
dev->frontend.set_offset(0, (top + bottom) / 2);
|
||||
dev->frontend.set_offset(1, (top + bottom) / 2);
|
||||
dev->frontend.set_offset(2, (top + bottom) / 2);
|
||||
|
||||
// scan with no move
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
dev->interface->write_registers(regs);
|
||||
DBG(DBG_info, "%s: starting second line reading\n", __func__);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
char fn[30];
|
||||
std::snprintf(fn, 30, "gl847_offset%03d.pnm", dev->frontend.get_offset(1));
|
||||
sanei_genesys_write_pnm_file(fn, second_line);
|
||||
}
|
||||
|
||||
avg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
|
||||
DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
|
||||
|
||||
/* compute new boundaries */
|
||||
if (topavg == avg)
|
||||
{
|
||||
topavg = avg;
|
||||
top = dev->frontend.get_offset(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomavg = avg;
|
||||
bottom = dev->frontend.get_offset(1);
|
||||
}
|
||||
}
|
||||
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
|
||||
dev->frontend.get_offset(0),
|
||||
dev->frontend.get_offset(1),
|
||||
dev->frontend.get_offset(2));
|
||||
scanner_offset_calibration(*dev, sensor, regs);
|
||||
}
|
||||
|
||||
void CommandSetGl847::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs, int dpi) const
|
||||
{
|
||||
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
|
||||
float gain[3],coeff;
|
||||
int code, lines;
|
||||
|
||||
// no gain nor offset for AKM AFE
|
||||
uint8_t reg04 = dev->interface->read_register(REG_0x04);
|
||||
if ((reg04 & REG_0x04_FESET) == 0x02) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* coarse gain calibration is always done in color mode */
|
||||
unsigned channels = 3;
|
||||
|
||||
/* follow CKSEL */
|
||||
if(dev->settings.xres<sensor.optical_res)
|
||||
{
|
||||
coeff = 0.9f;
|
||||
}
|
||||
else
|
||||
{
|
||||
coeff=1.0;
|
||||
}
|
||||
lines=10;
|
||||
|
||||
ScanSession session;
|
||||
session.params.xres = sensor.optical_res;
|
||||
session.params.yres = sensor.optical_res;
|
||||
session.params.startx = 0;
|
||||
session.params.starty = 0;
|
||||
session.params.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
|
||||
session.params.lines = lines;
|
||||
session.params.depth = 8;
|
||||
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 = ScanFlag::DISABLE_SHADING |
|
||||
ScanFlag::DISABLE_GAMMA |
|
||||
ScanFlag::SINGLE_LINE |
|
||||
ScanFlag::IGNORE_STAGGER_OFFSET |
|
||||
ScanFlag::IGNORE_COLOR_OFFSET;
|
||||
compute_session(dev, session, sensor);
|
||||
|
||||
try {
|
||||
init_regs_for_scan_session(dev, sensor, ®s, session);
|
||||
} catch (...) {
|
||||
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
|
||||
throw;
|
||||
}
|
||||
|
||||
sanei_genesys_set_motor_power(regs, false);
|
||||
|
||||
dev->interface->write_registers(regs);
|
||||
|
||||
set_fe(dev, sensor, AFE_SET);
|
||||
begin_scan(dev, sensor, ®s, true);
|
||||
|
||||
if (is_testing_mode()) {
|
||||
dev->interface->test_checkpoint("coarse_gain_calibration");
|
||||
scanner_stop_action(*dev);
|
||||
move_back_home(dev, true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
|
||||
|
||||
if (DBG_LEVEL >= DBG_data) {
|
||||
sanei_genesys_write_pnm_file("gl847_gain.pnm", image);
|
||||
}
|
||||
|
||||
/* average value on each channel */
|
||||
for (unsigned ch = 0; ch < channels; ch++) {
|
||||
|
||||
auto width = image.get_width();
|
||||
|
||||
std::uint64_t total = 0;
|
||||
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
|
||||
total += image.get_raw_channel(x, 0, ch);
|
||||
}
|
||||
|
||||
total /= width / 2;
|
||||
|
||||
gain[ch] = (static_cast<float>(sensor.gain_white_ref) * coeff) / total;
|
||||
|
||||
/* turn logical gain value into gain code, checking for overflow */
|
||||
code = static_cast<int>(283 - 208 / gain[ch]);
|
||||
code = clamp(code, 0, 255);
|
||||
dev->frontend.set_gain(ch, code);
|
||||
|
||||
DBG(DBG_proc, "%s: channel %d, total=%d, gain = %f, setting:%d\n", __func__, ch,
|
||||
static_cast<unsigned>(total), gain[ch], dev->frontend.get_gain(ch));
|
||||
}
|
||||
|
||||
if (dev->model->is_cis) {
|
||||
uint8_t gain0 = dev->frontend.get_gain(0);
|
||||
if (gain0 > dev->frontend.get_gain(1)) {
|
||||
gain0 = dev->frontend.get_gain(1);
|
||||
}
|
||||
if (gain0 > dev->frontend.get_gain(2)) {
|
||||
gain0 = dev->frontend.get_gain(2);
|
||||
}
|
||||
dev->frontend.set_gain(0, gain0);
|
||||
dev->frontend.set_gain(1, gain0);
|
||||
dev->frontend.set_gain(2, gain0);
|
||||
}
|
||||
|
||||
if (channels == 1) {
|
||||
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
|
||||
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
|
||||
}
|
||||
|
||||
scanner_stop_action(*dev);
|
||||
|
||||
move_back_home(dev, true);
|
||||
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
|
||||
}
|
||||
|
||||
bool CommandSetGl847::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
|
||||
|
|
|
@ -70,49 +70,6 @@ static Gpio_Profile gpios[]={
|
|||
{ GpioId::UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t dramsel;
|
||||
uint8_t rd0;
|
||||
uint8_t rd1;
|
||||
uint8_t rd2;
|
||||
uint8_t re0;
|
||||
uint8_t re1;
|
||||
uint8_t re2;
|
||||
uint8_t re3;
|
||||
uint8_t re4;
|
||||
uint8_t re5;
|
||||
uint8_t re6;
|
||||
uint8_t re7;
|
||||
} Memory_layout;
|
||||
|
||||
static Memory_layout layouts[]={
|
||||
/* LIDE 100 */
|
||||
{
|
||||
0x29,
|
||||
0x0a, 0x15, 0x20,
|
||||
0x00, 0xac, 0x02, 0x55, 0x02, 0x56, 0x03, 0xff
|
||||
},
|
||||
/* LIDE 200 */
|
||||
{
|
||||
0x29,
|
||||
0x0a, 0x1f, 0x34,
|
||||
0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
|
||||
},
|
||||
/* 5600F */
|
||||
{
|
||||
0x29,
|
||||
0x0a, 0x1f, 0x34,
|
||||
0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
|
||||
},
|
||||
/* LIDE 700F */
|
||||
{
|
||||
0x2a,
|
||||
0x0a, 0x33, 0x5c,
|
||||
0x02, 0x14, 0x09, 0x09, 0x09, 0x0a, 0x0f, 0xff
|
||||
}
|
||||
};
|
||||
|
||||
class CommandSetGl847 : public CommandSetCommon
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -190,6 +190,7 @@ static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
|
|||
static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
|
||||
static constexpr RegMask REG_0x1DS_TGSHLD = 0;
|
||||
|
||||
static constexpr RegAddr REG_0x1E = 0x1e;
|
||||
static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
|
||||
static constexpr RegMask REG_0x1ES_WDTIME = 4;
|
||||
static constexpr RegMask REG_0x1E_LINESEL = 0x0f;
|
||||
|
|
|
@ -1335,6 +1335,32 @@ std::uint8_t compute_frontend_gain_wolfson(float value, float target_value)
|
|||
return clamp(code, 0, 255);
|
||||
}
|
||||
|
||||
std::uint8_t compute_frontend_gain_lide_80(float value, float target_value)
|
||||
{
|
||||
int code = static_cast<int>((target_value / value) * 12);
|
||||
return clamp(code, 0, 255);
|
||||
}
|
||||
|
||||
std::uint8_t compute_frontend_gain_wolfson_gl841(float value, float target_value)
|
||||
{
|
||||
// this code path is similar to what generic wolfson code path uses and uses similar constants,
|
||||
// but is likely incorrect.
|
||||
float inv_gain = target_value / value;
|
||||
inv_gain *= 0.69f;
|
||||
int code = static_cast<int>(283 - 208 / inv_gain);
|
||||
return clamp(code, 0, 255);
|
||||
}
|
||||
|
||||
std::uint8_t compute_frontend_gain_wolfson_gl846_gl847_gl124(float value, float target_value)
|
||||
{
|
||||
// this code path is similar to what generic wolfson code path uses and uses similar constants,
|
||||
// but is likely incorrect.
|
||||
float inv_gain = target_value / value;
|
||||
int code = static_cast<int>(283 - 208 / inv_gain);
|
||||
return clamp(code, 0, 255);
|
||||
}
|
||||
|
||||
|
||||
std::uint8_t compute_frontend_gain_analog_devices(float value, float target_value)
|
||||
{
|
||||
/* The flow of data through the frontend ADC is as follows (see e.g. AD9826 datasheet)
|
||||
|
@ -1359,13 +1385,22 @@ std::uint8_t compute_frontend_gain_analog_devices(float value, float target_valu
|
|||
std::uint8_t compute_frontend_gain(float value, float target_value,
|
||||
FrontendType frontend_type)
|
||||
{
|
||||
if (frontend_type == FrontendType::WOLFSON) {
|
||||
return compute_frontend_gain_wolfson(value, target_value);
|
||||
switch (frontend_type) {
|
||||
case FrontendType::WOLFSON:
|
||||
return compute_frontend_gain_wolfson(value, target_value);
|
||||
case FrontendType::ANALOG_DEVICES:
|
||||
return compute_frontend_gain_analog_devices(value, target_value);
|
||||
case FrontendType::CANON_LIDE_80:
|
||||
return compute_frontend_gain_lide_80(value, target_value);
|
||||
case FrontendType::WOLFSON_GL841:
|
||||
return compute_frontend_gain_wolfson_gl841(value, target_value);
|
||||
case FrontendType::WOLFSON_GL846:
|
||||
case FrontendType::WOLFSON_GL847:
|
||||
case FrontendType::WOLFSON_GL124:
|
||||
return compute_frontend_gain_wolfson_gl846_gl847_gl124(value, target_value);
|
||||
default:
|
||||
throw SaneException("Unknown frontend to compute gain for");
|
||||
}
|
||||
if (frontend_type == FrontendType::ANALOG_DEVICES) {
|
||||
return compute_frontend_gain_analog_devices(value, target_value);
|
||||
}
|
||||
throw SaneException("Unknown frontend to compute gain for");
|
||||
}
|
||||
|
||||
/** @brief initialize device
|
||||
|
|
|
@ -303,6 +303,17 @@ void scanner_move_back_home_ta(Genesys_Device& dev);
|
|||
*/
|
||||
void scanner_search_strip(Genesys_Device& dev, bool forward, bool black);
|
||||
|
||||
bool should_calibrate_only_active_area(const Genesys_Device& dev,
|
||||
const Genesys_Settings& settings);
|
||||
|
||||
float get_model_x_offset_ta(const Genesys_Device& dev, const Genesys_Settings& settings);
|
||||
|
||||
void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs);
|
||||
|
||||
void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
|
||||
Genesys_Register_Set& regs, unsigned dpi);
|
||||
|
||||
void scanner_clear_scan_and_feed_counts(Genesys_Device& dev);
|
||||
|
||||
extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data,
|
||||
|
@ -455,12 +466,14 @@ inline T clamp(const T& value, const T& lo, const T& hi)
|
|||
extern StaticInit<std::vector<Genesys_Sensor>> s_sensors;
|
||||
extern StaticInit<std::vector<Genesys_Frontend>> s_frontends;
|
||||
extern StaticInit<std::vector<Genesys_Gpo>> s_gpo;
|
||||
extern StaticInit<std::vector<MemoryLayout>> s_memory_layout;
|
||||
extern StaticInit<std::vector<Genesys_Motor>> s_motors;
|
||||
extern StaticInit<std::vector<Genesys_USB_Device_Entry>> s_usb_devices;
|
||||
|
||||
void genesys_init_sensor_tables();
|
||||
void genesys_init_frontend_tables();
|
||||
void genesys_init_gpo_tables();
|
||||
void genesys_init_memory_layout_tables();
|
||||
void genesys_init_motor_tables();
|
||||
void genesys_init_usb_device_tables();
|
||||
void verify_usb_device_tables();
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include <vector>
|
||||
#include "enums.h"
|
||||
#include "sensor.h"
|
||||
#include "value_filter.h"
|
||||
|
||||
namespace genesys {
|
||||
|
||||
|
@ -151,9 +152,9 @@ struct MotorProfile
|
|||
int motor_vref = -1;
|
||||
|
||||
// the resolutions this profile is good for
|
||||
ResolutionFilter resolutions = ResolutionFilter::ANY;
|
||||
ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY;
|
||||
// the scan method this profile is good for. If the list is empty, good for any method.
|
||||
ScanMethodFilter scan_methods = ScanMethodFilter::ANY;
|
||||
ValueFilterAny<ScanMethod> scan_methods = VALUE_FILTER_ANY;
|
||||
|
||||
unsigned max_exposure = 0; // 0 - any exposure
|
||||
};
|
||||
|
|
|
@ -64,6 +64,11 @@ std::ostream& operator<<(std::ostream& out, const FrontendType& type)
|
|||
case FrontendType::UNKNOWN: out << "UNKNOWN"; break;
|
||||
case FrontendType::WOLFSON: out << "WOLFSON"; break;
|
||||
case FrontendType::ANALOG_DEVICES: out << "ANALOG_DEVICES"; break;
|
||||
case FrontendType::CANON_LIDE_80: out << "CANON_LIDE_80"; break;
|
||||
case FrontendType::WOLFSON_GL841: out << "WOLFSON_GL841"; break;
|
||||
case FrontendType::WOLFSON_GL846: out << "WOLFSON_GL846"; break;
|
||||
case FrontendType::WOLFSON_GL847: out << "WOLFSON_GL847"; break;
|
||||
case FrontendType::WOLFSON_GL124: out << "WOLFSON_GL124"; break;
|
||||
default: out << "(unknown value)";
|
||||
}
|
||||
return out;
|
||||
|
@ -112,26 +117,6 @@ std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure)
|
|||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const ResolutionFilter& resolutions)
|
||||
{
|
||||
if (resolutions.matches_any()) {
|
||||
out << "ANY";
|
||||
return out;
|
||||
}
|
||||
out << format_vector_unsigned(4, resolutions.resolutions());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const ScanMethodFilter& methods)
|
||||
{
|
||||
if (methods.matches_any()) {
|
||||
out << "ANY";
|
||||
return out;
|
||||
}
|
||||
out << format_vector_unsigned(4, methods.methods());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor)
|
||||
{
|
||||
out << "Genesys_Sensor{\n"
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "enums.h"
|
||||
#include "register.h"
|
||||
#include "serialize.h"
|
||||
#include "value_filter.h"
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
|
@ -114,9 +115,14 @@ std::ostream& operator<<(std::ostream& out, const StaggerConfig& config);
|
|||
|
||||
enum class FrontendType : unsigned
|
||||
{
|
||||
UNKNOWN,
|
||||
UNKNOWN = 0,
|
||||
WOLFSON,
|
||||
ANALOG_DEVICES
|
||||
ANALOG_DEVICES,
|
||||
CANON_LIDE_80,
|
||||
WOLFSON_GL841, // old code path, likely wrong calculation
|
||||
WOLFSON_GL846, // old code path, likely wrong calculation
|
||||
WOLFSON_GL847, // old code path, likely wrong calculation
|
||||
WOLFSON_GL124, // old code path, likely wrong calculation
|
||||
};
|
||||
|
||||
inline void serialize(std::istream& str, FrontendType& x)
|
||||
|
@ -242,94 +248,6 @@ struct SensorExposure {
|
|||
std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure);
|
||||
|
||||
|
||||
class ResolutionFilter
|
||||
{
|
||||
public:
|
||||
struct Any {};
|
||||
static constexpr Any ANY{};
|
||||
|
||||
ResolutionFilter() : matches_any_{false} {}
|
||||
ResolutionFilter(Any) : matches_any_{true} {}
|
||||
ResolutionFilter(std::initializer_list<unsigned> resolutions) :
|
||||
matches_any_{false},
|
||||
resolutions_{resolutions}
|
||||
{}
|
||||
|
||||
bool matches(unsigned resolution) const
|
||||
{
|
||||
if (matches_any_)
|
||||
return true;
|
||||
auto it = std::find(resolutions_.begin(), resolutions_.end(), resolution);
|
||||
return it != resolutions_.end();
|
||||
}
|
||||
|
||||
bool operator==(const ResolutionFilter& other) const
|
||||
{
|
||||
return matches_any_ == other.matches_any_ && resolutions_ == other.resolutions_;
|
||||
}
|
||||
|
||||
bool matches_any() const { return matches_any_; }
|
||||
const std::vector<unsigned>& resolutions() const { return resolutions_; }
|
||||
|
||||
private:
|
||||
bool matches_any_ = false;
|
||||
std::vector<unsigned> resolutions_;
|
||||
|
||||
template<class Stream>
|
||||
friend void serialize(Stream& str, ResolutionFilter& x);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const ResolutionFilter& resolutions);
|
||||
|
||||
template<class Stream>
|
||||
void serialize(Stream& str, ResolutionFilter& x)
|
||||
{
|
||||
serialize(str, x.matches_any_);
|
||||
serialize_newline(str);
|
||||
serialize(str, x.resolutions_);
|
||||
}
|
||||
|
||||
|
||||
class ScanMethodFilter
|
||||
{
|
||||
public:
|
||||
struct Any {};
|
||||
static constexpr Any ANY{};
|
||||
|
||||
ScanMethodFilter() : matches_any_{false} {}
|
||||
ScanMethodFilter(Any) : matches_any_{true} {}
|
||||
ScanMethodFilter(std::initializer_list<ScanMethod> methods) :
|
||||
matches_any_{false},
|
||||
methods_{methods}
|
||||
{}
|
||||
|
||||
bool matches(ScanMethod method) const
|
||||
{
|
||||
if (matches_any_)
|
||||
return true;
|
||||
auto it = std::find(methods_.begin(), methods_.end(), method);
|
||||
return it != methods_.end();
|
||||
}
|
||||
|
||||
bool operator==(const ScanMethodFilter& other) const
|
||||
{
|
||||
return matches_any_ == other.matches_any_ && methods_ == other.methods_;
|
||||
}
|
||||
|
||||
bool matches_any() const { return matches_any_; }
|
||||
const std::vector<ScanMethod>& methods() const { return methods_; }
|
||||
|
||||
private:
|
||||
bool matches_any_ = false;
|
||||
std::vector<ScanMethod> methods_;
|
||||
|
||||
template<class Stream>
|
||||
friend void serialize(Stream& str, ResolutionFilter& x);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const ScanMethodFilter& methods);
|
||||
|
||||
|
||||
struct Genesys_Sensor {
|
||||
|
||||
Genesys_Sensor() = default;
|
||||
|
@ -343,7 +261,7 @@ struct Genesys_Sensor {
|
|||
unsigned optical_res = 0;
|
||||
|
||||
// the resolution list that the sensor is usable at.
|
||||
ResolutionFilter resolutions = ResolutionFilter::ANY;
|
||||
ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY;
|
||||
|
||||
// the channel list that the sensor is usable at
|
||||
std::vector<unsigned> channels = { 1, 3 };
|
||||
|
|
|
@ -198,6 +198,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::CANON_LIDE_35;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL841;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x01, 0x3d },
|
||||
|
@ -242,6 +243,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::WOLFSON_XP300;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL841;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x01, 0x35 },
|
||||
|
@ -286,6 +288,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::WOLFSON_DSM600;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL841;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x01, 0x35 },
|
||||
|
@ -308,6 +311,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::CANON_LIDE_200;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL847;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x9d },
|
||||
{ 0x01, 0x91 },
|
||||
|
@ -330,6 +334,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::CANON_LIDE_700F;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL847;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x9d },
|
||||
{ 0x01, 0x9e },
|
||||
|
@ -396,6 +401,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::CANON_LIDE_110;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL124;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x80 },
|
||||
{ 0x01, 0x8a },
|
||||
|
@ -422,6 +428,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::CANON_LIDE_120;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL124;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x80 },
|
||||
{ 0x01, 0xa3 },
|
||||
|
@ -583,6 +590,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::IMG101;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL846;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x78 },
|
||||
{ 0x01, 0xf0 },
|
||||
|
@ -605,6 +613,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::PLUSTEK_OPTICBOOK_3800;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::WOLFSON_GL846;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x78 },
|
||||
{ 0x01, 0xf0 },
|
||||
|
@ -631,6 +640,7 @@ void genesys_init_frontend_tables()
|
|||
fe = Genesys_Frontend();
|
||||
fe.id = AdcId::CANON_LIDE_80;
|
||||
fe.layout = wolfson_layout;
|
||||
fe.layout.type = FrontendType::CANON_LIDE_80;
|
||||
fe.regs = {
|
||||
{ 0x00, 0x70 },
|
||||
{ 0x01, 0x16 },
|
||||
|
|
|
@ -382,10 +382,8 @@ void genesys_init_gpo_tables()
|
|||
gpo = Genesys_Gpo();
|
||||
gpo.id = GpioId::IMG101;
|
||||
gpo.regs = {
|
||||
{ 0x6c, 0x41 },
|
||||
{ 0x6d, 0xa4 },
|
||||
{ 0x6e, 0x13 },
|
||||
{ 0x6f, 0xa7 },
|
||||
{ 0x6b, 0x72 }, { 0x6c, 0x1f }, { 0x6d, 0xa4 }, { 0x6e, 0x13 }, { 0x6f, 0xa7 },
|
||||
{ 0xa6, 0x11 }, { 0xa7, 0xff }, { 0xa8, 0x19 }, { 0xa9, 0x05 },
|
||||
};
|
||||
s_gpo->push_back(gpo);
|
||||
|
||||
|
@ -393,10 +391,8 @@ void genesys_init_gpo_tables()
|
|||
gpo = Genesys_Gpo();
|
||||
gpo.id = GpioId::PLUSTEK_OPTICBOOK_3800;
|
||||
gpo.regs = {
|
||||
{ 0x6c, 0x41 },
|
||||
{ 0x6d, 0xa4 },
|
||||
{ 0x6e, 0x13 },
|
||||
{ 0x6f, 0xa7 },
|
||||
{ 0x6b, 0x30 }, { 0x6c, 0x01 }, { 0x6d, 0x80 }, { 0x6e, 0x2d }, { 0x6f, 0x80 },
|
||||
{ 0xa6, 0x0c }, { 0xa7, 0x8f }, { 0xa8, 0x08 }, { 0xa9, 0x04 },
|
||||
};
|
||||
s_gpo->push_back(gpo);
|
||||
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/* sane - Scanner Access Now Easy.
|
||||
|
||||
Copyright (C) 2020 Povilas Kanapickas <povilas@radix.lt>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#define DEBUG_DECLARE_ONLY
|
||||
|
||||
#include "low.h"
|
||||
|
||||
namespace genesys {
|
||||
|
||||
StaticInit<std::vector<MemoryLayout>> s_memory_layout;
|
||||
|
||||
void genesys_init_memory_layout_tables()
|
||||
{
|
||||
s_memory_layout.init();
|
||||
|
||||
MemoryLayout ml;
|
||||
ml.models = { ModelId::CANON_IMAGE_FORMULA_101 };
|
||||
// FIXME: this scanner does not set all required registers
|
||||
ml.regs = {
|
||||
{ 0xe0, 0x00 }, { 0xe1, 0xb0 }, { 0xe2, 0x05 }, { 0xe3, 0xe7 },
|
||||
{ 0xe4, 0x05 }, { 0xe5, 0xe8 }, { 0xe6, 0x0b }, { 0xe7, 0x1f },
|
||||
{ 0xe8, 0x0b }, { 0xe9, 0x20 },
|
||||
};
|
||||
s_memory_layout->push_back(ml);
|
||||
|
||||
|
||||
ml = MemoryLayout();
|
||||
ml.models = { ModelId::PLUSTEK_OPTICBOOK_3800 };
|
||||
// FIXME: this scanner does not set all required registers
|
||||
ml.regs = {
|
||||
{ 0xe0, 0x00 }, { 0xe1, 0x68 }, { 0xe2, 0x03 }, { 0xe3, 0x00 },
|
||||
{ 0xe4, 0x03 }, { 0xe5, 0x01 }, { 0xe6, 0x05 }, { 0xe7, 0x99 },
|
||||
{ 0xe8, 0x05 }, { 0xe9, 0x9a },
|
||||
};
|
||||
s_memory_layout->push_back(ml);
|
||||
|
||||
|
||||
/* On GL847 and GL124, the values of the base address for shading data must be multiplied by
|
||||
8192=0x4000 to give address on AHB
|
||||
|
||||
On GL847 and GL124, the values of the base address for scanned data must be multiplied by
|
||||
1024*2=0x0800 to give address on AHB
|
||||
*/
|
||||
ml = MemoryLayout();
|
||||
ml.models = { ModelId::CANON_LIDE_100 };
|
||||
ml.regs = {
|
||||
{ 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 },
|
||||
{ 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x02 }, { 0xe3, 0x55 },
|
||||
{ 0xe4, 0x02 }, { 0xe5, 0x56 }, { 0xe6, 0x03 }, { 0xe7, 0xff },
|
||||
{ 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x02 }, { 0xeb, 0x55 },
|
||||
{ 0xec, 0x02 }, { 0xed, 0x56 }, { 0xee, 0x03 }, { 0xef, 0xff },
|
||||
{ 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x02 }, { 0xf3, 0x55 },
|
||||
{ 0xf4, 0x02 }, { 0xf5, 0x56 }, { 0xf6, 0x03 }, { 0xf7, 0xff },
|
||||
};
|
||||
s_memory_layout->push_back(ml);
|
||||
|
||||
|
||||
ml = MemoryLayout();
|
||||
// BUG: we shouldn't use LIDE_200 data for 5600F
|
||||
ml.models = { ModelId::CANON_LIDE_200, ModelId::CANON_5600F };
|
||||
ml.regs = {
|
||||
{ 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 },
|
||||
{ 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x02 }, { 0xe3, 0x91 },
|
||||
{ 0xe4, 0x02 }, { 0xe5, 0x92 }, { 0xe6, 0x03 }, { 0xe7, 0xff },
|
||||
{ 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x02 }, { 0xeb, 0x91 },
|
||||
{ 0xec, 0x02 }, { 0xed, 0x92 }, { 0xee, 0x03 }, { 0xef, 0xff },
|
||||
{ 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x02 }, { 0xf3, 0x91 },
|
||||
{ 0xf4, 0x02 }, { 0xf5, 0x92 }, { 0xf6, 0x03 }, { 0xf7, 0xff },
|
||||
};
|
||||
s_memory_layout->push_back(ml);
|
||||
|
||||
|
||||
ml = MemoryLayout();
|
||||
ml.models = { ModelId::CANON_LIDE_700F };
|
||||
ml.regs = {
|
||||
{ 0xd0, 0x0a }, { 0xd1, 0x33 }, { 0xd2, 0x5c },
|
||||
{ 0xe0, 0x02 }, { 0xe1, 0x14 }, { 0xe2, 0x09 }, { 0xe3, 0x09 },
|
||||
{ 0xe4, 0x09 }, { 0xe5, 0x0a }, { 0xe6, 0x0f }, { 0xe7, 0xff },
|
||||
{ 0xe8, 0x02 }, { 0xe9, 0x14 }, { 0xea, 0x09 }, { 0xeb, 0x09 },
|
||||
{ 0xec, 0x09 }, { 0xed, 0x0a }, { 0xee, 0x0f }, { 0xef, 0xff },
|
||||
{ 0xf0, 0x02 }, { 0xf1, 0x14 }, { 0xf2, 0x09 }, { 0xf3, 0x09 },
|
||||
{ 0xf4, 0x09 }, { 0xf5, 0x0a }, { 0xf6, 0x0f }, { 0xf7, 0xff },
|
||||
};
|
||||
s_memory_layout->push_back(ml);
|
||||
|
||||
|
||||
ml = MemoryLayout();
|
||||
ml.models = { ModelId::CANON_LIDE_110, ModelId::CANON_LIDE_120 };
|
||||
ml.regs = {
|
||||
{ 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 },
|
||||
{ 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x08 }, { 0xe3, 0x55 },
|
||||
{ 0xe4, 0x08 }, { 0xe5, 0x56 }, { 0xe6, 0x0f }, { 0xe7, 0xff },
|
||||
{ 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x08 }, { 0xeb, 0x55 },
|
||||
{ 0xec, 0x08 }, { 0xed, 0x56 }, { 0xee, 0x0f }, { 0xef, 0xff },
|
||||
{ 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x08 }, { 0xf3, 0x55 },
|
||||
{ 0xf4, 0x08 }, { 0xf5, 0x56 }, { 0xf6, 0x0f }, { 0xf7, 0xff },
|
||||
|
||||
};
|
||||
s_memory_layout->push_back(ml);
|
||||
|
||||
|
||||
ml = MemoryLayout();
|
||||
ml.models = { ModelId::CANON_LIDE_210, ModelId::CANON_LIDE_220 };
|
||||
ml.regs = {
|
||||
{ 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 },
|
||||
{ 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x08 }, { 0xe3, 0x91 },
|
||||
{ 0xe4, 0x08 }, { 0xe5, 0x92 }, { 0xe6, 0x0f }, { 0xe7, 0xff },
|
||||
{ 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x08 }, { 0xeb, 0x91 },
|
||||
{ 0xec, 0x08 }, { 0xed, 0x92 }, { 0xee, 0x0f }, { 0xef, 0xff },
|
||||
{ 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x08 }, { 0xf3, 0x91 },
|
||||
{ 0xf4, 0x08 }, { 0xf5, 0x92 }, { 0xf6, 0x0f }, { 0xf7, 0xff },
|
||||
};
|
||||
s_memory_layout->push_back(ml);
|
||||
}
|
||||
|
||||
} // namespace genesys
|
|
@ -270,7 +270,7 @@ void genesys_init_motor_tables()
|
|||
profile.slope = MotorSlope::create_from_steps(20202 * 4, 333 * 4, 100);
|
||||
profile.step_type = StepType::QUARTER;
|
||||
profile.motor_vref = 0;
|
||||
profile.resolutions = ResolutionFilter::ANY;
|
||||
profile.resolutions = VALUE_FILTER_ANY;
|
||||
profile.scan_methods = { ScanMethod::FLATBED };
|
||||
motor.profiles.push_back(std::move(profile));
|
||||
|
||||
|
@ -278,7 +278,7 @@ void genesys_init_motor_tables()
|
|||
profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 100);
|
||||
profile.step_type = StepType::QUARTER;
|
||||
profile.motor_vref = 2;
|
||||
profile.resolutions = ResolutionFilter::ANY;
|
||||
profile.resolutions = VALUE_FILTER_ANY;
|
||||
profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED };
|
||||
motor.profiles.push_back(std::move(profile));
|
||||
|
||||
|
@ -286,8 +286,8 @@ void genesys_init_motor_tables()
|
|||
profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 200);
|
||||
profile.step_type = StepType::QUARTER;
|
||||
profile.motor_vref = 2;
|
||||
profile.resolutions = ResolutionFilter::ANY;
|
||||
profile.scan_methods = ScanMethodFilter::ANY;
|
||||
profile.resolutions = VALUE_FILTER_ANY;
|
||||
profile.scan_methods = VALUE_FILTER_ANY;
|
||||
motor.fast_profiles.push_back(std::move(profile));
|
||||
|
||||
s_motors->push_back(std::move(motor));
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,140 @@
|
|||
/* sane - Scanner Access Now Easy.
|
||||
|
||||
Copyright (C) 2020 Povilas Kanapickas <povilas@radix.lt>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef BACKEND_GENESYS_VALUE_FILTER_H
|
||||
#define BACKEND_GENESYS_VALUE_FILTER_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace genesys {
|
||||
|
||||
struct AnyTag {};
|
||||
constexpr AnyTag VALUE_FILTER_ANY{};
|
||||
|
||||
template<class T>
|
||||
class ValueFilterAny
|
||||
{
|
||||
public:
|
||||
ValueFilterAny() : matches_any_{false} {}
|
||||
ValueFilterAny(AnyTag) : matches_any_{true} {}
|
||||
ValueFilterAny(std::initializer_list<T> values) :
|
||||
matches_any_{false},
|
||||
values_{values}
|
||||
{}
|
||||
|
||||
bool matches(T value) const
|
||||
{
|
||||
if (matches_any_)
|
||||
return true;
|
||||
auto it = std::find(values_.begin(), values_.end(), value);
|
||||
return it != values_.end();
|
||||
}
|
||||
|
||||
bool operator==(const ValueFilterAny& other) const
|
||||
{
|
||||
return matches_any_ == other.matches_any_ && values_ == other.values_;
|
||||
}
|
||||
|
||||
bool matches_any() const { return matches_any_; }
|
||||
const std::vector<T>& values() const { return values_; }
|
||||
|
||||
private:
|
||||
bool matches_any_ = false;
|
||||
std::vector<T> values_;
|
||||
|
||||
template<class Stream, class U>
|
||||
friend void serialize(Stream& str, ValueFilterAny<U>& x);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
std::ostream& operator<<(std::ostream& out, const ValueFilterAny<T>& values)
|
||||
{
|
||||
if (values.matches_any()) {
|
||||
out << "ANY";
|
||||
return out;
|
||||
}
|
||||
out << format_vector_indent_braced(4, "", values.values());
|
||||
return out;
|
||||
}
|
||||
|
||||
template<class Stream, class T>
|
||||
void serialize(Stream& str, ValueFilterAny<T>& x)
|
||||
{
|
||||
serialize(str, x.matches_any_);
|
||||
serialize_newline(str);
|
||||
serialize(str, x.values_);
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
class ValueFilter
|
||||
{
|
||||
public:
|
||||
ValueFilter() = default;
|
||||
ValueFilter(std::initializer_list<T> values) :
|
||||
values_{values}
|
||||
{}
|
||||
|
||||
bool matches(T value) const
|
||||
{
|
||||
auto it = std::find(values_.begin(), values_.end(), value);
|
||||
return it != values_.end();
|
||||
}
|
||||
|
||||
bool operator==(const ValueFilter& other) const
|
||||
{
|
||||
return values_ == other.values_;
|
||||
}
|
||||
|
||||
const std::vector<T>& values() const { return values_; }
|
||||
|
||||
private:
|
||||
std::vector<T> values_;
|
||||
|
||||
template<class Stream, class U>
|
||||
friend void serialize(Stream& str, ValueFilter<U>& x);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
std::ostream& operator<<(std::ostream& out, const ValueFilter<T>& values)
|
||||
{
|
||||
if (values.values().empty()) {
|
||||
out << "(none)";
|
||||
return out;
|
||||
}
|
||||
out << format_vector_indent_braced(4, "", values.values());
|
||||
return out;
|
||||
}
|
||||
|
||||
template<class Stream, class T>
|
||||
void serialize(Stream& str, ValueFilter<T>& x)
|
||||
{
|
||||
serialize_newline(str);
|
||||
serialize(str, x.values_);
|
||||
}
|
||||
|
||||
} // namespace genesys
|
||||
|
||||
#endif // BACKEND_GENESYS_VALUE_FILTER_H
|
Ładowanie…
Reference in New Issue