From 39c461c01a9e08de897432b5c4fdfb98e39a2db1 Mon Sep 17 00:00:00 2001 From: WangYuli Date: Thu, 18 Apr 2024 21:44:01 +0800 Subject: [PATCH 1/2] drm: Support JMGPU JM9100 Add Jemoic JM9100 GPU driver support. Signed-off-by: WangYuli --- arch/x86/configs/deepin_x86_desktop_defconfig | 1 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/mwv207/Kconfig | 15 + drivers/gpu/drm/mwv207/Makefile | 34 + drivers/gpu/drm/mwv207/dc/mwv207_dvo.c | 180 +++ drivers/gpu/drm/mwv207/dc/mwv207_edp.c | 22 + drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c | 1086 +++++++++++++++++ drivers/gpu/drm/mwv207/dc/mwv207_i2c.c | 312 +++++ drivers/gpu/drm/mwv207/dc/mwv207_kms.c | 204 ++++ drivers/gpu/drm/mwv207/dc/mwv207_kms.h | 30 + drivers/gpu/drm/mwv207/dc/mwv207_va.c | 691 +++++++++++ drivers/gpu/drm/mwv207/dc/mwv207_va.h | 25 + drivers/gpu/drm/mwv207/dc/mwv207_vga.c | 287 +++++ drivers/gpu/drm/mwv207/dc/mwv207_vi.c | 86 ++ drivers/gpu/drm/mwv207/dc/mwv207_vi.h | 96 ++ drivers/gpu/drm/mwv207/mwv207.h | 125 ++ drivers/gpu/drm/mwv207/mwv207_bo.c | 279 +++++ drivers/gpu/drm/mwv207/mwv207_bo.h | 111 ++ drivers/gpu/drm/mwv207/mwv207_ctx.c | 245 ++++ drivers/gpu/drm/mwv207/mwv207_ctx.h | 50 + drivers/gpu/drm/mwv207/mwv207_db.c | 136 +++ drivers/gpu/drm/mwv207/mwv207_db.h | 32 + drivers/gpu/drm/mwv207/mwv207_devfreq.c | 278 +++++ drivers/gpu/drm/mwv207/mwv207_devfreq.h | 31 + drivers/gpu/drm/mwv207/mwv207_dma.h | 29 + drivers/gpu/drm/mwv207/mwv207_drm.h | 158 +++ drivers/gpu/drm/mwv207/mwv207_drv.c | 487 ++++++++ drivers/gpu/drm/mwv207/mwv207_gem.c | 275 +++++ drivers/gpu/drm/mwv207/mwv207_gem.h | 41 + drivers/gpu/drm/mwv207/mwv207_irq.c | 187 +++ drivers/gpu/drm/mwv207/mwv207_irq.h | 26 + drivers/gpu/drm/mwv207/mwv207_pipe_2d.c | 556 +++++++++ drivers/gpu/drm/mwv207/mwv207_pipe_3d.c | 597 +++++++++ .../gpu/drm/mwv207/mwv207_pipe_codec_common.c | 86 ++ .../gpu/drm/mwv207/mwv207_pipe_codec_common.h | 41 + drivers/gpu/drm/mwv207/mwv207_pipe_dec.c | 254 ++++ drivers/gpu/drm/mwv207/mwv207_pipe_dma.c | 412 +++++++ drivers/gpu/drm/mwv207/mwv207_pipe_enc.c | 260 ++++ drivers/gpu/drm/mwv207/mwv207_sched.c | 374 ++++++ drivers/gpu/drm/mwv207/mwv207_sched.h | 149 +++ drivers/gpu/drm/mwv207/mwv207_submit.c | 631 ++++++++++ drivers/gpu/drm/mwv207/mwv207_submit.h | 23 + drivers/gpu/drm/mwv207/mwv207_ttm.c | 502 ++++++++ drivers/gpu/drm/mwv207/mwv207_ttm.h | 25 + drivers/gpu/drm/mwv207/mwv207_vbios.c | 189 +++ drivers/gpu/drm/mwv207/mwv207_vbios.h | 51 + drivers/gpu/drm/mwv207/mwv207_vcmd.c | 173 +++ drivers/gpu/drm/mwv207/selftest/selftest.c | 105 ++ 49 files changed, 9990 insertions(+) create mode 100644 drivers/gpu/drm/mwv207/Kconfig create mode 100644 drivers/gpu/drm/mwv207/Makefile create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_dvo.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_edp.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_i2c.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_kms.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_kms.h create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_va.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_va.h create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_vga.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_vi.c create mode 100644 drivers/gpu/drm/mwv207/dc/mwv207_vi.h create mode 100644 drivers/gpu/drm/mwv207/mwv207.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_bo.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_bo.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_ctx.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_ctx.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_db.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_db.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_devfreq.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_devfreq.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_dma.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_drm.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_drv.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_gem.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_gem.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_irq.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_irq.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_pipe_2d.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_pipe_3d.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_pipe_dec.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_pipe_dma.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_pipe_enc.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_sched.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_sched.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_submit.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_submit.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_ttm.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_ttm.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_vbios.c create mode 100644 drivers/gpu/drm/mwv207/mwv207_vbios.h create mode 100644 drivers/gpu/drm/mwv207/mwv207_vcmd.c create mode 100644 drivers/gpu/drm/mwv207/selftest/selftest.c diff --git a/arch/x86/configs/deepin_x86_desktop_defconfig b/arch/x86/configs/deepin_x86_desktop_defconfig index d39babadd09b5..d2e82ae362c12 100644 --- a/arch/x86/configs/deepin_x86_desktop_defconfig +++ b/arch/x86/configs/deepin_x86_desktop_defconfig @@ -3224,6 +3224,7 @@ CONFIG_DRM_VMWGFX=m CONFIG_DRM_GMA500=m CONFIG_DRM_UDL=m CONFIG_DRM_AST=m +CONFIG_DRM_MWV207=m CONFIG_DRM_MGAG200=m CONFIG_DRM_QXL=m CONFIG_DRM_VIRTIO_GPU=m diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index c7edba18a6f09..96ef4a0695c63 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -316,6 +316,8 @@ source "drivers/gpu/drm/udl/Kconfig" source "drivers/gpu/drm/ast/Kconfig" +source "drivers/gpu/drm/mwv207/Kconfig" + source "drivers/gpu/drm/mgag200/Kconfig" source "drivers/gpu/drm/armada/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 104b42df2e956..377442538f19b 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -148,6 +148,7 @@ obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ +obj-$(CONFIG_DRM_MWV207) += mwv207/ obj-$(CONFIG_DRM_ARMADA) += armada/ obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ obj-y += renesas/ diff --git a/drivers/gpu/drm/mwv207/Kconfig b/drivers/gpu/drm/mwv207/Kconfig new file mode 100644 index 0000000000000..5fb991ed9e21c --- /dev/null +++ b/drivers/gpu/drm/mwv207/Kconfig @@ -0,0 +1,15 @@ +config DRM_MWV207 + tristate "MWV207 chip" + depends on DRM && SND_PCM + select DRM_KMS_HELPER + select DRM_VRAM_HELPER + select DRM_TTM + select DRM_SCHED + select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION + select DRM_GEM_SHMEM_HELPER + select DRM_DEVFREQ + select DRM_TTM_HELPER + help + Choose this option if you have a Jingjia graphics card. + If M is selected, the module will be called mwv207 (JM9100). + diff --git a/drivers/gpu/drm/mwv207/Makefile b/drivers/gpu/drm/mwv207/Makefile new file mode 100644 index 0000000000000..f9032a63d578b --- /dev/null +++ b/drivers/gpu/drm/mwv207/Makefile @@ -0,0 +1,34 @@ + +mwv207-y := mwv207_drv.o \ + mwv207_irq.o \ + mwv207_db.o \ + mwv207_ctx.o \ + mwv207_submit.o \ + mwv207_sched.o \ + mwv207_pipe_codec_common.o \ + mwv207_devfreq.o \ + mwv207_pipe_2d.o \ + mwv207_pipe_3d.o \ + mwv207_pipe_dec.o \ + mwv207_pipe_enc.o \ + mwv207_pipe_dma.o \ + mwv207_gem.o \ + mwv207_bo.o \ + mwv207_ttm.o \ + mwv207_vcmd.o \ + mwv207_vbios.o \ + dc/mwv207_kms.o \ + dc/mwv207_va.o \ + dc/mwv207_edp.o \ + dc/mwv207_hdmi.o \ + dc/mwv207_vga.o \ + dc/mwv207_dvo.o \ + dc/mwv207_vi.o \ + dc/mwv207_i2c.o \ + selftest/selftest.o + +obj-$(CONFIG_DRM_MWV207) := mwv207.o + +ccflags-y := -I$(src) + + diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_dvo.c b/drivers/gpu/drm/mwv207/dc/mwv207_dvo.c new file mode 100644 index 0000000000000..f0c0eda04a31b --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_dvo.c @@ -0,0 +1,180 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207_vi.h" + +static void mwv207_dvo_switch(struct mwv207_output *output, bool on) +{ + mwv207_output_modify(output, 0x200, + 0x1 << 0, (on ? 1 : 0) << 0); +} + +static void mwv207_dvo_config(struct mwv207_output *output) +{ + struct drm_display_mode *mode = &output->cur_crtc->state->adjusted_mode; + int hpol, vpol; + + hpol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1; + vpol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1; + + mwv207_output_modify(output, 0x200, 0x1 << 9, hpol << 9); + mwv207_output_modify(output, 0x200, 0x1 << 8, vpol << 8); + +} + +static void mwv207_dvo_select_crtc(struct mwv207_output *output) +{ + + mwv207_output_modify(output, 0x200, 0x7 << 16, + drm_crtc_index(output->cur_crtc) << 16); + + jdev_modify(output->jdev, 0x9b003c, 0xf, + drm_crtc_index(output->cur_crtc)); +} +static enum drm_mode_status mwv207_dvo_mode_valid(struct mwv207_output *output, + const struct drm_display_mode *mode) +{ + + if (mode->clock > 162000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static enum drm_mode_status mwv207_dvo_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return mwv207_dvo_mode_valid(connector_to_output(connector), mode); +} + +static int mwv207_dvo_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct mwv207_output *output = connector_to_output(connector); + + if (mwv207_i2c_probe(output->ddc)) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void mwv207_dvo_destroy(struct drm_connector *conn) +{ + drm_connector_unregister(conn); + drm_connector_cleanup(conn); +} + +static const struct drm_connector_helper_funcs mwv207_dvo_connector_helper_funcs = { + .get_modes = mwv207_output_get_modes, + .mode_valid = mwv207_dvo_connector_mode_valid, + .detect_ctx = mwv207_dvo_detect_ctx +}; + +static const struct drm_connector_funcs mwv207_dvo_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .destroy = mwv207_dvo_destroy, + .late_register = mwv207_output_late_register, + .early_unregister = mwv207_output_early_unregister, +}; + +static enum drm_mode_status mwv207_dvo_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + return mwv207_dvo_mode_valid(output, mode); +} + +static int mwv207_dvo_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return 0; +} + +static void mwv207_dvo_encoder_enable(struct drm_encoder *encoder) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + mwv207_dvo_select_crtc(output); + + mwv207_dvo_config(output); + + mwv207_dvo_switch(output, true); +} + +static void mwv207_dvo_encoder_disable(struct drm_encoder *encoder) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + mwv207_dvo_switch(output, false); +} + +static void mwv207_dvo_encoder_reset(struct drm_encoder *encoder) +{ + mwv207_dvo_encoder_disable(encoder); +} + +static const struct drm_encoder_funcs mwv207_dvo_encoder_funcs = { + .destroy = drm_encoder_cleanup, + .reset = mwv207_dvo_encoder_reset, +}; + +static const struct drm_encoder_helper_funcs mwv207_dvo_encoder_helper_funcs = { + .mode_valid = mwv207_dvo_encoder_mode_valid, + .atomic_check = mwv207_dvo_encoder_atomic_check, + .enable = mwv207_dvo_encoder_enable, + .disable = mwv207_dvo_encoder_disable, +}; + +int mwv207_dvo_init(struct mwv207_device *jdev) +{ + struct mwv207_output *output; + int ret; + + output = devm_kzalloc(jdev->dev, sizeof(*output), GFP_KERNEL); + if (!output) + return -ENOMEM; + + output->jdev = jdev; + output->idx = 0; + output->mmio = jdev->mmio + 0x9a0000; + output->i2c_chan = 5; + + output->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + ret = drm_connector_init(&jdev->base, &output->connector, + &mwv207_dvo_connector_funcs, DRM_MODE_CONNECTOR_DVII); + if (ret) + return ret; + drm_connector_helper_add(&output->connector, &mwv207_dvo_connector_helper_funcs); + + output->encoder.possible_crtcs = (1 << jdev->base.mode_config.num_crtc) - 1; + ret = drm_encoder_init(&jdev->base, &output->encoder, + &mwv207_dvo_encoder_funcs, DRM_MODE_ENCODER_DAC, + "dvo-%d", output->idx); + if (ret) + return ret; + drm_encoder_helper_add(&output->encoder, &mwv207_dvo_encoder_helper_funcs); + + ret = drm_connector_attach_encoder(&output->connector, &output->encoder); + if (ret) + return ret; + + return drm_connector_register(&output->connector); +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_edp.c b/drivers/gpu/drm/mwv207/dc/mwv207_edp.c new file mode 100644 index 0000000000000..790b186a1521f --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_edp.c @@ -0,0 +1,22 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207_vi.h" + +int mwv207_edp_init(struct mwv207_device *jdev) +{ + pr_info("%s TBD", __func__); + return 0; +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c b/drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c new file mode 100644 index 0000000000000..854aa0dc68193 --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c @@ -0,0 +1,1086 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include "mwv207_vi.h" +#include "mwv207_vbios.h" + +/* hdmi specific, accessed by mwv207_hdmi_read/write */ +#define MWV207_HDMI_BASE(idx) (0x1200000 + 0x40000 * (idx)) + +/* vi global, accessed by mwv207_output_read/write */ +#define MWV207_HDMI_CTRL(idx) (0x400 + 0x100 * (idx)) +#define MWV207_HDMI_PHY(idx) (0x6000 + 0x800 * (idx)) + +#define connector_to_hdmi(conn) container_of(conn, struct mwv207_hdmi, base.connector) +#define encoder_to_hdmi(encoder) container_of(encoder, struct mwv207_hdmi, base.encoder) + +struct mwv207_hdmi_phy_data { + u32 min_freq_khz; + u32 max_freq_khz; + u32 bpp; + u8 config[47]; + u8 padding; +} __packed; + +static const struct mwv207_hdmi_phy_data +default_phy_data[19] = { + { 24000, 47999, 8, + { 0xD2, 0x78, 0xB0, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0x01, 0x25, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x8B, 0xF0, + 0x22, 0x82, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 28000, 95999, 8, + { 0xD2, 0x3C, 0x50, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0x01, 0x25, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x85, 0xF0, + 0x22, 0x82, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 96000, 143999, 8, + { 0xD4, 0x50, 0x30, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0x01, 0x25, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x83, 0xF0, + 0x22, 0x82, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 144000, 296999, 8, + { 0xD8, 0x50, 0x10, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0x01, 0x25, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x81, 0xF0, + 0x22, 0x82, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 297000, 340000, 8, + { 0xDC, 0x3C, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0x01, 0x25, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x80, 0xF0, + 0x22, 0x82, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 340001, 595000, 8, + { 0xDC, 0x3C, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0x01, 0x25, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x80, 0xC0, + 0xF4, 0x69, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0xFF, 0x00, 0x00}}, + { 24000, 27999, 10, + { 0xD2, 0x7D, 0x90, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xCD, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x99, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 28000, 55999, 10, + { 0xD4, 0xC8, 0x70, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xCD, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x97, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 56000, 111999, 10, + { 0xD4, 0x64, 0x30, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xCD, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x93, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 112000, 223999, 10, + { 0xD8, 0x64, 0x10, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xCD, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x91, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 224000, 272000, 10, + { 0xDC, 0x4B, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xCD, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x90, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 272001, 340000, 10, + { 0xDC, 0x4B, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xCD, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x90, 0xC0, + 0x3D, 0xFA, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 340001, 475200, 10, + { 0xDC, 0x4B, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xCD, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0x90, 0xC0, + 0x3D, 0xFA, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0xFF, 0x00, 0x00}}, + { 24000, 47999, 12, + { 0xD1, 0x3C, 0x70, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xAB, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0xA7, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 48000, 95999, 12, + { 0xD2, 0x3C, 0x30, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xAB, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0xA3, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 96000, 197999, 12, + { 0xD4, 0x3C, 0x10, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xAB, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0xA1, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 198000, 226999, 12, + { 0xD8, 0x3C, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xAB, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0xA0, 0xC0, + 0x3A, 0x74, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 227000, 340000, 12, + { 0xD8, 0x3C, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xAB, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0xA0, 0xC0, + 0x3D, 0xFA, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x0F, 0x3E, + 0xF8, 0x00, 0x00}}, + { 340001, 396000, 12, + { 0xD8, 0x3C, 0x00, 0x01, 0x00, 0x88, 0x02, 0x4F, 0x30, 0x33, 0x65, + 0x00, 0xAB, 0x24, 0x80, 0x6C, 0xF2, 0x67, 0x00, 0x10, 0xA0, 0xC0, + 0x3D, 0xFA, 0x8F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0xFF, 0x00, 0x00}}, +}; + +struct mwv207_hdmi { + struct mwv207_output base; + + void __iomem *mmio; + bool sink_is_hdmi; + bool sink_has_audio; + int vic; + + int irq; + + u8 mc_clkdis; + bool rgb_limited_range; + struct mwv207_hdmi_phy_data phy_data[19]; +}; + +static const u16 csc_coeff_default[3][4] = { + { 0x2000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x2000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x2000, 0x0000 } +}; + +static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = { + { 0x1b7c, 0x0000, 0x0000, 0x0020 }, + { 0x0000, 0x1b7c, 0x0000, 0x0020 }, + { 0x0000, 0x0000, 0x1b7c, 0x0020 } +}; + +static inline u8 mwv207_hdmi_readb(struct mwv207_hdmi *hdmi, u32 reg) +{ + return readl(hdmi->mmio + reg * 4); +} + +static inline void mwv207_hdmi_writeb(struct mwv207_hdmi *hdmi, u8 value, u32 reg) +{ + writel_relaxed(value, hdmi->mmio + reg * 4); +} + +static inline void mwv207_hdmi_modb(struct mwv207_hdmi *hdmi, u8 value, u8 mask, u32 reg) +{ + u8 rvalue = mwv207_hdmi_readb(hdmi, reg); + + rvalue = (rvalue & ~mask) | (value & mask); + mwv207_hdmi_writeb(hdmi, rvalue, reg); +} + +static void mwv207_hdmi_mask_writeb(struct mwv207_hdmi *hdmi, u8 data, unsigned int reg, + u8 shift, u8 mask) +{ + mwv207_hdmi_modb(hdmi, data << shift, mask, reg); +} + +static int mwv207_hdmi_phy_data_from_cfg(struct mwv207_hdmi *hdmi) +{ + const struct mwv207_vdat *vdat; + int i; + + vdat = mwv207_vbios_vdat(hdmi->base.jdev, 0xfff0 + hdmi->base.idx); + if (!vdat) + return -ENOENT; + if (vdat->len != sizeof(hdmi->phy_data)) + return -EINVAL; + memcpy(hdmi->phy_data, vdat->dat, vdat->len); + for (i = 0; i < 19; ++i) { + hdmi->phy_data[i].min_freq_khz = le32_to_cpu(hdmi->phy_data[i].min_freq_khz); + hdmi->phy_data[i].max_freq_khz = le32_to_cpu(hdmi->phy_data[i].max_freq_khz); + hdmi->phy_data[i].bpp = le32_to_cpu(hdmi->phy_data[i].bpp); + } + return 0; +} + +static void mwv207_hdmi_phy_data_init(struct mwv207_hdmi *hdmi) +{ + if (!mwv207_hdmi_phy_data_from_cfg(hdmi)) + return; + + DRM_INFO("mwv207: hdmi%d use default phy data", hdmi->base.idx); + + BUILD_BUG_ON(sizeof(hdmi->phy_data) != sizeof(default_phy_data)); + + memcpy(hdmi->phy_data, default_phy_data, sizeof(hdmi->phy_data)); +} + +static void mwv207_hdmi_phy_configure_data(struct mwv207_hdmi *hdmi, int kfreq, int bpp) +{ + struct mwv207_output *output = &hdmi->base; + u32 regbase = MWV207_HDMI_PHY(output->idx); + int i, cfg, offset; + + for (i = 0; i < 19; ++i) { + if (kfreq >= hdmi->phy_data[i].min_freq_khz + && kfreq <= hdmi->phy_data[i].max_freq_khz + && bpp == hdmi->phy_data[i].bpp) + break; + } + if (i >= 19) { + pr_warn("mwv207: no matching hdmi phydata found"); + return; + } + + cfg = i; + mwv207_output_write(output, regbase + 0x70, 0xc8); + mwv207_output_write(output, regbase + 0x74, 0x2); + for (i = 0; i < 47; i++) { + offset = 0x4 + i * 4; + if (offset == 0x84 || offset == 0x88 || offset == 0xB8) + continue; + + mwv207_output_write(output, regbase + offset, + hdmi->phy_data[cfg].config[i]); + } +} + +static void mwv207_hdmi_phy_switch(struct mwv207_hdmi *hdmi, int enable) +{ + struct mwv207_output *output = &hdmi->base; + u32 regbase = MWV207_HDMI_PHY(output->idx); + + if (enable) { + mwv207_output_write(output, regbase + 0x84, 0x80); + mwv207_output_modify(output, MWV207_HDMI_CTRL(output->idx), + 1 << 28, 0 << 28); + } else { + mwv207_output_modify(output, MWV207_HDMI_CTRL(output->idx), + 1 << 28, 1 << 28); + mwv207_output_write(output, regbase + 0x84, 0x00); + } +} + +static void mwv207_hdmi_phy_power_on(struct mwv207_hdmi *hdmi) +{ + u8 val; + int i; + + mwv207_hdmi_phy_switch(hdmi, 1); + + for (i = 0; i < 5; ++i) { + val = mwv207_hdmi_readb(hdmi, 0x3004) & 0x01; + if (val) + break; + usleep_range(1000, 2000); + } + + if (!val) + pr_warn("mwv207: hdmi PHY PLL failed to lock\n"); +} + +static void mwv207_hdmi_phy_power_off(struct mwv207_hdmi *hdmi) +{ + u8 val; + int i; + + mwv207_hdmi_phy_switch(hdmi, 0); + + for (i = 0; i < 5; ++i) { + val = mwv207_hdmi_readb(hdmi, 0x3004); + if (!(val & 0x01)) + break; + + usleep_range(1000, 2000); + } + + if (val & 0x01) + pr_warn("mwv207: PHY failed to power down\n"); +} + +static void mwv207_hdmi_phy_configure(struct mwv207_hdmi *hdmi, int kfreq, int bpp) +{ + mwv207_hdmi_phy_power_off(hdmi); + + mwv207_hdmi_phy_configure_data(hdmi, kfreq, bpp); + + mwv207_hdmi_phy_power_on(hdmi); +} + +static void mwv207_hdmi_clear_overflow(struct mwv207_hdmi *hdmi) +{ + u8 val; + + mwv207_hdmi_writeb(hdmi, (u8) ~0x02, 0x4002); + + val = mwv207_hdmi_readb(hdmi, 0x1000); + mwv207_hdmi_writeb(hdmi, val, 0x1000); +} + +static void mwv207_hdmi_ih_mutes(struct mwv207_hdmi *hdmi) +{ + u8 ih_mute; + + ih_mute = mwv207_hdmi_readb(hdmi, 0x01FF) | 0x2 | 0x1; + + mwv207_hdmi_writeb(hdmi, ih_mute, 0x01FF); + + mwv207_hdmi_writeb(hdmi, 0xff, 0x0807); + mwv207_hdmi_writeb(hdmi, 0xff, 0x10D2); + mwv207_hdmi_writeb(hdmi, 0xff, 0x10D6); + mwv207_hdmi_writeb(hdmi, 0xff, 0x10DA); + mwv207_hdmi_writeb(hdmi, 0xff, 0x3006); + mwv207_hdmi_writeb(hdmi, 0xff, 0x3027); + mwv207_hdmi_writeb(hdmi, 0xff, 0x3028); + mwv207_hdmi_writeb(hdmi, 0xff, 0x3102); + mwv207_hdmi_writeb(hdmi, 0xff, 0x3302); + mwv207_hdmi_writeb(hdmi, 0xff, 0x3404); + mwv207_hdmi_writeb(hdmi, 0xff, 0x3505); + mwv207_hdmi_writeb(hdmi, 0xff, 0x5008); + mwv207_hdmi_writeb(hdmi, 0xff, 0x7E05); + mwv207_hdmi_writeb(hdmi, 0xff, 0x7E06); + + mwv207_hdmi_writeb(hdmi, 0xff, 0x0180); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0181); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0182); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0183); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0184); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0185); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0186); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0187); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0188); + mwv207_hdmi_writeb(hdmi, 0xff, 0x0189); + + ih_mute &= ~(0x2 | 0x1); + mwv207_hdmi_writeb(hdmi, ih_mute, 0x01FF); +} + +static void mwv207_hdmi_phy_setup_hpd(struct mwv207_hdmi *hdmi) +{ + + mwv207_hdmi_writeb(hdmi, 0x02, 0x3007); + mwv207_hdmi_writeb(hdmi, 0x1, 0x0104); + + mwv207_hdmi_writeb(hdmi, ~0x02, 0x3006); + + mwv207_hdmi_writeb(hdmi, 0x1, 0x0104); + mwv207_hdmi_writeb(hdmi, ~0x1, 0x0184); +} + +static irqreturn_t mwv207_hdmi_hard_irq(int irq, void *dev_id) +{ + struct mwv207_hdmi *hdmi = dev_id; + irqreturn_t ret = IRQ_NONE; + u8 intr_stat; + + intr_stat = mwv207_hdmi_readb(hdmi, 0x0104); + if (intr_stat) { + mwv207_hdmi_writeb(hdmi, ~0, 0x0184); + return IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t mwv207_hdmi_irq(int irq, void *dev_id) +{ + u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat; + struct mwv207_hdmi *hdmi = dev_id; + + intr_stat = mwv207_hdmi_readb(hdmi, 0x0104); + phy_int_pol = mwv207_hdmi_readb(hdmi, 0x3007); + phy_stat = mwv207_hdmi_readb(hdmi, 0x3004); + + phy_pol_mask = 0; + if (intr_stat & 0x1) + phy_pol_mask |= 0x02; + if (intr_stat & 0x4) + phy_pol_mask |= 0x10; + if (intr_stat & 0x8) + phy_pol_mask |= 0x20; + if (intr_stat & 0x10) + phy_pol_mask |= 0x40; + if (intr_stat & 0x20) + phy_pol_mask |= 0x80; + + if (phy_pol_mask) + mwv207_hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask, 0x3007); + + if (intr_stat & 0x1) + drm_helper_hpd_irq_event(&hdmi->base.jdev->base); + + mwv207_hdmi_writeb(hdmi, intr_stat, 0x0104); + mwv207_hdmi_writeb(hdmi, ~0x1, 0x0184); + + return IRQ_HANDLED; +} + +static void mwv207_hdmi_switch(struct mwv207_output *output, bool on) +{ + mwv207_output_modify(output, MWV207_HDMI_CTRL(output->idx), 1, on ? 1 : 0); +} + +static void mwv207_hdmi_select_crtc(struct mwv207_output *output) +{ + + mwv207_output_modify(output, MWV207_HDMI_CTRL(output->idx), + 0x3 << 4, drm_crtc_index(output->cur_crtc) << 4); + + jdev_modify(output->jdev, 0x9b003c, 0b111 << (8 + output->idx * 4), + drm_crtc_index(output->cur_crtc) << (8 + output->idx * 4)); +} + +static void mwv207_hdmi_disable_overflow_interrupts(struct mwv207_hdmi *hdmi) +{ + mwv207_hdmi_writeb(hdmi, 0x03, 0x0182); +} + +static bool mwv207_hdmi_support_scdc(struct mwv207_hdmi *hdmi, + const struct drm_display_info *display) +{ + + if (!display->hdmi.scdc.supported || + !display->hdmi.scdc.scrambling.supported) + return false; + + if (!display->hdmi.scdc.scrambling.low_rates && + display->max_tmds_clock <= 340000) + return false; + + return true; +} + +static void mwv207_hdmi_av_composer(struct mwv207_hdmi *hdmi, const struct drm_display_mode *mode) +{ + const struct drm_display_info *display = &hdmi->base.connector.display_info; + const struct drm_hdmi_info *hdmi_info = &display->hdmi; + int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; + struct mwv207_output *output = &hdmi->base; + unsigned int vdisplay, hdisplay; + u32 tmdsclock, rate; + u8 inv_val, bytes; + + tmdsclock = mode->clock * 1000; + + inv_val = (mwv207_hdmi_support_scdc(hdmi, display) && + (tmdsclock > 340000000 || + hdmi_info->scdc.scrambling.low_rates) ? 0x80 : 0x00); + + inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ? 0x40 : 0x00; + + inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ? 0x20 : 0x00; + + inv_val |= 0x10; + + if (hdmi->vic == 39) + inv_val |= 0x2; + else + inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 0x2 : 0x0; + + inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 0x1 : 0x0; + + inv_val |= hdmi->sink_is_hdmi ? 0x8 : 0x0; + + mwv207_hdmi_writeb(hdmi, inv_val, 0x1000); + + rate = drm_mode_vrefresh(mode) * 1000; + mwv207_hdmi_writeb(hdmi, rate >> 16, 0x1010); + mwv207_hdmi_writeb(hdmi, rate >> 8, 0x100F); + mwv207_hdmi_writeb(hdmi, rate, 0x100E); + + mwv207_output_modify(output, MWV207_HDMI_CTRL(output->idx), 0x1 << 9, + (mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0) << 9); + mwv207_output_modify(output, MWV207_HDMI_CTRL(output->idx), 0x1 << 8, + (mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0) << 8); + mwv207_output_modify(output, MWV207_HDMI_CTRL(output->idx), 0x1 << 10, 1 << 10); + + hdisplay = mode->hdisplay; + hblank = mode->htotal - mode->hdisplay; + h_de_hs = mode->hsync_start - mode->hdisplay; + hsync_len = mode->hsync_end - mode->hsync_start; + + vdisplay = mode->vdisplay; + vblank = mode->vtotal - mode->vdisplay; + v_de_vs = mode->vsync_start - mode->vdisplay; + vsync_len = mode->vsync_end - mode->vsync_start; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vdisplay /= 2; + vblank /= 2; + v_de_vs /= 2; + vsync_len /= 2; + } + + if (mwv207_hdmi_support_scdc(hdmi, display)) { + if (tmdsclock > 340000000 || + hdmi_info->scdc.scrambling.low_rates) { + drm_scdc_readb(hdmi->base.ddc, SCDC_SINK_VERSION, &bytes); + drm_scdc_writeb(hdmi->base.ddc, SCDC_SOURCE_VERSION, + min_t(u8, bytes, 0x1)); + + drm_scdc_set_scrambling(&hdmi->base.connector, 1); + + mwv207_hdmi_writeb(hdmi, (u8)~0x02, 0x4002); + mwv207_hdmi_writeb(hdmi, 1, 0x10E1); + } else { + mwv207_hdmi_writeb(hdmi, 0, 0x10E1); + mwv207_hdmi_writeb(hdmi, (u8)~0x02, 0x4002); + drm_scdc_set_scrambling(&hdmi->base.connector, 0); + } + } + + mwv207_hdmi_writeb(hdmi, hdisplay >> 8, 0x1002); + mwv207_hdmi_writeb(hdmi, hdisplay, 0x1001); + + mwv207_hdmi_writeb(hdmi, vdisplay >> 8, 0x1006); + mwv207_hdmi_writeb(hdmi, vdisplay, 0x1005); + + mwv207_hdmi_writeb(hdmi, hblank >> 8, 0x1004); + mwv207_hdmi_writeb(hdmi, hblank, 0x1003); + + mwv207_hdmi_writeb(hdmi, vblank >> 8, 0x102E); + mwv207_hdmi_writeb(hdmi, vblank, 0x1007); + + mwv207_hdmi_writeb(hdmi, h_de_hs >> 8, 0x1009); + mwv207_hdmi_writeb(hdmi, h_de_hs, 0x1008); + + mwv207_hdmi_writeb(hdmi, v_de_vs >> 8, 0x102F); + mwv207_hdmi_writeb(hdmi, v_de_vs, 0x100C); + + mwv207_hdmi_writeb(hdmi, hsync_len >> 8, 0x100B); + mwv207_hdmi_writeb(hdmi, hsync_len, 0x100A); + + mwv207_hdmi_writeb(hdmi, vsync_len, 0x100D); +} + +static void mwv207_hdmi_enable_video_path(struct mwv207_hdmi *hdmi) +{ + + mwv207_hdmi_writeb(hdmi, 12, 0x1011); + mwv207_hdmi_writeb(hdmi, 32, 0x1012); + mwv207_hdmi_writeb(hdmi, 1, 0x1013); + + mwv207_hdmi_writeb(hdmi, 0x0B, 0x1014); + mwv207_hdmi_writeb(hdmi, 0x16, 0x1015); + mwv207_hdmi_writeb(hdmi, 0x21, 0x1016); + + hdmi->mc_clkdis |= 0x40 | 0x8 | 0x10 | 0x4 | 0x2; + hdmi->mc_clkdis &= ~0x1; + mwv207_hdmi_writeb(hdmi, hdmi->mc_clkdis, 0x4001); + + hdmi->mc_clkdis &= ~0x2; + mwv207_hdmi_writeb(hdmi, hdmi->mc_clkdis, 0x4001); + + if (hdmi->rgb_limited_range) { + hdmi->mc_clkdis &= ~0x10; + mwv207_hdmi_writeb(hdmi, hdmi->mc_clkdis, 0x4001); + + mwv207_hdmi_writeb(hdmi, 0x1, 0x4004); + } else { + hdmi->mc_clkdis |= 0x10; + mwv207_hdmi_writeb(hdmi, hdmi->mc_clkdis, 0x4001); + + mwv207_hdmi_writeb(hdmi, 0x0, 0x4004); + } +} + +static void mwv207_hdmi_enable_audio_clk(struct mwv207_hdmi *hdmi, bool enable) +{ + if (enable) + hdmi->mc_clkdis &= ~0x8; + else + hdmi->mc_clkdis |= 0x8; + mwv207_hdmi_writeb(hdmi, hdmi->mc_clkdis, 0x4001); +} + +static void mwv207_hdmi_config_AVI(struct mwv207_hdmi *hdmi, + struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + struct hdmi_avi_infoframe frame; + u8 val; + + drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); + + drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, + hdmi->rgb_limited_range ? + HDMI_QUANTIZATION_RANGE_LIMITED : + HDMI_QUANTIZATION_RANGE_FULL); + + frame.colorspace = HDMI_COLORSPACE_RGB; + frame.colorimetry = HDMI_COLORIMETRY_NONE; + frame.extended_colorimetry = + HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; + + val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 3); + if (frame.active_aspect & 15) + val |= 0x40; + if (frame.top_bar || frame.bottom_bar) + val |= 0x08; + if (frame.left_bar || frame.right_bar) + val |= 0x04; + mwv207_hdmi_writeb(hdmi, val, 0x1019); + + val = ((frame.colorimetry & 0x3) << 6) | + ((frame.picture_aspect & 0x3) << 4) | + (frame.active_aspect & 0xf); + mwv207_hdmi_writeb(hdmi, val, 0x101A); + + val = ((frame.extended_colorimetry & 0x7) << 4) | + ((frame.quantization_range & 0x3) << 2) | + (frame.nups & 0x3); + if (frame.itc) + val |= 0x80; + mwv207_hdmi_writeb(hdmi, val, 0x101B); + + val = frame.video_code & 0x7f; + mwv207_hdmi_writeb(hdmi, val, 0x101C); + + val = ((1 << 4) & 0xF0); + mwv207_hdmi_writeb(hdmi, val, 0x10E0); + + val = ((frame.ycc_quantization_range & 0x3) << 2) | + (frame.content_type & 0x3); + mwv207_hdmi_writeb(hdmi, val, 0x1017); + + mwv207_hdmi_writeb(hdmi, frame.top_bar & 0xff, 0x101D); + mwv207_hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, 0x101E); + mwv207_hdmi_writeb(hdmi, frame.bottom_bar & 0xff, 0x101F); + mwv207_hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, 0x1020); + mwv207_hdmi_writeb(hdmi, frame.left_bar & 0xff, 0x1021); + mwv207_hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, 0x1022); + mwv207_hdmi_writeb(hdmi, frame.right_bar & 0xff, 0x1023); + mwv207_hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, 0x1024); +} + +static void mwv207_hdmi_config_vendor_specific_infoframe(struct mwv207_hdmi *hdmi, + struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + struct hdmi_vendor_infoframe frame; + u8 buffer[10]; + ssize_t err; + + err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, connector, + mode); + if (err < 0) + return; + + err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + DRM_ERROR("Failed to pack vendor infoframe: %zd\n", err); + return; + } + mwv207_hdmi_mask_writeb(hdmi, 0, 0x10B3, 3, 0x08); + + mwv207_hdmi_writeb(hdmi, buffer[2], 0x102A); + + mwv207_hdmi_writeb(hdmi, buffer[4], 0x1029); + mwv207_hdmi_writeb(hdmi, buffer[5], 0x1030); + mwv207_hdmi_writeb(hdmi, buffer[6], 0x1031); + + mwv207_hdmi_writeb(hdmi, buffer[7], 0x1032); + mwv207_hdmi_writeb(hdmi, buffer[8], 0x1033); + + if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + mwv207_hdmi_writeb(hdmi, buffer[9], 0x1034); + + mwv207_hdmi_writeb(hdmi, 1, 0x10B4); + + mwv207_hdmi_writeb(hdmi, 0x11, 0x10B5); + + mwv207_hdmi_mask_writeb(hdmi, 1, 0x10B3, 3, 0x08); +} + +static void mwv207_hdmi_config_drm_infoframe(struct mwv207_hdmi *hdmi, + const struct drm_connector *connector) +{ + const struct drm_connector_state *conn_state = connector->state; + struct hdmi_drm_infoframe frame; + u8 buffer[30]; + ssize_t err; + int i; + + mwv207_hdmi_modb(hdmi, 0x00, 0x80, 0x10E3); + + err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); + if (err < 0) + return; + + err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) + return; + + mwv207_hdmi_writeb(hdmi, frame.version, 0x1168); + mwv207_hdmi_writeb(hdmi, frame.length, 0x1169); + + for (i = 0; i < frame.length; i++) + mwv207_hdmi_writeb(hdmi, buffer[4 + i], 0x116A + i); + + mwv207_hdmi_writeb(hdmi, 1, 0x1167); + mwv207_hdmi_modb(hdmi, 0x80, 0x80, 0x10E3); +} + +static void mwv207_hdmi_video_packetize(struct mwv207_hdmi *hdmi) +{ + unsigned int output_select = 0x3; + unsigned int remap_size = 0x0; + unsigned int color_depth = 4; + u8 val, vp_conf; + + val = ((color_depth << 4) & 0xF0); + mwv207_hdmi_writeb(hdmi, val, 0x0801); + + mwv207_hdmi_modb(hdmi, 0x1, 0x1, 0x0802); + + vp_conf = 0x00 | 0x4; + + mwv207_hdmi_modb(hdmi, vp_conf, 0x10 | 0x4, 0x0804); + + mwv207_hdmi_modb(hdmi, 1 << 5, 0x20, 0x0802); + + mwv207_hdmi_writeb(hdmi, remap_size, 0x0803); + + vp_conf = 0x40 | 0x00 | 0x0; + + mwv207_hdmi_modb(hdmi, vp_conf, 0x40 | 0x20 | 0x8, 0x0804); + + mwv207_hdmi_modb(hdmi, 0x2 | 0x4, 0x2 | 0x4, 0x0802); + + mwv207_hdmi_modb(hdmi, output_select, 0x3, 0x0804); +} + +static void mwv207_hdmi_update_csc_coeffs(struct mwv207_hdmi *hdmi) +{ + const u16 (*csc_coeff)[3][4] = &csc_coeff_default; + unsigned int i; + u32 csc_scale = 1; + + if (hdmi->rgb_limited_range) + csc_coeff = &csc_coeff_rgb_full_to_rgb_limited; + else + csc_coeff = &csc_coeff_default; + + for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) { + u16 coeff_a = (*csc_coeff)[0][i]; + u16 coeff_b = (*csc_coeff)[1][i]; + u16 coeff_c = (*csc_coeff)[2][i]; + + mwv207_hdmi_writeb(hdmi, coeff_a & 0xff, 0x4103 + i * 2); + mwv207_hdmi_writeb(hdmi, coeff_a >> 8, 0x4102 + i * 2); + mwv207_hdmi_writeb(hdmi, coeff_b & 0xff, 0x410B + i * 2); + mwv207_hdmi_writeb(hdmi, coeff_b >> 8, 0x410A + i * 2); + mwv207_hdmi_writeb(hdmi, coeff_c & 0xff, 0x4113 + i * 2); + mwv207_hdmi_writeb(hdmi, coeff_c >> 8, 0x4112 + i * 2); + } + + mwv207_hdmi_modb(hdmi, csc_scale, 0x03, 0x4101); +} + +static void mwv207_hdmi_video_csc(struct mwv207_hdmi *hdmi) +{ + int color_depth = 0x00; + + mwv207_hdmi_writeb(hdmi, 0, 0x4100); + mwv207_hdmi_modb(hdmi, color_depth, 0xF0, 0x4101); + + mwv207_hdmi_update_csc_coeffs(hdmi); +} + +static void mwv207_hdmi_video_sample(struct mwv207_hdmi *hdmi) +{ + int color_format = 1; + u8 val; + + val = 0x00 | ((color_format << 0) & 0x1F); + mwv207_hdmi_writeb(hdmi, val, 0x0200); + + val = 0x4 | 0x2 | 0x1; + mwv207_hdmi_writeb(hdmi, val, 0x0201); + mwv207_hdmi_writeb(hdmi, 0x0, 0x0202); + mwv207_hdmi_writeb(hdmi, 0x0, 0x0203); + mwv207_hdmi_writeb(hdmi, 0x0, 0x0204); + mwv207_hdmi_writeb(hdmi, 0x0, 0x0205); + mwv207_hdmi_writeb(hdmi, 0x0, 0x0206); + mwv207_hdmi_writeb(hdmi, 0x0, 0x0207); +} + +static void mwv207_hdmi_tx_hdcp_config(struct mwv207_hdmi *hdmi) +{ + u8 de = 0x10; + + mwv207_hdmi_modb(hdmi, 0x0, 0x4, 0x5000); + + mwv207_hdmi_modb(hdmi, de, 0x10, 0x5009); + + mwv207_hdmi_modb(hdmi, 0x2, 0x2, 0x5001); +} + +static void mwv207_hdmi_set_timing(struct mwv207_hdmi *hdmi) +{ + struct drm_display_mode *mode; + + mwv207_hdmi_disable_overflow_interrupts(hdmi); + + mode = &hdmi->base.cur_crtc->state->adjusted_mode; + hdmi->vic = drm_match_cea_mode(mode); + + hdmi->rgb_limited_range = hdmi->sink_is_hdmi && + drm_default_rgb_quant_range(mode) == + HDMI_QUANTIZATION_RANGE_LIMITED; + + mwv207_hdmi_av_composer(hdmi, mode); + + mwv207_hdmi_phy_configure(hdmi, mode->clock, 8); + + mwv207_hdmi_enable_video_path(hdmi); + + if (hdmi->sink_has_audio) { + mwv207_hdmi_enable_audio_clk(hdmi, 1); + + } + + if (hdmi->sink_is_hdmi) { + struct drm_connector *conn = &hdmi->base.connector; + + mwv207_hdmi_config_AVI(hdmi, conn, mode); + mwv207_hdmi_config_vendor_specific_infoframe(hdmi, conn, mode); + mwv207_hdmi_config_drm_infoframe(hdmi, conn); + } + + mwv207_hdmi_video_packetize(hdmi); + mwv207_hdmi_video_csc(hdmi); + mwv207_hdmi_video_sample(hdmi); + mwv207_hdmi_tx_hdcp_config(hdmi); + + mwv207_hdmi_clear_overflow(hdmi); +} + +static int mwv207_hdmi_get_modes(struct drm_connector *connector) +{ + struct mwv207_hdmi *hdmi = connector_to_hdmi(connector); + int count; + + count = mwv207_output_get_modes(connector); + + if (connector->edid_blob_ptr) { + struct edid *edid; + + edid = (struct edid *)connector->edid_blob_ptr->data; + hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); + hdmi->sink_has_audio = drm_detect_monitor_audio(edid); + } else { + hdmi->sink_is_hdmi = false; + hdmi->sink_has_audio = false; + } + + return count; +} + +static enum drm_mode_status mwv207_hdmi_mode_valid(struct mwv207_hdmi *hdmi, + const struct drm_display_mode *mode) +{ + + if (mode->clock >= 384000) + return MODE_CLOCK_HIGH; + + if (mode->clock > 594000) + return MODE_CLOCK_HIGH; + + if (!hdmi->sink_is_hdmi && mode->clock >= 165000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static enum drm_mode_status mwv207_hdmi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct mwv207_hdmi *hdmi = connector_to_hdmi(connector); + + return mwv207_hdmi_mode_valid(hdmi, mode); +} + +static int mwv207_hdmi_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct mwv207_hdmi *hdmi = connector_to_hdmi(connector); + u32 value; + + value = mwv207_hdmi_readb(hdmi, 0x3004); + if (value & 0x2) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void mwv207_hdmi_destroy(struct drm_connector *conn) +{ + struct mwv207_hdmi *hdmi = connector_to_hdmi(conn); + + free_irq(hdmi->irq, hdmi); + drm_connector_unregister(conn); + drm_connector_cleanup(conn); +} + +static const struct drm_connector_helper_funcs mwv207_hdmi_connector_helper_funcs = { + .get_modes = mwv207_hdmi_get_modes, + .mode_valid = mwv207_hdmi_connector_mode_valid, + .detect_ctx = mwv207_hdmi_detect_ctx +}; + +static const struct drm_connector_funcs mwv207_hdmi_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .destroy = mwv207_hdmi_destroy, + .late_register = mwv207_output_late_register, + .early_unregister = mwv207_output_early_unregister, +}; + +static enum drm_mode_status mwv207_hdmi_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + struct mwv207_hdmi *hdmi = encoder_to_hdmi(encoder); + + return mwv207_hdmi_mode_valid(hdmi, mode); +} + +static int mwv207_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return 0; +} + +static void mwv207_hdmi_encoder_enable(struct drm_encoder *encoder) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + mwv207_hdmi_select_crtc(output); + + mwv207_hdmi_set_timing(encoder_to_hdmi(encoder)); + + mwv207_hdmi_switch(output, true); +} + +static void mwv207_hdmi_encoder_disable(struct drm_encoder *encoder) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + mwv207_hdmi_switch(output, false); +} + +static void mwv207_hdmi_encoder_reset(struct drm_encoder *encoder) +{ + mwv207_hdmi_encoder_disable(encoder); + mwv207_hdmi_ih_mutes(encoder_to_hdmi(encoder)); + mwv207_hdmi_phy_setup_hpd(encoder_to_hdmi(encoder)); +} + +static const struct drm_encoder_funcs mwv207_hdmi_encoder_funcs = { + .destroy = drm_encoder_cleanup, + .reset = mwv207_hdmi_encoder_reset, +}; + +static const struct drm_encoder_helper_funcs mwv207_hdmi_encoder_helper_funcs = { + .mode_valid = mwv207_hdmi_encoder_mode_valid, + .atomic_check = mwv207_hdmi_encoder_atomic_check, + .enable = mwv207_hdmi_encoder_enable, + .disable = mwv207_hdmi_encoder_disable, +}; + +static int mwv207_hdmi_init_single(struct mwv207_device *jdev, int idx) +{ + struct mwv207_output *output; + struct mwv207_hdmi *hdmi; + int ret; + + hdmi = devm_kzalloc(jdev->dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + output = &hdmi->base; + output->jdev = jdev; + output->idx = idx; + output->mmio = jdev->mmio + 0x9a0000; + output->i2c_chan = 0 + idx; + hdmi->mmio = jdev->mmio + MWV207_HDMI_BASE(idx); + hdmi->mc_clkdis = 0x7f; + + mwv207_hdmi_phy_data_init(hdmi); + + output->connector.polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(&jdev->base, &output->connector, + &mwv207_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + if (ret) + return ret; + drm_connector_helper_add(&output->connector, &mwv207_hdmi_connector_helper_funcs); + + output->encoder.possible_crtcs = (1 << jdev->base.mode_config.num_crtc) - 1; + ret = drm_encoder_init(&jdev->base, &output->encoder, + &mwv207_hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS, + "hdmi-%d", output->idx); + if (ret) + return ret; + drm_encoder_helper_add(&output->encoder, &mwv207_hdmi_encoder_helper_funcs); + + ret = drm_connector_attach_encoder(&output->connector, &output->encoder); + if (ret) + return ret; + + hdmi->irq = irq_find_mapping(jdev->irq_domain, output->idx + 32); + BUG_ON(hdmi->irq == 0); + + ret = request_threaded_irq(hdmi->irq, + mwv207_hdmi_hard_irq, mwv207_hdmi_irq, + IRQF_SHARED, output->encoder.name, hdmi); + if (ret) + return ret; + + ret = drm_connector_register(&output->connector); + if (ret) + free_irq(hdmi->irq, hdmi); + + return ret; +} + +int mwv207_hdmi_init(struct mwv207_device *jdev) +{ + int i, ret; + + for (i = 0; i < 4; i++) { + + if (jdev_read(jdev, MWV207_HDMI_BASE(i)) != 0x21) + continue; + ret = mwv207_hdmi_init_single(jdev, i); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_i2c.c b/drivers/gpu/drm/mwv207/dc/mwv207_i2c.c new file mode 100644 index 0000000000000..21df07c6f7fcf --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_i2c.c @@ -0,0 +1,312 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include "mwv207.h" +#include "mwv207_vi.h" + +static int i2c_scl_gpio[8] = {19, 21, 23, 25, 27, 29, 31, 33}; +static int i2c_sda_gpio[8] = {20, 22, 24, 26, 28, 30, 32, 34}; + +struct mwv207_i2c { + struct i2c_adapter adapter; + struct mwv207_device *jdev; + struct i2c_algo_bit_data bit; + struct mutex *mutex; + u32 sda_in_addr; + u32 sda_out_addr; + u32 sda_mask; + u32 sda_dir; + u32 scl_in_addr; + u32 scl_out_addr; + u32 scl_mask; + u32 scl_dir; +}; + +static inline void mwv207_i2c_set_dir(struct mwv207_i2c *i2c, u32 mask, + u32 reg, bool is_input) +{ + + mb(); + + jdev_modify(i2c->jdev, reg, mask, is_input ? mask : 0); + + mb(); +} + +static void mwv207_i2c_gpio_multi(struct mwv207_i2c *i2c, int mask) +{ + struct mwv207_device *jdev = i2c->jdev; + + switch (mask) { + case (1 << 19): + + jdev_modify(jdev, (0x9b0918), 0x3 << 12, 0); + jdev_modify(jdev, (0x9b0918), 0x3 << 16, 0); + break; + case (1 << 21): + + jdev_modify(jdev, (0x9b0918), 0x3 << 20, 0); + jdev_modify(jdev, (0x9b0918), 0x3 << 24, 0); + break; + case (1 << 23): + + jdev_modify(jdev, (0x9b0918), 0x3 << 28, 0); + jdev_modify(jdev, (0x9b091c), 0x3, 0); + break; + case (1 << 25): + + jdev_modify(jdev, (0x9b091c), 0x3 << 4, 0); + jdev_modify(jdev, (0x9b091c), 0x3 << 8, 0); + break; + case (1 << 27): + + jdev_modify(jdev, (0x9b091c), 0x3 << 12, 0); + jdev_modify(jdev, (0x9b091c), 0x3 << 16, 0); + break; + case (1 << 29): + + jdev_modify(jdev, (0x9b091c), 0x3 << 20, 0); + jdev_modify(jdev, (0x9b091c), 0x3 << 24, 0); + break; + case (1 << 31): + + jdev_modify(jdev, (0x9b091c), 0x3 << 28, 0); + jdev_modify(jdev, (0x9b0910), 0x3, 0); + break; + case (1 << 1): + + jdev_modify(jdev, (0x9b0910), 0x3 << 4, 0); + jdev_modify(jdev, (0x9b0910), 0x3 << 8, 0); + break; + default: + break; + } +} + +static int mwv207_i2c_pre_xfer(struct i2c_adapter *i2c_adap) +{ + struct mwv207_i2c *i2c = i2c_get_adapdata(i2c_adap); + struct mwv207_device *jdev = i2c->jdev; + + mutex_lock(i2c->mutex); + mwv207_i2c_gpio_multi(i2c, i2c->scl_mask); + + jdev_modify(jdev, i2c->scl_out_addr, i2c->scl_mask, 0); + jdev_modify(jdev, i2c->sda_out_addr, i2c->sda_mask, 0); + + mwv207_i2c_set_dir(i2c, i2c->scl_mask, i2c->scl_dir, 1); + mwv207_i2c_set_dir(i2c, i2c->sda_mask, i2c->sda_dir, 1); + + return 0; +} + +static void mwv207_i2c_post_xfer(struct i2c_adapter *i2c_adap) +{ + struct mwv207_i2c *i2c = i2c_get_adapdata(i2c_adap); + + mutex_unlock(i2c->mutex); +} + +static int mwv207_i2c_get_clock(void *i2c_priv) +{ + struct mwv207_i2c *i2c = (struct mwv207_i2c *)i2c_priv; + struct mwv207_device *jdev = i2c->jdev; + u32 val; + + mwv207_i2c_set_dir(i2c, i2c->scl_mask, i2c->scl_dir, 1); + + val = jdev_read(jdev, i2c->scl_in_addr); + val &= i2c->scl_mask; + + return val ? 1 : 0; +} + +static int mwv207_i2c_get_data(void *i2c_priv) +{ + struct mwv207_i2c *i2c = (struct mwv207_i2c *)i2c_priv; + struct mwv207_device *jdev = i2c->jdev; + u32 val; + + mwv207_i2c_set_dir(i2c, i2c->sda_mask, i2c->sda_dir, 1); + + val = jdev_read(jdev, i2c->sda_in_addr); + val &= i2c->sda_mask; + + return val ? 1 : 0; +} + +static void mwv207_i2c_set_clock(void *i2c_priv, int clock) +{ + struct mwv207_i2c *i2c = (struct mwv207_i2c *)i2c_priv; + struct mwv207_device *jdev = i2c->jdev; + + mwv207_i2c_set_dir(i2c, i2c->scl_mask, i2c->scl_dir, 0); + jdev_modify(jdev, i2c->scl_out_addr, i2c->scl_mask, clock ? i2c->scl_mask : 0); +} + +static void mwv207_i2c_set_data(void *i2c_priv, int data) +{ + struct mwv207_i2c *i2c = (struct mwv207_i2c *)i2c_priv; + + if (data) + mwv207_i2c_set_dir(i2c, i2c->sda_mask, i2c->sda_dir, 1); + else + mwv207_i2c_set_dir(i2c, i2c->sda_mask, i2c->sda_dir, 0); +} + +static int mwv207_i2c_set_addr(struct mwv207_i2c *i2c, int channel) +{ + switch (channel) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + i2c->sda_in_addr = (0x9aa000); + i2c->scl_in_addr = (0x9aa000); + i2c->sda_out_addr = (0x9aa004); + i2c->scl_out_addr = (0x9aa004); + i2c->scl_mask = (1 << i2c_scl_gpio[channel]); + i2c->sda_mask = (1 << i2c_sda_gpio[channel]); + i2c->scl_dir = (0x9aa008); + i2c->sda_dir = (0x9aa008); + break; + case 6: + i2c->sda_in_addr = (0x9aa000); + i2c->scl_in_addr = (0x9aa010); + i2c->sda_out_addr = (0x9aa004); + i2c->scl_out_addr = (0x9aa014); + i2c->scl_mask = (1 << i2c_scl_gpio[channel]); + i2c->sda_mask = (1 << (i2c_sda_gpio[channel] - 32)); + i2c->scl_dir = (0x9aa008); + i2c->sda_dir = (0x9aa018); + break; + case 7: + i2c->sda_in_addr = (0x9aa010); + i2c->scl_in_addr = (0x9aa010); + i2c->sda_out_addr = (0x9aa014); + i2c->scl_out_addr = (0x9aa014); + i2c->scl_mask = (1 << (i2c_scl_gpio[channel] - 32)); + i2c->sda_mask = (1 << (i2c_sda_gpio[channel] - 32)); + i2c->scl_dir = (0x9aa018); + i2c->sda_dir = (0x9aa018); + break; + } + + return 0; +} + +void mwv207_i2c_put_byte(struct i2c_adapter *i2c, + u8 slave_addr, + u8 addr, + u8 val) +{ + uint8_t out_buf[2]; + struct i2c_msg msg = { + .addr = slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = val; + + if (i2c_transfer(i2c, &msg, 1) != 1) + pr_warn("i2c 0x%02x 0x%02x write failed\n", + addr, val); +} + +void mwv207_i2c_destroy(struct i2c_adapter *adapter) +{ + i2c_del_adapter(adapter); +} + +struct i2c_adapter *mwv207_i2c_create(struct mwv207_device *jdev, int i2c_chan) +{ + struct mwv207_i2c *i2c; + int ret; + + if (i2c_chan < 0 || i2c_chan >= 6) + return NULL; + + i2c = devm_kzalloc(jdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return NULL; + + if (mwv207_i2c_set_addr(i2c, i2c_chan)) + return NULL; + + i2c->adapter.owner = THIS_MODULE; + i2c->adapter.class = I2C_CLASS_DDC; + i2c->adapter.dev.parent = jdev->dev; + i2c_set_adapdata(&i2c->adapter, i2c); + i2c->mutex = &jdev->gpio_lock; + i2c->jdev = jdev; + + snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), + "MWV207_I2C_%d", i2c_chan); + i2c->adapter.algo_data = &i2c->bit; + i2c->bit.pre_xfer = mwv207_i2c_pre_xfer; + i2c->bit.post_xfer = mwv207_i2c_post_xfer; + i2c->bit.setsda = mwv207_i2c_set_data; + i2c->bit.setscl = mwv207_i2c_set_clock; + i2c->bit.getsda = mwv207_i2c_get_data; + i2c->bit.getscl = mwv207_i2c_get_clock; + i2c->bit.udelay = 10; + i2c->bit.timeout = usecs_to_jiffies(2200); + i2c->bit.data = i2c; + + ret = i2c_bit_add_bus(&i2c->adapter); + if (ret) + return NULL; + + return &i2c->adapter; +} + +bool mwv207_i2c_probe(struct i2c_adapter *i2c_bus) +{ + u8 out = 0x0; + u8 buf[8]; + int ret; + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &out, + }, + { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = 8, + .buf = buf, + } + }; + + ret = i2c_transfer(i2c_bus, msgs, 2); + if (ret != 2) { + + return false; + } + + if (drm_edid_header_is_valid(buf) < 6) { + return false; + } + return true; +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_kms.c b/drivers/gpu/drm/mwv207/dc/mwv207_kms.c new file mode 100644 index 0000000000000..ca6b9f721a20a --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_kms.c @@ -0,0 +1,204 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mwv207_gem.h" +#include "mwv207_kms.h" +#include "mwv207_va.h" +#include "mwv207_vi.h" + +static const struct drm_framebuffer_funcs fb_funcs = { + .destroy = drm_gem_fb_destroy, + .create_handle = drm_gem_fb_create_handle, +}; + +int mwv207_framebuffer_init(struct mwv207_device *jdev, struct drm_framebuffer *fb, + const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object *gobj) +{ + int ret; + + fb->obj[0] = gobj; + drm_helper_mode_fill_fb_struct(&jdev->base, fb, mode_cmd); + + ret = drm_framebuffer_init(&jdev->base, fb, &fb_funcs); + return ret; +} + +static struct drm_framebuffer *mwv207_framebuffer_create( + struct drm_device *dev, + struct drm_file *filp, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *gobj = drm_gem_object_lookup(filp, mode_cmd->handles[0]); + struct drm_framebuffer *fb; + int err = -ENOENT; + + if (!gobj) { + DRM_DEBUG_DRIVER("fbdev failed to lookup gem object, handle = %d\n", + mode_cmd->handles[0]); + return ERR_PTR(-ENOENT); + } + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) { + err = -ENOMEM; + goto err_unref; + } + + err = mwv207_framebuffer_init(dev->dev_private, fb, mode_cmd, gobj); + if (err) + goto err_free; + + return fb; +err_free: + kfree(fb); +err_unref: + mwv207_gem_object_put(gobj); + return ERR_PTR(err); +} + +static void mwv207_atomic_prepare_vblank(struct drm_device *dev, + struct drm_atomic_state *old_state) +{ + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) + mwv207_crtc_prepare_vblank(crtc); +} + +static void mwv207_atomic_commit_tail(struct drm_atomic_state *old_state) +{ + struct drm_device *dev = old_state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, old_state); + drm_atomic_helper_commit_planes(dev, old_state, 0); + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + mwv207_atomic_prepare_vblank(dev, old_state); + + drm_atomic_helper_commit_hw_done(old_state); + + drm_atomic_helper_wait_for_vblanks(dev, old_state); + drm_atomic_helper_cleanup_planes(dev, old_state); +} + +static struct drm_mode_config_helper_funcs mwv207_mode_config_helper_funcs = { + .atomic_commit_tail = mwv207_atomic_commit_tail, +}; + +static const struct drm_mode_config_funcs mwv207_mode_config_funcs = { + .fb_create = mwv207_framebuffer_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .output_poll_changed = drm_fb_helper_output_poll_changed, +}; + +static void mwv207_modeset_init(struct drm_device *dev) +{ + drm_mode_config_init(dev); + + dev->mode_config.min_width = 20; + dev->mode_config.min_height = 20; + + dev->mode_config.max_width = 4096 * 4; + dev->mode_config.max_height = 4096 * 4; + + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + dev->mode_config.fb_modifiers_not_supported = false; + + dev->mode_config.async_page_flip = true; + + dev->mode_config.funcs = &mwv207_mode_config_funcs; + dev->mode_config.helper_private = &mwv207_mode_config_helper_funcs; + + dev->mode_config.cursor_width = 64; + dev->mode_config.cursor_height = 64; +} + +int mwv207_kms_suspend(struct mwv207_device *jdev) +{ + return drm_mode_config_helper_suspend(&jdev->base); +} + +int mwv207_kms_resume(struct mwv207_device *jdev) +{ + int ret; + + ret = drm_mode_config_helper_resume(&jdev->base); + if (ret) + pr_warn("mwv207: failed to resume kms, ret: %d", ret); + + return ret; +} + +int mwv207_kms_init(struct mwv207_device *jdev) +{ + int ret; + + mwv207_modeset_init(&jdev->base); + + ret = mwv207_va_init(jdev); + if (ret) + goto cleanup; + + ret = mwv207_vi_init(jdev); + if (ret) + goto cleanup; + + jdev->va_irq = irq_find_mapping(jdev->irq_domain, 42); + BUG_ON(jdev->va_irq == 0); + ret = request_irq(jdev->va_irq, mwv207_va_handle_vblank, 0, "mwv207_va", jdev); + if (ret) + goto cleanup; + + ret = drm_vblank_init(&jdev->base, jdev->base.mode_config.num_crtc); + if (ret) + goto free_irq; + + drm_mode_config_reset(&jdev->base); + + drm_kms_helper_poll_init(&jdev->base); + + DRM_INFO("number of crtc: %d\n", jdev->base.mode_config.num_crtc); + DRM_INFO("number of encoder: %d\n", jdev->base.mode_config.num_encoder); + DRM_INFO("number of connector: %d\n", jdev->base.mode_config.num_connector); + + return 0; +free_irq: + free_irq(jdev->va_irq, jdev); +cleanup: + drm_mode_config_cleanup(&jdev->base); + return ret; +} + +void mwv207_kms_fini(struct mwv207_device *jdev) +{ + drm_kms_helper_poll_fini(&jdev->base); + drm_atomic_helper_shutdown(&jdev->base); + free_irq(jdev->va_irq, jdev); + drm_mode_config_cleanup(&jdev->base); +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_kms.h b/drivers/gpu/drm/mwv207/dc/mwv207_kms.h new file mode 100644 index 0000000000000..6c6c1776e9302 --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_kms.h @@ -0,0 +1,30 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_KMS_H_VBCHTKVP +#define MWV207_KMS_H_VBCHTKVP + +#include +#include "mwv207.h" + +int mwv207_framebuffer_init(struct mwv207_device *jdev, struct drm_framebuffer *fb, + const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object *gobj); + +int mwv207_kms_init(struct mwv207_device *jdev); +void mwv207_kms_fini(struct mwv207_device *jdev); + +int mwv207_kms_suspend(struct mwv207_device *jdev); +int mwv207_kms_resume(struct mwv207_device *jdev); +#endif diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_va.c b/drivers/gpu/drm/mwv207/dc/mwv207_va.c new file mode 100644 index 0000000000000..9a9d33c8a2c9e --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_va.c @@ -0,0 +1,691 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mwv207_bo.h" +#include "mwv207_va.h" +#include "mwv207_vi.h" +#include "mwv207_vbios.h" + +#define MWV207_WIN_BASE(idx) (0x990000 + 0x100 * (idx) + ((idx) > 1 ? 0xC00 : 0)) + +#define MWV207_VA_BASE(idx) (0x990000 + 0x100 * (idx) + ((idx) > 1 ? 0x600 : 0)) + +#define crtc_to_va(crtc) container_of(crtc, struct mwv207_va, crtc) +#define plane_to_va(plane) (((plane)->type == DRM_PLANE_TYPE_PRIMARY) \ + ? container_of(plane, struct mwv207_va, primary) \ + : container_of(plane, struct mwv207_va, cursor)) +struct mwv207_va { + struct drm_crtc crtc; + struct drm_plane primary; + struct drm_plane cursor; + void __iomem *mmio; + struct mwv207_device *jdev; + struct drm_pending_vblank_event *event; + int idx; + uint16_t lutdata[768]; +}; + +static u32 rgb_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB2101010, +}; + +static uint64_t format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static u32 cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static inline u32 mwv207_va_read(struct mwv207_va *va, u32 reg) +{ + return readl(va->mmio + reg); +} + +static inline void mwv207_va_write(struct mwv207_va *va, u32 reg, u32 value) +{ + writel_relaxed(value, va->mmio + reg); +} + +static inline void mwv207_va_modify(struct mwv207_va *va, u32 reg, + u32 mask, u32 value) +{ + u32 rvalue = mwv207_va_read(va, reg); + + rvalue = (rvalue & ~mask) | (value & mask); + mwv207_va_write(va, reg, rvalue); +} + +static bool mwv207_plane_format_mod_supported(struct drm_plane *plane, + uint32_t format, uint64_t modifier) +{ + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + return false; +} + +static int mwv207_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + + return 0; +} + +static int mwv207_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct mwv207_bo *jbo; + int ret; + + if (!new_state->fb) + return 0; + + jbo = mwv207_bo_from_gem(new_state->fb->obj[0]); + + ret = mwv207_bo_reserve(jbo, false); + if (ret) + return ret; + + jbo->flags |= (1<<0); + ret = mwv207_bo_pin_reserved(jbo, 0x2); + mwv207_bo_unreserve(jbo); + if (ret) + return ret; + + mwv207_bo_ref(jbo); + + drm_gem_plane_helper_prepare_fb(plane, new_state); + + return 0; +} + +static void mwv207_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct mwv207_bo *jbo; + + if (!old_state->fb) + return; + + jbo = mwv207_bo_from_gem(old_state->fb->obj[0]); + if (!jbo) + return; + + mwv207_bo_unpin(jbo); + mwv207_bo_unref(jbo); +} + +static u64 mwv207_plane_fb_addr(struct drm_plane *plane) +{ + struct drm_framebuffer *fb; + struct mwv207_bo *bo; + u32 src_x, src_y; + u64 fbaddr; + + src_x = plane->state->src_x >> 16; + src_y = plane->state->src_y >> 16; + + fb = plane->state->fb; + bo = mwv207_bo_from_gem(fb->obj[0]); + fbaddr = mwv207_bo_gpu_phys(bo); + fbaddr += src_x * fb->format->cpp[0] + fb->pitches[0] * src_y; + + return fbaddr; +} + +static void mwv207_primary_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + struct mwv207_va *va = plane_to_va(plane); + struct drm_framebuffer *fb; + u32 src_w, src_h; + u64 fbaddr; + + if (!plane->state->fb || !plane->state->crtc) + return; + + src_w = plane->state->src_w >> 16; + src_h = plane->state->src_h >> 16; + fbaddr = mwv207_plane_fb_addr(plane); + fb = plane->state->fb; + + mwv207_va_write(va, 0x434, fb->pitches[0] >> 4); + + mwv207_va_write(va, 0x43C, ((src_h - 1) << 16) | (src_w - 1) | 0x3); + + mwv207_va_write(va, 0x4F8, (src_w - 1) << 16); + + mwv207_va_write(va, 0x438, fbaddr >> 6); + + switch (fb->format->format) { + case DRM_FORMAT_RGB565: + mwv207_va_modify(va, 0x430, 1, 1); + break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + mwv207_va_modify(va, 0x430, 1 << 1, 1 << 1); + break; + case DRM_FORMAT_ARGB8888: + mwv207_va_modify(va, 0x430, 0x3, 0); + break; + default: + break; + } +} + +static void mwv207_primary_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + +} + +static void mwv207_cursor_switch(struct mwv207_va *va, bool on) +{ + mwv207_va_modify(va, 0x478, 0xff, on ? 6 : 0); +} + +static void mwv207_cursor_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + int crtc_x, crtc_y, pos_x, pos_y, hot_x, hot_y; + struct mwv207_va *va = plane_to_va(plane); + u64 fbaddr; + + if (!plane->state->fb || !plane->state->crtc) + return; + + crtc_x = plane->state->crtc_x; + crtc_y = plane->state->crtc_y; + fbaddr = mwv207_plane_fb_addr(plane); + + mwv207_va_write(va, 0x4B4, fbaddr >> 6); + + pos_x = crtc_x < 0 ? 0 : crtc_x; + hot_x = crtc_x < 0 ? -crtc_x : 0; + pos_y = crtc_y < 0 ? 0 : crtc_y; + hot_y = crtc_y < 0 ? -crtc_y : 0; + mwv207_va_write(va, 0x454, (hot_x & 0x3f) | ((hot_y & 0x3f) << 16)); + mwv207_va_write(va, 0x4A8, (pos_x & 0xffff) | ((pos_y & 0xffff) << 16)); + + mwv207_cursor_switch(va, 1); +} + +static void mwv207_cursor_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + struct mwv207_va *va = plane_to_va(plane); + + mwv207_cursor_switch(va, 0); +} + +static const struct drm_plane_funcs mwv207_plane_funcs = { + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .format_mod_supported = mwv207_plane_format_mod_supported, + .destroy = drm_plane_cleanup, +}; + +static const struct drm_plane_helper_funcs mwv207_primary_helper_funcs = { + .prepare_fb = mwv207_plane_prepare_fb, + .cleanup_fb = mwv207_plane_cleanup_fb, + .atomic_check = mwv207_plane_atomic_check, + .atomic_update = mwv207_primary_atomic_update, + .atomic_disable = mwv207_primary_atomic_disable, +}; + +static const struct drm_plane_helper_funcs mwv207_cursor_helper_funcs = { + .prepare_fb = mwv207_plane_prepare_fb, + .cleanup_fb = mwv207_plane_cleanup_fb, + .atomic_check = mwv207_plane_atomic_check, + .atomic_update = mwv207_cursor_atomic_update, + .atomic_disable = mwv207_cursor_atomic_disable, +}; + +static void mwv207_va_misc_reset(struct mwv207_va *va) +{ + struct mwv207_device *jdev = va->jdev; + + jdev_write(jdev, (0x9b001c), 0xfffff); + jdev_write(jdev, (0x9b0020), 0xfffff); + jdev_write(jdev, (0x9b0024), 0xfffff); + + jdev_write(jdev, (0x9b0028), 0x03020100); + + jdev_write(jdev, MWV207_WIN_BASE(va->idx) + 0x2c, 0); +} + +static int mwv207_va_lut_fifo_wait(struct mwv207_va *va, u32 used) +{ + int i; + + mb(); + for (i = 0; i < 120; ++i) { + if (mwv207_va_read(va, 0x450) <= used) + return 0; + usleep_range(1000, 1001); + } + + return -EBUSY; +} + +static void mwv207_va_lut_fifo_activate(struct mwv207_va *va, int rgb) +{ + + mb(); + mwv207_va_write(va, 0x44C, rgb); + mwv207_va_write(va, 0x448, 1); + udelay(2); + mwv207_va_write(va, 0x448, 0); + udelay(2); +} + +static int mwv207_va_lut_fifo_input(struct mwv207_va *va, u32 value) +{ + int ret; + + ret = mwv207_va_lut_fifo_wait(va, 10); + if (ret) + return ret; + + mb(); + mwv207_va_write(va, 0x444, value); + + return 0; +} + +static void mwv207_va_lut_enable(struct mwv207_va *va) +{ + u32 active_ram, value; + int i, ram, rgb; + u16 *data; + + mwv207_va_modify(va, 0x460, 1 << 31, 0 << 31); + value = (va->lutdata[384] * 15 / 8) | 0x80000000; + jdev_write(va->jdev, MWV207_WIN_BASE(va->idx) + 0x38, value); + + for (ram = 0; ram < 2; ram++) { + active_ram = mwv207_va_read(va, 0x440); + for (rgb = 0; rgb < 3; rgb++) { + data = &va->lutdata[rgb * 256]; + mwv207_va_lut_fifo_activate(va, rgb); + for (i = 0; i < 1024; i++) { + + value = min_t(u32, 1024 - 5, (data[i / 4] << 2) | 0x03); + if (mwv207_va_lut_fifo_input(va, value)) + pr_warn("mwv207: failed to write lut"); + } + + if (mwv207_va_lut_fifo_wait(va, 0)) + pr_warn("mwv207: failed to configure lut"); + } + + mb(); + mwv207_va_write(va, 0x440, 1 - active_ram); + } +} + +static void mwv207_mode_fixup(struct drm_display_mode *adjusted_mode, struct drm_display_mode *mode) +{ + memcpy(adjusted_mode, mode, sizeof(*adjusted_mode)); + + mode->crtc_hdisplay = adjusted_mode->crtc_hdisplay * 2; + mode->crtc_hblank_start = adjusted_mode->crtc_hblank_start * 2; + mode->crtc_hblank_end = adjusted_mode->crtc_hblank_end * 2; + mode->crtc_hsync_start = adjusted_mode->crtc_hsync_start * 2; + mode->crtc_hsync_end = adjusted_mode->crtc_hsync_end * 2; + mode->crtc_htotal = adjusted_mode->crtc_htotal * 2; + mode->crtc_clock = adjusted_mode->crtc_clock * 2; +} + +static void mwv207_va_config_timing(struct mwv207_va *va) +{ + struct drm_display_mode *m = &va->crtc.state->adjusted_mode; + struct drm_display_mode *native = NULL; + u32 hsbegin, hsend, hblankbegin, hblankend, htotal, hhalf; + u32 vblank_1field_begin, vblank_1field_end; + u32 vblank_2field_begin, vblank_2field_end; + u32 vs_1field_begin, vs_1field_end; + u32 vs_2field_begin, vs_2field_end; + u32 is_interleaved; + u32 vfrontporch, vsync, vtotal; + int ret, value; + + ret = mwv207_vbios_set_pll(va->jdev, va->idx + MWV207_PLL_GRAPH0, m->crtc_clock); + if (ret) { + DRM_ERROR("mwv207: failed to set crtc clock: %d", ret); + return; + } + + is_interleaved = DRM_MODE_FLAG_INTERLACE & m->flags ? 1 : 0; + if (is_interleaved && m->crtc_hdisplay == 720) { + native = kmalloc(sizeof(*m), GFP_KERNEL); + if (!native) { + DRM_ERROR("mwv207: failed to kmalloc native mode\r\n"); + return; + } + mwv207_mode_fixup(m, native); + m = native; + } + + hblankbegin = 0; + hblankend = m->crtc_htotal - m->crtc_hdisplay; + htotal = m->crtc_htotal; + hsbegin = m->crtc_hsync_start - m->crtc_hdisplay; + hsend = m->crtc_hsync_end - m->crtc_hdisplay; + hhalf = is_interleaved ? (htotal / 2 + hsbegin) : htotal / 2; + + vblank_1field_begin = 0; + vblank_1field_end = is_interleaved ? + ((m->crtc_vtotal - m->crtc_vdisplay) * 2) + : (m->crtc_vtotal - m->crtc_vdisplay); + + vblank_2field_begin = + (m->crtc_vdisplay + (m->crtc_vtotal - m->crtc_vdisplay)) * 2; + vblank_2field_end = + (m->crtc_vdisplay + (m->crtc_vtotal - m->crtc_vdisplay)) * 2 + + (m->crtc_vtotal - m->crtc_vdisplay + 1) * 2; + + vtotal = m->crtc_vtotal; + vfrontporch = m->crtc_vsync_start - m->crtc_vdisplay; + vsync = m->crtc_vsync_end - m->crtc_vsync_start; + vs_1field_begin = is_interleaved ? (vfrontporch * 2) : (vfrontporch); + vs_1field_end = is_interleaved ? ((vfrontporch + vsync) * 2) + : (vfrontporch + vsync); + vs_2field_begin = + (m->crtc_vdisplay + (m->crtc_vtotal - m->crtc_vdisplay)) * 2 + + vfrontporch * 2 + 1; + vs_2field_end = + (m->crtc_vdisplay + (m->crtc_vtotal - m->crtc_vdisplay)) * 2 + + (vfrontporch + vsync) * 2 + 1; + + mwv207_va_write(va, 0x400, hsbegin | (hsend << 16)); + mwv207_va_write(va, 0x404, hblankbegin | (hblankend << 16)); + mwv207_va_write(va, 0x408, htotal | (hhalf << 16)); + + mwv207_va_write(va, 0x40C, vblank_1field_begin | (vblank_1field_end << 16)); + mwv207_va_write(va, 0x458, vblank_1field_begin | (vblank_1field_end << 16)); + mwv207_va_write(va, 0x410, vblank_2field_begin | (vblank_2field_end << 16)); + mwv207_va_write(va, 0x45C, vblank_2field_begin | (vblank_2field_end << 16)); + + mwv207_va_write(va, 0x418, vs_1field_begin | (vs_1field_end << 16)); + mwv207_va_write(va, 0x41C, vs_2field_begin | (vs_2field_end << 16)); + + mwv207_va_modify(va, 0x414, 0xffff, vtotal); + + value = is_interleaved; + if (is_interleaved && m->crtc_hdisplay == 1440) + value |= 0x2; + mwv207_va_write(va, 0x464, value); + + mwv207_va_write(va, 0x420, 1); + + value = mwv207_va_read(va, 0x2a0); + value &= ~0x6; + value |= 1; + mwv207_va_write(va, 0x2a0, value); + + kfree(native); +} + +static int mwv207_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct mwv207_va *va = crtc_to_va(crtc); + + jdev_modify(va->jdev, (0x9907f0), + 1 << (12 + va->idx), 1 << (12 + va->idx)); + jdev_modify(va->jdev, (0x9907f8), + 1 << (12 + va->idx), 1 << (12 + va->idx)); + return 0; +} + +static void mwv207_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct mwv207_va *va = crtc_to_va(crtc); + + jdev_modify(va->jdev, (0x9907f0), + 1 << (12 + va->idx), 0 << (12 + va->idx)); + jdev_modify(va->jdev, (0x9907f8), + 1 << (12 + va->idx), 0 << (12 + va->idx)); +} + +static int mwv207_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + if (crtc_state->gamma_lut && drm_color_lut_size(crtc_state->gamma_lut) != 256) + return -EINVAL; + + return 0; +} + +static void mwv207_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *old_crtc_state) +{ + struct mwv207_va *va = crtc_to_va(crtc); + struct drm_encoder *encoder; + + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) + mwv207_output_set_crtc(encoder, crtc); + + mwv207_va_config_timing(va); + drm_crtc_vblank_on(crtc); +} + +static void mwv207_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *old_crtc_state) +{ + struct mwv207_va *va = crtc_to_va(crtc); + + drm_crtc_vblank_off(crtc); + + mwv207_va_write(va, 0x2c4, 0); + mwv207_va_write(va, 0x2c0, 0); + mwv207_va_write(va, 0x2a0, 2); + mwv207_va_write(va, 0x2c4, 1); + +} + +static void mwv207_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *old_crtc_state) +{ + struct mwv207_va *va = crtc_to_va(crtc); + struct drm_color_lut *lut; + int i; + + if (crtc->state->color_mgmt_changed) { + if (crtc->state->gamma_lut) { + lut = (struct drm_color_lut *)crtc->state->gamma_lut->data; + for (i = 0; i < 256; i++) { + va->lutdata[i] = drm_color_lut_extract(lut[i].red, 8); + va->lutdata[i + 256] = drm_color_lut_extract(lut[i].green, 8); + va->lutdata[i + 512] = drm_color_lut_extract(lut[i].blue, 8); + } + mwv207_va_lut_enable(va); + } + } +} + +static void mwv207_va_reset(struct drm_crtc *crtc) +{ + struct mwv207_va *va = crtc_to_va(crtc); + + mwv207_cursor_switch(va, 0); + + mwv207_va_lut_enable(va); + mwv207_va_misc_reset(va); + drm_atomic_helper_crtc_reset(crtc); +} + +static const struct drm_crtc_funcs mwv207_crtc_funcs = { + .reset = mwv207_va_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .page_flip = drm_atomic_helper_page_flip, + .enable_vblank = mwv207_crtc_enable_vblank, + .disable_vblank = mwv207_crtc_disable_vblank, +}; + +static const struct drm_crtc_helper_funcs mwv207_crtc_helper_funcs = { + .atomic_check = mwv207_crtc_atomic_check, + .atomic_flush = mwv207_crtc_atomic_flush, + .atomic_enable = mwv207_crtc_atomic_enable, + .atomic_disable = mwv207_crtc_atomic_disable, +}; + +void mwv207_crtc_prepare_vblank(struct drm_crtc *crtc) +{ + struct drm_pending_vblank_event *event = crtc->state->event; + struct mwv207_va *va = crtc_to_va(crtc); + unsigned long flags; + + if (event) { + if (crtc->state->active) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + spin_lock_irqsave(&crtc->dev->event_lock, flags); + va->event = event; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + } else { + spin_lock_irqsave(&crtc->dev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + } + crtc->state->event = NULL; + } +} + +void mwv207_crtc_finish_vblank(struct drm_crtc *crtc) +{ + struct mwv207_va *va = crtc_to_va(crtc); + int put_vblank = false; + unsigned long flags; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (va->event) { + drm_crtc_send_vblank_event(crtc, va->event); + va->event = NULL; + put_vblank = true; + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + if (put_vblank) + drm_crtc_vblank_put(crtc); +} + +irqreturn_t mwv207_va_handle_vblank(int irq, void *data) +{ + struct mwv207_device *jdev = data; + struct drm_crtc *crtc; + u32 fired; + + fired = jdev_read(jdev, (0x9907f4)); + if (!fired) + return IRQ_NONE; + jdev_write(jdev, (0x9907f4), fired); + + fired = (fired >> 12) & 0xf; + drm_for_each_crtc(crtc, &jdev->base) { + if (fired & (1 << drm_crtc_index(crtc))) { + drm_crtc_handle_vblank(crtc); + mwv207_crtc_finish_vblank(crtc); + } + } + + return IRQ_HANDLED; +} + +static int mwv207_va_init_single(struct mwv207_device *jdev, int idx) +{ + struct mwv207_va *va; + uint16_t *lut; + int ret, i; + + va = devm_kzalloc(jdev->dev, sizeof(*va), GFP_KERNEL); + if (!va) + return -ENOMEM; + + va->mmio = jdev->mmio + MWV207_VA_BASE(idx); + va->jdev = jdev; + va->idx = idx; + + ret = drm_universal_plane_init(&jdev->base, &va->primary, 1 << idx, + &mwv207_plane_funcs, + rgb_formats, ARRAY_SIZE(rgb_formats), + format_modifiers, DRM_PLANE_TYPE_PRIMARY, "primary_plane_%d", idx); + if (ret) + return ret; + ret = drm_universal_plane_init(&jdev->base, &va->cursor, 1 << idx, + &mwv207_plane_funcs, + cursor_formats, ARRAY_SIZE(cursor_formats), + format_modifiers, DRM_PLANE_TYPE_CURSOR, "cursor_%d", idx); + if (ret) + return ret; + + drm_plane_helper_add(&va->primary, &mwv207_primary_helper_funcs); + drm_plane_helper_add(&va->cursor, &mwv207_cursor_helper_funcs); + + ret = drm_crtc_init_with_planes(&jdev->base, &va->crtc, + &va->primary, &va->cursor, &mwv207_crtc_funcs, "crtc_%d", + idx); + if (ret) + return ret; + + drm_crtc_helper_add(&va->crtc, &mwv207_crtc_helper_funcs); + + lut = &va->lutdata[0]; + for (i = 0; i < 256; i++) + lut[i] = lut[i + 256] = lut[i + 512] = i; + drm_crtc_enable_color_mgmt(&va->crtc, 768, true, 768); + ret = drm_mode_crtc_set_gamma_size(&va->crtc, 256); + + BUG_ON(drm_crtc_index(&va->crtc) != va->idx); + + return ret; +} + +int mwv207_va_init(struct mwv207_device *jdev) +{ + int i, nr, ret; + + nr = jdev->lite ? 2 : 4; + for (i = 0; i < nr; i++) { + ret = mwv207_va_init_single(jdev, i); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_va.h b/drivers/gpu/drm/mwv207/dc/mwv207_va.h new file mode 100644 index 0000000000000..2758507b4bfb6 --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_va.h @@ -0,0 +1,25 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#ifndef MWV207_VA_H_BSY8LF4F +#define MWV207_VA_H_BSY8LF4F + +struct drm_crtc; +int mwv207_va_init(struct mwv207_device *jdev); + +void mwv207_crtc_prepare_vblank(struct drm_crtc *crtc); +irqreturn_t mwv207_va_handle_vblank(int irq, void *data); +#endif diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_vga.c b/drivers/gpu/drm/mwv207/dc/mwv207_vga.c new file mode 100644 index 0000000000000..b09e9d614a4e6 --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_vga.c @@ -0,0 +1,287 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207_vi.h" + +#define MWV207_DAC_HPD_VS_CLC(_chan) (0x64 + (_chan) * 0x14) +#define MWV207_DAC_HPD_PULSE_H(_chan) (0x68 + (_chan) * 0x14) +#define MWV207_DAC_HPD_PULSE_L(_chan) (0x6C + (_chan) * 0x14) +#define MWV207_DAC_HPD_DET_PER(_chan) (0x70 + (_chan) * 0x14) +#define MWV207_DAC_HPD_DET_THR(_chan) (0x74 + (_chan) * 0x14) + +#define output_to_vga(output) container_of(output, struct mwv207_vga, base) + +static uint mwv207_vga_load_detect = 1; +module_param(mwv207_vga_load_detect, uint, 0444); +MODULE_PARM_DESC(mwv207_vga_load_detect, "Enable vga load detect if set to 1, default is 1"); + +struct mwv207_vga { + struct mwv207_output base; + spinlock_t load_detect_lock; + bool active; +}; + +static void mwv207_vga_set_detect_mode(struct mwv207_output *output, bool active) +{ + u32 val, status, load_mode; + int i; + + mwv207_output_write(output, 0xA4, 0); + + val = 0x3ff * 7 / 10; + status = mwv207_output_read(output, 0xA8); + mwv207_output_write(output, 0xA8, status); + + status = val << 20 | val << 10 | val; + mwv207_output_write(output, 0xAC, status); + + load_mode = active ? 1 : 0; + status = load_mode << 20 | load_mode << 16 | load_mode << 12 | 0x1 << 8 | 0x1 << 4 | 0x1; + mwv207_output_write(output, 0xAC, status); + + for (i = 0; i < 3; i++) { + if (active) { + mwv207_output_write(output, MWV207_DAC_HPD_VS_CLC(i), 256); + mwv207_output_write(output, MWV207_DAC_HPD_PULSE_H(i), 48); + mwv207_output_write(output, MWV207_DAC_HPD_DET_PER(i), 188); + mwv207_output_write(output, MWV207_DAC_HPD_DET_THR(i), 164); + } else { + mwv207_output_write(output, MWV207_DAC_HPD_PULSE_H(i), 1600000); + mwv207_output_write(output, MWV207_DAC_HPD_PULSE_L(i), 798400000); + mwv207_output_write(output, MWV207_DAC_HPD_DET_PER(i), 1600128); + mwv207_output_write(output, MWV207_DAC_HPD_DET_THR(i), 8128); + } + } + + mwv207_output_write(output, 0xA4, 0x111111); +} + +static void mwv207_vga_switch(struct mwv207_output *output, bool on) +{ + struct mwv207_vga *vga = output_to_vga(output); + + if (!output->jdev->lite || !mwv207_vga_load_detect) { + mwv207_output_modify(output, 0x0, 1 << 31, (on ? 1 : 0) << 31); + return; + } + + spin_lock(&vga->load_detect_lock); + mwv207_output_modify(output, 0x0, 1 << 31, (on ? 1 : 0) << 31); + mwv207_vga_set_detect_mode(output, on); + vga->active = on; + spin_unlock(&vga->load_detect_lock); +} + +static void mwv207_vga_config(struct mwv207_output *output) +{ + struct drm_display_mode *mode = &output->cur_crtc->state->adjusted_mode; + int hpol, vpol; + + hpol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1; + vpol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1; + + mwv207_output_write(output, 0x4C, 0); + mwv207_output_modify(output, 0x0, 0x7 << 16, 0x7 << 16); + mwv207_output_modify(output, 0x0, 0x1 << 2, hpol << 2); + mwv207_output_modify(output, 0x0, 0x1 << 3, vpol << 3); + + mwv207_output_write(output, 0x4, 0x1666); + mwv207_output_write(output, 0x18, 0x1666); + mwv207_output_write(output, 0x2C, 0x1666); +} + +static void mwv207_vga_select_crtc(struct mwv207_output *output) +{ + + mwv207_output_modify(output, 0x0, 0x3 << 24, + drm_crtc_index(output->cur_crtc) << 24); + + jdev_modify(output->jdev, 0x9b0038, 0xf << 16, + drm_crtc_index(output->cur_crtc) << 16); +} +static enum drm_mode_status mwv207_vga_mode_valid(struct mwv207_output *output, + const struct drm_display_mode *mode) +{ + + if (mode->clock > 193250) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static enum drm_mode_status mwv207_vga_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return mwv207_vga_mode_valid(connector_to_output(connector), mode); +} + +static int mwv207_vga_detect_load(struct mwv207_output *output) +{ + struct mwv207_vga *vga = output_to_vga(output); + u32 r_state, g_state, b_state, value; + bool active; + + if (mwv207_i2c_probe(output->ddc)) + return connector_status_connected; + + spin_lock(&vga->load_detect_lock); + value = mwv207_output_read(output, 0xA8); + active = vga->active; + spin_unlock(&vga->load_detect_lock); + + r_state = (value >> 8) & 0x1; + g_state = (value >> 4) & 0x1; + b_state = (value >> 0) & 0x1; + DRM_DEBUG_DRIVER("active %d r_state = 0x%x, g_state = 0x%x, b_state = 0x%x", + active, r_state, g_state, b_state); + + if (active) + return (r_state && g_state && b_state) ? + connector_status_disconnected : connector_status_connected; + + return (r_state || g_state || b_state) ? + connector_status_connected : connector_status_disconnected; +} + +static int mwv207_vga_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct mwv207_output *output = connector_to_output(connector); + + if (!output->jdev->lite || !mwv207_vga_load_detect) + return mwv207_i2c_probe(output->ddc) ? + connector_status_connected : connector_status_disconnected; + + return mwv207_vga_detect_load(output); +} + +static void mwv207_vga_destroy(struct drm_connector *conn) +{ + drm_connector_unregister(conn); + drm_connector_cleanup(conn); +} + +static const struct drm_connector_helper_funcs mwv207_vga_connector_helper_funcs = { + .get_modes = mwv207_output_get_modes, + .mode_valid = mwv207_vga_connector_mode_valid, + .detect_ctx = mwv207_vga_detect_ctx +}; + +static const struct drm_connector_funcs mwv207_vga_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .destroy = mwv207_vga_destroy, + .late_register = mwv207_output_late_register, + .early_unregister = mwv207_output_early_unregister, +}; + +static enum drm_mode_status mwv207_vga_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + return mwv207_vga_mode_valid(output, mode); +} + +static int mwv207_vga_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return 0; +} + +static void mwv207_vga_encoder_enable(struct drm_encoder *encoder) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + mwv207_vga_select_crtc(output); + + mwv207_vga_config(output); + + mwv207_vga_switch(output, true); +} + +static void mwv207_vga_encoder_disable(struct drm_encoder *encoder) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + mwv207_vga_switch(output, false); +} + +static void mwv207_vga_encoder_reset(struct drm_encoder *encoder) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + if (output->jdev->lite && !mwv207_vga_load_detect) { + mwv207_output_write(output, 0xA4, 0); + mwv207_output_modify(output, 0x58, 0x1 << 16, 0); + } + + mwv207_vga_encoder_disable(encoder); +} + +static const struct drm_encoder_funcs mwv207_vga_encoder_funcs = { + .destroy = drm_encoder_cleanup, + .reset = mwv207_vga_encoder_reset, +}; + +static const struct drm_encoder_helper_funcs mwv207_vga_encoder_helper_funcs = { + .mode_valid = mwv207_vga_encoder_mode_valid, + .atomic_check = mwv207_vga_encoder_atomic_check, + .enable = mwv207_vga_encoder_enable, + .disable = mwv207_vga_encoder_disable, +}; + +int mwv207_vga_init(struct mwv207_device *jdev) +{ + struct mwv207_output *output; + struct mwv207_vga *vga; + int ret; + + vga = devm_kzalloc(jdev->dev, sizeof(*vga), GFP_KERNEL); + if (!vga) + return -ENOMEM; + + spin_lock_init(&vga->load_detect_lock); + output = &vga->base; + output->jdev = jdev; + output->idx = 0; + output->mmio = jdev->mmio + 0x9a0000; + output->i2c_chan = 4; + + output->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + ret = drm_connector_init(&jdev->base, &output->connector, + &mwv207_vga_connector_funcs, DRM_MODE_CONNECTOR_VGA); + if (ret) + return ret; + drm_connector_helper_add(&output->connector, &mwv207_vga_connector_helper_funcs); + + output->encoder.possible_crtcs = (1 << jdev->base.mode_config.num_crtc) - 1; + ret = drm_encoder_init(&jdev->base, &output->encoder, + &mwv207_vga_encoder_funcs, DRM_MODE_ENCODER_DAC, + "vga-%d", output->idx); + if (ret) + return ret; + drm_encoder_helper_add(&output->encoder, &mwv207_vga_encoder_helper_funcs); + + ret = drm_connector_attach_encoder(&output->connector, &output->encoder); + if (ret) + return ret; + + return drm_connector_register(&output->connector); +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_vi.c b/drivers/gpu/drm/mwv207/dc/mwv207_vi.c new file mode 100644 index 0000000000000..8881ead9ce16f --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_vi.c @@ -0,0 +1,86 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207_vi.h" + +int mwv207_output_late_register(struct drm_connector *connector) +{ + struct mwv207_output *output = connector_to_output(connector); + + output->ddc = mwv207_i2c_create(output->jdev, output->i2c_chan); + if (!output->ddc) { + DRM_ERROR("Failed to create i2c adapter\n"); + return -ENODEV; + } + + return 0; +} + +void mwv207_output_early_unregister(struct drm_connector *connector) +{ + struct mwv207_output *output = connector_to_output(connector); + + mwv207_i2c_destroy(output->ddc); +} + +int mwv207_output_get_modes(struct drm_connector *connector) +{ + struct mwv207_output *output = connector_to_output(connector); + struct edid *edid; + int count; + + if (output->edidforce && output->edid) + edid = output->edid; + else + edid = drm_get_edid(connector, output->ddc); + + output->has_audio = drm_detect_monitor_audio(edid); + drm_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + + if (!(output->edidforce && output->edid)) + kfree(edid); + + return count; +} + +void mwv207_output_set_crtc(struct drm_encoder *encoder, struct drm_crtc *crtc) +{ + struct mwv207_output *output = encoder_to_output(encoder); + + output->cur_crtc = crtc; +} + +int mwv207_vi_init(struct mwv207_device *jdev) +{ + int ret; + + mutex_init(&jdev->gpio_lock); + + ret = mwv207_edp_init(jdev); + if (ret) + return ret; + ret = mwv207_hdmi_init(jdev); + if (ret) + return ret; + ret = mwv207_vga_init(jdev); + if (ret) + return ret; + ret = mwv207_dvo_init(jdev); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_vi.h b/drivers/gpu/drm/mwv207/dc/mwv207_vi.h new file mode 100644 index 0000000000000..effa6d2ce7e3e --- /dev/null +++ b/drivers/gpu/drm/mwv207/dc/mwv207_vi.h @@ -0,0 +1,96 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207.h" +#include +#include +#include +#include +#include + +#ifndef MWV207_DAL_H_OUVHEJAX +#define MWV207_DAL_H_OUVHEJAX + +struct i2c_adapter; + +struct mwv207_output { + struct drm_connector connector; + struct drm_encoder encoder; + + struct drm_crtc *cur_crtc; + + /* mapped to video interface global control regs + * which are shared among all outputs + */ + void __iomem *mmio; + struct mwv207_device *jdev; + + struct i2c_adapter *ddc; + struct edid *edid; + bool edidforce; + int i2c_chan; + + /* local index, e.g. hdmi0/1/2/3, edp0/1 */ + int idx; + + bool has_audio; +}; +#define connector_to_output(conn) container_of(conn, struct mwv207_output, connector) +#define encoder_to_output(encoder) container_of(encoder, struct mwv207_output, encoder) + +/* Note: any resources allocated by following funcs should be: + * 1. devres managed or + * 2. cleaned up by drm_mode_config_cleanup + */ +int mwv207_vi_init(struct mwv207_device *jdev); +int mwv207_edp_init(struct mwv207_device *jdev); +int mwv207_hdmi_init(struct mwv207_device *jdev); +int mwv207_vga_init(struct mwv207_device *jdev); +int mwv207_dvo_init(struct mwv207_device *jdev); + +struct i2c_adapter *mwv207_i2c_create(struct mwv207_device *jdev, int i2c_chan); +void mwv207_i2c_destroy(struct i2c_adapter *adapter); +bool mwv207_i2c_probe(struct i2c_adapter *i2c_bus); + +int mwv207_output_late_register(struct drm_connector *connector); +void mwv207_output_early_unregister(struct drm_connector *connector); + +int mwv207_output_get_modes(struct drm_connector *connector); +void mwv207_output_set_crtc(struct drm_encoder *encoder, struct drm_crtc *crtc); + +static inline u32 mwv207_output_read(struct mwv207_output *output, u32 reg) +{ + BUG_ON(reg >= 0x8000); + return readl(output->mmio + reg); +} + +static inline void mwv207_output_write(struct mwv207_output *output, u32 reg, u32 value) +{ + BUG_ON(reg >= 0x8000); + writel_relaxed(value, output->mmio + reg); +} + +static inline void mwv207_output_modify(struct mwv207_output *output, u32 reg, + u32 mask, u32 value) +{ + u32 rvalue; + + BUG_ON(reg >= 0x8000); + rvalue = mwv207_output_read(output, reg); + rvalue = (rvalue & ~mask) | (value & mask); + mwv207_output_write(output, reg, rvalue); +} + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207.h b/drivers/gpu/drm/mwv207/mwv207.h new file mode 100644 index 0000000000000..d5c302d82e412 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207.h @@ -0,0 +1,125 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_H_VTIQLF2Y +#define MWV207_H_VTIQLF2Y + +#include +#include +#include +#include +#include +#include + +struct mwv207_db; + +struct mwv207_vbios { + + struct mutex vcmd_lock; + + struct mutex cfg_lock; + struct idr cfg_table; + + int indexer_valid; + int sector_count; +}; + +#define ddev_to_jdev(ddev) ((struct mwv207_device *) (ddev)->dev_private) +#define bdev_to_jdev(dev) container_of(dev, struct mwv207_device, bdev) +struct mwv207_device { + struct drm_device base; + struct pci_dev *pdev; + struct ttm_device bdev; + + void __iomem *mmio; + void __iomem *iatu; + + void *win; + + spinlock_t win_lock; + u64 visible_vram_size; + u64 vram_size; + u64 vram_bar_base; + u64 vram_last_win; + u64 pci_win_base; + + struct device *dev; + struct drm_fb_helper *fb_helper; + + struct drm_gpu_scheduler *sched[6]; + + struct drm_gpu_scheduler **sched_3d; + struct drm_gpu_scheduler **sched_dec; + struct drm_gpu_scheduler **sched_enc; + struct drm_gpu_scheduler **sched_2d; + struct drm_gpu_scheduler **sched_dma; + int nr_3d; + int nr_2d; + int nr_dec; + int nr_enc; + int nr_dma; + + struct drm_sched_entity *dma_entity; + + spinlock_t irq_lock; + struct irq_domain *irq_domain; + u32 irq_enable_reg[2]; + int va_irq; + + /* key/value database */ + struct mwv207_db *db; + + struct mwv207_vbios vbios; + + struct mutex gpio_lock; + + bool lite; +}; + +struct mwv207_ctx_mgr { + struct mutex lock; + struct idr handle_table; +}; + +struct mwv207_fpriv { + struct mwv207_ctx_mgr ctx_mgr; +}; + +int mwv207_test(struct mwv207_device *jdev); +void jdev_write_vram(struct mwv207_device *jdev, u64 vram_addr, void *buf, int size); + +static inline void jdev_write(struct mwv207_device *jdev, u32 reg, u32 value) +{ + BUG_ON(reg <= 0x100000); + writel_relaxed(value, jdev->mmio + reg); +} + +static inline u32 jdev_read(struct mwv207_device *jdev, u32 reg) +{ + return readl(jdev->mmio + reg); +} + +static inline void jdev_modify(struct mwv207_device *jdev, u32 reg, u32 mask, u32 value) +{ + u32 rvalue = jdev_read(jdev, reg); + + rvalue = (rvalue & ~mask) | (value & mask); + jdev_write(jdev, reg, rvalue); + BUG_ON(reg <= 0x100000); +} + +#define to_fpriv(drm_file) ((drm_file)->driver_priv) + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_bo.c b/drivers/gpu/drm/mwv207/mwv207_bo.c new file mode 100644 index 0000000000000..0a1423d0b357f --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_bo.c @@ -0,0 +1,279 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include "mwv207_drm.h" +#include "mwv207_gem.h" +#include "mwv207_bo.h" + +static void mwv207_ttm_bo_destroy(struct ttm_buffer_object *bo) +{ + struct mwv207_bo *jbo = to_jbo(bo); + + WARN_ON_ONCE(jbo->map_count > 0); + if (bo->base.import_attach) + drm_prime_gem_destroy(&bo->base, NULL); + drm_gem_object_release(&jbo->tbo.base); + kfree(jbo); +} + +bool mwv207_ttm_bo_is_mwv207_bo(struct ttm_buffer_object *jbo) +{ + if (jbo->destroy == &mwv207_ttm_bo_destroy) + return true; + return false; +} + +void mwv207_bo_placement_from_domain(struct mwv207_bo *jbo, u32 domain, bool pinned) +{ + struct mwv207_device *jdev = bdev_to_jdev(jbo->tbo.bdev); + u32 c = 0; + + jbo->placement.placement = jbo->placements; + jbo->placement.busy_placement = jbo->placements; + + if (domain & 0x2) { + jbo->placements[c].fpfn = 0; + + if (jbo->flags & (1<<2)) + jbo->placements[c].lpfn = 0x100000000ul >> PAGE_SHIFT; + else + jbo->placements[c].lpfn = 0; + + if (jbo->flags & (1<<0)) + jbo->placements[c].lpfn = jdev->visible_vram_size >> PAGE_SHIFT; + jbo->placements[c].flags = 0; + jbo->placements[c].mem_type = TTM_PL_VRAM; + +#ifdef MWV207_DEBUG_BO_MIGRATION + if (!pinned) { + if (jbo->placements[c].lpfn == 0 + || jbo->placements[c].lpfn >= jdev->vram_size >> PAGE_SHIFT) + jbo->placements[c].lpfn = jdev->vram_size >> PAGE_SHIFT; + if (jbo->toggle & 0x1) + jbo->placements[c].fpfn = jbo->placements[c].lpfn / 2; + else + jbo->placements[c].lpfn = jbo->placements[c].lpfn / 2; + jbo->toggle ^= 0x1; + } +#endif + c++; + } + if (domain & 0x4) { + jbo->placements[c].fpfn = 0; + jbo->placements[c].lpfn = 0; + jbo->placements[c].flags = 0; + jbo->placements[c].mem_type = TTM_PL_TT; + c++; + } + if (domain & 0x1) { + jbo->placements[c].fpfn = 0; + jbo->placements[c].lpfn = 0; + jbo->placements[c].flags = 0; + jbo->placements[c].mem_type = TTM_PL_SYSTEM; + c++; + } + if (!c) { + jbo->placements[c].fpfn = 0; + jbo->placements[c].lpfn = 0; + jbo->placements[c].flags = 0; + jbo->placements[c].mem_type = TTM_PL_SYSTEM; + c++; + } + + BUG_ON(c > 3); + jbo->placement.num_placement = c; + jbo->placement.num_busy_placement = c; +} + +int mwv207_bo_create(struct mwv207_device *jdev, + u64 size, u64 align, enum ttm_bo_type type, + u32 domain, u32 flags, + struct mwv207_bo **pbo) +{ + struct mwv207_bo *jbo; + int ret; + + *pbo = NULL; + if ((flags & (1<<2)) && !(domain & 0x2)) + return -EINVAL; + + jbo = kzalloc(sizeof(struct mwv207_bo), GFP_KERNEL); + if (jbo == NULL) + return -ENOMEM; + size = roundup(size, PAGE_SIZE); + ret = drm_gem_object_init(&jdev->base, &jbo->tbo.base, size); + if (unlikely(ret)) { + kfree(jbo); + return ret; + } + jbo->flags = flags; + jbo->domain = domain; + + jbo->tbo.base.funcs = &mwv207_gem_object_funcs; + + jbo->tbo.bdev = &jdev->bdev; + mwv207_bo_placement_from_domain(jbo, domain, false); + + ret = ttm_bo_init_validate(&jdev->bdev, &jbo->tbo, type, + &jbo->placement, align >> PAGE_SHIFT, + type != ttm_bo_type_kernel, + NULL, NULL, &mwv207_ttm_bo_destroy); + + if (unlikely(ret)) + return ret; + *pbo = jbo; + return 0; +} + +int mwv207_bo_reserve(struct mwv207_bo *jbo, bool no_intr) +{ + int ret; + + ret = ttm_bo_reserve(&jbo->tbo, !no_intr, false, NULL); + if (unlikely(ret)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("bo reserve failed\n"); + return ret; + } + return 0; +} + +void mwv207_bo_unreserve(struct mwv207_bo *jbo) +{ + ttm_bo_unreserve(&jbo->tbo); +} + +int mwv207_bo_kmap_reserved(struct mwv207_bo *jbo, void **ptr) +{ + bool is_iomem; + int ret; + + if (!jbo->flags & (1<<0)) + return -EPERM; + + if (jbo->kptr) { + if (ptr) + *ptr = jbo->kptr; + jbo->map_count++; + return 0; + } + + ret = ttm_bo_kmap(&jbo->tbo, 0, PFN_UP(jbo->tbo.base.size), &jbo->kmap); + + if (ret) + return ret; + jbo->kptr = ttm_kmap_obj_virtual(&jbo->kmap, &is_iomem); + if (ptr) + *ptr = jbo->kptr; + jbo->map_count = 1; + return 0; +} + +void mwv207_bo_kunmap_reserved(struct mwv207_bo *jbo) +{ + if (WARN_ON_ONCE(jbo->kptr == NULL)) + return; + jbo->map_count--; + if (jbo->map_count > 0) + return; + jbo->kptr = NULL; + ttm_bo_kunmap(&jbo->kmap); +} + +void mwv207_bo_unref(struct mwv207_bo *jbo) +{ + if (jbo == NULL) + return; + + ttm_bo_put(&jbo->tbo); + jbo = NULL; +} + +struct mwv207_bo *mwv207_bo_ref(struct mwv207_bo *jbo) +{ + if (jbo == NULL) + return NULL; + + ttm_bo_get(&jbo->tbo); + return jbo; +} + +int mwv207_bo_pin_reserved(struct mwv207_bo *jbo, u32 domain) +{ + struct ttm_operation_ctx ctx = {false, false}; + int ret; + + if (jbo->tbo.pin_count) { + ttm_bo_pin(&jbo->tbo); + return 0; + } + + jbo->flags |= (1<<2); + mwv207_bo_placement_from_domain(jbo, domain, true); + ret = ttm_bo_validate(&jbo->tbo, &jbo->placement, &ctx); + if (ret) + return ret; + + ttm_bo_pin(&jbo->tbo); + + return 0; +} + +int mwv207_bo_unpin_reserved(struct mwv207_bo *jbo) +{ + struct ttm_operation_ctx ctx = {false, false}; + int ret; + + if (WARN_ON_ONCE(!jbo->tbo.pin_count)) + return 0; + + ttm_bo_unpin(&jbo->tbo); + if (jbo->tbo.pin_count) + return 0; + + ret = ttm_bo_validate(&jbo->tbo, &jbo->placement, &ctx); + if (unlikely(ret)) + DRM_ERROR("validate failed for unpin"); + + return ret; +} + +int mwv207_bo_pin(struct mwv207_bo *jbo, u32 domain) +{ + int ret; + + ret = mwv207_bo_reserve(jbo, true); + if (ret) + return ret; + ret = mwv207_bo_pin_reserved(jbo, domain); + mwv207_bo_unreserve(jbo); + return ret; +} + +int mwv207_bo_unpin(struct mwv207_bo *jbo) +{ + int ret; + + ret = mwv207_bo_reserve(jbo, true); + if (ret) + return ret; + ret = mwv207_bo_unpin_reserved(jbo); + mwv207_bo_unreserve(jbo); + return ret; +} + diff --git a/drivers/gpu/drm/mwv207/mwv207_bo.h b/drivers/gpu/drm/mwv207/mwv207_bo.h new file mode 100644 index 0000000000000..5f18355360efe --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_bo.h @@ -0,0 +1,111 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_BO_H_T4ETFCB2 +#define MWV207_BO_H_T4ETFCB2 + +#include +#include +#include +#include +#include +#include +#include "mwv207.h" +#include "mwv207_drm.h" + +#undef MWV207_DEBUG_BO_MIGRATION + +#define to_jbo(bo) container_of(bo, struct mwv207_bo, tbo) +struct mwv207_bo { + struct ttm_buffer_object tbo; + struct ttm_placement placement; + struct ttm_bo_kmap_obj kmap; + struct ttm_place placements[3]; + u32 domain; + u32 flags; + struct iosys_map map; + int map_count; + void *kptr; +#ifdef MWV207_DEBUG_BO_MIGRATION + int toggle; +#endif +}; + +struct mwv207_ttm_tt { + struct ttm_tt ttm; + struct mwv207_device *jdev; +}; + +#define to_gtt(ttm_tt) container_of(ttm_tt, struct mwv207_ttm_tt, ttm) + +static inline struct mwv207_bo *mwv207_bo_from_gem(struct drm_gem_object *gobj) +{ + struct ttm_buffer_object *tbo = container_of(gobj, struct ttm_buffer_object, base); + + return container_of(tbo, struct mwv207_bo, tbo); +} + +static inline struct drm_gem_object *mwv207_gem_from_bo(struct mwv207_bo *jbo) +{ + return &jbo->tbo.base; +} + +static inline u64 mwv207_bo_size(struct mwv207_bo *jbo) +{ + return jbo->tbo.resource->size; +} + +static inline u64 mwv207_bo_mmap_offset(struct drm_gem_object *gobj) +{ + struct mwv207_bo *jbo = mwv207_bo_from_gem(gobj); + + return drm_vma_node_offset_addr(&jbo->tbo.base.vma_node); +} + +static inline u64 mwv207_bo_gpu_phys(struct mwv207_bo *jbo) +{ + return jbo->tbo.resource->start << PAGE_SHIFT; +} + +void mwv207_bo_placement_from_domain(struct mwv207_bo *jbo, u32 domain, bool pinned); + +bool mwv207_ttm_bo_is_mwv207_bo(struct ttm_buffer_object *tbo); +void mwv207_bo_placement(struct mwv207_bo *jbo, int domain); + +int mwv207_bo_create(struct mwv207_device *jdev, + u64 size, u64 align, enum ttm_bo_type type, + u32 domain, u32 flags, + struct mwv207_bo **pbo); + +int mwv207_bo_reserve(struct mwv207_bo *jbo, bool no_intr); +void mwv207_bo_unreserve(struct mwv207_bo *jbo); + +int mwv207_bo_pin_reserved(struct mwv207_bo *jbo, u32 domain); +int mwv207_bo_unpin_reserved(struct mwv207_bo *jbo); + +int mwv207_bo_pin(struct mwv207_bo *jbo, u32 domain); +int mwv207_bo_unpin(struct mwv207_bo *jbo); + +int mwv207_bo_kmap_reserved(struct mwv207_bo *jbo, void **ptr); +void mwv207_bo_kunmap_reserved(struct mwv207_bo *jbo); + +struct mwv207_bo *mwv207_bo_ref(struct mwv207_bo *jbo); +void mwv207_bo_unref(struct mwv207_bo *jbo); + +int mwv207_ttm_init(struct mwv207_device *jdev); +void mwv207_ttm_fini(struct mwv207_device *jdev); + + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_ctx.c b/drivers/gpu/drm/mwv207/mwv207_ctx.c new file mode 100644 index 0000000000000..c225b0cb52236 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_ctx.c @@ -0,0 +1,245 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include "mwv207.h" +#include "mwv207_drm.h" +#include "mwv207_ctx.h" + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +struct mwv207_ctx *mwv207_ctx_lookup(struct drm_device *dev, + struct drm_file *filp, u32 handle) +{ + struct mwv207_fpriv *fpriv = to_fpriv(filp); + struct mwv207_ctx_mgr *mgr = &fpriv->ctx_mgr; + struct mwv207_ctx *ctx; + + mutex_lock(&mgr->lock); + ctx = idr_find(&mgr->handle_table, handle); + if (ctx) + kref_get(&ctx->refcnt); + mutex_unlock(&mgr->lock); + + return ctx; +} + +static void mwv207_ctx_entity_fini(struct mwv207_ctx *ctx) +{ + int i; + + for (i = 0; i < 11; i++) { + if (ctx->entities[i]) { + drm_sched_entity_destroy(ctx->entities[i]); + kfree(ctx->entities[i]); + ctx->entities[i] = NULL; + } + } +} + +static int mwv207_ctx_entity_init_single(struct mwv207_ctx *ctx, + struct drm_gpu_scheduler **sched, + struct drm_sched_entity **entities, int nr) +{ + struct drm_gpu_scheduler *scheds[6]; + struct drm_sched_entity *entity; + int i, ret; + + for (i = 0; i < nr; i++) { + scheds[i] = sched[i]; + + entity = kmalloc(sizeof(struct drm_sched_entity), GFP_KERNEL); + if (!entity) + return -ENOMEM; + + ret = drm_sched_entity_init(entity, DRM_SCHED_PRIORITY_NORMAL, &scheds[i], 1, &ctx->guilty); + + if (ret) { + kfree(entity); + return ret; + } + entities[i] = entity; + } + if (i) { + + entity = kmalloc(sizeof(struct drm_sched_entity), GFP_KERNEL); + if (!entity) + return -ENOMEM; + + ret = drm_sched_entity_init(entity, DRM_SCHED_PRIORITY_NORMAL, &scheds[0], i, &ctx->guilty); + + if (ret) { + kfree(entity); + return ret; + } + entities[i] = entity; + } + + return 0; +} + +static int mwv207_ctx_entity_init(struct drm_device *dev, struct mwv207_ctx *ctx) +{ + struct mwv207_device *jdev = ddev_to_jdev(dev); + int ret; + + ctx->entity_3d = &ctx->entities[0]; + ctx->entity_dec = &ctx->entity_3d[2 + 1]; + ctx->entity_enc = &ctx->entity_dec[1 + 1]; + ctx->entity_2d = &ctx->entity_enc[1 + 1]; + ctx->entity_dma = &ctx->entity_2d[1 + 1]; + BUG_ON(ctx->entity_dma[1] != ctx->entities[11 - 1]); + + ret = mwv207_ctx_entity_init_single(ctx, jdev->sched_3d, ctx->entity_3d, jdev->nr_3d); + if (ret) + goto fini; + ret = mwv207_ctx_entity_init_single(ctx, jdev->sched_dec, ctx->entity_dec, jdev->nr_dec); + if (ret) + goto fini; + ret = mwv207_ctx_entity_init_single(ctx, jdev->sched_enc, ctx->entity_enc, jdev->nr_enc); + if (ret) + goto fini; + ret = mwv207_ctx_entity_init_single(ctx, jdev->sched_2d, ctx->entity_2d, jdev->nr_2d); + if (ret) + goto fini; + ret = mwv207_ctx_entity_init_single(ctx, jdev->sched_dma, ctx->entity_dma, jdev->nr_dma); + if (ret) + goto fini; + return 0; +fini: + mwv207_ctx_entity_fini(ctx); + return ret; +} + +static int mwv207_ctx_create(struct drm_device *dev, struct mwv207_ctx_mgr *mgr, + u32 flags, u32 *handle) +{ + struct mwv207_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(struct mwv207_ctx), GFP_KERNEL); + if (ctx == NULL) + return -ENOMEM; + + ctx->jdev = ddev_to_jdev(dev); + kref_init(&ctx->refcnt); + + ret = mwv207_ctx_entity_init(dev, ctx); + if (ret) + goto out_free; + + mutex_lock(&mgr->lock); + ret = idr_alloc(&mgr->handle_table, ctx, 1, 0, GFP_KERNEL); + mutex_unlock(&mgr->lock); + + if (ret < 0) + goto out_fini_entites; + + *handle = ret; + return 0; +out_fini_entites: + mwv207_ctx_entity_fini(ctx); +out_free: + kfree(ctx); + return ret; +} + +static void mwv207_ctx_release(struct kref *kref) +{ + struct mwv207_ctx *ctx = container_of(kref, struct mwv207_ctx, refcnt); + + mwv207_ctx_entity_fini(ctx); + kfree(ctx); +} + +int mwv207_ctx_put(struct mwv207_ctx *ctx) +{ + return kref_put(&ctx->refcnt, mwv207_ctx_release); +} + +static int mwv207_ctx_destroy(struct drm_device *dev, struct mwv207_ctx_mgr *mgr, u32 handle) +{ + struct mwv207_ctx *ctx; + + mutex_lock(&mgr->lock); + ctx = idr_remove(&mgr->handle_table, handle); + mutex_unlock(&mgr->lock); + + if (ctx == NULL) + return -EINVAL; + + mwv207_ctx_put(ctx); + return 0; +} + +int mwv207_ctx_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_mwv207_ctx *args = (struct drm_mwv207_ctx *)data; + struct mwv207_fpriv *fpriv = to_fpriv(filp); + + if (args->resv || args->flags) + return -EINVAL; + + switch (args->op) { + case 0: + return mwv207_ctx_create(dev, &fpriv->ctx_mgr, args->flags, &args->handle); + case 1: + return mwv207_ctx_destroy(dev, &fpriv->ctx_mgr, args->handle); + default: + return -EINVAL; + } +} + +int mwv207_ctx_mgr_init(struct drm_device *dev, struct mwv207_ctx_mgr *mgr) +{ + mutex_init(&mgr->lock); + idr_init(&mgr->handle_table); + return 0; +} + +void mwv207_ctx_mgr_fini(struct drm_device *dev, struct mwv207_ctx_mgr *mgr) +{ + struct mwv207_ctx *ctx; + uint32_t id; + + idr_for_each_entry(&mgr->handle_table, ctx, id) + mwv207_ctx_put(ctx); + + idr_destroy(&mgr->handle_table); + mutex_destroy(&mgr->lock); +} + +int mwv207_kctx_init(struct mwv207_device *jdev) +{ + struct drm_gpu_scheduler *scheds[1]; + int i; + + for (i = 0; i < 1; i++) { + scheds[i] = jdev->sched_dma[i]; + } + + jdev->dma_entity = devm_kzalloc(jdev->dev, sizeof(struct drm_sched_entity), GFP_KERNEL); + if (!jdev->dma_entity) + return -ENOMEM; + + return drm_sched_entity_init(jdev->dma_entity, DRM_SCHED_PRIORITY_NORMAL, &scheds[0], i, NULL); +} + +void mwv207_kctx_fini(struct mwv207_device *jdev) +{ + drm_sched_entity_destroy(jdev->dma_entity); +} diff --git a/drivers/gpu/drm/mwv207/mwv207_ctx.h b/drivers/gpu/drm/mwv207/mwv207_ctx.h new file mode 100644 index 0000000000000..7b204b6c779e7 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_ctx.h @@ -0,0 +1,50 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_CTX_H_QFMRESBO +#define MWV207_CTX_H_QFMRESBO +#include +#include +#include "mwv207.h" + +struct mwv207_ctx { + struct kref refcnt; + + struct drm_sched_entity *entities[11]; + + struct drm_sched_entity **entity_3d; + struct drm_sched_entity **entity_dec; + struct drm_sched_entity **entity_enc; + struct drm_sched_entity **entity_2d; + struct drm_sched_entity **entity_dma; + + struct mwv207_device *jdev; + atomic_t guilty; +}; + +struct mwv207_ctx *mwv207_ctx_lookup(struct drm_device *dev, + struct drm_file *filp, u32 handle); +int mwv207_ctx_put(struct mwv207_ctx *ctx); + +int mwv207_ctx_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +int mwv207_ctx_mgr_init(struct drm_device *dev, struct mwv207_ctx_mgr *mgr); +void mwv207_ctx_mgr_fini(struct drm_device *dev, struct mwv207_ctx_mgr *mgr); + +int mwv207_kctx_init(struct mwv207_device *jdev); +void mwv207_kctx_fini(struct mwv207_device *jdev); + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_db.c b/drivers/gpu/drm/mwv207/mwv207_db.c new file mode 100644 index 0000000000000..d7a2e5657ed9a --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_db.c @@ -0,0 +1,136 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include "mwv207_db.h" +#include "mwv207.h" +#include "mwv207_drm.h" + +struct mwv207_db { + u32 nr; + u32 max_nr; + u32 sorted; + u32 pad; + struct drm_mwv207_info_rec *db; +}; + +int mwv207_db_init(struct mwv207_device *jdev) +{ + struct mwv207_db *db; + u32 size; + + size = sizeof(struct mwv207_db); + size += 2048 * sizeof(struct drm_mwv207_info_rec); + db = devm_kmalloc(jdev->dev, size, GFP_KERNEL); + if (!db) + return -ENOMEM; + + db->db = (struct drm_mwv207_info_rec *)(db + 1); + db->max_nr = 2048; + db->nr = 0; + db->sorted = 0; + jdev->db = db; + return 0; +} + +void mwv207_db_fini(struct mwv207_device *jdev) +{ + devm_kfree(jdev->dev, jdev->db); +} + +int mwv207_db_add(struct mwv207_device *jdev, u32 key, u32 val) +{ + struct mwv207_db *db = jdev->db; + + if (db->nr >= db->max_nr) { + WARN_ONCE(1, "mwv207: maximum key count reached"); + return -ENOMEM; + } + + db->db[db->nr].key = key; + db->db[db->nr].val = val; + db->nr++; + + if (db->sorted) + mwv207_db_sort(jdev); + + return 0; +} + +static int cmp_key(const void *a, const void *b) +{ + struct drm_mwv207_info_rec const *rec_a = a, *rec_b = b; + + if (rec_a->key < rec_b->key) + return -1; + else if (rec_a->key > rec_b->key) + return 1; + else + return 0; +} + +void mwv207_db_sort(struct mwv207_device *jdev) +{ + struct mwv207_db *db = jdev->db; + int i; + + db->sorted = 1; + if (db->nr < 2) + return; + + sort(db->db, db->nr, 8, cmp_key, NULL); + + for (i = 0; i < db->nr - 1; ++i) { + if (db->db[i].key == db->db[i + 1].key) { + WARN_ONCE(1, "mwv207 key: %d is duplicated", db->db[i].key); + break; + } + } +} + +int mwv207_db_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct mwv207_device *jdev = dev->dev_private; + struct drm_mwv207_info_rec rec, *result; + struct drm_mwv207_info *args = data; + struct mwv207_db *db = jdev->db; + + switch (args->op) { + case 1: + rec.key = args->nr_recs; + result = bsearch(&rec, db->db, db->nr, 8, cmp_key); + if (!result) { + args->recs = 0; + return -ENOENT; + } + args->recs = result->val; + + break; + case 0: + if (args->recs) + return copy_to_user((void __user *)args->recs, db->db, + db->nr * sizeof(struct drm_mwv207_info_rec)); + args->nr_recs = db->nr; + + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/gpu/drm/mwv207/mwv207_db.h b/drivers/gpu/drm/mwv207/mwv207_db.h new file mode 100644 index 0000000000000..fd5c685d4ca33 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_db.h @@ -0,0 +1,32 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include "mwv207_drm.h" +#ifndef MWV207_DB_H_AGPY6ZIQ +#define MWV207_DB_H_AGPY6ZIQ + +struct mwv207_device; + +int mwv207_db_init(struct mwv207_device *jdev); +void mwv207_db_fini(struct mwv207_device *jdev); +void mwv207_db_sort(struct mwv207_device *jdev); + +int mwv207_db_add(struct mwv207_device *jdev, u32 key, u32 val); + +int mwv207_db_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_devfreq.c b/drivers/gpu/drm/mwv207/mwv207_devfreq.c new file mode 100644 index 0000000000000..de95a36ad5182 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_devfreq.c @@ -0,0 +1,278 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and * confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include "mwv207_devfreq.h" +#include "mwv207_vbios.h" +#include "mwv207_sched.h" + +#define MIN_FREQ 50 +#define MAX_FREQ 600 + +static void mwv207_devfreq_update_utilization(struct mwv207_pipe *pipe) +{ + ktime_t now; + ktime_t last; + + now = ktime_get(); + last = pipe->time_last_update; + + dev_dbg(pipe->dev, "pipe [%s] busy_count: %d", pipe->fname, pipe->busy_count); + + if (pipe->busy_count > 0) + pipe->busy_time += ktime_sub(now, last); + else + pipe->idle_time += ktime_sub(now, last); + + pipe->time_last_update = now; +} + +static int mwv207_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct mwv207_pipe *pipe = dev_get_drvdata(dev); + unsigned long freq_khz; + int ret; + + ret = mwv207_vbios_get_pll(pipe->jdev, pipe->pll_id, &freq_khz); + if (ret) + *freq = MIN_FREQ; + else + *freq = freq_khz / 1000; + + return 0; +} + +static int mwv207_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct mwv207_pipe *pipe = dev_get_drvdata(dev); + struct dev_pm_opp *opp; + unsigned long target_freq; + int ret; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_freq = dev_pm_opp_get_freq(opp); + dev_pm_opp_put(opp); + + dev_dbg(dev, "pipe [%s] target freq: %lu", pipe->fname, target_freq); + + ret = mwv207_vbios_set_pll(pipe->jdev, pipe->pll_id, target_freq * 1000); + if (ret) + return ret; + + return 0; +} + +static void mwv207_devfreq_pipe_reset(struct mwv207_pipe *pipe) +{ + pipe->busy_time = 0; + pipe->idle_time = 0; + pipe->time_last_update = ktime_get(); +} + +static int mwv207_devfreq_get_dev_status(struct device *dev, struct devfreq_dev_status *status) +{ + struct mwv207_pipe *pipe = dev_get_drvdata(dev); + unsigned long flags; + + mwv207_devfreq_get_cur_freq(dev, &status->current_frequency); + + spin_lock_irqsave(&pipe->devfreq_lock, flags); + + mwv207_devfreq_update_utilization(pipe); + + status->total_time = ktime_to_ns(ktime_add(pipe->busy_time, + pipe->idle_time)); + + status->busy_time = ktime_to_ns(pipe->busy_time); + + mwv207_devfreq_pipe_reset(pipe); + + spin_unlock_irqrestore(&pipe->devfreq_lock, flags); + + return 0; +} + +static struct devfreq_dev_profile mwv207_devfreq_profile = { + .polling_ms = 50, + .target = mwv207_devfreq_target, + .get_dev_status = mwv207_devfreq_get_dev_status, + .get_cur_freq = mwv207_devfreq_get_cur_freq, +}; + +static ssize_t utilization_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mwv207_pipe *pipe = dev_get_drvdata(dev); + unsigned long busy_time, total_time, flags; + + spin_lock_irqsave(&pipe->devfreq_lock, flags); + + mwv207_devfreq_update_utilization(pipe); + + total_time = ktime_to_ns(ktime_add(pipe->busy_time, pipe->idle_time)); + + busy_time = ktime_to_ns(pipe->busy_time); + + mwv207_devfreq_pipe_reset(pipe); + + spin_unlock_irqrestore(&pipe->devfreq_lock, flags); + + if (total_time == 0) + return sprintf(buf, "%d\n", 100); + else + return sprintf(buf, "%llu\n", div_u64(busy_time, total_time / 100)); +} + +static DEVICE_ATTR_RO(utilization); + +void mwv207_devfreq_unregister(struct mwv207_pipe *pipe) +{ + if (!pipe->devfreq) + return; + + device_remove_file(pipe->dev, &dev_attr_utilization); + sysfs_remove_link(&pipe->dev->kobj, "stats"); + dev_pm_opp_remove_all_dynamic(pipe->dev); + device_unregister(pipe->dev); +} + +static void mwv207_devfreq_dev_release(struct device *dev) +{ + kfree(dev); +} + +int mwv207_devfreq_register(struct mwv207_pipe *pipe, unsigned long max_freq) +{ + struct device *dev; + unsigned long freq; + int ret; + + dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->parent = pipe->jdev->dev; + dev->release = mwv207_devfreq_dev_release; + dev_set_name(dev, "devfreq_%s", pipe->fname); + dev_set_drvdata(dev, pipe); + + ret = device_register(dev); + if (ret) { + put_device(dev); + goto err_out; + } + + pipe->dev = dev; + pipe->busy_count = 0; + spin_lock_init(&pipe->devfreq_lock); + + mwv207_devfreq_profile.initial_freq = max_freq; + + for (freq = MIN_FREQ; freq <= max_freq; freq += 50) { + ret = dev_pm_opp_add(dev, freq, 0); + if (ret) + goto remove_opps; + } + + pipe->devfreq = devm_devfreq_add_device(dev, &mwv207_devfreq_profile, + DEVFREQ_GOV_PERFORMANCE, NULL); + if (IS_ERR(pipe->devfreq)) { + ret = PTR_ERR(pipe->devfreq); + dev_dbg(dev, "Couldn't initialize devfreq: %d", ret); + goto remove_opps; + } + + mwv207_devfreq_pipe_reset(pipe); + + pipe->devfreq->suspend_freq = MIN_FREQ; + + ret = sysfs_create_link(&dev->kobj, &pipe->devfreq->dev.kobj, "stats"); + if (ret) { + dev_dbg(dev, "Couldn't create sys link for stats: %d", ret); + goto remove_opps; + } + + ret = device_create_file(dev, &dev_attr_utilization); + if (ret) { + dev_dbg(dev, "Couldn't create device file for utilization: %d", ret); + goto remove_link; + } + + return 0; + +remove_link: + sysfs_remove_link(&dev->kobj, "stats"); +remove_opps: + dev_pm_opp_remove_all_dynamic(dev); + device_unregister(dev); +err_out: + return ret; +} + +int mwv207_devfreq_resume(struct mwv207_pipe *pipe) +{ + unsigned long flags; + + if (!pipe->devfreq) + return 0; + + spin_lock_irqsave(&pipe->devfreq_lock, flags); + + mwv207_devfreq_pipe_reset(pipe); + + spin_unlock_irqrestore(&pipe->devfreq_lock, flags); + + return devfreq_resume_device(pipe->devfreq); +} + +int mwv207_devfreq_suspend(struct mwv207_pipe *pipe) +{ + if (!pipe->devfreq) + return 0; + + return devfreq_suspend_device(pipe->devfreq); +} + +void mwv207_devfreq_record_busy(struct mwv207_pipe *pipe) +{ + unsigned long flags; + + if (!pipe->devfreq) + return; + + spin_lock_irqsave(&pipe->devfreq_lock, flags); + + mwv207_devfreq_update_utilization(pipe); + pipe->busy_count++; + + spin_unlock_irqrestore(&pipe->devfreq_lock, flags); +} + +void mwv207_devfreq_record_idle(struct mwv207_pipe *pipe) +{ + unsigned long flags; + + if (!pipe->devfreq) + return; + + spin_lock_irqsave(&pipe->devfreq_lock, flags); + + mwv207_devfreq_update_utilization(pipe); + WARN_ON(--pipe->busy_count < 0); + + spin_unlock_irqrestore(&pipe->devfreq_lock, flags); +} diff --git a/drivers/gpu/drm/mwv207/mwv207_devfreq.h b/drivers/gpu/drm/mwv207/mwv207_devfreq.h new file mode 100644 index 0000000000000..5cabf1c3a39c2 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_devfreq.h @@ -0,0 +1,31 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ + +#ifndef __MWV207_DEVFREQ_H__ +#define __MWV207_DEVFREQ_H__ + +struct mwv207_pipe; + +void mwv207_devfreq_unregister(struct mwv207_pipe *pipe); +int mwv207_devfreq_register(struct mwv207_pipe *pipe, unsigned long max_freq); + +void mwv207_devfreq_record_busy(struct mwv207_pipe *pipe); +void mwv207_devfreq_record_idle(struct mwv207_pipe *pipe); + +int mwv207_devfreq_suspend(struct mwv207_pipe *pipe); +int mwv207_devfreq_resume(struct mwv207_pipe *pipe); + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_dma.h b/drivers/gpu/drm/mwv207/mwv207_dma.h new file mode 100644 index 0000000000000..ff70d9a0e89ad --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_dma.h @@ -0,0 +1,29 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_EDMA_H_EPAWCJ4N +#define MWV207_EDMA_H_EPAWCJ4N +#include "mwv207.h" + +struct mwv207_dma; +struct mwv207_dma *mwv207_dma_create(struct device *dev, + void __iomem *regbase, u8 alignment); +void mwv207_dma_destroy(struct mwv207_dma *dma); + +int mwv207_dma_to_vram(struct mwv207_dma *dma, u64 vram_addr, void *va, u64 size); +int mwv207_dma_to_ram(struct mwv207_dma *dma, void *va, u64 vram_addr, u64 size); +int mwv207_dma_vram_vram(struct mwv207_dma *dma, u64 dst_vram_addr, u64 src_vram_addr, u64 size); + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_drm.h b/drivers/gpu/drm/mwv207/mwv207_drm.h new file mode 100644 index 0000000000000..13e32b53d46e5 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_drm.h @@ -0,0 +1,158 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef __MWV207_DRM_H__ +#define __MWV207_DRM_H__ + +#include "drm/drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/**********************GEM interface*************************/ + +struct drm_mwv207_gem_create_in { + __u64 size; + __u64 alignment; + __u32 preferred_domain; + __u32 flags; +}; +struct drm_mwv207_gem_create_out { + __u32 handle; + __u32 resv; +}; +union drm_mwv207_gem_create { + struct drm_mwv207_gem_create_in in; + struct drm_mwv207_gem_create_out out; +}; + +struct drm_mwv207_gem_mmap_in { + __u32 handle; + __u32 pad; +}; +struct drm_mwv207_gem_mmap_out { + __u64 offset; +}; +union drm_mwv207_gem_mmap { + struct drm_mwv207_gem_mmap_in in; + struct drm_mwv207_gem_mmap_out out; +}; + +struct drm_mwv207_gem_wait { + __u32 handle; + __u32 op; + __s64 timeout; +}; + +/**********************context interface*************************/ +struct drm_mwv207_ctx { + __u32 op; + __u32 flags; + __u32 handle; + __u32 resv; +}; + +/**********************submission interface************************* + * 1. a submission is sent to a single ctx + * 2. a submission optionally contains a relocation table and a bo table + * 3. bo table should not contain duplicates; + * 4. cmd buffer size should be limited to 16K + */ + +struct drm_mwv207_submit { + __u32 ctx; + __u32 engine_type; + __u32 engine_id; + __u32 flags; + __u32 fence_fd; /* in/out fence fd, according to MWV207_SUBMIT_FLAG_FENCE_IN/OUT */ + __u32 cmd_size; + __u32 nr_relocs; + __u32 nr_bos; + __u32 nr_cmd_dat; + __u32 padding; + __u64 cmds; + __u64 relocs; + __u64 bos; + __u64 cmd_dats; + /* syncobj support, reserved for vulkan + * __u32 nr_in_syncobjs; + * __u32 nr_out_syncobjs; + * __u64 in_syncobjs; + * __u64 out_syncobjs; + */ +}; + +/* in_syncobjs/out_syncobjs array element, reserved for vulkan */ +struct drm_mwv207_submit_syncobj { + __u32 handle; + __u32 flags; + __u64 point; +}; + +struct drm_mwv207_bo_acc { + __u32 handle; + __u32 flags; +}; + +struct drm_mwv207_submit_reloc { + __u32 idx; /* bo/cmd_dat idx in submit bos/cmd_dats */ + __u32 cmd_offset; + __u64 bo_offset; + __u32 type; + __u32 pad; +}; + +struct drm_mwv207_submit_cmd_dat { + __u32 nr_relocs; + __u32 dat_size; + __u64 relocs; + __u64 dat; +}; + +/**************** card capability interface *****************/ +/* XXX: keys are user/kernel ABI, DO THINK TWICE when + * add keys, once added it can never be removed. + */ +enum drm_mwv207_info_key { + DRM_MWV207_ACTIVE_3D_NR = 0, + DRM_MWV207_ACTIVE_DEC_NR, + DRM_MWV207_ACTIVE_ENC_NR, + DRM_MWV207_ACTIVE_2D_NR, + DRM_MWV207_ACTIVE_DMA_NR, +}; + +struct drm_mwv207_info_rec { + __u32 key; + __u32 val; +}; + +struct drm_mwv207_info { + __u32 op; + __u32 nr_recs; + __u64 recs; +}; + +#define DRM_IOCTL_MWV207_INFO DRM_IOWR(DRM_COMMAND_BASE + 0x00, struct drm_mwv207_info) +#define DRM_IOCTL_MWV207_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + 0x01, union drm_mwv207_gem_create) +#define DRM_IOCTL_MWV207_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + 0x02, union drm_mwv207_gem_mmap) +#define DRM_IOCTL_MWV207_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + 0x03, struct drm_mwv207_gem_wait) +#define DRM_IOCTL_MWV207_CTX DRM_IOWR(DRM_COMMAND_BASE + 0x04, struct drm_mwv207_ctx) +#define DRM_IOCTL_MWV207_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + 0x05, struct drm_mwv207_submit) +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_drv.c b/drivers/gpu/drm/mwv207/mwv207_drv.c new file mode 100644 index 0000000000000..c539fb7e3e032 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_drv.c @@ -0,0 +1,487 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mwv207_drm.h" +#include "mwv207_gem.h" +#include "mwv207_bo.h" +#include "dc/mwv207_kms.h" +#include "mwv207_ctx.h" +#include "mwv207_submit.h" +#include "mwv207_sched.h" +#include "mwv207_irq.h" +#include "mwv207_db.h" +#include "mwv207_vbios.h" + +#define DRIVER_NAME "mwv207" +#define DRIVER_DESC "mwv207 gpu driver" +#define DRIVER_DATE "20220407" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 1 + +#define WIN_SIZE 0x10000 + +#define IATU_BAR_LOW_OFFSET 0x108 +#define IATU_BAR_HIGH_OFFSET 0x10c +#define IATU_BAR_LIMIT_LOW_OFFSET 0x110 + +#define IATU_REGION_MODE_ADDR 0 + +static int selftest; +module_param(selftest, int, 0644); +MODULE_PARM_DESC(selftest, "run selftest when startup"); + + +static int mwv207_driver_open(struct drm_device *dev, struct drm_file *file_priv) +{ + struct mwv207_fpriv *fpriv; + + fpriv = kzalloc(sizeof(struct mwv207_fpriv), GFP_KERNEL); + if (fpriv == NULL) + return -ENOMEM; + + mwv207_ctx_mgr_init(dev, &fpriv->ctx_mgr); + + file_priv->driver_priv = fpriv; + + return 0; +} + +static void mwv207_driver_postclose(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct mwv207_fpriv *fpriv = to_fpriv(file_priv); + + mwv207_ctx_mgr_fini(dev, &fpriv->ctx_mgr); + kfree(fpriv); +} + +DEFINE_DRM_GEM_FOPS(mwv207_drm_driver_fops); + +static const struct drm_ioctl_desc mwv207_ioctls_drm[] = { + DRM_IOCTL_DEF_DRV(MWV207_INFO, mwv207_db_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MWV207_GEM_CREATE, mwv207_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MWV207_GEM_MMAP, mwv207_gem_mmap_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MWV207_GEM_WAIT, mwv207_gem_wait_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MWV207_CTX, mwv207_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MWV207_SUBMIT, mwv207_submit_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), +}; + +static struct drm_driver mwv207_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM | DRIVER_RENDER, + .fops = &mwv207_drm_driver_fops, + .open = mwv207_driver_open, + .postclose = mwv207_driver_postclose, + + .dumb_create = mwv207_gem_dumb_create, + .dumb_map_offset = drm_gem_ttm_dumb_map_offset, + + .gem_prime_import = drm_gem_prime_import, + + .ioctls = mwv207_ioctls_drm, + .num_ioctls = ARRAY_SIZE(mwv207_ioctls_drm), + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static inline void iatu_write(struct mwv207_device *jdev, int index, u32 offset, u32 value) +{ + u32 region_base = 0x10000 + index * 0x200; + writel(value, jdev->iatu + region_base + offset); +} + +static inline u32 iatu_read(struct mwv207_device *jdev, int index, u32 offset) +{ + u32 region_base = 0x10000 + index * 0x200; + return readl(jdev->iatu + region_base + offset); +} + +static void mwv207_win_slide(struct mwv207_device *jdev, int region, + u64 pci_win_base, u64 axi_win_base) +{ + iatu_write(jdev, region, IATU_BAR_LOW_OFFSET, pci_win_base & 0xffffffff); + iatu_write(jdev, region, IATU_BAR_HIGH_OFFSET, pci_win_base >> 32); + iatu_write(jdev, region, 0x114, axi_win_base & 0xffffffff); + iatu_write(jdev, region, 0x118, axi_win_base >> 32); + + iatu_write(jdev, region, IATU_BAR_LIMIT_LOW_OFFSET, + (pci_win_base + WIN_SIZE - 1) & 0xffffffff); + iatu_write(jdev, region, 0x100, 0x00); + iatu_write(jdev, region, 0x104, 1 << 31 | IATU_REGION_MODE_ADDR << 30 | 6 << 8); + + mb(); + if (iatu_read(jdev, region, 0x118) != (axi_win_base >> 32)) + pr_warn("mwv207: sliding window failed"); +} + +static int mwv207_iatu_map_bar(struct mwv207_device *jdev, int bar, u64 axi_addr) +{ + iatu_write(jdev, bar + 9, 0x114, axi_addr & 0xffffffff); + iatu_write(jdev, bar + 9, 0x118, axi_addr >> 32); + iatu_write(jdev, bar + 9, 0x100, 0x00); + iatu_write(jdev, bar + 9, 0x104, 1 << 31 | 1 << 30 | bar << 8); + + mb(); + if (iatu_read(jdev, bar + 9, 0x114) != (axi_addr & 0xffffffff)) + return -ENODEV; + + return 0; +} + +static int mwv207_iatu_init(struct mwv207_device *jdev) +{ + int ret; + + ret = mwv207_iatu_map_bar(jdev, 0, 0x10000000); + if (ret) + return ret; + + ret = mwv207_iatu_map_bar(jdev, 1, 0x00000000); + if (ret) + return ret; + + ret = mwv207_iatu_map_bar(jdev, 2, 0x10000000); + if (ret) + return ret; + + spin_lock_init(&jdev->win_lock); + + jdev->pci_win_base = pci_bus_address(jdev->pdev, 2) + + pci_resource_len(jdev->pdev, 2) - WIN_SIZE; + + mwv207_win_slide(jdev, 0, jdev->pci_win_base, 0x10000000); + + jdev->vram_last_win = 0; + + return 0; +} + +static void *mwv207_remap_region(struct mwv207_device *jdev, u64 vram_addr, u32 *len) +{ + u64 vram_win_base = (vram_addr & (~(WIN_SIZE - 1))); + u64 winlimit = vram_win_base + WIN_SIZE; + + if (vram_win_base != jdev->vram_last_win) { + mwv207_win_slide(jdev, 0, jdev->pci_win_base, vram_win_base + 0x10000000); + jdev->vram_last_win = vram_win_base; + } + + if (*len > winlimit - vram_addr) + *len = winlimit - vram_addr; + + return jdev->win + vram_addr - vram_win_base; +} + +void jdev_write_vram(struct mwv207_device *jdev, u64 vram_addr, void *buf, int size) +{ + int remain, len; + void *va; + + for (remain = size; remain > 0; remain -= len, buf += len, vram_addr += len) { + len = remain; + spin_lock(&jdev->win_lock); + va = mwv207_remap_region(jdev, vram_addr, &len); + if (!va || len <= 0) { + spin_unlock(&jdev->win_lock); + pr_warn("mwv207: write vram faild"); + return; + } + memcpy_toio(va, buf, len); + spin_unlock(&jdev->win_lock); + } +} + +static int mwv207_remove_conflicting_framebuffers(struct pci_dev *pdev) +{ + return drm_aperture_remove_conflicting_pci_framebuffers(pdev, &mwv207_drm_driver); +} + +static u64 mwv207_vram_size(struct mwv207_device *jdev) +{ + int nr = 0; + u64 size; + + switch (jdev_read(jdev, 0x9b0190) & 0x7) { + case 0: + size = 2048; + break; + case 1: + size = 1024; + break; + case 2: + size = 4096; + break; + case 3: + size = 8192; + break; + default: + pr_warn("mwv207: ddr size default to 256MB"); + return 256UL * 1024 * 1024; + } + + if (jdev->lite) { + nr += jdev_read(jdev, 0xd101bc) & 0x1; + nr += jdev_read(jdev, 0xd11cbc) & 0x1; + } else { + nr += (jdev_read(jdev, 0x9702f4) & 0x02000000) ? 1 : 0; + nr += (jdev_read(jdev, 0x9802f4) & 0x02000000) ? 1 : 0; + } + if (!nr) { + nr = 1; + pr_warn("mwv207: ddr cnt default to 1"); + } + + return size * 1024 * 1024 * nr; +} + +static int mwv207_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mwv207_device *jdev; + int ret; + + ret = mwv207_remove_conflicting_framebuffers(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + jdev = devm_drm_dev_alloc(&pdev->dev, &mwv207_drm_driver, + struct mwv207_device, base); + if (IS_ERR(jdev)) + return PTR_ERR(jdev); + + jdev->dev = &pdev->dev; + jdev->pdev = pdev; + jdev->base.dev_private = jdev; + + jdev->mmio = devm_ioremap(jdev->dev, + pci_resource_start(pdev, 1), pci_resource_len(pdev, 1)); + if (!jdev->mmio) + return -ENOMEM; + + jdev->iatu = devm_ioremap(jdev->dev, + pci_resource_start(pdev, 4), pci_resource_len(pdev, 4)); + if (!jdev->iatu) + return -ENOMEM; + + jdev->win = devm_ioremap(jdev->dev, + pci_resource_end(pdev, 2) - WIN_SIZE + 1, WIN_SIZE); + if (!jdev->win) + return -ENOMEM; + + ret = mwv207_iatu_init(jdev); + if (ret) + return ret; + + jdev->lite = jdev_read(jdev, 0x12c0000) == 0x21 ? false : true; + + pci_set_drvdata(pdev, jdev); + + ret = mwv207_db_init(jdev); + if (ret) + return ret; + + mwv207_vbios_init(jdev); + + ret = mwv207_irq_init(jdev); + if (ret) + goto err_vbios; + + jdev->visible_vram_size = pci_resource_len(pdev, 2) - WIN_SIZE; + jdev->vram_size = mwv207_vram_size(jdev); + jdev->vram_bar_base = pci_resource_start(pdev, 2); + + ret = mwv207_ttm_init(jdev); + if (ret) + goto err_irq; + + ret = mwv207_sched_init(jdev); + if (ret) + goto err_ttm; + + ret = mwv207_kctx_init(jdev); + if (ret) + goto err_sched; + + ret = mwv207_kms_init(jdev); + if (ret) + goto err_kctx; + + if (selftest) { + ret = mwv207_test(jdev); + if (ret) + goto err_kms; + } + + mwv207_db_sort(jdev); + ret = drm_dev_register(&jdev->base, 0); + if (ret) + goto err_kms; + drm_fbdev_generic_setup(&jdev->base, 32); + + return 0; + +err_kms: + mwv207_kms_fini(jdev); +err_kctx: + mwv207_kctx_fini(jdev); +err_sched: + mwv207_sched_fini(jdev); +err_ttm: + mwv207_ttm_fini(jdev); +err_irq: + mwv207_irq_fini(jdev); +err_vbios: + mwv207_vbios_fini(jdev); + mwv207_db_fini(jdev); + return ret; +} + +static void mwv207_pci_remove(struct pci_dev *pdev) +{ + struct mwv207_device *jdev = pci_get_drvdata(pdev); + + drm_dev_unregister(&jdev->base); + mwv207_kms_fini(jdev); + mwv207_kctx_fini(jdev); + mwv207_sched_fini(jdev); + mwv207_ttm_fini(jdev); + mwv207_irq_fini(jdev); + mwv207_vbios_fini(jdev); + mwv207_db_fini(jdev); + pci_clear_master(pdev); + pci_set_drvdata(pdev, NULL); +} + +static int mwv207_pmops_suspend(struct device *dev) +{ + struct mwv207_device *jdev = dev_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); + int ret; + struct ttm_resource_manager *man; + + ret = mwv207_kms_suspend(jdev); + if (ret) + return ret; + + man = ttm_manager_type(&jdev->bdev, TTM_PL_VRAM); + ttm_resource_manager_evict_all(&jdev->bdev, man); + + ret = mwv207_sched_suspend(jdev); + if (ret) + goto fail; + + mwv207_irq_suspend(jdev); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + udelay(200); + return 0; +fail: + mwv207_kms_resume(jdev); + return ret; +} + +static int mwv207_pmops_resume(struct device *dev) +{ + struct mwv207_device *jdev = dev_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); + int ret; + + msleep(100); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) { + pr_err("mwv207: failed to enable pci device"); + return ret; + } + pci_set_master(pdev); + + ret = mwv207_iatu_init(jdev); + if (ret) + return ret; + + mwv207_irq_resume(jdev); + + ret = mwv207_sched_resume(jdev); + if (ret) + return ret; + + return mwv207_kms_resume(jdev); +} + +static SIMPLE_DEV_PM_OPS(mwv207_pm_ops, mwv207_pmops_suspend, mwv207_pmops_resume); + +#define MWV207_PCI_DEVICE_DATA(vend, dev, data) \ + .vendor = vend, .device = dev, .driver_data = (kernel_ulong_t)(data), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ + .class = 0, .class_mask = 0 + +static const struct pci_device_id pciidlist[] = { + {MWV207_PCI_DEVICE_DATA(0x0731, 0x9100, NULL)}, + {0}, +}; + +MODULE_DEVICE_TABLE(pci, pciidlist); +static struct pci_driver mwv207_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = mwv207_pci_probe, + .remove = mwv207_pci_remove, + .driver.pm = &mwv207_pm_ops, +}; + +static int __init mwv207_init(void) +{ + return pci_register_driver(&mwv207_pci_driver); +} + +static void __exit mwv207_exit(void) +{ + pci_unregister_driver(&mwv207_pci_driver); +} + +module_init(mwv207_init); +module_exit(mwv207_exit); +MODULE_DESCRIPTION("MWV207 Graphics Driver"); +MODULE_AUTHOR("shanjinkui"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/mwv207/mwv207_gem.c b/drivers/gpu/drm/mwv207/mwv207_gem.c new file mode 100644 index 0000000000000..40ee95c5086f0 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_gem.c @@ -0,0 +1,275 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include "mwv207_drm.h" +#include "mwv207_gem.h" +#include "mwv207_bo.h" +#include "mwv207_ttm.h" + +static vm_fault_t mwv207_gem_fault(struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo = vmf->vma->vm_private_data; + vm_fault_t ret; + + ret = ttm_bo_vm_reserve(bo, vmf); + if (ret) + goto unlock_resv; + + ret = mwv207_bo_fault_reserve_notify(bo); + if (ret) + goto unlock_resv; + + ret = ttm_bo_vm_fault_reserved(vmf, vmf->vma->vm_page_prot, TTM_BO_VM_NUM_PREFAULT); + if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) + return ret; + +unlock_resv: + dma_resv_unlock(bo->base.resv); + return ret; +} + +static const struct vm_operations_struct mwv207_ttm_vm_ops = { + .fault = mwv207_gem_fault, + .open = ttm_bo_vm_open, + .close = ttm_bo_vm_close, + .access = ttm_bo_vm_access +}; + +int mwv207_gem_prime_pin(struct drm_gem_object *gem) +{ + struct mwv207_bo *jbo = mwv207_bo_from_gem(gem); + int ret; + + ret = mwv207_bo_reserve(jbo, true); + if (ret) + return ret; + ret = mwv207_bo_pin_reserved(jbo, 0x2); + mwv207_bo_unreserve(jbo); + return ret; +} + +void mwv207_gem_prime_unpin(struct drm_gem_object *gem) +{ + struct mwv207_bo *jbo = mwv207_bo_from_gem(gem); + int ret; + + ret = mwv207_bo_reserve(jbo, true); + if (ret) + return; + ret = mwv207_bo_unpin_reserved(jbo); + mwv207_bo_unreserve(jbo); +} + +int mwv207_gem_prime_vmap(struct drm_gem_object *gobj, struct iosys_map *map) +{ + struct mwv207_bo *jbo = mwv207_bo_from_gem(gobj); + int ret; + + if (jbo->kptr) { + jbo->map_count++; + goto out; + } + + ret = mwv207_bo_pin_reserved(jbo, 0x2); + if (unlikely(ret)) { + drm_err(gobj->dev, "pin %p for vmap failed\n", jbo); + return ret; + } + + ret = ttm_bo_vmap(&jbo->tbo, &jbo->map); + if (ret) { + drm_err(gobj->dev, "ttm bo vmap failed\n"); + mwv207_bo_unpin_reserved(jbo); + return ret; + } + jbo->map_count = 1; + +out: + *map = jbo->map; + + return 0; +} + +void mwv207_gem_prime_vunmap(struct drm_gem_object *gobj, struct iosys_map *map) +{ + struct mwv207_bo *jbo = mwv207_bo_from_gem(gobj); + + if (unlikely(!jbo->map_count)) { + drm_warn(gobj->dev, "%p is not mapped\n", jbo); + return; + } + + jbo->map_count --; + if (jbo->map_count == 0) { + ttm_bo_vunmap(&jbo->tbo, &jbo->map); + + mwv207_bo_unpin_reserved(jbo); + } +} + +const struct drm_gem_object_funcs mwv207_gem_object_funcs = { + .free = mwv207_gem_free_object, + .export = drm_gem_prime_export, + .pin = mwv207_gem_prime_pin, + .unpin = mwv207_gem_prime_unpin, + .vmap = mwv207_gem_prime_vmap, + .vunmap = mwv207_gem_prime_vunmap, + .mmap = drm_gem_ttm_mmap, + .vm_ops = &mwv207_ttm_vm_ops, +}; + +static int mwv207_gem_create(struct mwv207_device *jdev, + u64 size, u64 align, u32 preferred_domain, + u32 flags, struct drm_gem_object **gobj) +{ + struct mwv207_bo *jbo; + int ret; + + *gobj = NULL; +retry: + + ret = mwv207_bo_create(jdev, size, align, ttm_bo_type_device, + preferred_domain, flags, &jbo); + if (ret) { + if (ret != -ERESTARTSYS) { + if (flags & (1<<0)) { + flags &= ~(1<<0); + goto retry; + } + if (preferred_domain == 0x2) { + preferred_domain |= 0x1; + goto retry; + } + DRM_DEBUG("Failed to allocate GEM object (%lld, %d, %llu, %d)\n", + size, preferred_domain, align, ret); + } + return ret; + } + + *gobj = &jbo->tbo.base; + (*gobj)->funcs = &mwv207_gem_object_funcs; + + return 0; +} + +int mwv207_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct mwv207_device *jdev = dev->dev_private; + struct drm_gem_object *gobj; + u32 handle; + int ret; + + args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 64); + args->size = args->pitch * args->height; + ret = mwv207_gem_create(jdev, args->size, 0x10000, + 0x2, + (1<<0), + &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(file, gobj, &handle); + mwv207_gem_object_put(gobj); + if (ret) + return ret; + + args->handle = handle; + return 0; +} + +void mwv207_gem_free_object(struct drm_gem_object *gobj) +{ + struct mwv207_bo *jbo; + + if (!gobj) + return; + + jbo = mwv207_bo_from_gem(gobj); + mwv207_bo_unref(jbo); +} + +int mwv207_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct mwv207_device *jdev = dev->dev_private; + union drm_mwv207_gem_create *args = data; + struct drm_gem_object *gobj; + int ret; + + if (args->in.size == 0) + return -EINVAL; + if (args->in.alignment & (args->in.alignment - 1)) + return -EINVAL; + if (args->in.preferred_domain & ~0x7) + return -EINVAL; + if (args->in.flags & ~((1<<0)|(1<<1)|(1<<2))) + return -EINVAL; + + ret = mwv207_gem_create(jdev, args->in.size, args->in.alignment, + args->in.preferred_domain, + args->in.flags, &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(filp, gobj, &args->out.handle); + mwv207_gem_object_put(gobj); + + return ret; +} + +int mwv207_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + union drm_mwv207_gem_mmap *args = data; + struct drm_gem_object *obj; + + if (args->in.pad) + return -EINVAL; + + obj = drm_gem_object_lookup(filp, args->in.handle); + if (!obj) + return -ENOENT; + args->out.offset = mwv207_bo_mmap_offset(obj); + mwv207_gem_object_put(obj); + + return 0; +} + +int mwv207_gem_wait_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_mwv207_gem_wait *args = (struct drm_mwv207_gem_wait *)data; + long timeout; + bool write; + int ret; + + if (args->op & ~(0x00000002 | 0x00000001)) + return -EINVAL; + + write = args->op & 0x00000002; + timeout = drm_timeout_abs_to_jiffies(args->timeout); + + ret = drm_gem_dma_resv_wait(filp, args->handle, write, timeout); + if (ret == -ETIME) + ret = timeout ? -ETIMEDOUT : -EBUSY; + + return ret; +} + diff --git a/drivers/gpu/drm/mwv207/mwv207_gem.h b/drivers/gpu/drm/mwv207/mwv207_gem.h new file mode 100644 index 0000000000000..2a882307bb877 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_gem.h @@ -0,0 +1,41 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_GEM_H_TJTW9M4R +#define MWV207_GEM_H_TJTW9M4R +#include +#include +#include +#include "mwv207.h" + +extern const struct drm_gem_object_funcs mwv207_gem_object_funcs; + +int mwv207_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); + +void mwv207_gem_free_object(struct drm_gem_object *obj); + +int mwv207_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +int mwv207_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +int mwv207_gem_wait_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +#define mwv207_gem_object_put drm_gem_object_put + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_irq.c b/drivers/gpu/drm/mwv207/mwv207_irq.c new file mode 100644 index 0000000000000..e9857bf24b0d8 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_irq.c @@ -0,0 +1,187 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include "mwv207.h" +#include "mwv207_irq.h" + +static irqreturn_t mwv207_isr(int irq_unused, void *dev_id) +{ + struct mwv207_device *jdev = dev_id; + int i, j, hwirq, virq, ret = IRQ_NONE; + u32 stat; + + for (i = 0; i < 2; ++i) { + stat = jdev_read(jdev, (0x009A802c) + i * 4); + stat &= jdev->irq_enable_reg[i]; + while ((j = ffs(stat))) { + j--; + hwirq = i * 32 + j; + virq = irq_find_mapping(jdev->irq_domain, hwirq); + if (virq) { + ret = generic_handle_irq(virq); + if (ret < 0) + pr_warn("mwv207: hwirq(%d) handled with %d", hwirq, ret); + } else + pr_warn("mwv207: no irq mapping set on %d", hwirq); + + jdev_write(jdev, (0x009A802c) + i * 4, (1UL << j)); + stat &= ~(1UL << j); + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static void mwv207_irq_mask(struct irq_data *d) +{ + struct mwv207_device *jdev = irq_data_get_irq_chip_data(d); + int irq = irqd_to_hwirq(d); + unsigned long flags; + u32 reg; + + reg = (0x009A8020) + (irq / 32) * 4; + + spin_lock_irqsave(&jdev->irq_lock, flags); + jdev_modify(jdev, reg, 1 << (irq % 32), 0); + jdev->irq_enable_reg[irq / 32] = jdev_read(jdev, reg); + spin_unlock_irqrestore(&jdev->irq_lock, flags); +} + +static void mwv207_irq_unmask(struct irq_data *d) +{ + struct mwv207_device *jdev = irq_data_get_irq_chip_data(d); + int irq = irqd_to_hwirq(d); + unsigned long flags; + u32 reg; + + reg = (0x009A8020) + (irq / 32) * 4; + + spin_lock_irqsave(&jdev->irq_lock, flags); + jdev_modify(jdev, reg, 1 << (irq % 32), 1 << (irq % 32)); + jdev->irq_enable_reg[irq / 32] = jdev_read(jdev, reg); + spin_unlock_irqrestore(&jdev->irq_lock, flags); +} + +void mwv207_irq_suspend(struct mwv207_device *jdev) +{ + int i; + + for (i = 0; i < 2; ++i) + jdev->irq_enable_reg[i] = jdev_read(jdev, + (0x009A8020) + i * 4); +} + +void mwv207_irq_resume(struct mwv207_device *jdev) +{ + int i; + + for (i = 0; i < 2; ++i) { + jdev_write(jdev, (0x009A802c) + i * 4, 0xffffffff); + jdev_write(jdev, (0x009A8020) + i * 4, + jdev->irq_enable_reg[i]); + } +} + +static struct irq_chip mwv207_irq_chip = { + .name = "mwv207", + .irq_mask = mwv207_irq_mask, + .irq_unmask = mwv207_irq_unmask, +}; + +static int mwv207_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mwv207_device *jdev = d->host_data; + + if (hwirq >= 64) + return -EPERM; + + irq_set_chip_and_handler(irq, &mwv207_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, jdev); + + return 0; +} + +static const struct irq_domain_ops mwv207_irq_domain_ops = { + .map = mwv207_irq_domain_map, +}; + +int mwv207_irq_init(struct mwv207_device *jdev) +{ + struct pci_dev *pdev = jdev->pdev; + int ret, i; + + for (i = 0; i < 2; ++i) { + jdev_write(jdev, (0x009A802c) + i * 4, 0xffffffff); + jdev_write(jdev, (0x009A8020) + i * 4, 0); + } + + spin_lock_init(&jdev->irq_lock); + + jdev->irq_domain = irq_domain_add_linear(NULL, 64, + &mwv207_irq_domain_ops, jdev); + if (!jdev->irq_domain) + return -ENODEV; + + for (i = 0; i < 64; ++i) + irq_create_mapping(jdev->irq_domain, i); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY); + if (ret < 1) { + ret = -ENODEV; + goto free_mapping; + } + + ret = request_irq(pdev->irq, mwv207_isr, IRQF_SHARED, "mwv207_isr", jdev); + if (ret) + goto free_vec; + + return 0; + +free_vec: + pci_free_irq_vectors(pdev); +free_mapping: + for (i = 0; i < 64; ++i) { + int irq = irq_find_mapping(jdev->irq_domain, i); + + irq_dispose_mapping(irq); + } + + irq_domain_remove(jdev->irq_domain); + + return ret; +} + +void mwv207_irq_fini(struct mwv207_device *jdev) +{ + struct pci_dev *pdev = jdev->pdev; + int i; + + free_irq(pdev->irq, jdev); + pci_free_irq_vectors(pdev); + + for (i = 0; i < 64; ++i) { + int irq = irq_find_mapping(jdev->irq_domain, i); + + irq_dispose_mapping(irq); + } + + irq_domain_remove(jdev->irq_domain); +} diff --git a/drivers/gpu/drm/mwv207/mwv207_irq.h b/drivers/gpu/drm/mwv207/mwv207_irq.h new file mode 100644 index 0000000000000..eabcc61e62c47 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_irq.h @@ -0,0 +1,26 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_IRQ_H_Z8YGVNB2 +#define MWV207_IRQ_H_Z8YGVNB2 + +struct mwv207_device; + +int mwv207_irq_init(struct mwv207_device *jdev); +void mwv207_irq_fini(struct mwv207_device *jdev); + +void mwv207_irq_suspend(struct mwv207_device *jdev); +void mwv207_irq_resume(struct mwv207_device *jdev); +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_2d.c b/drivers/gpu/drm/mwv207/mwv207_pipe_2d.c new file mode 100644 index 0000000000000..d75dffd818ffe --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_2d.c @@ -0,0 +1,556 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include +#include + +#include "mwv207_bo.h" +#include "mwv207_sched.h" +#include "mwv207_vbios.h" +#include "mwv207_devfreq.h" + +#define DEFAULT_MAX_FREQ_2D 600 + +#define wait_for(COND, MS) ({ \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1; \ + int ret__ = 0; \ + while (!(COND)) { \ + if (time_after(jiffies, timeout__)) { \ + if (!(COND)) \ + ret__ = -ETIMEDOUT; \ + break; \ + } \ + msleep(1); \ + } \ + ret__; \ +}) + +struct mwv207_pipe_2d { + struct mwv207_pipe base; + struct mwv207_bo *ring_bo; + struct mwv207_device *jdev; + u32 *ring_start; + u32 *ring_end; + u32 *head; + u32 *tail; + u32 *last_wait; + u64 ring_start_addr; + int id; + unsigned int irq; + + u64 fence_ctx; + u32 next_fence; + u32 completed_fence; + spinlock_t fence_lock; + spinlock_t fence_queue_lock; + struct list_head fence_queue; +}; + +struct pipe_2d_fence { + struct dma_fence base; + struct mwv207_pipe_2d *pipe; + u32 *rpos; + + struct list_head q; +}; + +#define to_pipe_2d(p) container_of(p, struct mwv207_pipe_2d, base) +#define to_pipe_2d_fence(f) container_of(f, struct pipe_2d_fence, base) + +static inline void pipe_2d_write(struct mwv207_pipe_2d *pipe, + u32 reg, u32 value) +{ + pipe_write(&pipe->base, reg, value); +} + +static inline u32 pipe_2d_read(struct mwv207_pipe_2d *pipe, u32 reg) +{ + return pipe_read(&pipe->base, reg); +} + +static inline void pipe_2d_barrier(struct mwv207_pipe_2d *pipe) +{ + pipe_2d_read(pipe, 0x4100); + mb(); +} + +static inline bool fence16_after(u16 a, u16 b) +{ + return (s16)(a - b) > 0; +} + +static inline bool fence_after(u32 a, u32 b) +{ + return (s32)(a - b) > 0; +} + +static const char *pipe_2d_fence_get_driver_name(struct dma_fence *fence) +{ + return "mwv207"; +} + +static const char *pipe_2d_fence_get_timeline_name(struct dma_fence *fence) +{ + return to_pipe_2d_fence(fence)->pipe->base.fname; +} + +static bool pipe_2d_fence_signaled(struct dma_fence *fence) +{ + struct pipe_2d_fence *f = to_pipe_2d_fence(fence); + + return fence_after(f->pipe->completed_fence, f->base.seqno); +} + +static void pipe_2d_fence_release(struct dma_fence *fence) +{ + struct pipe_2d_fence *f = to_pipe_2d_fence(fence); + + kfree_rcu(f, base.rcu); +} + +static const struct dma_fence_ops pipe_2d_fence_ops = { + .get_driver_name = pipe_2d_fence_get_driver_name, + .get_timeline_name = pipe_2d_fence_get_timeline_name, + .signaled = pipe_2d_fence_signaled, + .release = pipe_2d_fence_release, +}; + +static inline u32 pipe_2d_ptr_span(void *end, void *start) +{ + return (unsigned long)end - (unsigned long)start; +} + +static inline u32 pipe_2d_gpu_addr(struct mwv207_pipe_2d *pipe, u32 *ptr) +{ + return pipe->ring_start_addr + pipe_2d_ptr_span(ptr, pipe->ring_start); +} + +static inline bool pipe_2d_idle_on_noc(struct mwv207_pipe_2d *pipe) +{ + struct mwv207_device *jdev = pipe->jdev; + u32 pending1, pending2; + u32 val; + + pending1 = jdev->lite ? 0x70038000 : 0xF87FC000; + pending2 = jdev->lite ? 0x00000000 : 0x0000000F; + + val = jdev_read(jdev, (0x009B0194)); + if ((val & pending1) != pending1) + return false; + val = jdev_read(jdev, (0x009B0198)); + if ((val & pending2) != pending2) + return false; + return true; +} + +static int pipe_2d_hw_init(struct mwv207_pipe_2d *pipe) +{ + int ret; + u32 val; + + ret = wait_for(pipe_2d_idle_on_noc(pipe), 5000); + if (ret) { + pr_err("mwv207: failed to wait %s idle, %#x %#x", + pipe->base.fname, + jdev_read(pipe->jdev, (0x009B0194)), + jdev_read(pipe->jdev, (0x009B0198))); + return ret; + } + + val = jdev_read(pipe->jdev, (0x009B0048)); + val &= ~(1 << 8); + jdev_write(pipe->jdev, (0x009B0048), val); + + val = jdev_read(pipe->jdev, (0x009B0014)); + val &= ~(1 << 16); + jdev_write(pipe->jdev, (0x009B0014), val); + + val = jdev_read(pipe->jdev, (0x009B0048)); + val |= 1 << 8; + jdev_write(pipe->jdev, (0x009B0048), val); + + val = jdev_read(pipe->jdev, (0x009B0014)); + val |= 1 << 16; + jdev_write(pipe->jdev, (0x009B0014), val); + + msleep(10); + + pipe_2d_write(pipe, 0x4800, 0); + + ret = wait_for(pipe_2d_read(pipe, 0x4800), 5000); + if (ret) { + pr_err("mwv207: failed to reset %s status RAM", + pipe->base.fname); + return ret; + } + + return 0; +} + +static void pipe_2d_reset_ring(struct mwv207_pipe_2d *pipe) +{ + struct pipe_2d_fence *fence, *p; + unsigned long flags; + + pipe->head = pipe->ring_start; + pipe->tail = pipe->ring_start; + + spin_lock_irqsave(&pipe->fence_queue_lock, flags); + list_for_each_entry_safe(fence, p, &pipe->fence_queue, q) { + list_del(&fence->q); + dma_fence_put(&fence->base); + } + spin_unlock_irqrestore(&pipe->fence_queue_lock, flags); +} + +static u32 *pipe_2d_append_wl(struct mwv207_pipe_2d *pipe) +{ + u32 *wait = pipe->tail; + + *pipe->tail++ = 0x88000000; + *pipe->tail++ = 0x80000000; + *pipe->tail++ = 0x89000000; + *pipe->tail++ = pipe_2d_gpu_addr(pipe, wait);; + + return wait; +} + +static void pipe_2d_start(struct mwv207_pipe_2d *pipe) +{ + u32 regval = pipe->ring_start_addr >> 4 | 1 << 29; + + pipe_2d_write(pipe, 0x4200, regval); + + *pipe->tail++ = 0x80000000; + *pipe->tail++ = 1; + *pipe->tail++ = 0x80000000; + *pipe->tail++ = 0x80000000; + + pipe->last_wait = pipe_2d_append_wl(pipe); + BUG_ON(pipe_2d_ptr_span(pipe->tail, pipe->ring_start) != 32); + pipe_2d_barrier(pipe); + pipe_2d_write(pipe, 0x4C00, 1); + + dev_dbg(pipe->base.jdev->dev, "start 2d wait link at 0x%llx", + pipe->ring_start_addr); +} + +static inline void pipe_2d_stop(struct mwv207_pipe_2d *pipe) +{ + pr_info("mwv207: %s %s TBD", pipe->base.fname, __func__); +} + +static void mwv207_pipe_2d_reset(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_2d *pipe = to_pipe_2d(mpipe); + + if (pipe_2d_hw_init(pipe)) + return; + pipe_2d_reset_ring(pipe); + pipe_2d_start(pipe); +} + +static u32 *pipe_2d_wait_for_space(struct mwv207_pipe_2d *pipe, u32 size) +{ + int i; + + for (i = 0; i < 10000; i++) { + u32 *head = READ_ONCE(pipe->head); + if (head <= pipe->tail) { + if (pipe_2d_ptr_span(pipe->ring_end, pipe->tail) >= size) + return pipe->tail; + if (pipe_2d_ptr_span(head, pipe->ring_start) >= size) + return pipe->ring_start; + } else { + if (pipe_2d_ptr_span(head, pipe->tail) >= size) + return pipe->tail; + } + usleep_range(200, 200); + } + + return NULL; +} + +static struct dma_fence *pipe_2d_append_fence(struct mwv207_pipe_2d *pipe) +{ + struct pipe_2d_fence *fence; + unsigned long flags; + u32 regno; + + fence = kzalloc(sizeof(struct pipe_2d_fence), GFP_KERNEL); + if (!fence) { + pr_err("mwv207: failed to alloc memory for %s", + pipe->base.fname); + return ERR_PTR(-ENOMEM); + } + dma_fence_init(&fence->base, &pipe_2d_fence_ops, &pipe->fence_lock, + pipe->fence_ctx, ++pipe->next_fence); + fence->pipe = pipe; + fence->rpos = pipe->tail; + regno = (pipe->next_fence & 0x1f) + 1; + + spin_lock_irqsave(&pipe->fence_queue_lock, flags); + list_add_tail(&fence->q, &pipe->fence_queue); + spin_unlock_irqrestore(&pipe->fence_queue_lock, flags); + + *pipe->tail++ = (0x40000000 | 0x6000 | (0) << 2); + *pipe->tail++ = (u32)(fence->base.seqno & 0xffff); + *pipe->tail++ = (0x40000000 | 0x6000 | (regno) << 2); + *pipe->tail++ = regno; + + pipe_2d_write(pipe, 0x6000 + regno * 4, regno); + + dma_fence_get(&fence->base); + return &fence->base; +} + +static void pipe_2d_wtol(struct mwv207_pipe_2d *pipe, u32 *start, u32 *new_wait) +{ + u32 *pos = pipe->last_wait; + + pos[1] = pipe_2d_gpu_addr(pipe, start); + pipe_2d_barrier(pipe); + pos[0] = 0x89000000; + pipe_2d_barrier(pipe); + pipe_2d_write(pipe, 0x4C00, 1); + + pipe->last_wait = new_wait; +} + +static void mwv207_pipe_2d_dump_state(struct mwv207_pipe *mpipe) +{ + pr_info("%s %08x: %08x, %08x: %08x", mpipe->fname, + 0x4100, pipe_read(mpipe, 0x4100), + 0x961100, jdev_read(mpipe->jdev, 0x961100)); +} + +static struct dma_fence * +mwv207_pipe_2d_submit(struct mwv207_pipe *mpipe, struct mwv207_job *mjob) +{ + struct mwv207_pipe_2d *pipe = to_pipe_2d(mpipe); + u32 size = ALIGN(mjob->cmd_size, 16) + 128; + u32 *start, *wait, *last_tail, nop_cnt; + struct dma_fence *fence; + + start = pipe_2d_wait_for_space(pipe, size); + if (!start) { + pr_err("mwv207: %s is stuck", mpipe->fname); + return ERR_PTR(-EBUSY); + } + last_tail = pipe->tail; + pipe->tail = start; + + *pipe->tail++ = 0x80000000; + pipe->tail++; + *pipe->tail++ = 0x80000000; + *pipe->tail++ = 0x80000000; + + memcpy_toio(pipe->tail, mjob->cmds, mjob->cmd_size); + pipe->tail += mjob->cmd_size >> 2; + for (nop_cnt = (ALIGN(mjob->cmd_size, 16) - mjob->cmd_size); + nop_cnt > 0; nop_cnt -= 4) + *pipe->tail++ = 0x80000000; + + fence = pipe_2d_append_fence(pipe); + if (IS_ERR(fence)) { + pipe->tail = last_tail; + return fence; + } + + wait = pipe_2d_append_wl(pipe); + + BUG_ON(pipe_2d_ptr_span(pipe->tail, start) & 0xf); + start[1] = pipe_2d_ptr_span(pipe->tail, start) / 16 - 1; + + pipe_2d_wtol(pipe, start, wait); + + BUG_ON(pipe_2d_ptr_span(pipe->tail, start) > size); + + return fence; +} + +static irqreturn_t mwv207_pipe_2d_isr(int irq_unused, void *data) +{ + struct mwv207_pipe_2d *pipe = data; + struct pipe_2d_fence *fence, *p; + u32 seqno; + + pipe_2d_write(pipe, 0x4308, 0xFFFFFFFF); + seqno = pipe_2d_read(pipe, 0x6000); + + spin_lock(&pipe->fence_queue_lock); + list_for_each_entry_safe(fence, p, &pipe->fence_queue, q) { + if (fence16_after(fence->base.seqno & 0xffff, seqno & 0xffff)) + break; + list_del(&fence->q); + pipe->completed_fence = fence->base.seqno; + WRITE_ONCE(pipe->head, fence->rpos); + mwv207_devfreq_record_idle(&pipe->base); + dma_fence_signal(&fence->base); + dma_fence_put(&fence->base); + } + spin_unlock(&pipe->fence_queue_lock); + + return IRQ_HANDLED; +} + +static void mwv207_pipe_2d_destroy(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_2d *pipe = to_pipe_2d(mpipe); + struct mwv207_bo *mbo = pipe->ring_bo; + int ret; + + free_irq(pipe->irq, pipe); + pipe_2d_stop(pipe); + + ret = mwv207_bo_reserve(mbo, true); + if (ret) { + pr_err("mwv207: failed to reserve bo"); + return; + } + mwv207_bo_kunmap_reserved(mbo); + mwv207_bo_unpin_reserved(mbo); + mwv207_bo_unreserve(mbo); + mwv207_bo_unref(mbo); + + mwv207_devfreq_unregister(mpipe); +} + +static unsigned long pipe_2d_max_freq_get(struct mwv207_device *jdev) +{ + unsigned long freq; + + switch (jdev->pdev->subsystem_device) + { + case 0x9103: + freq = 600; + break; + case 0x9210: + case 0x9211: + freq = 800; + break; + case 0x930B: + freq = 1000; + break; + case 0x9101: + case 0x9102: + case 0x910A: + case 0x910B: + case 0x910C: + case 0x9200: + case 0x920A: + case 0x920B: + case 0x920C: + case 0x9230: + case 0x9231: + case 0x930A: + case 0x930C: + freq = 1200; + break; + default: + freq = DEFAULT_MAX_FREQ_2D; + break; + } + + return freq; +} + +struct mwv207_pipe *mwv207_pipe_2d_create(struct mwv207_device *jdev, + int id, u32 regbase, + const char *fname) +{ + struct mwv207_pipe_2d *pipe; + struct mwv207_bo *mbo; + void *addr; + int ret; + + pipe = devm_kzalloc(jdev->dev, sizeof(*pipe), GFP_KERNEL); + if (!pipe) + return NULL; + pipe->base.pll_id = MWV207_PLL_GU2D_PLL; + pipe->base.jdev = jdev; + pipe->base.fname = fname; + pipe->base.regbase = jdev->mmio + regbase; + pipe->base.iosize = 32 * 1024; + pipe->base.submit = mwv207_pipe_2d_submit; + pipe->base.reset = mwv207_pipe_2d_reset; + pipe->base.destroy = mwv207_pipe_2d_destroy; + pipe->base.dump_state = mwv207_pipe_2d_dump_state; + + pipe->jdev = jdev; + pipe->id = id; + pipe->fence_ctx = dma_fence_context_alloc(1); + INIT_LIST_HEAD(&pipe->fence_queue); + spin_lock_init(&pipe->fence_lock); + spin_lock_init(&pipe->fence_queue_lock); + + ret = mwv207_devfreq_register(&pipe->base, pipe_2d_max_freq_get(jdev)); + if (ret) + pipe->base.devfreq = NULL; + + ret = mwv207_bo_create(jdev, 0x40000, 0x1000, + ttm_bo_type_kernel, 0x2, + (1<<0), &mbo); + if (ret) + goto unregister_devfreq; + + ret = mwv207_bo_reserve(mbo, true); + if (ret) + goto free_bo; + ret = mwv207_bo_pin_reserved(mbo, 0x2); + if (ret) + goto unreserve_bo; + ret = mwv207_bo_kmap_reserved(mbo, &addr); + if (ret) + goto unpin_bo; + pipe->ring_bo = mbo; + + pipe->ring_start = (u32 *)addr; + pipe->ring_start_addr = mwv207_bo_gpu_phys(mbo); + pipe->ring_end = pipe->ring_start + (0x40000 >> 2); + + mwv207_pipe_2d_reset(&pipe->base); + + pipe->irq = irq_find_mapping(jdev->irq_domain, 17 + id); + if (pipe->irq == 0) { + pr_err("mwv207: failed to find %s irq mapping", fname); + goto unmap_bo; + } + + ret = request_irq(pipe->irq, mwv207_pipe_2d_isr, 0, fname, pipe); + if (ret) { + pr_err("mwv207: failed to request irq for %s", fname); + goto unmap_bo; + } + + mwv207_bo_unreserve(mbo); + + return &pipe->base; +unmap_bo: + mwv207_bo_kunmap_reserved(mbo); +unpin_bo: + mwv207_bo_unpin_reserved(mbo); +unreserve_bo: + mwv207_bo_unreserve(mbo); +free_bo: + mwv207_bo_unref(mbo); +unregister_devfreq: + mwv207_devfreq_unregister(&pipe->base); + return NULL; +} diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_3d.c b/drivers/gpu/drm/mwv207/mwv207_pipe_3d.c new file mode 100644 index 0000000000000..d0764e45226bc --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_3d.c @@ -0,0 +1,597 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include +#include +#include "mwv207_bo.h" +#include "mwv207_sched.h" +#include "mwv207_vbios.h" +#include "mwv207_devfreq.h" + +#define DEFAULT_MAX_FREQ_3D 600 + +#define to_3d_pipe(pipe) container_of(pipe, struct mwv207_pipe_3d, base) +struct mwv207_pipe_3d { + struct mwv207_pipe base; + struct mwv207_bo *ringbuf_bo; + u32 *ringbuf; + u32 *head; + u32 *tail; + u32 *end; + u32 ringbuf_gpu_addr; + int unit; + unsigned int irq; + + u64 fence_ctx; + u32 next_fence; + u32 completed_fence; + spinlock_t fence_lock; + + DECLARE_BITMAP(event_bitmap, 30); + struct dma_fence *event_fence[30]; + spinlock_t event_lock; +}; + +#define to_pipe_3d_fence(f) container_of(f, struct pipe_3d_fence, base) +struct pipe_3d_fence { + struct dma_fence base; + u32 *rpos; + struct mwv207_pipe_3d *pipe; +}; + +static inline void pipe_3d_write(struct mwv207_pipe_3d *pipe, u32 reg, u32 value) +{ + pipe_write(&pipe->base, reg, value); +} + +static inline u32 pipe_3d_read(struct mwv207_pipe_3d *pipe, u32 reg) +{ + return pipe_read(&pipe->base, reg); +} + +static inline bool fence_after(u32 a, u32 b) +{ + return (s32)(a - b) > 0; +} + +static const char *pipe_3d_fence_get_driver_name(struct dma_fence *fence) +{ + return "mwv207"; +} + +static const char *pipe_3d_fence_get_timeline_name(struct dma_fence *fence) +{ + return to_pipe_3d_fence(fence)->pipe->base.fname; +} + +static bool pipe_3d_fence_signaled(struct dma_fence *fence) +{ + struct pipe_3d_fence *f = to_pipe_3d_fence(fence); + + return fence_after(f->pipe->completed_fence, f->base.seqno); +} + +static void pipe_3d_fence_release(struct dma_fence *fence) +{ + struct pipe_3d_fence *f = to_pipe_3d_fence(fence); + + kfree_rcu(f, base.rcu); +} + +static const struct dma_fence_ops pipe_3d_fence_ops = { + .get_driver_name = pipe_3d_fence_get_driver_name, + .get_timeline_name = pipe_3d_fence_get_timeline_name, + .signaled = pipe_3d_fence_signaled, + .release = pipe_3d_fence_release, +}; + +static inline struct dma_fence *pipe_3d_event_pop_irq(struct mwv207_pipe_3d *pipe, u32 event) +{ + struct dma_fence *fence; + + spin_lock(&pipe->event_lock); + fence = pipe->event_fence[event]; + pipe->event_fence[event] = NULL; + clear_bit(event, pipe->event_bitmap); + spin_unlock(&pipe->event_lock); + + return fence; +} + +static inline u32 pipe_3d_event_push(struct mwv207_pipe_3d *pipe, struct dma_fence *fence) +{ + u32 event; + + spin_lock_irq(&pipe->event_lock); + event = find_first_zero_bit(pipe->event_bitmap, 30); + if (likely(event < 30)) + pipe->event_fence[event] = dma_fence_get(fence); + set_bit(event, pipe->event_bitmap); + spin_unlock_irq(&pipe->event_lock); + + BUG_ON(event >= 30); + + return event; +} + +static inline u32 pipe_3d_ptr_span(void *end, void *start) +{ + return (unsigned long)end - (unsigned long)start; +} + +static inline u32 pipe_3d_gpu_addr(struct mwv207_pipe_3d *pipe, u32 *ptr) +{ + return pipe->ringbuf_gpu_addr + pipe_3d_ptr_span(ptr, pipe->ringbuf); +} + +static inline void pipe_3d_link(struct mwv207_pipe_3d *pipe, u32 prefetch, u32 addr) +{ + pipe->tail = PTR_ALIGN(pipe->tail, 8); + BUG_ON(pipe->tail >= pipe->end - 1 + || (((prefetch + 7) / 8) & 0xffff0000) + || addr & 0x7); + *pipe->tail++ = 0x40000000 | ((prefetch + 7) / 8); + *pipe->tail++ = addr; +} + +static inline void pipe_3d_wait(struct mwv207_pipe_3d *pipe) +{ + pipe->tail = PTR_ALIGN(pipe->tail, 8); + BUG_ON(pipe->tail >= pipe->end); + + *pipe->tail++ = 0x38000000 | 200; +} + +static inline void pipe_3d_wl(struct mwv207_pipe_3d *pipe) +{ + pipe_3d_wait(pipe); + pipe_3d_link(pipe, 16, pipe_3d_gpu_addr(pipe, pipe->tail) - 4); +} + +static inline void pipe_3d_wtol(struct mwv207_pipe_3d *pipe, u32 *w, u32 target, u32 size) +{ + BUG_ON((((size + 7) / 8) & 0xffff0000) || target & 0x7 + || w < pipe->ringbuf || w >= pipe->end - 1); + w[1] = target; + pipe_3d_read(pipe, 0x4); + mb(); + w[0] = 0x40000000 | ((size + 7) / 8); +} + +static inline void pipe_3d_loadstate(struct mwv207_pipe_3d *pipe, u32 reg, u32 val) +{ + pipe->tail = PTR_ALIGN(pipe->tail, 8); + BUG_ON(pipe->tail >= pipe->end - 1); + + *pipe->tail++ = 0x08010000 | reg; + *pipe->tail++ = val; +} + +static inline void pipe_3d_sem(struct mwv207_pipe_3d *pipe, u32 from, u32 to) +{ + from &= 0x1f; + to &= 0x1f; + to <<= 8; + pipe_3d_loadstate(pipe, 0x0e02, from | to); +} + +static inline void pipe_3d_stall(struct mwv207_pipe_3d *pipe, u32 from, u32 to) +{ + pipe->tail = PTR_ALIGN(pipe->tail, 8); + BUG_ON(pipe->tail >= pipe->end - 1); + from &= 0x1f; + to &= 0x1f; + to <<= 8; + *pipe->tail++ = 0x48000000; + *pipe->tail++ = from | to; +} + +static inline void pipe_3d_start(struct mwv207_pipe_3d *pipe) +{ + u32 *start = pipe->tail; + u32 len; + + dev_dbg(pipe->base.jdev->dev, "start wait link at 0x%x", pipe->ringbuf_gpu_addr); + pipe_3d_loadstate(pipe, 0x0e21, 0x00020202); + pipe_3d_loadstate(pipe, 0x0e44, 0x0000000f); + pipe_3d_loadstate(pipe, 0x0e80, 0x00000000); + pipe_3d_loadstate(pipe, 0x7003, 0x00000001); + + pipe_3d_wl(pipe); + + len = (pipe->tail - start) * 4; + pipe_3d_write(pipe, 0x014, 0xffffffff); + pipe_3d_write(pipe, 0x654, pipe->ringbuf_gpu_addr); + pipe_3d_read(pipe, 0x4); + mb(); + pipe_3d_write(pipe, 0x658, 0x10000 | (len / 8)); + pipe_3d_write(pipe, 0x3a4, 0x10000 | (len / 8)); +} + +static inline int pipe_3d_stop(struct mwv207_pipe_3d *pipe) +{ + pr_info("%s TBD", __func__); + return 0; +} + +static void pipe_3d_core_reset(struct mwv207_pipe_3d *pipe) +{ + int i; + + for (i = 0; i < 2; i++) { + + pipe_3d_write(pipe, 0x104, 0); + + pipe_3d_write(pipe, 0x10c, 0x015b0880); + pipe_3d_write(pipe, 0x10c, 0x015b0881); + pipe_3d_write(pipe, 0x10c, 0x015b0880); + + pipe_3d_write(pipe, 0x000, 0x00010b00); + pipe_3d_write(pipe, 0x000, 0x00010900); + + usleep_range(1000, 1001); + + pipe_3d_write(pipe, 0x000, 0x00090900); + + pipe_3d_write(pipe, 0x3a8, 1); + pipe_3d_write(pipe, 0x3a8, 0); + + usleep_range(1000, 1001); + + pipe_3d_write(pipe, 0x000, 0x00010900); + } +} + +static void pipe_3d_hw_init(struct mwv207_pipe_3d *pipe) +{ + pipe_3d_write(pipe, 0x000, 0x00010900); + pipe_3d_core_reset(pipe); + + pipe_3d_write(pipe, 0x55c, 0x00ffffff); + pipe_3d_write(pipe, 0x414, 0x3c000000); + pipe_3d_write(pipe, 0x090, pipe_3d_read(pipe, 0x090) & 0xffffffbf); + + pipe_3d_write(pipe, 0x000, 0x00010900); + pipe_3d_write(pipe, 0x000, 0x00070100); + pipe_3d_write(pipe, 0x3ac, 0x00002300); + pipe_3d_write(pipe, 0x3a8, 0x2); + + pipe_3d_write(pipe, 0x03c, 0xffffffff); + pipe_3d_write(pipe, 0x03c, 0x00000000); + + pipe_3d_write(pipe, 0x100, 0x00140021); + + pipe_3d_write(pipe, 0x154, 0x1); + pipe_3d_write(pipe, 0x104, 0x00430408); + pipe_3d_write(pipe, 0x10c, 0x015b0880); +} + +static void mwv207_pipe_3d_reset(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_3d *pipe = to_3d_pipe(mpipe); + int i; + + spin_lock_irq(&pipe->event_lock); + bitmap_zero(pipe->event_bitmap, 30); + for (i = 0; i < 30; i++) { + dma_fence_put(pipe->event_fence[i]); + pipe->event_fence[i] = NULL; + } + spin_unlock_irq(&pipe->event_lock); + + pipe_3d_hw_init(pipe); + pipe->tail = pipe->ringbuf; + pipe->head = pipe->ringbuf; + + pipe_3d_start(pipe); +} + +static u32 *pipe_3d_wait_for_space(struct mwv207_pipe_3d *pipe, u32 size) +{ + u32 *head; + int i; + + for (i = 0; i < 10000; ++i) { + head = READ_ONCE(pipe->head); + if (head <= pipe->tail) { + if (pipe_3d_ptr_span(pipe->end, pipe->tail) >= size) + return pipe->tail; + if (pipe_3d_ptr_span(head, pipe->ringbuf) >= size) + return pipe->ringbuf; + } else { + if (pipe_3d_ptr_span(head, pipe->tail) >= size) + return pipe->tail; + } + usleep_range(200, 200); + } + return NULL; +} + +static struct dma_fence *mwv207_pipe_3d_submit(struct mwv207_pipe *mpipe, + struct mwv207_job *mjob) +{ + struct mwv207_pipe_3d *pipe = to_3d_pipe(mpipe); + struct pipe_3d_fence *fence; + u32 *last_tail, *start; + u32 size, event; + + size = ALIGN(mjob->cmd_size, 8) + 256; + last_tail = pipe->tail; + start = pipe_3d_wait_for_space(pipe, size); + if (start == NULL) { + pr_err("mwv207: engine 3d is stuck"); + + return ERR_PTR(-EBUSY); + } + memcpy_toio(start, mjob->cmds, mjob->cmd_size); + pipe->tail = start + mjob->cmd_size / 4; + + pipe_3d_loadstate(pipe, 0x0e03, 0xc63); + pipe_3d_loadstate(pipe, 0x594, 0x41); + + pipe_3d_sem(pipe, 0x7, 0x1); + pipe_3d_stall(pipe, 0x7, 0x1); + pipe_3d_loadstate(pipe, 0x502e, 0x1); + pipe_3d_loadstate(pipe, 0x502b, 0x3); + pipe_3d_sem(pipe, 0x10, 0x1); + pipe_3d_stall(pipe, 0x10, 0x1); + pipe_3d_loadstate(pipe, 0x502e, 0x0); + + fence = kzalloc(sizeof(struct pipe_3d_fence), GFP_KERNEL); + if (!fence) { + pipe->tail = last_tail; + pr_err("mwv207: failed to alloc memory for 3d pipe"); + + return ERR_PTR(-ENOMEM); + } + dma_fence_init(&fence->base, &pipe_3d_fence_ops, &pipe->fence_lock, + pipe->fence_ctx, ++pipe->next_fence); + fence->rpos = pipe->tail; + fence->pipe = pipe; + event = pipe_3d_event_push(pipe, &fence->base); + + pipe_3d_loadstate(pipe, 0x502e, 0x1); + pipe_3d_loadstate(pipe, 0x50ce, 0xf); + pipe_3d_loadstate(pipe, 0x0e01, 0x80 | event); + pipe_3d_loadstate(pipe, 0x502e, 0x0); + + pipe_3d_wl(pipe); + BUG_ON(pipe_3d_ptr_span(pipe->tail, start) > size); + pipe_3d_wtol(pipe, last_tail - 4, pipe_3d_gpu_addr(pipe, start), + pipe_3d_ptr_span(pipe->tail, start)); + return &fence->base; +} + +static void mwv207_pipe_3d_dump_state(struct mwv207_pipe *mpipe) +{ + u32 state0, addr0; + u32 state1, addr1; + int i; + + state0 = pipe_read(mpipe, 0x660); + addr0 = pipe_read(mpipe, 0x664); + for (i = 0; i < 500; ++i) { + state1 = pipe_read(mpipe, 0x660); + addr1 = pipe_read(mpipe, 0x664); + if (state1 != state0 || addr1 != addr0) + break; + } + + if (state1 != state0) + pr_info("%s state changing, s0: 0x%08x, s1: 0x%08x", mpipe->fname, state0, state1); + pr_info("%s current stat: 0x%08x", mpipe->fname, pipe_read(mpipe, 0x660)); + + if (addr1 != addr0) + pr_info("%s addr changing, a0: 0x%08x, a1: 0x%08x", mpipe->fname, addr0, addr1); + pr_info("%s current addr: 0x%08x", mpipe->fname, pipe_read(mpipe, 0x664)); + + pr_info("%s current cmdl: 0x%08x", mpipe->fname, pipe_read(mpipe, 0x668)); + pr_info("%s current cmdh: 0x%08x", mpipe->fname, pipe_read(mpipe, 0x66c)); +} + +static irqreturn_t mwv207_pipe_3d_isr(int irq_unused, void *dev_id) +{ + struct mwv207_pipe_3d *pipe = dev_id; + struct dma_fence *fence; + u32 event, intr; + + intr = pipe_3d_read(pipe, 0x10); + if (unlikely(!intr)) { + pr_warn("mwv207: 3d interrupt with no event"); + return IRQ_NONE; + } + if (unlikely(intr & 0x80000000)) { + pr_warn("mwv207: 3d axi bus error"); + intr &= ~0x80000000; + } + if (unlikely(intr & 0x40000000)) { + pr_warn("mwv207: 3d mmu error"); + intr &= ~0x40000000; + } + while ((event = ffs(intr))) { + event -= 1; + intr &= ~(1 << event); + + fence = pipe_3d_event_pop_irq(pipe, event); + if (!fence) { + pr_warn("unexpected event: %d", event); + continue; + } + + if (fence_after(fence->seqno, pipe->completed_fence)) { + struct pipe_3d_fence *pipe_fence = to_pipe_3d_fence(fence); + + pipe->completed_fence = fence->seqno; + pipe->head = pipe_fence->rpos; + } + mwv207_devfreq_record_idle(&pipe->base); + dma_fence_signal(fence); + dma_fence_put(fence); + } + return IRQ_HANDLED; +} + +static void mwv207_pipe_3d_destroy(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_3d *pipe = to_3d_pipe(mpipe); + struct mwv207_bo *jbo; + int ret; + + free_irq(pipe->irq, pipe); + pipe_3d_stop(pipe); + + jbo = pipe->ringbuf_bo; + ret = mwv207_bo_reserve(jbo, true); + if (ret) { + pr_info("failed to reserve bo"); + return; + } + mwv207_bo_kunmap_reserved(jbo); + mwv207_bo_unpin_reserved(jbo); + mwv207_bo_unreserve(jbo); + mwv207_bo_unref(jbo); + + mwv207_devfreq_unregister(mpipe); +} + +static unsigned long pipe_3d_max_freq_get(struct mwv207_device *jdev) +{ + unsigned long freq; + + switch (jdev->pdev->subsystem_device) + { + case 0x9103: + freq = 600; + break; + case 0x9210: + case 0x9211: + freq = 800; + break; + case 0x9101: + case 0x9102: + case 0x910A: + case 0x910B: + case 0x910C: + case 0x920C: + case 0x930B: + freq = 1000; + break; + case 0x9200: + case 0x920A: + case 0x920B: + case 0x9230: + case 0x9231: + case 0x930A: + case 0x930C: + freq = 1200; + break; + default: + freq = DEFAULT_MAX_FREQ_3D; + break; + } + + return freq; +} + +struct mwv207_pipe *mwv207_pipe_3d_create(struct mwv207_device *jdev, int unit, u32 regbase, + const char *fname) +{ + struct mwv207_pipe_3d *pipe; + struct mwv207_bo *jbo; + void *logical; + int ret; + + if (jdev->lite && unit) + return NULL; + + pipe = devm_kzalloc(jdev->dev, sizeof(struct mwv207_pipe_3d), GFP_KERNEL); + if (!pipe) + return NULL; + + pipe->base.pll_id = unit ? MWV207_PLL_GU3D1 : MWV207_PLL_GU3D0; + pipe->base.jdev = jdev; + pipe->base.fname = fname; + pipe->base.regbase = jdev->mmio + regbase; + pipe->base.iosize = 16 * 1024; + pipe->base.submit = mwv207_pipe_3d_submit; + pipe->base.reset = mwv207_pipe_3d_reset; + pipe->base.destroy = mwv207_pipe_3d_destroy; + pipe->base.dump_state = mwv207_pipe_3d_dump_state; + + pipe->unit = unit; + pipe->fence_ctx = dma_fence_context_alloc(1); + spin_lock_init(&pipe->fence_lock); + spin_lock_init(&pipe->event_lock); + + ret = mwv207_devfreq_register(&pipe->base, pipe_3d_max_freq_get(jdev)); + if (ret) + pipe->base.devfreq = NULL; + + ret = mwv207_bo_create(jdev, 0x40000, 0x1000, + ttm_bo_type_kernel, 0x2, (1<<0), + &jbo); + if (ret) + goto unregister_devfreq; + + ret = mwv207_bo_reserve(jbo, true); + if (ret) + goto free_bo; + + ret = mwv207_bo_pin_reserved(jbo, 0x2); + if (ret) + goto unreserve_bo; + + ret = mwv207_bo_kmap_reserved(jbo, &logical); + if (ret) + goto unpin_bo; + + pipe->ringbuf_bo = jbo; + + pipe->ringbuf = (u32 *)logical; + pipe->ringbuf_gpu_addr = mwv207_bo_gpu_phys(jbo); + pipe->end = pipe->ringbuf + (0x40000 / 4); + + mwv207_pipe_3d_reset(&pipe->base); + + pipe->irq = irq_find_mapping(jdev->irq_domain, 19 + unit); + if (pipe->irq == 0) { + pr_err("mwv207: failed to create 3D irq mapping"); + goto unmap_bo; + } + + ret = request_irq(pipe->irq, mwv207_pipe_3d_isr, 0, fname, pipe); + if (ret) { + pr_err("mwv207: failed to request 3d irq"); + goto unmap_bo; + } + + mwv207_bo_unreserve(jbo); + return &pipe->base; +unmap_bo: + mwv207_bo_kunmap_reserved(jbo); +unpin_bo: + mwv207_bo_unpin_reserved(jbo); +unreserve_bo: + mwv207_bo_unreserve(jbo); +free_bo: + mwv207_bo_unref(pipe->ringbuf_bo); +unregister_devfreq: + mwv207_devfreq_unregister(&pipe->base); + return NULL; +} diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c new file mode 100644 index 0000000000000..49e78201069e1 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c @@ -0,0 +1,86 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207_pipe_codec_common.h" + +static void pipe_codec_sw_pp_submit_ex(struct pipe_codec_sw_pp *sw_pp, struct mwv207_pipe *pipe, + u64 vram, u32 reg, int nr) +{ + if (sw_pp->wb_cnt >= JPIPE_WRITE_ENTRY_SIZE) { + dev_dbg(pipe->jdev->dev, "mwv207: too much write back entry"); + return; + } + + sw_pp->wb[sw_pp->wb_cnt].vram = vram; + sw_pp->wb[sw_pp->wb_cnt].reg = reg; + sw_pp->wb[sw_pp->wb_cnt].nr = nr; + + sw_pp->wb_cnt++; + sw_pp->pending = true; +} + +void pipe_codec_sw_pp_reset(struct pipe_codec_sw_pp *sw_pp) +{ + sw_pp->wb_cnt = 0; +} + +void pipe_codec_sw_pp_excute(struct pipe_codec_sw_pp *sw_pp, struct mwv207_pipe *pipe) +{ + int i, j; + + if (!sw_pp->pending) + return; + + for (i = 0; i < sw_pp->wb_cnt; i++) { + for (j = 0; j < sw_pp->wb[i].nr && sw_pp->wb[i].reg + j * 4 <= pipe->iosize - 4; j++) + sw_pp->wb_buf[j] = pipe_read(pipe, sw_pp->wb[i].reg + j * 4); + + if (!j) + continue; + + jdev_write_vram(pipe->jdev, sw_pp->wb[i].vram, &sw_pp->wb_buf[0], j * 4); + } + + wmb(); + + sw_pp->wb_cnt = 0; + sw_pp->pending = false; +} + +void pipe_codec_sw_pp_submit(struct pipe_codec_sw_pp *sw_pp, struct mwv207_pipe *pipe, + struct mwv207_job *mjob, int pos, int *cmdlen) +{ + int nr, reg; + u64 vram; + u32 cmd; + + *cmdlen = 16; + + if (pos + 16 > mjob->cmd_size) { + dev_dbg(pipe->jdev->dev, + "mwv207: cmd %d write back out of range", pos); + return; + } + + cmd = *(u32 *)(mjob->cmds + pos); + reg = (cmd & 0xffff) * 4; + nr = (cmd >> 16) & 0x3ff; + if (nr == 0) + nr = 1024; + + vram = *(u64 *)(mjob->cmds + pos + 8); + pipe_codec_sw_pp_submit_ex(sw_pp, pipe, vram, reg, nr); +} + diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h new file mode 100644 index 0000000000000..5e1c4e5cde3ad --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h @@ -0,0 +1,41 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207_sched.h" + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define JPIPE_WRITE_ENTRY_SIZE 16 +#define JPIPE_ENC_REG_NUM 500 +#define JPIPE_DEC_REG_NUM 503 +#define JPIPE_REG_NUM MAX(JPIPE_ENC_REG_NUM, JPIPE_DEC_REG_NUM) + +struct pipe_codec_wb_entry { + u64 vram; + u32 reg; + u32 nr; +}; + +struct pipe_codec_sw_pp { + struct pipe_codec_wb_entry wb[JPIPE_WRITE_ENTRY_SIZE]; + u32 wb_buf[JPIPE_REG_NUM]; + int wb_cnt; + bool pending; +}; + +void pipe_codec_sw_pp_reset(struct pipe_codec_sw_pp *sw_pp); +void pipe_codec_sw_pp_excute(struct pipe_codec_sw_pp *sw_pp, struct mwv207_pipe *pipe); +void pipe_codec_sw_pp_submit(struct pipe_codec_sw_pp *sw_pp, struct mwv207_pipe *pipe, + struct mwv207_job *mjob, int pos, int *cmdlen); + diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_dec.c b/drivers/gpu/drm/mwv207/mwv207_pipe_dec.c new file mode 100644 index 0000000000000..33a3b83821acd --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_dec.c @@ -0,0 +1,254 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include "mwv207_pipe_codec_common.h" + +#define to_dec_pipe(pipe) container_of(pipe, struct mwv207_pipe_dec, base) +struct pipe_dec_fence; +struct mwv207_pipe_dec { + struct mwv207_pipe base; + + u64 fence_ctx; + u64 fence_seqno; + spinlock_t fence_lock; + + struct pipe_dec_fence *current_fence; + struct task_struct *poll_thread; + struct pipe_codec_sw_pp sw_pp; +}; + +#define to_pipe_dec_fence(f) container_of(f, struct pipe_dec_fence, base) +struct pipe_dec_fence { + struct dma_fence base; + struct mwv207_pipe_dec *pipe; + struct list_head node; +}; + +static inline void pipe_dec_write(struct mwv207_pipe_dec *pipe, u32 reg, u32 value) +{ + pipe_write(&pipe->base, reg, value); +} + +static inline u32 pipe_dec_read(struct mwv207_pipe_dec *pipe, u32 reg) +{ + return pipe_read(&pipe->base, reg); +} + +static const char *pipe_dec_fence_get_driver_name(struct dma_fence *fence) +{ + return "mwv207"; +} + +static const char *pipe_dec_fence_get_timeline_name(struct dma_fence *fence) +{ + return to_pipe_dec_fence(fence)->pipe->base.fname; +} + +static void pipe_dec_fence_release(struct dma_fence *fence) +{ + struct pipe_dec_fence *f = to_pipe_dec_fence(fence); + + kfree_rcu(f, base.rcu); +} + +static const struct dma_fence_ops pipe_dec_fence_ops = { + .get_driver_name = pipe_dec_fence_get_driver_name, + .get_timeline_name = pipe_dec_fence_get_timeline_name, + .release = pipe_dec_fence_release, +}; + +static void pipe_dec_stat_reset(struct mwv207_pipe_dec *pipe, u32 stat) +{ + pipe_dec_write(pipe, 0x4, 0x0); +} + +static int pipe_dec_polling(void *priv) +{ + struct mwv207_pipe_dec *pipe = priv; + struct pipe_dec_fence *fence; + u32 stat; + + set_freezable(); + while (!kthread_should_stop()) { + usleep_range(1000, 1001); + try_to_freeze(); + + fence = smp_load_acquire(&pipe->current_fence); + if (fence == NULL) + continue; + + stat = pipe_dec_read(pipe, 0x4); + if ((stat & 0x1100) != 0x1100) + continue; + + pipe_codec_sw_pp_excute(&pipe->sw_pp, &pipe->base); + pipe_dec_stat_reset(pipe, stat); + + smp_store_release(&pipe->current_fence, NULL); + + dma_fence_signal(&fence->base); + dma_fence_put(&fence->base); + } + + return 0; +} + +static void mwv207_pipe_dec_reset(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_dec *pipe = to_dec_pipe(mpipe); + struct pipe_dec_fence *fence; + u32 stat; + int i; + + pipe_codec_sw_pp_reset(&pipe->sw_pp); + + stat = pipe_dec_read(pipe, 0x4); + if (stat & 0x1) + pipe_dec_write(pipe, 0x4, 0x20 | 0x30); + + pipe_dec_write(pipe, 0x4, 0); + pipe_dec_write(pipe, 0x8, 0x454); + + for (i = 12; i < pipe->base.iosize; i += 0x4) + pipe_dec_write(pipe, i, 0); + + fence = smp_load_acquire(&pipe->current_fence); + if (fence) { + dma_fence_put(&fence->base); + + smp_store_release(&pipe->current_fence, NULL); + } +} + +static struct dma_fence *mwv207_pipe_dec_submit(struct mwv207_pipe *mpipe, + struct mwv207_job *mjob) +{ + struct mwv207_pipe_dec *pipe = to_dec_pipe(mpipe); + struct pipe_dec_fence *fence; + int i, nr, pos, cmdlen; + bool hw_run = 0; + u32 cmd, reg; + + fence = smp_load_acquire(&pipe->current_fence); + if (fence) + return ERR_PTR(-EBUSY); + + for (pos = 0; pos <= mjob->cmd_size - 4; pos += cmdlen) { + cmd = *(u32 *)(mjob->cmds + pos); + switch ((cmd >> 27) & 0x1f) { + case 0x01: + if ((cmd >> 26) & 0x1) + return ERR_PTR(-EINVAL); + reg = (cmd & 0xffff) * 4; + nr = (cmd >> 16) & 0x3ff; + if (nr == 0) + nr = 1024; + for (i = pos + 4; i <= pos + nr * 4 + && i <= mjob->cmd_size - 4 + && reg <= pipe->base.iosize - 4; + i += 4, reg += 4) { + if (reg == 4) { + pipe_dec_read(pipe, 0x4); + hw_run = (*(u32 *)(mjob->cmds + i)) & 0x1; + + mb(); + } + pipe_dec_write(pipe, reg, *(u32 *)(mjob->cmds + i)); + } + + if (i <= pos + nr * 4) + dev_dbg(mpipe->jdev->dev, + "mwv207: invalid dec register writes dropped"); + + cmdlen = ALIGN(4 + nr * 4, 8); + break; + case 0x02: + pipe_codec_sw_pp_submit(&pipe->sw_pp, &pipe->base, mjob, pos, &cmdlen); + break; + default: + + return ERR_PTR(-EINVAL); + } + } + + fence = kzalloc(sizeof(struct pipe_dec_fence), GFP_KERNEL); + if (!fence) + return ERR_PTR(-ENOMEM); + fence->pipe = pipe; + dma_fence_init(&fence->base, &pipe_dec_fence_ops, &pipe->fence_lock, + pipe->fence_ctx, ++pipe->fence_seqno); + + if (hw_run) { + dma_fence_get(&fence->base); + + smp_store_release(&pipe->current_fence, fence); + } else { + pipe_codec_sw_pp_excute(&pipe->sw_pp, &pipe->base); + dma_fence_signal(&fence->base); + } + + return &fence->base; +} + +static void mwv207_pipe_dec_dump_state(struct mwv207_pipe *mpipe) +{ + pr_info("%s irq state reg: 0x%08x", mpipe->fname, + pipe_read(mpipe, 0x4)); +} + +static void mwv207_pipe_dec_destroy(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_dec *pipe = to_dec_pipe(mpipe); + + kthread_stop(pipe->poll_thread); +} + +struct mwv207_pipe *mwv207_pipe_dec_create(struct mwv207_device *jdev, int unit, u32 regbase, + const char *fname) +{ + struct mwv207_pipe_dec *pipe; + + if (jdev->lite && unit) + return NULL; + + pipe = devm_kzalloc(jdev->dev, sizeof(struct mwv207_pipe_dec), GFP_KERNEL); + if (!pipe) + return NULL; + + pipe->base.jdev = jdev; + pipe->base.fname = fname; + pipe->base.regbase = jdev->mmio + regbase; + pipe->base.iosize = JPIPE_DEC_REG_NUM * 4; + pipe->base.submit = mwv207_pipe_dec_submit; + pipe->base.reset = mwv207_pipe_dec_reset; + pipe->base.destroy = mwv207_pipe_dec_destroy; + pipe->base.dump_state = mwv207_pipe_dec_dump_state; + + spin_lock_init(&pipe->fence_lock); + pipe->fence_ctx = dma_fence_context_alloc(1); + pipe->fence_seqno = 0; + + mwv207_pipe_dec_reset(&pipe->base); + + pipe->poll_thread = kthread_run(pipe_dec_polling, pipe, "dec_polling"); + if (IS_ERR(pipe->poll_thread)) + return NULL; + + return &pipe->base; +} diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_dma.c b/drivers/gpu/drm/mwv207/mwv207_pipe_dma.c new file mode 100644 index 0000000000000..34939079cda04 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_dma.c @@ -0,0 +1,412 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include +#include +#include "mwv207_bo.h" +#include "mwv207_sched.h" + +#undef MWV207_DMA_DEBUG + +#define DMA_MAX_LLI_COUNT (0x400000 / sizeof(struct mwv207_lli)) + +#define DMA_TS_NOT_ALIGNED(size) ((size) - 1) +#define DMA_TS_ALIGNED(size, alignment) (((size) >= (alignment)) ? ((size) / (alignment) - 1) : 0) +#define DMA_CTL_NOT_ALIGNED ((1UL<<(63)) \ + | (1UL<<(58)) \ + | (1UL<<(30)) \ + | (1UL<<(38)) \ + | (1UL<<(47)) \ + | ((32ul & (0xFF))<<(39)) \ + | ((32ul & (0xFF))<<(48)) \ + | ((0UL & (0x7))<<(8)) \ + | ((0UL & (0x7))<<(11))) + +#define DMA_CTL_ALIGNED(a) ((1UL<<(63)) \ + | (1UL<<(58)) \ + | (1UL<<(30)) \ + | (1UL<<(38)) \ + | (1UL<<(47)) \ + | ((32ul & (0xFF))<<(39)) \ + | ((32ul & (0xFF))<<(48)) \ + | (((((a) == 64)?6UL:5UL) & (0x7))<<(8)) \ + | (((((a) == 64)?6UL:5UL) & (0x7))<<(11))) + +#define mwv207_timed_loop(tick, cond, timeout) \ + for ((tick) = jiffies; (cond) && time_before(jiffies, (tick) + msecs_to_jiffies(timeout));) + +struct mwv207_lli { + u64 sar; + u64 dar; + u32 ts; + u32 resv1; + u64 llp; + u64 ctl; + u32 sstat; + u32 dstat; + u64 llpstat; + u32 resv2; + u32 resv3; +}; + +#define to_dma_pipe(pipe) container_of(pipe, struct mwv207_pipe_dma, base) +struct mwv207_pipe_dma { + struct mwv207_pipe base; + int unit; + u32 alignment; + struct mwv207_lli *lli; + struct mwv207_lli *last_lli; + dma_addr_t lli_bus_addr; + int cur_idx; + + bool coherent; +}; + +struct mwv207_dma_cursor { + bool is_vram; + u64 line_pos; + u64 cur_pos; + + dma_addr_t *dma_address; + u64 nr_pages; + + u64 width; + u64 stride; +}; +static inline void pipe_dma_write(struct mwv207_pipe_dma *pipe, u32 reg, u32 value) +{ + pipe_write(&pipe->base, reg, value); +} + +static inline u32 pipe_dma_read(struct mwv207_pipe_dma *pipe, u32 reg) +{ + return pipe_read(&pipe->base, reg); +} + +static inline void pipe_dma_chan_write(struct mwv207_pipe_dma *pipe, int chan, u32 reg, u32 val) +{ + pipe_dma_write(pipe, chan * 0x200 + reg, val); +} + +static inline u32 pipe_dma_chan_read(struct mwv207_pipe_dma *pipe, int chan, u32 reg) +{ + return pipe_dma_read(pipe, chan * 0x200 + reg); +} + +static inline int pipe_dma_chan_done(struct mwv207_pipe_dma *pipe, int chan) +{ + u32 status; + + if (pipe->last_lli->ctl & (1UL << (63))) + return 0; + + status = pipe_dma_chan_read(pipe, chan, (0x188)); + return !!(status & (1 << (1))); +} + +static inline void pipe_dma_chan_clear(struct mwv207_pipe_dma *pipe, int chan) +{ + pipe_dma_chan_write(pipe, chan, (0x198), 1 << (1)); +} + +static inline int pipe_dma_stop(struct mwv207_pipe_dma *pipe) +{ + pr_info("%s TBD", __func__); + return 0; +} + +static void mwv207_pipe_dma_reset(struct mwv207_pipe *mpipe) +{ + pr_info("%s todo", __func__); +} + +static void pipe_dma_commit(struct mwv207_pipe_dma *pipe) +{ + unsigned long tick; + int chan = 0; + u64 llp; + + if (pipe->cur_idx == 0) + return; + + pipe->last_lli = &pipe->lli[pipe->cur_idx - 1]; + pipe->last_lli->ctl |= (1UL << (62)); + + mb(); + + pipe_dma_chan_write(pipe, chan, (0x120), + ((0x3)<<(2)) + |((0x3)<<(0))); + pipe_dma_chan_write(pipe, chan, (0x124), 0x3b8e0000); + + llp = pipe->lli_bus_addr + 0x800000000ULL; + pipe_dma_chan_write(pipe, chan, (0x128), llp & 0xFFFFFFFF); + pipe_dma_chan_write(pipe, chan, (0x12C), llp >> 32); + + pipe_dma_write(pipe, (0x10), + (1 << (1)) + |(1 << (0))); + + pipe_dma_write(pipe, (0x18), + ((1<cur_idx = 0; +} + +static void pipe_dma_fill_lli(struct mwv207_pipe_dma *pipe, u64 src, u64 dst, u64 len) +{ + struct mwv207_lli *lli; + + if (pipe->cur_idx >= DMA_MAX_LLI_COUNT) + pipe_dma_commit(pipe); + + lli = &pipe->lli[pipe->cur_idx]; + lli->sar = src; + lli->dar = dst; + + if (IS_ALIGNED(src, pipe->alignment) && + IS_ALIGNED(dst, pipe->alignment) && + IS_ALIGNED(len, pipe->alignment)) { + lli->ts = DMA_TS_ALIGNED(len, pipe->alignment); + lli->ctl = DMA_CTL_ALIGNED(pipe->alignment); + } else { + lli->ts = DMA_TS_NOT_ALIGNED(len); + lli->ctl = DMA_CTL_NOT_ALIGNED; + } + +#ifdef MWV207_DMA_DEBUG + { + pr_info("[dma]: lli[%04d] sar = 0x%010llx", pipe->cur_idx, lli->sar); + pr_info("[dma]: lli[%04d] dar = 0x%010llx", pipe->cur_idx, lli->dar); + pr_info("[dma]: lli[%04d] ts = 0x%08x", pipe->cur_idx, lli->ts); + pr_info("[dma]: lli[%04d] llp = 0x%010llx", pipe->cur_idx, lli->llp); + pr_info("[dma]: lli[%04d] ctl = 0x%010llx", pipe->cur_idx, lli->ctl); + } +#endif + pipe->cur_idx++; +} + +static void mwv207_cursor_init(struct mwv207_dma_cursor *cursor, struct mwv207_dma_loc *loc, + u64 width, u64 stride) +{ + cursor->width = width; + cursor->stride = stride; + cursor->is_vram = MWV207_DMA_IS_VRAM(loc->pg_nr_type); + if (cursor->is_vram) { + cursor->line_pos = loc->base + loc->offset; + cursor->cur_pos = cursor->line_pos; + cursor->dma_address = NULL; + cursor->nr_pages = MWV207_DMA_NR_PAGES(loc->pg_nr_type); + } else { + cursor->line_pos = loc->offset; + cursor->cur_pos = cursor->line_pos; + cursor->dma_address = (dma_addr_t *)loc->base; + cursor->nr_pages = MWV207_DMA_NR_PAGES(loc->pg_nr_type); + } +} + +static void mwv207_cursor_reset(struct mwv207_dma_cursor *cursor, struct mwv207_dma_loc *loc) +{ + if (cursor->is_vram) { + cursor->line_pos = loc->base + loc->offset; + cursor->cur_pos = cursor->line_pos; + } else { + cursor->line_pos = loc->offset; + cursor->cur_pos = cursor->line_pos; + } +} + +static inline u64 cursor_seg_len(struct mwv207_dma_cursor *cursor) +{ + u64 len = cursor->line_pos + cursor->width - cursor->cur_pos; + u64 offset, idx, contig_len; + + if (cursor->is_vram) + return len; + offset = cursor->cur_pos & (PAGE_SIZE - 1); + idx = cursor->cur_pos >> PAGE_SHIFT; + + contig_len = PAGE_SIZE - offset; + while (contig_len < len && + idx + 1 < cursor->nr_pages && + cursor->dma_address[idx] + PAGE_SIZE == cursor->dma_address[idx + 1]) { + contig_len += PAGE_SIZE; + idx++; + } + return min_t(u64, len, contig_len); +} + +static inline u64 cursor_axi(struct mwv207_dma_cursor *cursor) +{ + u64 idx, offset; + + if (cursor->is_vram) + return cursor->cur_pos; + + idx = cursor->cur_pos >> PAGE_SHIFT; + offset = cursor->cur_pos & (PAGE_SIZE - 1); + return 0x800000000ULL + cursor->dma_address[idx] + offset; +} + +static inline void cursor_advance(struct mwv207_dma_cursor *cursor, u64 len) +{ + BUG_ON(cursor->cur_pos + len > cursor->line_pos + cursor->width); + + if (cursor->cur_pos + len == cursor->line_pos + cursor->width) { + cursor->line_pos += cursor->stride; + cursor->cur_pos = cursor->line_pos; + } else + cursor->cur_pos += len; +} + +static void pipe_dma_flush(struct mwv207_pipe_dma *pipe, + struct mwv207_dma_cursor *cursor, u64 size) +{ + u64 remain, len; + + if (cursor->is_vram || pipe->coherent) + return; + + for (remain = size; remain > 0; remain -= len) { + len = min_t(u64, cursor_seg_len(cursor), remain); + dma_sync_single_for_device(pipe->base.jdev->dev, + cursor_axi(cursor) - 0x800000000ULL, + len, DMA_TO_DEVICE); + cursor_advance(cursor, len); + } +} + +static void pipe_dma_invalidate(struct mwv207_pipe_dma *pipe, + struct mwv207_dma_cursor *cursor, u64 size) +{ + u64 remain, len; + + if (cursor->is_vram || pipe->coherent) + return; + + for (remain = size; remain > 0; remain -= len) { + len = min_t(u64, cursor_seg_len(cursor), remain); + dma_sync_single_for_cpu(pipe->base.jdev->dev, + cursor_axi(cursor) - 0x800000000ULL, + len, DMA_FROM_DEVICE); + cursor_advance(cursor, len); + } +} + +static struct dma_fence *mwv207_pipe_dma_submit(struct mwv207_pipe *mpipe, + struct mwv207_job *mjob) +{ + struct mwv207_pipe_dma *pipe = to_dma_pipe(mpipe); + struct mwv207_dma_cmd *cmd = (struct mwv207_dma_cmd *)mjob->cmds; + struct mwv207_dma_cursor src, dst; + u64 remain, len; + + BUG_ON(mjob->cmd_size != sizeof(struct mwv207_dma_cmd)); + + mwv207_cursor_init(&src, &cmd->src, cmd->width, cmd->src.stride); + pipe_dma_flush(pipe, &src, cmd->width * cmd->height); + + mwv207_cursor_reset(&src, &cmd->src); + mwv207_cursor_init(&dst, &cmd->dst, cmd->width, cmd->dst.stride); + for (remain = cmd->width * cmd->height; remain > 0; remain -= len) { + len = min_t(u64, cursor_seg_len(&src), 0x400000); + len = min_t(u64, cursor_seg_len(&dst), len); + len = min_t(u64, len, remain); + + pipe_dma_fill_lli(pipe, cursor_axi(&src), cursor_axi(&dst), len); + + cursor_advance(&src, len); + cursor_advance(&dst, len); + } + pipe_dma_commit(pipe); + + mwv207_cursor_reset(&dst, &cmd->dst); + pipe_dma_invalidate(pipe, &dst, cmd->width * cmd->height); + + return dma_fence_get_stub(); +} + +static void mwv207_pipe_dma_dump_state(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_dma *pipe = to_dma_pipe(mpipe); + pr_info("%s todo: %p", __func__, pipe); +} + +static void mwv207_pipe_dma_destroy(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_dma *pipe = to_dma_pipe(mpipe); + + pr_info("%s todo: %p", __func__, pipe); +} + +struct mwv207_pipe *mwv207_pipe_dma_create(struct mwv207_device *jdev, int unit, u32 regbase, + const char *fname) +{ + struct mwv207_pipe_dma *pipe; + u64 llp; + int i; + + pipe = devm_kzalloc(jdev->dev, sizeof(struct mwv207_pipe_dma), GFP_KERNEL); + if (!pipe) + return NULL; + + pipe->base.jdev = jdev; + pipe->base.fname = fname; + pipe->base.regbase = jdev->mmio + regbase; + pipe->base.iosize = 16 * 1024; + pipe->base.submit = mwv207_pipe_dma_submit; + pipe->base.reset = mwv207_pipe_dma_reset; + pipe->base.destroy = mwv207_pipe_dma_destroy; + pipe->base.dump_state = mwv207_pipe_dma_dump_state; + + /* current TTM impl uses ttm_dma_populate which is coherent. + * set to false if ttm_populate_and_map_pages is used + * */ + pipe->coherent = true; + + pipe->alignment = jdev->lite ? 32 : 64; + pipe->unit = unit; + + pipe->lli = dmam_alloc_coherent(jdev->dev, + sizeof(struct mwv207_lli) * DMA_MAX_LLI_COUNT, + &pipe->lli_bus_addr, GFP_KERNEL); + if (!pipe->lli) + return NULL; + + memset(pipe->lli, 0, sizeof(struct mwv207_lli) * DMA_MAX_LLI_COUNT); + llp = pipe->lli_bus_addr + 0x800000000ULL; + for (i = 0; i < DMA_MAX_LLI_COUNT; i++) { + llp += sizeof(struct mwv207_lli); + pipe->lli[i].llp = llp; + } + pipe->cur_idx = 0; + + mwv207_pipe_dma_reset(&pipe->base); + return &pipe->base; +} diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_enc.c b/drivers/gpu/drm/mwv207/mwv207_pipe_enc.c new file mode 100644 index 0000000000000..61de0606bd68b --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_enc.c @@ -0,0 +1,260 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include "mwv207_pipe_codec_common.h" + +#define JPIPE_STATUS_ALL \ + (0x1000 | 0x200 | \ + 0x100 | 0x080 | \ + 0x040 | 0x020 | \ + 0x010 | 0x008 | 0x004) + +#define to_enc_pipe(pipe) container_of(pipe, struct mwv207_pipe_enc, base) +struct pipe_enc_fence; +struct mwv207_pipe_enc { + struct mwv207_pipe base; + + u64 fence_ctx; + u64 fence_seqno; + spinlock_t fence_lock; + + struct pipe_enc_fence *current_fence; + struct task_struct *poll_thread; + struct pipe_codec_sw_pp sw_pp; +}; + +#define to_pipe_enc_fence(f) container_of(f, struct pipe_enc_fence, base) +struct pipe_enc_fence { + struct dma_fence base; + struct mwv207_pipe_enc *pipe; + struct list_head node; +}; + +static inline void pipe_enc_write(struct mwv207_pipe_enc *pipe, u32 reg, u32 value) +{ + pipe_write(&pipe->base, reg, value); +} + +static inline u32 pipe_enc_read(struct mwv207_pipe_enc *pipe, u32 reg) +{ + return pipe_read(&pipe->base, reg); +} + +static const char *pipe_enc_fence_get_driver_name(struct dma_fence *fence) +{ + return "mwv207"; +} + +static const char *pipe_enc_fence_get_timeline_name(struct dma_fence *fence) +{ + return to_pipe_enc_fence(fence)->pipe->base.fname; +} + +static void pipe_enc_fence_release(struct dma_fence *fence) +{ + struct pipe_enc_fence *f = to_pipe_enc_fence(fence); + + kfree_rcu(f, base.rcu); +} + +static const struct dma_fence_ops pipe_enc_fence_ops = { + .get_driver_name = pipe_enc_fence_get_driver_name, + .get_timeline_name = pipe_enc_fence_get_timeline_name, + .release = pipe_enc_fence_release, +}; + +static void pipe_enc_stat_reset(struct mwv207_pipe_enc *pipe, u32 stat) +{ + if (stat & 0x20) + pipe_enc_write(pipe, 0x14, 0); + + pipe_enc_write(pipe, 0x4, stat & (~0x1FD)); +} + +static int pipe_enc_polling(void *priv) +{ + struct mwv207_pipe_enc *pipe = priv; + struct pipe_enc_fence *fence; + u32 stat; + + set_freezable(); + while (!kthread_should_stop()) { + usleep_range(1000, 1001); + try_to_freeze(); + + fence = smp_load_acquire(&pipe->current_fence); + if (fence == NULL) + continue; + + stat = pipe_enc_read(pipe, 0x4); + if (!(stat & JPIPE_STATUS_ALL)) + continue; + + pipe_codec_sw_pp_excute(&pipe->sw_pp, &pipe->base); + pipe_enc_stat_reset(pipe, stat); + + smp_store_release(&pipe->current_fence, NULL); + + dma_fence_signal(&fence->base); + dma_fence_put(&fence->base); + } + + return 0; +} + +static void mwv207_pipe_enc_reset(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_enc *pipe = to_enc_pipe(mpipe); + struct pipe_enc_fence *fence; + int i; + + pipe_codec_sw_pp_reset(&pipe->sw_pp); + + pipe_enc_write(pipe, 0x14, 0); + + for (i = 4; i < pipe->base.iosize; i += 0x4) + pipe_enc_write(pipe, i, 0); + + fence = smp_load_acquire(&pipe->current_fence); + if (fence) { + dma_fence_put(&fence->base); + + smp_store_release(&pipe->current_fence, NULL); + } +} + +static struct dma_fence *mwv207_pipe_enc_submit(struct mwv207_pipe *mpipe, + struct mwv207_job *mjob) +{ + struct mwv207_pipe_enc *pipe = to_enc_pipe(mpipe); + struct pipe_enc_fence *fence; + int i, nr, pos, cmdlen; + bool hw_run = 0; + u32 cmd, reg; + + /* sanity check, there should be no previous job. + * acquire pairs with release in polling thread + */ + fence = smp_load_acquire(&pipe->current_fence); + if (fence) + return ERR_PTR(-EBUSY); + + for (pos = 0; pos <= mjob->cmd_size - 4; pos += cmdlen) { + cmd = *(u32 *)(mjob->cmds + pos); + switch ((cmd >> 27) & 0x1f) { + case 0x01: + if ((cmd >> 26) & 0x1) + return ERR_PTR(-EINVAL); + reg = (cmd & 0xffff) * 4; + nr = (cmd >> 16) & 0x3ff; + if (nr == 0) + nr = 1024; + for (i = pos + 4; i <= pos + nr * 4 + && i <= mjob->cmd_size - 4 + && reg <= pipe->base.iosize - 4; + i += 4, reg += 4) { + if (reg == 20) { + pipe_enc_read(pipe, 0x14); + hw_run = (*(u32 *)(mjob->cmds + i)) & 0x1; + + mb(); + } + pipe_enc_write(pipe, reg, *(u32 *)(mjob->cmds + i)); + } + + if (i <= pos + nr * 4) + dev_dbg(mpipe->jdev->dev, + "mwv207: invalid enc register writes dropped"); + + cmdlen = ALIGN(4 + nr * 4, 8); + break; + case 0x02: + pipe_codec_sw_pp_submit(&pipe->sw_pp, &pipe->base, mjob, pos, &cmdlen); + break; + default: + + return ERR_PTR(-EINVAL); + } + } + + fence = kzalloc(sizeof(struct pipe_enc_fence), GFP_KERNEL); + if (!fence) + return ERR_PTR(-ENOMEM); + fence->pipe = pipe; + dma_fence_init(&fence->base, &pipe_enc_fence_ops, &pipe->fence_lock, + pipe->fence_ctx, ++pipe->fence_seqno); + + if (hw_run) { + dma_fence_get(&fence->base); + + smp_store_release(&pipe->current_fence, fence); + } else { + pipe_codec_sw_pp_excute(&pipe->sw_pp, &pipe->base); + dma_fence_signal(&fence->base); + } + + return &fence->base; +} + +static void mwv207_pipe_enc_dump_state(struct mwv207_pipe *mpipe) +{ + pr_info("%s irq state reg: 0x%08x", mpipe->fname, + pipe_read(mpipe, 0x4)); +} + +static void mwv207_pipe_enc_destroy(struct mwv207_pipe *mpipe) +{ + struct mwv207_pipe_enc *pipe = to_enc_pipe(mpipe); + + kthread_stop(pipe->poll_thread); +} + +struct mwv207_pipe *mwv207_pipe_enc_create(struct mwv207_device *jdev, u32 regbase, + const char *fname) +{ + struct mwv207_pipe_enc *pipe; + + if (jdev->lite) + return NULL; + + pipe = devm_kzalloc(jdev->dev, sizeof(struct mwv207_pipe_enc), GFP_KERNEL); + if (!pipe) + return NULL; + + pipe->base.jdev = jdev; + pipe->base.fname = fname; + pipe->base.regbase = jdev->mmio + regbase; + pipe->base.iosize = JPIPE_ENC_REG_NUM * 4; + pipe->base.submit = mwv207_pipe_enc_submit; + pipe->base.reset = mwv207_pipe_enc_reset; + pipe->base.destroy = mwv207_pipe_enc_destroy; + pipe->base.dump_state = mwv207_pipe_enc_dump_state; + + spin_lock_init(&pipe->fence_lock); + pipe->fence_ctx = dma_fence_context_alloc(1); + pipe->fence_seqno = 0; + + mwv207_pipe_enc_reset(&pipe->base); + + pipe->poll_thread = kthread_run(pipe_enc_polling, pipe, "enc_polling"); + if (IS_ERR(pipe->poll_thread)) + return NULL; + + return &pipe->base; +} diff --git a/drivers/gpu/drm/mwv207/mwv207_sched.c b/drivers/gpu/drm/mwv207/mwv207_sched.c new file mode 100644 index 0000000000000..a4e90351d49f5 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_sched.c @@ -0,0 +1,374 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include "mwv207.h" +#include "mwv207_sched.h" +#include "mwv207_bo.h" +#include "mwv207_ctx.h" +#include "mwv207_db.h" +#include "mwv207_devfreq.h" + +static int dump_hang_job = 1; +module_param(dump_hang_job, int, 0644); +MODULE_PARM_DESC(dump_hang_job, "dump time out job"); + +struct mwv207_job *mwv207_job_alloc(void) +{ + struct mwv207_job *mjob; + + mjob = kzalloc(sizeof(*mjob), GFP_KERNEL); + if (!mjob) + return NULL; + + kref_init(&mjob->refcount); + INIT_LIST_HEAD(&mjob->tvblist); + return mjob; +} + +static void mwv207_job_fini(struct kref *kref) +{ + struct mwv207_job *mjob = container_of(kref, struct mwv207_job, refcount); + struct mwv207_tvb *mtvb; + struct mwv207_submit_cmd_dat *dat; + int i; + + dma_fence_put(mjob->fence_in); + if (mjob->base.s_fence) + drm_sched_job_cleanup(&mjob->base); + + mwv207_for_each_mtvb(mtvb, mjob) { + for (i = 0; i < mtvb->nr_shared; ++i) + dma_fence_put(mtvb->shared[i]); + dma_fence_put(mtvb->excl); + kfree(mtvb->shared); + mwv207_bo_unref(to_jbo(mtvb->base.bo)); + } + + if (mjob->nr_cmd_dat) { + for (i = 0; i < mjob->nr_cmd_dat; ++i) { + dat = &mjob->cmd_dats[i]; + kvfree(dat->dat); + kvfree(dat->relocs); + mwv207_bo_unref(dat->jbo); + } + } + + if (mjob->ctx) + mwv207_ctx_put(mjob->ctx); + + kvfree(mjob->cmd_dats); + kvfree(mjob->mtvb); + kvfree(mjob->relocs); + kvfree(mjob->cmds); + + kfree(mjob); +} + +struct mwv207_job *mwv207_job_get(struct mwv207_job *mjob) +{ + kref_get(&mjob->refcount); + return mjob; +} + +void mwv207_job_put(struct mwv207_job *mjob) +{ + kref_put(&mjob->refcount, mwv207_job_fini); +} + +static void mwv207_sched_dump_job(struct mwv207_job *mjob) +{ + int lines, i; + u32 *cmd; + + cmd = (u32 *)mjob->cmds; + lines = (mjob->cmd_size / 4) & ~0x3; + for (i = 0; i < lines; i += 4) { + pr_info("0x%08x : %08x %08x %08x %08x", i, + cmd[i], cmd[i+1], cmd[i+2], cmd[i+3]); + } + + switch (mjob->cmd_size / 4 - lines) { + case 0: + return; + case 1: + pr_info("0x%08x : %08x", i, cmd[i]); + break; + case 2: + pr_info("0x%08x : %08x %08x", i, cmd[i], cmd[i+1]); + break; + case 3: + pr_info("0x%08x : %08x %08x %08x", i, + cmd[i], cmd[i+1], cmd[i+2]); + break; + default: + pr_err("should never happen"); + break; + + } +} + +static struct dma_fence *mwv207_sched_dependency(struct drm_sched_job *job, + struct drm_sched_entity *entity) +{ + struct mwv207_job *mjob = to_mwv207_job(job); + struct mwv207_tvb *mtvb; + struct dma_fence *fence; + int i; + + if (unlikely(mjob->fence_in)) { + fence = mjob->fence_in; + mjob->fence_in = NULL; + if (!dma_fence_is_signaled(fence)) + return fence; + dma_fence_put(fence); + } + + mwv207_for_each_mtvb(mtvb, mjob) { + if (mtvb->excl) { + fence = mtvb->excl; + mtvb->excl = NULL; + if (!dma_fence_is_signaled(fence)) + return fence; + dma_fence_put(fence); + } + + for (i = 0; i < mtvb->nr_shared; i++) { + if (!mtvb->shared[i]) + continue; + fence = mtvb->shared[i]; + mtvb->shared[i] = NULL; + if (!dma_fence_is_signaled(fence)) + return fence; + dma_fence_put(fence); + } + kfree(mtvb->shared); + mtvb->nr_shared = 0; + mtvb->shared = NULL; + } + + return NULL; +} + +static struct dma_fence *mwv207_sched_run_job(struct drm_sched_job *job) +{ + struct mwv207_sched *sched = to_mwv207_sched(job->sched); + struct dma_fence *fence; + + if (unlikely(job->s_fence->finished.error)) + return ERR_PTR(-ECANCELED); + + mwv207_devfreq_record_busy(sched->pipe); + + fence = sched->pipe->submit(sched->pipe, to_mwv207_job(job)); + if (IS_ERR_OR_NULL(fence)) + DRM_ERROR("%s: submit failed, errcode=%ld", + sched->pipe->fname, PTR_ERR(fence)); + return fence; +} + +static enum drm_gpu_sched_stat mwv207_sched_timedout_job(struct drm_sched_job *job) +{ + struct mwv207_sched *sched = to_mwv207_sched(job->sched); + struct mwv207_job *mjob = to_mwv207_job(job); + struct drm_sched_job *timedout_job; + + pr_info("mwv207: job from '%s' to '%s', timed out!", + mjob->comm, sched->pipe->fname); + + drm_sched_stop(&sched->base, job); + + if (dump_hang_job) { + sched->pipe->dump_state(sched->pipe); + mwv207_sched_dump_job(mjob); + } + + drm_sched_increase_karma(job); + + sched->pipe->reset(sched->pipe); + + list_for_each_entry(timedout_job, &sched->base.pending_list, list) + mwv207_devfreq_record_idle(sched->pipe); + + drm_sched_resubmit_jobs(&sched->base); + + drm_sched_start(&sched->base, true); + + return DRM_GPU_SCHED_STAT_NOMINAL; +} + +static void mwv207_sched_free_job(struct drm_sched_job *job) +{ + struct mwv207_job *mjob = to_mwv207_job(job); + + mwv207_job_put(mjob); +} + +static const struct drm_sched_backend_ops mwv207_sched_ops = { + .prepare_job = mwv207_sched_dependency, + .run_job = mwv207_sched_run_job, + .timedout_job = mwv207_sched_timedout_job, + .free_job = mwv207_sched_free_job, +}; + +static struct drm_gpu_scheduler *mwv207_sched_create(struct mwv207_device *jdev, + unsigned int hw_submission, unsigned int hang_limit, long timeout, + struct mwv207_pipe *pipe) +{ + struct mwv207_sched *sched; + int ret; + + if (pipe == NULL) + return NULL; + + sched = devm_kzalloc(jdev->dev, sizeof(struct mwv207_sched), GFP_KERNEL); + if (!sched) + goto err; + + sched->pipe = pipe; + + ret = drm_sched_init(&sched->base, &mwv207_sched_ops, hw_submission, hang_limit, + msecs_to_jiffies(timeout), + NULL, NULL, + pipe->fname, jdev->dev); + if (ret) + goto err; + + return &sched->base; +err: + sched->pipe->destroy(sched->pipe); + return NULL; +} + +static void mwv207_sched_destroy(struct drm_gpu_scheduler *sched) +{ + struct mwv207_sched *msched = to_mwv207_sched(sched); + + if (sched == NULL) + return; + + drm_sched_fini(sched); + msched->pipe->destroy(msched->pipe); +} + +int mwv207_sched_suspend(struct mwv207_device *jdev) +{ + struct mwv207_sched *sched; + int i, ret; + + for (i = 0; i < 6; i++) { + if (!jdev->sched[i]) + continue; + sched = to_mwv207_sched(jdev->sched[i]); + + if (atomic_read(&jdev->sched[i]->hw_rq_count)) + return -EBUSY; + + ret = mwv207_devfreq_suspend(sched->pipe); + if (ret) + return ret; + } + + return 0; +} + +int mwv207_sched_resume(struct mwv207_device *jdev) +{ + struct mwv207_sched *sched; + int i, ret; + + for (i = 0; i < 6; i++) { + if (!jdev->sched[i]) + continue; + sched = to_mwv207_sched(jdev->sched[i]); + + ret = mwv207_devfreq_resume(sched->pipe); + if (ret) + dev_err(sched->pipe->dev, "devfreq resume failed: %d", ret); + + sched->pipe->reset(sched->pipe); + } + + return 0; +} + +int mwv207_sched_init(struct mwv207_device *jdev) +{ + struct mwv207_pipe *pipe; + + jdev->sched_3d = &jdev->sched[0]; + jdev->sched_dec = &jdev->sched_3d[2]; + jdev->sched_enc = &jdev->sched_dec[1]; + jdev->sched_2d = &jdev->sched_enc[1]; + jdev->sched_dma = &jdev->sched_2d[1]; + + pipe = mwv207_pipe_3d_create(jdev, 0, 0x900000, "mwv207_3d0"); + jdev->sched_3d[0] = mwv207_sched_create(jdev, 30, 5, 2000, pipe); + if (jdev->sched_3d[0] == NULL) + return -ENODEV; + + pipe = mwv207_pipe_3d_create(jdev, 1, 0x910000, "mwv207_3d1"); + jdev->sched_3d[1] = mwv207_sched_create(jdev, 30, 5, 2000, pipe); + + pipe = mwv207_pipe_dec_create(jdev, 0, 0x930000, "mwv207_dec0"); + jdev->sched_dec[0] = mwv207_sched_create(jdev, 1, 5, 2000, pipe); + if (jdev->sched_dec[0] == NULL) + goto err; + + pipe = mwv207_pipe_dec_create(jdev, 1, 0x940000, "mwv207_dec1"); + jdev->sched_dec[1] = mwv207_sched_create(jdev, 1, 5, 2000, pipe); + + pipe = mwv207_pipe_enc_create(jdev, 0x920000, "mwv207_enc0"); + jdev->sched_enc[0] = mwv207_sched_create(jdev, 1, 5, 2000, pipe); + + pipe = mwv207_pipe_2d_create(jdev, 0, 0x968000, "mwv207_2d0"); + jdev->sched_2d[0] = mwv207_sched_create(jdev, 30, 5, 2000, pipe); + if (jdev->sched_2d[0] == NULL) + goto err; + + pipe = mwv207_pipe_dma_create(jdev, 0, 0x9d0000, "mwv207_dma0"); + jdev->sched_dma[0] = mwv207_sched_create(jdev, 1, 5, 2000, pipe); + if (jdev->sched_dma[0] == NULL) + goto err; + + jdev->nr_3d = jdev->sched_3d[1] ? 2 : 1; + jdev->nr_enc = jdev->sched_enc[0] ? 1 : 0; + jdev->nr_dec = jdev->sched_dec[1] ? 2 : 1; + jdev->nr_2d = 1; + jdev->nr_dma = 1; + + mwv207_db_add(jdev, DRM_MWV207_ACTIVE_3D_NR, jdev->nr_3d); + mwv207_db_add(jdev, DRM_MWV207_ACTIVE_ENC_NR, jdev->nr_enc); + mwv207_db_add(jdev, DRM_MWV207_ACTIVE_DEC_NR, jdev->nr_dec); + mwv207_db_add(jdev, DRM_MWV207_ACTIVE_2D_NR, jdev->nr_2d); + mwv207_db_add(jdev, DRM_MWV207_ACTIVE_DMA_NR, jdev->nr_dma); + + return 0; +err: + mwv207_sched_fini(jdev); + return -ENODEV; +} + +void mwv207_sched_fini(struct mwv207_device *jdev) +{ + int i; + + for (i = 0; i < 6; ++i) { + if (!jdev->sched[i]) + continue; + mwv207_sched_destroy(jdev->sched[i]); + } +} diff --git a/drivers/gpu/drm/mwv207/mwv207_sched.h b/drivers/gpu/drm/mwv207/mwv207_sched.h new file mode 100644 index 0000000000000..52322eea959db --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_sched.h @@ -0,0 +1,149 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_SCHED_H_TGQQ2LXS +#define MWV207_SCHED_H_TGQQ2LXS +#include +#include +#include +#include "mwv207.h" +#include "mwv207_bo.h" + +struct mwv207_submit_cmd_dat { + int nr_relocs; + int dat_size; + struct drm_mwv207_submit_reloc *relocs; + char *dat; + struct mwv207_bo *jbo; +}; + +struct mwv207_tvb { + struct ttm_validate_buffer base; + struct dma_fence *excl; + unsigned int nr_shared; + struct dma_fence **shared; +}; + +#define to_mwv207_job(job) container_of(job, struct mwv207_job, base) +struct mwv207_job { + struct drm_sched_job base; + + struct list_head tvblist; + struct mwv207_tvb *mtvb; + int nr_bos; + + struct drm_mwv207_submit_reloc *relocs; + int nr_relocs; + + struct mwv207_submit_cmd_dat *cmd_dats; + int nr_cmd_dat; + + char *cmds; + int cmd_size; + + struct dma_fence *fence_in; + + struct mwv207_ctx *ctx; + struct drm_sched_entity *engine_entity; + + struct kref refcount; + + bool is_dma; + + char comm[TASK_COMM_LEN]; +}; +#define mwv207_for_each_mtvb(mtvb, mjob) list_for_each_entry(mtvb, &(mjob)->tvblist, base.head) + +struct mwv207_job *mwv207_job_alloc(void); +struct mwv207_job *mwv207_job_get(struct mwv207_job *mjob); +void mwv207_job_put(struct mwv207_job *mjob); + +static inline struct mwv207_bo *mwv207_job_bo(struct mwv207_job *mjob, int idx) +{ + BUG_ON(idx >= mjob->nr_bos + mjob->nr_cmd_dat); + return to_jbo(mjob->mtvb[idx].base.bo); +} + +struct mwv207_dma_loc { + u64 base; + u64 pg_nr_type; + u64 offset; + u64 stride; +}; +#define MWV207_DMA_NR_PAGES_VRAM(nr) ((nr) | (0ULL << 63)) +#define MWV207_DMA_NR_PAGES_RAM(nr) ((nr) | (1ULL << 63)) +#define MWV207_DMA_NR_PAGES(nr) ((nr) & ~(1ULL << 63)) +#define MWV207_DMA_IS_VRAM(nr) (((nr) >> 63) == 0) + +struct mwv207_dma_cmd { + struct mwv207_dma_loc src; + struct mwv207_dma_loc dst; + u64 width; + u64 height; +}; + +struct devfreq; +#define to_mwv207_sched(sched) container_of(sched, struct mwv207_sched, base) +struct mwv207_pipe { + struct dma_fence* (*submit)(struct mwv207_pipe *pipe, struct mwv207_job *job); + void (*reset)(struct mwv207_pipe *pipe); + void (*destroy)(struct mwv207_pipe *pipe); + void (*dump_state)(struct mwv207_pipe *pipe); + struct mwv207_device *jdev; + const char *fname; + int iosize; + void __iomem *regbase; + + struct device *dev; + struct devfreq *devfreq; + spinlock_t devfreq_lock; + int busy_count; + int pll_id; + ktime_t busy_time; + ktime_t idle_time; + ktime_t time_last_update; +}; + +struct mwv207_sched { + struct drm_gpu_scheduler base; + struct mwv207_pipe *pipe; +}; + +static inline void pipe_write(struct mwv207_pipe *pipe, u32 reg, u32 value) +{ + writel_relaxed(value, pipe->regbase + reg); +} + +static inline u32 pipe_read(struct mwv207_pipe *pipe, u32 reg) +{ + return readl(pipe->regbase + reg); +} + +int mwv207_sched_init(struct mwv207_device *jdev); +void mwv207_sched_fini(struct mwv207_device *jdev); +int mwv207_sched_suspend(struct mwv207_device *jdev); +int mwv207_sched_resume(struct mwv207_device *jdev); + +struct mwv207_pipe *mwv207_pipe_3d_create(struct mwv207_device *jdev, int unit, + u32 regbase, const char *fname); +struct mwv207_pipe *mwv207_pipe_2d_create(struct mwv207_device *jdev, int unit, + u32 regbase, const char *fname); +struct mwv207_pipe *mwv207_pipe_dec_create(struct mwv207_device *jdev, int unit, + u32 regbase, const char *fname); +struct mwv207_pipe *mwv207_pipe_enc_create(struct mwv207_device *jdev, + u32 regbase, const char *fname); +struct mwv207_pipe *mwv207_pipe_dma_create(struct mwv207_device *jdev, int unit, + u32 regbase, const char *fname); +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_submit.c b/drivers/gpu/drm/mwv207/mwv207_submit.c new file mode 100644 index 0000000000000..a2a150292e802 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_submit.c @@ -0,0 +1,631 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include "mwv207_drm.h" +#include "mwv207_submit.h" +#include "mwv207_sched.h" +#include "mwv207_ctx.h" +#include "mwv207_bo.h" +#include "mwv207_gem.h" + +static inline int mwv207_submit_select_engine(struct drm_device *dev, struct mwv207_job *mjob, + struct drm_mwv207_submit *args) +{ + struct mwv207_device *jdev = ddev_to_jdev(dev); + struct drm_sched_entity **entity_array; + int id_max, id; + + switch (args->engine_type) { + case 0: + entity_array = &mjob->ctx->entity_3d[0]; + id_max = jdev->nr_3d; + break; + case 1: + entity_array = &mjob->ctx->entity_dec[0]; + id_max = jdev->nr_dec; + break; + case 2: + entity_array = &mjob->ctx->entity_enc[0]; + id_max = jdev->nr_enc; + break; + case 3: + entity_array = &mjob->ctx->entity_2d[0]; + id_max = jdev->nr_2d; + break; + case 4: + entity_array = &mjob->ctx->entity_dma[0]; + id_max = jdev->nr_dma; + mjob->is_dma = true; + break; + + default: + id_max = 0; + } + if (id_max <= 0) + return -ENODEV; + + if (args->engine_id == 0xffffffff) + id = id_max; + else if (args->engine_id >= 0 && args->engine_id < id_max) + id = args->engine_id; + else + return -ENODEV; + mjob->engine_entity = entity_array[id]; + return 0; +} + +static int mwv207_submit_init_job_reloc(struct mwv207_job *mjob, struct drm_mwv207_submit *args) +{ + if ((int)args->nr_relocs < 0) + return -EINVAL; + mjob->nr_relocs = (int)args->nr_relocs; + if (mjob->nr_relocs == 0) + return 0; + if (args->relocs == 0) + return -EINVAL; + + mjob->relocs = kvmalloc_array(mjob->nr_relocs, + sizeof(struct drm_mwv207_submit_reloc), GFP_KERNEL); + + return copy_from_user(mjob->relocs, (void __user *)args->relocs, + sizeof(struct drm_mwv207_submit_reloc) * mjob->nr_relocs); +} + +static int mwv207_submit_init_job_bo(struct mwv207_job *mjob, + struct drm_mwv207_submit *args, struct drm_file *filp) +{ + struct drm_mwv207_bo_acc ubos_stack[32]; + struct drm_mwv207_bo_acc *ubos = ubos_stack; + struct ttm_validate_buffer *tvb; + struct drm_gem_object *gobj; + int ret = 0, i; + + if ((int)args->nr_bos < 0) + return -EINVAL; + + mjob->nr_bos = (int)args->nr_bos; + if (mjob->nr_bos == 0) { + if (mjob->nr_cmd_dat) + goto setup_mtvb; + return 0; + } + + if (!args->bos) + return -EINVAL; + + if (mjob->nr_bos > 32) + ubos = kvmalloc_array(mjob->nr_bos, sizeof(*ubos), GFP_KERNEL); + if (unlikely(!ubos)) + return -ENOMEM; + + ret = copy_from_user(ubos, (void __user *)args->bos, + mjob->nr_bos * sizeof(*ubos)); + if (ret) + goto out; +setup_mtvb: + mjob->mtvb = kvcalloc(mjob->nr_bos + mjob->nr_cmd_dat, + sizeof(struct mwv207_tvb), GFP_KERNEL); + if (!mjob->mtvb) { + ret = -ENOMEM; + goto out; + } + spin_lock(&filp->table_lock); + for (i = 0; i < mjob->nr_bos; i++) { + tvb = &mjob->mtvb[i].base; + tvb->num_shared = (ubos[i].flags & 0x00000001) ? 0 : 1; + gobj = idr_find(&filp->object_idr, ubos[i].handle); + if (unlikely(!gobj)) { + spin_unlock(&filp->table_lock); + ret = -ENOENT; + goto out; + } + tvb->bo = &mwv207_bo_ref(mwv207_bo_from_gem(gobj))->tbo; + list_add_tail(&tvb->head, &mjob->tvblist); + } + spin_unlock(&filp->table_lock); + for (i = 0; i < mjob->nr_cmd_dat; i++) { + tvb = &mjob->mtvb[mjob->nr_bos + i].base; + tvb->num_shared = 0; + tvb->bo = &mwv207_bo_ref(mjob->cmd_dats[i].jbo)->tbo; + list_add_tail(&tvb->head, &mjob->tvblist); + } +out: + if (unlikely(ubos != ubos_stack)) + kvfree(ubos); + + return ret; +} + +static int mwv207_submit_init_job_cmd(struct mwv207_job *mjob, struct drm_mwv207_submit *args) +{ + mjob->cmd_size = args->cmd_size; + if ((int)mjob->cmd_size <= 0) + return -EINVAL; + + if (mjob->cmd_size > 0x20000) + return -E2BIG; + + if (args->cmds == 0) + return -EINVAL; + + if (mjob->is_dma) + mjob->cmds = kmalloc(args->cmd_size, GFP_KERNEL); + else + mjob->cmds = kvmalloc(args->cmd_size, GFP_KERNEL); + if (!mjob->cmds) + return -ENOMEM; + + return copy_from_user(mjob->cmds, (void __user *)args->cmds, mjob->cmd_size); +} + +static int mwv207_submit_init_job_cmd_dat(struct drm_device *dev, + struct mwv207_job *mjob, struct drm_mwv207_submit *args) +{ + struct drm_mwv207_submit_cmd_dat udats_stack[8]; + struct drm_mwv207_submit_cmd_dat *udats = udats_stack; + struct mwv207_device *jdev = ddev_to_jdev(dev); + struct mwv207_submit_cmd_dat *mdat; + int ret, i; + + if ((int)args->nr_cmd_dat < 0) + return -EINVAL; + mjob->nr_cmd_dat = args->nr_cmd_dat; + if (mjob->nr_cmd_dat == 0) + return 0; + if (args->cmd_dats == 0) + return -EINVAL; + if (mjob->nr_cmd_dat > 8) + udats = kvmalloc_array(mjob->nr_cmd_dat, + sizeof(struct drm_mwv207_submit_cmd_dat), GFP_KERNEL); + + if (unlikely(!udats)) + return -ENOMEM; + + mjob->cmd_dats = kvcalloc(mjob->nr_cmd_dat, + sizeof(struct mwv207_submit_cmd_dat), GFP_KERNEL); + if (!mjob->cmd_dats) { + ret = -ENOMEM; + goto out; + } + + ret = copy_from_user(udats, (void __user *)args->cmd_dats, + mjob->nr_cmd_dat * sizeof(struct drm_mwv207_submit_cmd_dat)); + if (ret) + goto out; + + for (i = 0; i < mjob->nr_cmd_dat; ++i) { + if ((int)udats[i].nr_relocs < 0) { + ret = -EINVAL; + goto out; + } + if ((int)udats[i].dat_size <= 0 || udats[i].dat == 0) { + ret = -EINVAL; + goto out; + } + if (udats[i].nr_relocs != 0 && udats[i].relocs == 0) { + ret = -EINVAL; + goto out; + } + if (udats[i].dat_size > 0x20000) { + ret = -E2BIG; + goto out; + } + mdat = &mjob->cmd_dats[i]; + mdat->nr_relocs = udats[i].nr_relocs; + if (mdat->nr_relocs) { + mdat->relocs = kvmalloc_array(mdat->nr_relocs, + sizeof(struct drm_mwv207_submit_reloc), GFP_KERNEL); + if (!mdat->relocs) { + ret = -ENOMEM; + goto out; + } + ret = copy_from_user(mdat->relocs, (void __user *)udats[i].relocs, + mdat->nr_relocs * sizeof(struct drm_mwv207_submit_reloc)); + if (ret) + goto out; + } + mdat->dat_size = udats[i].dat_size; + mdat->dat = kvmalloc(mdat->dat_size, GFP_KERNEL); + if (!mdat->dat) { + ret = -ENOMEM; + goto out; + } + ret = copy_from_user(mdat->dat, (void __user *)udats[i].dat, mdat->dat_size); + if (ret) + goto out; + ret = mwv207_bo_create(jdev, mdat->dat_size, 0x1000, ttm_bo_type_device, + 0x2, (1<<0), + &mdat->jbo); + if (ret) + goto out; + } + +out: + if (unlikely(udats != udats_stack)) + kvfree(udats); + return ret; +} + +static int mwv207_submit_init_job(struct drm_device *dev, struct mwv207_job *mjob, + struct drm_mwv207_submit *args, struct drm_file *filp) +{ + int ret; + + get_task_comm(mjob->comm, current); + + mjob->ctx = mwv207_ctx_lookup(dev, filp, args->ctx); + if (!mjob->ctx) + return -ENOENT; + ret = mwv207_submit_select_engine(dev, mjob, args); + if (ret) + return ret; + ret = mwv207_submit_init_job_cmd_dat(dev, mjob, args); + if (ret) + return ret; + ret = mwv207_submit_init_job_bo(mjob, args, filp); + if (ret) + return ret; + ret = mwv207_submit_init_job_reloc(mjob, args); + if (ret) + return ret; + ret = mwv207_submit_init_job_cmd(mjob, args); + if (ret) + return ret; + + return 0; +} + +static struct mwv207_job *mwv207_submit_init(struct drm_device *dev, struct drm_file *filp, + struct drm_mwv207_submit *args) +{ + struct mwv207_job *mjob; + int ret; + + mjob = mwv207_job_alloc(); + if (!mjob) + return ERR_PTR(-ENOMEM); + + ret = mwv207_submit_init_job(dev, mjob, args, filp); + if (ret) + goto err; + + return mjob; +err: + mwv207_job_put(mjob); + return ERR_PTR(ret); +} + +static int mwv207_submit_patch_entry(struct mwv207_job *mjob, + struct drm_mwv207_submit_reloc *reloc, + struct mwv207_bo *jbo, char *dat, u32 dat_size) +{ + u64 addr, bo_size; + + if (reloc->pad) + return -EINVAL; + + bo_size = mwv207_bo_size(jbo); + if (reloc->bo_offset >= bo_size) + return -ERANGE; + addr = mwv207_bo_gpu_phys(jbo) + reloc->bo_offset; + + switch (reloc->type & 0xffff) { + case 0: + if (reloc->cmd_offset + 8 > dat_size) + return -ERANGE; + *(u64 *)(dat + reloc->cmd_offset) = addr; + break; + case 1: + if (reloc->cmd_offset + 4 > dat_size) + return -ERANGE; + *(u32 *)(dat + reloc->cmd_offset) = addr & 0xffffffff; + break; + case 2: + if (reloc->cmd_offset + 4 > dat_size) + return -ERANGE; + *(u32 *)(dat + reloc->cmd_offset) = addr >> 32; + break; + case 3: + if (reloc->cmd_offset + 2 > dat_size) + return -ERANGE; + *(u16 *)(dat + reloc->cmd_offset) = (addr >> 16) & 0xffff; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mwv207_submit_patch_single(struct mwv207_job *mjob, + struct drm_mwv207_submit_reloc *reloc, char *dat, u32 dat_size) +{ + struct mwv207_bo *jbo; + + if (reloc->type & 0x80000000) { + if (reloc->idx >= mjob->nr_cmd_dat) + return -EINVAL; + jbo = mwv207_job_bo(mjob, reloc->idx + mjob->nr_bos); + } else { + if (reloc->idx >= mjob->nr_bos) + return -EINVAL; + jbo = mwv207_job_bo(mjob, reloc->idx); + } + + return mwv207_submit_patch_entry(mjob, reloc, jbo, dat, dat_size); +} + +static int mwv207_submit_patch_dma_entry(struct mwv207_job *mjob, + struct mwv207_dma_loc *loc, struct mwv207_bo *jbo, + u64 width, u64 height) +{ + struct mwv207_ttm_tt *gtt; + + if (loc->stride == 0 || loc->pg_nr_type != 0 || width == 0 || height == 0) + return -EINVAL; + + switch (jbo->tbo.resource->mem_type) + { + case TTM_PL_VRAM: + loc->base = mwv207_bo_gpu_phys(jbo); + loc->pg_nr_type = MWV207_DMA_NR_PAGES_VRAM(PFN_UP(jbo->tbo.resource->size)); + break; + case TTM_PL_TT: + gtt = to_gtt(jbo->tbo.ttm); + loc->base = (u64)gtt->ttm.dma_address; + loc->pg_nr_type = MWV207_DMA_NR_PAGES_RAM(PFN_UP(jbo->tbo.resource->size)); + break; + default: + BUG_ON(1); + break; + } + + if (loc->offset >= mwv207_bo_size(jbo)) + return -ERANGE; + if (loc->offset + loc->stride * (height - 1) + width <= loc->offset) + return -ERANGE; + if (loc->offset + loc->stride * (height - 1) + width > mwv207_bo_size(jbo)) + return -ERANGE; + + if ((loc->offset % loc->stride) + width > loc->stride) + return -ERANGE; + + return 0; +} + +static int mwv207_submit_patch_dma(struct drm_device *dev, struct mwv207_job *mjob) +{ + struct mwv207_bo *src_bo, *dst_bo, *jbo; + struct drm_mwv207_submit_reloc *reloc; + struct mwv207_dma_cmd *dma_cmd; + int i, ret; + + if (mjob->nr_bos != 2 || mjob->nr_relocs != 2 || mjob->nr_cmd_dat) + return -EINVAL; + if (mjob->cmd_size != sizeof(struct mwv207_dma_cmd)) + return -EINVAL; + + src_bo = dst_bo = NULL; + for (i = 0; i < mjob->nr_relocs; i++) { + reloc = &mjob->relocs[i]; + if (reloc->type & 0x80000000) + return -EINVAL; + if ((reloc->type & 0xffff) != 0) + return -EINVAL; + if (reloc->bo_offset) + return -EINVAL; + jbo = mwv207_job_bo(mjob, reloc->idx); + if (reloc->cmd_offset == offsetof(struct mwv207_dma_cmd, src.base)) + src_bo = jbo; + else if (reloc->cmd_offset == offsetof(struct mwv207_dma_cmd, dst.base)) + dst_bo = jbo; + } + if (!src_bo || !dst_bo || src_bo == dst_bo) + return -EINVAL; + + dma_cmd = (struct mwv207_dma_cmd *)mjob->cmds; + ret = mwv207_submit_patch_dma_entry(mjob, &dma_cmd->src, src_bo, + dma_cmd->width, dma_cmd->height); + if (ret) + return ret; + return mwv207_submit_patch_dma_entry(mjob, &dma_cmd->dst, dst_bo, + dma_cmd->width, dma_cmd->height); +} + +static int mwv207_submit_patch(struct drm_device *dev, struct mwv207_job *mjob) +{ + struct mwv207_bo *jbo; + struct ttm_operation_ctx ctx = { + .interruptible = true, + .no_wait_gpu = false + }; + int i, j, ret; + void *ptr; + + if (mjob->is_dma) + return mwv207_submit_patch_dma(dev, mjob); + + for (i = 0; i < mjob->nr_relocs; ++i) { + ret = mwv207_submit_patch_single(mjob, &mjob->relocs[i], + mjob->cmds, mjob->cmd_size); + if (ret) + return ret; + } + + for (i = 0; i < mjob->nr_cmd_dat; ++i) { + if (mjob->cmd_dats[i].nr_relocs) { + for (j = 0; j < mjob->cmd_dats[i].nr_relocs; j++) { + ret = mwv207_submit_patch_single(mjob, &mjob->cmd_dats[i].relocs[j], + mjob->cmd_dats[i].dat, mjob->cmd_dats[i].dat_size); + if (ret) + return ret; + } + } + + jbo = mjob->cmd_dats[i].jbo; + + ret = ttm_bo_wait_ctx(&jbo->tbo, &ctx); + if (ret) + return ret; + + ret = mwv207_bo_kmap_reserved(jbo, &ptr); + if (ret) + return ret; + memcpy_toio(ptr, mjob->cmd_dats[i].dat, mjob->cmd_dats[i].dat_size); + mwv207_bo_kunmap_reserved(jbo); + } + + return 0; +} + +static int mwv207_submit_validate(struct drm_device *dev, struct mwv207_job *mjob) +{ + struct ttm_operation_ctx ctx = { + .interruptible = true, + .no_wait_gpu = false + }; + struct mwv207_bo *jbo; + int i, ret; + + for (i = 0; i < mjob->nr_bos + mjob->nr_cmd_dat; ++i) { + jbo = mwv207_job_bo(mjob, i); + + if (jbo->tbo.pin_count) + continue; + + if (mjob->is_dma) { + if (jbo->tbo.resource->mem_type != TTM_PL_SYSTEM) + continue; + + mwv207_bo_placement_from_domain(jbo, 0x4, 0); + } else { + + jbo->flags |= (1<<2); + if (i >= mjob->nr_bos) + jbo->flags |= (1<<0); + mwv207_bo_placement_from_domain(jbo, 0x2, 0); + } + + ret = ttm_bo_validate(&jbo->tbo, &jbo->placement, &ctx); + if (ret) + return ret; + } + + return 0; +} + +static int mwv207_submit_attach_dependency(struct mwv207_job *mjob, struct drm_mwv207_submit *args) +{ + struct drm_gem_object *gobj; + struct mwv207_tvb *mtvb; + bool write; + int ret; + + mwv207_for_each_mtvb(mtvb, mjob) { + gobj = &mtvb->base.bo->base; + write = mtvb->base.num_shared == 0 ? 1 : 0; + + ret = drm_sched_job_add_implicit_dependencies(&mjob->base, gobj, write); + if (ret) + return ret; + } + + if (unlikely(args->flags & 0x00000001)) { + mjob->fence_in = sync_file_get_fence(args->fence_fd); + if (!mjob->fence_in) + return -EINVAL; + } + + return 0; +} + +static int mwv207_submit_commit(struct mwv207_job *mjob, + struct drm_mwv207_submit *args, struct dma_fence **fence) +{ + if (args->flags & 0x00000002) { + struct sync_file *sync_file; + int fd; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return fd; + sync_file = sync_file_create(&mjob->base.s_fence->finished); + if (!sync_file) { + put_unused_fd(fd); + return -ENOMEM; + } + fd_install(fd, sync_file->file); + args->fence_fd = fd; + } + + *fence = &mjob->base.s_fence->finished; + + mwv207_job_get(mjob); + drm_sched_entity_push_job(&mjob->base); + return 0; +} + +int mwv207_submit_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_mwv207_submit *args = data; + struct ww_acquire_ctx ticket; + struct mwv207_job *mjob; + struct dma_fence *fence = NULL; + int ret; + + if (args->padding || (args->flags & ~0x00000003)) + return -EINVAL; + if (args->cmd_size == 0 || args->cmds == 0) + return -EINVAL; + + mjob = mwv207_submit_init(dev, filp, args); + if (IS_ERR(mjob)) + return PTR_ERR(mjob); + + ret = drm_sched_job_init(&mjob->base, mjob->engine_entity, mjob->ctx); + if (ret) + return ret; + + drm_sched_job_arm(&mjob->base); + + ret = ttm_eu_reserve_buffers(&ticket, &mjob->tvblist, true, NULL); + if (ret) + goto put_job; + ret = mwv207_submit_validate(dev, mjob); + if (ret) + goto unreserve; + ret = mwv207_submit_patch(dev, mjob); + if (ret) + goto unreserve; + ret = mwv207_submit_attach_dependency(mjob, args); + if (ret) + goto unreserve; + ret = mwv207_submit_commit(mjob, args, &fence); + if (ret) + goto unreserve; +unreserve: + if (likely(fence)) + ttm_eu_fence_buffer_objects(&ticket, &mjob->tvblist, fence); + else + ttm_eu_backoff_reservation(&ticket, &mjob->tvblist); +put_job: + mwv207_job_put(mjob); + + return ret; +} diff --git a/drivers/gpu/drm/mwv207/mwv207_submit.h b/drivers/gpu/drm/mwv207/mwv207_submit.h new file mode 100644 index 0000000000000..194fceafa88ee --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_submit.h @@ -0,0 +1,23 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#ifndef MWV207_SUBMIT_H_2S1GH9LD +#define MWV207_SUBMIT_H_2S1GH9LD + +int mwv207_submit_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_ttm.c b/drivers/gpu/drm/mwv207/mwv207_ttm.c new file mode 100644 index 0000000000000..bb6d50590d851 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_ttm.c @@ -0,0 +1,502 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mwv207.h" +#include "mwv207_bo.h" +#include "mwv207_drm.h" +#include "mwv207_sched.h" + +static int mwv207_mem_visible(struct mwv207_device *jdev, struct ttm_resource *mem) +{ + if (mem->mem_type == TTM_PL_SYSTEM || + mem->mem_type == TTM_PL_TT) + return true; + if (mem->mem_type != TTM_PL_VRAM) + return false; + + return (((mem->start << PAGE_SHIFT) + mem->size) < jdev->visible_vram_size); +} + +static void mwv207_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + struct mwv207_device *jdev; + struct mwv207_bo *jbo; + static const struct ttm_place placements = { + .fpfn = 0, + .lpfn = 0, + .flags = 0, + .mem_type = TTM_PL_SYSTEM + }; + + if (!mwv207_ttm_bo_is_mwv207_bo(bo)) { + placement->placement = &placements; + placement->busy_placement = &placements; + placement->num_placement = 1; + placement->num_busy_placement = 1; + return; + } + + jbo = to_jbo(bo); + jdev = ddev_to_jdev(jbo->tbo.base.dev); + + if (bo->resource->mem_type == TTM_PL_VRAM) { + if (jdev->visible_vram_size != jdev->vram_size + && mwv207_mem_visible(jdev, bo->resource)) { + jbo->flags &= ~((1<<0) | (1<<2)); + mwv207_bo_placement_from_domain(jbo, + 0x2 | 0x4, + false); + BUG_ON(!(jbo->placements[0].mem_type == TTM_PL_VRAM)); + BUG_ON(!(jbo->placements[1].mem_type == TTM_PL_TT)); + jbo->placements[0].fpfn = jdev->visible_vram_size >> PAGE_SHIFT; + jbo->placements[0].lpfn = 0; + jbo->placement.placement = &jbo->placements[0]; + jbo->placement.busy_placement = &jbo->placements[1]; + jbo->placement.num_placement = 1; + jbo->placement.num_busy_placement = 1; + } else + mwv207_bo_placement_from_domain(jbo, 0x4, false); + } else + mwv207_bo_placement_from_domain(jbo, 0x1, false); + + *placement = jbo->placement; +} + +static int mwv207_ttm_io_mem_reserve(struct ttm_device *bdev, + struct ttm_resource *mem) +{ + struct mwv207_device *jdev = bdev_to_jdev(bdev); + size_t bus_size = (size_t)mem->size; + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + case TTM_PL_TT: + + return 0; + case TTM_PL_VRAM: + + mem->bus.offset = mem->start << PAGE_SHIFT; + if (mem->bus.offset + bus_size > jdev->visible_vram_size) + return -EINVAL; + mem->bus.is_iomem = true; + mem->bus.offset += jdev->vram_bar_base; + mem->bus.caching = ttm_write_combined; + break; + default: + return -EINVAL; + } + return 0; +} + +static void mwv207_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) +{ + struct mwv207_ttm_tt *gtt = to_gtt(ttm); + + ttm_tt_fini(>t->ttm); + kfree(gtt); +} + +static struct ttm_tt *mwv207_ttm_tt_create(struct ttm_buffer_object *bo, + u32 page_flags) +{ + struct mwv207_device *jdev; + struct mwv207_ttm_tt *gtt; + + jdev = ddev_to_jdev(bo->base.dev); + gtt = kzalloc(sizeof(struct mwv207_ttm_tt), GFP_KERNEL); + if (gtt == NULL) + return NULL; + + gtt->jdev = jdev; + + if (ttm_sg_tt_init(>t->ttm, bo, page_flags, ttm_cached)) { + kfree(gtt); + return NULL; + } + return >t->ttm; +} + +static int mwv207_ttm_tt_populate(struct ttm_device *bdev, + struct ttm_tt *ttm, + struct ttm_operation_ctx *ctx) +{ + struct mwv207_device *jdev = bdev_to_jdev(bdev); + + return ttm_pool_alloc(&jdev->bdev.pool, ttm, ctx); +} + +static void mwv207_ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) +{ + struct mwv207_device *jdev = bdev_to_jdev(bdev); + + return ttm_pool_free(&jdev->bdev.pool, ttm); +} + +static int mwv207_ttm_job_submit(struct mwv207_job *mjob, struct dma_fence **fence) +{ + *fence = &mjob->base.s_fence->finished; + + mwv207_job_get(mjob); + drm_sched_entity_push_job(&mjob->base); + + return 0; +} + +static struct mwv207_job *mwv207_ttm_job_alloc(struct ttm_buffer_object *bo) +{ + struct mwv207_job *mjob = mwv207_job_alloc(); + struct mwv207_dma_cmd *dma_cmd; + struct mwv207_tvb *mtvb; + int ret; + + if (!mjob) + return ERR_PTR(-ENOMEM); + + dma_cmd = kmalloc(sizeof(*dma_cmd), GFP_KERNEL); + if (!dma_cmd) { + ret = -ENOMEM; + goto err; + } + dma_cmd->src.offset = 0; + dma_cmd->dst.offset = 0; + dma_cmd->height = 1; + dma_cmd->src.stride = bo->resource->size; + dma_cmd->dst.stride = bo->resource->size; + dma_cmd->width = bo->resource->size; + + mjob->cmds = (char *)dma_cmd; + mjob->cmd_size = sizeof(*dma_cmd); + mjob->engine_entity = ddev_to_jdev(bo->base.dev)->dma_entity; + mjob->is_dma = true; + + mjob->mtvb = kzalloc(sizeof(struct mwv207_tvb), GFP_KERNEL); + if (!mjob->mtvb) { + ret = -ENOMEM; + goto err; + } + mtvb = &mjob->mtvb[0]; + list_add_tail(&mtvb->base.head, &mjob->tvblist); + + ret = drm_sched_job_init(&mjob->base, mjob->engine_entity, NULL); + if (ret) + goto err; + + drm_sched_job_arm(&mjob->base); + + ret = drm_sched_job_add_implicit_dependencies(&mjob->base, &bo->base, true); + + if (ret) + goto err; + + return mjob; +err: + mwv207_job_put(mjob); + return ERR_PTR(ret); +} + +static int mwv207_move_vram_gtt(struct ttm_buffer_object *bo, bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem) +{ + struct ttm_tt *ttm = bo->ttm; + struct mwv207_ttm_tt *gtt = to_gtt(ttm); + struct mwv207_dma_cmd *dma_cmd; + struct mwv207_job *mjob; + struct dma_fence *fence; + int ret; + + mjob = mwv207_ttm_job_alloc(bo); + if (IS_ERR(mjob)) + return PTR_ERR(mjob); + + dma_cmd = (struct mwv207_dma_cmd *)mjob->cmds; + dma_cmd->src.base = bo->resource->start << PAGE_SHIFT; + dma_cmd->src.pg_nr_type = MWV207_DMA_NR_PAGES_VRAM(PFN_UP(bo->resource->size)); + dma_cmd->dst.base = (u64)gtt->ttm.dma_address; + dma_cmd->dst.pg_nr_type = MWV207_DMA_NR_PAGES_RAM(ttm->num_pages); + + ret = mwv207_ttm_job_submit(mjob, &fence); + if (!ret) + ret = ttm_bo_move_accel_cleanup(bo, fence, evict, true, new_mem); + + mwv207_job_put(mjob); + return ret; +} + +static int mwv207_move_gtt_vram(struct ttm_buffer_object *bo, bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem) +{ + struct ttm_tt *ttm = bo->ttm; + struct mwv207_ttm_tt *gtt = to_gtt(ttm); + struct mwv207_dma_cmd *dma_cmd; + struct mwv207_job *mjob; + struct dma_fence *fence; + int ret; + + mjob = mwv207_ttm_job_alloc(bo); + if (IS_ERR(mjob)) + return PTR_ERR(mjob); + + dma_cmd = (struct mwv207_dma_cmd *)mjob->cmds; + dma_cmd->src.base = (u64)gtt->ttm.dma_address; + dma_cmd->src.pg_nr_type = MWV207_DMA_NR_PAGES_RAM(ttm->num_pages); + dma_cmd->dst.base = new_mem->start << PAGE_SHIFT; + dma_cmd->dst.pg_nr_type = MWV207_DMA_NR_PAGES_VRAM(PFN_UP(bo->resource->size)); + + ret = mwv207_ttm_job_submit(mjob, &fence); + if (!ret) + ret = ttm_bo_move_accel_cleanup(bo, fence, evict, true, new_mem); + + mwv207_job_put(mjob); + return ret; +} + +static int mwv207_move_vram_vram(struct ttm_buffer_object *bo, bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem) +{ + struct mwv207_dma_cmd *dma_cmd; + struct mwv207_job *mjob; + struct dma_fence *fence; + int ret; + + mjob = mwv207_ttm_job_alloc(bo); + if (IS_ERR(mjob)) + return PTR_ERR(mjob); + + dma_cmd = (struct mwv207_dma_cmd *)mjob->cmds; + dma_cmd->src.base = bo->resource->start << PAGE_SHIFT; + dma_cmd->src.pg_nr_type = MWV207_DMA_NR_PAGES_VRAM(PFN_UP(bo->resource->size)); + dma_cmd->dst.base = new_mem->start << PAGE_SHIFT; + dma_cmd->dst.pg_nr_type = MWV207_DMA_NR_PAGES_VRAM(PFN_UP(new_mem->size)); + + ret = mwv207_ttm_job_submit(mjob, &fence); + if (!ret) + ret = ttm_bo_move_accel_cleanup(bo, fence, evict, true, new_mem); + + mwv207_job_put(mjob); + return ret; +} + +static int mwv207_bo_move(struct ttm_buffer_object *bo, bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem, + struct ttm_place *hop) +{ + struct ttm_resource *old_mem = bo->resource; + struct mwv207_bo *jbo = to_jbo(bo); + struct mwv207_device *jdev; + int ret; + + ret = ttm_bo_wait_ctx(bo, ctx); + if (ret) + return ret; + + if (WARN_ON_ONCE(jbo->tbo.pin_count > 0)) + return -EINVAL; + + jdev = ddev_to_jdev(jbo->tbo.base.dev); + if (!old_mem || (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL)) { + ttm_bo_move_null(bo, new_mem); + goto out; + } + + if (old_mem->mem_type == TTM_PL_SYSTEM && + new_mem->mem_type == TTM_PL_TT) { + ttm_bo_move_null(bo, new_mem); + goto out; + } + + if (old_mem->mem_type == TTM_PL_TT && + new_mem->mem_type == TTM_PL_SYSTEM) { + ttm_resource_free(bo, &bo->resource); + ttm_bo_assign_mem(bo, new_mem); + goto out; + } + + if ((old_mem->mem_type == TTM_PL_SYSTEM && + new_mem->mem_type == TTM_PL_VRAM) || + (old_mem->mem_type == TTM_PL_VRAM && + new_mem->mem_type == TTM_PL_SYSTEM)) { + hop->fpfn = 0; + hop->lpfn = 0; + hop->mem_type = TTM_PL_TT; + hop->flags = 0; + return -EMULTIHOP; + } + + if (old_mem->mem_type == TTM_PL_TT && new_mem->mem_type == TTM_PL_VRAM) { + ret = mwv207_move_gtt_vram(bo, evict, ctx, new_mem); + } else if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_TT) { + ret = mwv207_move_vram_gtt(bo, evict, ctx, new_mem); + } else if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_VRAM) { + ret = mwv207_move_vram_vram(bo, evict, ctx, new_mem); + } else { + ret = -EINVAL; + } + if (ret) { + ret = ttm_bo_move_memcpy(bo, ctx, new_mem); + if (ret) + return ret; + } + + if (!mwv207_mem_visible(jdev, new_mem)) + jbo->flags &= ~(1<<0); + +out: + return 0; +} + +static void mwv207_bo_move_notify(struct ttm_buffer_object *bo, + unsigned int old_type, + struct ttm_resource *new_mem) +{ + struct mwv207_bo *jbo; + struct mwv207_device *jdev; + + if (!mwv207_ttm_bo_is_mwv207_bo(bo)) + return; + jbo = to_jbo(bo); + jdev = ddev_to_jdev(jbo->tbo.base.dev); +} + +int mwv207_bo_fault_reserve_notify(struct ttm_buffer_object *bo) +{ + struct ttm_operation_ctx ctx = { + .interruptible = true, + .no_wait_gpu = false + }; + struct mwv207_device *jdev; + struct mwv207_bo *jbo; + u64 size, offset; + u32 domain; + int ret; + + if (!mwv207_ttm_bo_is_mwv207_bo(bo)) + return 0; + if (bo->resource->mem_type != TTM_PL_VRAM) + return 0; + + jbo = to_jbo(bo); + jdev = ddev_to_jdev(jbo->tbo.base.dev); + size = bo->resource->size; + offset = bo->resource->start << PAGE_SHIFT; + + jbo->flags |= (1<<0); + + if ((offset + size) <= jdev->visible_vram_size) + return 0; + + domain = 0x2; +retry: + mwv207_bo_placement_from_domain(jbo, domain, false); + ret = ttm_bo_validate(bo, &jbo->placement, &ctx); + if (unlikely(ret)) { + if (ret != -ERESTARTSYS) { + if (domain == 0x2) { + + domain = 0x1; + goto retry; + } + } + return ret; + } + + offset = bo->resource->start << PAGE_SHIFT; + if (bo->resource->mem_type == TTM_PL_VRAM && + (offset + size) > jdev->visible_vram_size) { + DRM_ERROR("bo validation goes crazy"); + return -EINVAL; + } + + return ret; +} + +static void mwv207_bo_delete_mem_notify(struct ttm_buffer_object *bo) +{ + unsigned int old_type = TTM_PL_SYSTEM; + + if (bo->resource) + old_type = bo->resource->mem_type; + mwv207_bo_move_notify(bo, old_type, NULL); +} + +static struct ttm_device_funcs mwv207_bo_driver = { + .ttm_tt_create = &mwv207_ttm_tt_create, + .ttm_tt_populate = &mwv207_ttm_tt_populate, + .ttm_tt_unpopulate = &mwv207_ttm_tt_unpopulate, + .ttm_tt_destroy = &mwv207_ttm_tt_destroy, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = &mwv207_evict_flags, + .move = &mwv207_bo_move, + .io_mem_reserve = &mwv207_ttm_io_mem_reserve, + .delete_mem_notify = &mwv207_bo_delete_mem_notify, +}; + +int mwv207_ttm_init(struct mwv207_device *jdev) +{ + int ret; + + ret = ttm_device_init(&jdev->bdev, + &mwv207_bo_driver, jdev->dev, + jdev->base.anon_inode->i_mapping, + jdev->base.vma_offset_manager, true, + true); + if (ret) { + DRM_ERROR("failed to initialize buffer object driver(%d).\n", ret); + return ret; + } + + ret = ttm_range_man_init(&jdev->bdev, TTM_PL_TT, true, + 0x80000000ULL >> PAGE_SHIFT); + if (ret) { + DRM_ERROR("failed to initialize GTT heap.\n"); + goto out_no_vram; + } + + ret = ttm_range_man_init(&jdev->bdev, TTM_PL_VRAM, false, + jdev->vram_size >> PAGE_SHIFT); + if (ret) { + DRM_ERROR("failed to initialize VRAM heap.\n"); + goto out_no_vram; + } + DRM_INFO("mwv207: %lldM of VRAM memory size\n", jdev->vram_size / (1024 * 1024)); + return 0; + +out_no_vram: + ttm_device_fini(&jdev->bdev); + return ret; +} + +void mwv207_ttm_fini(struct mwv207_device *jdev) +{ + ttm_range_man_fini(&jdev->bdev, TTM_PL_VRAM); + ttm_range_man_fini(&jdev->bdev, TTM_PL_TT); + ttm_device_fini(&jdev->bdev); + DRM_INFO("mwv207: ttm finalized\n"); +} diff --git a/drivers/gpu/drm/mwv207/mwv207_ttm.h b/drivers/gpu/drm/mwv207/mwv207_ttm.h new file mode 100644 index 0000000000000..9cbf61ed32004 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_ttm.h @@ -0,0 +1,25 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_TTM_H_TJTW9M4R +#define MWV207_TTM_H_TJTW9M4R +#include +#include +#include +#include "mwv207.h" + +int mwv207_bo_fault_reserve_notify(struct ttm_buffer_object *bo); + +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_vbios.c b/drivers/gpu/drm/mwv207/mwv207_vbios.c new file mode 100644 index 0000000000000..0da8244efdb97 --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_vbios.c @@ -0,0 +1,189 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207.h" +#include "mwv207_vbios.h" + +/* + */ +static inline u8 mwv207_flash_read8(struct mwv207_device *jdev, u32 offset) +{ + return ioread8(jdev->mmio + 0x100000 + offset); +} + +static inline u16 mwv207_flash_read16(struct mwv207_device *jdev, u32 offset) +{ + u16 dat = ioread16(jdev->mmio + 0x100000 + offset); + + return le16_to_cpu(dat); +} + +static inline u32 mwv207_flash_read32(struct mwv207_device *jdev, u32 offset) +{ + u32 dat = ioread32(jdev->mmio + 0x100000 + offset); + + return le32_to_cpu(dat); +} + +static inline void mwv207_flash_cpy(struct mwv207_device *jdev, void *dst, u32 offset, u32 len) +{ + memcpy_fromio(dst, jdev->mmio + 0x100000 + offset, len); +} + +static int mwv207_vbios_check_indexer(struct mwv207_device *jdev, u32 offset) +{ + u16 count, chksum, calc; + + if (mwv207_flash_read16(jdev, offset) != 0xaa55) + return -EINVAL; + + count = mwv207_flash_read16(jdev, offset + 2); + chksum = mwv207_flash_read16(jdev, offset + 4); + + jdev->vbios.sector_count = count; + for (calc = 0, offset += 8, count *= 8; count > 0; count--) + calc += mwv207_flash_read8(jdev, offset++); + + if (calc != chksum) + return -EINVAL; + + jdev->vbios.indexer_valid = 1; + return 0; +} + +static inline int mwv207_vbios_sector(struct mwv207_device *jdev, u32 idx, u32 *poffset) +{ + if (poffset == NULL || !jdev->vbios.indexer_valid || idx >= jdev->vbios.sector_count) + return -EINVAL; + + *poffset = mwv207_flash_read32(jdev, 0xa000 + 0x8 + idx * 8 + 4); + return 0; +} + +static inline int mwv207_vbios_insert_vdat(struct mwv207_device *jdev, u32 key, + struct mwv207_vdat *vdat) +{ + int ret; + + mutex_lock(&jdev->vbios.cfg_lock); + ret = idr_alloc(&jdev->vbios.cfg_table, vdat, key, key + 1, GFP_KERNEL); + mutex_unlock(&jdev->vbios.cfg_lock); + + return ret; +} + +static void mwv207_vbios_parse_hdmi(struct mwv207_device *jdev) +{ + u32 count, offset, chksum, len; + struct mwv207_vdat *vdat[4]; + u32 select[4]; + u8 magic[8]; + int ret, i; + + ret = mwv207_vbios_sector(jdev, 0xa, &offset); + if (ret) + return; + + mwv207_flash_cpy(jdev, magic, offset, 8); + if (memcmp(magic, "JMPHYCFG", 8)) + return; + + len = mwv207_flash_read32(jdev, offset + 0x18); + if (len > 0x1200 || (len % 1140)) + return; + count = len / 1140; + + for (chksum = 0, i = 0; i < len; i += 4) + chksum += mwv207_flash_read32(jdev, offset + 0x20 + i); + if (chksum != mwv207_flash_read32(jdev, offset + 0x14)) + return; + + for (i = 0; i < 4; i++) { + select[i] = mwv207_flash_read8(jdev, offset + 0x1c + i); + if (select[i] > min_t(u32, 3, count - 1)) + return; + } + + for (i = 0; i < 4; i++) { + void *dat; + + vdat[i] = devm_kzalloc(jdev->dev, sizeof(*vdat[i]), GFP_KERNEL); + if (!vdat[i]) + return; + vdat[i]->len = 1140; + dat = devm_kmalloc(jdev->dev, vdat[i]->len, GFP_KERNEL); + if (!dat) + return; + mwv207_flash_cpy(jdev, dat, + offset + 0x20 + select[i] * 1140, + 1140); + + vdat[i]->dat = dat; + + (void)mwv207_vbios_insert_vdat(jdev, 0xfff0 + i, vdat[i]); + } +} + +static void mwv207_vbios_parse_edp(struct mwv207_device *jdev) +{ + +} + +static void mwv207_vbios_parse_cfg(struct mwv207_device *jdev) +{ + +} + +const struct mwv207_vdat *mwv207_vbios_vdat(struct mwv207_device *jdev, u32 key) +{ + struct mwv207_vdat *dat; + + mutex_lock(&jdev->vbios.cfg_lock); + dat = idr_find(&jdev->vbios.cfg_table, key); + mutex_unlock(&jdev->vbios.cfg_lock); + + return dat; +} + +void mwv207_vbios_init(struct mwv207_device *jdev) +{ + int ret; + + mutex_init(&jdev->vbios.vcmd_lock); + mutex_init(&jdev->vbios.cfg_lock); + idr_init(&jdev->vbios.cfg_table); + + ret = mwv207_vbios_check_indexer(jdev, 0xa000); + if (ret) { + pr_warn("mwv207: vbios indexer is not valid"); + return; + } + + mwv207_vbios_parse_hdmi(jdev); + mwv207_vbios_parse_edp(jdev); + mwv207_vbios_parse_cfg(jdev); +} + +void mwv207_vbios_fini(struct mwv207_device *jdev) +{ + struct mwv207_vdat *vdat; + u32 id; + + idr_for_each_entry(&jdev->vbios.cfg_table, vdat, id) { + devm_kfree(jdev->dev, vdat->dat); + devm_kfree(jdev->dev, vdat); + } + idr_destroy(&jdev->vbios.cfg_table); +} diff --git a/drivers/gpu/drm/mwv207/mwv207_vbios.h b/drivers/gpu/drm/mwv207/mwv207_vbios.h new file mode 100644 index 0000000000000..75796f865645b --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_vbios.h @@ -0,0 +1,51 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#ifndef MWV207_VBIOS_H_D2JHN3FG +#define MWV207_VBIOS_H_D2JHN3FG + +struct mwv207_device; + +enum pll_id { + MWV207_PLL_GPU = 0, + MWV207_PLL_GRAPH0, + MWV207_PLL_GRAPH1, + MWV207_PLL_GRAPH2, + MWV207_PLL_GRAPH3, + MWV207_PLL_GRAPH4, + MWV207_PLL_HD, + MWV207_PLL_GU3D0, + MWV207_PLL_EDP, + MWV207_PLL_GU3D1, + MWV207_PLL_RESERVED, + MWV207_PLL_GU2D_PLL, + MWV207_PLL_DDR, + MWV207_PLL_COUNT +}; + +struct mwv207_vdat { + const void *dat; + u32 len; +}; + +int mwv207_vbios_set_pll(struct mwv207_device *jdev, int pll_id, u32 kfreq); + +int mwv207_vbios_get_pll(struct mwv207_device *jdev, int pll_idx, unsigned long *kfreq); + +const struct mwv207_vdat *mwv207_vbios_vdat(struct mwv207_device *jdev, u32 key); + +void mwv207_vbios_init(struct mwv207_device *jdev); +void mwv207_vbios_fini(struct mwv207_device *jdev); +#endif diff --git a/drivers/gpu/drm/mwv207/mwv207_vcmd.c b/drivers/gpu/drm/mwv207/mwv207_vcmd.c new file mode 100644 index 0000000000000..e5128c2be0d6d --- /dev/null +++ b/drivers/gpu/drm/mwv207/mwv207_vcmd.c @@ -0,0 +1,173 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include +#include +#include "mwv207.h" +#include "mwv207_vbios.h" + +#define MWV207_PLL_TIMEOUT msecs_to_jiffies(1000) + +#define MWV207_PLL_STATE_INDEX(id) (0x100 * ((id) / 6) + 0x20 * ((id) % 6)) +#define MWV207_PLL_STATE_BASE(id) (0x9b0000 + 0xA00 + MWV207_PLL_STATE_INDEX(id)) + +#define MWV207_PLL_FBDIV_REFDIV(id) (MWV207_PLL_STATE_BASE(id) + 0x00) +#define MWV207_PLL_FRAC(id) (MWV207_PLL_STATE_BASE(id) + 0x04) +#define MWV207_PLL_POSTDIV(id) (MWV207_PLL_STATE_BASE(id) + 0x08) + +static inline int mwv207_vcmd_idle(struct mwv207_device *jdev) +{ + u32 state; + + state = jdev_read(jdev, (0x9b0144)) >> 28; + + return state == 0; +} + +static inline int mwv207_vcmd_wait_idle(struct mwv207_device *jdev) +{ + int i; + + for (i = 0; i < 1000 && !mwv207_vcmd_idle(jdev); i++) + usleep_range(1000, 1001); + return mwv207_vcmd_idle(jdev) ? 0 : -ETIMEDOUT; +} + +static inline void mwv207_vcmd_req(struct mwv207_device *jdev, int cmd, + int idx, u32 value, int enable) +{ + u32 req; + + req = cmd << 28; + req |= idx << 24; + req |= value << 1; + req |= enable ? 1 : 0; + + jdev_write(jdev, (0x9b0140), req); +} + +static inline void mwv207_vcmd_start(struct mwv207_device *jdev) +{ + jdev_write(jdev, (0x9b0144), 1 << 28); +} + +static inline int mwv207_vcmd_result(struct mwv207_device *jdev) +{ + return (jdev_read(jdev, (0x9b0144)) >> 24) & 0xf; +} + +static int mwv207_vcmd_execute(struct mwv207_device *jdev, int cmd, + int idx, u32 value, int enable) +{ + int ret; + + mutex_lock(&jdev->vbios.vcmd_lock); + + ret = mwv207_vcmd_wait_idle(jdev); + if (ret) + goto unlock; + + mwv207_vcmd_req(jdev, cmd, idx, value, enable); + + mwv207_vcmd_start(jdev); + + ret = mwv207_vcmd_wait_idle(jdev); + if (ret) + goto unlock; + + ret = mwv207_vcmd_result(jdev); + switch (ret) { + case 0: + goto unlock; + case 1: + pr_err("unsupport pll type!)"); + break; + case 2: + + pr_info_once("pll set, invalid freq!"); + break; + case 3: + pr_err("pll busy!"); + break; + case 4: + pr_err("pll set, invalid argument!"); + break; + case 9: + pr_err("PLL REQ cmd code unsupport!"); + break; + default: + pr_err("unknown pll error! (code 0x%x)", ret); + break; + } + ret = -EIO; +unlock: + mutex_unlock(&jdev->vbios.vcmd_lock); + return ret; +} + +int mwv207_vbios_set_pll(struct mwv207_device *jdev, int pll_idx, u32 kfreq) +{ + if (pll_idx >= MWV207_PLL_COUNT) + return -EINVAL; + + return mwv207_vcmd_execute(jdev, 1, pll_idx, + kfreq, kfreq ? 1 : 0); +} + +int mwv207_vbios_get_pll(struct mwv207_device *jdev, int pll_idx, unsigned long *kfreq) +{ + u32 port, state1, state2, state3; + u32 refdiv, fbintdiv, fbracdiv, postdiv1, postdiv2; + u32 postdiv_mask, postdiv_offset; + u64 freq; + + if (pll_idx >= MWV207_PLL_COUNT) + return -EINVAL; + + port = pll_idx ? 0 : 1; + + mutex_lock(&jdev->vbios.vcmd_lock); + state1 = jdev_read(jdev, MWV207_PLL_FBDIV_REFDIV(pll_idx)); + state2 = jdev_read(jdev, MWV207_PLL_FRAC(pll_idx)); + state3 = jdev_read(jdev, MWV207_PLL_POSTDIV(pll_idx)); + mutex_unlock(&jdev->vbios.vcmd_lock); + + refdiv = state1 & 0x3f; + fbintdiv = (state1 & 0xfff0000) >> 16; + fbracdiv = state2 & 0xffffff; + postdiv1 = 4; + + postdiv_offset = 8 * (port % 4); + postdiv_mask = 0x7f << postdiv_offset; + postdiv2 = (state3 & postdiv_mask) >> postdiv_offset; + + if (!refdiv || !postdiv1 || !postdiv2) { + dev_dbg(jdev->dev, "get pll failed"); + return -EINVAL; + } + + freq = 100000 * 100000ull / refdiv; + freq = freq * fbintdiv + ((freq * fbracdiv) >> 24); + freq /= postdiv1; + freq /= postdiv2; + freq = DIV_ROUND_CLOSEST_ULL(freq, 100000); + + *kfreq = freq; + + dev_dbg(jdev->dev, "get pll result: freq %llu kHz\n", freq); + + return 0; +} + diff --git a/drivers/gpu/drm/mwv207/selftest/selftest.c b/drivers/gpu/drm/mwv207/selftest/selftest.c new file mode 100644 index 0000000000000..04c3121f37737 --- /dev/null +++ b/drivers/gpu/drm/mwv207/selftest/selftest.c @@ -0,0 +1,105 @@ +/* +* SPDX-License-Identifier: GPL +* +* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. +* All rights reserved. +* +* Author: +* shanjinkui +* +* The software and information contained herein is proprietary and +* confidential to JingJiaMicro Electronics. This software can only be +* used by JingJiaMicro Electronics Corporation. Any use, reproduction, +* or disclosure without the written permission of JingJiaMicro +* Electronics Corporation is strictly prohibited. +*/ +#include "mwv207.h" +#include "mwv207_bo.h" +#include "mwv207_dma.h" + +static int mwv207_test_create_and_map_bo(struct mwv207_device *jdev, u64 size, + u32 domain, u32 flags, struct mwv207_bo **pjbo, void **plogical) +{ + struct mwv207_bo *jbo; + void *logical; + int ret; + + ret = mwv207_bo_create(jdev, size, 0, ttm_bo_type_kernel, + domain, flags, &jbo); + if (ret) + return ret; + ret = mwv207_bo_reserve(jbo, true); + if (ret) + goto free_bo; + ret = mwv207_bo_kmap_reserved(jbo, &logical); + if (ret) + goto unreserve_bo; + + *pjbo = jbo; + *plogical = logical; + return 0; + +unreserve_bo: + mwv207_bo_unreserve(jbo); +free_bo: + mwv207_bo_unref(jbo); + return ret; +} + +static void mwv207_test_destroy_and_unmap_bo(struct mwv207_bo *jbo) +{ + if (!jbo) + return; + + mwv207_bo_kunmap_reserved(jbo); + mwv207_bo_unreserve(jbo); + mwv207_bo_unref(jbo); +} + +static int mwv207_test_bo_acc(struct mwv207_device *jdev) +{ + struct mwv207_bo *jbo; + int i, ret; + void *logical; + + ret = mwv207_test_create_and_map_bo(jdev, 0x1000, + 0x2, (1<<0), &jbo, &logical); + if (ret) + return ret; + + memset(logical, 0x5a, 0x1000); + for (i = 0; i < 0x1000; i++) { + if (*((char *)logical + i) != 0x5a) { + ret = -EIO; + break; + } + } + + mwv207_test_destroy_and_unmap_bo(jbo); + + return ret; +} + +static int mwv207_test_bo(struct mwv207_device *jdev) +{ + int ret; + + ret = mwv207_test_bo_acc(jdev); + if (ret) { + pr_err("mwv207: bo access test failed with %d", ret); + return ret; + } + + return ret; +} + +int mwv207_test(struct mwv207_device *jdev) +{ + int ret; + + ret = mwv207_test_bo(jdev); + if (ret == 0) + pr_info("mwv207 selftest passed"); + + return ret; +} From 2e8b96ffbc772b512d5761315aaa64b3113b2797 Mon Sep 17 00:00:00 2001 From: WangYuli Date: Thu, 18 Apr 2024 23:26:18 +0800 Subject: [PATCH 2/2] drm/mwv207: Cleaning up terrible code styles The code styles of this driver is quite frightening. These files are littered with unaligned comments, redundant braces, misspellings and other inelegant and undisciplined code which makes the whole driver has a huge obstacle before becoming acceptable. We cannot let this get the way of jemoic GPU. Signed-off-by: WangYuli --- drivers/gpu/drm/mwv207/Kconfig | 2 +- drivers/gpu/drm/mwv207/Makefile | 1 - drivers/gpu/drm/mwv207/dc/mwv207_dvo.c | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_edp.c | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c | 31 +++++++-------- drivers/gpu/drm/mwv207/dc/mwv207_i2c.c | 35 ++++++++--------- drivers/gpu/drm/mwv207/dc/mwv207_kms.c | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_kms.h | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_va.c | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_va.h | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_vga.c | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_vi.c | 27 +++++++------ drivers/gpu/drm/mwv207/dc/mwv207_vi.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_bo.c | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_bo.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_ctx.c | 39 ++++++++++--------- drivers/gpu/drm/mwv207/mwv207_ctx.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_db.c | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_db.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_devfreq.c | 25 ++++++------ drivers/gpu/drm/mwv207/mwv207_devfreq.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_dma.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_drm.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_drv.c | 31 ++++++++------- drivers/gpu/drm/mwv207/mwv207_gem.c | 29 +++++++------- drivers/gpu/drm/mwv207/mwv207_gem.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_irq.c | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_irq.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_pipe_2d.c | 33 ++++++++-------- drivers/gpu/drm/mwv207/mwv207_pipe_3d.c | 30 +++++++------- .../gpu/drm/mwv207/mwv207_pipe_codec_common.c | 31 ++++++++------- .../gpu/drm/mwv207/mwv207_pipe_codec_common.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_pipe_dec.c | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_pipe_dma.c | 30 +++++++------- drivers/gpu/drm/mwv207/mwv207_pipe_enc.c | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_sched.c | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_sched.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_submit.c | 30 +++++++------- drivers/gpu/drm/mwv207/mwv207_submit.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_ttm.c | 37 +++++++++--------- drivers/gpu/drm/mwv207/mwv207_ttm.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_vbios.c | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_vbios.h | 27 +++++++------ drivers/gpu/drm/mwv207/mwv207_vcmd.c | 27 +++++++------ drivers/gpu/drm/mwv207/selftest/selftest.c | 27 +++++++------ 46 files changed, 602 insertions(+), 646 deletions(-) diff --git a/drivers/gpu/drm/mwv207/Kconfig b/drivers/gpu/drm/mwv207/Kconfig index 5fb991ed9e21c..fb584af69ca66 100644 --- a/drivers/gpu/drm/mwv207/Kconfig +++ b/drivers/gpu/drm/mwv207/Kconfig @@ -10,6 +10,6 @@ config DRM_MWV207 select DRM_DEVFREQ select DRM_TTM_HELPER help - Choose this option if you have a Jingjia graphics card. + Choose this option if you have a Jingjia graphics card. If M is selected, the module will be called mwv207 (JM9100). diff --git a/drivers/gpu/drm/mwv207/Makefile b/drivers/gpu/drm/mwv207/Makefile index f9032a63d578b..45bc9805e7eb4 100644 --- a/drivers/gpu/drm/mwv207/Makefile +++ b/drivers/gpu/drm/mwv207/Makefile @@ -1,4 +1,3 @@ - mwv207-y := mwv207_drv.o \ mwv207_irq.o \ mwv207_db.o \ diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_dvo.c b/drivers/gpu/drm/mwv207/dc/mwv207_dvo.c index f0c0eda04a31b..ae25e8dbdfd83 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_dvo.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_dvo.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207_vi.h" static void mwv207_dvo_switch(struct mwv207_output *output, bool on) diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_edp.c b/drivers/gpu/drm/mwv207/dc/mwv207_edp.c index 790b186a1521f..917feb7c54e14 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_edp.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_edp.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207_vi.h" int mwv207_edp_init(struct mwv207_device *jdev) diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c b/drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c index 854aa0dc68193..7029a4d229e82 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_hdmi.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -857,11 +856,9 @@ static void mwv207_hdmi_set_timing(struct mwv207_hdmi *hdmi) mwv207_hdmi_enable_video_path(hdmi); - if (hdmi->sink_has_audio) { + if (hdmi->sink_has_audio) mwv207_hdmi_enable_audio_clk(hdmi, 1); - } - if (hdmi->sink_is_hdmi) { struct drm_connector *conn = &hdmi->base.connector; diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_i2c.c b/drivers/gpu/drm/mwv207/dc/mwv207_i2c.c index 21df07c6f7fcf..17cc6129669f7 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_i2c.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_i2c.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include "mwv207.h" @@ -300,13 +299,11 @@ bool mwv207_i2c_probe(struct i2c_adapter *i2c_bus) }; ret = i2c_transfer(i2c_bus, msgs, 2); - if (ret != 2) { - + if (ret != 2) return false; - } - if (drm_edid_header_is_valid(buf) < 6) { + if (drm_edid_header_is_valid(buf) < 6) return false; - } + return true; } diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_kms.c b/drivers/gpu/drm/mwv207/dc/mwv207_kms.c index ca6b9f721a20a..4e9c0261a80c5 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_kms.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_kms.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_kms.h b/drivers/gpu/drm/mwv207/dc/mwv207_kms.h index 6c6c1776e9302..017ab470963fb 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_kms.h +++ b/drivers/gpu/drm/mwv207/dc/mwv207_kms.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_KMS_H_VBCHTKVP #define MWV207_KMS_H_VBCHTKVP diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_va.c b/drivers/gpu/drm/mwv207/dc/mwv207_va.c index 9a9d33c8a2c9e..35abef94680d2 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_va.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_va.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_va.h b/drivers/gpu/drm/mwv207/dc/mwv207_va.h index 2758507b4bfb6..bb28e67891d9f 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_va.h +++ b/drivers/gpu/drm/mwv207/dc/mwv207_va.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #ifndef MWV207_VA_H_BSY8LF4F #define MWV207_VA_H_BSY8LF4F diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_vga.c b/drivers/gpu/drm/mwv207/dc/mwv207_vga.c index b09e9d614a4e6..74929c7b5313e 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_vga.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_vga.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207_vi.h" #define MWV207_DAC_HPD_VS_CLC(_chan) (0x64 + (_chan) * 0x14) diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_vi.c b/drivers/gpu/drm/mwv207/dc/mwv207_vi.c index 8881ead9ce16f..7c6ba8c7c681e 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_vi.c +++ b/drivers/gpu/drm/mwv207/dc/mwv207_vi.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207_vi.h" int mwv207_output_late_register(struct drm_connector *connector) diff --git a/drivers/gpu/drm/mwv207/dc/mwv207_vi.h b/drivers/gpu/drm/mwv207/dc/mwv207_vi.h index effa6d2ce7e3e..5dd7dd1a6236c 100644 --- a/drivers/gpu/drm/mwv207/dc/mwv207_vi.h +++ b/drivers/gpu/drm/mwv207/dc/mwv207_vi.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207.h" #include #include diff --git a/drivers/gpu/drm/mwv207/mwv207.h b/drivers/gpu/drm/mwv207/mwv207.h index d5c302d82e412..1c83e8943c1b1 100644 --- a/drivers/gpu/drm/mwv207/mwv207.h +++ b/drivers/gpu/drm/mwv207/mwv207.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_H_VTIQLF2Y #define MWV207_H_VTIQLF2Y diff --git a/drivers/gpu/drm/mwv207/mwv207_bo.c b/drivers/gpu/drm/mwv207/mwv207_bo.c index 0a1423d0b357f..a45ad8f70c2a6 100644 --- a/drivers/gpu/drm/mwv207/mwv207_bo.c +++ b/drivers/gpu/drm/mwv207/mwv207_bo.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include diff --git a/drivers/gpu/drm/mwv207/mwv207_bo.h b/drivers/gpu/drm/mwv207/mwv207_bo.h index 5f18355360efe..2e531166d9bb7 100644 --- a/drivers/gpu/drm/mwv207/mwv207_bo.h +++ b/drivers/gpu/drm/mwv207/mwv207_bo.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_BO_H_T4ETFCB2 #define MWV207_BO_H_T4ETFCB2 diff --git a/drivers/gpu/drm/mwv207/mwv207_ctx.c b/drivers/gpu/drm/mwv207/mwv207_ctx.c index c225b0cb52236..be9c22ab4a951 100644 --- a/drivers/gpu/drm/mwv207/mwv207_ctx.c +++ b/drivers/gpu/drm/mwv207/mwv207_ctx.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -65,7 +64,8 @@ static int mwv207_ctx_entity_init_single(struct mwv207_ctx *ctx, if (!entity) return -ENOMEM; - ret = drm_sched_entity_init(entity, DRM_SCHED_PRIORITY_NORMAL, &scheds[i], 1, &ctx->guilty); + ret = drm_sched_entity_init(entity, DRM_SCHED_PRIORITY_NORMAL, + &scheds[i], 1, &ctx->guilty); if (ret) { kfree(entity); @@ -79,7 +79,8 @@ static int mwv207_ctx_entity_init_single(struct mwv207_ctx *ctx, if (!entity) return -ENOMEM; - ret = drm_sched_entity_init(entity, DRM_SCHED_PRIORITY_NORMAL, &scheds[0], i, &ctx->guilty); + ret = drm_sched_entity_init(entity, DRM_SCHED_PRIORITY_NORMAL, + &scheds[0], i, &ctx->guilty); if (ret) { kfree(entity); @@ -228,15 +229,15 @@ int mwv207_kctx_init(struct mwv207_device *jdev) struct drm_gpu_scheduler *scheds[1]; int i; - for (i = 0; i < 1; i++) { + for (i = 0; i < 1; i++) scheds[i] = jdev->sched_dma[i]; - } jdev->dma_entity = devm_kzalloc(jdev->dev, sizeof(struct drm_sched_entity), GFP_KERNEL); if (!jdev->dma_entity) return -ENOMEM; - return drm_sched_entity_init(jdev->dma_entity, DRM_SCHED_PRIORITY_NORMAL, &scheds[0], i, NULL); + return drm_sched_entity_init(jdev->dma_entity, DRM_SCHED_PRIORITY_NORMAL, + &scheds[0], i, NULL); } void mwv207_kctx_fini(struct mwv207_device *jdev) diff --git a/drivers/gpu/drm/mwv207/mwv207_ctx.h b/drivers/gpu/drm/mwv207/mwv207_ctx.h index 7b204b6c779e7..b5be4ca2d1f7d 100644 --- a/drivers/gpu/drm/mwv207/mwv207_ctx.h +++ b/drivers/gpu/drm/mwv207/mwv207_ctx.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_CTX_H_QFMRESBO #define MWV207_CTX_H_QFMRESBO #include diff --git a/drivers/gpu/drm/mwv207/mwv207_db.c b/drivers/gpu/drm/mwv207/mwv207_db.c index d7a2e5657ed9a..001d2e656c9b5 100644 --- a/drivers/gpu/drm/mwv207/mwv207_db.c +++ b/drivers/gpu/drm/mwv207/mwv207_db.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include diff --git a/drivers/gpu/drm/mwv207/mwv207_db.h b/drivers/gpu/drm/mwv207/mwv207_db.h index fd5c685d4ca33..1f7421ada09b9 100644 --- a/drivers/gpu/drm/mwv207/mwv207_db.h +++ b/drivers/gpu/drm/mwv207/mwv207_db.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include "mwv207_drm.h" #ifndef MWV207_DB_H_AGPY6ZIQ diff --git a/drivers/gpu/drm/mwv207/mwv207_devfreq.c b/drivers/gpu/drm/mwv207/mwv207_devfreq.c index de95a36ad5182..9497cec04b450 100644 --- a/drivers/gpu/drm/mwv207/mwv207_devfreq.c +++ b/drivers/gpu/drm/mwv207/mwv207_devfreq.c @@ -1,17 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and * confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and * confidential + * to JingJiaMicro Electronics. This software can only be used by JingJiaMicro + * Electronics Corporation. Any use, reproduction, or disclosure without the written + * permission of JingJiaMicro Electronics Corporation is strictly prohibited. + */ #include #include #include "mwv207_devfreq.h" diff --git a/drivers/gpu/drm/mwv207/mwv207_devfreq.h b/drivers/gpu/drm/mwv207/mwv207_devfreq.h index 5cabf1c3a39c2..1a655dfeae9ad 100644 --- a/drivers/gpu/drm/mwv207/mwv207_devfreq.h +++ b/drivers/gpu/drm/mwv207/mwv207_devfreq.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef __MWV207_DEVFREQ_H__ #define __MWV207_DEVFREQ_H__ diff --git a/drivers/gpu/drm/mwv207/mwv207_dma.h b/drivers/gpu/drm/mwv207/mwv207_dma.h index ff70d9a0e89ad..89720f86b4430 100644 --- a/drivers/gpu/drm/mwv207/mwv207_dma.h +++ b/drivers/gpu/drm/mwv207/mwv207_dma.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_EDMA_H_EPAWCJ4N #define MWV207_EDMA_H_EPAWCJ4N #include "mwv207.h" diff --git a/drivers/gpu/drm/mwv207/mwv207_drm.h b/drivers/gpu/drm/mwv207/mwv207_drm.h index 13e32b53d46e5..80ad08573ae23 100644 --- a/drivers/gpu/drm/mwv207/mwv207_drm.h +++ b/drivers/gpu/drm/mwv207/mwv207_drm.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef __MWV207_DRM_H__ #define __MWV207_DRM_H__ diff --git a/drivers/gpu/drm/mwv207/mwv207_drv.c b/drivers/gpu/drm/mwv207/mwv207_drv.c index c539fb7e3e032..109f47a930e22 100644 --- a/drivers/gpu/drm/mwv207/mwv207_drv.c +++ b/drivers/gpu/drm/mwv207/mwv207_drv.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -119,12 +118,14 @@ static struct drm_driver mwv207_drm_driver = { static inline void iatu_write(struct mwv207_device *jdev, int index, u32 offset, u32 value) { u32 region_base = 0x10000 + index * 0x200; + writel(value, jdev->iatu + region_base + offset); } static inline u32 iatu_read(struct mwv207_device *jdev, int index, u32 offset) { u32 region_base = 0x10000 + index * 0x200; + return readl(jdev->iatu + region_base + offset); } @@ -215,7 +216,7 @@ void jdev_write_vram(struct mwv207_device *jdev, u64 vram_addr, void *buf, int s va = mwv207_remap_region(jdev, vram_addr, &len); if (!va || len <= 0) { spin_unlock(&jdev->win_lock); - pr_warn("mwv207: write vram faild"); + pr_warn("mwv207: write vram failed"); return; } memcpy_toio(va, buf, len); diff --git a/drivers/gpu/drm/mwv207/mwv207_gem.c b/drivers/gpu/drm/mwv207/mwv207_gem.c index 40ee95c5086f0..12310e22f1d9f 100644 --- a/drivers/gpu/drm/mwv207/mwv207_gem.c +++ b/drivers/gpu/drm/mwv207/mwv207_gem.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -115,7 +114,7 @@ void mwv207_gem_prime_vunmap(struct drm_gem_object *gobj, struct iosys_map *map) return; } - jbo->map_count --; + jbo->map_count--; if (jbo->map_count == 0) { ttm_bo_vunmap(&jbo->tbo, &jbo->map); diff --git a/drivers/gpu/drm/mwv207/mwv207_gem.h b/drivers/gpu/drm/mwv207/mwv207_gem.h index 2a882307bb877..210d1e0afd1be 100644 --- a/drivers/gpu/drm/mwv207/mwv207_gem.h +++ b/drivers/gpu/drm/mwv207/mwv207_gem.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_GEM_H_TJTW9M4R #define MWV207_GEM_H_TJTW9M4R #include diff --git a/drivers/gpu/drm/mwv207/mwv207_irq.c b/drivers/gpu/drm/mwv207/mwv207_irq.c index e9857bf24b0d8..a0b8a139dd318 100644 --- a/drivers/gpu/drm/mwv207/mwv207_irq.c +++ b/drivers/gpu/drm/mwv207/mwv207_irq.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include diff --git a/drivers/gpu/drm/mwv207/mwv207_irq.h b/drivers/gpu/drm/mwv207/mwv207_irq.h index eabcc61e62c47..9476ba9ae3dc7 100644 --- a/drivers/gpu/drm/mwv207/mwv207_irq.h +++ b/drivers/gpu/drm/mwv207/mwv207_irq.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_IRQ_H_Z8YGVNB2 #define MWV207_IRQ_H_Z8YGVNB2 diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_2d.c b/drivers/gpu/drm/mwv207/mwv207_pipe_2d.c index d75dffd818ffe..4e26758baab17 100644 --- a/drivers/gpu/drm/mwv207/mwv207_pipe_2d.c +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_2d.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -226,7 +225,7 @@ static u32 *pipe_2d_append_wl(struct mwv207_pipe_2d *pipe) *pipe->tail++ = 0x88000000; *pipe->tail++ = 0x80000000; *pipe->tail++ = 0x89000000; - *pipe->tail++ = pipe_2d_gpu_addr(pipe, wait);; + *pipe->tail++ = pipe_2d_gpu_addr(pipe, wait); return wait; } @@ -272,6 +271,7 @@ static u32 *pipe_2d_wait_for_space(struct mwv207_pipe_2d *pipe, u32 size) for (i = 0; i < 10000; i++) { u32 *head = READ_ONCE(pipe->head); + if (head <= pipe->tail) { if (pipe_2d_ptr_span(pipe->ring_end, pipe->tail) >= size) return pipe->tail; @@ -436,8 +436,7 @@ static unsigned long pipe_2d_max_freq_get(struct mwv207_device *jdev) { unsigned long freq; - switch (jdev->pdev->subsystem_device) - { + switch (jdev->pdev->subsystem_device) { case 0x9103: freq = 600; break; diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_3d.c b/drivers/gpu/drm/mwv207/mwv207_pipe_3d.c index d0764e45226bc..873d380c1549a 100644 --- a/drivers/gpu/drm/mwv207/mwv207_pipe_3d.c +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_3d.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -474,8 +473,7 @@ static unsigned long pipe_3d_max_freq_get(struct mwv207_device *jdev) { unsigned long freq; - switch (jdev->pdev->subsystem_device) - { + switch (jdev->pdev->subsystem_device) { case 0x9103: freq = 600; break; diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c index 49e78201069e1..0eb7d98e6b105 100644 --- a/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207_pipe_codec_common.h" static void pipe_codec_sw_pp_submit_ex(struct pipe_codec_sw_pp *sw_pp, struct mwv207_pipe *pipe, @@ -44,7 +43,9 @@ void pipe_codec_sw_pp_excute(struct pipe_codec_sw_pp *sw_pp, struct mwv207_pipe return; for (i = 0; i < sw_pp->wb_cnt; i++) { - for (j = 0; j < sw_pp->wb[i].nr && sw_pp->wb[i].reg + j * 4 <= pipe->iosize - 4; j++) + for (j = 0; + j < sw_pp->wb[i].nr && sw_pp->wb[i].reg + j * 4 <= pipe->iosize - 4; + j++) sw_pp->wb_buf[j] = pipe_read(pipe, sw_pp->wb[i].reg + j * 4); if (!j) diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h index 5e1c4e5cde3ad..dd47390a58c11 100644 --- a/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_codec_common.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207_sched.h" #define MAX(a, b) ((a) > (b) ? (a) : (b)) diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_dec.c b/drivers/gpu/drm/mwv207/mwv207_pipe_dec.c index 33a3b83821acd..1cf84c44fd551 100644 --- a/drivers/gpu/drm/mwv207/mwv207_pipe_dec.c +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_dec.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_dma.c b/drivers/gpu/drm/mwv207/mwv207_pipe_dma.c index 34939079cda04..4564af10d9976 100644 --- a/drivers/gpu/drm/mwv207/mwv207_pipe_dma.c +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_dma.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -355,6 +354,7 @@ static struct dma_fence *mwv207_pipe_dma_submit(struct mwv207_pipe *mpipe, static void mwv207_pipe_dma_dump_state(struct mwv207_pipe *mpipe) { struct mwv207_pipe_dma *pipe = to_dma_pipe(mpipe); + pr_info("%s todo: %p", __func__, pipe); } @@ -387,7 +387,7 @@ struct mwv207_pipe *mwv207_pipe_dma_create(struct mwv207_device *jdev, int unit, /* current TTM impl uses ttm_dma_populate which is coherent. * set to false if ttm_populate_and_map_pages is used - * */ + */ pipe->coherent = true; pipe->alignment = jdev->lite ? 32 : 64; diff --git a/drivers/gpu/drm/mwv207/mwv207_pipe_enc.c b/drivers/gpu/drm/mwv207/mwv207_pipe_enc.c index 61de0606bd68b..dfe16b9ab8e48 100644 --- a/drivers/gpu/drm/mwv207/mwv207_pipe_enc.c +++ b/drivers/gpu/drm/mwv207/mwv207_pipe_enc.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include diff --git a/drivers/gpu/drm/mwv207/mwv207_sched.c b/drivers/gpu/drm/mwv207/mwv207_sched.c index a4e90351d49f5..a7d38622ec093 100644 --- a/drivers/gpu/drm/mwv207/mwv207_sched.c +++ b/drivers/gpu/drm/mwv207/mwv207_sched.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include "mwv207.h" diff --git a/drivers/gpu/drm/mwv207/mwv207_sched.h b/drivers/gpu/drm/mwv207/mwv207_sched.h index 52322eea959db..27800f03abd4c 100644 --- a/drivers/gpu/drm/mwv207/mwv207_sched.h +++ b/drivers/gpu/drm/mwv207/mwv207_sched.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_SCHED_H_TGQQ2LXS #define MWV207_SCHED_H_TGQQ2LXS #include diff --git a/drivers/gpu/drm/mwv207/mwv207_submit.c b/drivers/gpu/drm/mwv207/mwv207_submit.c index a2a150292e802..1eff84f4f551e 100644 --- a/drivers/gpu/drm/mwv207/mwv207_submit.c +++ b/drivers/gpu/drm/mwv207/mwv207_submit.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -379,8 +378,7 @@ static int mwv207_submit_patch_dma_entry(struct mwv207_job *mjob, if (loc->stride == 0 || loc->pg_nr_type != 0 || width == 0 || height == 0) return -EINVAL; - switch (jbo->tbo.resource->mem_type) - { + switch (jbo->tbo.resource->mem_type) { case TTM_PL_VRAM: loc->base = mwv207_bo_gpu_phys(jbo); loc->pg_nr_type = MWV207_DMA_NR_PAGES_VRAM(PFN_UP(jbo->tbo.resource->size)); diff --git a/drivers/gpu/drm/mwv207/mwv207_submit.h b/drivers/gpu/drm/mwv207/mwv207_submit.h index 194fceafa88ee..3d5fe18bde045 100644 --- a/drivers/gpu/drm/mwv207/mwv207_submit.h +++ b/drivers/gpu/drm/mwv207/mwv207_submit.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #ifndef MWV207_SUBMIT_H_2S1GH9LD #define MWV207_SUBMIT_H_2S1GH9LD diff --git a/drivers/gpu/drm/mwv207/mwv207_ttm.c b/drivers/gpu/drm/mwv207/mwv207_ttm.c index bb6d50590d851..6475998955af8 100644 --- a/drivers/gpu/drm/mwv207/mwv207_ttm.c +++ b/drivers/gpu/drm/mwv207/mwv207_ttm.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include @@ -350,15 +349,15 @@ static int mwv207_bo_move(struct ttm_buffer_object *bo, bool evict, return -EMULTIHOP; } - if (old_mem->mem_type == TTM_PL_TT && new_mem->mem_type == TTM_PL_VRAM) { + if (old_mem->mem_type == TTM_PL_TT && new_mem->mem_type == TTM_PL_VRAM) ret = mwv207_move_gtt_vram(bo, evict, ctx, new_mem); - } else if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_TT) { + else if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_TT) ret = mwv207_move_vram_gtt(bo, evict, ctx, new_mem); - } else if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_VRAM) { + else if (old_mem->mem_type == TTM_PL_VRAM && new_mem->mem_type == TTM_PL_VRAM) ret = mwv207_move_vram_vram(bo, evict, ctx, new_mem); - } else { + else ret = -EINVAL; - } + if (ret) { ret = ttm_bo_move_memcpy(bo, ctx, new_mem); if (ret) diff --git a/drivers/gpu/drm/mwv207/mwv207_ttm.h b/drivers/gpu/drm/mwv207/mwv207_ttm.h index 9cbf61ed32004..6c93e4002e238 100644 --- a/drivers/gpu/drm/mwv207/mwv207_ttm.h +++ b/drivers/gpu/drm/mwv207/mwv207_ttm.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_TTM_H_TJTW9M4R #define MWV207_TTM_H_TJTW9M4R #include diff --git a/drivers/gpu/drm/mwv207/mwv207_vbios.c b/drivers/gpu/drm/mwv207/mwv207_vbios.c index 0da8244efdb97..7620996a02dae 100644 --- a/drivers/gpu/drm/mwv207/mwv207_vbios.c +++ b/drivers/gpu/drm/mwv207/mwv207_vbios.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207.h" #include "mwv207_vbios.h" diff --git a/drivers/gpu/drm/mwv207/mwv207_vbios.h b/drivers/gpu/drm/mwv207/mwv207_vbios.h index 75796f865645b..ed1d31ab07bdd 100644 --- a/drivers/gpu/drm/mwv207/mwv207_vbios.h +++ b/drivers/gpu/drm/mwv207/mwv207_vbios.h @@ -1,18 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #ifndef MWV207_VBIOS_H_D2JHN3FG #define MWV207_VBIOS_H_D2JHN3FG diff --git a/drivers/gpu/drm/mwv207/mwv207_vcmd.c b/drivers/gpu/drm/mwv207/mwv207_vcmd.c index e5128c2be0d6d..d9d245f610448 100644 --- a/drivers/gpu/drm/mwv207/mwv207_vcmd.c +++ b/drivers/gpu/drm/mwv207/mwv207_vcmd.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include #include #include "mwv207.h" diff --git a/drivers/gpu/drm/mwv207/selftest/selftest.c b/drivers/gpu/drm/mwv207/selftest/selftest.c index 04c3121f37737..48399d85671be 100644 --- a/drivers/gpu/drm/mwv207/selftest/selftest.c +++ b/drivers/gpu/drm/mwv207/selftest/selftest.c @@ -1,18 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -* SPDX-License-Identifier: GPL -* -* Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. -* All rights reserved. -* -* Author: -* shanjinkui -* -* The software and information contained herein is proprietary and -* confidential to JingJiaMicro Electronics. This software can only be -* used by JingJiaMicro Electronics Corporation. Any use, reproduction, -* or disclosure without the written permission of JingJiaMicro -* Electronics Corporation is strictly prohibited. -*/ + * Copyright (c) 2020 ChangSha JingJiaMicro Electronics Co., Ltd. + * All rights reserved. + * + * Author: + * shanjinkui + * + * The software and information contained herein is proprietary and + * confidential to JingJiaMicro Electronics. This software can only be + * used by JingJiaMicro Electronics Corporation. Any use, reproduction, + * or disclosure without the written permission of JingJiaMicro + * Electronics Corporation is strictly prohibited. + */ #include "mwv207.h" #include "mwv207_bo.h" #include "mwv207_dma.h"