|
| 1 | +#include "hal/anyka.h" |
| 2 | + |
| 3 | +#include <stdint.h> |
| 4 | +#include <stdlib.h> |
| 5 | +#include <string.h> |
| 6 | + |
| 7 | +#include "chipid.h" |
| 8 | +#include "hal/common.h" |
| 9 | +#include "tools.h" |
| 10 | + |
| 11 | +/* Addresses as the vendor's own sensor drivers in |
| 12 | + * drivers/media/video/plat-anyka/ spell them, which is the 8-bit write form |
| 13 | + * ipctool's tables use. SOI parts sit at either end: the JX-F2x/H63 drivers |
| 14 | + * use 0x80 and the H65 one 0x60. */ |
| 15 | +static unsigned char sony_addrs[] = {0x34, 0}; |
| 16 | +static unsigned char soi_addrs[] = {0x80, 0x60, 0}; |
| 17 | +static unsigned char ssens_addrs[] = {0x60, 0}; |
| 18 | +static unsigned char onsemi_addrs[] = {0x20, 0}; |
| 19 | +static unsigned char omni_addrs[] = {0x6c, 0}; |
| 20 | +static unsigned char gc_addrs[] = {0x42, 0x6e, 0}; |
| 21 | +static unsigned char superpix_addrs[] = {0x7a, 0x78, 0}; |
| 22 | + |
| 23 | +static sensor_addr_t anyka_possible_i2c_addrs[] = { |
| 24 | + {SENSOR_SONY, sony_addrs}, {SENSOR_SOI, soi_addrs}, |
| 25 | + {SENSOR_SMARTSENS, ssens_addrs}, {SENSOR_ONSEMI, onsemi_addrs}, |
| 26 | + {SENSOR_OMNIVISION, omni_addrs}, {SENSOR_GALAXYCORE, gc_addrs}, |
| 27 | + {SENSOR_SUPERPIX, superpix_addrs}, {0, NULL}}; |
| 28 | + |
| 29 | +/* Raw chip ID as it was read, kept so the report can show the number the |
| 30 | + * kernel's own "ANYKA CPU ... (ID 0x...)" banner prints. Zero when /dev/mem |
| 31 | + * would not give it up. */ |
| 32 | +static uint32_t anyka_chip_id; |
| 33 | + |
| 34 | +static const struct { |
| 35 | + uint32_t id; |
| 36 | + /* The 2019 and later parts moved the ID up into bits 31:8 and match on |
| 37 | + * the top 24 bits only, leaving the low byte to vary; the mask falls out |
| 38 | + * of the shift. */ |
| 39 | + uint32_t shift; |
| 40 | + int generation; |
| 41 | +} anyka_socs[] = { |
| 42 | + {0x20120100, 0, AK39_EV2}, |
| 43 | + {0x20150200, 0, AK39_EV2}, |
| 44 | + {0x20160100, 0, AK39_EV3}, |
| 45 | + {0x20160101, 0, AK39_EV330}, |
| 46 | + {0x20170200, 0, AK37_D}, |
| 47 | + {0x00201902, 8, AK37_E}, |
| 48 | + {0x00535335, 8, AK39_AV100}, |
| 49 | + {0x00535434, 8, AK39_EV300L}, |
| 50 | +}; |
| 51 | + |
| 52 | +int anyka_generation(uint32_t chip_id) { |
| 53 | + for (size_t i = 0; i < ARRCNT(anyka_socs); i++) { |
| 54 | + uint32_t shift = anyka_socs[i].shift; |
| 55 | + |
| 56 | + if (((chip_id >> shift) & (0xFFFFFFFFu >> shift)) == anyka_socs[i].id) |
| 57 | + return anyka_socs[i].generation; |
| 58 | + } |
| 59 | + return 0; |
| 60 | +} |
| 61 | + |
| 62 | +/* These kernels predate device tree on the camera parts -- the 3.4.35 in |
| 63 | + * issue #134 has no /proc/device-tree at all -- and the SoC registers no UART |
| 64 | + * with /proc/iomem, so the machine string is the only signal that does not |
| 65 | + * need /dev/mem. Every Anyka camera board spells the part into it: |
| 66 | + * "CLOUD39EV3_AK3918EV300_MNBD", "Cloud39EV2_AK3918E80PIN_MNBD", |
| 67 | + * "Aimer39_AK3918_MB_V1.0.0", "AK39EV330". */ |
| 68 | +bool anyka_machine_name(const char *cpuinfo, char *out, size_t outlen) { |
| 69 | + if (!line_from_file(cpuinfo, "Hardware.*:.*([Aa][Kk]3[0-9][0-9A-Za-z]*)", |
| 70 | + out, outlen)) |
| 71 | + return false; |
| 72 | + |
| 73 | + /* line_from_file() leaves no terminator behind when the match fills the |
| 74 | + * buffer, and this one gets copied on into chip_name. */ |
| 75 | + out[outlen - 1] = '\0'; |
| 76 | + return true; |
| 77 | +} |
| 78 | + |
| 79 | +bool anyka_detect_cpu(char *chip_name) { |
| 80 | + /* As wide as chip_name, since that is where it lands. */ |
| 81 | + char machine[128]; |
| 82 | + uint32_t reg; |
| 83 | + |
| 84 | + if (!anyka_machine_name("/proc/cpuinfo", machine, sizeof(machine))) |
| 85 | + return false; |
| 86 | + strcpy(chip_name, machine); |
| 87 | + |
| 88 | + /* Only ever a refinement: the board already named the part, and a kernel |
| 89 | + * built with strict devmem filtering can refuse the window outright. */ |
| 90 | + if (mem_reg(AK_REG_CHIP_ID, ®, OP_READ)) { |
| 91 | + anyka_chip_id = reg; |
| 92 | + chip_generation = anyka_generation(reg); |
| 93 | + } |
| 94 | + |
| 95 | + return true; |
| 96 | +} |
| 97 | + |
| 98 | +#ifndef STANDALONE_LIBRARY |
| 99 | +/* Total DRAM is not something Linux can see here. The boot loader keeps the |
| 100 | + * foot of the bank for the ISP and the encoder and hands the kernel only what |
| 101 | + * is left -- 37 MB of a 64 MB part on the camera in issue #134 -- so |
| 102 | + * /proc/meminfo is short by exactly the media reservation. The vendor boot |
| 103 | + * argument `memsize` carries the real size, which is why the SDK reads it, |
| 104 | + * and /proc/iomem says how much of it the kernel ended up with. */ |
| 105 | +unsigned long anyka_media_mem(const char *cmdline, const char *iomem) { |
| 106 | + char buf[256]; |
| 107 | + unsigned long memsize, start, end; |
| 108 | + |
| 109 | + if (!line_from_file(cmdline, "memsize=([0-9]+)M", buf, sizeof(buf))) |
| 110 | + return 0; |
| 111 | + memsize = strtoul(buf, NULL, 10) * 1024; |
| 112 | + |
| 113 | + if (!line_from_file(iomem, "^(\\w+)-\\w+ *: *System RAM", buf, |
| 114 | + sizeof(buf))) |
| 115 | + return 0; |
| 116 | + start = strtoul(buf, NULL, 16); |
| 117 | + |
| 118 | + if (!line_from_file(iomem, "^\\w+-(\\w+) *: *System RAM", buf, |
| 119 | + sizeof(buf))) |
| 120 | + return 0; |
| 121 | + end = strtoul(buf, NULL, 16); |
| 122 | + |
| 123 | + if (end <= start) |
| 124 | + return 0; |
| 125 | + |
| 126 | + unsigned long kernel_ram = (end - start + 1) / 1024; |
| 127 | + return memsize > kernel_ram ? memsize - kernel_ram : 0; |
| 128 | +} |
| 129 | + |
| 130 | +static unsigned long anyka_totalmem(unsigned long *media_mem) { |
| 131 | + *media_mem = anyka_media_mem("/proc/cmdline", "/proc/iomem"); |
| 132 | + return *media_mem + kernel_mem(); |
| 133 | +} |
| 134 | + |
| 135 | +static void anyka_chip_properties(cJSON *j_inner) { |
| 136 | + /* getchipfamily() falls back to the model when the generation is unknown, |
| 137 | + * and repeating the model as its own family says nothing. */ |
| 138 | + if (chip_generation) |
| 139 | + ADD_PARAM("family", getchipfamily()); |
| 140 | + |
| 141 | + if (anyka_chip_id) |
| 142 | + ADD_PARAM_FMT("id", "0x%08x", anyka_chip_id); |
| 143 | +} |
| 144 | +#endif |
| 145 | + |
| 146 | +void anyka_setup_hal() { |
| 147 | + possible_i2c_addrs = anyka_possible_i2c_addrs; |
| 148 | +#ifndef STANDALONE_LIBRARY |
| 149 | + hal_totalmem = anyka_totalmem; |
| 150 | + hal_chip_properties = anyka_chip_properties; |
| 151 | +#endif |
| 152 | +} |
0 commit comments