diff --git a/Documentation/devicetree/bindings/media/apple,t6041-isp.yaml b/Documentation/devicetree/bindings/media/apple,t6041-isp.yaml new file mode 100644 index 0000000000000..e66161db7c371 --- /dev/null +++ b/Documentation/devicetree/bindings/media/apple,t6041-isp.yaml @@ -0,0 +1,190 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/apple,t6041-isp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple T604x Image Signal Processor + +maintainers: + - Jonathan Goyvaerts + +description: + The T604x ISP controls the attached camera sensor and processes its frames + through firmware the bootloader loads. The firmware segments are described + as reserved memory mapped at the addresses the firmware runs them at. The + host exchanges commands and image buffers through three DART instances. + +select: + properties: + compatible: + contains: + const: apple,t6041-isp + required: + - compatible + +properties: + compatible: + items: + - const: apple,t6041-isp + - const: apple,isp + + reg: + items: + - description: Coprocessor registers up to its control block + - description: Mailbox interrupt registers + - description: Firmware handshake registers + - description: Mailbox doorbell registers + + reg-names: + items: + - const: coproc + - const: mbox + - const: gpio + - const: mbox2 + + interrupts: + maxItems: 1 + + iommus: + items: + - description: First ISP DART stream + - description: Second ISP DART stream + - description: Third ISP DART stream + + power-domains: + items: + - description: ISP system domain + - description: ISP CPU domain + - description: ISP FE domain + - description: ISP DPRX domain + - description: ISP secure domain + - description: ISP BE domain + - description: ISP CLR domain + - description: Camera sensor domain + + memory-region: + items: + - description: Firmware text loaded by the bootloader + - description: Firmware data loaded by the bootloader + - description: Firmware main memory window; host allocations follow it + + memory-region-names: + items: + - const: text + - const: data + - const: heap + + apple,dart-vm-size: + $ref: /schemas/types.yaml#/definitions/uint64 + description: + Size in bytes of the host allocation aperture used by the ISP driver. + + apple,platform-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Platform identifier passed in the firmware boot descriptor. + + apple,cam-connections-scheme: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Camera connection layout of the board passed in the firmware boot + descriptor. + + apple,temporal-filter: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Firmware temporal-filter strength selected for the attached sensor. + + sensor-presets: + type: object + description: + Sensor firmware presets and the host output geometry associated with them. + patternProperties: + "^preset[0-9]+$": + type: object + properties: + apple,config-index: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Sensor configuration index understood by the firmware + apple,input-size: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Input width in pixels + - description: Input height in pixels + description: Sensor input dimensions + apple,output-size: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Output width in pixels + - description: Output height in pixels + description: Delivered frame dimensions + apple,crop: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Horizontal crop origin in pixels + - description: Vertical crop origin in pixels + - description: Crop width in pixels + - description: Crop height in pixels + description: Cropped sensor rectangle + required: + - apple,config-index + - apple,input-size + - apple,output-size + - apple,crop + additionalProperties: false + additionalProperties: false + +required: + - compatible + - reg + - reg-names + - interrupts + - iommus + - power-domains + - memory-region + - memory-region-names + - apple,dart-vm-size + - apple,platform-id + - sensor-presets + +additionalProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + isp@580000000 { + compatible = "apple,t6041-isp", "apple,isp"; + reg = <0x5 0x80000000 0x0 0x1604000>, + <0x5 0x82924000 0x0 0x40>, + <0x5 0x829241d0 0x0 0x20>, + <0x5 0x82924464 0x0 0x10>; + reg-names = "coproc", "mbox", "gpio", "mbox2"; + interrupt-parent = <&aic>; + interrupts = ; + iommus = <&isp_dart0 0>, <&isp_dart1 0>, <&isp_dart2 0>; + power-domains = <&ps_isp_sys>, <&ps_isp_cpu>, <&ps_isp_fe>, + <&ps_isp_dprx>, <&ps_isp_secure>, <&ps_isp_be>, + <&ps_isp_clr>, <&ps_ispsens0>; + memory-region = <&isp_fw_text>, <&isp_fw_data>, <&isp_heap>; + memory-region-names = "text", "data", "heap"; + apple,dart-vm-size = /bits/ 64 <0xde600000>; + apple,platform-id = <5>; + apple,cam-connections-scheme = <0x10>; + + sensor-presets { + preset0 { + apple,config-index = <5>; + apple,input-size = <1920 2160>; + apple,output-size = <1920 1080>; + apple,crop = <0 0 1920 1080>; + }; + }; + }; + }; diff --git a/drivers/media/platform/apple/isp/isp-cam.c b/drivers/media/platform/apple/isp/isp-cam.c index 5090ed2ed5b98..a5881c35ffe15 100644 --- a/drivers/media/platform/apple/isp/isp-cam.c +++ b/drivers/media/platform/apple/isp/isp-cam.c @@ -70,6 +70,7 @@ static const struct isp_setfile isp_setfiles[] = { [ISP_IMX814_2123_01] = {0x814, 0x21230101, "apple/isp_2123_01XX.dat", 0xed54}, [ISP_IMX853_7622_01] = {0x853, 0x76220112, "apple/isp_7622_01XX.dat", 0x247f8}, [ISP_IMX913_7523_01] = {0x913, 0x75230107, "apple/isp_7523_01XX.dat", 0x247f8}, + [ISP_IMX958_2224_01] = {0x958, 0x22240106, "apple/isp_2224_01XX.dat", 0x6ff4}, [ISP_VD56G0_6221_01] = {0xd56, 0x62210102, "apple/isp_6221_01XX.dat", 0x1b80}, [ISP_VD56G0_6222_01] = {0xd56, 0x62220102, "apple/isp_6222_01XX.dat", 0x1b80}, }; @@ -158,6 +159,10 @@ static int isp_ch_get_sensor_id(struct apple_isp *isp, u32 ch) case 0x913: id = ISP_IMX913_7523_01; break; + case 0x958: + /* Section 0x50f of 2224 names sensor 0x958; 2924's names 0x614. */ + id = ISP_IMX958_2224_01; + break; case 0xd56: id = ISP_VD56G0_6221_01; break; @@ -228,10 +233,14 @@ static int isp_ch_cache_sensor_info(struct apple_isp *isp, u32 ch) dev_dbg(isp->dev, "found sensor %x on ch %d\n", args->version, ch); - /* The metadata buffers are allocated for the sizes the driver knows. */ + /* + * The metadata buffers are allocated for the sizes the driver knows; + * without a capture metadata pool its size is not used. + */ if (isp->hw->fw_abi == ISP_FW_ABI_H17 && (args->unk_68 != isp->hw->meta_size || - args->unk_78 != isp->hw->capture_meta_size)) { + (isp->hw->capture_meta_size && + args->unk_78 != isp->hw->capture_meta_size))) { dev_err(isp->dev, "metadata sizes 0x%x/0x%x, expected 0x%x/0x%x\n", args->unk_68, args->unk_78, isp->hw->meta_size, @@ -245,7 +254,8 @@ static int isp_ch_cache_sensor_info(struct apple_isp *isp, u32 ch) err = isp_ch_get_sensor_id(isp, ch); if (err || (fmt->id != ISP_IMX248_1820_01 && fmt->id != ISP_IMX558_1921_01 && - fmt->id != ISP_IMX558_1925_03 && fmt->id != ISP_IMX364_8720_01)) { + fmt->id != ISP_IMX558_1925_03 && fmt->id != ISP_IMX364_8720_01 && + fmt->id != ISP_IMX958_2224_01)) { dev_err(isp->dev, "ch %d: unsupported sensor. Please file a bug report with hardware info & dmesg trace.\n", ch); @@ -334,6 +344,49 @@ int apple_isp_detect_camera(struct apple_isp *isp) return err; } +/* + * A setfile is a chain of { le32 size, le32 0, le32 tag, payload } sections + * from 0x14 on, size counting what follows it. The lens shading sections + * hold 18-byte table records after a 10-byte header: { ?, 0x20, 0x20, + * offset lo, offset hi, 0, 0, width, height }. The xx24 setfiles keep the + * row stride (2 * width) in "offset hi"; the H17 firmware reads it as the + * offset's high half ("baseHi") and faults far past the file. Their xx25 + * twins are the same tables with it zeroed, as macOS hands them over. + */ +static void isp_setfile_fixup_h17(void *data, size_t len) +{ + size_t off = 0x14; + + while (off + 12 <= len) { + u32 size = get_unaligned_le32(data + off); + size_t end = off + 4 + size; + size_t rec; + int i; + + if (size < 8 || end > len) + break; + + switch (get_unaligned_le32(data + off + 8)) { + case 0xc003: + case 0xc01b: + case 0xc01c: + case 0xc01f: + case 0xc021: + rec = off + 12 + 10; + for (i = 0; i < 16 && rec + 18 <= end; i++, rec += 18) { + if (get_unaligned_le16(data + rec + 2) != 0x20 || + get_unaligned_le16(data + rec + 4) != 0x20) + break; + if (get_unaligned_le16(data + rec + 8) == + 2 * get_unaligned_le16(data + rec + 14)) + put_unaligned_le16(0, data + rec + 8); + } + break; + } + off = end; + } +} + static int isp_ch_load_setfile(struct apple_isp *isp, u32 ch) { struct isp_format *fmt = isp_get_format(isp, ch); @@ -372,6 +425,8 @@ static int isp_ch_load_setfile(struct apple_isp *isp, u32 ch) memcpy(isp->data_surf->virt, fw->data, len); memset(isp->data_surf->virt + len, 0, setfile->size - len); release_firmware(fw); + if (isp->hw->fw_abi == ISP_FW_ABI_H17) + isp_setfile_fixup_h17(isp->data_surf->virt, len); return isp_cmd_ch_set_file_load(isp, ch, isp->data_surf->iova, setfile->size); @@ -406,10 +461,13 @@ static int isp_ch_configure_pools(struct apple_isp *isp, u32 ch) if (err) return err; - err = isp_cmd_ch_buffer_pool_config_set(isp, ch, - CISP_POOL_TYPE_META_CAPTURE); - if (err) - return err; + /* H17 firmware without capture metadata has no such pool. */ + if (isp->hw->fw_abi != ISP_FW_ABI_H17 || isp->hw->capture_meta_size) { + err = isp_cmd_ch_buffer_pool_config_set(isp, ch, + CISP_POOL_TYPE_META_CAPTURE); + if (err) + return err; + } /* The H17 firmware also takes the geometry of the output buffers. */ if (isp->hw->fw_abi != ISP_FW_ABI_H17) diff --git a/drivers/media/platform/apple/isp/isp-cmd.c b/drivers/media/platform/apple/isp/isp-cmd.c index 42c448fd0f30c..fd7dcfd52899a 100644 --- a/drivers/media/platform/apple/isp/isp-cmd.c +++ b/drivers/media/platform/apple/isp/isp-cmd.c @@ -151,16 +151,24 @@ int isp_cmd_set_dsid_clr_req_base(struct apple_isp *isp, u64 dsid_clr_base, return CISP_SEND_IN(isp, args); } -int isp_cmd_set_dsid_clr_multi_bc(struct apple_isp *isp, u64 dsid_clr_base, - u32 dsid_clr_range) +int isp_cmd_set_dsid_clr_multi_bc(struct apple_isp *isp, const u64 *bases, + unsigned int count, u32 dsid_clr_range) { struct cmd_set_dsid_clr_multi_bc_reg_base args = { .opcode = CISP_OPCODE(CISP_CMD_SET_DSID_CLR_MULTI_BC_REG_BASE), - .count = 1, + .count = count, .dsid_clr_range = dsid_clr_range, - .dsid_clr_base = dsid_clr_base, }; - return CISP_SEND_IN(isp, args); + + /* The caller, isp_set_dsid_clr(), bounds count by the array. */ + if (!count) + return -EINVAL; + memcpy(args.dsid_clr_base, bases, count * sizeof(*bases)); + + return cisp_send(isp, &args, + offsetof(typeof(args), dsid_clr_base) + + count * sizeof(*bases), 0, + CISP_TIMEOUT); } int isp_cmd_pmp_ctrl_set(struct apple_isp *isp, u64 clock_scratch, @@ -481,7 +489,8 @@ int isp_cmd_ch_buffer_pool_config_set_rendered(struct apple_isp *isp, u32 chan, struct cmd_ch_buffer_pool_config_set_rendered args = { .opcode = CISP_OPCODE(CISP_CMD_CH_BUFFER_POOL_CONFIG_SET), .chan = chan, - .type = CISP_POOL_TYPE_RENDERED, + .type = isp->hw->scl1 ? CISP_POOL_TYPE_RENDERED_SCL1 : + CISP_POOL_TYPE_RENDERED, .count = count, .plane0_size = plane0_size, .plane0_stride = plane0_stride, diff --git a/drivers/media/platform/apple/isp/isp-cmd.h b/drivers/media/platform/apple/isp/isp-cmd.h index ab0c85023bebf..695c0a9f7ab70 100644 --- a/drivers/media/platform/apple/isp/isp-cmd.h +++ b/drivers/media/platform/apple/isp/isp-cmd.h @@ -197,15 +197,20 @@ struct cmd_set_dsid_clr_req_base { } __packed; static_assert(sizeof(struct cmd_set_dsid_clr_req_base) == 0x14); -/* H17: a count of broadcast-clear windows and the first window */ +/* + * H17: a count of broadcast-clear windows, their common range and the + * windows; the command carries only the @count windows. + */ +#define ISP_DSID_MULTI_BC_MAX 8 + struct cmd_set_dsid_clr_multi_bc_reg_base { u64 opcode; u8 count; u8 pad_9[3]; u32 dsid_clr_range; - u64 dsid_clr_base; + u64 dsid_clr_base[ISP_DSID_MULTI_BC_MAX]; } __packed; -static_assert(sizeof(struct cmd_set_dsid_clr_multi_bc_reg_base) == 0x18); +static_assert(sizeof(struct cmd_set_dsid_clr_multi_bc_reg_base) == 0x50); struct cmd_pmp_ctrl_set { u64 opcode; @@ -260,8 +265,8 @@ int isp_cmd_config_get(struct apple_isp *isp, struct cmd_config_get *args); int isp_cmd_set_isp_pmu_base(struct apple_isp *isp, u64 pmu_base); int isp_cmd_set_dsid_clr_req_base(struct apple_isp *isp, u64 dsid_clr_base, u32 dsid_clr_range); -int isp_cmd_set_dsid_clr_multi_bc(struct apple_isp *isp, u64 dsid_clr_base, - u32 dsid_clr_range); +int isp_cmd_set_dsid_clr_multi_bc(struct apple_isp *isp, const u64 *bases, + unsigned int count, u32 dsid_clr_range); int isp_cmd_set_dsid_clr_req_base2(struct apple_isp *isp, u64 dsid_clr_base0, u64 dsid_clr_base1, u64 dsid_clr_base2, u64 dsid_clr_base3, u32 dsid_clr_range0, diff --git a/drivers/media/platform/apple/isp/isp-drv.c b/drivers/media/platform/apple/isp/isp-drv.c index 45e73995657fe..12d17070c7d4a 100644 --- a/drivers/media/platform/apple/isp/isp-drv.c +++ b/drivers/media/platform/apple/isp/isp-drv.c @@ -144,17 +144,44 @@ static int apple_isp_map_fw_mmio(struct apple_isp *isp) return 0; } +/* The IOVA range of a memory-region, from its last iommu-addresses entry. */ +static int isp_of_region_iova(struct device *dev, const char *name, + phys_addr_t *base, size_t *size) +{ + const __be32 *maps, *end; + int idx, len; + + idx = of_property_match_string(dev->of_node, "memory-region-names", + name); + if (idx < 0) + return -ENOENT; + + struct device_node *np __free(device_node) = + of_parse_phandle(dev->of_node, "memory-region", idx); + if (!np) + return -ENODEV; + + maps = of_get_property(np, "iommu-addresses", &len); + if (!maps || !len) + return -ENODEV; + + end = maps + len / sizeof(__be32); + + while (maps < end) { + maps++; + maps = of_translate_dma_region(dev->of_node, maps, base, size); + } + + return 0; +} + static int apple_isp_init_iommu(struct apple_isp *isp) { struct device *dev = isp->dev; - phys_addr_t heap_base; - size_t heap_size; + phys_addr_t heap_base, data_base; + size_t heap_size, data_size; u64 vm_size; int err; - int idx; - int size; - struct device_node *mem_node; - const __be32 *maps, *end; isp->domain = iommu_get_domain_for_dev(isp->dev); if (!isp->domain) @@ -162,30 +189,36 @@ static int apple_isp_init_iommu(struct apple_isp *isp) isp->shift = __ffs(isp->domain->pgsize_bitmap); isp->fw_iova_mask = isp->hw->fw_iova_mask ?: U64_MAX; - idx = of_property_match_string(dev->of_node, "memory-region-names", - "heap"); - mem_node = of_parse_phandle(dev->of_node, "memory-region", idx); - if (!mem_node) { - dev_err(dev, "No memory-region found for heap\n"); - return -ENODEV; - } - - maps = of_get_property(mem_node, "iommu-addresses", &size); - if (!maps || !size) { + err = isp_of_region_iova(dev, "heap", &heap_base, &heap_size); + if (err) { dev_err(dev, "No valid iommu-addresses found for heap\n"); return -ENODEV; } - end = maps + size / sizeof(__be32); + isp->fw.heap_base = heap_base; + isp->fw.heap_top = heap_base + heap_size; - while (maps < end) { - maps++; - maps = of_translate_dma_region(dev->of_node, maps, &heap_base, - &heap_size); + /* + * Where the bootloader loads the firmware's data, it backs the + * firmware's main memory only up to the end of it. The rest, up to + * the heap top, is the host's to back. + */ + err = isp_of_region_iova(dev, "data", &data_base, &data_size); + if (!err) { + isp->fw.data_top = data_base + data_size; + if (isp->fw.data_top <= isp->fw.heap_base || + isp->fw.data_top > isp->fw.heap_top || + !IS_ALIGNED(isp->fw.data_top, 1UL << isp->shift)) { + dev_err(dev, "data 0x%llx outside the heap 0x%llx-0x%llx\n", + isp->fw.data_top, isp->fw.heap_base, + isp->fw.heap_top); + return -EINVAL; + } + } else if (err != -ENOENT) { + dev_err(dev, "No valid iommu-addresses found for data\n"); + return -ENODEV; } - isp->fw.heap_top = heap_base + heap_size; - err = of_property_read_u64(dev->of_node, "apple,dart-vm-size", &vm_size); if (err) { @@ -421,6 +454,10 @@ static int apple_isp_probe(struct platform_device *pdev) if (err) isp->temporal_filter = 0; + /* the sensor connection layout, for firmware that takes it at boot */ + of_property_read_u32(dev->of_node, "apple,cam-connections-scheme", + &isp->cam_connections_scheme); + err = apple_isp_init_presets(isp); if (err) { dev_err(dev, "failed to initialize presets\n"); @@ -788,6 +825,64 @@ static const struct apple_isp_hw apple_isp_hw_t8140 = { .resident_fw = true, }; +/* The same for T6040, from the J616s ADT's DART address filter. */ +static const struct isp_mmio_window apple_isp_fw_mmio_t6040[] = { + { 0x220004000, 0x1d4000 }, /* DSID broadcast-clear windows */ + { 0x222004000, 0x1d4000 }, + { 0x224004000, 0x1d4000 }, + { 0x226004000, 0x1d4000 }, + { 0x485660000, 0x4000 }, + { 0x5003c0000, 0x28000 }, /* PMGR, with the PMP clock scratch */ + { 0x500e3c000, 0x4000 }, + { 0x502284000, 0x4000 }, /* PMGR, ISP power states */ + { 0x502880000, 0x8000 }, + { 0x504824000, 0x4000 }, /* PMP bandwidth scratch */ + { 0x51062c000, 0x20000 }, +}; + +/* + * T6040 runs the same coprocessor and firmware interface as T8140, booted + * as macOS boots it on the J616s (macOS 26.6.2 camera log, kext register + * map) and as aurora's m1n1 proxy captures with it: no coprocessor reset, + * four broadcast-clear windows in eight slots, the scaler 1 output path and + * no capture metadata pool or watchdog. + */ +static const struct apple_isp_hw apple_isp_hw_t6040 = { + .gen = ISP_GEN_T6040, + .fw_abi = ISP_FW_ABI_H17, + .pmu_base = 0x0, + + .dsid_count = 4, + .dsid_slots = 8, + .dsid_clr_base0 = 0x2201d4000, + .dsid_clr_base1 = 0x2221d4000, + .dsid_clr_base2 = 0x2241d4000, + .dsid_clr_base3 = 0x2261d4000, + .dsid_clr_range0 = 0x2fc, + + /* the PMGR and PMP scratch words of the ISP's DAPF windows */ + .clock_scratch = 0x5003d0fa8, + .clock_base = 0x0, + .clock_bit = 0x0, + .clock_size = 0x8, + .bandwidth_scratch = 0x504824000, + .bandwidth_base = 0x0, + .bandwidth_bit = 0x0, + .bandwidth_size = 0x8, + .mbox_irq_enable = ISP_MBOX_IRQ_ENABLE_T6031, + + .scl1 = true, + .lpdp = false, + .meta_size = ISP_META_SIZE_T8140, + .fw_mmio = apple_isp_fw_mmio_t6040, + .num_fw_mmio = ARRAY_SIZE(apple_isp_fw_mmio_t6040), + + .coproc_control = ISP_COPROC_CONTROL_T8140, + .boot_mode = 1, + .shared_size = 0xde600000, + .resident_fw = true, +}; + static const struct of_device_id apple_isp_of_match[] = { { .compatible = "apple,t8103-isp", .data = &apple_isp_hw_t8103 }, { .compatible = "apple,t8112-isp", .data = &apple_isp_hw_t8112 }, @@ -797,6 +892,7 @@ static const struct of_device_id apple_isp_of_match[] = { { .compatible = "apple,t6030-isp", .data = &apple_isp_hw_t6030 }, { .compatible = "apple,t6031-isp", .data = &apple_isp_hw_t6031 }, { .compatible = "apple,t8140-isp", .data = &apple_isp_hw_t8140 }, + { .compatible = "apple,t6041-isp", .data = &apple_isp_hw_t6040 }, {}, }; MODULE_DEVICE_TABLE(of, apple_isp_of_match); diff --git a/drivers/media/platform/apple/isp/isp-drv.h b/drivers/media/platform/apple/isp/isp-drv.h index 8f945c26f00e6..ed3e48eef569e 100644 --- a/drivers/media/platform/apple/isp/isp-drv.h +++ b/drivers/media/platform/apple/isp/isp-drv.h @@ -51,6 +51,7 @@ enum isp_generation { ISP_GEN_T8112, ISP_GEN_T6031, ISP_GEN_T8140, + ISP_GEN_T6040, }; /* @@ -146,6 +147,8 @@ struct apple_isp_hw { u64 pmu_base; int dsid_count; + /* windows of the H17 multi broadcast-clear command, the unused all ones */ + u8 dsid_slots; u64 dsid_clr_base0; u64 dsid_clr_base1; u64 dsid_clr_base2; @@ -186,6 +189,9 @@ struct apple_isp_hw { /* ISP_GPIO_6 boot mode */ u32 boot_mode; + /* shared memory size in the H16 boot descriptor, 0 for the T8140 one */ + u64 shared_size; + /* size of a capture metadata buffer, 0 for no such pool */ u32 capture_meta_size; @@ -244,6 +250,7 @@ enum isp_sensor_id { ISP_IMX814_2123_01, ISP_IMX853_7622_01, ISP_IMX913_7523_01, + ISP_IMX958_2224_01, ISP_VD56G0_6221_01, ISP_VD56G0_6222_01, }; @@ -264,6 +271,7 @@ struct apple_isp { enum isp_firmware_version fw_compat; u32 platform_id; u32 temporal_filter; + u32 cam_connections_scheme; struct isp_preset *presets; int num_presets; @@ -306,7 +314,9 @@ struct apple_isp { struct mutex iovad_lock; struct isp_firmware { + u64 heap_base; u64 heap_top; + u64 data_top; /* 0 unless the bootloader loads the data */ } fw; /* changes under video_lock once the video device is registered */ enum isp_fw_state fw_state; @@ -314,6 +324,7 @@ struct apple_isp { struct isp_surf *ipc_surf; struct isp_surf *extra_surf; struct isp_surf *data_surf; + struct isp_surf *main_tail_surf; struct isp_surf *log_surf; struct isp_surf *bt_surf; struct isp_surf *meta_surfs[ISP_MAX_BUFFERS]; diff --git a/drivers/media/platform/apple/isp/isp-fw.c b/drivers/media/platform/apple/isp/isp-fw.c index c96681173fd0f..30ff704ef193f 100644 --- a/drivers/media/platform/apple/isp/isp-fw.c +++ b/drivers/media/platform/apple/isp/isp-fw.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "isp-cam.h" #include "isp-cmd.h" @@ -163,6 +164,7 @@ static_assert(sizeof(struct isp_firmware_bootargs) == 0x180); */ #define ISP_H16_DESCRIPTOR_SIZE 0x200 #define ISP_H16_SHARED_SIZE 0xee000000ULL +#define ISP_H16_DESC_CAM_SCHEME 0x60 #define ISP_H16_DESC_NO_OPTICAL_CARD_ID 0xb0 struct isp_firmware_bootargs_h16 { @@ -315,6 +317,31 @@ static int isp_reset_coproc_t8140(struct apple_isp *isp) return 0; } +/* + * The T6040 firmware is started as the bootloader left it, without a reset: + * the coprocessor must never have run, and its reset vector must be the + * entry of the firmware text at the base of the heap region. + */ +static int isp_check_coproc_t6040(struct apple_isp *isp) +{ + u32 control = isp_coproc_read32(isp, isp_coproc_control(isp)); + u64 rvbar = readq(isp->coproc + ISP_COPROC_RVBAR); + + if (control) { + isp_err(isp, "coprocessor already started (control 0x%x)\n", + control); + return -EBUSY; + } + + if (rvbar != (isp->fw.heap_base | 1)) { + isp_err(isp, "reset vector 0x%llx is not the firmware at 0x%llx\n", + rvbar, isp->fw.heap_base); + return -ENODEV; + } + + return 0; +} + static int isp_reset_coproc(struct apple_isp *isp) { int retries; @@ -323,6 +350,8 @@ static int isp_reset_coproc(struct apple_isp *isp) if (isp->hw->gen == ISP_GEN_T8140) return isp_reset_coproc_t8140(isp); + if (isp->hw->gen == ISP_GEN_T6040) + return isp_check_coproc_t6040(isp); isp_coproc_write32(isp, ISP_COPROC_EDPRCR, 0x2); @@ -377,8 +406,8 @@ static int isp_firmware_boot_stage1(struct apple_isp *isp) if (err < 0) return err; - /* ISP17a has no clock enable word */ - if (isp->hw->gen != ISP_GEN_T8140) + /* ISP17a and T6040 have no clock enable word */ + if (isp->hw->gen != ISP_GEN_T8140 && isp->hw->gen != ISP_GEN_T6040) isp_gpio_write32(isp, ISP_GPIO_CLOCK_EN, 0x1); #if 0 @@ -445,11 +474,27 @@ int apple_isp_alloc_firmware_surface(struct apple_isp *isp) dev_dbg(isp->dev, "Data surface iova: 0x%llx\n", (long long)isp->data_surf->iova); + /* Without it, the firmware faults on its first writes there. */ + if (isp->fw.data_top && isp->fw.data_top < isp->fw.heap_top) { + isp->main_tail_surf = + isp_alloc_surface_at(isp, isp->fw.data_top, + isp->fw.heap_top - isp->fw.data_top); + if (!isp->main_tail_surf) { + isp_err(isp, "failed to back main memory 0x%llx-0x%llx\n", + isp->fw.data_top, isp->fw.heap_top); + isp_free_surface(isp, isp->data_surf); + isp_free_surface(isp, isp->ipc_surf); + return -ENOMEM; + } + } + return 0; } void apple_isp_free_firmware_surface(struct apple_isp *isp) { + if (isp->main_tail_surf) + isp_free_surface(isp, isp->main_tail_surf); isp_free_surface(isp, isp->data_surf); isp_free_surface(isp, isp->ipc_surf); } @@ -491,12 +536,14 @@ static void isp_write_bootargs_h16(struct apple_isp *isp, void *virt, memset(&args, 0, sizeof(args)); args.ipc_iova = isp->ipc_surf->iova; args.shared_base = isp->fw.heap_top & 0xffffffff; - args.shared_size = ISP_H16_SHARED_SIZE; + args.shared_size = isp->hw->shared_size ?: ISP_H16_SHARED_SIZE; args.extra_iova = isp->extra_surf->iova; args.extra_size = isp->extra_surf->size; args.platform_id = isp->platform_id; args.ipc_queue_size = (u64)args_offset + 1; args.unk_68 = 0x40; + put_unaligned_le64(isp->cam_connections_scheme, + &args.descriptor[ISP_H16_DESC_CAM_SCHEME]); args.descriptor[ISP_H16_DESC_NO_OPTICAL_CARD_ID] = 1; memcpy(virt, &args, sizeof(args)); } @@ -825,12 +872,26 @@ static int isp_set_dsid_clr(struct apple_isp *isp) int err; /* - * The H17 firmware takes no PMU base, and its single broadcast-clear - * window in the multi-window form. + * The H17 firmware takes no PMU base, and its broadcast-clear windows + * in the multi-window form. */ - if (hw->fw_abi == ISP_FW_ABI_H17) - return isp_cmd_set_dsid_clr_multi_bc(isp, hw->dsid_clr_base0, + if (hw->fw_abi == ISP_FW_ABI_H17) { + const u64 used[] = { hw->dsid_clr_base0, hw->dsid_clr_base1, + hw->dsid_clr_base2, hw->dsid_clr_base3 }; + u64 bases[ISP_DSID_MULTI_BC_MAX]; + unsigned int slots = max_t(unsigned int, hw->dsid_count, + hw->dsid_slots); + + if (WARN_ON(hw->dsid_count > ARRAY_SIZE(used) || + slots > ARRAY_SIZE(bases))) + return -EINVAL; + + for (unsigned int i = 0; i < slots; i++) + bases[i] = i < hw->dsid_count ? used[i] : U64_MAX; + + return isp_cmd_set_dsid_clr_multi_bc(isp, bases, slots, hw->dsid_clr_range0); + } err = isp_cmd_set_isp_pmu_base(isp, hw->pmu_base); if (err) diff --git a/drivers/media/platform/apple/isp/isp-iommu.c b/drivers/media/platform/apple/isp/isp-iommu.c index ade5a4a31f499..8e77a71f939e5 100644 --- a/drivers/media/platform/apple/isp/isp-iommu.c +++ b/drivers/media/platform/apple/isp/isp-iommu.c @@ -218,6 +218,35 @@ struct isp_surf *isp_alloc_surface_vmap(struct apple_isp *isp, u64 size) return surf; } +/* A surface at a fixed IOVA, outside the range the others come from. */ +struct isp_surf *isp_alloc_surface_at(struct apple_isp *isp, dma_addr_t iova, + u64 size) +{ + struct isp_surf *surf; + + surf = kzalloc_obj(*surf); + if (!surf) + return NULL; + + __isp_surf_init(isp, surf, size, false); + surf->iova = iova; + + if (isp_surf_alloc_pages(surf) < 0) + goto free_surf; + + if (isp_surf_iommu_map(isp, surf) < 0) + goto free_pages; + + refcount_set(&surf->refcount, 1); + return surf; + +free_pages: + isp_surf_free_pages(surf); +free_surf: + kfree(surf); + return NULL; +} + void isp_free_surface(struct apple_isp *isp, struct isp_surf *surf) { if (refcount_dec_and_test(&surf->refcount)) { diff --git a/drivers/media/platform/apple/isp/isp-iommu.h b/drivers/media/platform/apple/isp/isp-iommu.h index a7fce2713f584..3a999a8c5b3d5 100644 --- a/drivers/media/platform/apple/isp/isp-iommu.h +++ b/drivers/media/platform/apple/isp/isp-iommu.h @@ -10,6 +10,8 @@ struct isp_surf *__isp_alloc_surface(struct apple_isp *isp, u64 size, bool gc); #define isp_alloc_surface(isp, size) (__isp_alloc_surface(isp, size, false)) #define isp_alloc_surface_gc(isp, size) (__isp_alloc_surface(isp, size, true)) struct isp_surf *isp_alloc_surface_vmap(struct apple_isp *isp, u64 size); +struct isp_surf *isp_alloc_surface_at(struct apple_isp *isp, dma_addr_t iova, + u64 size); int isp_surf_vmap(struct apple_isp *isp, struct isp_surf *surf); void isp_free_surface(struct apple_isp *isp, struct isp_surf *surf); void isp_free_released_surfaces(struct apple_isp *isp);