15dd0baa8Skettenis // SPDX-License-Identifier: GPL-2.0-only OR MIT
25dd0baa8Skettenis /* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
35dd0baa8Skettenis
45dd0baa8Skettenis #include <linux/align.h>
55dd0baa8Skettenis #include <linux/apple-mailbox.h>
65dd0baa8Skettenis #include <linux/bitmap.h>
75dd0baa8Skettenis #include <linux/clk.h>
85dd0baa8Skettenis #include <linux/completion.h>
95dd0baa8Skettenis #include <linux/component.h>
105dd0baa8Skettenis #include <linux/delay.h>
115dd0baa8Skettenis #include <linux/dma-mapping.h>
125dd0baa8Skettenis #include <linux/gpio/consumer.h>
135dd0baa8Skettenis #include <linux/iommu.h>
145dd0baa8Skettenis #include <linux/jiffies.h>
155dd0baa8Skettenis #include <linux/kernel.h>
165dd0baa8Skettenis #include <linux/module.h>
175dd0baa8Skettenis #include <linux/moduleparam.h>
185dd0baa8Skettenis #include <linux/of_address.h>
195dd0baa8Skettenis #include <linux/of_device.h>
205dd0baa8Skettenis #include <linux/of_platform.h>
215dd0baa8Skettenis #include <linux/slab.h>
225dd0baa8Skettenis #include <linux/soc/apple/rtkit.h>
235dd0baa8Skettenis #include <linux/string.h>
245dd0baa8Skettenis #include <linux/workqueue.h>
255dd0baa8Skettenis
265dd0baa8Skettenis #include <drm/drm_fb_dma_helper.h>
275dd0baa8Skettenis #include <drm/drm_fourcc.h>
285dd0baa8Skettenis #include <drm/drm_framebuffer.h>
295dd0baa8Skettenis #include <drm/drm_module.h>
305dd0baa8Skettenis #include <drm/drm_probe_helper.h>
315dd0baa8Skettenis #include <drm/drm_vblank.h>
325dd0baa8Skettenis
335dd0baa8Skettenis #include "afk.h"
345dd0baa8Skettenis #include "dcp.h"
355dd0baa8Skettenis #include "dcp-internal.h"
365dd0baa8Skettenis #include "iomfb.h"
375dd0baa8Skettenis #include "parser.h"
385dd0baa8Skettenis #include "trace.h"
395dd0baa8Skettenis
405dd0baa8Skettenis #define APPLE_DCP_COPROC_CPU_CONTROL 0x44
415dd0baa8Skettenis #define APPLE_DCP_COPROC_CPU_CONTROL_RUN BIT(4)
425dd0baa8Skettenis
435dd0baa8Skettenis #define DCP_BOOT_TIMEOUT msecs_to_jiffies(1000)
445dd0baa8Skettenis
455dd0baa8Skettenis static bool show_notch;
465dd0baa8Skettenis module_param(show_notch, bool, 0644);
475dd0baa8Skettenis MODULE_PARM_DESC(show_notch, "Use the full display height and shows the notch");
485dd0baa8Skettenis
495dd0baa8Skettenis /* HACK: moved here to avoid circular dependency between apple_drv and dcp */
dcp_drm_crtc_vblank(struct apple_crtc * crtc)505dd0baa8Skettenis void dcp_drm_crtc_vblank(struct apple_crtc *crtc)
515dd0baa8Skettenis {
525dd0baa8Skettenis unsigned long flags;
535dd0baa8Skettenis
545dd0baa8Skettenis spin_lock_irqsave(&crtc->base.dev->event_lock, flags);
555dd0baa8Skettenis if (crtc->event) {
565dd0baa8Skettenis drm_crtc_send_vblank_event(&crtc->base, crtc->event);
575dd0baa8Skettenis crtc->event = NULL;
585dd0baa8Skettenis }
595dd0baa8Skettenis spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags);
605dd0baa8Skettenis }
615dd0baa8Skettenis
dcp_set_dimensions(struct apple_dcp * dcp)625dd0baa8Skettenis void dcp_set_dimensions(struct apple_dcp *dcp)
635dd0baa8Skettenis {
645dd0baa8Skettenis int i;
655dd0baa8Skettenis int width_mm = dcp->width_mm;
665dd0baa8Skettenis int height_mm = dcp->height_mm;
675dd0baa8Skettenis
685dd0baa8Skettenis if (width_mm == 0 || height_mm == 0) {
695dd0baa8Skettenis width_mm = dcp->panel.width_mm;
705dd0baa8Skettenis height_mm = dcp->panel.height_mm;
715dd0baa8Skettenis }
725dd0baa8Skettenis
735dd0baa8Skettenis /* Set the connector info */
745dd0baa8Skettenis if (dcp->connector) {
755dd0baa8Skettenis struct drm_connector *connector = &dcp->connector->base;
765dd0baa8Skettenis
775dd0baa8Skettenis mutex_lock(&connector->dev->mode_config.mutex);
785dd0baa8Skettenis connector->display_info.width_mm = width_mm;
795dd0baa8Skettenis connector->display_info.height_mm = height_mm;
805dd0baa8Skettenis mutex_unlock(&connector->dev->mode_config.mutex);
815dd0baa8Skettenis }
825dd0baa8Skettenis
835dd0baa8Skettenis /*
845dd0baa8Skettenis * Fix up any probed modes. Modes are created when parsing
855dd0baa8Skettenis * TimingElements, dimensions are calculated when parsing
865dd0baa8Skettenis * DisplayAttributes, and TimingElements may be sent first
875dd0baa8Skettenis */
885dd0baa8Skettenis for (i = 0; i < dcp->nr_modes; ++i) {
895dd0baa8Skettenis dcp->modes[i].mode.width_mm = width_mm;
905dd0baa8Skettenis dcp->modes[i].mode.height_mm = height_mm;
915dd0baa8Skettenis }
925dd0baa8Skettenis }
935dd0baa8Skettenis
dcp_has_panel(struct apple_dcp * dcp)945dd0baa8Skettenis bool dcp_has_panel(struct apple_dcp *dcp)
955dd0baa8Skettenis {
965dd0baa8Skettenis return dcp->panel.width_mm > 0;
975dd0baa8Skettenis }
985dd0baa8Skettenis
995dd0baa8Skettenis /*
1005dd0baa8Skettenis * Helper to send a DRM vblank event. We do not know how call swap_submit_dcp
1015dd0baa8Skettenis * without surfaces. To avoid timeouts in drm_atomic_helper_wait_for_vblanks
1025dd0baa8Skettenis * send a vblank event via a workqueue.
1035dd0baa8Skettenis */
dcp_delayed_vblank(struct work_struct * work)1045dd0baa8Skettenis static void dcp_delayed_vblank(struct work_struct *work)
1055dd0baa8Skettenis {
1065dd0baa8Skettenis struct apple_dcp *dcp;
1075dd0baa8Skettenis
1085dd0baa8Skettenis dcp = container_of(work, struct apple_dcp, vblank_wq);
1095dd0baa8Skettenis mdelay(5);
1105dd0baa8Skettenis dcp_drm_crtc_vblank(dcp->crtc);
1115dd0baa8Skettenis }
1125dd0baa8Skettenis
dcp_recv_msg(void * cookie,u8 endpoint,u64 message)1135dd0baa8Skettenis static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message)
1145dd0baa8Skettenis {
1155dd0baa8Skettenis struct apple_dcp *dcp = cookie;
1165dd0baa8Skettenis
1175dd0baa8Skettenis trace_dcp_recv_msg(dcp, endpoint, message);
1185dd0baa8Skettenis
1195dd0baa8Skettenis switch (endpoint) {
1205dd0baa8Skettenis case IOMFB_ENDPOINT:
1215dd0baa8Skettenis return iomfb_recv_msg(dcp, message);
1225dd0baa8Skettenis case SYSTEM_ENDPOINT:
1235dd0baa8Skettenis afk_receive_message(dcp->systemep, message);
1245dd0baa8Skettenis return;
1255dd0baa8Skettenis case DISP0_ENDPOINT:
1265dd0baa8Skettenis afk_receive_message(dcp->ibootep, message);
1275dd0baa8Skettenis return;
1285dd0baa8Skettenis case DPTX_ENDPOINT:
1295dd0baa8Skettenis afk_receive_message(dcp->dptxep, message);
1305dd0baa8Skettenis return;
1315dd0baa8Skettenis default:
132*44a5d259Skettenis WARN(endpoint, "unknown DCP endpoint %hhu\n", endpoint);
1335dd0baa8Skettenis }
1345dd0baa8Skettenis }
1355dd0baa8Skettenis
dcp_rtk_crashed(void * cookie)1365dd0baa8Skettenis static void dcp_rtk_crashed(void *cookie)
1375dd0baa8Skettenis {
1385dd0baa8Skettenis struct apple_dcp *dcp = cookie;
1395dd0baa8Skettenis
1405dd0baa8Skettenis dcp->crashed = true;
141*44a5d259Skettenis dev_err(dcp->dev, "DCP has crashed\n");
1425dd0baa8Skettenis if (dcp->connector) {
1435dd0baa8Skettenis dcp->connector->connected = 0;
1445dd0baa8Skettenis schedule_work(&dcp->connector->hotplug_wq);
1455dd0baa8Skettenis }
1465dd0baa8Skettenis complete(&dcp->start_done);
1475dd0baa8Skettenis }
1485dd0baa8Skettenis
dcp_rtk_shmem_setup(void * cookie,struct apple_rtkit_shmem * bfr)1495dd0baa8Skettenis static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr)
1505dd0baa8Skettenis {
1515dd0baa8Skettenis struct apple_dcp *dcp = cookie;
1525dd0baa8Skettenis
1535dd0baa8Skettenis if (bfr->iova) {
1545dd0baa8Skettenis struct iommu_domain *domain =
1555dd0baa8Skettenis iommu_get_domain_for_dev(dcp->dev);
1565dd0baa8Skettenis phys_addr_t phy_addr;
1575dd0baa8Skettenis
1585dd0baa8Skettenis if (!domain)
1595dd0baa8Skettenis return -ENOMEM;
1605dd0baa8Skettenis
1615dd0baa8Skettenis // TODO: get map from device-tree
1625dd0baa8Skettenis phy_addr = iommu_iova_to_phys(domain, bfr->iova);
1635dd0baa8Skettenis if (!phy_addr)
1645dd0baa8Skettenis return -ENOMEM;
1655dd0baa8Skettenis
1665dd0baa8Skettenis // TODO: verify phy_addr, cache attribute
1675dd0baa8Skettenis bfr->buffer = memremap(phy_addr, bfr->size, MEMREMAP_WB);
1685dd0baa8Skettenis if (!bfr->buffer)
1695dd0baa8Skettenis return -ENOMEM;
1705dd0baa8Skettenis
1715dd0baa8Skettenis bfr->is_mapped = true;
1725dd0baa8Skettenis dev_info(dcp->dev,
173*44a5d259Skettenis "shmem_setup: iova: %lx -> pa: %lx -> iomem: %lx\n",
1745dd0baa8Skettenis (uintptr_t)bfr->iova, (uintptr_t)phy_addr,
1755dd0baa8Skettenis (uintptr_t)bfr->buffer);
1765dd0baa8Skettenis } else {
1775dd0baa8Skettenis bfr->buffer = dma_alloc_coherent(dcp->dev, bfr->size,
1785dd0baa8Skettenis &bfr->iova, GFP_KERNEL);
1795dd0baa8Skettenis if (!bfr->buffer)
1805dd0baa8Skettenis return -ENOMEM;
1815dd0baa8Skettenis
182*44a5d259Skettenis dev_info(dcp->dev, "shmem_setup: iova: %lx, buffer: %lx\n",
1835dd0baa8Skettenis (uintptr_t)bfr->iova, (uintptr_t)bfr->buffer);
1845dd0baa8Skettenis }
1855dd0baa8Skettenis
1865dd0baa8Skettenis return 0;
1875dd0baa8Skettenis }
1885dd0baa8Skettenis
dcp_rtk_shmem_destroy(void * cookie,struct apple_rtkit_shmem * bfr)1895dd0baa8Skettenis static void dcp_rtk_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr)
1905dd0baa8Skettenis {
1915dd0baa8Skettenis struct apple_dcp *dcp = cookie;
1925dd0baa8Skettenis
1935dd0baa8Skettenis if (bfr->is_mapped)
1945dd0baa8Skettenis memunmap(bfr->buffer);
1955dd0baa8Skettenis else
1965dd0baa8Skettenis dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, bfr->iova);
1975dd0baa8Skettenis }
1985dd0baa8Skettenis
1995dd0baa8Skettenis static struct apple_rtkit_ops rtkit_ops = {
2005dd0baa8Skettenis .crashed = dcp_rtk_crashed,
2015dd0baa8Skettenis .recv_message = dcp_recv_msg,
2025dd0baa8Skettenis .shmem_setup = dcp_rtk_shmem_setup,
2035dd0baa8Skettenis .shmem_destroy = dcp_rtk_shmem_destroy,
2045dd0baa8Skettenis };
2055dd0baa8Skettenis
dcp_send_message(struct apple_dcp * dcp,u8 endpoint,u64 message)2065dd0baa8Skettenis void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message)
2075dd0baa8Skettenis {
2085dd0baa8Skettenis trace_dcp_send_msg(dcp, endpoint, message);
2095dd0baa8Skettenis apple_rtkit_send_message(dcp->rtk, endpoint, message, NULL,
2105dd0baa8Skettenis true);
2115dd0baa8Skettenis }
2125dd0baa8Skettenis
dcp_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)2135dd0baa8Skettenis int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
2145dd0baa8Skettenis {
2155dd0baa8Skettenis struct platform_device *pdev = to_apple_crtc(crtc)->dcp;
2165dd0baa8Skettenis struct apple_dcp *dcp = platform_get_drvdata(pdev);
2175dd0baa8Skettenis struct drm_plane_state *new_state;
2185dd0baa8Skettenis struct drm_plane *plane;
2195dd0baa8Skettenis struct drm_crtc_state *crtc_state;
2205dd0baa8Skettenis int plane_idx, plane_count = 0;
2215dd0baa8Skettenis bool needs_modeset;
2225dd0baa8Skettenis
2235dd0baa8Skettenis if (dcp->crashed)
2245dd0baa8Skettenis return -EINVAL;
2255dd0baa8Skettenis
2265dd0baa8Skettenis crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
2275dd0baa8Skettenis
2285dd0baa8Skettenis needs_modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode;
2295dd0baa8Skettenis if (!needs_modeset && !dcp->connector->connected) {
230*44a5d259Skettenis dev_err(dcp->dev, "crtc_atomic_check: disconnected but no modeset\n");
2315dd0baa8Skettenis return -EINVAL;
2325dd0baa8Skettenis }
2335dd0baa8Skettenis
2345dd0baa8Skettenis for_each_new_plane_in_state(state, plane, new_state, plane_idx) {
2355dd0baa8Skettenis /* skip planes not for this crtc */
2365dd0baa8Skettenis if (new_state->crtc != crtc)
2375dd0baa8Skettenis continue;
2385dd0baa8Skettenis
2395dd0baa8Skettenis plane_count += 1;
2405dd0baa8Skettenis }
2415dd0baa8Skettenis
2425dd0baa8Skettenis if (plane_count > DCP_MAX_PLANES) {
243*44a5d259Skettenis dev_err(dcp->dev, "crtc_atomic_check: Blend supports only 2 layers!\n");
2445dd0baa8Skettenis return -EINVAL;
2455dd0baa8Skettenis }
2465dd0baa8Skettenis
2475dd0baa8Skettenis return 0;
2485dd0baa8Skettenis }
2495dd0baa8Skettenis EXPORT_SYMBOL_GPL(dcp_crtc_atomic_check);
2505dd0baa8Skettenis
dcp_get_connector_type(struct platform_device * pdev)2515dd0baa8Skettenis int dcp_get_connector_type(struct platform_device *pdev)
2525dd0baa8Skettenis {
2535dd0baa8Skettenis struct apple_dcp *dcp = platform_get_drvdata(pdev);
2545dd0baa8Skettenis
2555dd0baa8Skettenis return (dcp->connector_type);
2565dd0baa8Skettenis }
2575dd0baa8Skettenis EXPORT_SYMBOL_GPL(dcp_get_connector_type);
2585dd0baa8Skettenis
2595dd0baa8Skettenis #define DPTX_CONNECT_TIMEOUT msecs_to_jiffies(1000)
2605dd0baa8Skettenis
dcp_dptx_connect(struct apple_dcp * dcp,u32 port)2615dd0baa8Skettenis static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port)
2625dd0baa8Skettenis {
2635dd0baa8Skettenis int ret = 0;
2645dd0baa8Skettenis
2655dd0baa8Skettenis if (!dcp->phy) {
2665dd0baa8Skettenis dev_warn(dcp->dev, "dcp_dptx_connect: missing phy\n");
2675dd0baa8Skettenis return -ENODEV;
2685dd0baa8Skettenis }
2695dd0baa8Skettenis dev_info(dcp->dev, "%s(port=%d)\n", __func__, port);
2705dd0baa8Skettenis
2715dd0baa8Skettenis mutex_lock(&dcp->hpd_mutex);
2725dd0baa8Skettenis if (!dcp->dptxport[port].enabled) {
2735dd0baa8Skettenis dev_warn(dcp->dev, "dcp_dptx_connect: dptx service for port %d not enabled\n", port);
2745dd0baa8Skettenis ret = -ENODEV;
2755dd0baa8Skettenis goto out_unlock;
2765dd0baa8Skettenis }
2775dd0baa8Skettenis
2785dd0baa8Skettenis if (dcp->dptxport[port].connected)
2795dd0baa8Skettenis goto out_unlock;
2805dd0baa8Skettenis
2815dd0baa8Skettenis reinit_completion(&dcp->dptxport[port].linkcfg_completion);
2825dd0baa8Skettenis dcp->dptxport[port].atcphy = dcp->phy;
2835dd0baa8Skettenis dptxport_connect(dcp->dptxport[port].service, 0, dcp->dptx_phy, dcp->dptx_die);
2845dd0baa8Skettenis dptxport_request_display(dcp->dptxport[port].service);
2855dd0baa8Skettenis dcp->dptxport[port].connected = true;
2865dd0baa8Skettenis
2875dd0baa8Skettenis mutex_unlock(&dcp->hpd_mutex);
2885dd0baa8Skettenis ret = wait_for_completion_timeout(&dcp->dptxport[port].linkcfg_completion,
2895dd0baa8Skettenis DPTX_CONNECT_TIMEOUT);
2905dd0baa8Skettenis if (ret < 0)
2915dd0baa8Skettenis dev_warn(dcp->dev, "dcp_dptx_connect: port %d link complete failed:%d\n",
2925dd0baa8Skettenis port, ret);
2935dd0baa8Skettenis else
2945dd0baa8Skettenis dev_dbg(dcp->dev, "dcp_dptx_connect: waited %d ms for link\n",
2955dd0baa8Skettenis jiffies_to_msecs(DPTX_CONNECT_TIMEOUT - ret));
2965dd0baa8Skettenis
2975dd0baa8Skettenis usleep_range(5, 10);
2985dd0baa8Skettenis
2995dd0baa8Skettenis return 0;
3005dd0baa8Skettenis
3015dd0baa8Skettenis out_unlock:
3025dd0baa8Skettenis mutex_unlock(&dcp->hpd_mutex);
3035dd0baa8Skettenis return ret;
3045dd0baa8Skettenis }
3055dd0baa8Skettenis
dcp_dptx_disconnect(struct apple_dcp * dcp,u32 port)3065dd0baa8Skettenis static int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port)
3075dd0baa8Skettenis {
3085dd0baa8Skettenis dev_info(dcp->dev, "%s(port=%d)\n", __func__, port);
3095dd0baa8Skettenis
3105dd0baa8Skettenis mutex_lock(&dcp->hpd_mutex);
3115dd0baa8Skettenis if (dcp->dptxport[port].enabled && dcp->dptxport[port].connected) {
3125dd0baa8Skettenis dptxport_release_display(dcp->dptxport[port].service);
3135dd0baa8Skettenis dcp->dptxport[port].connected = false;
3145dd0baa8Skettenis }
3155dd0baa8Skettenis mutex_unlock(&dcp->hpd_mutex);
3165dd0baa8Skettenis
3175dd0baa8Skettenis return 0;
3185dd0baa8Skettenis }
3195dd0baa8Skettenis
dcp_dp2hdmi_hpd(int irq,void * data)3205dd0baa8Skettenis static irqreturn_t dcp_dp2hdmi_hpd(int irq, void *data)
3215dd0baa8Skettenis {
3225dd0baa8Skettenis struct apple_dcp *dcp = data;
3235dd0baa8Skettenis bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
3245dd0baa8Skettenis
3255dd0baa8Skettenis /* do nothing on disconnect and trust that dcp detects it itself.
3265dd0baa8Skettenis * Parallel disconnect HPDs result drm disabling the CRTC even when it
3275dd0baa8Skettenis * should not.
3285dd0baa8Skettenis * The interrupt should be changed to rising but for now the disconnect
3295dd0baa8Skettenis * IRQs might be helpful for debugging.
3305dd0baa8Skettenis */
3315dd0baa8Skettenis dev_info(dcp->dev, "DP2HDMI HPD irq, connected:%d\n", connected);
3325dd0baa8Skettenis
3335dd0baa8Skettenis if (connected)
3345dd0baa8Skettenis dcp_dptx_connect(dcp, 0);
3355dd0baa8Skettenis
3365dd0baa8Skettenis return IRQ_HANDLED;
3375dd0baa8Skettenis }
3385dd0baa8Skettenis
dcp_link(struct platform_device * pdev,struct apple_crtc * crtc,struct apple_connector * connector)3395dd0baa8Skettenis void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc,
3405dd0baa8Skettenis struct apple_connector *connector)
3415dd0baa8Skettenis {
3425dd0baa8Skettenis struct apple_dcp *dcp = platform_get_drvdata(pdev);
3435dd0baa8Skettenis
3445dd0baa8Skettenis dcp->crtc = crtc;
3455dd0baa8Skettenis dcp->connector = connector;
3465dd0baa8Skettenis }
3475dd0baa8Skettenis EXPORT_SYMBOL_GPL(dcp_link);
3485dd0baa8Skettenis
dcp_start(struct platform_device * pdev)3495dd0baa8Skettenis int dcp_start(struct platform_device *pdev)
3505dd0baa8Skettenis {
3515dd0baa8Skettenis struct apple_dcp *dcp = platform_get_drvdata(pdev);
3525dd0baa8Skettenis int ret;
3535dd0baa8Skettenis
3545dd0baa8Skettenis init_completion(&dcp->start_done);
3555dd0baa8Skettenis
3565dd0baa8Skettenis /* start RTKit endpoints */
3575dd0baa8Skettenis ret = systemep_init(dcp);
3585dd0baa8Skettenis if (ret)
359*44a5d259Skettenis dev_warn(dcp->dev, "Failed to start system endpoint: %d\n", ret);
3605dd0baa8Skettenis
3615dd0baa8Skettenis if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) {
3625dd0baa8Skettenis ret = ibootep_init(dcp);
3635dd0baa8Skettenis if (ret)
364*44a5d259Skettenis dev_warn(dcp->dev, "Failed to start IBOOT endpoint: %d\n",
3655dd0baa8Skettenis ret);
3665dd0baa8Skettenis
3675dd0baa8Skettenis ret = dptxep_init(dcp);
3685dd0baa8Skettenis if (ret)
369*44a5d259Skettenis dev_warn(dcp->dev, "Failed to start DPTX endpoint: %d\n",
3705dd0baa8Skettenis ret);
3715dd0baa8Skettenis else if (dcp->dptxport[0].enabled) {
3725dd0baa8Skettenis bool connected;
3735dd0baa8Skettenis /* force disconnect on start - necessary if the display
3745dd0baa8Skettenis * is already up from m1n1
3755dd0baa8Skettenis */
3765dd0baa8Skettenis dptxport_set_hpd(dcp->dptxport[0].service, false);
3775dd0baa8Skettenis dptxport_release_display(dcp->dptxport[0].service);
3785dd0baa8Skettenis usleep_range(10 * USEC_PER_MSEC, 25 * USEC_PER_MSEC);
3795dd0baa8Skettenis
3805dd0baa8Skettenis connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
3815dd0baa8Skettenis dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected);
3825dd0baa8Skettenis
3835dd0baa8Skettenis // necessary on j473/j474 but not on j314c
3845dd0baa8Skettenis if (connected)
3855dd0baa8Skettenis dcp_dptx_connect(dcp, 0);
3865dd0baa8Skettenis }
3875dd0baa8Skettenis } else if (dcp->phy)
3885dd0baa8Skettenis dev_warn(dcp->dev, "OS firmware incompatible with dptxport EP\n");
3895dd0baa8Skettenis
3905dd0baa8Skettenis ret = iomfb_start_rtkit(dcp);
3915dd0baa8Skettenis if (ret)
392*44a5d259Skettenis dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d\n", ret);
3935dd0baa8Skettenis
3945dd0baa8Skettenis return ret;
3955dd0baa8Skettenis }
3965dd0baa8Skettenis EXPORT_SYMBOL(dcp_start);
3975dd0baa8Skettenis
dcp_enable_dp2hdmi_hpd(struct apple_dcp * dcp)3985dd0baa8Skettenis static int dcp_enable_dp2hdmi_hpd(struct apple_dcp *dcp)
3995dd0baa8Skettenis {
4005dd0baa8Skettenis if (dcp->hdmi_hpd_irq)
4015dd0baa8Skettenis enable_irq(dcp->hdmi_hpd_irq);
4025dd0baa8Skettenis
4035dd0baa8Skettenis return 0;
4045dd0baa8Skettenis }
4055dd0baa8Skettenis
dcp_wait_ready(struct platform_device * pdev,u64 timeout)4065dd0baa8Skettenis int dcp_wait_ready(struct platform_device *pdev, u64 timeout)
4075dd0baa8Skettenis {
4085dd0baa8Skettenis struct apple_dcp *dcp = platform_get_drvdata(pdev);
4095dd0baa8Skettenis int ret;
4105dd0baa8Skettenis
4115dd0baa8Skettenis if (dcp->crashed)
4125dd0baa8Skettenis return -ENODEV;
4135dd0baa8Skettenis if (dcp->active)
4145dd0baa8Skettenis return dcp_enable_dp2hdmi_hpd(dcp);;
4155dd0baa8Skettenis if (timeout <= 0)
4165dd0baa8Skettenis return -ETIMEDOUT;
4175dd0baa8Skettenis
4185dd0baa8Skettenis ret = wait_for_completion_timeout(&dcp->start_done, timeout);
4195dd0baa8Skettenis if (ret < 0)
4205dd0baa8Skettenis return ret;
4215dd0baa8Skettenis
4225dd0baa8Skettenis if (dcp->crashed)
4235dd0baa8Skettenis return -ENODEV;
4245dd0baa8Skettenis
4255dd0baa8Skettenis if (dcp->active)
4265dd0baa8Skettenis dcp_enable_dp2hdmi_hpd(dcp);
4275dd0baa8Skettenis
4285dd0baa8Skettenis return dcp->active ? 0 : -ETIMEDOUT;
4295dd0baa8Skettenis }
4305dd0baa8Skettenis EXPORT_SYMBOL(dcp_wait_ready);
4315dd0baa8Skettenis
dcp_sleep(struct apple_dcp * dcp)4325dd0baa8Skettenis static void __maybe_unused dcp_sleep(struct apple_dcp *dcp)
4335dd0baa8Skettenis {
4345dd0baa8Skettenis switch (dcp->fw_compat) {
4355dd0baa8Skettenis case DCP_FIRMWARE_V_12_3:
4365dd0baa8Skettenis iomfb_sleep_v12_3(dcp);
4375dd0baa8Skettenis break;
4385dd0baa8Skettenis case DCP_FIRMWARE_V_13_5:
4395dd0baa8Skettenis iomfb_sleep_v13_3(dcp);
4405dd0baa8Skettenis break;
4415dd0baa8Skettenis default:
4425dd0baa8Skettenis WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
4435dd0baa8Skettenis break;
4445dd0baa8Skettenis }
4455dd0baa8Skettenis }
4465dd0baa8Skettenis
dcp_poweron(struct platform_device * pdev)4475dd0baa8Skettenis void dcp_poweron(struct platform_device *pdev)
4485dd0baa8Skettenis {
4495dd0baa8Skettenis struct apple_dcp *dcp = platform_get_drvdata(pdev);
4505dd0baa8Skettenis
4515dd0baa8Skettenis if (dcp->hdmi_hpd) {
4525dd0baa8Skettenis bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
4535dd0baa8Skettenis dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected);
4545dd0baa8Skettenis
4555dd0baa8Skettenis if (connected)
4565dd0baa8Skettenis dcp_dptx_connect(dcp, 0);
4575dd0baa8Skettenis }
4585dd0baa8Skettenis
4595dd0baa8Skettenis switch (dcp->fw_compat) {
4605dd0baa8Skettenis case DCP_FIRMWARE_V_12_3:
4615dd0baa8Skettenis iomfb_poweron_v12_3(dcp);
4625dd0baa8Skettenis break;
4635dd0baa8Skettenis case DCP_FIRMWARE_V_13_5:
4645dd0baa8Skettenis iomfb_poweron_v13_3(dcp);
4655dd0baa8Skettenis break;
4665dd0baa8Skettenis default:
4675dd0baa8Skettenis WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
4685dd0baa8Skettenis break;
4695dd0baa8Skettenis }
4705dd0baa8Skettenis }
4715dd0baa8Skettenis EXPORT_SYMBOL(dcp_poweron);
4725dd0baa8Skettenis
dcp_poweroff(struct platform_device * pdev)4735dd0baa8Skettenis void dcp_poweroff(struct platform_device *pdev)
4745dd0baa8Skettenis {
4755dd0baa8Skettenis struct apple_dcp *dcp = platform_get_drvdata(pdev);
4765dd0baa8Skettenis
4775dd0baa8Skettenis switch (dcp->fw_compat) {
4785dd0baa8Skettenis case DCP_FIRMWARE_V_12_3:
4795dd0baa8Skettenis iomfb_poweroff_v12_3(dcp);
4805dd0baa8Skettenis break;
4815dd0baa8Skettenis case DCP_FIRMWARE_V_13_5:
4825dd0baa8Skettenis iomfb_poweroff_v13_3(dcp);
4835dd0baa8Skettenis break;
4845dd0baa8Skettenis default:
4855dd0baa8Skettenis WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
4865dd0baa8Skettenis break;
4875dd0baa8Skettenis }
4885dd0baa8Skettenis
4895dd0baa8Skettenis if (dcp->hdmi_hpd) {
4905dd0baa8Skettenis bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
4915dd0baa8Skettenis if (!connected)
4925dd0baa8Skettenis dcp_dptx_disconnect(dcp, 0);
4935dd0baa8Skettenis }
4945dd0baa8Skettenis }
4955dd0baa8Skettenis EXPORT_SYMBOL(dcp_poweroff);
4965dd0baa8Skettenis
dcp_work_register_backlight(struct work_struct * work)4975dd0baa8Skettenis static void dcp_work_register_backlight(struct work_struct *work)
4985dd0baa8Skettenis {
4995dd0baa8Skettenis int ret;
5005dd0baa8Skettenis struct apple_dcp *dcp;
5015dd0baa8Skettenis
5025dd0baa8Skettenis dcp = container_of(work, struct apple_dcp, bl_register_wq);
5035dd0baa8Skettenis
5045dd0baa8Skettenis mutex_lock(&dcp->bl_register_mutex);
5055dd0baa8Skettenis if (dcp->brightness.bl_dev)
5065dd0baa8Skettenis goto out_unlock;
5075dd0baa8Skettenis
5085dd0baa8Skettenis /* try to register backlight device, */
5095dd0baa8Skettenis ret = dcp_backlight_register(dcp);
5105dd0baa8Skettenis if (ret) {
5115dd0baa8Skettenis dev_err(dcp->dev, "Unable to register backlight device\n");
5125dd0baa8Skettenis dcp->brightness.maximum = 0;
5135dd0baa8Skettenis }
5145dd0baa8Skettenis
5155dd0baa8Skettenis out_unlock:
5165dd0baa8Skettenis mutex_unlock(&dcp->bl_register_mutex);
5175dd0baa8Skettenis }
5185dd0baa8Skettenis
dcp_work_update_backlight(struct work_struct * work)51948fd60f3Skettenis static void dcp_work_update_backlight(struct work_struct *work)
52048fd60f3Skettenis {
52148fd60f3Skettenis struct apple_dcp *dcp;
52248fd60f3Skettenis
52348fd60f3Skettenis dcp = container_of(work, struct apple_dcp, bl_update_wq);
52448fd60f3Skettenis
52548fd60f3Skettenis dcp_backlight_update(dcp);
52648fd60f3Skettenis }
52748fd60f3Skettenis
dcp_create_piodma_iommu_dev(struct apple_dcp * dcp)5285dd0baa8Skettenis static int dcp_create_piodma_iommu_dev(struct apple_dcp *dcp)
5295dd0baa8Skettenis {
5305dd0baa8Skettenis int ret;
5315dd0baa8Skettenis struct device_node *node = of_get_child_by_name(dcp->dev->of_node, "piodma");
5325dd0baa8Skettenis
5335dd0baa8Skettenis if (!node)
5345dd0baa8Skettenis return dev_err_probe(dcp->dev, -ENODEV,
5355dd0baa8Skettenis "Failed to get piodma child DT node\n");
5365dd0baa8Skettenis
5375dd0baa8Skettenis dcp->piodma = of_platform_device_create(node, NULL, dcp->dev);
5385dd0baa8Skettenis if (!dcp->piodma) {
5395dd0baa8Skettenis of_node_put(node);
5405dd0baa8Skettenis return dev_err_probe(dcp->dev, -ENODEV, "Failed to gcreate piodma pdev for %pOF\n", node);
5415dd0baa8Skettenis }
5425dd0baa8Skettenis
5435dd0baa8Skettenis ret = dma_set_mask_and_coherent(&dcp->piodma->dev, DMA_BIT_MASK(42));
5445dd0baa8Skettenis if (ret)
5455dd0baa8Skettenis goto err_destroy_pdev;
5465dd0baa8Skettenis
5475dd0baa8Skettenis ret = of_dma_configure(&dcp->piodma->dev, node, true);
5485dd0baa8Skettenis if (ret) {
5495dd0baa8Skettenis ret = dev_err_probe(dcp->dev, ret,
5505dd0baa8Skettenis "Failed to configure IOMMU child DMA\n");
5515dd0baa8Skettenis goto err_destroy_pdev;
5525dd0baa8Skettenis }
5535dd0baa8Skettenis of_node_put(node);
5545dd0baa8Skettenis
5555dd0baa8Skettenis dcp->iommu_dom = iommu_domain_alloc(&platform_bus_type);
5565dd0baa8Skettenis if (!dcp->iommu_dom) {
5575dd0baa8Skettenis ret = -ENOMEM;
5585dd0baa8Skettenis goto err_destroy_pdev;
5595dd0baa8Skettenis }
5605dd0baa8Skettenis
5615dd0baa8Skettenis ret = iommu_attach_device(dcp->iommu_dom, &dcp->piodma->dev);
5625dd0baa8Skettenis if (ret) {
5635dd0baa8Skettenis ret = dev_err_probe(dcp->dev, ret,
5645dd0baa8Skettenis "Failed to attach IOMMU child domain\n");
5655dd0baa8Skettenis goto err_free_domain;
5665dd0baa8Skettenis }
5675dd0baa8Skettenis
5685dd0baa8Skettenis return 0;
5695dd0baa8Skettenis err_free_domain:
5705dd0baa8Skettenis iommu_domain_free(dcp->iommu_dom);
5715dd0baa8Skettenis err_destroy_pdev:
5725dd0baa8Skettenis of_node_put(node);
5735dd0baa8Skettenis of_platform_device_destroy(&dcp->piodma->dev, NULL);
5745dd0baa8Skettenis return ret;
5755dd0baa8Skettenis }
5765dd0baa8Skettenis
dcp_get_bw_scratch_reg(struct apple_dcp * dcp,u32 expected)5775dd0baa8Skettenis static int dcp_get_bw_scratch_reg(struct apple_dcp *dcp, u32 expected)
5785dd0baa8Skettenis {
5795dd0baa8Skettenis struct of_phandle_args ph_args;
5805dd0baa8Skettenis u32 addr_idx, disp_idx, offset;
5815dd0baa8Skettenis int ret;
5825dd0baa8Skettenis
5835dd0baa8Skettenis ret = of_parse_phandle_with_args(dcp->dev->of_node, "apple,bw-scratch",
5845dd0baa8Skettenis "#apple,bw-scratch-cells", 0, &ph_args);
5855dd0baa8Skettenis if (ret < 0) {
5865dd0baa8Skettenis dev_err(dcp->dev, "Failed to read 'apple,bw-scratch': %d\n", ret);
5875dd0baa8Skettenis return ret;
5885dd0baa8Skettenis }
5895dd0baa8Skettenis
5905dd0baa8Skettenis if (ph_args.args_count != 3) {
5915dd0baa8Skettenis dev_err(dcp->dev, "Unexpected 'apple,bw-scratch' arg count %d\n",
5925dd0baa8Skettenis ph_args.args_count);
5935dd0baa8Skettenis ret = -EINVAL;
5945dd0baa8Skettenis goto err_of_node_put;
5955dd0baa8Skettenis }
5965dd0baa8Skettenis
5975dd0baa8Skettenis addr_idx = ph_args.args[0];
5985dd0baa8Skettenis disp_idx = ph_args.args[1];
5995dd0baa8Skettenis offset = ph_args.args[2];
6005dd0baa8Skettenis
6015dd0baa8Skettenis if (disp_idx != expected || disp_idx >= MAX_DISP_REGISTERS) {
6025dd0baa8Skettenis dev_err(dcp->dev, "Unexpected disp_reg value in 'apple,bw-scratch': %d\n",
6035dd0baa8Skettenis disp_idx);
6045dd0baa8Skettenis ret = -EINVAL;
6055dd0baa8Skettenis goto err_of_node_put;
6065dd0baa8Skettenis }
6075dd0baa8Skettenis
6085dd0baa8Skettenis ret = of_address_to_resource(ph_args.np, addr_idx, &dcp->disp_bw_scratch_res);
6095dd0baa8Skettenis if (ret < 0) {
6105dd0baa8Skettenis dev_err(dcp->dev, "Failed to get 'apple,bw-scratch' resource %d from %pOF\n",
6115dd0baa8Skettenis addr_idx, ph_args.np);
6125dd0baa8Skettenis goto err_of_node_put;
6135dd0baa8Skettenis }
6145dd0baa8Skettenis if (offset > resource_size(&dcp->disp_bw_scratch_res) - 4) {
6155dd0baa8Skettenis ret = -EINVAL;
6165dd0baa8Skettenis goto err_of_node_put;
6175dd0baa8Skettenis }
6185dd0baa8Skettenis
6195dd0baa8Skettenis dcp->disp_registers[disp_idx] = &dcp->disp_bw_scratch_res;
6205dd0baa8Skettenis dcp->disp_bw_scratch_index = disp_idx;
6215dd0baa8Skettenis dcp->disp_bw_scratch_offset = offset;
6225dd0baa8Skettenis ret = 0;
6235dd0baa8Skettenis
6245dd0baa8Skettenis err_of_node_put:
6255dd0baa8Skettenis of_node_put(ph_args.np);
6265dd0baa8Skettenis return ret;
6275dd0baa8Skettenis }
6285dd0baa8Skettenis
dcp_get_bw_doorbell_reg(struct apple_dcp * dcp,u32 expected)6295dd0baa8Skettenis static int dcp_get_bw_doorbell_reg(struct apple_dcp *dcp, u32 expected)
6305dd0baa8Skettenis {
6315dd0baa8Skettenis struct of_phandle_args ph_args;
6325dd0baa8Skettenis u32 addr_idx, disp_idx;
6335dd0baa8Skettenis int ret;
6345dd0baa8Skettenis
6355dd0baa8Skettenis ret = of_parse_phandle_with_args(dcp->dev->of_node, "apple,bw-doorbell",
6365dd0baa8Skettenis "#apple,bw-doorbell-cells", 0, &ph_args);
6375dd0baa8Skettenis if (ret < 0) {
6385dd0baa8Skettenis dev_err(dcp->dev, "Failed to read 'apple,bw-doorbell': %d\n", ret);
6395dd0baa8Skettenis return ret;
6405dd0baa8Skettenis }
6415dd0baa8Skettenis
6425dd0baa8Skettenis if (ph_args.args_count != 2) {
6435dd0baa8Skettenis dev_err(dcp->dev, "Unexpected 'apple,bw-doorbell' arg count %d\n",
6445dd0baa8Skettenis ph_args.args_count);
6455dd0baa8Skettenis ret = -EINVAL;
6465dd0baa8Skettenis goto err_of_node_put;
6475dd0baa8Skettenis }
6485dd0baa8Skettenis
6495dd0baa8Skettenis addr_idx = ph_args.args[0];
6505dd0baa8Skettenis disp_idx = ph_args.args[1];
6515dd0baa8Skettenis
6525dd0baa8Skettenis if (disp_idx != expected || disp_idx >= MAX_DISP_REGISTERS) {
6535dd0baa8Skettenis dev_err(dcp->dev, "Unexpected disp_reg value in 'apple,bw-doorbell': %d\n",
6545dd0baa8Skettenis disp_idx);
6555dd0baa8Skettenis ret = -EINVAL;
6565dd0baa8Skettenis goto err_of_node_put;
6575dd0baa8Skettenis }
6585dd0baa8Skettenis
6595dd0baa8Skettenis ret = of_address_to_resource(ph_args.np, addr_idx, &dcp->disp_bw_doorbell_res);
6605dd0baa8Skettenis if (ret < 0) {
6615dd0baa8Skettenis dev_err(dcp->dev, "Failed to get 'apple,bw-doorbell' resource %d from %pOF\n",
6625dd0baa8Skettenis addr_idx, ph_args.np);
6635dd0baa8Skettenis goto err_of_node_put;
6645dd0baa8Skettenis }
6655dd0baa8Skettenis dcp->disp_bw_doorbell_index = disp_idx;
6665dd0baa8Skettenis dcp->disp_registers[disp_idx] = &dcp->disp_bw_doorbell_res;
6675dd0baa8Skettenis ret = 0;
6685dd0baa8Skettenis
6695dd0baa8Skettenis err_of_node_put:
6705dd0baa8Skettenis of_node_put(ph_args.np);
6715dd0baa8Skettenis return ret;
6725dd0baa8Skettenis }
6735dd0baa8Skettenis
dcp_get_disp_regs(struct apple_dcp * dcp)6745dd0baa8Skettenis static int dcp_get_disp_regs(struct apple_dcp *dcp)
6755dd0baa8Skettenis {
6765dd0baa8Skettenis struct platform_device *pdev = to_platform_device(dcp->dev);
6775dd0baa8Skettenis int count = pdev->num_resources - 1;
6785dd0baa8Skettenis int i, ret;
6795dd0baa8Skettenis
6805dd0baa8Skettenis if (count <= 0 || count > MAX_DISP_REGISTERS)
6815dd0baa8Skettenis return -EINVAL;
6825dd0baa8Skettenis
6835dd0baa8Skettenis for (i = 0; i < count; ++i) {
6845dd0baa8Skettenis dcp->disp_registers[i] =
6855dd0baa8Skettenis platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
6865dd0baa8Skettenis }
6875dd0baa8Skettenis
6885dd0baa8Skettenis /* load pmgr bandwidth scratch resource and offset */
6895dd0baa8Skettenis ret = dcp_get_bw_scratch_reg(dcp, count);
6905dd0baa8Skettenis if (ret < 0)
6915dd0baa8Skettenis return ret;
6925dd0baa8Skettenis count += 1;
6935dd0baa8Skettenis
6945dd0baa8Skettenis /* load pmgr bandwidth doorbell resource if present (only on t8103) */
6955dd0baa8Skettenis if (of_property_present(dcp->dev->of_node, "apple,bw-doorbell")) {
6965dd0baa8Skettenis ret = dcp_get_bw_doorbell_reg(dcp, count);
6975dd0baa8Skettenis if (ret < 0)
6985dd0baa8Skettenis return ret;
6995dd0baa8Skettenis count += 1;
7005dd0baa8Skettenis }
7015dd0baa8Skettenis
7025dd0baa8Skettenis dcp->nr_disp_registers = count;
7035dd0baa8Skettenis return 0;
7045dd0baa8Skettenis }
7055dd0baa8Skettenis
7065dd0baa8Skettenis #define DCP_FW_VERSION_MIN_LEN 3
7075dd0baa8Skettenis #define DCP_FW_VERSION_MAX_LEN 5
7085dd0baa8Skettenis #define DCP_FW_VERSION_STR_LEN (DCP_FW_VERSION_MAX_LEN * 4)
7095dd0baa8Skettenis
dcp_read_fw_version(struct device * dev,const char * name,char * version_str)7105dd0baa8Skettenis static int dcp_read_fw_version(struct device *dev, const char *name,
7115dd0baa8Skettenis char *version_str)
7125dd0baa8Skettenis {
7135dd0baa8Skettenis u32 ver[DCP_FW_VERSION_MAX_LEN];
7145dd0baa8Skettenis int len_str;
7155dd0baa8Skettenis int len;
7165dd0baa8Skettenis
7175dd0baa8Skettenis len = of_property_read_variable_u32_array(dev->of_node, name, ver,
7185dd0baa8Skettenis DCP_FW_VERSION_MIN_LEN,
7195dd0baa8Skettenis DCP_FW_VERSION_MAX_LEN);
7205dd0baa8Skettenis
7215dd0baa8Skettenis switch (len) {
7225dd0baa8Skettenis case 3:
7235dd0baa8Skettenis len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN,
7245dd0baa8Skettenis "%d.%d.%d", ver[0], ver[1], ver[2]);
7255dd0baa8Skettenis break;
7265dd0baa8Skettenis case 4:
7275dd0baa8Skettenis len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN,
7285dd0baa8Skettenis "%d.%d.%d.%d", ver[0], ver[1], ver[2],
7295dd0baa8Skettenis ver[3]);
7305dd0baa8Skettenis break;
7315dd0baa8Skettenis case 5:
7325dd0baa8Skettenis len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN,
7335dd0baa8Skettenis "%d.%d.%d.%d.%d", ver[0], ver[1], ver[2],
7345dd0baa8Skettenis ver[3], ver[4]);
7355dd0baa8Skettenis break;
7365dd0baa8Skettenis default:
7375dd0baa8Skettenis len_str = strscpy(version_str, "UNKNOWN",
7385dd0baa8Skettenis DCP_FW_VERSION_STR_LEN);
7395dd0baa8Skettenis if (len >= 0)
7405dd0baa8Skettenis len = -EOVERFLOW;
7415dd0baa8Skettenis break;
7425dd0baa8Skettenis }
7435dd0baa8Skettenis
7445dd0baa8Skettenis if (len_str >= DCP_FW_VERSION_STR_LEN)
7455dd0baa8Skettenis dev_warn(dev, "'%s' truncated: '%s'\n", name, version_str);
7465dd0baa8Skettenis
7475dd0baa8Skettenis return len;
7485dd0baa8Skettenis }
7495dd0baa8Skettenis
dcp_check_firmware_version(struct device * dev)7505dd0baa8Skettenis static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev)
7515dd0baa8Skettenis {
7525dd0baa8Skettenis char compat_str[DCP_FW_VERSION_STR_LEN];
7535dd0baa8Skettenis char fw_str[DCP_FW_VERSION_STR_LEN];
7545dd0baa8Skettenis int ret;
7555dd0baa8Skettenis
7565dd0baa8Skettenis /* firmware version is just informative */
7575dd0baa8Skettenis dcp_read_fw_version(dev, "apple,firmware-version", fw_str);
7585dd0baa8Skettenis
7595dd0baa8Skettenis ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str);
7605dd0baa8Skettenis if (ret < 0) {
7615dd0baa8Skettenis dev_err(dev, "Could not read 'apple,firmware-compat': %d\n", ret);
7625dd0baa8Skettenis return DCP_FIRMWARE_UNKNOWN;
7635dd0baa8Skettenis }
7645dd0baa8Skettenis
7655dd0baa8Skettenis if (strncmp(compat_str, "12.3.0", sizeof(compat_str)) == 0)
7665dd0baa8Skettenis return DCP_FIRMWARE_V_12_3;
7675dd0baa8Skettenis /*
7685dd0baa8Skettenis * m1n1 reports firmware version 13.5 as compatible with 13.3. This is
7695dd0baa8Skettenis * only true for the iomfb endpoint. The interface for the dptx-port
7705dd0baa8Skettenis * endpoint changed between 13.3 and 13.5. The driver will only support
7715dd0baa8Skettenis * firmware 13.5. Check the actual firmware version for compat version
7725dd0baa8Skettenis * 13.3 until m1n1 reports 13.5 as "firmware-compat".
7735dd0baa8Skettenis */
7745dd0baa8Skettenis else if ((strncmp(compat_str, "13.3.0", sizeof(compat_str)) == 0) &&
7755dd0baa8Skettenis (strncmp(fw_str, "13.5.0", sizeof(compat_str)) == 0))
7765dd0baa8Skettenis return DCP_FIRMWARE_V_13_5;
7775dd0baa8Skettenis else if (strncmp(compat_str, "13.5.0", sizeof(compat_str)) == 0)
7785dd0baa8Skettenis return DCP_FIRMWARE_V_13_5;
7795dd0baa8Skettenis
7805dd0baa8Skettenis dev_err(dev, "DCP firmware-compat %s (FW: %s) is not supported\n",
7815dd0baa8Skettenis compat_str, fw_str);
7825dd0baa8Skettenis
7835dd0baa8Skettenis return DCP_FIRMWARE_UNKNOWN;
7845dd0baa8Skettenis }
7855dd0baa8Skettenis
dcp_comp_bind(struct device * dev,struct device * main,void * data)7865dd0baa8Skettenis static int dcp_comp_bind(struct device *dev, struct device *main, void *data)
7875dd0baa8Skettenis {
7885dd0baa8Skettenis struct device_node *panel_np;
7895dd0baa8Skettenis struct apple_dcp *dcp = dev_get_drvdata(dev);
7905dd0baa8Skettenis u32 cpu_ctrl;
7915dd0baa8Skettenis int ret;
7925dd0baa8Skettenis
7935dd0baa8Skettenis ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(42));
7945dd0baa8Skettenis if (ret)
7955dd0baa8Skettenis return ret;
7965dd0baa8Skettenis
7975dd0baa8Skettenis dcp->coproc_reg = devm_platform_ioremap_resource_byname(to_platform_device(dev), "coproc");
7985dd0baa8Skettenis if (IS_ERR(dcp->coproc_reg))
7995dd0baa8Skettenis return PTR_ERR(dcp->coproc_reg);
8005dd0baa8Skettenis
8015dd0baa8Skettenis of_property_read_u32(dev->of_node, "apple,dcp-index",
8025dd0baa8Skettenis &dcp->index);
8035dd0baa8Skettenis of_property_read_u32(dev->of_node, "apple,dptx-phy",
8045dd0baa8Skettenis &dcp->dptx_phy);
8055dd0baa8Skettenis of_property_read_u32(dev->of_node, "apple,dptx-die",
8065dd0baa8Skettenis &dcp->dptx_die);
8075dd0baa8Skettenis if (dcp->index || dcp->dptx_phy || dcp->dptx_die)
8085dd0baa8Skettenis dev_info(dev, "DCP index:%u dptx target phy: %u dptx die: %u\n",
8095dd0baa8Skettenis dcp->index, dcp->dptx_phy, dcp->dptx_die);
8105dd0baa8Skettenis rw_init(&dcp->hpd_mutex, "aplhpd");
8115dd0baa8Skettenis
8125dd0baa8Skettenis if (!show_notch)
8135dd0baa8Skettenis ret = of_property_read_u32(dev->of_node, "apple,notch-height",
8145dd0baa8Skettenis &dcp->notch_height);
8155dd0baa8Skettenis
8165dd0baa8Skettenis if (dcp->notch_height > MAX_NOTCH_HEIGHT)
8175dd0baa8Skettenis dcp->notch_height = MAX_NOTCH_HEIGHT;
8185dd0baa8Skettenis if (dcp->notch_height > 0)
8195dd0baa8Skettenis dev_info(dev, "Detected display with notch of %u pixel\n", dcp->notch_height);
8205dd0baa8Skettenis
821172aa44cSkettenis /* initialize brightness scale to a sensible default to avoid divide by 0*/
8225dd0baa8Skettenis dcp->brightness.scale = 65536;
8235dd0baa8Skettenis panel_np = of_get_compatible_child(dev->of_node, "apple,panel-mini-led");
8245dd0baa8Skettenis if (panel_np)
8255dd0baa8Skettenis dcp->panel.has_mini_led = true;
8265dd0baa8Skettenis else
8275dd0baa8Skettenis panel_np = of_get_compatible_child(dev->of_node, "apple,panel");
8285dd0baa8Skettenis
8295dd0baa8Skettenis if (panel_np) {
8305dd0baa8Skettenis const char height_prop[2][16] = { "adj-height-mm", "height-mm" };
8315dd0baa8Skettenis
8325dd0baa8Skettenis if (of_device_is_available(panel_np)) {
8335dd0baa8Skettenis ret = of_property_read_u32(panel_np, "apple,max-brightness",
8345dd0baa8Skettenis &dcp->brightness.maximum);
8355dd0baa8Skettenis if (ret)
8365dd0baa8Skettenis dev_err(dev, "Missing property 'apple,max-brightness'\n");
8375dd0baa8Skettenis }
8385dd0baa8Skettenis
8395dd0baa8Skettenis of_property_read_u32(panel_np, "width-mm", &dcp->panel.width_mm);
8405dd0baa8Skettenis /* use adjusted height as long as the notch is hidden */
8415dd0baa8Skettenis of_property_read_u32(panel_np, height_prop[!dcp->notch_height],
8425dd0baa8Skettenis &dcp->panel.height_mm);
8435dd0baa8Skettenis
8445dd0baa8Skettenis of_node_put(panel_np);
8455dd0baa8Skettenis dcp->connector_type = DRM_MODE_CONNECTOR_eDP;
8465dd0baa8Skettenis INIT_WORK(&dcp->bl_register_wq, dcp_work_register_backlight);
8475dd0baa8Skettenis rw_init(&dcp->bl_register_mutex, "dcpbl");
84848fd60f3Skettenis INIT_WORK(&dcp->bl_update_wq, dcp_work_update_backlight);
8495dd0baa8Skettenis } else if (of_property_match_string(dev->of_node, "apple,connector-type", "HDMI-A") >= 0)
8505dd0baa8Skettenis dcp->connector_type = DRM_MODE_CONNECTOR_HDMIA;
8515dd0baa8Skettenis else if (of_property_match_string(dev->of_node, "apple,connector-type", "DP") >= 0)
8525dd0baa8Skettenis dcp->connector_type = DRM_MODE_CONNECTOR_DisplayPort;
8535dd0baa8Skettenis else if (of_property_match_string(dev->of_node, "apple,connector-type", "USB-C") >= 0)
8545dd0baa8Skettenis dcp->connector_type = DRM_MODE_CONNECTOR_USB;
8555dd0baa8Skettenis else
8565dd0baa8Skettenis dcp->connector_type = DRM_MODE_CONNECTOR_Unknown;
8575dd0baa8Skettenis
8585dd0baa8Skettenis ret = dcp_create_piodma_iommu_dev(dcp);
8595dd0baa8Skettenis if (ret)
8605dd0baa8Skettenis return dev_err_probe(dev, ret,
8615dd0baa8Skettenis "Failed to created PIODMA iommu child device");
8625dd0baa8Skettenis
8635dd0baa8Skettenis ret = dcp_get_disp_regs(dcp);
8645dd0baa8Skettenis if (ret) {
8655dd0baa8Skettenis dev_err(dev, "failed to find display registers\n");
8665dd0baa8Skettenis return ret;
8675dd0baa8Skettenis }
8685dd0baa8Skettenis
8695dd0baa8Skettenis dcp->clk = devm_clk_get(dev, NULL);
8705dd0baa8Skettenis if (IS_ERR(dcp->clk))
8715dd0baa8Skettenis return dev_err_probe(dev, PTR_ERR(dcp->clk),
8725dd0baa8Skettenis "Unable to find clock\n");
8735dd0baa8Skettenis
8745dd0baa8Skettenis bitmap_zero(dcp->memdesc_map, DCP_MAX_MAPPINGS);
8755dd0baa8Skettenis // TDOD: mem_desc IDs start at 1, for simplicity just skip '0' entry
8765dd0baa8Skettenis set_bit(0, dcp->memdesc_map);
8775dd0baa8Skettenis
8785dd0baa8Skettenis INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank);
8795dd0baa8Skettenis
8805dd0baa8Skettenis dcp->swapped_out_fbs =
8815dd0baa8Skettenis (struct list_head)LIST_HEAD_INIT(dcp->swapped_out_fbs);
8825dd0baa8Skettenis
8835dd0baa8Skettenis cpu_ctrl =
8845dd0baa8Skettenis readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL);
8855dd0baa8Skettenis writel_relaxed(cpu_ctrl | APPLE_DCP_COPROC_CPU_CONTROL_RUN,
8865dd0baa8Skettenis dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL);
8875dd0baa8Skettenis
8885dd0baa8Skettenis dcp->rtk = devm_apple_rtkit_init(dev, dcp, "mbox", 0, &rtkit_ops);
8895dd0baa8Skettenis if (IS_ERR(dcp->rtk))
8905dd0baa8Skettenis return dev_err_probe(dev, PTR_ERR(dcp->rtk),
891*44a5d259Skettenis "Failed to initialize RTKit\n");
8925dd0baa8Skettenis
8935dd0baa8Skettenis ret = apple_rtkit_wake(dcp->rtk);
8945dd0baa8Skettenis if (ret)
8955dd0baa8Skettenis return dev_err_probe(dev, ret,
896*44a5d259Skettenis "Failed to boot RTKit: %d\n", ret);
8975dd0baa8Skettenis return ret;
8985dd0baa8Skettenis }
8995dd0baa8Skettenis
9005dd0baa8Skettenis /*
9015dd0baa8Skettenis * We need to shutdown DCP before tearing down the display subsystem. Otherwise
9025dd0baa8Skettenis * the DCP will crash and briefly flash a green screen of death.
9035dd0baa8Skettenis */
dcp_comp_unbind(struct device * dev,struct device * main,void * data)9045dd0baa8Skettenis static void dcp_comp_unbind(struct device *dev, struct device *main, void *data)
9055dd0baa8Skettenis {
9065dd0baa8Skettenis struct apple_dcp *dcp = dev_get_drvdata(dev);
9075dd0baa8Skettenis
9085dd0baa8Skettenis if (dcp->hdmi_hpd_irq)
9095dd0baa8Skettenis disable_irq(dcp->hdmi_hpd_irq);
9105dd0baa8Skettenis
9115dd0baa8Skettenis if (dcp && dcp->shmem)
9125dd0baa8Skettenis iomfb_shutdown(dcp);
9135dd0baa8Skettenis
9145dd0baa8Skettenis if (dcp->piodma) {
9155dd0baa8Skettenis iommu_detach_device(dcp->iommu_dom, &dcp->piodma->dev);
9165dd0baa8Skettenis iommu_domain_free(dcp->iommu_dom);
9175dd0baa8Skettenis /* TODO: the piodma platform device has to be destroyed but
9185dd0baa8Skettenis * doing so leads to all kind of breakage.
9195dd0baa8Skettenis */
9205dd0baa8Skettenis // of_platform_device_destroy(&dcp->piodma->dev, NULL);
9215dd0baa8Skettenis dcp->piodma = NULL;
9225dd0baa8Skettenis }
9235dd0baa8Skettenis
9245dd0baa8Skettenis devm_clk_put(dev, dcp->clk);
9255dd0baa8Skettenis dcp->clk = NULL;
9265dd0baa8Skettenis }
9275dd0baa8Skettenis
9285dd0baa8Skettenis static const struct component_ops dcp_comp_ops = {
9295dd0baa8Skettenis .bind = dcp_comp_bind,
9305dd0baa8Skettenis .unbind = dcp_comp_unbind,
9315dd0baa8Skettenis };
9325dd0baa8Skettenis
dcp_platform_probe(struct platform_device * pdev)9335dd0baa8Skettenis static int dcp_platform_probe(struct platform_device *pdev)
9345dd0baa8Skettenis {
9355dd0baa8Skettenis enum dcp_firmware_version fw_compat;
9365dd0baa8Skettenis struct device *dev = &pdev->dev;
9375dd0baa8Skettenis struct apple_dcp *dcp;
9385dd0baa8Skettenis u32 mux_index;
9395dd0baa8Skettenis
9405dd0baa8Skettenis fw_compat = dcp_check_firmware_version(dev);
9415dd0baa8Skettenis if (fw_compat == DCP_FIRMWARE_UNKNOWN)
9425dd0baa8Skettenis return -ENODEV;
9435dd0baa8Skettenis
9445dd0baa8Skettenis /* Check for "apple,bw-scratch" to avoid probing appledrm with outdated
9455dd0baa8Skettenis * device trees. This prevents replacing simpledrm and ending up without
9465dd0baa8Skettenis * display.
9475dd0baa8Skettenis */
9485dd0baa8Skettenis if (!of_property_present(dev->of_node, "apple,bw-scratch"))
9495dd0baa8Skettenis return dev_err_probe(dev, -ENODEV, "Incompatible devicetree! "
9505dd0baa8Skettenis "Use devicetree matching this kernel.\n");
9515dd0baa8Skettenis
9525dd0baa8Skettenis dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL);
9535dd0baa8Skettenis if (!dcp)
9545dd0baa8Skettenis return -ENOMEM;
9555dd0baa8Skettenis
9565dd0baa8Skettenis dcp->fw_compat = fw_compat;
9575dd0baa8Skettenis dcp->dev = dev;
9585dd0baa8Skettenis dcp->hw = *(struct apple_dcp_hw_data *)of_device_get_match_data(dev);
9595dd0baa8Skettenis
9605dd0baa8Skettenis platform_set_drvdata(pdev, dcp);
9615dd0baa8Skettenis
9625dd0baa8Skettenis dcp->phy = devm_phy_optional_get(dev, "dp-phy");
9635dd0baa8Skettenis if (IS_ERR(dcp->phy)) {
964*44a5d259Skettenis dev_err(dev, "Failed to get dp-phy: %ld\n", PTR_ERR(dcp->phy));
9655dd0baa8Skettenis return PTR_ERR(dcp->phy);
9665dd0baa8Skettenis }
9675dd0baa8Skettenis if (dcp->phy) {
9685dd0baa8Skettenis int ret;
9695dd0baa8Skettenis /*
9705dd0baa8Skettenis * Request DP2HDMI related GPIOs as optional for DP-altmode
9715dd0baa8Skettenis * compatibility. J180D misses a dp2hdmi-pwren GPIO in the
9725dd0baa8Skettenis * template ADT. TODO: check device ADT
9735dd0baa8Skettenis */
9745dd0baa8Skettenis dcp->hdmi_hpd = devm_gpiod_get_optional(dev, "hdmi-hpd", GPIOD_IN);
9755dd0baa8Skettenis if (IS_ERR(dcp->hdmi_hpd))
9765dd0baa8Skettenis return PTR_ERR(dcp->hdmi_hpd);
9775dd0baa8Skettenis if (dcp->hdmi_hpd) {
9785dd0baa8Skettenis int irq = gpiod_to_irq(dcp->hdmi_hpd);
9795dd0baa8Skettenis if (irq < 0) {
9805dd0baa8Skettenis dev_err(dev, "failed to translate HDMI hpd GPIO to IRQ\n");
9815dd0baa8Skettenis return irq;
9825dd0baa8Skettenis }
9835dd0baa8Skettenis dcp->hdmi_hpd_irq = irq;
9845dd0baa8Skettenis
9855dd0baa8Skettenis ret = devm_request_threaded_irq(dev, dcp->hdmi_hpd_irq,
9865dd0baa8Skettenis NULL, dcp_dp2hdmi_hpd,
9875dd0baa8Skettenis IRQF_ONESHOT | IRQF_NO_AUTOEN |
9885dd0baa8Skettenis IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
9895dd0baa8Skettenis "dp2hdmi-hpd-irq", dcp);
9905dd0baa8Skettenis if (ret < 0) {
991*44a5d259Skettenis dev_err(dev, "failed to request HDMI hpd irq %d: %d\n",
9925dd0baa8Skettenis irq, ret);
9935dd0baa8Skettenis return ret;
9945dd0baa8Skettenis }
9955dd0baa8Skettenis }
9965dd0baa8Skettenis
9975dd0baa8Skettenis /*
9985dd0baa8Skettenis * Power DP2HDMI on as it is required for the HPD irq.
9995dd0baa8Skettenis * TODO: check if one is sufficient for the hpd to save power
10005dd0baa8Skettenis * on battery powered Macbooks.
10015dd0baa8Skettenis */
10025dd0baa8Skettenis dcp->hdmi_pwren = devm_gpiod_get_optional(dev, "hdmi-pwren", GPIOD_OUT_HIGH);
10035dd0baa8Skettenis if (IS_ERR(dcp->hdmi_pwren))
10045dd0baa8Skettenis return PTR_ERR(dcp->hdmi_pwren);
10055dd0baa8Skettenis
10065dd0baa8Skettenis dcp->dp2hdmi_pwren = devm_gpiod_get_optional(dev, "dp2hdmi-pwren", GPIOD_OUT_HIGH);
10075dd0baa8Skettenis if (IS_ERR(dcp->dp2hdmi_pwren))
10085dd0baa8Skettenis return PTR_ERR(dcp->dp2hdmi_pwren);
10095dd0baa8Skettenis
10105dd0baa8Skettenis ret = of_property_read_u32(dev->of_node, "mux-index", &mux_index);
10115dd0baa8Skettenis if (!ret) {
10125dd0baa8Skettenis dcp->xbar = devm_mux_control_get(dev, "dp-xbar");
10135dd0baa8Skettenis if (IS_ERR(dcp->xbar)) {
1014*44a5d259Skettenis dev_err(dev, "Failed to get dp-xbar: %ld\n", PTR_ERR(dcp->xbar));
10155dd0baa8Skettenis return PTR_ERR(dcp->xbar);
10165dd0baa8Skettenis }
10175dd0baa8Skettenis ret = mux_control_select(dcp->xbar, mux_index);
10185dd0baa8Skettenis if (ret)
10195dd0baa8Skettenis dev_warn(dev, "mux_control_select failed: %d\n", ret);
10205dd0baa8Skettenis }
10215dd0baa8Skettenis }
10225dd0baa8Skettenis
10235dd0baa8Skettenis return component_add(&pdev->dev, &dcp_comp_ops);
10245dd0baa8Skettenis }
10255dd0baa8Skettenis
10265dd0baa8Skettenis #ifdef __linux__
10275dd0baa8Skettenis
dcp_platform_remove(struct platform_device * pdev)10285dd0baa8Skettenis static int dcp_platform_remove(struct platform_device *pdev)
10295dd0baa8Skettenis {
10305dd0baa8Skettenis component_del(&pdev->dev, &dcp_comp_ops);
10315dd0baa8Skettenis
10325dd0baa8Skettenis return 0;
10335dd0baa8Skettenis }
10345dd0baa8Skettenis
dcp_platform_shutdown(struct platform_device * pdev)10355dd0baa8Skettenis static void dcp_platform_shutdown(struct platform_device *pdev)
10365dd0baa8Skettenis {
10375dd0baa8Skettenis component_del(&pdev->dev, &dcp_comp_ops);
10385dd0baa8Skettenis }
10395dd0baa8Skettenis
10405dd0baa8Skettenis #endif
10415dd0baa8Skettenis
dcp_platform_suspend(struct device * dev)10425dd0baa8Skettenis static int dcp_platform_suspend(struct device *dev)
10435dd0baa8Skettenis {
10445dd0baa8Skettenis struct apple_dcp *dcp = dev_get_drvdata(dev);
10455dd0baa8Skettenis
10465dd0baa8Skettenis if (dcp->hdmi_hpd_irq) {
10475dd0baa8Skettenis disable_irq(dcp->hdmi_hpd_irq);
10485dd0baa8Skettenis dcp_dptx_disconnect(dcp, 0);
10495dd0baa8Skettenis }
10505dd0baa8Skettenis /*
10515dd0baa8Skettenis * Set the device as a wakeup device, which forces its power
10525dd0baa8Skettenis * domains to stay on. We need this as we do not support full
10535dd0baa8Skettenis * shutdown properly yet.
10545dd0baa8Skettenis */
10555dd0baa8Skettenis device_set_wakeup_path(dev);
10565dd0baa8Skettenis
10575dd0baa8Skettenis return 0;
10585dd0baa8Skettenis }
10595dd0baa8Skettenis
dcp_platform_resume(struct device * dev)10605dd0baa8Skettenis static int dcp_platform_resume(struct device *dev)
10615dd0baa8Skettenis {
10625dd0baa8Skettenis struct apple_dcp *dcp = dev_get_drvdata(dev);
10635dd0baa8Skettenis
10645dd0baa8Skettenis if (dcp->hdmi_hpd_irq)
10655dd0baa8Skettenis enable_irq(dcp->hdmi_hpd_irq);
10665dd0baa8Skettenis
10675dd0baa8Skettenis if (dcp->hdmi_hpd) {
10685dd0baa8Skettenis bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
10695dd0baa8Skettenis dev_info(dcp->dev, "resume: HPD connected:%d\n", connected);
10705dd0baa8Skettenis if (connected)
10715dd0baa8Skettenis dcp_dptx_connect(dcp, 0);
10725dd0baa8Skettenis }
10735dd0baa8Skettenis
10745dd0baa8Skettenis return 0;
10755dd0baa8Skettenis }
10765dd0baa8Skettenis
10775dd0baa8Skettenis static DEFINE_SIMPLE_DEV_PM_OPS(dcp_platform_pm_ops,
10785dd0baa8Skettenis dcp_platform_suspend, dcp_platform_resume);
10795dd0baa8Skettenis
10805dd0baa8Skettenis
10815dd0baa8Skettenis static const struct apple_dcp_hw_data apple_dcp_hw_t6020 = {
10825dd0baa8Skettenis .num_dptx_ports = 1,
10835dd0baa8Skettenis };
10845dd0baa8Skettenis
10855dd0baa8Skettenis static const struct apple_dcp_hw_data apple_dcp_hw_t8112 = {
10865dd0baa8Skettenis .num_dptx_ports = 2,
10875dd0baa8Skettenis };
10885dd0baa8Skettenis
10895dd0baa8Skettenis static const struct apple_dcp_hw_data apple_dcp_hw_dcp = {
10905dd0baa8Skettenis .num_dptx_ports = 0,
10915dd0baa8Skettenis };
10925dd0baa8Skettenis
10935dd0baa8Skettenis static const struct apple_dcp_hw_data apple_dcp_hw_dcpext = {
10945dd0baa8Skettenis .num_dptx_ports = 2,
10955dd0baa8Skettenis };
10965dd0baa8Skettenis
10975dd0baa8Skettenis static const struct of_device_id of_match[] = {
10985dd0baa8Skettenis { .compatible = "apple,t6020-dcp", .data = &apple_dcp_hw_t6020, },
10995dd0baa8Skettenis { .compatible = "apple,t8112-dcp", .data = &apple_dcp_hw_t8112, },
11005dd0baa8Skettenis { .compatible = "apple,dcp", .data = &apple_dcp_hw_dcp, },
11015dd0baa8Skettenis { .compatible = "apple,dcpext", .data = &apple_dcp_hw_dcpext, },
11025dd0baa8Skettenis {}
11035dd0baa8Skettenis };
11045dd0baa8Skettenis MODULE_DEVICE_TABLE(of, of_match);
11055dd0baa8Skettenis
11065dd0baa8Skettenis #ifdef __linux__
11075dd0baa8Skettenis
11085dd0baa8Skettenis static struct platform_driver apple_platform_driver = {
11095dd0baa8Skettenis .probe = dcp_platform_probe,
11105dd0baa8Skettenis .remove = dcp_platform_remove,
11115dd0baa8Skettenis .shutdown = dcp_platform_shutdown,
11125dd0baa8Skettenis .driver = {
11135dd0baa8Skettenis .name = "apple-dcp",
11145dd0baa8Skettenis .of_match_table = of_match,
11155dd0baa8Skettenis .pm = pm_sleep_ptr(&dcp_platform_pm_ops),
11165dd0baa8Skettenis },
11175dd0baa8Skettenis };
11185dd0baa8Skettenis
11195dd0baa8Skettenis drm_module_platform_driver(apple_platform_driver);
11205dd0baa8Skettenis
11215dd0baa8Skettenis MODULE_AUTHOR("Alyssa Rosenzweig <alyssa@rosenzweig.io>");
11225dd0baa8Skettenis MODULE_DESCRIPTION("Apple Display Controller DRM driver");
11235dd0baa8Skettenis MODULE_LICENSE("Dual MIT/GPL");
11245dd0baa8Skettenis
11255dd0baa8Skettenis #endif
1126