diff --git a/CLAUDE.md b/CLAUDE.md index 32009f9..a12dfb6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -120,12 +120,13 @@ CMake knobs worth knowing: the project has a Longse camera. Note it uses a local `CHECK` macro rather than `assert()`: the release flags carry `-DNDEBUG`, so an `assert()`-based test compiles away to nothing and passes unconditionally. -- `./build/anyka_test`: the Anyka HAL's three parsers -- the `/proc/cpuinfo` - machine string, the chip-ID table and the media-memory arithmetic -- fed - fixture files through the path arguments `src/hal/anyka.h` exposes for the - purpose. Same situation as `longse_test` and the same `CHECK` macro: nobody - on the project has an Anyka camera, so this is all the verification that - code gets until a reporter runs it. +- `./build/anyka_test`: the Anyka HAL's parsers -- the `/proc/cpuinfo` + machine string, the device tree's `/memory` cells, the chip-ID table, the + model naming, the CIS clock divider and + the media-memory arithmetic -- fed fixture files through the path arguments + `src/hal/anyka.h` exposes for the purpose, with inputs taken from real boot + logs. Same `CHECK` macro as `longse_test`. The detection itself, including + the sensor power-up, was run on an AK3918EV200 with OpenIPC's kernel. - `./build/ambarella_test`: the `/proc/cpuinfo` machine string the Ambarella HAL parses, same `CHECK` macro and same reason. - `tools/test_pipeline.sh`: hardware-free end-to-end check of the sensor @@ -204,16 +205,36 @@ per-generation tables in each subcommand that should support it. Adding a new vendor also needs an entry in `IPCHW_OPTIONAL_VENDORS` in `CMakeLists.txt`, an include in `hal/common.h`, and a guarded row in `manufacturers[]`. -Anyka (`src/hal/anyka.c`) is the smallest worked example of that whole shape, -and the one that shows what to do when the SoC hands you no clean key: its -kernels have no device tree and register no UART with `/proc/iomem`, so the -`/proc/cpuinfo` machine string is what names the part +Anyka (`src/hal/anyka.c`) shows what to do when the SoC hands you no clean +key. The vendor 3.4 kernels have no device tree and register no UART with +`/proc/iomem`, so the `/proc/cpuinfo` machine string is what names the part (`CLOUD39EV3_AK3918EV300_MNBD`), while the chip-ID word at `0x08000000` names only the generation -- one vendor kernel prints the same `0x20160100` as AK3916, AK3918 or AK3919 depending on which `CONFIG_CPU_AK39xx` it was built -with. Reading the register is therefore a refinement and never the gate, which -is also what lets detection survive a kernel that will not hand over -`/dev/mem`. +with, and `0x20120100` is both the Aimer39 AK3918's and some EV200s'. Board +strings often carry only the package (`AK3918E80PIN` is an EV200 on one board +and an EV300 on another), so the generation completes the name; a device +tree's root compatible (OpenIPC's and the EV330 vendor kernels) names the part +outright and wins. The AV130 keeps its ID at `0x080003fc` instead. Reading the +registers is a refinement and never the gate, which is also what lets +detection survive a kernel that will not hand over `/dev/mem`. + +Anyka sensors answer I2C only while the camera driver has them clocked and +out of reset, so on EV200/EV300 `hal_enable_sensor_clock` starts the CIS +clock (SYSCTRL `0x18`, from the peripheral PLL in `0x14`) and pulses the +reset/power-down GPIOs the device tree names for the camera node, and +`hal_cleanup` puts back exactly what it changed. It touches nothing when the +clock is already running, which is a pipeline streaming. + +`ipcinfo` is in every camera image and the size check holds it to 1 KiB of +growth per PR, so it carries the least that works, behind +`#ifndef STANDALONE_LIBRARY` for the rest. It starts the CIS clock and takes +the part from the device tree; the sensor drivers' power-off leaves reset an +input, so that is enough where the camera node has no `pwdn-gpio` (those +drivers do assert power-down, on a board that has the pin). Only `ipctool` +pulses the GPIOs, works a part number out of a vendor kernel's board string, +and knows the PrimeSensor and BYD sensors, which have no tuning in the firmware +package. Ambarella (`src/hal/ambarella.c`) stops at the machine string on purpose. The exact part (S3L33M, S3L55M, S3L66, ...) is a word the DSP firmware writes, diff --git a/src/chipid.c b/src/chipid.c index e9ce82b..8718496 100644 --- a/src/chipid.c +++ b/src/chipid.c @@ -338,6 +338,12 @@ const char *getchipfamily() { return "rv1106"; #endif #ifdef IPCHW_VENDOR_ANYKA +#ifndef STANDALONE_LIBRARY + case AK39_EV1: + return "ak3918"; + case AK39_AV130: + return "ak3918av130"; +#endif case AK39_EV2: return "ak3918ev200"; case AK39_EV3: diff --git a/src/hal/anyka.c b/src/hal/anyka.c index 8a29559..a28b18a 100644 --- a/src/hal/anyka.c +++ b/src/hal/anyka.c @@ -1,35 +1,58 @@ #include "hal/anyka.h" +#include +#include +#include #include +#include #include #include +#include #include "chipid.h" #include "hal/common.h" #include "tools.h" -/* Addresses as the vendor's own sensor drivers in - * drivers/media/video/plat-anyka/ spell them, which is the 8-bit write form - * ipctool's tables use. SOI parts sit at either end: the JX-F2x/H63 drivers - * use 0x80 and the H65 one 0x60. */ +/* Addresses as the vendor's own sensor drivers spell them -- the 3.4 SDKs' + * drivers/media/video/plat-anyka/ and the sensor modules on AK3918EV200, + * EV300 and EV330 firmware -- which is the 8-bit write form ipctool's tables + * use. SOI parts sit at either end: the JX-F2x/H63 drivers use 0x80, the + * H42/H62/H65 ones 0x60. The OV9712 shares 0x60 with SmartSens, the GC1024 + * sits at 0x78 and the EV330's GC4653 at 0x52. */ static unsigned char sony_addrs[] = {0x34, 0}; static unsigned char soi_addrs[] = {0x80, 0x60, 0}; -static unsigned char ssens_addrs[] = {0x60, 0}; +static unsigned char ssens_addrs[] = {0x60, 0x64, 0}; static unsigned char onsemi_addrs[] = {0x20, 0}; -static unsigned char omni_addrs[] = {0x6c, 0}; -static unsigned char gc_addrs[] = {0x42, 0x6e, 0}; +static unsigned char omni_addrs[] = {0x6c, 0x60, 0}; +static unsigned char gc_addrs[] = {0x42, 0x6e, 0x78, 0x52, 0}; static unsigned char superpix_addrs[] = {0x7a, 0x78, 0}; +static unsigned char imagedesign_addrs[] = {0x60, 0}; +#ifndef STANDALONE_LIBRARY +static unsigned char primesensor_addrs[] = {0x90, 0}; +static unsigned char byd_addrs[] = {0xdc, 0}; +#endif static sensor_addr_t anyka_possible_i2c_addrs[] = { - {SENSOR_SONY, sony_addrs}, {SENSOR_SOI, soi_addrs}, - {SENSOR_SMARTSENS, ssens_addrs}, {SENSOR_ONSEMI, onsemi_addrs}, - {SENSOR_OMNIVISION, omni_addrs}, {SENSOR_GALAXYCORE, gc_addrs}, - {SENSOR_SUPERPIX, superpix_addrs}, {0, NULL}}; + {SENSOR_SONY, sony_addrs}, + {SENSOR_SOI, soi_addrs}, + {SENSOR_SMARTSENS, ssens_addrs}, + {SENSOR_ONSEMI, onsemi_addrs}, + {SENSOR_OMNIVISION, omni_addrs}, + {SENSOR_GALAXYCORE, gc_addrs}, + {SENSOR_SUPERPIX, superpix_addrs}, + {SENSOR_IMAGEDESIGN, imagedesign_addrs}, +#ifndef STANDALONE_LIBRARY + {SENSOR_PRIMESENSOR, primesensor_addrs}, + {SENSOR_BYD, byd_addrs}, +#endif + {0, NULL}}; /* Raw chip ID as it was read, kept so the report can show the number the * kernel's own "ANYKA CPU ... (ID 0x...)" banner prints. Zero when /dev/mem * would not give it up. */ +#ifndef STANDALONE_LIBRARY static uint32_t anyka_chip_id; +#endif static const struct { uint32_t id; @@ -39,6 +62,8 @@ static const struct { uint32_t shift; int generation; } anyka_socs[] = { + /* The Aimer39 AK3918 and some AK3918EV200 boards share this one; the + * board string or the device tree tells them apart. */ {0x20120100, 0, AK39_EV2}, {0x20150200, 0, AK39_EV2}, {0x20160100, 0, AK39_EV3}, @@ -47,6 +72,8 @@ static const struct { {0x00201902, 8, AK37_E}, {0x00535335, 8, AK39_AV100}, {0x00535434, 8, AK39_EV300L}, + /* AK3918AV130, revisions A and B, read at AK_REG_CHIP_ID_AV. */ + {0x00565735, 8, AK39_AV130}, }; int anyka_generation(uint32_t chip_id) { @@ -61,9 +88,11 @@ int anyka_generation(uint32_t chip_id) { /* These kernels predate device tree on the camera parts -- the 3.4.35 in * issue #134 has no /proc/device-tree at all -- and the SoC registers no UART - * with /proc/iomem, so the machine string is the only signal that does not - * need /dev/mem. Every Anyka camera board spells the part into it: - * "CLOUD39EV3_AK3918EV300_MNBD", "Cloud39EV2_AK3918E80PIN_MNBD", + * with /proc/iomem, so the machine string is the one signal that every + * kernel gives without /dev/mem. Every Anyka camera board spells a part + * number into it: "CLOUD39EV3_AK3918EV300_MNBD", + * "Cloud39EV2_AK3918E80PIN_MNBD", "AK3918EV200_GWELL_V1", + * "Cloud39E_AK3918E+H42_V1.0.2", * "Aimer39_AK3918_MB_V1.0.0", "AK39EV330". */ bool anyka_machine_name(const char *cpuinfo, char *out, size_t outlen) { if (!line_from_file(cpuinfo, "Hardware.*:.*([Aa][Kk]3[0-9][0-9A-Za-z]*)", @@ -76,60 +105,179 @@ bool anyka_machine_name(const char *cpuinfo, char *out, size_t outlen) { return true; } -bool anyka_detect_cpu(char *chip_name) { +#ifndef STANDALONE_LIBRARY +/* Is this match of the board or device tree patterns below "AK" + four + * digits, e.g. AK3918, and not the generation-wide AK39EV330? */ +static bool is_part_base(const char *s) { + return s[0] && isdigit(s[4]) && isdigit(s[5]); +} + +static bool is_bare_part(const char *s) { + return is_part_base(s) && !s[6]; +} + +/* ...followed by EVnnn or AVnnn: the whole part number, AK3918EV200. A + * package, "AK3918E80PIN", has no V there. */ +static bool is_full_part(const char *s) { + return is_part_base(s) && s[6] && (s[7] | 32) == 'v'; +} + +/* The model to report. `compat` is what follows "anyka," in the device tree's + * root compatible, whose first entry is the most specific: the exact part + * where the tree has one. Otherwise the board string names it when it carries + * a whole part number. Where it carries only the package -- "AK3918E80PIN" is + * an EV200 on one board and an EV300 on another -- the chip ID's generation + * completes it, and its constant spells the suffix: 0x3918E200 is EV200. + * Aimer39 has no suffix and the EV300L's constant spells none. EV330 boards + * say only "AK39EV330", which is no part: that one is named as the vendor + * kernel's CPU banner names it. */ +void anyka_model_name(const char *machine, const char *compat, int generation, + char *out, size_t outlen) { + const char *part = is_full_part(compat) ? compat : machine; + + if (generation == AK39_EV330 && !is_full_part(part)) + part = "AK39XXEV330"; + if (generation && !(generation & 0xf) && is_part_base(part) && + !is_full_part(part)) + snprintf(out, outlen, "AK%.4s%XV%03X", part + 2, generation >> 12 & 0xf, + generation & 0xfff); + else + snprintf(out, outlen, "%s", part); + for (char *p = out; *p; p++) + *p = toupper((unsigned char)*p); +} + +/* Up to len bytes of a file, or 0. */ +static int read_file(const char *path, void *buf, size_t len) { + int fd = open(path, O_RDONLY), n; + + if (fd < 0) + return 0; + n = read(fd, buf, len); + close(fd); + return n; +} + +/* Everything a vendor kernel's board string leaves to be worked out. */ +static bool anyka_full_model(char *chip_name, uint32_t id) { /* As wide as chip_name, since that is where it lands. */ - char machine[128]; - uint32_t reg; + char machine[128], compat[32]; + + machine[0] = compat[0] = '\0'; + anyka_machine_name("/proc/cpuinfo", machine, sizeof(machine)); + line_from_file("/proc/device-tree/compatible", "^anyka,(ak.*)", compat, + sizeof(compat)); + compat[sizeof(compat) - 1] = '\0'; + + /* 0x20120100 is also the Aimer39 AK3918's, a generation of its own. Its + * boards and device trees name a bare part, "Aimer39_AK3918_MB_V1.0.0" + * and "anyka,ak3918", where an EV200's carry more. */ + if (id == 0x20120100 && (is_bare_part(machine) || is_bare_part(compat))) + chip_generation = AK39_EV1; - if (!anyka_machine_name("/proc/cpuinfo", machine, sizeof(machine))) + anyka_model_name(machine, compat, chip_generation, chip_name, 128); + return *chip_name != '\0'; +} +#endif + +bool anyka_detect_cpu(char *chip_name) { + uint32_t reg = 0; + + /* OpenIPC's kernels name the exact part in the device tree, so a board + * string is then not needed to claim the SoC. */ + if (!line_from_file("/proc/device-tree/compatible", + "^anyka,(ak[0-9]{4}[ae]v.*)", chip_name, 128) && + !anyka_machine_name("/proc/cpuinfo", chip_name, 128)) return false; - strcpy(chip_name, machine); /* Only ever a refinement: the board already named the part, and a kernel * built with strict devmem filtering can refuse the window outright. */ - if (mem_reg(AK_REG_CHIP_ID, ®, OP_READ)) { - anyka_chip_id = reg; + if (mem_reg(AK_REG_CHIP_ID, ®, OP_READ)) chip_generation = anyka_generation(reg); - } +#ifndef STANDALONE_LIBRARY + /* The AV130 keeps its ID at the far end of the same block. */ + uint32_t av; + if (reg && !chip_generation && mem_reg(AK_REG_CHIP_ID_AV, &av, OP_READ) && + anyka_generation(av) == AK39_AV130) { + reg = av; + chip_generation = AK39_AV130; + } + anyka_chip_id = reg; + return anyka_full_model(chip_name, reg); +#else return true; +#endif } #ifndef STANDALONE_LIBRARY -/* Total DRAM is not something Linux can see here. The boot loader keeps the - * foot of the bank for the ISP and the encoder and hands the kernel only what - * is left -- 37 MB of a 64 MB part on the camera in issue #134 -- so - * /proc/meminfo is short by exactly the media reservation. The vendor boot - * argument `memsize` carries the real size, which is why the SDK reads it, - * and /proc/iomem says how much of it the kernel ended up with. */ -unsigned long anyka_media_mem(const char *cmdline, const char *iomem) { - char buf[256]; - unsigned long memsize, start, end; - - if (!line_from_file(cmdline, "memsize=([0-9]+)M", buf, sizeof(buf))) - return 0; - memsize = strtoul(buf, NULL, 10) * 1024; +/* Total DRAM is not something Linux can always see here. A vendor boot + * loader keeps the foot of the bank for the ISP and the encoder and hands the + * kernel only what is left -- 37 MB of a 64 MB part on the camera in issue + * #134 -- and says how much there is in `memsize=`, which is why the SDK + * reads it. OpenIPC's kernels take mem= alone and leave the rest of the + * memory the device tree declares to the ISP. Either way /proc/iomem says + * how much of it the kernel ended up with. */ +static bool iomem_kernel_ram(const char *iomem, unsigned long *start, + unsigned long *end) { + char buf[64]; if (!line_from_file(iomem, "^(\\w+)-\\w+ *: *System RAM", buf, sizeof(buf))) - return 0; - start = strtoul(buf, NULL, 16); + return false; + *start = strtoul(buf, NULL, 16); if (!line_from_file(iomem, "^\\w+-(\\w+) *: *System RAM", buf, sizeof(buf))) + return false; + *end = strtoul(buf, NULL, 16); + return *end > *start; +} + +unsigned long anyka_media_mem(const char *cmdline, const char *iomem) { + char buf[256]; + unsigned long memsize, start, end; + + if (!line_from_file(cmdline, "memsize=([0-9]+)M", buf, sizeof(buf))) return 0; - end = strtoul(buf, NULL, 16); + memsize = strtoul(buf, NULL, 10) * 1024; - if (end <= start) + if (!iomem_kernel_ram(iomem, &start, &end)) return 0; unsigned long kernel_ram = (end - start + 1) / 1024; return memsize > kernel_ram ? memsize - kernel_ram : 0; } +/* The device tree's /memory, in kB: one pair of 32-bit + * big-endian cells, as every AK39 device tree has it. ARM_ATAG_DTB_COMPAT + * puts the boot loader's own figure there. */ +unsigned long anyka_dt_memsize(const char *reg_path) { + unsigned char cells[8]; + + if (read_file(reg_path, cells, sizeof(cells)) != (int)sizeof(cells)) + return 0; + uint32_t size = (uint32_t)cells[4] << 24 | (uint32_t)cells[5] << 16 | + (uint32_t)cells[6] << 8 | cells[7]; + return size / 1024; +} + static unsigned long anyka_totalmem(unsigned long *media_mem) { + unsigned long dt_total, start, end; + *media_mem = anyka_media_mem("/proc/cmdline", "/proc/iomem"); - return *media_mem + kernel_mem(); + if (*media_mem) + return *media_mem + kernel_mem(); + + dt_total = anyka_dt_memsize("/proc/device-tree/memory/reg"); + if (dt_total && iomem_kernel_ram("/proc/iomem", &start, &end)) { + unsigned long kernel_ram = (end - start + 1) / 1024; + if (dt_total > kernel_ram) { + *media_mem = dt_total - kernel_ram; + return dt_total; + } + } + return kernel_mem(); } static void anyka_chip_properties(cJSON *j_inner) { @@ -143,8 +291,177 @@ static void anyka_chip_properties(cJSON *j_inner) { } #endif +/* + * The sensor answers I2C only while it is clocked and out of reset, and on + * these SoCs that is only while the camera driver is using it: it powers the + * sensor up to probe it and down again afterwards. So the probe powers it up + * itself, the way the drivers do -- the AK3918EV200 and the Cloud39EV300 + * SDK's ak_camera.c and OpenIPC's ak-cam.c alike: the CIS clock from the + * peripheral PLL in SYSCTRL 0x18 (divider in bits 15:10, latched by bit 19, + * enabled by bit 18; 24 MHz, which every sensor these SoCs drive accepts), + * then a reset pulse on the board's reset and power-down GPIOs, which only a + * device tree names. Measured on an AK3918EV200 (SC1345): the sensor reads + * 0xffff before and its chip ID after. + * + * ipcinfo, in every image, starts the clock alone: the sensor drivers' power + * off leaves those pins as inputs, not held asserted, and the SC1345 answers + * with just the clock. ipctool also pulses the pins. + * + * Nothing is touched when the clock is already running: then a pipeline may + * be streaming, the sensor already answers, and a reset would stop it. What + * is touched is put back by anyka_hal_cleanup(), and only that. + */ +#define AK_REG_PERI_PLL1 (AK_PA_SYSCTRL + 0x14) +#define AK_REG_PERI_PLL2 (AK_PA_SYSCTRL + 0x18) +#define AK_GPIO_DIR 0x20170000 +#define AK_GPIO_OUT 0x2017000c + +/* The CIS clock divider field for 24 MHz from the peripheral PLL register, + * 12 * M / (N * 2^OD) MHz over 24, minus one; 0 for a register the camera + * drivers would refuse. Counted up to keep division out of the binary. */ +unsigned anyka_cis_divider(uint32_t pll1) { + unsigned m = pll1 & 0xff, n = pll1 >> 8 & 0xf, od = pll1 >> 12 & 0x3, d = 0; + + if (!od || n - 2 > 4) + return 0; + while ((2 * (d + 2) * n << od) <= m) + d++; + return d; +} + +/* What each register held before the power-up first wrote it: the PLL's two + * and, in ipctool, direction and level for each of a camera's two pins. + * Restored in one write each: replaying every write to the PLL register left + * its latch bit set on an AK3918EV200. */ +#ifdef STANDALONE_LIBRARY +#define AK_UNDO_MAX 2 +#else +#define AK_UNDO_MAX 6 +#endif +static struct { + uint32_t addr, val; +} anyka_undo[AK_UNDO_MAX]; +static int anyka_undo_n; + +static void anyka_rmw(uint32_t addr, uint32_t clear, uint32_t set) { + uint32_t v = 0; + + mem_reg(addr, &v, OP_READ); + v = (v & ~clear) | set; + mem_reg(addr, &v, OP_WRITE); +} + +/* anyka_rmw() for a register's first write, remembering what it held. */ +static void anyka_poke(uint32_t addr, uint32_t clear, uint32_t set) { + anyka_undo[anyka_undo_n].addr = addr; + mem_reg(addr, &anyka_undo[anyka_undo_n++].val, OP_READ); + anyka_rmw(addr, clear, set); +} + +#ifndef STANDALONE_LIBRARY +/* The camera nodes under the I2C adapter being probed: its device tree node + * is what /sys/bus/i2c/devices/i2c-N/of_node points at. A kernel that does + * not link it leaves the glob over every adapter, which is used only when it + * finds a single camera. */ +static void camera_gpio_pattern(char *out, size_t len) { + char link[64], node[256]; + const char *rel; + ssize_t n; + + snprintf(link, sizeof(link), "/sys/bus/i2c/devices/i2c-%d/of_node", + i2c_adapter_nr); + n = readlink(link, node, sizeof(node) - 1); + if (n > 0) { + node[n] = '\0'; + rel = strstr(node, "devicetree/base/"); + if (rel) { + snprintf(out, len, "/sys/firmware/%s/camera@*/*-gpio", rel); + return; + } + } + snprintf(out, len, "/proc/device-tree/soc/i2c*/camera@*/*-gpio"); +} + +/* of each GPIO property of the camera node; flags bit 0 + * is GPIO_ACTIVE_LOW. The AK39 device trees have one GPIO controller, so the + * phandle is not followed. More matches than one camera has -- whose pins + * they are is then not known -- and none is touched. Power down is released + * and reset asserted, then reset released. */ +static void anyka_pulse_camera_pins(void) { + uint32_t rst_reg = 0, rst_bit = 0, rst_val = 0; + char pattern[320]; + glob_t g; + + camera_gpio_pattern(pattern, sizeof(pattern)); + if (glob(pattern, 0, NULL, &g)) + return; + for (size_t i = 0; g.gl_pathc <= 2 && i < g.gl_pathc; i++) { + unsigned char c[12]; + + if (read_file(g.gl_pathv[i], c, sizeof(c)) != sizeof(c) || c[7] > 127) + continue; + uint32_t bank = (c[7] >> 5) * 4, bit = 1u << (c[7] & 31); + bool low = c[11] & 1, reset = strstr(g.gl_pathv[i], "reset"); + + anyka_poke(AK_GPIO_OUT + bank, bit, reset != low ? bit : 0); + anyka_poke(AK_GPIO_DIR + bank, 0, bit); + if (reset) { + rst_reg = AK_GPIO_OUT + bank; + rst_bit = bit; + rst_val = low ? bit : 0; + } + } + globfree(&g); + usleep(10000); + if (rst_reg) + anyka_rmw(rst_reg, rst_bit, rst_val); +} +#endif + +static void anyka_enable_sensor_clock() { + uint32_t pll1, pll2; + unsigned div; + + if (anyka_undo_n || (chip_generation | 0x100) != AK39_EV3 || + !mem_reg(AK_REG_PERI_PLL1, &pll1, OP_READ) || + !mem_reg(AK_REG_PERI_PLL2, &pll2, OP_READ) || + pll2 & 1u << 18) /* running: leave it to whoever runs it */ + return; + div = anyka_cis_divider(pll1); + if (!div) + return; + + /* The probe calls hal_cleanup() only after it has opened the bus, so a + * power-up for a bus that will not open would never be undone. */ + int fd = open_i2c_sensor_fd(i2c_adapter_nr); + if (fd < 0) + return; + close_sensor_fd(fd); + + if (pll1 & 1u << 25) + anyka_poke(AK_REG_PERI_PLL1, 1u << 25, 0); + anyka_poke(AK_REG_PERI_PLL2, 0x3fu << 10, div << 10 | 1u << 19); + anyka_rmw(AK_REG_PERI_PLL2, 0, 1u << 18); + +#ifndef STANDALONE_LIBRARY + anyka_pulse_camera_pins(); +#endif + usleep(20000); +} + +static void anyka_hal_cleanup() { + while (anyka_undo_n) { + anyka_undo_n--; + mem_reg(anyka_undo[anyka_undo_n].addr, &anyka_undo[anyka_undo_n].val, + OP_WRITE); + } + restore_printk(); +} + void anyka_setup_hal() { possible_i2c_addrs = anyka_possible_i2c_addrs; + hal_enable_sensor_clock = anyka_enable_sensor_clock; + hal_cleanup = anyka_hal_cleanup; #ifndef STANDALONE_LIBRARY hal_totalmem = anyka_totalmem; hal_chip_properties = anyka_chip_properties; diff --git a/src/hal/anyka.h b/src/hal/anyka.h index 9a41313..5ae8bb6 100644 --- a/src/hal/anyka.h +++ b/src/hal/anyka.h @@ -11,19 +11,29 @@ * "akpcm_AnalogCtrlRegs" -- so it has to be spelled out here. */ #define AK_PA_SYSCTRL 0x08000000 #define AK_REG_CHIP_ID (AK_PA_SYSCTRL + 0x00) +/* The AK3918AV130 keeps its ID at the far end of the same block + * (mach-ak3918av130.c: AK_VA_SYSCTRL + 0x3FC). */ +#define AK_REG_CHIP_ID_AV (AK_PA_SYSCTRL + 0x3FC) /* chip_generation values. The ID word names a generation, never the exact * part: a vendor kernel compares it against the single constant picked by its * CONFIG_CPU_AK39xx and prints whichever name it was built for, so the same * 0x20160100 is announced as AK3916, AK3918 or AK3919 by three different * boards. The part number comes from the machine string instead. */ +/* The first generation, Aimer39 (AK3918, AK3916, AK3910): its 0x20120100 is + * shared with some AK3918EV200 boards, so the board string decides. */ +/* A constant whose low nibble is 0 spells its part's suffix in its low 16 + * bits, 0xE200 being EV200, and anyka_model_name() prints it; the others have + * none to give. */ +#define AK39_EV1 0x3918E101 #define AK39_EV2 0x3918E200 #define AK39_EV3 0x3918E300 #define AK39_EV330 0x3939E330 #define AK39_EV300L 0x3918E30C #define AK39_AV100 0x3918A100 -#define AK37_D 0x3737D000 -#define AK37_E 0x3737E000 +#define AK39_AV130 0x3918A130 +#define AK37_D 0x3737D001 +#define AK37_E 0x3737E001 bool anyka_detect_cpu(char *chip_name); void anyka_setup_hal(); @@ -34,6 +44,10 @@ void anyka_setup_hal(); * /proc paths. */ bool anyka_machine_name(const char *cpuinfo, char *out, size_t outlen); int anyka_generation(uint32_t chip_id); +void anyka_model_name(const char *machine, const char *compat, int generation, + char *out, size_t outlen); unsigned long anyka_media_mem(const char *cmdline, const char *iomem); +unsigned long anyka_dt_memsize(const char *reg_path); +unsigned anyka_cis_divider(uint32_t pll1); #endif /* HAL_ANYKA_H */ diff --git a/src/hal/anyka_test.c b/src/hal/anyka_test.c index 2c16f1c..ed19e05 100644 --- a/src/hal/anyka_test.c +++ b/src/hal/anyka_test.c @@ -137,6 +137,10 @@ static void chip_ids(void) { CHECK(anyka_generation(0x53533542) == AK39_AV100); CHECK(anyka_generation(0x53543400) == AK39_EV300L); + /* AK3918AV130 revisions A and B, at SYSCTRL + 0x3FC. */ + CHECK(anyka_generation(0x56573500) == AK39_AV130); + CHECK(anyka_generation(0x56573502) == AK39_AV130); + /* A shifted row must not swallow a raw one, or an EV300 would report as * whatever 0x201601.. happened to be listed. */ CHECK(anyka_generation(0x20160102) == 0); @@ -145,6 +149,78 @@ static void chip_ids(void) { CHECK(anyka_generation(0xffffffff) == 0); } + +static void expect_model(const char *machine, const char *compat, + int generation, const char *want) { + char got[128]; + + anyka_model_name(machine, compat, generation, got, sizeof(got)); + CHECK(!strcmp(got, want)); + if (strcmp(got, want)) + fprintf(stderr, " (wanted \"%s\", got \"%s\")\n", want, got); +} + +/* "AK3918E80PIN" is a package: an EV200's on one board, an EV300's on + * another, so the generation completes it. The boot logs these come from: + * OpenIPC's EV200 kernel, a V380E2 and a gwell AK3918EV200 (vendor 3.4.35, + * ID 0x20150200), an AK3916EV301 camera whose vendor kernel says EV300 + * (0x20160100), and EV330 vendor kernels, which carry a device tree. */ +static void model_names(void) { + expect_model("AK3918E80PIN", "ak3918ev200", AK39_EV2, "AK3918EV200"); + expect_model("AK3918E80PIN", "", AK39_EV2, "AK3918EV200"); + expect_model("AK3918E80PIN", "", AK39_EV3, "AK3918EV300"); + expect_model("AK3918E", "", AK39_EV2, "AK3918EV200"); + expect_model("AK3918EV200", "", AK39_EV2, "AK3918EV200"); + expect_model("AK3918EV300", "", AK39_EV3, "AK3918EV300"); + expect_model("AK39EV330", "ak3919ev330", AK39_EV330, "AK3919EV330"); + /* Only the generation-wide compatible: name the generation, as the + * vendor banner does ("ANYKA CPU AK39XXEV330"), not the board's short + * form. */ + expect_model("AK39EV330", "ak39ev330", AK39_EV330, "AK39XXEV330"); + expect_model("AK39EV330", "", AK39_EV330, "AK39XXEV330"); + /* Aimer39 has no suffix, and a generation nobody can name adds none. */ + expect_model("AK3918", "", AK39_EV1, "AK3918"); + expect_model("AK3918E80PIN", "", 0, "AK3918E80PIN"); + expect_model("AK3918AV130", "", AK39_AV130, "AK3918AV130"); + /* The suffix comes out of the generation constant. */ + expect_model("AK3918E80PIN", "", AK39_AV130, "AK3918AV130"); + expect_model("AK3918E80PIN", "", AK39_AV100, "AK3918AV100"); + expect_model("AK3918E80PIN", "", AK39_EV300L, "AK3918E80PIN"); + /* AK37 constants spell no AK39 suffix. */ + expect_model("AK3760", "", AK37_D, "AK3760"); + /* Device tree alone, no board string to go on. */ + expect_model("", "ak3918ev200", 0, "AK3918EV200"); +} + +static const char *binary_fixture(const unsigned char *data, size_t len) { + const char *path = fixture(""); + FILE *f = fopen(path, "wb"); + + if (f) { + fwrite(data, 1, len, f); + fclose(f); + } + return path; +} + +static void device_tree(void) { + /* OpenIPC's EV200: /memory reg = <0x80000000 0x04000000>. */ + static const unsigned char mem[] = {0x80, 0, 0, 0, 0x04, 0, 0, 0}; + const char *path = binary_fixture(mem, sizeof(mem)); + CHECK(anyka_dt_memsize(path) == 64 * 1024); + unlink(path); + + /* reset-gpio = <&gpio 0 GPIO_ACTIVE_LOW>, the gwell camera's. */ + static const unsigned char gpio[] = {0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1}; + /* The EV200's peripheral PLL, 0x00a512c8: M 200, N 2, OD 1 -> 600 MHz, + * and 600 / 24 - 1 = 24. Values the camera drivers refuse give 0. */ + CHECK(anyka_cis_divider(0x00a512c8) == 24); + CHECK(anyka_cis_divider(0x00000000) == 0); + /* OD 0, and N 1: both outside what the drivers accept. */ + CHECK(anyka_cis_divider(0x000002c8) == 0); + CHECK(anyka_cis_divider(0x000011c8) == 0); +} + static void media_mem(void) { /* The PC420 again: 64 MB of DDR, of which the kernel was handed * 0x81b00000-0x83ffffff = 37 MB, so 27 MB went to the ISP and encoder. */ @@ -177,6 +253,8 @@ static void media_mem(void) { int main(void) { machine_names(); chip_ids(); + model_names(); + device_tree(); media_mem(); if (failures) { diff --git a/src/hal/common.c b/src/hal/common.c index 20fa94e..7cefdc6 100644 --- a/src/hal/common.c +++ b/src/hal/common.c @@ -89,19 +89,23 @@ int universal_i2c_write_register(int fd, unsigned char i2c_addr, unsigned int reg_addr, unsigned int reg_width, unsigned int data, unsigned int data_width) { (void)i2c_addr; - (void)data; - char buf[2]; - - if (reg_width == 2) { - buf[0] = (reg_addr >> 8) & 0xff; - buf[1] = reg_addr & 0xff; - } else { - buf[0] = reg_addr & 0xff; - } - - if (write(fd, buf, data_width) != (int)data_width) { + unsigned char buf[4]; + int len = 0; + + /* The register address, then the value, both most significant byte + * first: one I2C write. This used to send `data_width` bytes of the + * address alone, so every write through it -- `ipctool i2cset`, the + * SuperPix page select -- stored nothing, and with two-byte addresses + * not even the whole address went out. */ + if (reg_width == 2) + buf[len++] = (reg_addr >> 8) & 0xff; + buf[len++] = reg_addr & 0xff; + if (data_width == 2) + buf[len++] = (data >> 8) & 0xff; + buf[len++] = data & 0xff; + + if (write(fd, buf, len) != len) return -1; - } return 0; } diff --git a/src/hal/common.h b/src/hal/common.h index 94c74a3..329052f 100644 --- a/src/hal/common.h +++ b/src/hal/common.h @@ -65,6 +65,8 @@ enum SENSORS { SENSOR_IMAGEDESIGN, SENSOR_VISEMI, SENSOR_CVSENS, + SENSOR_PRIMESENSOR, + SENSOR_BYD, }; typedef struct { diff --git a/src/sensors.c b/src/sensors.c index fe39b51..dcf5976 100644 --- a/src/sensors.c +++ b/src/sensors.c @@ -533,11 +533,11 @@ static int detect_smartsens_sensor(sensor_ctx_t *ctx, int fd, break; case 0x1235: break; - case 0x1245: { - int sw = i2c_read_register(fd, i2c_addr, 0x3020, 2, 1); - sprintf(ctx->sensor_id, "SC2145H_%c", sw == 2 ? 'A' : 'B'); + case 0x1245: + /* 0x3020 tells two silicon revisions apart (the Anyka driver picks + * its init variant by it, 2 or 3); it is not a different part. */ + strcpy(ctx->sensor_id, "SC2145H"); return true; - } case 0x2032: res = 0x2035; break; @@ -1201,6 +1201,72 @@ static int detect_cvsens_sensor(sensor_ctx_t *ctx, int fd, } } +#ifndef STANDALONE_LIBRARY +/* PrimeSensor PS52xx, as the Anyka EV200/EV300 drivers read it: the ID is in + * bank 0, registers 0x00/0x01, byte addresses, and the bank is selected + * through 0xef. The bank is put back as it was found, like the SuperPix page: + * a streamer driving the sensor writes its exposure to another bank. */ +static int detect_primesensor_sensor(sensor_ctx_t *ctx, int fd, + unsigned char i2c_addr) { + if (i2c_change_addr(fd, i2c_addr) < 0) + return false; + + int bank = i2c_read_register(fd, i2c_addr, 0xef, 1, 1); + if (bank == -1 || bank == 0xff) + return false; + /* A bank that cannot be selected would make the ID reads come from the + * wrong one: give up rather than name the sensor from them. */ + if (bank && i2c_write_register(fd, i2c_addr, 0xef, 1, 0x00, 1) < 0) + return false; + int msb = i2c_read_register(fd, i2c_addr, 0x00, 1, 1); + int lsb = i2c_read_register(fd, i2c_addr, 0x01, 1, 1); + /* Retried once: a streamer driving this sensor writes to the bank it + * left selected. */ + if (bank && i2c_write_register(fd, i2c_addr, 0xef, 1, bank, 1) < 0) + i2c_write_register(fd, i2c_addr, 0xef, 1, bank, 1); + if (msb == -1 || lsb == -1) + return false; + + int res = msb << 8 | lsb; + switch (res) { + case 0x5250: + case 0x5260: + case 0x5268: + case 0x5270: + case 0x5280: + sprintf(ctx->sensor_id, "PS%04x", res); + return true; + default: + SENSOR_ERR("PrimeSensor", res); + return false; + } +} + +/* BYD BF3703 (VGA, Anyka EV200 SDK), ID at byte registers 0xfc/0xfd. */ +static int detect_byd_sensor(sensor_ctx_t *ctx, int fd, + unsigned char i2c_addr) { + if (i2c_change_addr(fd, i2c_addr) < 0) + return false; + + int msb = i2c_read_register(fd, i2c_addr, 0xfc, 1, 1); + if (msb == -1) + return false; + int lsb = i2c_read_register(fd, i2c_addr, 0xfd, 1, 1); + if (lsb == -1) + return false; + + int res = msb << 8 | lsb; + switch (res) { + case 0x3703: + sprintf(ctx->sensor_id, "BF%04x", res); + return true; + default: + SENSOR_ERR("BYD", res); + return false; + } +} +#endif /* !STANDALONE_LIBRARY */ + static int detect_possible_sensors(sensor_ctx_t *ctx, int fd, int (*detect_fn)(sensor_ctx_t *ctx, int, unsigned char), @@ -1287,6 +1353,18 @@ static bool get_sensor_id_i2c(sensor_ctx_t *ctx) { SENSOR_CVSENS)) { strcpy(ctx->vendor, "CVSENS"); detected = true; +#ifndef STANDALONE_LIBRARY + } else if (detect_possible_sensors(ctx, fd, detect_primesensor_sensor, + SENSOR_PRIMESENSOR)) { + strcpy(ctx->vendor, "PrimeSensor"); + ctx->reg_width = 1; + detected = true; + } else if (detect_possible_sensors(ctx, fd, detect_byd_sensor, + SENSOR_BYD)) { + strcpy(ctx->vendor, "BYD"); + ctx->reg_width = 1; + detected = true; +#endif } exit: close_sensor_fd(fd); diff --git a/src/sensors_golden.txt b/src/sensors_golden.txt index 032b068..8323024 100644 --- a/src/sensors_golden.txt +++ b/src/sensors_golden.txt @@ -5,7 +5,7 @@ smartsens 0132 SC132GS smartsens 1045 SC1045 smartsens 1145 SC1145 smartsens 1235 SC1235 -smartsens 1245 SC2145H_B +smartsens 1245 SC2145H smartsens 17cb SC210IoT smartsens 2032 SC2035 smartsens 2045 SC2045