diff --git a/CLAUDE.md b/CLAUDE.md index feef0c2e..e9d0e54e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -225,7 +225,7 @@ or OP-TEE leaves every interrupt secure and Linux hangs after the timer. shell. Video input: `aw-vinc` paints test frames into buffers behind `aw-iommu` (the media masters use IOMMU addresses); `aw-ve` answers the VE ID CedarC's compiled-in table and its LBC check accept (0xE0/0xE4 = -0x21110/0x21210); the sensor is `hisi-gc2053` on TWI1; `aw-pio` reads +0x21110/0x21210); the sensor is the catalogue's `gc2053` on TWI1; `aw-pio` reads inputs high (stored zeros were the YI reset button held down). `qemu-boot/yi-v831-prepare.sh` makes a YI image that runs as if paired. `aw-ve` encodes H.264: CedarC puts the headers through the bitstream @@ -596,12 +596,20 @@ in this order: `chip_variant` (and `v1_chip_id_88`/`_8c` for the V1 quartet) or firmware prints `Wrong chip-id`/`hiunknown` and `load_hisilicon` bails before loading any vendor module. CI greps for exactly this. -- **Sensors** are I2C/SSI slave models in `qemu/hw/i2c/` and `qemu/hw/ssi/`, - selected by name in a `strcmp` chain in `hisilicon_common_init()` - (`imx335`, `imx307`, `f37`, `gc2053`, `sc2315e`, … , or `none`). - The Xiongmai machines take a QOM type name instead (`sensor=hisi-imx335`, - `sensor=none`) plus `sensor-addr=`; `xm-sensor` is a generic register-file - sensor whose ID comes from `-global xm-sensor.regs=` (`docs/xiongmai.md`). +- **Sensors** are I2C slave models in `qemu/hw/i2c/`, shared by every family + and built for every target. `sensor=` names one from the catalogue in + `hw/i2c/image-sensor.c` (name, model, 7-bit address, chip ID): add a part + there, not in a machine, and every machine takes it through + `image_sensor_create()`. A part new to the catalogue usually needs only a + line: SmartSens and GalaxyCore parts share a model. Take its ID from the + vendor driver. An ISP reads a sensor through the `image-sensor` interface + (`image_sensor_exposure()`); a model implements it from its registers. The + plain Sony models, `hisi-mis2006` and `hisi-sp2305` do not yet: on an ISP + that needs a picture from the sensor they are found but draw nothing. + The Xiongmai machines also take a QOM type name (`sensor=hisi-imx335`) plus + `sensor-addr=`; `xm-sensor` is a generic register-file sensor whose ID comes + from `-global xm-sensor.regs=` (`docs/xiongmai.md`). The SSI `imx122` stays + in `hisilicon_common_init()`. ## Adding a SoC or a device diff --git a/docs/allwinner-v83x.md b/docs/allwinner-v83x.md index 4719cc2b..ffcf88b8 100644 --- a/docs/allwinner-v83x.md +++ b/docs/allwinner-v83x.md @@ -160,7 +160,7 @@ starts, stays up, and with a configured camera opens its H.264 encoder. on LBC input. 0x1707 (an older guess) passes the VE layer but the encoder refuses it. The ID is chosen from the firmware's table, not read from silicon. -- **Sensor.** The repo's GC2053 (`hisi-gc2053`, ID 0x2053 at 0xF0/0xF1) +- **Sensor.** The catalogue's GC2053 (`galaxycore-sensor`, ID 0x2053 at 0xF0/0xF1) sits at 0x37 on TWI1. The VIN driver registers its video nodes and the ISP library finds its `gc2053_mipi_1936_1096_20` tuning. - **Frames.** `aw-vinc` paints the OpenIPC logo (still, white on navy) diff --git a/docs/sigmastar.md b/docs/sigmastar.md index 095a857f..732131d2 100644 --- a/docs/sigmastar.md +++ b/docs/sigmastar.md @@ -147,6 +147,27 @@ timers count the 12 MHz crystal, timer 0 the 432 MHz MPLL divided by Reading a counter's low half latches its high half: U-Boot takes any step back for a 32-bit wrap. +## Boards + +The `msc313e` machine takes other infinity3 boards through its options. The +firmware expects the board's own sensor and RAM: with the wrong sensor an +app gives up (Anjoy's reboots, Raysharp's dies under its watchdog), and with +too little RAM the kernel's CMA pool wraps onto low memory and corrupts it. +A board's real values are in its dump: the sensor from `Sensor_id_table` in +the `libdrv_ms_cus_*.so` it ships, the RAM from the kernel's `Memory:` line. + +| Board | Options | Result | +|---|---|---| +| Jabsco C3001, C301-M15 | `sensor=jxf22` (the default) | login, app runs | +| Anjoy YM200J, YM200J2 | `sensor=imx307` | YM200J: H.264 over RTSP; AE converges | +| Anjoy MC300E10 (its "SC32xx" name notwithstanding) | `sensor=sc2315e` | no reboot, AE converges | +| Raysharp MSC316D (RS-CH228M2NN) | `sensor=sc2310,rtc=ds1338 -m 128M` | video pipeline runs, AE converges | +| MSC316DC | `sensor=imx323 -m 128M` | video pipeline runs; picture saturates, AE does not settle | + +`rtc=ds1338` puts a DS1307-family RTC at 0x68 (0xD0 in 8-bit) on the first +I2C bus, where the Raysharp app reads its time. The Anjoy `CRY` driver +(`/dev/cry`) reads "failed" on every board here and the firmware runs on. + ## Devices - **SPI NOR** (`sstar-fsp`). One generic 3-byte-address NOR behind the four diff --git a/qemu/hw/arm/allwinner-v83x.c b/qemu/hw/arm/allwinner-v83x.c index f21d6501..f492a305 100644 --- a/qemu/hw/arm/allwinner-v83x.c +++ b/qemu/hw/arm/allwinner-v83x.c @@ -24,6 +24,7 @@ #include "hw/char/serial-mm.h" #include "hw/i2c/allwinner-i2c.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/intc/arm_gic.h" #include "hw/net/allwinner-sun8i-emac.h" #include "hw/intc/arm_gic_common.h" @@ -457,8 +458,7 @@ static void aw_v83x_common_init(MachineState *machine, const AwSoCConfig *c) * The camera sensor, a GC2053 at 0x37 on TWI1 (the vendor * vind DT and gc2053_mipi.c): ID 0x2053 at 0xF0/0xF1. */ - i2c_slave_create_simple(I2C_BUS(qdev_get_child_bus(twi, "i2c")), - "hisi-gc2053", 0x37); + image_sensor_create(I2C_BUS(qdev_get_child_bus(twi, "i2c")), "gc2053"); } sysbus_create_simple("aw-twi", base + 0x200, qdev_get_gpio_in(orgate, 1)); diff --git a/qemu/hw/arm/fullhan.c b/qemu/hw/arm/fullhan.c index 592d8166..103c0408 100644 --- a/qemu/hw/arm/fullhan.c +++ b/qemu/hw/arm/fullhan.c @@ -52,6 +52,7 @@ #include "hw/boards.h" #include "hw/cpu/a15mpcore.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/misc/unimp.h" @@ -434,51 +435,6 @@ static void fh_machine_set_sensor(Object *obj, const char *value, * turn: the FH885xV200 ones GC2063 (0x37), GC4653 (0x29), MIS2006 (0x30); * the V100 / FH8626 ones GC2053, GC2093, GC1054, JX-F37, MIS2006. */ -static const struct { - const char *name; - const char *type; /* NULL: galaxycore-sensor */ - uint8_t addr; - bool reg16; - uint16_t id_reg, id; -} fh_sensors[] = { - { "gc4653", NULL, 0x29, true, 0x03F0, 0x4653 }, - { "gc2053", NULL, 0x37, false, 0xF0, 0x2053 }, - { "gc2063", NULL, 0x37, false, 0xF0, 0x2063 }, - { "gc2093", NULL, 0x37, true, 0x03F0, 0x2093 }, - { "gc1054", NULL, 0x21, false, 0xF0, 0x1054 }, - { "mis2006", "hisi-mis2006", 0x30 }, - { "f37", "hisi-f37", 0x40 }, -}; - -static void fh_create_sensor(const char *name, I2CBus *bus) -{ - DeviceState *dev; - int n; - - if (!name || !strcmp(name, "none")) { - return; - } - for (n = 0; n < ARRAY_SIZE(fh_sensors); n++) { - if (!strcmp(name, fh_sensors[n].name)) { - break; - } - } - if (n == ARRAY_SIZE(fh_sensors)) { - error_report("fullhan: unknown sensor '%s' (none, gc4653, gc2053, " - "gc2063, gc2093, gc1054, mis2006, f37)", name); - exit(1); - } - if (fh_sensors[n].type) { - i2c_slave_create_simple(bus, fh_sensors[n].type, fh_sensors[n].addr); - return; - } - dev = DEVICE(i2c_slave_new("galaxycore-sensor", fh_sensors[n].addr)); - qdev_prop_set_bit(dev, "reg16", fh_sensors[n].reg16); - qdev_prop_set_uint16(dev, "id-reg", fh_sensors[n].id_reg); - qdev_prop_set_uint16(dev, "id", fh_sensors[n].id); - i2c_slave_realize_and_unref(I2C_SLAVE(dev), bus, &error_fatal); -} - /* Interrupt n of the SoC's numbering: DW ICTL input n, or GIC SPI n. */ static qemu_irq fh_irq(FhMachineState *fms, int n) { @@ -826,8 +782,8 @@ static void fullhan_common_init(MachineState *machine, const FhSoCConfig *c) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, fh_irq(fms, c->i2c_irq[n])); if (n == 0) { - fh_create_sensor(fms->sensor ? fms->sensor : c->sensor, - (I2CBus *)qdev_get_child_bus(dev, "i2c")); + image_sensor_create(I2C_BUS(qdev_get_child_bus(dev, "i2c")), + fms->sensor ? fms->sensor : c->sensor); } } diff --git a/qemu/hw/arm/goke.c b/qemu/hw/arm/goke.c index 2128bff1..beff1dba 100644 --- a/qemu/hw/arm/goke.c +++ b/qemu/hw/arm/goke.c @@ -24,6 +24,7 @@ #include "hw/boards.h" #include "hw/char/serial-mm.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/or-irq.h" #include "hw/irq.h" #include "hw/loader.h" @@ -284,16 +285,6 @@ static void gk_machine_set_sensor(Object *obj, const char *value, /* Sensors a GK7102 board may carry that have a model: I2C type, 7-bit * address on IDC 0. */ -static const struct { - const char *name; - const char *type; - uint8_t addr; -} gk_sensors[] = { - { "jxh42", "hisi-jxh42", 0x30 }, - { "jxh62", "hisi-jxh62", 0x30 }, - { "f37", "hisi-f37", 0x40 }, -}; - /* * The mask ROM's boot header, in CPU words (the flash holds each word * byte-swapped, see gk-sflash.c): @@ -745,21 +736,9 @@ static void goke_common_init(MachineState *machine, const GkSoCConfig *c) gk_map_model(c, dev, c->idc_bases[n], "idc", 0x1000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, gk_irq(vic, c, c->idc_irqs[n])); - if (n == 0 && gms->sensor && strcmp(gms->sensor, "none")) { - int i; - - for (i = 0; i < ARRAY_SIZE(gk_sensors); i++) { - if (!strcmp(gms->sensor, gk_sensors[i].name)) { - break; - } - } - if (i == ARRAY_SIZE(gk_sensors)) { - error_report("goke: unknown sensor '%s' (none, jxh42, " - "jxh62, f37)", gms->sensor); - exit(1); - } - i2c_slave_create_simple(I2C_BUS(qdev_get_child_bus(dev, "i2c")), - gk_sensors[i].type, gk_sensors[i].addr); + if (n == 0) { + image_sensor_create(I2C_BUS(qdev_get_child_bus(dev, "i2c")), + gms->sensor); } } diff --git a/qemu/hw/arm/grainmedia.c b/qemu/hw/arm/grainmedia.c index a4ada343..c80d1055 100644 --- a/qemu/hw/arm/grainmedia.c +++ b/qemu/hw/arm/grainmedia.c @@ -82,6 +82,7 @@ #include "hw/boards.h" #include "hw/char/serial-mm.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/misc/unimp.h" @@ -215,16 +216,6 @@ static const struct { * (fisp_.ko) probe them: 16-bit register addresses, the ID in * two registers. */ -static const struct { - const char *name; - uint8_t addr; - uint16_t id_reg; - uint16_t id; -} gm_sensors[] = { - { "ov2715", 0x36, 0x300A, 0x2715 }, /* the Longse cameras' */ - { "ov9715", 0x30, 0x300A, 0x9711 }, -}; - struct GmMachineState { MachineState parent_obj; char *flash_file; @@ -275,31 +266,6 @@ static void gm_machine_set_sensor(Object *obj, const char *value, s->sensor = g_strdup(value); } -static void gm_create_sensor(const char *name, I2CBus *bus) -{ - DeviceState *dev; - int i; - - if (!name || !strcmp(name, "none")) { - return; - } - for (i = 0; i < ARRAY_SIZE(gm_sensors); i++) { - if (!strcmp(name, gm_sensors[i].name)) { - break; - } - } - if (i == ARRAY_SIZE(gm_sensors)) { - error_report("grainmedia: unknown sensor '%s'", name); - exit(1); - } - dev = qdev_new("galaxycore-sensor"); - qdev_prop_set_bit(dev, "reg16", true); - qdev_prop_set_uint16(dev, "id-reg", gm_sensors[i].id_reg); - qdev_prop_set_uint16(dev, "id", gm_sensors[i].id); - qdev_prop_set_uint8(dev, "address", gm_sensors[i].addr); - qdev_realize_and_unref(dev, BUS(bus), &error_fatal); -} - static qemu_irq gm_irq(GmMachineState *gms, int n) { return qdev_get_gpio_in(gms->intc, n); @@ -659,7 +625,7 @@ static void grainmedia_common_init(MachineState *machine, dev = sysbus_create_simple("gm-i2c", GM_I2C_BASE, gm_irq(gms, GM_IRQ_I2C)); - gm_create_sensor(gms->sensor, (I2CBus *)qdev_get_child_bus(dev, "i2c")); + image_sensor_create(I2C_BUS(qdev_get_child_bus(dev, "i2c")), gms->sensor); binfo.ram_size = machine->ram_size; binfo.loader_start = 0; diff --git a/qemu/hw/arm/hisilicon.c b/qemu/hw/arm/hisilicon.c index e2e3bc1e..39582f9d 100644 --- a/qemu/hw/arm/hisilicon.c +++ b/qemu/hw/arm/hisilicon.c @@ -43,6 +43,7 @@ #include "system/block-backend.h" #include "net/net.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/clock.h" #include "hw/qdev-clock.h" #include "target/arm/cpu-qom.h" @@ -6056,90 +6057,8 @@ static void hisilicon_common_init(MachineState *machine, DeviceState *sensor = qdev_new("hisi-imx122"); qdev_realize_and_unref(sensor, ssi_bus, &error_fatal); } else if (sensor_name && c->num_i2c > 0 && i2c_devs[0]) { - BusState *i2c_bus = qdev_get_child_bus(i2c_devs[0], "i2c"); - DeviceState *sensor = NULL; - uint8_t i2c_addr = 0; - - if (!strcmp(sensor_name, "imx335")) { - sensor = qdev_new("hisi-imx335"); - i2c_addr = 0x1A; - } else if (!strcmp(sensor_name, "imx307")) { - sensor = qdev_new("hisi-imx307"); - i2c_addr = 0x1A; - } else if (!strcmp(sensor_name, "imx291")) { - sensor = qdev_new("hisi-imx291"); - i2c_addr = 0x1A; - } else if (!strcmp(sensor_name, "imx415")) { - sensor = qdev_new("hisi-imx415"); - i2c_addr = 0x1A; - } else if (!strcmp(sensor_name, "imx385")) { - sensor = qdev_new("hisi-imx385"); - i2c_addr = 0x1A; - } else if (!strcmp(sensor_name, "f37")) { - sensor = qdev_new("hisi-f37"); - i2c_addr = 0x40; - } else if (!strcmp(sensor_name, "gc2053")) { - sensor = qdev_new("hisi-gc2053"); - i2c_addr = 0x37; - } else if (!strcmp(sensor_name, "sp2305")) { - sensor = qdev_new("hisi-sp2305"); - i2c_addr = 0x3C; - } else if (!strcmp(sensor_name, "mis2006")) { - sensor = qdev_new("hisi-mis2006"); - i2c_addr = 0x30; - } else if (!strcmp(sensor_name, "nvp6124b")) { - sensor = qdev_new("hisi-nvp6124b"); - i2c_addr = 0x60; /* 7-bit; vendor uses 0x60..0x66 */ - } else if (!strcmp(sensor_name, "sc2315e")) { - sensor = qdev_new("hisi-smartsens"); - qdev_prop_set_uint8(sensor, "id_high", 0x22); - qdev_prop_set_uint8(sensor, "id_low", 0x38); - i2c_addr = 0x30; - } else if (!strcmp(sensor_name, "sc2315")) { - sensor = qdev_new("hisi-smartsens"); - qdev_prop_set_uint8(sensor, "id_high", 0x23); - qdev_prop_set_uint8(sensor, "id_low", 0x11); - i2c_addr = 0x30; - } else if (!strcmp(sensor_name, "sc2235p")) { - sensor = qdev_new("hisi-smartsens"); - qdev_prop_set_uint8(sensor, "id_high", 0x22); - qdev_prop_set_uint8(sensor, "id_low", 0x32); - qdev_prop_set_uint8(sensor, "disc", 0x01); - i2c_addr = 0x30; - } else if (!strcmp(sensor_name, "sc2235e")) { - sensor = qdev_new("hisi-smartsens"); - qdev_prop_set_uint8(sensor, "id_high", 0x22); - qdev_prop_set_uint8(sensor, "id_low", 0x32); - qdev_prop_set_uint8(sensor, "disc", 0x20); - i2c_addr = 0x30; - } else if (!strcmp(sensor_name, "sc2335")) { - sensor = qdev_new("hisi-smartsens"); - qdev_prop_set_uint8(sensor, "id_high", 0xCB); - qdev_prop_set_uint8(sensor, "id_low", 0x14); - i2c_addr = 0x30; - } else if (!strcmp(sensor_name, "sc2239")) { - sensor = qdev_new("hisi-smartsens"); - qdev_prop_set_uint8(sensor, "id_high", 0xCB); - qdev_prop_set_uint8(sensor, "id_low", 0x10); - i2c_addr = 0x30; - } else if (!strcmp(sensor_name, "sc307h")) { - sensor = qdev_new("hisi-smartsens"); - qdev_prop_set_uint8(sensor, "id_high", 0xCB); - qdev_prop_set_uint8(sensor, "id_low", 0x1C); - i2c_addr = 0x30; - } else { - error_report("Unknown sensor '%s' (supported: imx291, " - "imx307, imx335, imx385, imx415, f37, " - "gc2053, sp2305, mis2006, nvp6124b, " - "sc2235p, sc2235e, sc2315, sc2315e, sc2335, " - "sc2239, sc307h, imx122, imx222; or 'none' " - "to disable default)", - sensor_name); - exit(1); - } - - qdev_prop_set_uint8(sensor, "address", i2c_addr); - qdev_realize_and_unref(sensor, i2c_bus, &error_fatal); + image_sensor_create(I2C_BUS(qdev_get_child_bus(i2c_devs[0], "i2c")), + sensor_name); } else if (sensor_name) { error_report("sensor '%s' requested but this SoC has no I2C " "or matching SPI controller", sensor_name); diff --git a/qemu/hw/arm/novatek.c b/qemu/hw/arm/novatek.c index 320a232f..6aa44609 100644 --- a/qemu/hw/arm/novatek.c +++ b/qemu/hw/arm/novatek.c @@ -26,6 +26,7 @@ #include "hw/loader.h" #include "hw/char/serial-mm.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/ide/ahci.h" #include "hw/ide/ahci-sysbus.h" #include "hw/intc/arm_gic.h" @@ -152,18 +153,10 @@ static const struct { }; /* - * Image sensors for sensor=: SmartSens parts the OpenIPC release has a - * driver for (nvt_sen_.ko), at 0x30 on I2C 0 where ipcinfo's - * Novatek HAL probes (ssens_addrs 0x60, 8-bit), chip ID at 0x3107/0x3108. + * sensor= on I2C 0, where ipcinfo's Novatek HAL probes: the catalogue's + * parts, of which OpenIPC's firmware has a driver (nvt_sen_.ko) for + * sc401ai and sc501ai. */ -static const struct { - const char *name; - uint8_t id_high, id_low; -} nvt_sensors[] = { - { "sc401ai", 0xCD, 0x2E }, - { "sc501ai", 0xCE, 0x1F }, -}; - /* * The SD hosts: card n from -drive if=sd,index=n. reset is the host's * nvt-cg "sys-reset" line (CG_SYS_RESET_REG1 bit 2 / 3 / 14). The EVB @@ -1786,22 +1779,7 @@ static void novatek_common_init(MachineState *machine, const NvtSoCConfig *c) i2c0 = (I2CBus *)qdev_get_child_bus(dev, "i2c"); } } - if (nms->sensor && strcmp(nms->sensor, "none")) { - for (n = 0; n < ARRAY_SIZE(nvt_sensors); n++) { - if (!strcmp(nms->sensor, nvt_sensors[n].name)) { - break; - } - } - if (n == ARRAY_SIZE(nvt_sensors)) { - error_report("novatek: unknown sensor '%s' (none, sc401ai, " - "sc501ai)", nms->sensor); - exit(1); - } - dev = DEVICE(i2c_slave_new("hisi-smartsens", 0x30)); - qdev_prop_set_uint8(dev, "id_high", nvt_sensors[n].id_high); - qdev_prop_set_uint8(dev, "id_low", nvt_sensors[n].id_low); - i2c_slave_realize_and_unref(I2C_SLAVE(dev), i2c0, &error_fatal); - } + image_sensor_create(i2c0, nms->sensor); /* * USB: the EHCI (region 0) over the FOTG200 glue (region 1), at the diff --git a/qemu/hw/arm/sigmastar.c b/qemu/hw/arm/sigmastar.c index cd341c54..8546fb7d 100644 --- a/qemu/hw/arm/sigmastar.c +++ b/qemu/hw/arm/sigmastar.c @@ -25,6 +25,7 @@ #include "hw/boards.h" #include "hw/cpu/a15mpcore.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/ide/ahci.h" #include "hw/ide/ahci-sysbus.h" #include "hw/irq.h" @@ -311,22 +312,6 @@ static const SsSoCConfig msc313e_soc = { .default_sensor = "jxf22", }; -/* - * Image sensors for sensor=, at their address on the die's sensor bus - * (miic_sensor_bus). SmartSens parts are hisi-smartsens with the ID in - * 0x3107/0x3108 (ssc375: the DTB's sensorif snr0_mipi_i2c = <1>, as the - * board's sc431hai_mipi.ko compares it); SOI parts have their own types. - */ -static const struct { - const char *name; - const char *type; - uint8_t addr; - uint8_t id_high, id_low; -} ss_sensors[] = { - { "sc431hai", "hisi-smartsens", 0x30, 0xCD, 0x6B }, - { "jxf22", "hisi-jxf22", 0x40 }, -}; - #define TYPE_SS_MACHINE MACHINE_TYPE_NAME("sigmastar") OBJECT_DECLARE_SIMPLE_TYPE(SsMachineState, SS_MACHINE) @@ -336,6 +321,7 @@ struct SsMachineState { char *flash_file; uint32_t flash_jedec; char *sensor; + char *rtc; uint64_t uuid; bool night; DeviceState *i3_gpio; @@ -365,6 +351,19 @@ static void ss_machine_set_flash_file(Object *obj, const char *value, s->flash_file = g_strdup(value); } +static char *ss_machine_get_rtc(Object *obj, Error **errp) +{ + return g_strdup(SS_MACHINE(obj)->rtc); +} + +static void ss_machine_set_rtc(Object *obj, const char *value, Error **errp) +{ + SsMachineState *s = SS_MACHINE(obj); + + g_free(s->rtc); + s->rtc = g_strdup(value); +} + static char *ss_machine_get_sensor(Object *obj, Error **errp) { return g_strdup(SS_MACHINE(obj)->sensor); @@ -1044,35 +1043,31 @@ static void sigmastar_common_init(MachineState *machine, const SsSoCConfig *c) ss_spi(gic, intc, spi[n])); bus[n] = I2C_BUS(qdev_get_child_bus(dev, "i2c")); } - if (sensor && strcmp(sensor, "none")) { - for (n = 0; n < ARRAY_SIZE(ss_sensors); n++) { - if (!strcmp(sensor, ss_sensors[n].name)) { - break; - } - } - if (n == ARRAY_SIZE(ss_sensors)) { - error_report("sigmastar: unknown sensor '%s' (none, " - "sc431hai, jxf22)", sensor); + /* + * A board's battery RTC, on the first bus: the Raysharp app reads + * a DS1307-family part at 0xD0 (8-bit), which the SoC has no twin of. + */ + if (sms->rtc && strcmp(sms->rtc, "none")) { + if (strcmp(sms->rtc, "ds1338")) { + error_report("sigmastar: unknown rtc '%s' (none, ds1338)", + sms->rtc); exit(1); } - dev = DEVICE(i2c_slave_new(ss_sensors[n].type, - ss_sensors[n].addr)); - if (ss_sensors[n].id_high || ss_sensors[n].id_low) { - qdev_prop_set_uint8(dev, "id_high", ss_sensors[n].id_high); - qdev_prop_set_uint8(dev, "id_low", ss_sensors[n].id_low); - } - i2c_slave_realize_and_unref(I2C_SLAVE(dev), - bus[c->miic_sensor_bus], - &error_fatal); - /* the infinity3 ISP renders its picture from the sensor's */ - if (i3_isp) { - object_property_set_link(OBJECT(i3_isp), "sensor", - OBJECT(dev), &error_fatal); - } + i2c_slave_create_simple(bus[0], "ds1338", 0x68); + } + + dev = image_sensor_create(bus[c->miic_sensor_bus], sensor); + /* the infinity3 ISP renders its picture from the sensor's */ + if (dev && i3_isp) { + object_property_set_link(OBJECT(i3_isp), "sensor", OBJECT(dev), + &error_fatal); } } else if (sms->sensor && strcmp(sms->sensor, "none")) { error_report("sigmastar: this machine has no sensor bus"); exit(1); + } else if (sms->rtc && strcmp(sms->rtc, "none")) { + error_report("sigmastar: this machine has no I2C bus for an rtc"); + exit(1); } /* @@ -1248,10 +1243,16 @@ static void ss_machine_class_init(ObjectClass *oc, const void *data) object_class_property_set_description(oc, "flash-jedec", "JEDEC ID the SPI NOR answers with (e.g. 0xEF4018); by default " "the board's part, its capacity byte taken from the image size"); + object_class_property_add_str(oc, "rtc", + ss_machine_get_rtc, ss_machine_set_rtc); + object_class_property_set_description(oc, "rtc", + "Battery RTC on the first I2C bus: ds1338 (a DS1307 at 0x68), " + "or none, the default"); object_class_property_add_str(oc, "sensor", ss_machine_get_sensor, ss_machine_set_sensor); object_class_property_set_description(oc, "sensor", - "Image sensor on the sensor I2C bus (ssc375: sc431hai, the " + "Image sensor on the sensor I2C bus, by its catalogue name " + "(hw/i2c/image-sensor.c; ssc375: sc431hai, the " "default; msc313e: jxf22, the default; or none)"); object_class_property_add_str(oc, "daynight", ss_machine_get_daynight, ss_machine_set_daynight); diff --git a/qemu/hw/arm/xiongmai.c b/qemu/hw/arm/xiongmai.c index 21d845af..6c69639d 100644 --- a/qemu/hw/arm/xiongmai.c +++ b/qemu/hw/arm/xiongmai.c @@ -27,6 +27,7 @@ #include "hw/sd/sd.h" #include "hw/irq.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/sysbus.h" #include "qobject/qlist.h" #include "target/arm/arm-powerctl.h" @@ -491,6 +492,11 @@ static void xm_attach_sensors(const XmSoCConfig *c, XmMachineState *ms, if (!type || !strcmp(type, "none")) { return; } + if (image_sensor_known(ms->sensor)) { /* sensor= the catalogue's name */ + image_sensor_create_at(bus, ms->sensor, + ms->sensor_addr_set ? ms->sensor_addr : 0); + return; + } if (!object_class_dynamic_cast(object_class_by_name(type), TYPE_I2C_SLAVE)) { error_report("sensor=%s: not an I2C device model", type); diff --git a/qemu/hw/i2c/galaxycore.c b/qemu/hw/i2c/galaxycore.c index 40f9399e..eb4e8c38 100644 --- a/qemu/hw/i2c/galaxycore.c +++ b/qemu/hw/i2c/galaxycore.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/i2c/ingenic-sensor.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -142,6 +143,15 @@ bool galaxycore_exposure(Object *sensor, IngenicSensorExposure *e) return true; } +static void galaxycore_exposure_sensor(Object *sensor, ImageSensorExposure *e) +{ + IngenicSensorExposure x = { 0 }; + + galaxycore_exposure(sensor, &x); + e->lines = x.lines; + e->gain = x.gain; +} + static void gc_reset(DeviceState *dev) { GcSensorState *s = GALAXYCORE_SENSOR(dev); @@ -188,6 +198,7 @@ static void gc_class_init(ObjectClass *klass, const void *data) k->event = gc_event; k->send = gc_send; k->recv = gc_recv; + IMAGE_SENSOR_CLASS(klass)->exposure = galaxycore_exposure_sensor; } static const TypeInfo gc_sensor_info = { @@ -195,6 +206,10 @@ static const TypeInfo gc_sensor_info = { .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(GcSensorState), .class_init = gc_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_IMAGE_SENSOR }, + { } + }, }; static void gc_sensor_register_types(void) diff --git a/qemu/hw/i2c/hisi-f37.c b/qemu/hw/i2c/hisi-f37.c index 9b137eb9..fa2a9d69 100644 --- a/qemu/hw/i2c/hisi-f37.c +++ b/qemu/hw/i2c/hisi-f37.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/i2c/hisi-soi.h" #include "qemu/log.h" #include "qemu/module.h" @@ -113,6 +114,18 @@ bool hisi_soi_exposure(Object *sensor, HisiSoiExposure *e) return true; } +static void hisi_f37_sensor_exposure(Object *sensor, ImageSensorExposure *e) +{ + HisiSoiExposure soi; + + hisi_soi_exposure(sensor, &soi); + e->lines = soi.lines; + e->vts = soi.vts; + e->gain = soi.gain; + e->width = soi.width; + e->height = soi.height; +} + static void hisi_f37_reset(DeviceState *dev) { HisiF37State *s = HISI_F37(dev); @@ -132,6 +145,7 @@ static void hisi_f37_class_init(ObjectClass *klass, const void *data) k->recv = hisi_f37_recv; k->send = hisi_f37_send; HISI_F37_CLASS(klass)->id = 0x0F37; + IMAGE_SENSOR_CLASS(klass)->exposure = hisi_f37_sensor_exposure; } static void hisi_jxh62_class_init(ObjectClass *klass, const void *data) @@ -149,6 +163,11 @@ static void hisi_jxf22_class_init(ObjectClass *klass, const void *data) HISI_F37_CLASS(klass)->id = 0x0F22; } +static const InterfaceInfo hisi_f37_interfaces[] = { + { TYPE_IMAGE_SENSOR }, + { } +}; + static void hisi_f37_register_types(void) { static const TypeInfo types[] = { @@ -158,6 +177,7 @@ static void hisi_f37_register_types(void) .instance_size = sizeof(HisiF37State), .class_size = sizeof(HisiSoiClass), .class_init = hisi_f37_class_init, + .interfaces = hisi_f37_interfaces, }, { .name = TYPE_HISI_JXH62, .parent = TYPE_HISI_F37, diff --git a/qemu/hw/i2c/hisi-gc2053.c b/qemu/hw/i2c/hisi-gc2053.c deleted file mode 100644 index 9deeacea..00000000 --- a/qemu/hw/i2c/hisi-gc2053.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * GalaxyCore GC2053 image sensor I2C stub. - * - * Sofia (XiongMai) detects GC2053 by: - * 1. Setting I2C addr 0x6E (8-bit) = 0x37 (7-bit slave addr) - * 2. Reading reg 0xF0 (high byte) and 0xF1 (low byte) — 8-bit reg, 8-bit data - * 3. Checking (high << 8) | low == 0x2053 - * - * Returning 0x20 at 0xF0 and 0x53 at 0xF1 satisfies the probe. - * - * Copyright (c) 2026 OpenIPC. - * Written by Dmitry Ilyin - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "hw/i2c/i2c.h" -#include "qemu/log.h" -#include "qemu/module.h" - -#define TYPE_HISI_GC2053 "hisi-gc2053" -OBJECT_DECLARE_SIMPLE_TYPE(HisiGC2053State, HISI_GC2053) - -#define GC2053_REG_COUNT 0x100 - -struct HisiGC2053State { - I2CSlave parent_obj; - uint8_t cur_reg; - bool have_reg; - uint8_t regs[GC2053_REG_COUNT]; -}; - -static void hisi_gc2053_reset_regs(HisiGC2053State *s) -{ - memset(s->regs, 0, sizeof(s->regs)); - /* Sofia checks regs 0xF0:0xF1 → expects 0x2053 */ - s->regs[0xF0] = 0x20; - s->regs[0xF1] = 0x53; -} - -static int hisi_gc2053_event(I2CSlave *i2c, enum i2c_event event) -{ - HisiGC2053State *s = HISI_GC2053(i2c); - - if (event == I2C_START_SEND) { - s->have_reg = false; - } - return 0; -} - -static int hisi_gc2053_send(I2CSlave *i2c, uint8_t data) -{ - HisiGC2053State *s = HISI_GC2053(i2c); - - if (!s->have_reg) { - s->cur_reg = data; - s->have_reg = true; - } else { - s->regs[s->cur_reg] = data; - s->cur_reg++; - } - return 0; -} - -static uint8_t hisi_gc2053_recv(I2CSlave *i2c) -{ - HisiGC2053State *s = HISI_GC2053(i2c); - uint8_t val = s->regs[s->cur_reg]; - s->cur_reg++; - return val; -} - -static void hisi_gc2053_reset(DeviceState *dev) -{ - HisiGC2053State *s = HISI_GC2053(dev); - - s->cur_reg = 0; - s->have_reg = false; - hisi_gc2053_reset_regs(s); -} - -static void hisi_gc2053_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - device_class_set_legacy_reset(dc, hisi_gc2053_reset); - k->event = hisi_gc2053_event; - k->recv = hisi_gc2053_recv; - k->send = hisi_gc2053_send; -} - -static const TypeInfo hisi_gc2053_info = { - .name = TYPE_HISI_GC2053, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(HisiGC2053State), - .class_init = hisi_gc2053_class_init, -}; - -static void hisi_gc2053_register_types(void) -{ - type_register_static(&hisi_gc2053_info); -} - -type_init(hisi_gc2053_register_types) diff --git a/qemu/hw/i2c/hisi-imx307.c b/qemu/hw/i2c/hisi-imx307.c index bf397e6d..4a4cb2b4 100644 --- a/qemu/hw/i2c/hisi-imx307.c +++ b/qemu/hw/i2c/hisi-imx307.c @@ -17,8 +17,10 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "qemu/log.h" #include "qemu/module.h" +#include #define TYPE_HISI_IMX307 "hisi-imx307" OBJECT_DECLARE_SIMPLE_TYPE(HisiIMX307State, HISI_IMX307) @@ -39,6 +41,9 @@ static void hisi_imx307_reset_regs(HisiIMX307State *s) memset(s->regs, 0, sizeof(s->regs)); /* Sofia checks reg 0x31DC: (val & 6) == 4 → bit 2 set, bit 1 clear */ s->regs[0x31DC - IMX307_REG_BASE] = 0x04; + /* the MStar driver's Sensor_id_table (libdrv_ms_cus_imx307_MIPI.so) */ + s->regs[0x3004 - IMX307_REG_BASE] = 0x10; + s->regs[0x3009 - IMX307_REG_BASE] = 0x01; } static int hisi_imx307_event(I2CSlave *i2c, enum i2c_event event) @@ -91,6 +96,26 @@ static uint8_t hisi_imx307_recv(I2CSlave *i2c) return val; } +/* + * VMAX in 0x3018-A, SHS1 (rows from the frame's end the exposure starts) + * in 0x3020-2, the gain in 0x3014 in 0.3 dB steps, the output size in + * 0x3472/3 and 0x3418/9: the datasheet's little-endian register groups. + */ +static void hisi_imx307_exposure(Object *sensor, ImageSensorExposure *e) +{ + HisiIMX307State *s = HISI_IMX307(sensor); + uint32_t shs; + +#define R(a) s->regs[(a) - IMX307_REG_BASE] + e->vts = (R(0x301A) & 3) << 16 | R(0x3019) << 8 | R(0x3018); + shs = (R(0x3022) & 0xF) << 16 | R(0x3021) << 8 | R(0x3020); + e->lines = e->vts > shs + 1 ? e->vts - shs - 1 : 1; + e->gain = 1024 * pow(10, R(0x3014) * 0.3 / 20); + e->width = (R(0x3473) & 0x1F) << 8 | R(0x3472); + e->height = (R(0x3419) & 0x1F) << 8 | R(0x3418); +#undef R +} + static void hisi_imx307_reset(DeviceState *dev) { HisiIMX307State *s = HISI_IMX307(dev); @@ -110,6 +135,7 @@ static void hisi_imx307_class_init(ObjectClass *klass, const void *data) k->event = hisi_imx307_event; k->recv = hisi_imx307_recv; k->send = hisi_imx307_send; + IMAGE_SENSOR_CLASS(klass)->exposure = hisi_imx307_exposure; } static const TypeInfo hisi_imx307_info = { @@ -117,6 +143,10 @@ static const TypeInfo hisi_imx307_info = { .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(HisiIMX307State), .class_init = hisi_imx307_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_IMAGE_SENSOR }, + { } + }, }; static void hisi_imx307_register_types(void) diff --git a/qemu/hw/i2c/hisi-smartsens.c b/qemu/hw/i2c/hisi-smartsens.c index ae667ee1..3384faf8 100644 --- a/qemu/hw/i2c/hisi-smartsens.c +++ b/qemu/hw/i2c/hisi-smartsens.c @@ -33,6 +33,7 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/i2c/ingenic-sensor.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -52,6 +53,7 @@ struct HisiSmartSensState { uint8_t id_high; uint8_t id_low; uint8_t disc; + uint8_t gain_fine; /* 0x3E09 at 1x; 0: the SC2336's gain registers */ /* I2C state */ uint8_t reg_buf[2]; /* register address accumulator (big-endian) */ @@ -149,6 +151,44 @@ bool smartsens_exposure(Object *sensor, IngenicSensorExposure *e) return true; } +/* + * The frame around that exposure: 0x320E/F the frame length, 0x3208-B the + * output window, as every SmartSens driver's mode tables set them. + */ +static void smartsens_sensor_exposure(Object *sensor, ImageSensorExposure *e) +{ + HisiSmartSensState *s = HISI_SMARTSENS(sensor); + IngenicSensorExposure x = { 0 }; + +#define R(a) s->regs[(a) - SS_REG_BASE] +#define R16(a) (R(a) << 8 | R((a) + 1)) + smartsens_exposure(sensor, &x); + e->lines = x.lines; + e->gain = x.gain; + if (s->gain_fine) { + /* + * The SC2310's pCus_GetAEGain, with the half rows its + * pCus_SetAEUSecs writes (capped at twice the frame less 6): + * coarse 0x3E08[4:2] + 1 (bit 5: HCG, x2.72), fine 0x3E09, digital + * 0x3E07 / 0x80 and 0x3E06[3:0] + 1. + */ + uint8_t fine = R(0x3E09) ? R(0x3E09) : s->gain_fine; + uint8_t dig = R(0x3E07) ? R(0x3E07) : 0x80; + double gain = (((R(0x3E08) >> 2) & 7) + 1) * + ((double)fine / s->gain_fine) * (dig / 128.0) * + ((R(0x3E06) & 0xF) + 1) * + (R(0x3E08) & 0x20 ? 2.72 : 1.0); + + e->lines = x.lines / 2; + e->gain = gain * 1024; + } + e->vts = R16(0x320E); + e->width = R16(0x3208); + e->height = R16(0x320A); +#undef R16 +#undef R +} + static void hisi_smartsens_reset(DeviceState *dev) { HisiSmartSensState *s = HISI_SMARTSENS(dev); @@ -162,6 +202,7 @@ static void hisi_smartsens_reset(DeviceState *dev) static const Property hisi_smartsens_props[] = { DEFINE_PROP_UINT8("id_high", HisiSmartSensState, id_high, 0x00), DEFINE_PROP_UINT8("id_low", HisiSmartSensState, id_low, 0x00), + DEFINE_PROP_UINT8("gain-fine", HisiSmartSensState, gain_fine, 0), DEFINE_PROP_UINT8("disc", HisiSmartSensState, disc, 0x00), }; @@ -191,6 +232,7 @@ static void hisi_smartsens_class_init(ObjectClass *klass, const void *data) k->event = hisi_smartsens_event; k->recv = hisi_smartsens_recv; k->send = hisi_smartsens_send; + IMAGE_SENSOR_CLASS(klass)->exposure = smartsens_sensor_exposure; } static const TypeInfo hisi_smartsens_info = { @@ -198,6 +240,10 @@ static const TypeInfo hisi_smartsens_info = { .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(HisiSmartSensState), .class_init = hisi_smartsens_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_IMAGE_SENSOR }, + { } + }, }; static void hisi_smartsens_register_types(void) diff --git a/qemu/hw/i2c/image-sensor.c b/qemu/hw/i2c/image-sensor.c new file mode 100644 index 00000000..4a3ce594 --- /dev/null +++ b/qemu/hw/i2c/image-sensor.c @@ -0,0 +1,177 @@ +/* + * The image sensor catalogue: every sensor= name any machine takes, with + * the model that answers for it, its usual 7-bit I2C address and the chip + * ID its driver probes for. A sensor is the same part on any SoC, so a + * machine names one here instead of keeping a table of its own. + * + * The IDs come from the vendor sensor drivers (Sensor_id_table in the + * MStar libdrv_ms_cus_*.so; the Ingenic and FullHan SDKs; Sofia). + * + * Copyright (c) 2026 OpenIPC. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "hw/i2c/image-sensor.h" +#include "hw/qdev-properties.h" + +#define GALAXYCORE "galaxycore-sensor" +#define SMARTSENS "hisi-smartsens" +#define OV2735 "ingenic-ov2735" + +typedef struct ImageSensorSpec { + const char *name; + const char *type; + uint8_t addr; + uint16_t id; /* GALAXYCORE, SMARTSENS; OV2735: its revision */ + uint16_t id_reg; /* GALAXYCORE: the ID's high byte */ + bool reg16; /* GALAXYCORE: 16-bit register addresses */ + uint8_t disc; /* SMARTSENS: 0x3109 where parts share an ID */ + uint8_t gain_fine; /* SMARTSENS: 0x3E09 at 1x, on the SC2310's layout */ +} ImageSensorSpec; + +/* GalaxyCore's register pairs; OmniVision's OV27x5/OV97x5 have the same */ +#define GC8(n, a, i) { n, GALAXYCORE, a, i, 0xF0, false } +#define GC16(n, a, i) { n, GALAXYCORE, a, i, 0x03F0, true } +#define OV16(n, a, i) { n, GALAXYCORE, a, i, 0x300A, true } +#define SC(n, i) { n, SMARTSENS, 0x30, i } +#define SC_DISC(n, i, d) { n, SMARTSENS, 0x30, i, .disc = d } + +static const ImageSensorSpec image_sensors[] = { + /* Sony */ + { "imx291", "hisi-imx291", 0x1A }, + { "imx307", "hisi-imx307", 0x1A }, + { "imx323", "imx323", 0x1A }, + { "imx335", "hisi-imx335", 0x1A }, + { "imx385", "hisi-imx385", 0x1A }, + { "imx415", "hisi-imx415", 0x1A }, + + /* SOI / JX */ + { "f37", "hisi-f37", 0x40 }, + { "jxf37", "hisi-f37", 0x40 }, + { "jxf22", "hisi-jxf22", 0x40 }, + { "jxh42", "hisi-jxh42", 0x30 }, + { "jxh62", "hisi-jxh62", 0x30 }, + + /* GalaxyCore */ + GC8("gc1054", 0x21, 0x1054), + GC8("gc2053", 0x37, 0x2053), + GC8("gc2053s1", 0x3F, 0x2053), /* T23 SDK's second sensor */ + GC8("gc2063", 0x37, 0x2063), + GC16("gc2083", 0x37, 0x2083), + GC16("gc2093", 0x37, 0x2093), + GC16("gc4653", 0x29, 0x4653), + GC16("gc4663", 0x29, 0x4653), + + /* SmartSens */ + SC("sc2235", 0x2235), + SC_DISC("sc2235e", 0x2232, 0x20), + SC_DISC("sc2235p", 0x2232, 0x01), + SC("sc2239", 0xCB10), + { "sc2310", SMARTSENS, 0x30, 0x2311, .gain_fine = 0x20 }, + { "sc2315", SMARTSENS, 0x30, 0x2311, .gain_fine = 0x20 }, + { "sc2315e", SMARTSENS, 0x30, 0x2238, .disc = 0x20, .gain_fine = 0x10 }, + SC("sc2335", 0xCB14), + SC("sc2336", 0xCB3A), + SC("sc307h", 0xCB1C), + SC("sc401ai", 0xCD2E), + SC("sc431hai", 0xCD6B), + SC("sc501ai", 0xCE1F), + SC("sc5035", 0x5035), + SC("sc5238", 0x5235), + + /* OmniVision */ + OV16("ov2715", 0x36, 0x2715), + OV16("ov9715", 0x30, 0x9711), + { "ov2735", OV2735, 0x3C, 0x05 }, + { "ov2735b", OV2735, 0x3C, 0x07 }, + + /* others */ + { "mis2006", "hisi-mis2006", 0x30 }, + { "sp2305", "hisi-sp2305", 0x3C }, + { "nvp6124b", "hisi-nvp6124b", 0x60 }, /* a decoder in the sensor's place */ +}; + +static const ImageSensorSpec *image_sensor_find(const char *name) +{ + int n; + + for (n = 0; n < ARRAY_SIZE(image_sensors); n++) { + if (!strcmp(name, image_sensors[n].name)) { + return &image_sensors[n]; + } + } + return NULL; +} + +bool image_sensor_known(const char *name) +{ + return name && image_sensor_find(name); +} + +DeviceState *image_sensor_create_at(I2CBus *bus, const char *name, + uint8_t addr) +{ + const ImageSensorSpec *spec; + DeviceState *dev; + int n; + + if (!name || !strcmp(name, "none")) { + return NULL; + } + spec = image_sensor_find(name); + if (!spec) { + error_report("unknown sensor '%s'; one of: none", name); + for (n = 0; n < ARRAY_SIZE(image_sensors); n++) { + error_printf(" %s\n", image_sensors[n].name); + } + exit(1); + } + dev = DEVICE(i2c_slave_new(spec->type, addr ? addr : spec->addr)); + if (!strcmp(spec->type, GALAXYCORE)) { + qdev_prop_set_bit(dev, "reg16", spec->reg16); + qdev_prop_set_uint16(dev, "id-reg", spec->id_reg); + qdev_prop_set_uint16(dev, "id", spec->id); + } else if (!strcmp(spec->type, SMARTSENS)) { + qdev_prop_set_uint8(dev, "id_high", spec->id >> 8); + qdev_prop_set_uint8(dev, "id_low", spec->id & 0xFF); + qdev_prop_set_uint8(dev, "disc", spec->disc); + qdev_prop_set_uint8(dev, "gain-fine", spec->gain_fine); + } else if (!strcmp(spec->type, OV2735)) { + qdev_prop_set_uint8(dev, "rev", spec->id); + } + i2c_slave_realize_and_unref(I2C_SLAVE(dev), bus, &error_fatal); + return dev; +} + +DeviceState *image_sensor_create(I2CBus *bus, const char *name) +{ + return image_sensor_create_at(bus, name, 0); +} + +bool image_sensor_exposure(Object *sensor, ImageSensorExposure *e) +{ + if (!sensor || !object_dynamic_cast(sensor, TYPE_IMAGE_SENSOR)) { + return false; + } + memset(e, 0, sizeof(*e)); + IMAGE_SENSOR_GET_CLASS(sensor)->exposure(sensor, e); + return true; +} + +static const TypeInfo image_sensor_info = { + .name = TYPE_IMAGE_SENSOR, + .parent = TYPE_INTERFACE, + .class_size = sizeof(ImageSensorClass), +}; + +static void image_sensor_register_types(void) +{ + type_register_static(&image_sensor_info); +} + +type_init(image_sensor_register_types) diff --git a/qemu/hw/i2c/imx323.c b/qemu/hw/i2c/imx323.c new file mode 100644 index 00000000..deeedc29 --- /dev/null +++ b/qemu/hw/i2c/imx323.c @@ -0,0 +1,124 @@ +/* + * Sony IMX323: 1080p parallel sensor at 0x34 (8-bit) = 0x1A, with 16-bit + * register addresses and 8-bit data. Unlike the IMX307 family its timing + * registers sit in the SMIA space below 0x1000 as well as at 0x3000: the + * shutter at 0x0202 and the frame length at 0x0340, big-endian. All from + * libdrv_ms_cus_imx323.so (expo_reg, vts_reg, Sensor_init_table), and its + * Sensor_id_table: 0x301C reads 0x50. It reports no output window: 0x034C + * says 1984 wide, while the MStar ISP crops 1920 at x = 106 out of the + * parallel data, so that register does not bound what reaches the ISP. + * + * The driver's gain is 20 log10(g) / 0.3. That it lands in 0x301E is not + * from the driver: the register is in its init table, as the IMX family's. + * + * Copyright (c) 2026 OpenIPC. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" +#include "qemu/module.h" +#include + +#define TYPE_IMX323 "imx323" +OBJECT_DECLARE_SIMPLE_TYPE(IMX323State, IMX323) + +#define IMX323_REGS 0x4000 + +struct IMX323State { + I2CSlave parent_obj; + + uint8_t addr[2]; + uint8_t addr_len; + uint16_t reg; + uint8_t regs[IMX323_REGS]; +}; + +static int imx323_event(I2CSlave *i2c, enum i2c_event event) +{ + IMX323State *s = IMX323(i2c); + + if (event == I2C_START_SEND) { + s->addr_len = 0; + } + return 0; +} + +static int imx323_send(I2CSlave *i2c, uint8_t data) +{ + IMX323State *s = IMX323(i2c); + + if (s->addr_len < 2) { + s->addr[s->addr_len++] = data; + s->reg = s->addr[0] << 8 | s->addr[1]; + } else { + if (s->reg < IMX323_REGS) { + s->regs[s->reg] = data; + } + s->reg++; + } + return 0; +} + +static uint8_t imx323_recv(I2CSlave *i2c) +{ + IMX323State *s = IMX323(i2c); + uint8_t val = s->reg < IMX323_REGS ? s->regs[s->reg] : 0xFF; + + s->reg++; + return val; +} + +static void imx323_exposure(Object *sensor, ImageSensorExposure *e) +{ + IMX323State *s = IMX323(sensor); + +#define R16(a) (s->regs[a] << 8 | s->regs[(a) + 1]) + e->vts = R16(0x0340); + /* SHS: the rows from the frame's end, 0 the longest exposure */ + e->lines = e->vts > R16(0x0202) ? e->vts - R16(0x0202) : 1; +#undef R16 + e->gain = 1024 * pow(10, s->regs[0x301E] * 0.3 / 20); +} + +static void imx323_reset(DeviceState *dev) +{ + IMX323State *s = IMX323(dev); + + s->addr_len = 0; + s->reg = 0; + memset(s->regs, 0, sizeof(s->regs)); + s->regs[0x301C] = 0x50; +} + +static void imx323_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + device_class_set_legacy_reset(dc, imx323_reset); + k->event = imx323_event; + k->send = imx323_send; + k->recv = imx323_recv; + IMAGE_SENSOR_CLASS(klass)->exposure = imx323_exposure; +} + +static const TypeInfo imx323_info = { + .name = TYPE_IMX323, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(IMX323State), + .class_init = imx323_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_IMAGE_SENSOR }, + { } + }, +}; + +static void imx323_register_types(void) +{ + type_register_static(&imx323_info); +} + +type_init(imx323_register_types) diff --git a/qemu/hw/i2c/ingenic-ov2735.c b/qemu/hw/i2c/ingenic-ov2735.c index 8d5c7b8f..424ed3cf 100644 --- a/qemu/hw/i2c/ingenic-ov2735.c +++ b/qemu/hw/i2c/ingenic-ov2735.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/i2c/ingenic-sensor.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -73,6 +74,15 @@ bool ov2735_exposure(Object *sensor, IngenicSensorExposure *e) return true; } +static void ov2735_exposure_sensor(Object *sensor, ImageSensorExposure *e) +{ + IngenicSensorExposure x = { 0 }; + + ov2735_exposure(sensor, &x); + e->lines = x.lines; + e->gain = x.gain; +} + static int ov_event(I2CSlave *i2c, enum i2c_event event) { IngenicOv2735State *s = INGENIC_OV2735(i2c); @@ -147,6 +157,7 @@ static void ov_class_init(ObjectClass *oc, const void *data) k->event = ov_event; k->send = ov_send; k->recv = ov_recv; + IMAGE_SENSOR_CLASS(oc)->exposure = ov2735_exposure_sensor; } static const TypeInfo ov_info = { @@ -154,6 +165,10 @@ static const TypeInfo ov_info = { .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(IngenicOv2735State), .class_init = ov_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_IMAGE_SENSOR }, + { } + }, }; static void ov_register_types(void) diff --git a/qemu/hw/mips/ingenic.c b/qemu/hw/mips/ingenic.c index d8f71001..f613882e 100644 --- a/qemu/hw/mips/ingenic.c +++ b/qemu/hw/mips/ingenic.c @@ -88,6 +88,7 @@ #include "hw/boards.h" #include "hw/char/serial-mm.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/image-sensor.h" #include "hw/i2c/bitbang_i2c.h" #include "hw/clock.h" #include "hw/qdev-clock.h" @@ -915,65 +916,6 @@ static void ingenic_kernel_boot(IngenicMachineState *ims) * sensor sits on I2C0 (I2C1 on T40); MCLK and the PA18 reset pulse need * nothing here -- the models answer whether or not they are clocked. */ -static const struct { - const char *name; - const char *type; /* NULL: galaxycore-sensor */ - uint8_t addr; - bool reg16; /* galaxycore: 16-bit register addresses */ - uint16_t id_reg, id; /* galaxycore / hisi-smartsens; ov2735 rev */ -} ingenic_sensors[] = { - { "gc2053", NULL, 0x37, false, 0xF0, 0x2053 }, - /* T23 SDK's second-sensor driver: a gc2053 at 0x3f (sensor1=) */ - { "gc2053s1", NULL, 0x3F, false, 0xF0, 0x2053 }, - { "gc2083", NULL, 0x37, true, 0x03F0, 0x2083 }, - { "sc2336", "hisi-smartsens", 0x30, true, 0x3107, 0xCB3A }, - { "sc2335", "hisi-smartsens", 0x30, true, 0x3107, 0xCB14 }, - { "jxf37", "hisi-f37", 0x40 }, - { "jxf22", "hisi-jxf22", 0x40 }, - { "jxh62", "hisi-jxh62", 0x30 }, - { "gc4653", NULL, 0x29, true, 0x03F0, 0x4653 }, - { "gc4663", NULL, 0x29, true, 0x03F0, 0x4653 }, - { "ov2735b", "ingenic-ov2735", 0x3C, false, 0, 0x07 }, - { "ov2735", "ingenic-ov2735", 0x3C, false, 0, 0x05 }, -}; - -static DeviceState *ingenic_create_sensor(const char *name, I2CBus *bus) -{ - DeviceState *dev; - int n; - - for (n = 0; n < ARRAY_SIZE(ingenic_sensors); n++) { - if (!strcmp(name, ingenic_sensors[n].name)) { - break; - } - } - if (n == ARRAY_SIZE(ingenic_sensors)) { - error_report("ingenic: unknown sensor '%s'; one of: none", name); - for (n = 0; n < ARRAY_SIZE(ingenic_sensors); n++) { - error_printf(" %s\n", ingenic_sensors[n].name); - } - exit(1); - } - if (!ingenic_sensors[n].type) { - dev = DEVICE(i2c_slave_new("galaxycore-sensor", - ingenic_sensors[n].addr)); - qdev_prop_set_bit(dev, "reg16", ingenic_sensors[n].reg16); - qdev_prop_set_uint16(dev, "id-reg", ingenic_sensors[n].id_reg); - qdev_prop_set_uint16(dev, "id", ingenic_sensors[n].id); - } else { - dev = DEVICE(i2c_slave_new(ingenic_sensors[n].type, - ingenic_sensors[n].addr)); - if (!strcmp(ingenic_sensors[n].type, "hisi-smartsens")) { - qdev_prop_set_uint8(dev, "id_high", ingenic_sensors[n].id >> 8); - qdev_prop_set_uint8(dev, "id_low", ingenic_sensors[n].id & 0xFF); - } else if (!strcmp(ingenic_sensors[n].type, "ingenic-ov2735")) { - qdev_prop_set_uint8(dev, "rev", ingenic_sensors[n].id); - } - } - i2c_slave_realize_and_unref(I2C_SLAVE(dev), bus, &error_fatal); - return dev; -} - /* --- machine ------------------------------------------------------------ */ static void ingenic_cpu_reset(void *opaque) @@ -1264,8 +1206,7 @@ static void ingenic_init(MachineState *ms) ingenic_irq(ims, IRQ_I2C0 - i)); if (i == info->sensor_bus && !info->sensor_gpio_sda && ims->sensor && strcmp(ims->sensor, "none")) { - ims->sensor_dev = ingenic_create_sensor(ims->sensor, - I2C_BUS(qdev_get_child_bus(dev, "i2c"))); + ims->sensor_dev = image_sensor_create(I2C_BUS(qdev_get_child_bus(dev, "i2c")), ims->sensor); } /* * the second sensor: T40's on I2C3 through CSI1, T23's (the SDK's @@ -1273,14 +1214,12 @@ static void ingenic_init(MachineState *ms) */ if (info->sensor1_bus && i == info->sensor1_bus - 1 && ims->sensor1 && strcmp(ims->sensor1, "none")) { - ims->sensor1_dev = ingenic_create_sensor(ims->sensor1, - I2C_BUS(qdev_get_child_bus(dev, "i2c"))); + ims->sensor1_dev = image_sensor_create(I2C_BUS(qdev_get_child_bus(dev, "i2c")), ims->sensor1); } /* and its third, on I2C0 through VIC2 (the kernel has I2C2 off) */ if (info->sensor2_bus && i == info->sensor2_bus - 1 && ims->sensor2 && strcmp(ims->sensor2, "none")) { - ims->sensor2_dev = ingenic_create_sensor(ims->sensor2, - I2C_BUS(qdev_get_child_bus(dev, "i2c"))); + ims->sensor2_dev = image_sensor_create(I2C_BUS(qdev_get_child_bus(dev, "i2c")), ims->sensor2); } } @@ -1298,8 +1237,7 @@ static void ingenic_init(MachineState *ms) qdev_connect_gpio_out_named(gpio, "drive", sda + 1, qdev_get_gpio_in(dev, 1)); qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(gpio, sda)); - ims->sensor_dev = ingenic_create_sensor(ims->sensor, - I2C_BUS(qdev_get_child_bus(dev, "i2c"))); + ims->sensor_dev = image_sensor_create(I2C_BUS(qdev_get_child_bus(dev, "i2c")), ims->sensor); } /* the image pipeline: ISP core, VIC, MIPI CSI host and D-PHY */ diff --git a/qemu/hw/misc/sstar-i3-isp.c b/qemu/hw/misc/sstar-i3-isp.c index 57a725b3..d4651f5a 100644 --- a/qemu/hw/misc/sstar-i3-isp.c +++ b/qemu/hw/misc/sstar-i3-isp.c @@ -48,7 +48,7 @@ * ("light-r", "light-b", per mille, and "light", the level: 1/1000 LSB * per row of exposure for a white under G; all three may change while * running, qom-set /machine/isp). From the exposure rows and analog gain - * in its I2C registers (hisi_soi_exposure()) it makes 10-bit Bayer at its + * in its I2C registers (image_sensor_exposure()) it makes 10-bit Bayer at its * window size with black at 64, in the colour order ISP0 0x11 [1:0] says * (0 RGGB, 1 GRBG, 2 BGGR -- the camera's, 3 GBRG), cropped by ISP0 * 0x12-0x15. The stages, as the camera application programs them (all @@ -130,7 +130,7 @@ #include "hw/sysbus.h" #include "hw/misc/sstar-riu.h" #include "hw/misc/sstar-i3-isp.h" -#include "hw/i2c/hisi-soi.h" +#include "hw/i2c/image-sensor.h" #include "aw-vinc-logo.h" #include "hw/misc/sstar-pattern.h" #include "migration/vmstate.h" @@ -531,18 +531,22 @@ static void i3_isp_scene_row(SstarI3IspState *s, int w, int h, int y, */ static bool i3_isp_sense(SstarI3IspState *s, int cx, int cy, int w, int h) { - HisiSoiExposure e; + ImageSensorExposure e; uint16_t pal[256][4]; uint8_t col[4]; int sw, sh, x, y, i, c, band = -1; double k, light[3]; - if (!s->sensor || !hisi_soi_exposure(s->sensor, &e)) { + if (!s->sensor || !image_sensor_exposure(s->sensor, &e)) { return false; } sw = e.width ? e.width : cx + w; sh = e.height ? e.height : cy + h; if (sw < cx + w || sh < cy + h || sw > MAX_W) { + if (s->trace) { + qemu_log("sstar-i3-isp: no picture: sensor window %dx%d, " + "crop %dx%d at %d,%d\n", sw, sh, w, h, cx, cy); + } return false; } if (e.vts) { @@ -877,10 +881,10 @@ static void i3_isp_process(SstarI3IspState *s) i3_isp_write_histo(s, hisy); s->have_frame = true; if (s->trace) { - HisiSoiExposure e; + ImageSensorExposure e; uint64_t sum[3] = { 0 }; - hisi_soi_exposure(s->sensor, &e); + image_sensor_exposure(s->sensor, &e); for (i = 0; i < ow * oh; i++) { sum[0] += s->yuv[i]; sum[1] += s->yuv[ow * oh + i]; diff --git a/qemu/include/hw/i2c/image-sensor.h b/qemu/include/hw/i2c/image-sensor.h new file mode 100644 index 00000000..51aef98e --- /dev/null +++ b/qemu/include/hw/i2c/image-sensor.h @@ -0,0 +1,52 @@ +/* + * Image sensors, whatever the SoC: the catalogue machines attach them from + * by name, and the interface an ISP model reads the exposure through. + * + * Copyright (c) 2026 OpenIPC. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_I2C_IMAGE_SENSOR_H +#define HW_I2C_IMAGE_SENSOR_H + +#include "hw/i2c/i2c.h" +#include "qom/object.h" + +#define TYPE_IMAGE_SENSOR "image-sensor" + +typedef struct ImageSensorExposure { + uint32_t lines; /* integration time, rows */ + uint32_t vts; /* frame length, rows; 0 if unset */ + uint32_t gain; /* total gain, 1.0 = 1024 */ + uint32_t width; /* output window; 0 if unset */ + uint32_t height; +} ImageSensorExposure; + +typedef struct ImageSensorClass { + InterfaceClass parent_class; + + /* Fill @e from the sensor's registers */ + void (*exposure)(Object *sensor, ImageSensorExposure *e); +} ImageSensorClass; + +#define IMAGE_SENSOR_CLASS(klass) \ + OBJECT_CLASS_CHECK(ImageSensorClass, (klass), TYPE_IMAGE_SENSOR) +#define IMAGE_SENSOR_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ImageSensorClass, (obj), TYPE_IMAGE_SENSOR) + +/* Fill @e from @sensor; false if @sensor implements no image-sensor. */ +bool image_sensor_exposure(Object *sensor, ImageSensorExposure *e); + +/* Whether the catalogue has a sensor called @name. */ +bool image_sensor_known(const char *name); + +/* + * The sensor @name from the catalogue on @bus at its usual address, or at + * @addr if that is not 0, realized. An unknown name lists the catalogue + * and exits. NULL for "none" or no name: the board has no sensor. + */ +DeviceState *image_sensor_create_at(I2CBus *bus, const char *name, + uint8_t addr); +DeviceState *image_sensor_create(I2CBus *bus, const char *name); + +#endif /* HW_I2C_IMAGE_SENSOR_H */ diff --git a/qemu/setup.sh b/qemu/setup.sh index 8d725a69..f0b56e85 100755 --- a/qemu/setup.sh +++ b/qemu/setup.sh @@ -238,7 +238,9 @@ cp qemu/hw/i2c/hisi-imx415.c "$QEMU_DIR/hw/i2c/" cp qemu/hw/i2c/hisi-imx385.c "$QEMU_DIR/hw/i2c/" cp qemu/hw/i2c/hisi-f37.c "$QEMU_DIR/hw/i2c/" cp qemu/include/hw/i2c/hisi-soi.h "$QEMU_DIR/include/hw/i2c/" -cp qemu/hw/i2c/hisi-gc2053.c "$QEMU_DIR/hw/i2c/" +cp qemu/include/hw/i2c/image-sensor.h "$QEMU_DIR/include/hw/i2c/" +cp qemu/hw/i2c/image-sensor.c "$QEMU_DIR/hw/i2c/" +cp qemu/hw/i2c/imx323.c "$QEMU_DIR/hw/i2c/" cp qemu/hw/i2c/hisi-sp2305.c "$QEMU_DIR/hw/i2c/" cp qemu/hw/i2c/hisi-mis2006.c "$QEMU_DIR/hw/i2c/" cp qemu/hw/i2c/hisi-nvp6124b.c "$QEMU_DIR/hw/i2c/" @@ -563,6 +565,7 @@ config SIGMASTAR default y depends on TCG && ARM select A15MPCORE + select DS1338 select ARM_GIC select SERIAL_MM select UNIMP @@ -574,7 +577,7 @@ config SIGMASTAR KCONFIG echo " hw/arm/Kconfig: added SIGMASTAR" else - for sym in A15MPCORE ARM_GIC SERIAL_MM UNIMP USB_EHCI_SYSBUS AHCI_SYSBUS I2C HISI_I2C SD; do + for sym in A15MPCORE DS1338 ARM_GIC SERIAL_MM UNIMP USB_EHCI_SYSBUS AHCI_SYSBUS I2C HISI_I2C SD; do if ! sed -n '/^config SIGMASTAR$/,/^$/p' "$QEMU_DIR/hw/arm/Kconfig" | \ grep -q "select $sym\$"; then sed -i "/^config SIGMASTAR\$/,/^\$/{/depends on TCG \&\& ARM\$/a\\ select $sym @@ -880,41 +883,19 @@ fi # hw/i2c/meson.build if ! grep -q hisi-i2c "$QEMU_DIR/hw/i2c/meson.build"; then - echo "system_ss.add(when: 'CONFIG_HISI_I2C', if_true: files('hisi-i2c.c', 'hisi-i2c-v1.c', 'hisi-i2c-dw.c', 'hisi-imx335.c', 'hisi-imx307.c', 'hisi-imx291.c', 'hisi-imx385.c', 'hisi-imx415.c', 'hisi-f37.c', 'hisi-gc2053.c', 'hisi-sp2305.c', 'hisi-mis2006.c', 'hisi-nvp6124b.c', 'hisi-smartsens.c'))" \ + echo "system_ss.add(when: 'CONFIG_HISI_I2C', if_true: files('hisi-i2c.c', 'hisi-i2c-v1.c', 'hisi-i2c-dw.c'))" \ >> "$QEMU_DIR/hw/i2c/meson.build" echo " patched hw/i2c/meson.build" -else - # Existing tree — splice in any new files that aren't already listed. - if ! grep -q hisi-i2c-v1 "$QEMU_DIR/hw/i2c/meson.build"; then - sed -i "s/'hisi-i2c.c', 'hisi-imx335.c'/'hisi-i2c.c', 'hisi-i2c-v1.c', 'hisi-imx335.c'/" \ - "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added hisi-i2c-v1.c" - fi - if ! grep -q hisi-i2c-dw "$QEMU_DIR/hw/i2c/meson.build"; then - sed -i "s/'hisi-i2c-v1.c', 'hisi-imx335.c'/'hisi-i2c-v1.c', 'hisi-i2c-dw.c', 'hisi-imx335.c'/" \ - "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added hisi-i2c-dw.c" - fi - if ! grep -q hisi-imx291 "$QEMU_DIR/hw/i2c/meson.build"; then - sed -i "s/'hisi-imx307.c'/'hisi-imx307.c', 'hisi-imx291.c'/" \ - "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added hisi-imx291.c" - fi - if ! grep -q hisi-imx385 "$QEMU_DIR/hw/i2c/meson.build"; then - sed -i "s/'hisi-imx291.c'/'hisi-imx291.c', 'hisi-imx385.c'/" \ - "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added hisi-imx385.c" - fi - if ! grep -q hisi-imx415 "$QEMU_DIR/hw/i2c/meson.build"; then - sed -i "s/'hisi-imx385.c'/'hisi-imx385.c', 'hisi-imx415.c'/" \ - "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added hisi-imx415.c" - fi - if ! grep -q hisi-nvp6124b "$QEMU_DIR/hw/i2c/meson.build"; then - sed -i "s/'hisi-mis2006.c'/'hisi-mis2006.c', 'hisi-nvp6124b.c'/" \ - "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added hisi-nvp6124b.c" - fi +fi +# The image sensors: any machine attaches any of them by name +# (hw/i2c/image-sensor.c), so every target builds them all. A tree from +# before this has them in the families' lists: take them out of those. +sed -i -E "/CONFIG_(HISI_I2C|FULLHAN|GRAINMEDIA|INGENIC)/s/'(hisi-(imx[0-9]+|f37|gc2053|sp2305|mis2006|nvp6124b|smartsens)|galaxycore|ingenic-ov2735)\.c'(, )?//g" \ + "$QEMU_DIR/hw/i2c/meson.build" +if ! grep -q "'image-sensor.c'" "$QEMU_DIR/hw/i2c/meson.build"; then + echo "system_ss.add(when: 'CONFIG_I2C', if_true: files('image-sensor.c', 'imx323.c', 'galaxycore.c', 'hisi-f37.c', 'hisi-imx291.c', 'hisi-imx307.c', 'hisi-imx335.c', 'hisi-imx385.c', 'hisi-imx415.c', 'hisi-mis2006.c', 'hisi-nvp6124b.c', 'hisi-smartsens.c', 'hisi-sp2305.c', 'ingenic-ov2735.c'))" \ + >> "$QEMU_DIR/hw/i2c/meson.build" + echo " hw/i2c/meson.build: added the image sensors" fi # hw/ssi/Kconfig @@ -1035,11 +1016,6 @@ for f in $(cd qemu && find hw -name 'fh-*.c' 2>/dev/null); do echo " $d/meson.build: added $b" fi done -if ! grep -q "'galaxycore.c'" "$QEMU_DIR/hw/i2c/meson.build"; then - echo "system_ss.add(when: 'CONFIG_FULLHAN', if_true: files('galaxycore.c'))" \ - >> "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added galaxycore.c" -fi if ! grep -q "'fullhan.c'" "$QEMU_DIR/hw/arm/meson.build"; then echo "arm_ss.add(when: 'CONFIG_FULLHAN', if_true: files('fullhan.c'))" \ >> "$QEMU_DIR/hw/arm/meson.build" @@ -1086,12 +1062,6 @@ for f in $(cd qemu && find hw -name 'gm-*.c' 2>/dev/null); do echo " $d/meson.build: added $b" fi done -if ! grep -q "CONFIG_GRAINMEDIA', if_true: files('galaxycore.c')" \ - "$QEMU_DIR/hw/i2c/meson.build"; then - echo "system_ss.add(when: 'CONFIG_GRAINMEDIA', if_true: files('galaxycore.c'))" \ - >> "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added galaxycore.c for GRAINMEDIA" -fi # shared models: the H.264 slice coder, the FTAES020, the JPEG scan coder for b in hantro-h264.c fh-aes.c nvt-pixel.c; do if ! grep -q "CONFIG_GRAINMEDIA', if_true: files('$b')" \ @@ -1151,15 +1121,6 @@ for f in $(cd qemu && find hw -name 'ingenic*.c' ! -path 'hw/mips/*' 2>/dev/null echo " $d/meson.build: added $b" fi done -# the image sensors sensor= attaches (models shared with the ARM families) -for b in galaxycore.c hisi-smartsens.c hisi-f37.c; do - if ! grep -q "CONFIG_INGENIC', if_true: files('$b')" \ - "$QEMU_DIR/hw/i2c/meson.build"; then - echo "system_ss.add(when: 'CONFIG_INGENIC', if_true: files('$b'))" \ - >> "$QEMU_DIR/hw/i2c/meson.build" - echo " hw/i2c/meson.build: added $b for INGENIC" - fi -done # shared coders for the VPU (ingenic-vpu.c): H.264 slices, H.265 slices, JPEG for b in hantro-h264.c hevc-enc.c jpeg-enc.c; do if ! grep -q "CONFIG_INGENIC', if_true: files('$b')" \ diff --git a/qemu/tests/test-sigmastar.py b/qemu/tests/test-sigmastar.py index 846dc824..f42d840f 100755 --- a/qemu/tests/test-sigmastar.py +++ b/qemu/tests/test-sigmastar.py @@ -41,6 +41,7 @@ python3 qemu/tests/test-sigmastar.py [--qemu PATH] """ +import re import argparse import base64 import atexit @@ -590,6 +591,47 @@ def test_msc313e(qemu, tmp): 'PM_IRIN low', vals, [0x3FF, 0x8, 0x11]) +def i2c_device_at(qtree, model): + """The I2C address of a @model in `info qtree` output; None if absent.""" + m = re.search(r'dev: %s, id ""[\s\S]*?address = \d+ \(0x([0-9a-f]+)\)' + % model, qtree) + return int(m.group(1), 16) if m else None + + +def test_sensor_catalogue(qemu): + print('image sensor catalogue (hw/i2c/image-sensor.c)') + for name, model, addr in (('imx307', 'hisi-imx307', 0x1A), + ('imx323', 'imx323', 0x1A), + ('sc2310', 'hisi-smartsens', 0x30), + ('sc2315e', 'hisi-smartsens', 0x30), + ('gc4653', 'galaxycore-sensor', 0x29), + ('ov2735', 'ingenic-ov2735', 0x3C)): + out = hmp(qemu, ['-M', 'msc313e,sensor=' + name, '-S'], ['info qtree']) + check('sensor=%s: %s at 0x%02x' % (name, model, addr), + i2c_device_at(out, model), addr) + out = hmp(qemu, ['-M', 'msc313e,sensor=nosuch', '-S'], []) + check('an unknown sensor is refused, the catalogue listed', + "unknown sensor 'nosuch'" in out and 'imx307' in out, True) + out = hmp(qemu, ['-M', 'msc313e,rtc=ds1338', '-S'], ['info qtree']) + check('rtc=ds1338: a DS1307 at 0x68', i2c_device_at(out, 'ds1338'), 0x68) + out = hmp(qemu, ['-M', 'msc313e,rtc=nosuch', '-S'], []) + check('an unknown rtc is refused', "unknown rtc 'nosuch'" in out, True) + out = hmp(qemu, ['-M', 'ssd201,rtc=ds1338', '-S'], []) + check('rtc= is refused where there is no I2C bus', + 'no I2C bus for an rtc' in out, True) + p = subprocess.run([qemu, '-M', 'none', '-display', 'none', '-nodefaults', + '-qmp', 'stdio'], capture_output=True, text=True, + timeout=60, + input='{"execute":"qmp_capabilities"}\n' + '{"execute":"qom-list-types","arguments":' + '{"implements":"image-sensor"}}\n' + '{"execute":"quit"}\n') + for model in ('galaxycore-sensor', 'ingenic-ov2735', 'hisi-smartsens', + 'hisi-imx307', 'imx323', 'hisi-jxf22'): + check('%s gives an ISP its exposure' % model, + '"%s"' % model in p.stdout, True) + + PMGPIO = riu(0x000F, 0) PADGPIO = riu(0x103C, 0) SAR = riu(0x0014, 0) @@ -1419,6 +1461,7 @@ def main(): qt.close() test_migration(args.qemu, tmp, path) test_msc313e(args.qemu, tmp) + test_sensor_catalogue(args.qemu) test_msc313e_sd(args.qemu, tmp) print('%d failure(s)' % failures) sys.exit(1 if failures else 0)