Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -2723,6 +2723,10 @@ F: drivers/pwm/pwm-apple.c
F: drivers/reset/reset-apple-cio.c
F: drivers/rtc/rtc-macsmc.c
F: drivers/soc/apple/*
F: tools/asahi/isp-preview-flow-tests.py
F: tools/asahi/isp-preview-tests.py
F: tools/asahi/isp-tests.py
F: tools/asahi/isp/
F: tools/asahi/pmp-bootargs-tests.rs
F: drivers/spi/spi-apple.c
F: drivers/spmi/spmi-apple-controller.c
Expand Down
25 changes: 25 additions & 0 deletions drivers/media/platform/apple/isp/.kunitconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Full driver suites on arm64 (requires an arm64 QEMU):
# ./tools/testing/kunit/kunit.py run --arch=arm64 \
# --kunitconfig=drivers/media/platform/apple/isp
# The independent IPC layout suite can also run alone on UML.
CONFIG_KUNIT=y
CONFIG_COMPILE_TEST=y
CONFIG_OF=y
CONFIG_PM=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_SUPPORT_FILTER=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_MEDIA_PLATFORM_DRIVERS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_VIDEO_APPLE_ISP=y
CONFIG_VIDEO_APPLE_ISP_KUNIT_TEST=y
CONFIG_VIDEO_APPLE_ISP_IPC_LAYOUT_KUNIT_TEST=y
CONFIG_DRM=y
CONFIG_IOMMU_SUPPORT=y
CONFIG_FW_LOADER=y
CONFIG_DEBUG_KERNEL=y
CONFIG_PROVE_LOCKING=y
CONFIG_DEBUG_SPINLOCK=y
4 changes: 2 additions & 2 deletions drivers/media/platform/apple/isp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ config VIDEO_APPLE_ISP_KUNIT_TEST
depends on KUNIT=y || VIDEO_APPLE_ISP=m
default KUNIT_ALL_TESTS
help
Builds unit tests for the videobuf2 queue set-up and for the
shared memory the firmware frees into the Apple ISP driver. They
Builds unit tests for videobuf2 queue setup, return ownership, error
wakeups and shared memory the firmware frees into the ISP driver. They
need no hardware. Only useful for kernel developers running KUnit.

If unsure, say N.
Expand Down
16 changes: 13 additions & 3 deletions drivers/media/platform/apple/isp/isp-cam.c
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,9 @@ static int isp_ch_configure_capture(struct apple_isp *isp, u32 ch)
if (err)
return err;

err = isp_cmd_ch_mbnr_enable(isp, ch, 0, ISP_MBNR_MODE_ENABLE, 1);
err = isp_cmd_ch_mbnr_enable(isp, ch, 0,
isp->preview_26 ? ISP_MBNR_MODE_DISABLE :
ISP_MBNR_MODE_ENABLE, 1);
if (err)
return err;

Expand Down Expand Up @@ -538,6 +540,14 @@ static int isp_ch_configure_capture(struct apple_isp *isp, u32 ch)
return err;
}

/* Preview has no consumer for temporal history on this firmware. */
if (isp->preview_26) {
err = isp_cmd_apple_ch_temporal_filter_disable(isp, ch);
if (err)
return err;
return isp_ch_configure_pools(isp, ch);
}

err = isp_cmd_apple_ch_temporal_filter_start(isp, ch, isp->temporal_filter);
if (err)
return err;
Expand Down Expand Up @@ -655,8 +665,8 @@ int apple_isp_start_capture(struct apple_isp *isp)

void apple_isp_stop_capture(struct apple_isp *isp)
{
isp_cmd_ch_stop(isp, 0); // TODO channel mask
isp_cmd_ch_buffer_return(isp, isp->current_ch);
if (!isp_cmd_ch_stop(isp, 0)) /* TODO channel mask */
isp_cmd_ch_buffer_return(isp, isp->current_ch);

/* ... until its buffers are back. */
apple_isp_wdt_stop(isp);
Expand Down
6 changes: 5 additions & 1 deletion drivers/media/platform/apple/isp/isp-cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ static int cisp_send(struct apple_isp *isp, void *args, u32 insize, u32 outsize,
{
struct isp_channel *chan = isp->chan_io;
struct isp_message *req = &chan->req;
int err;
int err = READ_ONCE(isp->ipc_error);

/* Do not overwrite a command the firmware may still be reading. */
if (err)
return err;

req->arg0 = isp->cmd_iova;
req->arg1 = insize;
Expand Down
32 changes: 32 additions & 0 deletions drivers/media/platform/apple/isp/isp-drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,11 @@ static int apple_isp_init_presets(struct apple_isp *isp)
return 0;
}

/* Preview only; processing and image quality need separate qualification. */
static bool preview_26 = true;
module_param(preview_26, bool, 0400);
MODULE_PARM_DESC(preview_26, "Enable J613 preview on ISP firmware 26.6.2 (default: true; false disables)");

static const char * isp_fw2str(enum isp_firmware_version version)
{
switch (version) {
Expand All @@ -300,6 +305,8 @@ static const char * isp_fw2str(enum isp_firmware_version version)
return "13.5";
case ISP_FIRMWARE_V_14_7:
return "14.7";
case ISP_FIRMWARE_V_26_6_2:
return "26.6.2";
default:
return "unknown";
}
Expand Down Expand Up @@ -329,6 +336,8 @@ static enum isp_firmware_version isp_read_fw_version(struct device *dev,
/* not provided, see isp_check_firmware_version() */
break;
case 3:
if (ver[0] == 26 && ver[1] == 6 && ver[2] == 2)
return ISP_FIRMWARE_V_26_6_2;
if (ver[0] == 12 && ver[1] == 3 && ver[2] <= 1)
return ISP_FIRMWARE_V_12_3;
else if (ver[0] == 12 && ver[1] == 4 && ver[2] == 0)
Expand Down Expand Up @@ -379,6 +388,18 @@ isp_check_firmware_version(struct apple_isp *isp)
return compat;
}

static bool isp_preview26_admitted(struct device *dev,
enum isp_firmware_version compat)
{
if (compat != ISP_FIRMWARE_V_26_6_2 || !preview_26 ||
!of_machine_is_compatible("apple,j613"))
return false;
if (!of_device_is_compatible(dev->of_node, "apple,t8122-isp"))
return false;
return isp_read_fw_version(dev, "apple,firmware-version") ==
ISP_FIRMWARE_V_26_6_2;
}

static int apple_isp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
Expand Down Expand Up @@ -407,6 +428,17 @@ static int apple_isp_probe(struct platform_device *pdev)
* with unknown firmware.
*/
isp->fw_compat = isp_check_firmware_version(isp);
if (isp->hw->fw_abi != ISP_FW_ABI_H17 &&
(isp->fw_compat == ISP_FIRMWARE_V_26_6_2 ||
isp_read_fw_version(dev, "apple,firmware-version") ==
ISP_FIRMWARE_V_26_6_2)) {
if (!isp_preview26_admitted(dev, isp->fw_compat)) {
err = dev_err_probe(dev, -ENODEV,
"26.6.2 preview requires J613/T8122; preview_26=0 disables it\n");
goto free_isp;
}
isp->preview_26 = true;
}

err = of_property_read_u32(dev->of_node, "apple,platform-id",
&isp->platform_id);
Expand Down
6 changes: 6 additions & 0 deletions drivers/media/platform/apple/isp/isp-drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ enum isp_firmware_version {
ISP_FIRMWARE_V_12_4,
ISP_FIRMWARE_V_13_5,
ISP_FIRMWARE_V_14_7,
ISP_FIRMWARE_V_26_6_2,
};

struct isp_surf {
Expand Down Expand Up @@ -262,6 +263,7 @@ struct apple_isp {
struct device *dev;
const struct apple_isp_hw *hw;
enum isp_firmware_version fw_compat;
bool preview_26;
u32 platform_id;
u32 temporal_filter;
struct isp_preset *presets;
Expand Down Expand Up @@ -310,6 +312,10 @@ struct apple_isp {
} fw;
/* changes under video_lock once the video device is registered */
enum isp_fw_state fw_state;
/* First IPC failure; cleared only after fresh channel initialization. */
int ipc_error;
/* End the current receive invocation before resident reclamation. */
bool ipc_quiescing;

struct isp_surf *ipc_surf;
struct isp_surf *extra_surf;
Expand Down
50 changes: 40 additions & 10 deletions drivers/media/platform/apple/isp/isp-fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,16 +225,24 @@ static irqreturn_t apple_isp_isr(int irq, void *dev)
static irqreturn_t apple_isp_isr_thread(int irq, void *dev)
{
struct apple_isp *isp = dev;
bool more;

wake_up_all(&isp->wait);
do {
wake_up_all(&isp->wait);

ipc_chan_handle(isp, isp->chan_sm);
wake_up_all(&isp->wait); /* Some commands depend on sm */
more = ipc_chan_handle(isp, isp->chan_sm) == -EAGAIN;
wake_up_all(&isp->wait); /* Some commands depend on sm */

ipc_chan_handle(isp, isp->chan_tm);
more |= ipc_chan_handle(isp, isp->chan_tm) == -EAGAIN;
more |= ipc_chan_handle(isp, isp->chan_bt) == -EAGAIN;
wake_up_all(&isp->wait);

ipc_chan_handle(isp, isp->chan_bt);
wake_up_all(&isp->wait);
/* Fair turns across channels, without depending on another IRQ. */
cond_resched();
} while (more && !READ_ONCE(isp->ipc_error) &&
!READ_ONCE(isp->ipc_quiescing));

apple_isp_video_error(isp);

return IRQ_HANDLED;
}
Expand Down Expand Up @@ -789,6 +797,9 @@ static int isp_firmware_boot_stage3(struct apple_isp *isp)

static int isp_stop_command_processor(struct apple_isp *isp)
{
if (READ_ONCE(isp->ipc_error))
return READ_ONCE(isp->ipc_error);

#if 0
int res = isp_cmd_stop(isp, 0);
if (res) {
Expand Down Expand Up @@ -920,6 +931,7 @@ static int isp_firmware_boot(struct apple_isp *isp)
/* Stage 1 stops the coprocessor and powers it down itself. */
isp_err(isp, "failed firmware boot stage 1: %d\n", err);
isp_collect_gc_surface(isp);
cmpxchg(&isp->ipc_error, 0, err);
return err;
}

Expand All @@ -936,6 +948,9 @@ static int isp_firmware_boot(struct apple_isp *isp)
goto stop;
}

/* The old coprocessor stopped and all channel slots were initialized. */
WRITE_ONCE(isp->ipc_error, 0);

err = isp_enable_irq(isp);
if (err < 0) {
isp_err(isp, "failed to enable interrupts: %d\n", err);
Expand All @@ -953,8 +968,10 @@ static int isp_firmware_boot(struct apple_isp *isp)
return 0;

disable_irqs:
cmpxchg(&isp->ipc_error, 0, err);
isp_disable_irq(isp);
stop:
cmpxchg(&isp->ipc_error, 0, err);
isp_firmware_stop(isp);
return err;
}
Expand All @@ -969,6 +986,8 @@ static void isp_firmware_shutdown(struct apple_isp *isp)
*/
if (isp_stop_command_processor(isp))
dev_warn(isp->dev, "firmware did not suspend, stopping it\n");
/* Also stop a receive thread whose producer never leaves a gap. */
cmpxchg(&isp->ipc_error, 0, -ESHUTDOWN);
isp_disable_irq(isp);
isp_firmware_stop(isp);
}
Expand All @@ -991,7 +1010,7 @@ int apple_isp_firmware_boot(struct apple_isp *isp)
break;
case ISP_FW_RUNNING:
/* Resident firmware keeps running between streams. */
return 0;
return READ_ONCE(isp->ipc_error);
case ISP_FW_DEAD:
dev_err_ratelimited(isp->dev,
"firmware was stopped and cannot be restarted before the next boot\n");
Expand Down Expand Up @@ -1022,7 +1041,7 @@ int apple_isp_firmware_boot(struct apple_isp *isp)
/* The end of a stream or of the camera detection at probe */
void apple_isp_firmware_shutdown(struct apple_isp *isp)
{
if (!isp->hw->resident_fw) {
if (!isp->hw->resident_fw || READ_ONCE(isp->ipc_error)) {
apple_isp_firmware_halt(isp);
return;
}
Expand All @@ -1033,9 +1052,20 @@ void apple_isp_firmware_shutdown(struct apple_isp *isp)
* The stream has stopped, so free them now: wait for the interrupt
* thread to finish the free requests the firmware has already sent.
*/
if (isp->fw_state == ISP_FW_RUNNING)
synchronize_irq(isp->irq);
if (isp->fw_state != ISP_FW_RUNNING)
return;

/* Finish a fair receive round even if the producer keeps refilling. */
WRITE_ONCE(isp->ipc_quiescing, true);
disable_irq(isp->irq);
/* No receive handler can race this lifetime decision while disabled. */
if (READ_ONCE(isp->ipc_error)) {
apple_isp_firmware_halt(isp);
return;
}
isp_free_released_surfaces(isp);
WRITE_ONCE(isp->ipc_quiescing, false);
enable_irq(isp->irq);
}

/* Stop the firmware; resident firmware cannot be started again. */
Expand Down
27 changes: 23 additions & 4 deletions drivers/media/platform/apple/isp/isp-ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,27 @@ static inline bool chan_rx_done(struct apple_isp *isp, struct isp_channel *chan)

int ipc_chan_handle(struct apple_isp *isp, struct isp_channel *chan)
{
int err = 0;
int err = -EAGAIN;

mutex_lock(&chan->lock);
while (1) {
/* One ring per turn, even if the producer refills acknowledged slots. */
for (u32 i = 0; i < chan->num; i++) {
if (READ_ONCE(isp->ipc_error)) {
err = READ_ONCE(isp->ipc_error);
break;
}
chan_read_msg(isp, chan, &chan->req);
if (chan_rx_done(isp, chan)) {
err = 0;
break;
}
err = chan_handle_once(isp, chan);
if (err < 0) {
cmpxchg(&isp->ipc_error, 0, err);
wake_up_all(&isp->wait);
break;
}
err = -EAGAIN;
}
mutex_unlock(&chan->lock);

Expand All @@ -175,6 +183,10 @@ int ipc_chan_send(struct apple_isp *isp, struct isp_channel *chan,
unsigned long timeout)
{
long t;
int err = READ_ONCE(isp->ipc_error);

if (err)
return err;

chan_write_msg(isp, chan, &chan->req);
dma_wmb();
Expand All @@ -184,15 +196,22 @@ int ipc_chan_send(struct apple_isp *isp, struct isp_channel *chan,
if (!timeout)
return 0;

t = wait_event_timeout(isp->wait, chan_tx_done(isp, chan), timeout);
t = wait_event_timeout(isp->wait,
READ_ONCE(isp->ipc_error) || chan_tx_done(isp, chan),
timeout);
if (t == 0) {
dev_err(isp->dev,
"%s: timed out on request [0x%llx, 0x%llx, 0x%llx]\n",
chan->name, chan->req.arg0, chan->req.arg1,
chan->req.arg2);
return -ETIME;
cmpxchg(&isp->ipc_error, 0, -ETIME);
wake_up_all(&isp->wait);
return READ_ONCE(isp->ipc_error);
}

if (READ_ONCE(isp->ipc_error))
return READ_ONCE(isp->ipc_error);

isp_dbg(isp, "%s: request success (%ld)\n", chan->name, t);

return 0;
Expand Down
Loading
Loading