diff options
26 files changed, 8124 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml b/Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml new file mode 100644 index 000000000000..04a8a3e21c7b --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 NXP +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/display/imx/nxp,imx8mq-dcss.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: iMX8MQ Display Controller Subsystem (DCSS) + +maintainers: + - Laurentiu Palcu <laurentiu.palcu@nxp.com> + +description: + + The DCSS (display controller sub system) is used to source up to three + display buffers, compose them, and drive a display using HDMI 2.0a(with HDCP + 2.2) or MIPI-DSI. The DCSS is intended to support up to 4kp60 displays. HDR10 + image processing capabilities are included to provide a solution capable of + driving next generation high dynamic range displays. + +properties: + compatible: + const: nxp,imx8mq-dcss + + reg: + maxItems: 2 + + interrupts: + maxItems: 3 + items: + - description: Context loader completion and error interrupt + - description: DTG interrupt used to signal context loader trigger time + - description: DTG interrupt for Vblank + + interrupt-names: + maxItems: 3 + items: + - const: ctx_ld + - const: ctxld_kick + - const: vblank + - const: dtrc_ch1 + - const: dtrc_ch2 + + clocks: + maxItems: 5 + items: + - description: Display APB clock for all peripheral PIO access interfaces + - description: Display AXI clock needed by DPR, Scaler, RTRAM_CTRL + - description: RTRAM clock + - description: Pixel clock, can be driver either by HDMI phy clock or MIPI + - description: DTRC clock, needed by video decompressor + - description: PLL source clock, usually VIDEO2_PLL, used when output is HDMI; + - description: PLL PHY reference clock, used when output is HDMI; + + clock-names: + items: + - const: apb + - const: axi + - const: rtrm + - const: pix + - const: dtrc + - const: pll_src + - const: pll_phy_ref + + port@0: + type: object + description: A port node pointing to a hdmi_in or mipi_in port node. + +examples: + - | + dcss: display-controller@32e00000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,imx8mq-dcss"; + reg = <0x32e00000 0x2d000>, <0x32e2f000 0x1000>; + interrupts = <6>, <8>, <9>, <16>, <17>; + interrupt-names = "ctx_ld", "ctxld_kick", "vblank", "dtrc_ch1", "dtrc_ch2"; + interrupt-parent = <&irqsteer>; + clocks = <&clk 248>, <&clk 247>, <&clk 249>, + <&clk 254>,<&clk 122>, <&clk 266>, <&clk 267>; + clock-names = "apb", "axi", "rtrm", "pix", "dtrc", + "pll_src", "pll_phy_ref"; + assigned-clocks = <&clk 107>, <&clk 109>, <&clk 266>; + assigned-clock-parents = <&clk 78>, <&clk 78>, <&clk 3>; + assigned-clock-rates = <800000000>, + <400000000>; + port@0 { + dcss_out: endpoint { + remote-endpoint = <&hdmi_in>; + }; + }; + }; + diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 82ff826b33cc..a6191f8d58fa 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -98,7 +98,7 @@ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STM) += stm/ obj-$(CONFIG_DRM_STI) += sti/ -obj-$(CONFIG_DRM_IMX) += imx/ +obj-y += imx/ obj-$(CONFIG_DRM_INGENIC) += ingenic/ obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ obj-$(CONFIG_DRM_MESON) += meson/ diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index 207bf7409dfb..6231048aa5aa 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -39,3 +39,5 @@ config DRM_IMX_HDMI depends on DRM_IMX help Choose this if you want to use HDMI on i.MX6. + +source "drivers/gpu/drm/imx/dcss/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 21cdcc2faabc..b644deffe948 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o +obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ diff --git a/drivers/gpu/drm/imx/dcss/Kconfig b/drivers/gpu/drm/imx/dcss/Kconfig new file mode 100644 index 000000000000..6399c12d4a1f --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/Kconfig @@ -0,0 +1,7 @@ +config DRM_IMX_DCSS + tristate "i.MX8MQ DCSS" + select RESET_CONTROLLER + select IMX_IRQSTEER + help + Choose this if you have a NXP i.MX8MQ based system and want to use the + Display Controller Subsystem. This option enables DCSS support. diff --git a/drivers/gpu/drm/imx/dcss/Makefile b/drivers/gpu/drm/imx/dcss/Makefile new file mode 100644 index 000000000000..eb3a1860edd0 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/Makefile @@ -0,0 +1,7 @@ +imx-dcss-objs := dcss-drv.o dcss-dev.o dcss-blkctl.o dcss-ctxld.o dcss-dtg.o \ + dcss-ss.o dcss-dpr.o dcss-scaler.o dcss-kms.o dcss-crtc.o \ + dcss-plane.o dcss-dec400d.o dcss-hdr10.o dcss-wrscl.o \ + dcss-rdsrc.o dcss-dtrc.o + +obj-$(CONFIG_DRM_IMX_DCSS) += imx-dcss.o + diff --git a/drivers/gpu/drm/imx/dcss/dcss-blkctl.c b/drivers/gpu/drm/imx/dcss/dcss-blkctl.c new file mode 100644 index 000000000000..ee7ffa18dd6d --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-blkctl.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/device.h> +#include <linux/of.h> + +#include "dcss-dev.h" + +#define DCSS_BLKCTL_RESET_CTRL 0x00 +#define B_CLK_RESETN BIT(0) +#define APB_CLK_RESETN BIT(1) +#define P_CLK_RESETN BIT(2) +#define RTR_CLK_RESETN BIT(3) +#define DCSS_BLKCTL_CONTROL0 0x10 +#define HDMI_MIPI_CLK_SEL BIT(0) +#define DISPMIX_REFCLK_SEL_POS 4 +#define DISPMIX_REFCLK_SEL_MASK GENMASK(5, 4) +#define DISPMIX_PIXCLK_SEL BIT(8) +#define HDMI_SRC_SECURE_EN BIT(16) + +struct dcss_blkctl { + struct device *dev; + void __iomem *base_reg; + + bool hdmi_output; +}; + +void dcss_blkctl_cfg(struct dcss_blkctl *blkctl) +{ + if (blkctl->hdmi_output) + dcss_writel(0, blkctl->base_reg + DCSS_BLKCTL_CONTROL0); + else + dcss_writel(DISPMIX_PIXCLK_SEL, + blkctl->base_reg + DCSS_BLKCTL_CONTROL0); + + dcss_set(B_CLK_RESETN | APB_CLK_RESETN | P_CLK_RESETN | RTR_CLK_RESETN, + blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL); +} + +int dcss_blkctl_init(struct dcss_dev *dcss, unsigned long blkctl_base) +{ + struct dcss_blkctl *blkctl; + + blkctl = devm_kzalloc(dcss->dev, sizeof(*blkctl), GFP_KERNEL); + if (!blkctl) + return -ENOMEM; + + blkctl->base_reg = devm_ioremap(dcss->dev, blkctl_base, SZ_4K); + if (!blkctl->base_reg) { + dev_err(dcss->dev, "unable to remap BLK CTRL base\n"); + devm_kfree(dcss->dev, blkctl); + return -ENOMEM; + } + + dcss->blkctl = blkctl; + blkctl->dev = dcss->dev; + blkctl->hdmi_output = dcss->hdmi_output; + + dcss_blkctl_cfg(blkctl); + + return 0; +} + +void dcss_blkctl_exit(struct dcss_blkctl *blkctl) +{ + dcss_clr(P_CLK_RESETN | RTR_CLK_RESETN, + blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL); + + if (blkctl->base_reg) + devm_iounmap(blkctl->dev, blkctl->base_reg); + + devm_kfree(blkctl->dev, blkctl); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-crtc.c b/drivers/gpu/drm/imx/dcss/dcss-crtc.c new file mode 100644 index 000000000000..f902db364325 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-crtc.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <linux/pm_runtime.h> + +#include "dcss-dev.h" +#include "dcss-kms.h" + +static int dcss_enable_vblank(struct drm_crtc *crtc) +{ + struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, + base); + struct dcss_dev *dcss = crtc->dev->dev_private; + + if (dcss_crtc->irq_enabled) + return 0; + + dcss_crtc->irq_enabled = true; + + dcss_dtg_vblank_irq_enable(dcss->dtg, true); + + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); + + enable_irq(dcss_crtc->irq); + + return 0; +} + +static void dcss_disable_vblank(struct drm_crtc *crtc) +{ + struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, + base); + struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; + + disable_irq_nosync(dcss_crtc->irq); + + dcss_dtg_vblank_irq_enable(dcss->dtg, false); + + if (!dcss_dtrc_is_running(dcss->dtrc)) + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false); + + dcss_crtc->irq_enabled = false; +} + +static const struct drm_crtc_funcs dcss_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = drm_crtc_cleanup, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = dcss_enable_vblank, + .disable_vblank = dcss_disable_vblank, +}; + +static void dcss_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + drm_crtc_vblank_on(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc)); + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static void dcss_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, + base); + struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; + + if (dcss_dtg_is_enabled(dcss->dtg)) + dcss_ctxld_enable(dcss->ctxld); +} + +static void dcss_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, + base); + struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct videomode vm; + + drm_display_mode_to_videomode(mode, &vm); + + pm_runtime_get_sync(dcss->dev); + + dcss_enable_vblank(crtc); + + vm.pixelclock = mode->crtc_clock * 1000; + + dcss_dtg_sync_set(dcss->dtg, &vm); + + dcss_ss_subsam_set(dcss->ss, dcss_crtc->output_is_yuv); + dcss_ss_sync_set(dcss->ss, &vm, mode->flags & DRM_MODE_FLAG_PHSYNC, + mode->flags & DRM_MODE_FLAG_PVSYNC); + + dcss_dtg_css_set(dcss->dtg, dcss_crtc->output_is_yuv); + + dcss_ss_enable(dcss->ss); + dcss_dtg_enable(dcss->dtg, true, NULL); + dcss_ctxld_enable(dcss->ctxld); + + reinit_completion(&dcss_crtc->en_completion); + wait_for_completion_timeout(&dcss_crtc->en_completion, + msecs_to_jiffies(500)); +} + +static void dcss_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, + base); + struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; + + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); + + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); + + dcss_ss_disable(dcss->ss); + dcss_dtg_enable(dcss->dtg, false, &dcss_crtc->dis_completion); + dcss_ctxld_enable(dcss->ctxld); + + reinit_completion(&dcss_crtc->dis_completion); + wait_for_completion_timeout(&dcss_crtc->dis_completion, + msecs_to_jiffies(100)); + + drm_crtc_vblank_off(crtc); + + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false); + + pm_runtime_put_sync(dcss->dev); +} + +static const struct drm_crtc_helper_funcs dcss_helper_funcs = { + .atomic_begin = dcss_crtc_atomic_begin, + .atomic_flush = dcss_crtc_atomic_flush, + .atomic_enable = dcss_crtc_atomic_enable, + .atomic_disable = dcss_crtc_atomic_disable, +}; + +static irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id) +{ + struct dcss_crtc *dcss_crtc = dev_id; + struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; + + if (!dcss_dtg_vblank_irq_valid(dcss->dtg)) + return IRQ_HANDLED; + + complete(&dcss_crtc->en_completion); + + if (dcss_ctxld_is_flushed(dcss->ctxld)) + drm_crtc_handle_vblank(&dcss_crtc->base); + + dcss_dtg_vblank_irq_clear(dcss->dtg); + + return IRQ_HANDLED; +} + +int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm) +{ + struct dcss_dev *dcss = drm->dev_private; + struct platform_device *pdev = to_platform_device(dcss->dev); + int ret; + + crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base), + DRM_PLANE_TYPE_PRIMARY, 2); + if (IS_ERR(crtc->plane[0])) + return PTR_ERR(crtc->plane[0]); + + crtc->base.port = dcss->of_port; + + drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs); + ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base, + NULL, &dcss_crtc_funcs, NULL); + if (ret) { + dev_err(dcss->dev, "failed to init crtc\n"); + return ret; + } + + crtc->plane[1] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base), + DRM_PLANE_TYPE_OVERLAY, 1); + if (IS_ERR(crtc->plane[1])) + crtc->plane[1] = NULL; + + crtc->plane[2] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base), + DRM_PLANE_TYPE_OVERLAY, 0); + if (IS_ERR(crtc->plane[2])) + crtc->plane[2] = NULL; + + drm_plane_create_alpha_property(&crtc->plane[0]->base); + + crtc->irq = platform_get_irq_byname(pdev, "vblank"); + if (crtc->irq < 0) { + dev_err(dcss->dev, "unable to get vblank interrupt\n"); + return crtc->irq; + } + + init_completion(&crtc->en_completion); + init_completion(&crtc->dis_completion); + + ret = devm_request_irq(dcss->dev, crtc->irq, dcss_crtc_irq_handler, + IRQF_TRIGGER_RISING, "dcss_drm", crtc); + if (ret) { + dev_err(dcss->dev, "irq request failed with %d.\n", ret); + return ret; + } + + disable_irq(crtc->irq); + + return 0; +} + +void dcss_crtc_attach_color_mgmt_properties(struct dcss_crtc *crtc) +{ + int i; + + /* create color management properties only for video planes */ + for (i = 1; i < 3; i++) { + if (crtc->plane[i]->type == DRM_PLANE_TYPE_PRIMARY) + return; + + drm_plane_create_color_properties(&crtc->plane[i]->base, + BIT(DRM_COLOR_YCBCR_BT601) | + BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + BIT(DRM_COLOR_YCBCR_FULL_RANGE) | + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), + DRM_COLOR_YCBCR_BT709, + DRM_COLOR_YCBCR_FULL_RANGE); + } +} + +void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm) +{ + struct dcss_dev *dcss = drm->dev_private; + + devm_free_irq(dcss->dev, crtc->irq, crtc); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-ctxld.c b/drivers/gpu/drm/imx/dcss/dcss-ctxld.c new file mode 100644 index 000000000000..74617ceb5ddb --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-ctxld.c @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#include "dcss-dev.h" + +#define DCSS_CTXLD_DEVNAME "dcss_ctxld" + +#define DCSS_CTXLD_CONTROL_STATUS 0x0 +#define CTXLD_ENABLE BIT(0) +#define ARB_SEL BIT(1) +#define RD_ERR_EN BIT(2) +#define DB_COMP_EN BIT(3) +#define SB_HP_COMP_EN BIT(4) +#define SB_LP_COMP_EN BIT(5) +#define DB_PEND_SB_REC_EN BIT(6) +#define SB_PEND_DISP_ACTIVE_EN BIT(7) +#define AHB_ERR_EN BIT(8) +#define RD_ERR BIT(16) +#define DB_COMP BIT(17) +#define SB_HP_COMP BIT(18) +#define SB_LP_COMP BIT(19) +#define DB_PEND_SB_REC BIT(20) +#define SB_PEND_DISP_ACTIVE BIT(21) +#define AHB_ERR BIT(22) +#define DCSS_CTXLD_DB_BASE_ADDR 0x10 +#define DCSS_CTXLD_DB_COUNT 0x14 +#define DCSS_CTXLD_SB_BASE_ADDR 0x18 +#define DCSS_CTXLD_SB_COUNT 0x1C +#define SB_HP_COUNT_POS 0 +#define SB_HP_COUNT_MASK 0xffff +#define SB_LP_COUNT_POS 16 +#define SB_LP_COUNT_MASK 0xffff0000 +#define DCSS_AHB_ERR_ADDR 0x20 + +#define CTXLD_IRQ_NAME "ctx_ld" +#define CTXLD_IRQ_COMPLETION (DB_COMP | SB_HP_COMP | SB_LP_COMP) +#define CTXLD_IRQ_ERROR (RD_ERR | DB_PEND_SB_REC | AHB_ERR) + +/* The following sizes are in context loader entries, 8 bytes each. */ +#define CTXLD_DB_CTX_ENTRIES 1024 /* max 65536 */ +#define CTXLD_SB_LP_CTX_ENTRIES 10240 /* max 65536 */ +#define CTXLD_SB_HP_CTX_ENTRIES 20000 /* max 65536 */ +#define CTXLD_SB_CTX_ENTRIES (CTXLD_SB_LP_CTX_ENTRIES + \ + CTXLD_SB_HP_CTX_ENTRIES) + +/* Sizes, in entries, of the DB, SB_HP and SB_LP context regions. */ +static u16 dcss_ctxld_ctx_size[3] = { + CTXLD_DB_CTX_ENTRIES, + CTXLD_SB_HP_CTX_ENTRIES, + CTXLD_SB_LP_CTX_ENTRIES +}; + +/* this represents an entry in the context loader map */ +struct dcss_ctxld_item { + u32 val; + u32 ofs; +}; + +#define CTX_ITEM_SIZE sizeof(struct dcss_ctxld_item) + +struct dcss_ctxld { + struct device *dev; + void __iomem *ctxld_reg; + int irq; + bool irq_en; + + struct dcss_ctxld_item *db[2]; + struct dcss_ctxld_item *sb_hp[2]; + struct dcss_ctxld_item *sb_lp[2]; + + dma_addr_t db_paddr[2]; + dma_addr_t sb_paddr[2]; + + u16 ctx_size[2][3]; /* holds the sizes of DB, SB_HP and SB_LP ctx */ + u8 current_ctx; + + bool in_use; + bool armed; + + spinlock_t lock; /* protects concurent access to private data */ + + void (*dtg_disable_cb)(void *data); + void *dtg_disable_data; +}; + +static int __dcss_ctxld_enable(struct dcss_ctxld *ctxld); + +static irqreturn_t dcss_ctxld_irq_handler(int irq, void *data) +{ + struct dcss_ctxld *ctxld = data; + u32 irq_status; + + irq_status = dcss_readl(ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS); + + if (irq_status & CTXLD_IRQ_COMPLETION && + !(irq_status & CTXLD_ENABLE) && ctxld->in_use) { + ctxld->in_use = false; + + if (ctxld->dtg_disable_cb) { + ctxld->dtg_disable_cb(ctxld->dtg_disable_data); + ctxld->dtg_disable_cb = NULL; + ctxld->dtg_disable_data = NULL; + } + } else if (irq_status & CTXLD_IRQ_ERROR) { + /* + * Except for throwing an error message and clearing the status + * register, there's not much we can do here. + */ + dev_err(ctxld->dev, "ctxld: error encountered: %08x\n", + irq_status); + dev_err(ctxld->dev, "ctxld: db=%d, sb_hp=%d, sb_lp=%d\n", + ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_DB], + ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_HP], + ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_LP]); + } + + dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION), + ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS); + + return IRQ_HANDLED; +} + +static int dcss_ctxld_irq_config(struct dcss_ctxld *ctxld, + struct platform_device *pdev) +{ + int ret; + + ctxld->irq = platform_get_irq_byname(pdev, CTXLD_IRQ_NAME); + if (ctxld->irq < 0) { + dev_err(ctxld->dev, "ctxld: can't get irq number\n"); + return ctxld->irq; + } + + ret = devm_request_irq(ctxld->dev, ctxld->irq, + dcss_ctxld_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + DCSS_CTXLD_DEVNAME, ctxld); + if (ret) { + dev_err(ctxld->dev, "ctxld: irq request failed.\n"); + return ret; + } + + ctxld->irq_en = true; + + return 0; +} + +void dcss_ctxld_hw_cfg(struct dcss_ctxld *ctxld) +{ + dcss_writel(RD_ERR_EN | SB_HP_COMP_EN | + DB_PEND_SB_REC_EN | AHB_ERR_EN | RD_ERR | AHB_ERR, + ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS); +} + +static void dcss_ctxld_free_ctx(struct dcss_ctxld *ctxld) +{ + struct dcss_ctxld_item *ctx; + int i; + + for (i = 0; i < 2; i++) { + if (ctxld->db[i]) { + dmam_free_coherent(ctxld->dev, + CTXLD_DB_CTX_ENTRIES * sizeof(*ctx), + ctxld->db[i], ctxld->db_paddr[i]); + ctxld->db[i] = NULL; + ctxld->db_paddr[i] = 0; + } + + if (ctxld->sb_hp[i]) { + dmam_free_coherent(ctxld->dev, + CTXLD_SB_CTX_ENTRIES * sizeof(*ctx), + ctxld->sb_hp[i], ctxld->sb_paddr[i]); + ctxld->sb_hp[i] = NULL; + ctxld->sb_paddr[i] = 0; + } + } +} + +static int dcss_ctxld_alloc_ctx(struct dcss_ctxld *ctxld) +{ + struct dcss_ctxld_item *ctx; + int i; + dma_addr_t dma_handle; + + for (i = 0; i < 2; i++) { + ctx = dmam_alloc_coherent(ctxld->dev, + CTXLD_DB_CTX_ENTRIES * sizeof(*ctx), + &dma_handle, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctxld->db[i] = ctx; + ctxld->db_paddr[i] = dma_handle; + + ctx = dmam_alloc_coherent(ctxld->dev, + CTXLD_SB_CTX_ENTRIES * sizeof(*ctx), + &dma_handle, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctxld->sb_hp[i] = ctx; + ctxld->sb_lp[i] = ctx + CTXLD_SB_HP_CTX_ENTRIES; + + ctxld->sb_paddr[i] = dma_handle; + } + + return 0; +} + +int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base) +{ + struct dcss_ctxld *ctxld; + int ret; + + ctxld = devm_kzalloc(dcss->dev, sizeof(struct dcss_ctxld), + GFP_KERNEL); + if (!ctxld) + return -ENOMEM; + + dcss->ctxld = ctxld; + ctxld->dev = dcss->dev; + + spin_lock_init(&ctxld->lock); + + ret = dcss_ctxld_alloc_ctx(ctxld); + if (ret) { + dev_err(dcss->dev, "ctxld: cannot allocate context memory.\n"); + goto err; + } + + ctxld->ctxld_reg = devm_ioremap(dcss->dev, ctxld_base, SZ_4K); + if (!ctxld->ctxld_reg) { + dev_err(dcss->dev, "ctxld: unable to remap ctxld base\n"); + ret = -ENOMEM; + goto err; + } + + ret = dcss_ctxld_irq_config(ctxld, to_platform_device(dcss->dev)); + if (ret) + goto err_irq; + + dcss_ctxld_hw_cfg(ctxld); + + return 0; + +err_irq: + devm_iounmap(ctxld->dev, ctxld->ctxld_reg); + +err: + dcss_ctxld_free_ctx(ctxld); + devm_kfree(ctxld->dev, ctxld); + + return ret; +} + +void dcss_ctxld_exit(struct dcss_ctxld *ctxld) +{ + devm_free_irq(ctxld->dev, ctxld->irq, ctxld); + + if (ctxld->ctxld_reg) + devm_iounmap(ctxld->dev, ctxld->ctxld_reg); + + dcss_ctxld_free_ctx(ctxld); + devm_kfree(ctxld->dev, ctxld); +} + +static int __dcss_ctxld_enable(struct dcss_ctxld *ctxld) +{ + int curr_ctx = ctxld->current_ctx; + u32 db_base, sb_base, sb_count; + u32 sb_hp_cnt, sb_lp_cnt, db_cnt; + struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev); + + dcss_dpr_write_sysctrl(dcss->dpr); + + dcss_scaler_write_sclctrl(dcss->scaler); + + if (dcss_dtrc_is_running(dcss->dtrc)) { + dcss_dtrc_switch_banks(dcss->dtrc); + ctxld->armed = true; + } + + sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP]; + sb_lp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_LP]; + db_cnt = ctxld->ctx_size[curr_ctx][CTX_DB]; + + /* make sure SB_LP context area comes after SB_HP */ + if (sb_lp_cnt && + ctxld->sb_lp[curr_ctx] != ctxld->sb_hp[curr_ctx] + sb_hp_cnt) { + struct dcss_ctxld_item *sb_lp_adjusted; + + sb_lp_adjusted = ctxld->sb_hp[curr_ctx] + sb_hp_cnt; + + memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx], + sb_lp_cnt * CTX_ITEM_SIZE); + } + + db_base = db_cnt ? ctxld->db_paddr[curr_ctx] : 0; + + dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_ADDR); + dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT); + + if (sb_hp_cnt) + sb_count = ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK) | + ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_COUNT_MASK); + else + sb_count = (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK; + + sb_base = sb_count ? ctxld->sb_paddr[curr_ctx] : 0; + + dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_ADDR); + dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT); + + /* enable the context loader */ + dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS); + + ctxld->in_use = true; + + /* + * Toggle the current context to the alternate one so that any updates + * in the modules' settings take place there. + */ + ctxld->current_ctx ^= 1; + + ctxld->ctx_size[ctxld->current_ctx][CTX_DB] = 0; + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] = 0; + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] = 0; + + return 0; +} + +int dcss_ctxld_enable(struct dcss_ctxld *ctxld) +{ + unsigned long flags; + + spin_lock_irqsave(&ctxld->lock, flags); + ctxld->armed = true; + spin_unlock_irqrestore(&ctxld->lock, flags); + + return 0; +} + +void dcss_ctxld_kick(struct dcss_ctxld *ctxld) +{ + unsigned long flags; + + spin_lock_irqsave(&ctxld->lock, flags); + if (ctxld->armed && !ctxld->in_use) { + ctxld->armed = false; + __dcss_ctxld_enable(ctxld); + } + spin_unlock_irqrestore(&ctxld->lock, flags); +} + +void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, u32 val, + u32 reg_ofs) +{ + int curr_ctx = ctxld->current_ctx; + struct dcss_ctxld_item *ctx[] = { + [CTX_DB] = ctxld->db[curr_ctx], + [CTX_SB_HP] = ctxld->sb_hp[curr_ctx], + [CTX_SB_LP] = ctxld->sb_lp[curr_ctx] + }; + int item_idx = ctxld->ctx_size[curr_ctx][ctx_id]; + + if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) { + WARN_ON(1); + return; + } + + ctx[ctx_id][item_idx].val = val; + ctx[ctx_id][item_idx].ofs = reg_ofs; + ctxld->ctx_size[curr_ctx][ctx_id] += 1; +} + +void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id, + u32 val, u32 reg_ofs) +{ + unsigned long flags; + + spin_lock_irqsave(&ctxld->lock, flags); + dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs); + spin_unlock_irqrestore(&ctxld->lock, flags); +} + +bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld) +{ + return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 && + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 && + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0; +} + +int dcss_ctxld_resume(struct dcss_ctxld *ctxld) +{ + dcss_ctxld_hw_cfg(ctxld); + + if (!ctxld->irq_en) { + enable_irq(ctxld->irq); + ctxld->irq_en = true; + } + + return 0; +} + +int dcss_ctxld_suspend(struct dcss_ctxld *ctxld) +{ + int ret = 0; + int wait_time_ms = 0; + unsigned long flags; + + dcss_ctxld_kick(ctxld); + + while (ctxld->in_use && wait_time_ms < 500) { + msleep(20); + wait_time_ms += 20; + } + + if (wait_time_ms > 500) + return -ETIMEDOUT; + + spin_lock_irqsave(&ctxld->lock, flags); + + if (ctxld->irq_en) { + disable_irq_nosync(ctxld->irq); + ctxld->irq_en = false; + } + + /* reset context region and sizes */ + ctxld->current_ctx = 0; + ctxld->ctx_size[0][CTX_DB] = 0; + ctxld->ctx_size[0][CTX_SB_HP] = 0; + ctxld->ctx_size[0][CTX_SB_LP] = 0; + + spin_unlock_irqrestore(&ctxld->lock, flags); + + return ret; +} + +void dcss_ctxld_register_dtg_disable_cb(struct dcss_ctxld *ctxld, + void (*cb)(void *), + void *data) +{ + ctxld->dtg_disable_cb = cb; + ctxld->dtg_disable_data = data; +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-dec400d.c b/drivers/gpu/drm/imx/dcss/dcss-dec400d.c new file mode 100644 index 000000000000..44675b686228 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-dec400d.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/device.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <drm/drm_fourcc.h> + +#include "dcss-dev.h" + +/* DEC400D registers offsets */ +#define DEC400D_READCONFIG_BASE 0x800 +#define DEC400D_READCONFIG(i) (DEC400D_READCONFIG_BASE + ((i) << 2)) +#define COMPRESSION_ENABLE_BIT BIT(0) +#define COMPRESSION_FORMAT_POS 3 +#define COMPRESSION_ALIGN_MODE_POS 16 +#define TILE_ALIGN_MODE_POS 22 +#define TILE_MODE_POS 25 +#define DEC400D_READBUFFERBASE0 0x900 +#define DEC400D_READCACHEBASE0 0x980 +#define DEC400D_CONTROL 0xB00 +#define DEC400D_CLEAR 0xB80 +#define DEC400D_READBUFFERBASE0 0x900 +#define DEC400D_READCACHEBASE0 0x980 +#define DEC400D_CONTROL 0xB00 +#define DISABLE_COMPRESSION_BIT BIT(1) +#define SHADOW_TRIGGER_BIT BIT(29) +#define DEC400_CFMT_ARGB8 0x0 +#define DEC400_CFMT_XRGB8 0x1 +#define DEC400_CFMT_AYUV 0x2 +#define DEC400_CFMT_UYVY 0x3 +#define DEC400_CFMT_YUY2 0x4 +#define DEC400_CFMT_YUV_ONLY 0x5 +#define DEC400_CFMT_UV_MIX 0x6 +#define DEC400_CFMT_ARGB4 0x7 +#define DEC400_CFMT_XRGB4 0x8 +#define DEC400_CFMT_A1R5G5B5 0x9 +#define DEC400_CFMT_X1R5G5B5 0xA +#define DEC400_CFMT_R5G6B5 0xB +#define DEC400_CFMT_Z24S8 0xC +#define DEC400_CFMT_Z24 0xD +#define DEC400_CFMT_Z16 0xE +#define DEC400_CFMT_A2R10G10B10 0xF +#define DEC400_CFMT_BAYER 0x10 +#define DEC400_CFMT_SIGNED_BAYER 0x11 + +struct dcss_dec400d { + struct device *dev; + void __iomem *base_reg; + u32 base_ofs; + struct dcss_ctxld *ctxld; + u32 ctx_id; + bool bypass; /* bypass or decompress */ +}; + +static void dcss_dec400d_write(struct dcss_dec400d *dec400d, + u32 value, + u32 offset) +{ + dcss_ctxld_write(dec400d->ctxld, dec400d->ctx_id, + value, dec400d->base_ofs + offset); +} + +int dcss_dec400d_init(struct dcss_dev *dcss, unsigned long dec400d_base) +{ + struct dcss_dec400d *dec400d; + int ret; + + dec400d = devm_kzalloc(dcss->dev, sizeof(*dec400d), GFP_KERNEL); + if (!dec400d) + return -ENOMEM; + + dcss->dec400d = dec400d; + dec400d->dev = dcss->dev; + dec400d->ctxld = dcss->ctxld; + + dec400d->base_reg = devm_ioremap(dcss->dev, dec400d_base, SZ_4K); + if (!dec400d->base_reg) { + dev_err(dcss->dev, "dec400d: unable to remap dec400d base\n"); + ret = -ENOMEM; + goto free_mem; + } + + dec400d->base_ofs = dec400d_base; + + dec400d->ctx_id = CTX_SB_HP; + + return 0; + +free_mem: + devm_kfree(dcss->dev, dcss->dec400d); + return ret; +} + +void dcss_dec400d_exit(struct dcss_dec400d *dec400d) +{ + if (dec400d->base_reg) + devm_iounmap(dec400d->dev, dec400d->base_reg); + + devm_kfree(dec400d->dev, dec400d); +} + +void dcss_dec400d_read_config(struct dcss_dec400d *dec400d, + u32 read_id, + bool compress_en, + u32 compress_format) +{ + u32 cformat = 0; + u32 read_config = 0x0; + + /* TODO: using 'read_id' 0 by default */ + if (read_id) { + WARN_ON(1); + return; + } + + if (!compress_en) + goto config; + + switch (compress_format) { + case _VIV_CFMT_ARGB8: + cformat = DEC400_CFMT_ARGB8; + break; + case _VIV_CFMT_XRGB8: + cformat = DEC400_CFMT_XRGB8; + break; + case _VIV_CFMT_AYUV: + cformat = DEC400_CFMT_AYUV; + break; + case _VIV_CFMT_UYVY: + cformat = DEC400_CFMT_UYVY; + break; + case _VIV_CFMT_YUY2: + cformat = DEC400_CFMT_YUY2; + break; + case _VIV_CFMT_YUV_ONLY: + cformat = DEC400_CFMT_YUV_ONLY; + break; + case _VIV_CFMT_UV_MIX: + cformat = DEC400_CFMT_UV_MIX; + break; + case _VIV_CFMT_ARGB4: + cformat = DEC400_CFMT_ARGB4; + break; + case _VIV_CFMT_XRGB4: + cformat = DEC400_CFMT_XRGB4; + break; + case _VIV_CFMT_A1R5G5B5: + cformat = DEC400_CFMT_A1R5G5B5; + break; + case _VIV_CFMT_X1R5G5B5: + cformat = DEC400_CFMT_X1R5G5B5; + break; + case _VIV_CFMT_R5G6B5: + cformat = DEC400_CFMT_R5G6B5; + break; + case _VIV_CFMT_Z24S8: + cformat = DEC400_CFMT_Z24S8; + break; + case _VIV_CFMT_Z24: + cformat = DEC400_CFMT_Z24; + break; + case _VIV_CFMT_Z16: + cformat = DEC400_CFMT_Z16; + break; + case _VIV_CFMT_A2R10G10B10: + cformat = DEC400_CFMT_A2R10G10B10; + break; + case _VIV_CFMT_BAYER: + cformat = DEC400_CFMT_BAYER; + break; + case _VIV_CFMT_SIGNED_BAYER: + cformat = DEC400_CFMT_SIGNED_BAYER; + break; + default: + /* TODO: not support yet */ + WARN_ON(1); + return; + } + + /* Dec compress format */ + read_config |= cformat << COMPRESSION_FORMAT_POS; + + /* ALIGN32_BYTE */ + read_config |= 0x2 << COMPRESSION_ALIGN_MODE_POS; + + /* TILE1_ALIGN */ + read_config |= 0x0 << TILE_ALIGN_MODE_POS; + + /* TILE8x4 */ + read_config |= 0x3 << TILE_MODE_POS; + + /* Compression Enable */ + read_config |= COMPRESSION_ENABLE_BIT; + +config: + dcss_dec400d_write(dec400d, read_config, DEC400D_READCONFIG(read_id)); +} + +void dcss_dec400d_bypass(struct dcss_dec400d *dec400d) +{ + u32 control; + + dcss_dec400d_read_config(dec400d, 0, false, 0); + + control = dcss_readl(dec400d->base_reg + DEC400D_CONTROL); + dev_dbg(dec400d->dev, "%s: dec400d control = %#x\n", __func__, control); + + control |= DISABLE_COMPRESSION_BIT; + dcss_dec400d_write(dec400d, control, DEC400D_CONTROL); + + /* Trigger shadow registers */ + control |= SHADOW_TRIGGER_BIT; + dcss_dec400d_write(dec400d, control, DEC400D_CONTROL); + + dec400d->bypass = true; +} + +void dcss_dec400d_shadow_trig(struct dcss_dec400d *dec400d) +{ + u32 control; + + /* do nothing */ + if (dec400d->bypass) + return; + + control = dcss_readl(dec400d->base_reg + DEC400D_CONTROL); + + /* Trigger shadow registers */ + control |= SHADOW_TRIGGER_BIT; + dcss_dec400d_write(dec400d, control, DEC400D_CONTROL); +} + +void dcss_dec400d_addr_set(struct dcss_dec400d *dec400d, u32 baddr, u32 caddr) +{ + /* set frame buffer base addr */ + dcss_dec400d_write(dec400d, baddr, DEC400D_READBUFFERBASE0); + + /* set tile status cache addr */ + dcss_dec400d_write(dec400d, caddr, DEC400D_READCACHEBASE0); + + dec400d->bypass = false; +} + +void dcss_dec400d_fast_clear_config(struct dcss_dec400d *dec400d, + u32 fc_value, + bool enable) +{ + dcss_dec400d_write(dec400d, fc_value, DEC400D_CLEAR); +} + +void dcss_dec400d_enable(struct dcss_dec400d *dec400d) +{ + u32 control; + + if (dec400d->bypass) + return; + + control = dcss_readl(dec400d->base_reg + DEC400D_CONTROL); + + /* enable compression */ + control &= ~(DISABLE_COMPRESSION_BIT); + dcss_dec400d_write(dec400d, control, DEC400D_CONTROL); + + /* Trigger shadow registers */ + control |= SHADOW_TRIGGER_BIT; + dcss_dec400d_write(dec400d, control, DEC400D_CONTROL); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.c b/drivers/gpu/drm/imx/dcss/dcss-dev.c new file mode 100644 index 000000000000..53b578d7785c --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-dev.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/clk.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/pm_runtime.h> +#include <linux/pm_qos.h> +#include <linux/busfreq-imx.h> +#include <drm/drm_modeset_helper.h> + +#include "dcss-dev.h" + +static void dcss_clocks_enable(struct dcss_dev *dcss) +{ + if (dcss->clks_on) + return; + + if (dcss->hdmi_output) { + clk_prepare_enable(dcss->pll_phy_ref_clk); + clk_prepare_enable(dcss->pll_src_clk); + } + + clk_prepare_enable(dcss->axi_clk); + clk_prepare_enable(dcss->apb_clk); + clk_prepare_enable(dcss->rtrm_clk); + clk_prepare_enable(dcss->dtrc_clk); + clk_prepare_enable(dcss->pix_clk); + + dcss->clks_on = true; +} + +static void dcss_clocks_disable(struct dcss_dev *dcss) +{ + if (!dcss->clks_on) + return; + + clk_disable_unprepare(dcss->pix_clk); + clk_disable_unprepare(dcss->dtrc_clk); + clk_disable_unprepare(dcss->rtrm_clk); + clk_disable_unprepare(dcss->apb_clk); + clk_disable_unprepare(dcss->axi_clk); + + if (dcss->hdmi_output) { + clk_disable_unprepare(dcss->pll_src_clk); + clk_disable_unprepare(dcss->pll_phy_ref_clk); + } + + dcss->clks_on = false; +} + +static void dcss_busfreq_enable(struct dcss_dev *dcss) +{ + if (dcss->bus_freq_on) + return; + + request_bus_freq(BUS_FREQ_HIGH); + + dcss->bus_freq_on = true; +} + +static void dcss_busfreq_disable(struct dcss_dev *dcss) +{ + if (!dcss->bus_freq_on) + return; + + release_bus_freq(BUS_FREQ_HIGH); + + dcss->bus_freq_on = false; +} + +static int dcss_submodules_init(struct dcss_dev *dcss) +{ + int ret = 0; + u32 base_addr = dcss->start_addr; + const struct dcss_type_data *devtype = dcss->devtype; + + dcss_clocks_enable(dcss); + + ret = dcss_blkctl_init(dcss, base_addr + devtype->blkctl_ofs); + if (ret) + return ret; + + ret = dcss_ctxld_init(dcss, base_addr + devtype->ctxld_ofs); + if (ret) + goto ctxld_err; + + ret = dcss_dtg_init(dcss, base_addr + devtype->dtg_ofs); + if (ret) + goto dtg_err; + + ret = dcss_ss_init(dcss, base_addr + devtype->ss_ofs); + if (ret) + goto ss_err; + + ret = dcss_dtrc_init(dcss, base_addr + devtype->dtrc_ofs); + if (ret) + goto dtrc_err; + + ret = dcss_dpr_init(dcss, base_addr + devtype->dpr_ofs); + if (ret) + goto dpr_err; + + ret = dcss_wrscl_init(dcss, base_addr + devtype->wrscl_ofs); + if (ret) + goto wrscl_err; + + ret = dcss_rdsrc_init(dcss, base_addr + devtype->rdsrc_ofs); + if (ret) + goto rdsrc_err; + + ret = dcss_scaler_init(dcss, base_addr + devtype->scaler_ofs); + if (ret) + goto scaler_err; + + ret = dcss_dec400d_init(dcss, base_addr + devtype->dec400d_ofs); + if (ret) + goto dec400d_err; + + ret = dcss_hdr10_init(dcss, base_addr + devtype->hdr10_ofs); + if (ret) + goto hdr10_err; + + return 0; + +hdr10_err: + dcss_dec400d_exit(dcss->dec400d); + +dec400d_err: + dcss_scaler_exit(dcss->scaler); + +scaler_err: + dcss_rdsrc_exit(dcss->rdsrc); + +rdsrc_err: + dcss_wrscl_exit(dcss->wrscl); + +wrscl_err: + dcss_dpr_exit(dcss->dpr); + +dpr_err: + dcss_dtrc_exit(dcss->dtrc); + +dtrc_err: + dcss_ss_exit(dcss->ss); + +ss_err: + dcss_dtg_exit(dcss->dtg); + +dtg_err: + dcss_ctxld_exit(dcss->ctxld); + +ctxld_err: + dcss_blkctl_exit(dcss->blkctl); + + dcss_clocks_disable(dcss); + + return ret; +} + +static void dcss_submodules_stop(struct dcss_dev *dcss) +{ + dcss_clocks_enable(dcss); + dcss_hdr10_exit(dcss->hdr10); + dcss_dec400d_exit(dcss->dec400d); + dcss_scaler_exit(dcss->scaler); + dcss_rdsrc_exit(dcss->rdsrc); + dcss_wrscl_exit(dcss->wrscl); + dcss_dpr_exit(dcss->dpr); + dcss_dtrc_exit(dcss->dtrc); + dcss_ss_exit(dcss->ss); + dcss_dtg_exit(dcss->dtg); + dcss_ctxld_exit(dcss->ctxld); + dcss_blkctl_exit(dcss->blkctl); + dcss_clocks_disable(dcss); +} + +static int dcss_clks_init(struct dcss_dev *dcss) +{ + int i; + struct { + const char *id; + struct clk **clk; + bool required; + } clks[] = { + {"apb", &dcss->apb_clk, true}, + {"axi", &dcss->axi_clk, true}, + {"pix", &dcss->pix_clk, true}, + {"rtrm", &dcss->rtrm_clk, true}, + {"dtrc", &dcss->dtrc_clk, true}, + {"pll_src", &dcss->pll_src_clk, dcss->hdmi_output}, + {"pll_phy_ref", &dcss->pll_phy_ref_clk, dcss->hdmi_output}, + }; + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + *clks[i].clk = devm_clk_get(dcss->dev, clks[i].id); + if (IS_ERR(*clks[i].clk) && clks[i].required) { + dev_err(dcss->dev, "failed to get %s clock\n", + clks[i].id); + return PTR_ERR(*clks[i].clk); + } + } + + dcss->clks_on = false; + + return 0; +} + +static void dcss_clks_release(struct dcss_dev *dcss) +{ + devm_clk_put(dcss->dev, dcss->dtrc_clk); + devm_clk_put(dcss->dev, dcss->rtrm_clk); + devm_clk_put(dcss->dev, dcss->pix_clk); + devm_clk_put(dcss->dev, dcss->axi_clk); + devm_clk_put(dcss->dev, dcss->apb_clk); +} + +struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output) +{ + struct platform_device *pdev = to_platform_device(dev); + int ret; + struct resource *res; + struct dcss_dev *dcss; + const struct dcss_type_data *devtype; + + devtype = of_device_get_match_data(dev); + if (!devtype) { + dev_err(dev, "no device match found\n"); + return ERR_PTR(-ENODEV); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "cannot get memory resource\n"); + return ERR_PTR(-EINVAL); + } + + dcss = devm_kzalloc(dev, sizeof(struct dcss_dev), GFP_KERNEL); + if (!dcss) + return ERR_PTR(-ENOMEM); + + dcss->dev = dev; + dcss->devtype = devtype; + dcss->hdmi_output = hdmi_output; + + ret = dcss_clks_init(dcss); + if (ret) { + dev_err(dev, "clocks initialization failed\n"); + goto err; + } + + dcss->of_port = of_graph_get_port_by_id(dev->of_node, 0); + if (!dcss->of_port) { + dev_err(dev, "no port@0 node in %s\n", dev->of_node->full_name); + ret = -ENODEV; + goto clks_err; + } + + dcss->start_addr = res->start; + + ret = dcss_submodules_init(dcss); + if (ret) { + dev_err(dev, "submodules initialization failed\n"); + goto clks_err; + } + + pm_runtime_enable(dev); + + return dcss; + +clks_err: + dcss_clks_release(dcss); + +err: + devm_kfree(dcss->dev, dcss); + + return ERR_PTR(ret); +} + +void dcss_dev_destroy(struct dcss_dev *dcss) +{ + pm_runtime_disable(dcss->dev); + + dcss_submodules_stop(dcss); + + dcss_clks_release(dcss); + + devm_kfree(dcss->dev, dcss); +} + +#ifdef CONFIG_PM_SLEEP +int dcss_dev_suspend(struct device *dev) +{ + struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); + int ret; + + drm_mode_config_helper_suspend(dcss_drv_dev_to_drm(dev)); + + if (pm_runtime_suspended(dev)) + return 0; + + ret = dcss_ctxld_suspend(dcss->ctxld); + if (ret) + return ret; + + dcss_clocks_disable(dcss); + + dcss_busfreq_disable(dcss); + + return 0; +} + +int dcss_dev_resume(struct device *dev) +{ + struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); + + if (pm_runtime_suspended(dev)) { + drm_mode_config_helper_resume(dcss_drv_dev_to_drm(dev)); + return 0; + } + + dcss_busfreq_enable(dcss); + + dcss_clocks_enable(dcss); + + dcss_blkctl_cfg(dcss->blkctl); + + dcss_ctxld_resume(dcss->ctxld); + + drm_mode_config_helper_resume(dcss_drv_dev_to_drm(dev)); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +int dcss_dev_runtime_suspend(struct device *dev) +{ + struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); + int ret; + + ret = dcss_ctxld_suspend(dcss->ctxld); + if (ret) + return ret; + + dcss_clocks_disable(dcss); + + dcss_busfreq_disable(dcss); + + return 0; +} + +int dcss_dev_runtime_resume(struct device *dev) +{ + struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); + + dcss_busfreq_enable(dcss); + + dcss_clocks_enable(dcss); + + dcss_blkctl_cfg(dcss->blkctl); + + dcss_ctxld_resume(dcss->ctxld); + + return 0; +} +#endif /* CONFIG_PM */ diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.h b/drivers/gpu/drm/imx/dcss/dcss-dev.h new file mode 100644 index 000000000000..591a4b7a474e --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-dev.h @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 NXP. + */ + +#ifndef __DCSS_PRV_H__ +#define __DCSS_PRV_H__ + +#include <drm/drm_atomic.h> +#include <drm/drm_fourcc.h> +#include <linux/io.h> +#include <video/videomode.h> + +#define SET 0x04 +#define CLR 0x08 +#define TGL 0x0C + +#define dcss_writel(v, c) writel((v), (c)) +#define dcss_readl(c) readl(c) +#define dcss_set(v, c) writel((v), (c) + SET) +#define dcss_clr(v, c) writel((v), (c) + CLR) +#define dcss_toggle(v, c) writel((v), (c) + TGL) + +static inline void dcss_update(u32 v, u32 m, void __iomem *c) +{ + writel((readl(c) & ~(m)) | (v), (c)); +} + +#define DCSS_DBG_REG(reg) {.name = #reg, .ofs = reg} + +enum { + DCSS_IMX8MQ = 0, +}; + +struct dcss_type_data { + const char *name; + u32 blkctl_ofs; + u32 ctxld_ofs; + u32 rdsrc_ofs; + u32 wrscl_ofs; + u32 dtg_ofs; + u32 scaler_ofs; + u32 ss_ofs; + u32 dpr_ofs; + u32 dtrc_ofs; + u32 dec400d_ofs; + u32 hdr10_ofs; +}; + +struct dcss_debug_reg { + char *name; + u32 ofs; +}; + +enum dcss_ctxld_ctx_type { + CTX_DB, + CTX_SB_HP, /* high-priority */ + CTX_SB_LP, /* low-priority */ +}; + +struct dcss_dev { + struct device *dev; + const struct dcss_type_data *devtype; + struct device_node *of_port; + + u32 start_addr; + + struct dcss_blkctl *blkctl; + struct dcss_ctxld *ctxld; + struct dcss_dpr *dpr; + struct dcss_dtg *dtg; + struct dcss_ss *ss; + struct dcss_hdr10 *hdr10; + struct dcss_scaler *scaler; + struct dcss_dtrc *dtrc; + struct dcss_dec400d *dec400d; + struct dcss_wrscl *wrscl; + struct dcss_rdsrc *rdsrc; + + struct clk *apb_clk; + struct clk *axi_clk; + struct clk *pix_clk; + struct clk *rtrm_clk; + struct clk *dtrc_clk; + struct clk *pll_src_clk; + struct clk *pll_phy_ref_clk; + + void (*dcss_disable_callback)(void *data); + + bool clks_on; + bool bus_freq_on; + bool hdmi_output; +}; + +enum dcss_color_space { + DCSS_COLORSPACE_RGB, + DCSS_COLORSPACE_YUV, + DCSS_COLORSPACE_UNKNOWN, +}; + +struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev); +struct drm_device *dcss_drv_dev_to_drm(struct device *dev); +struct dcss_dev *dcss_dev_create(struct device *dev, bool mipi_output); +void dcss_dev_destroy(struct dcss_dev *dcss); +int dcss_dev_runtime_suspend(struct device *dev); +int dcss_dev_runtime_resume(struct device *dev); +int dcss_dev_suspend(struct device *dev); +int dcss_dev_resume(struct device *dev); + +/* BLKCTL */ +int dcss_blkctl_init(struct dcss_dev *dcss, unsigned long blkctl_base); +void dcss_blkctl_cfg(struct dcss_blkctl *blkctl); +void dcss_blkctl_exit(struct dcss_blkctl *blkctl); + +/* CTXLD */ +int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base); +void dcss_ctxld_exit(struct dcss_ctxld *ctxld); +void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id, + u32 val, u32 reg_idx); +int dcss_ctxld_resume(struct dcss_ctxld *dcss_ctxld); +int dcss_ctxld_suspend(struct dcss_ctxld *dcss_ctxld); +void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctlxd, u32 ctx_id, u32 val, + u32 reg_ofs); +void dcss_ctxld_kick(struct dcss_ctxld *ctxld); +bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld); +int dcss_ctxld_enable(struct dcss_ctxld *ctxld); +void dcss_ctxld_register_dtg_disable_cb(struct dcss_ctxld *ctxld, + void (*cb)(void *), + void *data); +void dcss_ctxld_register_dtrc_cb(struct dcss_ctxld *ctxld, + bool (*cb)(void *), + void *data); + +/* DPR */ +enum dcss_tile_type { + TILE_LINEAR = 0, + TILE_GPU_STANDARD, + TILE_GPU_SUPER, + TILE_VPU_YUV420, + TILE_VPU_VP9, +}; + +enum dcss_pix_size { + PIX_SIZE_8, + PIX_SIZE_16, + PIX_SIZE_32, +}; + +int dcss_dpr_init(struct dcss_dev *dcss, unsigned long dpr_base); +void dcss_dpr_exit(struct dcss_dpr *dpr); +void dcss_dpr_write_sysctrl(struct dcss_dpr *dpr); +void dcss_dpr_set_res(struct dcss_dpr *dpr, int ch_num, u32 xres, u32 yres); +void dcss_dpr_addr_set(struct dcss_dpr *dpr, int ch_num, u32 luma_base_addr, + u32 chroma_base_addr, u16 pitch); +void dcss_dpr_enable(struct dcss_dpr *dpr, int ch_num, bool en); +void dcss_dpr_format_set(struct dcss_dpr *dpr, int ch_num, + const struct drm_format_info *format, u64 modifier); +void dcss_dpr_set_rotation(struct dcss_dpr *dpr, int ch_num, u32 rotation); + +/* DTG */ +int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base); +void dcss_dtg_exit(struct dcss_dtg *dtg); +bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg); +void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en); +void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg); +void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm); +void dcss_dtg_css_set(struct dcss_dtg *dtg, bool out_is_yuv); +void dcss_dtg_enable(struct dcss_dtg *dtg, bool en, + struct completion *dis_completion); +bool dcss_dtg_is_enabled(struct dcss_dtg *dtg); +void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en); +bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha); +void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num, + const struct drm_format_info *format, int alpha); +void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num, + int px, int py, int pw, int ph); +void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en); + +/* SUBSAM */ +int dcss_ss_init(struct dcss_dev *dcss, unsigned long subsam_base); +void dcss_ss_exit(struct dcss_ss *ss); +void dcss_ss_enable(struct dcss_ss *ss); +void dcss_ss_disable(struct dcss_ss *ss); +void dcss_ss_subsam_set(struct dcss_ss *ss, bool output_is_yuv); +void dcss_ss_sync_set(struct dcss_ss *ss, struct videomode *vm, + bool phsync, bool pvsync); + +/* SCALER */ +int dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base); +void dcss_scaler_exit(struct dcss_scaler *scl); +void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num, + const struct drm_format_info *format, + int src_xres, int src_yres, int dst_xres, int dst_yres, + u32 vrefresh_hz); +void dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en); +int dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num, + int *min, int *max); +void dcss_scaler_write_sclctrl(struct dcss_scaler *scl); + +/* DEC400D */ + +#define VIV_VIDMEM_METADATA_MAGIC fourcc_code('v', 'i', 'v', 'm') + +/* Compressed format now was defined same as dec400d, should be general. */ +typedef enum _VIV_COMPRESS_FMT +{ + _VIV_CFMT_ARGB8 = 0, + _VIV_CFMT_XRGB8, + _VIV_CFMT_AYUV, + _VIV_CFMT_UYVY, + _VIV_CFMT_YUY2, + _VIV_CFMT_YUV_ONLY, + _VIV_CFMT_UV_MIX, + _VIV_CFMT_ARGB4, + _VIV_CFMT_XRGB4, + _VIV_CFMT_A1R5G5B5, + _VIV_CFMT_X1R5G5B5, + _VIV_CFMT_R5G6B5, + _VIV_CFMT_Z24S8, + _VIV_CFMT_Z24, + _VIV_CFMT_Z16, + _VIV_CFMT_A2R10G10B10, + _VIV_CFMT_BAYER, + _VIV_CFMT_SIGNED_BAYER, + _VIV_CFMT_VAA16, + _VIV_CFMT_S8, + + _VIV_CFMT_MAX, +} _VIV_COMPRESS_FMT; + +/* Metadata for cross-device fd share with additional (ts) info. */ +typedef struct _VIV_VIDMEM_METADATA +{ + uint32_t magic; + + int32_t ts_fd; + void * ts_dma_buf; + + uint32_t fc_enabled; + uint32_t fc_value; + uint32_t fc_value_upper; + + uint32_t compressed; + uint32_t compress_format; +} _VIV_VIDMEM_METADATA; + +int dcss_dec400d_init(struct dcss_dev *dcss, unsigned long dec400d_base); +void dcss_dec400d_exit(struct dcss_dec400d *dec400d); +void dcss_dec400d_bypass(struct dcss_dec400d *dec400d); +void dcss_dec400d_shadow_trig(struct dcss_dec400d *dec400d); +void dcss_dec400d_enable(struct dcss_dec400d *dec400d); +void dcss_dec400d_fast_clear_config(struct dcss_dec400d *dec400d, + u32 fc_value, + bool enable); +void dcss_dec400d_read_config(struct dcss_dec400d *dec400d, + u32 read_id, + bool compress_en, + u32 compress_format); +void dcss_dec400d_addr_set(struct dcss_dec400d *dec400d, u32 baddr, u32 caddr); + +/* HDR10 */ +enum dcss_hdr10_nonlinearity { + NL_REC2084, + NL_REC709, + NL_BT1886, + NL_2100HLG, + NL_SRGB, +}; + +enum dcss_hdr10_pixel_range { + PR_LIMITED, + PR_FULL, +}; + +enum dcss_hdr10_gamut { + G_REC2020, + G_REC709, + G_REC601_NTSC, + G_REC601_PAL, + G_ADOBE_ARGB, +}; + +struct dcss_hdr10_pipe_cfg { + bool is_yuv; + enum dcss_hdr10_nonlinearity nl; + enum dcss_hdr10_pixel_range pr; + enum dcss_hdr10_gamut g; +}; + +int dcss_hdr10_init(struct dcss_dev *dcss, unsigned long hdr10_base); +void dcss_hdr10_exit(struct dcss_hdr10 *hdr10); +void dcss_hdr10_setup(struct dcss_hdr10 *hdr10, int ch_num, + struct dcss_hdr10_pipe_cfg *ipipe_cfg, + struct dcss_hdr10_pipe_cfg *opipe_cfg); + +/* enums common to both WRSCL and RDSRC */ +enum dcss_wrscl_rdsrc_psize { + PSIZE_64, + PSIZE_128, + PSIZE_256, + PSIZE_512, + PSIZE_1024, + PSIZE_2048, + PSIZE_4096, +}; + +enum dcss_wrscl_rdsrc_tsize { + TSIZE_64, + TSIZE_128, + TSIZE_256, + TSIZE_512, +}; + +enum dcss_wrscl_rdsrc_fifo_size { + FIFO_512, + FIFO_1024, + FIFO_2048, + FIFO_4096, +}; + +enum dcss_wrscl_rdsrc_bpp { + BPP_38, /* 38 bit unpacked components */ + BPP_32_UPCONVERT, + BPP_32_10BIT_OUTPUT, + BPP_20, /* 10-bit YUV422 */ + BPP_16, /* 8-bit YUV422 */ +}; + +/* WRSCL */ +int dcss_wrscl_init(struct dcss_dev *dcss, unsigned long wrscl_base); +void dcss_wrscl_exit(struct dcss_wrscl *wrscl); +u32 dcss_wrscl_setup(struct dcss_wrscl *wrscl, u32 pix_format, u32 pix_clk_hz, + u32 dst_xres, u32 dst_yres); +void dcss_wrscl_enable(struct dcss_wrscl *wrscl); +void dcss_wrscl_disable(struct dcss_wrscl *wrscl); + +/* RDSRC */ +int dcss_rdsrc_init(struct dcss_dev *dcss, unsigned long rdsrc_base); +void dcss_rdsrc_exit(struct dcss_rdsrc *rdsrc); +void dcss_rdsrc_setup(struct dcss_rdsrc *rdsrc, u32 pix_format, u32 dst_xres, + u32 dst_yres, u32 base_addr); +void dcss_rdsrc_enable(struct dcss_rdsrc *rdsrc); +void dcss_rdsrc_disable(struct dcss_rdsrc *rdsrc); + +/* DTRC */ +int dcss_dtrc_init(struct dcss_dev *dcss, unsigned long dtrc_base); +void dcss_dtrc_exit(struct dcss_dtrc *dtrc); +void dcss_dtrc_bypass(struct dcss_dtrc *dtrc, int ch_num); +void dcss_dtrc_set_format_mod(struct dcss_dtrc *dtrc, int ch_num, u64 modifier); +void dcss_dtrc_addr_set(struct dcss_dtrc *dtrc, int ch_num, + u32 p1_ba, u32 p2_ba, uint64_t dec_table_ofs); +bool dcss_dtrc_ch_running(struct dcss_dtrc *dtrc, int ch_num); +bool dcss_dtrc_is_running(struct dcss_dtrc *dtrc); +void dcss_dtrc_enable(struct dcss_dtrc *dtrc, int ch_num, bool enable); +void dcss_dtrc_set_res(struct dcss_dtrc *dtrc, int ch_num, + struct drm_plane_state *state, u32 *dtrc_w, u32 *dtrc_h); +void dcss_dtrc_switch_banks(struct dcss_dtrc *dtrc); + +#endif /* __DCSS_PRV_H__ */ diff --git a/drivers/gpu/drm/imx/dcss/dcss-dpr.c b/drivers/gpu/drm/imx/dcss/dcss-dpr.c new file mode 100644 index 000000000000..0feb0c66813b --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-dpr.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/device.h> + +#include "dcss-dev.h" + +#define DCSS_DPR_SYSTEM_CTRL0 0x000 +#define RUN_EN BIT(0) +#define SOFT_RESET BIT(1) +#define REPEAT_EN BIT(2) +#define SHADOW_LOAD_EN BIT(3) +#define SW_SHADOW_LOAD_SEL BIT(4) +#define BCMD2AXI_MSTR_ID_CTRL BIT(16) +#define DCSS_DPR_IRQ_MASK 0x020 +#define DCSS_DPR_IRQ_MASK_STATUS 0x030 +#define DCSS_DPR_IRQ_NONMASK_STATUS 0x040 +#define IRQ_DPR_CTRL_DONE BIT(0) +#define IRQ_DPR_RUN BIT(1) +#define IRQ_DPR_SHADOW_LOADED BIT(2) +#define IRQ_AXI_READ_ERR BIT(3) +#define DPR2RTR_YRGB_FIFO_OVFL BIT(4) +#define DPR2RTR_UV_FIFO_OVFL BIT(5) +#define DPR2RTR_FIFO_LD_BUF_RDY_YRGB_ERR BIT(6) +#define DPR2RTR_FIFO_LD_BUF_RDY_UV_ERR BIT(7) +#define DCSS_DPR_MODE_CTRL0 0x050 +#define RTR_3BUF_EN BIT(0) +#define RTR_4LINE_BUF_EN BIT(1) +#define TILE_TYPE_POS 2 +#define TILE_TYPE_MASK GENMASK(4, 2) +#define YUV_EN BIT(6) +#define COMP_2PLANE_EN BIT(7) +#define PIX_SIZE_POS 8 +#define PIX_SIZE_MASK GENMASK(9, 8) +#define PIX_LUMA_UV_SWAP BIT(10) +#define PIX_UV_SWAP BIT(11) +#define B_COMP_SEL_POS 12 +#define B_COMP_SEL_MASK GENMASK(13, 12) +#define G_COMP_SEL_POS 14 +#define G_COMP_SEL_MASK GENMASK(15, 14) +#define R_COMP_SEL_POS 16 +#define R_COMP_SEL_MASK GENMASK(17, 16) +#define A_COMP_SEL_POS 18 +#define A_COMP_SEL_MASK GENMASK(19, 18) +#define DCSS_DPR_FRAME_CTRL0 0x070 +#define HFLIP_EN BIT(0) +#define VFLIP_EN BIT(1) +#define ROT_ENC_POS 2 +#define ROT_ENC_MASK GENMASK(3, 2) +#define ROT_FLIP_ORDER_EN BIT(4) +#define PITCH_POS 16 +#define PITCH_MASK GENMASK(31, 16) +#define DCSS_DPR_FRAME_1P_CTRL0 0x090 +#define DCSS_DPR_FRAME_1P_PIX_X_CTRL 0x0A0 +#define DCSS_DPR_FRAME_1P_PIX_Y_CTRL 0x0B0 +#define DCSS_DPR_FRAME_1P_BASE_ADDR 0x0C0 +#define DCSS_DPR_FRAME_2P_CTRL0 0x0E0 +#define DCSS_DPR_FRAME_2P_PIX_X_CTRL 0x0F0 +#define DCSS_DPR_FRAME_2P_PIX_Y_CTRL 0x100 +#define DCSS_DPR_FRAME_2P_BASE_ADDR 0x110 +#define DCSS_DPR_STATUS_CTRL0 0x130 +#define STATUS_MUX_SEL_MASK GENMASK(2, 0) +#define STATUS_SRC_SEL_POS 16 +#define STATUS_SRC_SEL_MASK GENMASK(18, 16) +#define DCSS_DPR_STATUS_CTRL1 0x140 +#define DCSS_DPR_RTRAM_CTRL0 0x200 +#define NUM_ROWS_ACTIVE BIT(0) +#define THRES_HIGH_POS 1 +#define THRES_HIGH_MASK GENMASK(3, 1) +#define THRES_LOW_POS 4 +#define THRES_LOW_MASK GENMASK(6, 4) +#define ABORT_SEL BIT(7) + +struct dcss_dpr_ch { + struct dcss_dpr *dpr; + void __iomem *base_reg; + u32 base_ofs; + + struct drm_format_info format; + enum dcss_pix_size pix_size; + enum dcss_tile_type tile; + bool rtram_4line_en; + bool rtram_3buf_en; + + u32 frame_ctrl; + u32 mode_ctrl; + u32 sys_ctrl; + u32 rtram_ctrl; + + bool sys_ctrl_chgd; + + u32 pitch; + + int ch_num; + int irq; + + bool use_dtrc; +}; + +struct dcss_dpr { + struct device *dev; + struct dcss_dtrc *dtrc; + struct dcss_ctxld *ctxld; + u32 ctx_id; + + struct dcss_dpr_ch ch[3]; +}; + +static void dcss_dpr_write(struct dcss_dpr_ch *ch, u32 val, u32 ofs) +{ + struct dcss_dpr *dpr = ch->dpr; + + dcss_ctxld_write(dpr->ctxld, dpr->ctx_id, val, ch->base_ofs + ofs); +} + +static int dcss_dpr_ch_init_all(struct dcss_dpr *dpr, unsigned long dpr_base) +{ + struct dcss_dpr_ch *ch; + int i; + + for (i = 0; i < 3; i++) { + ch = &dpr->ch[i]; + + ch->base_ofs = dpr_base + i * 0x1000; + + ch->base_reg = devm_ioremap(dpr->dev, ch->base_ofs, SZ_4K); + if (!ch->base_reg) { + dev_err(dpr->dev, "dpr: unable to remap ch %d base\n", + i); + return -ENOMEM; + } + + ch->dpr = dpr; + ch->ch_num = i; + } + + return 0; +} + +int dcss_dpr_init(struct dcss_dev *dcss, unsigned long dpr_base) +{ + struct dcss_dpr *dpr; + + dpr = devm_kzalloc(dcss->dev, sizeof(struct dcss_dpr), GFP_KERNEL); + if (!dpr) + return -ENOMEM; + + dcss->dpr = dpr; + dpr->dev = dcss->dev; + dpr->ctxld = dcss->ctxld; + dpr->ctx_id = CTX_SB_HP; + dpr->dtrc = dcss->dtrc; + + if (dcss_dpr_ch_init_all(dpr, dpr_base)) { + int i; + + for (i = 0; i < 3; i++) { + if (dpr->ch[i].base_reg) + devm_iounmap(dpr->dev, dpr->ch[i].base_reg); + } + + devm_kfree(dpr->dev, dpr); + + return -ENOMEM; + } + + return 0; +} + +void dcss_dpr_exit(struct dcss_dpr *dpr) +{ + int ch_no; + + /* stop DPR on all channels */ + for (ch_no = 0; ch_no < 3; ch_no++) { + struct dcss_dpr_ch *ch = &dpr->ch[ch_no]; + + dcss_writel(0, ch->base_reg + DCSS_DPR_SYSTEM_CTRL0); + + if (ch->base_reg) + devm_iounmap(dpr->dev, ch->base_reg); + } + + devm_kfree(dpr->dev, dpr); +} + +static u32 dcss_dpr_x_pix_wide_adjust(struct dcss_dpr_ch *ch, u32 pix_wide, + u32 pix_format) +{ + u8 pix_in_64byte_map[3][5] = { + /* LIN, GPU_STD, GPU_SUP, VPU_YUV420, VPU_VP9 */ + { 64, 8, 8, 8, 16}, /* PIX_SIZE_8 */ + { 32, 8, 8, 8, 8}, /* PIX_SIZE_16 */ + { 16, 4, 4, 8, 8}, /* PIX_SIZE_32 */ + }; + u32 offset; + u32 div_64byte_mod, pix_in_64byte; + + pix_in_64byte = pix_in_64byte_map[ch->pix_size][ch->tile]; + + if (pix_format == DRM_FORMAT_NV12_10LE40) + pix_wide = pix_wide * 10 / 8; + + div_64byte_mod = pix_wide % pix_in_64byte; + offset = (div_64byte_mod == 0) ? 0 : (pix_in_64byte - div_64byte_mod); + + return pix_wide + offset; +} + +static u32 dcss_dpr_y_pix_high_adjust(struct dcss_dpr_ch *ch, u32 pix_high, + u32 pix_format) +{ + u8 num_rows_buf = ch->rtram_4line_en ? 4 : 8; + u32 offset, pix_y_mod; + + pix_y_mod = pix_high % num_rows_buf; + offset = pix_y_mod ? (num_rows_buf - pix_y_mod) : 0; + + return pix_high + offset; +} + +void dcss_dpr_set_res(struct dcss_dpr *dpr, int ch_num, u32 xres, u32 yres) +{ + struct dcss_dpr_ch *ch = &dpr->ch[ch_num]; + u32 pix_format = ch->format.format; + u32 gap = DCSS_DPR_FRAME_2P_BASE_ADDR - DCSS_DPR_FRAME_1P_BASE_ADDR; + int plane, max_planes = 1; + u32 pix_x_wide, pix_y_high; + + if (pix_format == DRM_FORMAT_NV12 || + pix_format == DRM_FORMAT_NV21 || + pix_format == DRM_FORMAT_NV12_10LE40) + max_planes = 2; + + for (plane = 0; plane < max_planes; plane++) { + yres = plane == 1 ? yres >> 1 : yres; + + pix_x_wide = dcss_dpr_x_pix_wide_adjust(ch, xres, pix_format); + pix_y_high = dcss_dpr_y_pix_high_adjust(ch, yres, pix_format); + + if (plane == 0) + ch->pitch = pix_x_wide; + + dcss_dpr_write(ch, pix_x_wide, + DCSS_DPR_FRAME_1P_PIX_X_CTRL + plane * gap); + dcss_dpr_write(ch, pix_y_high, + DCSS_DPR_FRAME_1P_PIX_Y_CTRL + plane * gap); + + dcss_dpr_write(ch, ch->use_dtrc ? 7 : 2, + DCSS_DPR_FRAME_1P_CTRL0 + plane * gap); + } +} + +void dcss_dpr_addr_set(struct dcss_dpr *dpr, int ch_num, u32 luma_base_addr, + u32 chroma_base_addr, u16 pitch) +{ + struct dcss_dpr_ch *ch = &dpr->ch[ch_num]; + + if (ch->use_dtrc) { + luma_base_addr = 0x0; + chroma_base_addr = 0x10000000; + } + + if (!dcss_dtrc_ch_running(dpr->dtrc, ch_num)) { + dcss_dpr_write(ch, luma_base_addr, DCSS_DPR_FRAME_1P_BASE_ADDR); + dcss_dpr_write(ch, chroma_base_addr, + DCSS_DPR_FRAME_2P_BASE_ADDR); + } + + if (ch->use_dtrc) + pitch = ch->pitch; + + ch->frame_ctrl &= ~PITCH_MASK; + ch->frame_ctrl |= (((u32)pitch << PITCH_POS) & PITCH_MASK); +} + +static void dcss_dpr_argb_comp_sel(struct dcss_dpr_ch *ch, int a_sel, int r_sel, + int g_sel, int b_sel) +{ + u32 sel; + + sel = ((a_sel << A_COMP_SEL_POS) & A_COMP_SEL_MASK) | + ((r_sel << R_COMP_SEL_POS) & R_COMP_SEL_MASK) | + ((g_sel << G_COMP_SEL_POS) & G_COMP_SEL_MASK) | + ((b_sel << B_COMP_SEL_POS) & B_COMP_SEL_MASK); + + ch->mode_ctrl &= ~(A_COMP_SEL_MASK | R_COMP_SEL_MASK | + G_COMP_SEL_MASK | B_COMP_SEL_MASK); + ch->mode_ctrl |= sel; +} + +static void dcss_dpr_pix_size_set(struct dcss_dpr_ch *ch, + const struct drm_format_info *format) +{ + u32 val; + + switch (format->format) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV12_10LE40: + val = 0; + break; + + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + val = 1; + break; + + default: + val = 2; + break; + } + + ch->pix_size = val; + + ch->mode_ctrl &= ~PIX_SIZE_MASK; + ch->mode_ctrl |= ((val << PIX_SIZE_POS) & PIX_SIZE_MASK); +} + +static void dcss_dpr_uv_swap(struct dcss_dpr_ch *ch, bool swap) +{ + ch->mode_ctrl &= ~PIX_UV_SWAP; + ch->mode_ctrl |= (swap ? PIX_UV_SWAP : 0); +} + +static void dcss_dpr_y_uv_swap(struct dcss_dpr_ch *ch, bool swap) +{ + ch->mode_ctrl &= ~PIX_LUMA_UV_SWAP; + ch->mode_ctrl |= (swap ? PIX_LUMA_UV_SWAP : 0); +} + +static void dcss_dpr_2plane_en(struct dcss_dpr_ch *ch, bool en) +{ + ch->mode_ctrl &= ~COMP_2PLANE_EN; + ch->mode_ctrl |= (en ? COMP_2PLANE_EN : 0); +} + +static void dcss_dpr_yuv_en(struct dcss_dpr_ch *ch, bool en) +{ + ch->mode_ctrl &= ~YUV_EN; + ch->mode_ctrl |= (en ? YUV_EN : 0); +} + +void dcss_dpr_enable(struct dcss_dpr *dpr, int ch_num, bool en) +{ + struct dcss_dpr_ch *ch = &dpr->ch[ch_num]; + u32 sys_ctrl; + + sys_ctrl = (en ? REPEAT_EN | RUN_EN : 0); + + if (en) { + dcss_dpr_write(ch, ch->mode_ctrl, DCSS_DPR_MODE_CTRL0); + dcss_dpr_write(ch, ch->frame_ctrl, DCSS_DPR_FRAME_CTRL0); + dcss_dpr_write(ch, ch->rtram_ctrl, DCSS_DPR_RTRAM_CTRL0); + } + + if (ch->sys_ctrl != sys_ctrl) + ch->sys_ctrl_chgd = true; + + ch->sys_ctrl = sys_ctrl; +} + +struct rgb_comp_sel { + u32 drm_format; + int a_sel; + int r_sel; + int g_sel; + int b_sel; +}; + +static struct rgb_comp_sel comp_sel_map[] = { + {DRM_FORMAT_ARGB8888, 3, 2, 1, 0}, + {DRM_FORMAT_XRGB8888, 3, 2, 1, 0}, + {DRM_FORMAT_ABGR8888, 3, 0, 1, 2}, + {DRM_FORMAT_XBGR8888, 3, 0, 1, 2}, + {DRM_FORMAT_RGBA8888, 0, 3, 2, 1}, + {DRM_FORMAT_RGBX8888, 0, 3, 2, 1}, + {DRM_FORMAT_BGRA8888, 0, 1, 2, 3}, + {DRM_FORMAT_BGRX8888, 0, 1, 2, 3}, +}; + +static int to_comp_sel(u32 pix_fmt, int *a_sel, int *r_sel, int *g_sel, + int *b_sel) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(comp_sel_map); i++) { + if (comp_sel_map[i].drm_format == pix_fmt) { + *a_sel = comp_sel_map[i].a_sel; + *r_sel = comp_sel_map[i].r_sel; + *g_sel = comp_sel_map[i].g_sel; + *b_sel = comp_sel_map[i].b_sel; + + return 0; + } + } + + return -1; +} + +static void dcss_dpr_rtram_set(struct dcss_dpr_ch *ch, u32 pix_format) +{ + u32 val, mask; + + switch (pix_format) { + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12_10LE40: + ch->rtram_3buf_en = true; + ch->rtram_4line_en = false; + break; + + default: + ch->rtram_3buf_en = true; + ch->rtram_4line_en = true; + break; + } + + val = (ch->rtram_4line_en ? RTR_4LINE_BUF_EN : 0); + val |= (ch->rtram_3buf_en ? RTR_3BUF_EN : 0); + mask = RTR_4LINE_BUF_EN | RTR_3BUF_EN; + + ch->mode_ctrl &= ~mask; + ch->mode_ctrl |= (val & mask); + + val = (ch->rtram_4line_en ? 0 : NUM_ROWS_ACTIVE); + val |= (3 << THRES_LOW_POS) & THRES_LOW_MASK; + val |= (4 << THRES_HIGH_POS) & THRES_HIGH_MASK; + mask = THRES_LOW_MASK | THRES_HIGH_MASK | NUM_ROWS_ACTIVE; + + ch->rtram_ctrl &= ~mask; + ch->rtram_ctrl |= (val & mask); +} + +static void dcss_dpr_setup_components(struct dcss_dpr_ch *ch, + const struct drm_format_info *format) +{ + int a_sel, r_sel, g_sel, b_sel; + bool uv_swap, y_uv_swap; + + switch (format->format) { + case DRM_FORMAT_YVYU: + uv_swap = true; + y_uv_swap = true; + break; + + case DRM_FORMAT_VYUY: + case DRM_FORMAT_NV21: + uv_swap = true; + y_uv_swap = false; + break; + + case DRM_FORMAT_YUYV: + uv_swap = false; + y_uv_swap = true; + break; + + default: + uv_swap = false; + y_uv_swap = false; + break; + } + + dcss_dpr_uv_swap(ch, uv_swap); + + dcss_dpr_y_uv_swap(ch, y_uv_swap); + + if (!format->is_yuv) { + if (!to_comp_sel(format->format, &a_sel, &r_sel, + &g_sel, &b_sel)) { + dcss_dpr_argb_comp_sel(ch, a_sel, r_sel, g_sel, b_sel); + } else { + dcss_dpr_argb_comp_sel(ch, 3, 2, 1, 0); + } + } else { + dcss_dpr_argb_comp_sel(ch, 0, 0, 0, 0); + } +} + +static void dcss_dpr_tile_set(struct dcss_dpr_ch *ch, uint64_t modifier) +{ + switch (ch->ch_num) { + case 0: + switch (modifier) { + case DRM_FORMAT_MOD_LINEAR: + ch->tile = TILE_LINEAR; + break; + case DRM_FORMAT_MOD_VIVANTE_TILED: + ch->tile = TILE_GPU_STANDARD; + break; + case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: + case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED_FC: + ch->tile = TILE_GPU_SUPER; + break; + default: + WARN_ON(1); + break; + } + break; + case 1: + case 2: + ch->tile = TILE_LINEAR; + break; + default: + WARN_ON(1); + return; + } + + ch->mode_ctrl &= ~TILE_TYPE_MASK; + ch->mode_ctrl |= ((ch->tile << TILE_TYPE_POS) & TILE_TYPE_MASK); +} + +void dcss_dpr_format_set(struct dcss_dpr *dpr, int ch_num, + const struct drm_format_info *format, u64 modifier) +{ + struct dcss_dpr_ch *ch = &dpr->ch[ch_num]; + + ch->format = *format; + ch->use_dtrc = ch_num && modifier != DRM_FORMAT_MOD_LINEAR; + + dcss_dpr_yuv_en(ch, format->is_yuv); + + dcss_dpr_pix_size_set(ch, format); + + dcss_dpr_setup_components(ch, format); + + dcss_dpr_2plane_en(ch, format->num_planes == 2); + + dcss_dpr_rtram_set(ch, format->format); + + dcss_dpr_tile_set(ch, modifier); +} + +/* This function will be called from interrupt context. */ +void dcss_dpr_write_sysctrl(struct dcss_dpr *dpr) +{ + int chnum; + + for (chnum = 0; chnum < 3; chnum++) { + struct dcss_dpr_ch *ch = &dpr->ch[chnum]; + + if (ch->sys_ctrl_chgd) { + dcss_ctxld_write_irqsafe(dpr->ctxld, dpr->ctx_id, + ch->sys_ctrl, + ch->base_ofs + + DCSS_DPR_SYSTEM_CTRL0); + ch->sys_ctrl_chgd = false; + } + } +} + +void dcss_dpr_set_rotation(struct dcss_dpr *dpr, int ch_num, u32 rotation) +{ + struct dcss_dpr_ch *ch = &dpr->ch[ch_num]; + + ch->frame_ctrl &= ~(HFLIP_EN | VFLIP_EN | ROT_ENC_MASK); + + ch->frame_ctrl |= rotation & DRM_MODE_REFLECT_X ? HFLIP_EN : 0; + ch->frame_ctrl |= rotation & DRM_MODE_REFLECT_Y ? VFLIP_EN : 0; + + if (rotation & DRM_MODE_ROTATE_90) + ch->frame_ctrl |= 1 << ROT_ENC_POS; + else if (rotation & DRM_MODE_ROTATE_180) + ch->frame_ctrl |= 2 << ROT_ENC_POS; + else if (rotation & DRM_MODE_ROTATE_270) + ch->frame_ctrl |= 3 << ROT_ENC_POS; +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c new file mode 100644 index 000000000000..4082e529f3f5 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/component.h> +#include <drm/drm_of.h> +#include <drm/drmP.h> + +#include "dcss-dev.h" +#include "dcss-kms.h" + +struct dcss_drv { + struct dcss_dev *dcss; + struct dcss_kms_dev *kms; + + bool is_componentized; +}; + +struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev) +{ + struct dcss_drv *mdrv = dev_get_drvdata(dev); + + return mdrv ? mdrv->dcss : NULL; +} + +struct drm_device *dcss_drv_dev_to_drm(struct device *dev) +{ + struct dcss_drv *mdrv = dev_get_drvdata(dev); + + return mdrv ? &mdrv->kms->base : NULL; +} + +static int dcss_drv_init(struct device *dev, bool componentized) +{ + struct dcss_drv *mdrv; + int err = 0; + + mdrv = devm_kzalloc(dev, sizeof(*mdrv), GFP_KERNEL); + if (!mdrv) + return -ENOMEM; + + mdrv->is_componentized = componentized; + + mdrv->dcss = dcss_dev_create(dev, componentized); + if (IS_ERR(mdrv->dcss)) { + err = PTR_ERR(mdrv->dcss); + goto err; + } + + dev_set_drvdata(dev, mdrv); + + mdrv->kms = dcss_kms_attach(mdrv->dcss, componentized); + if (IS_ERR(mdrv->kms)) { + err = PTR_ERR(mdrv->kms); + goto dcss_shutoff; + } + + return 0; + +dcss_shutoff: + dcss_dev_destroy(mdrv->dcss); + + dev_set_drvdata(dev, NULL); + +err: + devm_kfree(dev, mdrv); + return err; +} + +static void dcss_drv_deinit(struct device *dev, bool componentized) +{ + struct dcss_drv *mdrv = dev_get_drvdata(dev); + + if (!mdrv) + return; + + dcss_kms_detach(mdrv->kms, componentized); + dcss_dev_destroy(mdrv->dcss); + + dev_set_drvdata(dev, NULL); +} + +static int dcss_drv_bind(struct device *dev) +{ + return dcss_drv_init(dev, true); +} + +static void dcss_drv_unbind(struct device *dev) +{ + return dcss_drv_deinit(dev, true); +} + +static const struct component_master_ops dcss_master_ops = { + .bind = dcss_drv_bind, + .unbind = dcss_drv_unbind, +}; + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int dcss_drv_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct component_match *match = NULL; + struct device_node *remote; + + if (!dev->of_node) + return -ENODEV; + + remote = of_graph_get_remote_node(dev->of_node, 0, 0); + if (!remote) + return -ENODEV; + + if (of_device_is_compatible(remote, "fsl,imx8mq-nwl-dsi")) { + of_node_put(remote); + return dcss_drv_init(dev, false); + } + + drm_of_component_match_add(dev, &match, compare_of, remote); + of_node_put(remote); + + return component_master_add_with_match(dev, &dcss_master_ops, match); +} + +static int dcss_drv_platform_remove(struct platform_device *pdev) +{ + struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev); + + if (mdrv->is_componentized) + component_master_del(&pdev->dev, &dcss_master_ops); + else + dcss_drv_deinit(&pdev->dev, false); + + return 0; +} + +static struct dcss_type_data dcss_types[] = { + [DCSS_IMX8MQ] = { + .name = "DCSS_IMX8MQ", + .blkctl_ofs = 0x2F000, + .ctxld_ofs = 0x23000, + .dtg_ofs = 0x20000, + .rdsrc_ofs = 0x22000, + .wrscl_ofs = 0x21000, + .scaler_ofs = 0x1C000, + .ss_ofs = 0x1B000, + .dpr_ofs = 0x18000, + .dec400d_ofs = 0x15000, + .hdr10_ofs = 0x00000, + .dtrc_ofs = 0x16000, + }, +}; + +static const struct of_device_id dcss_of_match[] = { + { .compatible = "nxp,imx8mq-dcss", .data = &dcss_types[DCSS_IMX8MQ], }, + {}, +}; + +MODULE_DEVICE_TABLE(of, dcss_of_match); + +static const struct dev_pm_ops dcss_dev_pm = { + SET_SYSTEM_SLEEP_PM_OPS(dcss_dev_suspend, dcss_dev_resume) + SET_RUNTIME_PM_OPS(dcss_dev_runtime_suspend, + dcss_dev_runtime_resume, NULL) +}; + +static struct platform_driver dcss_platform_driver = { + .probe = dcss_drv_platform_probe, + .remove = dcss_drv_platform_remove, + .driver = { + .name = "imx-dcss", + .of_match_table = dcss_of_match, + .pm = &dcss_dev_pm, + }, +}; + +module_platform_driver(dcss_platform_driver); + +MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@nxp.com>"); +MODULE_DESCRIPTION("DCSS driver for i.MX8MQ"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/imx/dcss/dcss-dtg.c b/drivers/gpu/drm/imx/dcss/dcss-dtg.c new file mode 100644 index 000000000000..44879db7e0a4 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-dtg.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "dcss-dev.h" + +#define DCSS_DTG_TC_CONTROL_STATUS 0x00 +#define CH3_EN BIT(0) +#define CH2_EN BIT(1) +#define CH1_EN BIT(2) +#define OVL_DATA_MODE BIT(3) +#define BLENDER_VIDEO_ALPHA_SEL BIT(7) +#define DTG_START BIT(8) +#define DBY_MODE_EN BIT(9) +#define CH1_ALPHA_SEL BIT(10) +#define CSS_PIX_COMP_SWAP_POS 12 +#define CSS_PIX_COMP_SWAP_MASK GENMASK(14, 12) +#define DEFAULT_FG_ALPHA_POS 24 +#define DEFAULT_FG_ALPHA_MASK GENMASK(31, 24) +#define DCSS_DTG_TC_DTG 0x04 +#define DCSS_DTG_TC_DISP_TOP 0x08 +#define DCSS_DTG_TC_DISP_BOT 0x0C +#define DCSS_DTG_TC_CH1_TOP 0x10 +#define DCSS_DTG_TC_CH1_BOT 0x14 +#define DCSS_DTG_TC_CH2_TOP 0x18 +#define DCSS_DTG_TC_CH2_BOT 0x1C +#define DCSS_DTG_TC_CH3_TOP 0x20 +#define DCSS_DTG_TC_CH3_BOT 0x24 +#define TC_X_POS 0 +#define TC_X_MASK GENMASK(12, 0) +#define TC_Y_POS 16 +#define TC_Y_MASK GENMASK(28, 16) +#define DCSS_DTG_TC_CTXLD 0x28 +#define TC_CTXLD_DB_Y_POS 0 +#define TC_CTXLD_DB_Y_MASK GENMASK(12, 0) +#define TC_CTXLD_SB_Y_POS 16 +#define TC_CTXLD_SB_Y_MASK GENMASK(28, 16) +#define DCSS_DTG_TC_CH1_BKRND 0x2C +#define DCSS_DTG_TC_CH2_BKRND 0x30 +#define BKRND_R_Y_COMP_POS 20 +#define BKRND_R_Y_COMP_MASK GENMASK(29, 20) +#define BKRND_G_U_COMP_POS 10 +#define BKRND_G_U_COMP_MASK GENMASK(19, 10) +#define BKRND_B_V_COMP_POS 0 +#define BKRND_B_V_COMP_MASK GENMASK(9, 0) +#define DCSS_DTG_BLENDER_DBY_RANGEINV 0x38 +#define DCSS_DTG_BLENDER_DBY_RANGEMIN 0x3C +#define DCSS_DTG_BLENDER_DBY_BDP 0x40 +#define DCSS_DTG_BLENDER_BKRND_I 0x44 +#define DCSS_DTG_BLENDER_BKRND_P 0x48 +#define DCSS_DTG_BLENDER_BKRND_T 0x4C +#define DCSS_DTG_LINE0_INT 0x50 +#define DCSS_DTG_LINE1_INT 0x54 +#define DCSS_DTG_BG_ALPHA_DEFAULT 0x58 +#define DCSS_DTG_INT_STATUS 0x5C +#define DCSS_DTG_INT_CONTROL 0x60 +#define DCSS_DTG_TC_CH3_BKRND 0x64 +#define DCSS_DTG_INT_MASK 0x68 +#define LINE0_IRQ BIT(0) +#define LINE1_IRQ BIT(1) +#define LINE2_IRQ BIT(2) +#define LINE3_IRQ BIT(3) +#define DCSS_DTG_LINE2_INT 0x6C +#define DCSS_DTG_LINE3_INT 0x70 +#define DCSS_DTG_DBY_OL 0x74 +#define DCSS_DTG_DBY_BL 0x78 +#define DCSS_DTG_DBY_EL 0x7C + +struct dcss_dtg { + struct device *dev; + struct dcss_ctxld *ctxld; + void __iomem *base_reg; + u32 base_ofs; + + u32 ctx_id; + + bool in_use; + bool hdmi_output; + + u32 dis_ulc_x; + u32 dis_ulc_y; + + u32 control_status; + u32 alpha; + + int ctxld_kick_irq; + bool ctxld_kick_irq_en; + + struct clk *pix_clk; + struct clk *pll_src_clk; + struct clk *pll_phy_ref_clk; + + /* + * This will be passed on by DRM CRTC so that we can signal when DTG has + * been successfully stopped. Otherwise, any modesetting while DTG is + * still ON may result in unpredictable behavior. + */ + struct completion *dis_completion; +}; + +static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs) +{ + if (!dtg->in_use) + dcss_writel(val, dtg->base_reg + ofs); + + dcss_ctxld_write(dtg->ctxld, dtg->ctx_id, val, dtg->base_ofs + ofs); +} + +static irqreturn_t dcss_dtg_irq_handler(int irq, void *data) +{ + struct dcss_dtg *dtg = data; + u32 status; + + status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS); + + if (!(status & LINE0_IRQ)) + return IRQ_HANDLED; + + dcss_ctxld_kick(dtg->ctxld); + + dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL); + + return IRQ_HANDLED; +} + +static int dcss_dtg_irq_config(struct dcss_dtg *dtg, + struct platform_device *pdev) +{ + int ret; + + dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick"); + if (dtg->ctxld_kick_irq < 0) { + dev_err(dtg->dev, "dtg: can't get line2 irq number\n"); + return dtg->ctxld_kick_irq; + } + + ret = devm_request_irq(dtg->dev, dtg->ctxld_kick_irq, + dcss_dtg_irq_handler, + IRQF_TRIGGER_HIGH, + "dcss_ctxld_kick", dtg); + if (ret) { + dev_err(dtg->dev, "dtg: irq request failed.\n"); + return ret; + } + + disable_irq(dtg->ctxld_kick_irq); + + dtg->ctxld_kick_irq_en = false; + + dcss_update(LINE0_IRQ, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK); + + return 0; +} + +int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base) +{ + int ret = 0; + struct dcss_dtg *dtg; + + dtg = devm_kzalloc(dcss->dev, sizeof(*dtg), GFP_KERNEL); + if (!dtg) + return -ENOMEM; + + dcss->dtg = dtg; + dtg->dev = dcss->dev; + dtg->ctxld = dcss->ctxld; + dtg->hdmi_output = dcss->hdmi_output; + + dtg->base_reg = devm_ioremap(dcss->dev, dtg_base, SZ_4K); + if (!dtg->base_reg) { + dev_err(dcss->dev, "dtg: unable to remap dtg base\n"); + ret = -ENOMEM; + goto err_ioremap; + } + + dtg->base_ofs = dtg_base; + dtg->ctx_id = CTX_DB; + + dtg->pix_clk = dcss->pix_clk; + dtg->pll_src_clk = dcss->pll_src_clk; + dtg->pll_phy_ref_clk = dcss->pll_phy_ref_clk; + + dtg->alpha = 255; + + dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL | + ((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK); + + ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev)); + if (ret) + goto err_irq; + + return 0; + +err_irq: + devm_iounmap(dtg->dev, dtg->base_reg); + +err_ioremap: + devm_kfree(dtg->dev, dtg); + + return ret; +} + +void dcss_dtg_exit(struct dcss_dtg *dtg) +{ + /* stop DTG */ + dcss_writel(DTG_START, dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS); + + devm_free_irq(dtg->dev, dtg->ctxld_kick_irq, dtg); + + if (dtg->base_reg) + devm_iounmap(dtg->dev, dtg->base_reg); + + devm_kfree(dtg->dev, dtg); +} + +void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm) +{ + u16 dtg_lrc_x, dtg_lrc_y; + u16 dis_ulc_x, dis_ulc_y; + u16 dis_lrc_x, dis_lrc_y; + u32 sb_ctxld_trig, db_ctxld_trig; + u32 pixclock = vm->pixelclock; + u32 actual_clk; + + dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len + + vm->hactive - 1; + dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len + + vm->vactive - 1; + dis_ulc_x = vm->hsync_len + vm->hback_porch - 1; + dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1; + dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1; + dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch + + vm->vactive - 1; + + clk_disable_unprepare(dtg->pix_clk); + if (dtg->hdmi_output) { + int err; + + clk_disable_unprepare(dtg->pll_src_clk); + err = clk_set_parent(dtg->pll_src_clk, dtg->pll_phy_ref_clk); + if (err < 0) + dev_warn(dtg->dev, "clk_set_parent() returned %d", err); + clk_prepare_enable(dtg->pll_src_clk); + } + clk_set_rate(dtg->pix_clk, vm->pixelclock); + clk_prepare_enable(dtg->pix_clk); + + actual_clk = clk_get_rate(dtg->pix_clk); + if (pixclock != actual_clk) { + dev_info(dtg->dev, + "Pixel clock set to %u kHz instead of %u kHz.\n", + (actual_clk / 1000), (pixclock / 1000)); + } + + msleep(50); + + dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x), + DCSS_DTG_TC_DTG); + dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x), + DCSS_DTG_TC_DISP_TOP); + dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x), + DCSS_DTG_TC_DISP_BOT); + + dtg->dis_ulc_x = dis_ulc_x; + dtg->dis_ulc_y = dis_ulc_y; + + sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) & + TC_CTXLD_SB_Y_MASK; + db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) & + TC_CTXLD_DB_Y_MASK; + + dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD); + + /* vblank trigger */ + dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT); + + /* CTXLD trigger */ + dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT); +} + +void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num, + int px, int py, int pw, int ph) +{ + u16 p_ulc_x, p_ulc_y; + u16 p_lrc_x, p_lrc_y; + + p_ulc_x = dtg->dis_ulc_x + px; + p_ulc_y = dtg->dis_ulc_y + py; + p_lrc_x = p_ulc_x + pw; + p_lrc_y = p_ulc_y + ph; + + if (!px && !py && !pw && !ph) { + dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num); + dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num); + } else { + dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x), + DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num); + dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x), + DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num); + } +} + +bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha) +{ + if (ch_num) + return false; + + return alpha != dtg->alpha; +} + +void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num, + const struct drm_format_info *format, int alpha) +{ + u32 alpha_val; + + /* we care about alpha only when channel 0 is concerned */ + if (ch_num) + return; + + alpha_val = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK; + + /* + * Use global alpha if pixel format does not have alpha channel or the + * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE). + */ + if (!format->has_alpha || alpha != 255) { + dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK); + dtg->control_status |= alpha_val; + } else { /* use per-pixel alpha otherwise */ + dtg->control_status |= CH1_ALPHA_SEL; + } + + dtg->alpha = alpha; +} + +void dcss_dtg_css_set(struct dcss_dtg *dtg, bool out_is_yuv) +{ + dtg->control_status &= ~CSS_PIX_COMP_SWAP_MASK; + + if (out_is_yuv) + return; + + dtg->control_status |= + (0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK; +} + +static void dcss_dtg_disable_callback(void *data) +{ + struct dcss_dtg *dtg = data; + + dtg->control_status &= ~DTG_START; + + dcss_writel(dtg->control_status, + dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS); + + dtg->in_use = false; + + complete(dtg->dis_completion); +} + +void dcss_dtg_enable(struct dcss_dtg *dtg, bool en, + struct completion *dis_completion) +{ + if (!en) { + dtg->dis_completion = dis_completion; + dcss_ctxld_register_dtg_disable_cb(dtg->ctxld, + dcss_dtg_disable_callback, + dtg); + return; + } + + dtg->dis_completion = NULL; + + dtg->control_status |= DTG_START; + + dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS); + + dtg->in_use = true; +} + +bool dcss_dtg_is_enabled(struct dcss_dtg *dtg) +{ + return dtg->in_use; +} + +void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en) +{ + u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN}; + u32 control_status; + + control_status = dtg->control_status & ~ch_en_map[ch_num]; + control_status |= en ? ch_en_map[ch_num] : 0; + + if (dtg->control_status != control_status) + dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS); + + dtg->control_status = control_status; +} + +void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en) +{ + u32 status; + + dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK); + + if (en) { + status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS); + dcss_writel(status & LINE1_IRQ, + dtg->base_reg + DCSS_DTG_INT_CONTROL); + } +} + +void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en) +{ + u32 status; + + if (en) { + status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS); + + if (!dtg->ctxld_kick_irq_en) { + dcss_writel(status & LINE0_IRQ, + dtg->base_reg + DCSS_DTG_INT_CONTROL); + enable_irq(dtg->ctxld_kick_irq); + dtg->ctxld_kick_irq_en = true; + return; + } + + return; + } + + if (!dtg->ctxld_kick_irq_en) + return; + + disable_irq_nosync(dtg->ctxld_kick_irq); + dtg->ctxld_kick_irq_en = false; +} + +void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg) +{ + dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL); +} + +bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg) +{ + return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ); +} + diff --git a/drivers/gpu/drm/imx/dcss/dcss-dtrc.c b/drivers/gpu/drm/imx/dcss/dcss-dtrc.c new file mode 100644 index 000000000000..1caa293651a0 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-dtrc.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_rect.h> + +#include "dcss-dev.h" + +#define DTRC_F0_OFS 0x00 +#define DTRC_F1_OFS 0x60 + +#define DCSS_DTRC_DYDSADDR 0x00 +#define DCSS_DTRC_DCDSADDR 0x04 +#define DCSS_DTRC_DYTSADDR 0x08 +#define DCSS_DTRC_DCTSADDR 0x0C +#define DCSS_DTRC_SIZE 0x10 +#define FRAME_WIDTH_POS 0 +#define FRAME_WIDTH_MASK GENMASK(9, 0) +#define FRAME_HEIGHT_POS 16 +#define FRAME_HEIGHT_MASK GENMASK(25, 16) +#define DCSS_DTRC_SYSSA 0x14 +#define DCSS_DTRC_SYSEA 0x18 +#define DCSS_DTRC_SUVSSA 0x1C +#define DCSS_DTRC_SUVSEA 0x20 +#define DCSS_DTRC_CROPORIG 0x24 +#define DCSS_DTRC_CROPSIZE 0x28 +#define CROP_HEIGHT_POS 16 +#define CROP_HEIGHT_MASK GENMASK(28, 16) +#define CROP_WIDTH_POS 0 +#define CROP_WIDTH_MASK GENMASK(12, 0) +#define DCSS_DTRC_DCTL 0x2C +#define CROPPING_EN BIT(18) +#define COMPRESSION_DIS BIT(17) +#define PIX_DEPTH_8BIT_EN BIT(1) +#define CONFIG_READY BIT(0) +#define DCSS_DTRC_DYDSADDR_EXT 0x30 +#define DCSS_DTRC_DCDSADDR_EXT 0x34 +#define DCSS_DTRC_DYTSADDR_EXT 0x38 +#define DCSS_DTRC_DCTSADDR_EXT 0x3C +#define DCSS_DTRC_SYSSA_EXT 0x40 +#define DCSS_DTRC_SYSEA_EXT 0x44 +#define DCSS_DTRC_SUVSSA_EXT 0x48 +#define DCSS_DTRC_SUVSEA_EXT 0x4C + +#define DCSS_DTRC_INTEN 0xC0 +#define DCSS_DTRC_FDINTR 0xC4 +#define DCSS_DTRC_DTCTRL 0xC8 +#define CURRENT_FRAME BIT(31) +#define ADDRESS_ID_ENABLE BIT(30) +#define ENDIANNESS_10BIT BIT(29) +#define MERGE_ARID_ENABLE BIT(28) +#define NON_G1_2_SWAP_MODE_POS 24 +#define NON_G1_2_SWAP_MODE_MASK GENMASK(27, 24) +#define TABLE_DATA_SWAP_POS 20 +#define TABLE_DATA_SWAP_MASK GENMASK(23, 20) +#define TILED_SWAP_POS 16 +#define TILED_SWAP_MASK GENMASK(19, 16) +#define RASTER_SWAP_POS 12 +#define RASTER_SWAP_MASK GENMASK(15, 12) +#define BURST_LENGTH_POS 4 +#define BURST_LENGTH_MASK GENMASK(11, 4) +#define G1_TILED_DATA_EN BIT(3) +#define HOT_RESET BIT(2) +#define ARIDR_MODE_DETILE 0 +#define ARIDR_MODE_BYPASS 2 +#define DCSS_DTRC_ARIDR 0xCC +#define DCSS_DTRC_DTID2DDR 0xD0 +#define DCSS_DTRC_CONFIG 0xD4 +#define DCSS_DTRC_VER 0xD8 +#define DCSS_DTRC_PFCTRL 0xF0 +#define DCSS_DTRC_PFCR 0xF4 +#define DCSS_DTRC_TOCR 0xF8 + +struct dcss_dtrc_ch { + struct dcss_dtrc *dtrc; + + void __iomem *base_reg; + u32 base_ofs; + + u32 xres; + u32 yres; + u32 pix_format; + u64 format_modifier; + u32 y_dec_ofs; + u32 uv_dec_ofs; + + int curr_frame; + + u32 dctl; + + bool bypass; + bool running; + + int irq; + int ch_num; +}; + +struct dcss_dtrc { + struct device *dev; + + struct dcss_dtrc_ch ch[2]; + + u32 ctx_id; + struct dcss_ctxld *ctxld; +}; + +static irqreturn_t dcss_dtrc_irq_handler(int irq, void *data) +{ + struct dcss_dtrc_ch *ch = data; + u32 b0, b1, curr_bank; + + b0 = dcss_readl(ch->base_reg + DCSS_DTRC_DCTL) & 0x1; + b1 = dcss_readl(ch->base_reg + DTRC_F1_OFS + DCSS_DTRC_DCTL) & 0x1; + curr_bank = dcss_readl(ch->base_reg + DCSS_DTRC_DTCTRL) >> 31; + + dcss_update(1, 1, ch->base_reg + DCSS_DTRC_FDINTR); + + return IRQ_HANDLED; +} + +static int dcss_dtrc_irq_config(struct dcss_dtrc *dtrc, int ch_num) +{ + struct platform_device *pdev = to_platform_device(dtrc->dev); + struct dcss_dtrc_ch *ch = &dtrc->ch[ch_num]; + char irq_name[20]; + int ret; + + sprintf(irq_name, "dtrc_ch%d", ch_num + 1); + irq_name[8] = 0; + + ch->irq = platform_get_irq_byname(pdev, irq_name); + if (ch->irq < 0) { + dev_err(dtrc->dev, "dtrc: can't get DTRC irq\n"); + return ch->irq; + } + + ret = devm_request_irq(dtrc->dev, ch->irq, + dcss_dtrc_irq_handler, + IRQF_TRIGGER_HIGH, + "dcss-dtrc", ch); + if (ret) { + dev_err(dtrc->dev, "dtrc: irq request failed.\n"); + return ret; + } + + dcss_writel(1, ch->base_reg + DCSS_DTRC_INTEN); + + return 0; +} + +static int dcss_dtrc_ch_init_all(struct dcss_dtrc *dtrc, u32 dtrc_base) +{ + struct dcss_dtrc_ch *ch; + int i, ret; + + for (i = 0; i < 2; i++) { + ch = &dtrc->ch[i]; + + ch->base_ofs = dtrc_base + i * 0x1000; + + ch->base_reg = devm_ioremap(dtrc->dev, ch->base_ofs, SZ_4K); + if (!ch->base_reg) { + dev_err(dtrc->dev, "dtrc: unable to remap ch base\n"); + return -ENOMEM; + } + + ch->ch_num = i; + ch->dtrc = dtrc; + + ret = dcss_dtrc_irq_config(dtrc, i); + if (ret) + return ret; + } + + return 0; +} + +static void dcss_dtrc_write(struct dcss_dtrc_ch *ch, u32 val, u32 ofs) +{ + dcss_ctxld_write(ch->dtrc->ctxld, ch->dtrc->ctx_id, + val, ch->base_ofs + ofs); +} + +static void dcss_dtrc_write_irqsafe(struct dcss_dtrc_ch *ch, u32 val, u32 ofs) +{ + dcss_ctxld_write_irqsafe(ch->dtrc->ctxld, ch->dtrc->ctx_id, + val, ch->base_ofs + ofs); +} + +int dcss_dtrc_init(struct dcss_dev *dcss, unsigned long dtrc_base) +{ + struct dcss_dtrc *dtrc; + + dtrc = devm_kzalloc(dcss->dev, sizeof(*dtrc), GFP_KERNEL); + if (!dtrc) + return -ENOMEM; + + dcss->dtrc = dtrc; + dtrc->dev = dcss->dev; + dtrc->ctxld = dcss->ctxld; + dtrc->ctx_id = CTX_SB_HP; + + if (dcss_dtrc_ch_init_all(dtrc, dtrc_base)) { + struct dcss_dtrc_ch *ch; + int i; + + for (i = 0; i < 2; i++) { + ch = &dtrc->ch[i]; + + if (ch->irq) + devm_free_irq(dtrc->dev, ch->irq, ch); + + if (ch->base_reg) + devm_iounmap(dtrc->dev, ch->base_reg); + } + + devm_kfree(dtrc->dev, dtrc); + + return -ENOMEM; + } + + return 0; +} + +void dcss_dtrc_exit(struct dcss_dtrc *dtrc) +{ + int ch_no; + + for (ch_no = 0; ch_no < 2; ch_no++) { + struct dcss_dtrc_ch *ch = &dtrc->ch[ch_no]; + + if (ch->base_reg) { + /* reset the module to default */ + dcss_writel(HOT_RESET, + ch->base_reg + DCSS_DTRC_DTCTRL); + devm_iounmap(dtrc->dev, ch->base_reg); + } + } + + devm_kfree(dtrc->dev, dtrc); +} + +void dcss_dtrc_bypass(struct dcss_dtrc *dtrc, int ch_num) +{ + struct dcss_dtrc_ch *ch; + + if (ch_num == 0) + return; + + ch = &dtrc->ch[ch_num - 1]; + + if (ch->bypass) + return; + + dcss_dtrc_write(ch, ARIDR_MODE_BYPASS, DCSS_DTRC_DTCTRL); + dcss_dtrc_write(ch, 0, DCSS_DTRC_DYTSADDR); + dcss_dtrc_write(ch, 0, DCSS_DTRC_DCTSADDR); + dcss_dtrc_write(ch, 0x0f0e0100, DCSS_DTRC_ARIDR); + dcss_dtrc_write(ch, 0x0f0e, DCSS_DTRC_DTID2DDR); + + ch->bypass = true; +} + +void dcss_dtrc_addr_set(struct dcss_dtrc *dtrc, int ch_num, + u32 p1_ba, u32 p2_ba, uint64_t dec_table_ofs) +{ + struct dcss_dtrc_ch *ch; + + if (ch_num == 0) + return; + + ch = &dtrc->ch[ch_num - 1]; + + dcss_dtrc_write(ch, p1_ba, DCSS_DTRC_DYDSADDR); + dcss_dtrc_write(ch, p2_ba, DCSS_DTRC_DCDSADDR); + + dcss_dtrc_write(ch, p1_ba, DTRC_F1_OFS + DCSS_DTRC_DYDSADDR); + dcss_dtrc_write(ch, p2_ba, DTRC_F1_OFS + DCSS_DTRC_DCDSADDR); + + if (ch->format_modifier == DRM_FORMAT_MOD_VSI_G2_TILED_COMPRESSED) { + ch->y_dec_ofs = dec_table_ofs & 0xFFFFFFFF; + ch->uv_dec_ofs = dec_table_ofs >> 32; + + dcss_dtrc_write(ch, p1_ba + ch->y_dec_ofs, + DCSS_DTRC_DYTSADDR); + dcss_dtrc_write(ch, p1_ba + ch->uv_dec_ofs, + DCSS_DTRC_DCTSADDR); + dcss_dtrc_write(ch, p1_ba + ch->y_dec_ofs, + DTRC_F1_OFS + DCSS_DTRC_DYTSADDR); + dcss_dtrc_write(ch, p1_ba + ch->uv_dec_ofs, + DTRC_F1_OFS + DCSS_DTRC_DCTSADDR); + } + + ch->bypass = false; +} + +void dcss_dtrc_set_res(struct dcss_dtrc *dtrc, int ch_num, + struct drm_plane_state *state, u32 *dtrc_w, u32 *dtrc_h) +{ + struct drm_framebuffer *fb = state->fb; + u32 pixel_format = fb->format->format; + struct dcss_dtrc_ch *ch; + u32 frame_height, frame_width; + u32 crop_w, crop_h, crop_orig_w, crop_orig_h; + int bank; + u32 old_xres, old_yres, xres, yres; + u32 x1, y1, x2, y2; + u32 pix_depth; + u16 width_align = 0; + + if (ch_num == 0) + return; + + ch = &dtrc->ch[ch_num - 1]; + + bank = dcss_readl(ch->base_reg + DCSS_DTRC_DTCTRL) >> 31; + + ch->pix_format = pixel_format; + ch->format_modifier = fb->modifier; + + pix_depth = ch->pix_format == DRM_FORMAT_NV12_10LE40 ? 10 : 8; + + old_xres = state->src_w >> 16; + old_yres = state->src_h >> 16; + + x1 = (state->src.x1 >> 16) & ~1; + y1 = (state->src.y1 >> 16) & ~1; + x2 = state->src.x2 >> 16; + y2 = state->src.y2 >> 16; + + xres = x2 - x1; + yres = y2 - y1; + + frame_height = ((old_yres >> 3) << FRAME_HEIGHT_POS) & FRAME_HEIGHT_MASK; + frame_width = ((old_xres >> 3) << FRAME_WIDTH_POS) & FRAME_WIDTH_MASK; + + dcss_dtrc_write(ch, frame_height | frame_width, + DTRC_F1_OFS * bank + DCSS_DTRC_SIZE); + + dcss_dtrc_write(ch, frame_height | frame_width, + DTRC_F1_OFS * (bank ^ 1) + DCSS_DTRC_SIZE); + + /* + * Image original size is aligned: + * - 128 pixels for width (8-bit) or 256 (10-bit); + * - 8 lines for height; + */ + width_align = ch->pix_format == DRM_FORMAT_NV12_10LE40 ? 0xff : 0x7f; + + if (xres == old_xres && !(xres & width_align) && + yres == old_yres && !(yres & 0xf)) { + ch->dctl &= ~CROPPING_EN; + goto exit; + } + + /* align the image size: down align for compressed formats */ + if (ch->format_modifier == DRM_FORMAT_MOD_VSI_G2_TILED_COMPRESSED && x1) + xres = xres & ~width_align; + else + xres = (xres + width_align) & ~width_align; + + if (ch->format_modifier == DRM_FORMAT_MOD_VSI_G2_TILED_COMPRESSED && y1) + yres = yres & ~0xf; + else + yres = (yres + 0xf) & ~0xf; + + crop_orig_w = (x1 << CROP_WIDTH_POS) & CROP_WIDTH_MASK; + crop_orig_h = (y1 << CROP_HEIGHT_POS) & CROP_HEIGHT_MASK; + + dcss_dtrc_write(ch, crop_orig_w | crop_orig_h, + DCSS_DTRC_CROPORIG); + dcss_dtrc_write(ch, crop_orig_w | crop_orig_h, + DTRC_F1_OFS + DCSS_DTRC_CROPORIG); + + crop_w = (xres << CROP_WIDTH_POS) & CROP_WIDTH_MASK; + crop_h = (yres << CROP_HEIGHT_POS) & CROP_HEIGHT_MASK; + + dcss_dtrc_write(ch, crop_w | crop_h, + DTRC_F1_OFS * bank + DCSS_DTRC_CROPSIZE); + dcss_dtrc_write(ch, crop_w | crop_h, + DTRC_F1_OFS * (bank ^ 1) + DCSS_DTRC_CROPSIZE); + + ch->dctl |= CROPPING_EN; + +exit: + dcss_dtrc_write(ch, xres * yres * pix_depth / 8, + DCSS_DTRC_SYSEA); + dcss_dtrc_write(ch, xres * yres * pix_depth / 8, + DTRC_F1_OFS + DCSS_DTRC_SYSEA); + + dcss_dtrc_write(ch, 0x10000000 + xres * yres * pix_depth / 8 / 2, + DCSS_DTRC_SUVSEA); + dcss_dtrc_write(ch, 0x10000000 + xres * yres * pix_depth / 8 / 2, + DTRC_F1_OFS + DCSS_DTRC_SUVSEA); + + *dtrc_w = xres; + *dtrc_h = yres; + + if (ch->running) + return; + + dcss_dtrc_write(ch, 0x0, DCSS_DTRC_SYSSA); + dcss_dtrc_write(ch, 0x0, DTRC_F1_OFS + DCSS_DTRC_SYSSA); + + dcss_dtrc_write(ch, 0x10000000, DCSS_DTRC_SUVSSA); + dcss_dtrc_write(ch, 0x10000000, DTRC_F1_OFS + DCSS_DTRC_SUVSSA); +} + +void dcss_dtrc_enable(struct dcss_dtrc *dtrc, int ch_num, bool enable) +{ + struct dcss_dtrc_ch *ch; + int curr_frame; + u32 fdctl, dtctrl; + + if (ch_num == 0) + return; + + ch = &dtrc->ch[ch_num - 1]; + + if (ch->bypass) + return; + + if (!enable) { + ch->running = false; + return; + } + + if (ch->running) + return; + + dcss_update(HOT_RESET, HOT_RESET, ch->base_reg + DCSS_DTRC_DTCTRL); + while (dcss_readl(ch->base_reg + DCSS_DTRC_DTCTRL) & HOT_RESET) + usleep_range(100, 200); + + dcss_dtrc_write(ch, 0x0f0e0100, + DCSS_DTRC_ARIDR); + dcss_dtrc_write(ch, 0x0f0e, + DCSS_DTRC_DTID2DDR); + + dtctrl = ADDRESS_ID_ENABLE | MERGE_ARID_ENABLE | + ((0xF << TABLE_DATA_SWAP_POS) & TABLE_DATA_SWAP_MASK) | + ((0x10 << BURST_LENGTH_POS) & BURST_LENGTH_MASK); + + if (ch->format_modifier == DRM_FORMAT_MOD_VSI_G1_TILED) + dtctrl |= G1_TILED_DATA_EN; + + dcss_dtrc_write(ch, dtctrl, DCSS_DTRC_DTCTRL); + + curr_frame = dcss_readl(ch->base_reg + DCSS_DTRC_DTCTRL) >> 31; + + fdctl = ch->dctl & ~(PIX_DEPTH_8BIT_EN | COMPRESSION_DIS); + + fdctl |= ch->pix_format == DRM_FORMAT_NV12_10LE40 ? 0 : PIX_DEPTH_8BIT_EN; + + if (ch->format_modifier != DRM_FORMAT_MOD_VSI_G2_TILED_COMPRESSED) + fdctl |= COMPRESSION_DIS; + + dcss_dtrc_write(ch, fdctl, + (curr_frame ^ 1) * DTRC_F1_OFS + DCSS_DTRC_DCTL); + dcss_dtrc_write(ch, fdctl | CONFIG_READY, + curr_frame * DTRC_F1_OFS + DCSS_DTRC_DCTL); + + ch->curr_frame = curr_frame; + ch->dctl = fdctl; + ch->running = true; +} + +bool dcss_dtrc_ch_running(struct dcss_dtrc *dtrc, int ch_num) +{ + struct dcss_dtrc_ch *ch; + + if (ch_num == 0) + return false; + + ch = &dtrc->ch[ch_num - 1]; + + return ch->running; +} + +bool dcss_dtrc_is_running(struct dcss_dtrc *dtrc) +{ + return dtrc->ch[0].running || dtrc->ch[1].running; +} + +static void dcss_dtrc_ch_switch_banks(struct dcss_dtrc *dtrc, int dtrc_ch) +{ + struct dcss_dtrc_ch *ch = &dtrc->ch[dtrc_ch]; + u32 b0, b1; + + if (!ch->running) + return; + + b0 = dcss_readl(ch->base_reg + DCSS_DTRC_DCTL) & 0x1; + b1 = dcss_readl(ch->base_reg + DTRC_F1_OFS + DCSS_DTRC_DCTL) & 0x1; + + ch->curr_frame = dcss_readl(ch->base_reg + DCSS_DTRC_DTCTRL) >> 31; + + dcss_dtrc_write_irqsafe(ch, ch->dctl | CONFIG_READY, + (ch->curr_frame ^ 1) * DTRC_F1_OFS + DCSS_DTRC_DCTL); +} + +void dcss_dtrc_switch_banks(struct dcss_dtrc *dtrc) +{ + dcss_dtrc_ch_switch_banks(dtrc, 0); + dcss_dtrc_ch_switch_banks(dtrc, 1); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-hdr10-tables.h b/drivers/gpu/drm/imx/dcss/dcss-hdr10-tables.h new file mode 100644 index 000000000000..a8f3e1d42926 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-hdr10-tables.h @@ -0,0 +1,1311 @@ +/* + * Copyright (C) 2018 NXP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#ifndef __DCSS_HDR10_TABLES_H__ +#define __DCSS_HDR10_TABLES_H__ + +/* + * Table descriptor (64 bit) contains flags with identify to which + * input/output configurations' combinations the table is applied. Choosing the + * right CSC depends on both output/input pipe configuration. + * + * Bit 0-2: Table type + * * LUT - bit 0 + * * CSCA - bit 1 (CSC for output pipe) + * * CSCB - bit 2 (not used for output pipe) + * Bit 3: Pipe type + * * Input - unset + * * Output - set + * Bit 4-5: Input pipe bits-per-component + * * 8 bpc - bit 4 + * * 10 bpc - bit 5 + * Bit 6-7: Input pipe colorspace + * * RGB - bit 6 + * * YUV - bit 7 + * Bit 8-12: Input pipe nonlinearity + * * REC2084 - bit 8 + * * REC709 - bit 9 + * * BT1886 - bit 10 + * * REC2100HLG - bit 11 + * * SRGB - bit 12 + * Bit 13-14: Input pipe pixel range + * * Limited - bit 13 + * * Full - bit 14 + * Bit 15-19: Input pipe gamut + * * REC2020 - bit 15 + * * REC709 - bit 16 + * * REC601_NTSC - bit 17 + * * REC601_PAL - bit 18 + * * ADOBE_RGB - bit 19 + * Bit 20-21: Output pipe bits-per-component (see above) + * Bit 22-23: Output pipe colorspace (see above) + * Bit 24-28: Output pipe nonlinearity (see above) + * Bit 29-30: Ouptut pipe pixel range (see above) + * Bit 31-35: Output pipe gamut (see above) + */ + +static u32 dcss_hdr10_tables[] = { + /* table descriptor */ + 0x68effbe9, 0xf, + /* table length */ + 0x400, + /* table data */ + 0x552, 0x153, 0x10ef, 0x54, 0x2fd, 0x92d, 0x208d, 0x14, 0xbe, 0x213, 0x412, + 0x6ea, 0xc62, 0x1764, 0x2d8d, 0x5, 0x2f, 0x83, 0x103, 0x1ad, 0x282, 0x382, + 0x4ad, 0x60c, 0x7f2, 0xaa4, 0xe76, 0x13e1, 0x1b92, 0x267c, 0x35f8, 0x1, + 0xb, 0x20, 0x40, 0x6a, 0x9f, 0xdf, 0x12a, 0x17f, 0x1df, 0x249, 0x2be, + 0x33e, 0x3c9, 0x45e, 0x4fe, 0x5ab, 0x676, 0x768, 0x889, 0x9e0, 0xb79, + 0xd60, 0xfa4, 0x1258, 0x158f, 0x1964, 0x1df4, 0x2364, 0x29dd, 0x3193, + 0x3ac3, 0x0, 0x2, 0x7, 0xf, 0x1a, 0x27, 0x37, 0x49, 0x5f, 0x76, 0x91, 0xae, + 0xce, 0xf1, 0x116, 0x13e, 0x168, 0x196, 0x1c6, 0x1f8, 0x22d, 0x265, 0x2a0, + 0x2dd, 0x31d, 0x360, 0x3a5, 0x3ed, 0x438, 0x485, 0x4d5, 0x528, 0x57e, + 0x5db, 0x640, 0x6af, 0x728, 0x7ac, 0x83c, 0x8d9, 0x984, 0xa40, 0xb0c, + 0xbeb, 0xcde, 0xde8, 0xf0a, 0x1046, 0x119f, 0x1318, 0x14b4, 0x1674, 0x185e, + 0x1a75, 0x1cbd, 0x1f39, 0x21f1, 0x24e7, 0x2823, 0x2bab, 0x2f85, 0x33b9, + 0x3850, 0x3d52, 0x0, 0x0, 0x1, 0x3, 0x6, 0x9, 0xd, 0x12, 0x17, 0x1d, 0x23, + 0x2b, 0x33, 0x3b, 0x45, 0x4e, 0x59, 0x64, 0x70, 0x7d, 0x8a, 0x98, 0xa7, + 0xb6, 0xc6, 0xd7, 0xe8, 0xfa, 0x10c, 0x120, 0x134, 0x148, 0x15e, 0x174, + 0x18a, 0x1a1, 0x1b9, 0x1d2, 0x1eb, 0x205, 0x220, 0x23b, 0x257, 0x274, + 0x291, 0x2af, 0x2ce, 0x2ed, 0x30d, 0x32e, 0x34f, 0x371, 0x394, 0x3b7, + 0x3db, 0x400, 0x425, 0x44b, 0x472, 0x499, 0x4c1, 0x4ea, 0x513, 0x53d, + 0x568, 0x594, 0x5c3, 0x5f3, 0x626, 0x65b, 0x692, 0x6cc, 0x709, 0x748, + 0x78a, 0x7cf, 0x817, 0x862, 0x8b0, 0x902, 0x958, 0x9b2, 0xa0f, 0xa71, + 0xad7, 0xb42, 0xbb1, 0xc26, 0xc9f, 0xd1e, 0xda3, 0xe2e, 0xebf, 0xf56, + 0xff4, 0x109a, 0x1146, 0x11fb, 0x12b7, 0x137c, 0x1449, 0x1520, 0x1600, + 0x16eb, 0x17e0, 0x18e0, 0x19eb, 0x1b02, 0x1c26, 0x1d57, 0x1e95, 0x1fe2, + 0x213d, 0x22a8, 0x2423, 0x25af, 0x274d, 0x28fe, 0x2ac1, 0x2c99, 0x2e86, + 0x3089, 0x32a3, 0x34d5, 0x3721, 0x3986, 0x3c07, 0x3ea5, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x7, 0x8, 0xa, 0xc, 0xe, 0x10, 0x13, 0x16, 0x18, + 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x31, 0x35, 0x39, 0x3d, 0x42, 0x47, + 0x4c, 0x51, 0x56, 0x5c, 0x61, 0x67, 0x6d, 0x73, 0x7a, 0x80, 0x87, 0x8e, + 0x95, 0x9c, 0xa3, 0xaa, 0xb2, 0xba, 0xc2, 0xca, 0xd2, 0xdb, 0xe3, 0xec, + 0xf5, 0xfe, 0x108, 0x111, 0x11b, 0x125, 0x12f, 0x139, 0x143, 0x14e, 0x158, + 0x163, 0x16e, 0x179, 0x184, 0x190, 0x19c, 0x1a7, 0x1b3, 0x1bf, 0x1cc, + 0x1d8, 0x1e5, 0x1f2, 0x1ff, 0x20c, 0x219, 0x227, 0x234, 0x242, 0x250, + 0x25e, 0x26d, 0x27b, 0x28a, 0x299, 0x2a8, 0x2b7, 0x2c6, 0x2d6, 0x2e5, + 0x2f5, 0x305, 0x315, 0x325, 0x336, 0x347, 0x357, 0x368, 0x37a, 0x38b, + 0x39c, 0x3ae, 0x3c0, 0x3d2, 0x3e4, 0x3f6, 0x409, 0x41b, 0x42e, 0x441, + 0x454, 0x468, 0x47b, 0x48f, 0x4a3, 0x4b7, 0x4cb, 0x4df, 0x4f4, 0x508, + 0x51d, 0x532, 0x547, 0x55d, 0x573, 0x589, 0x5a0, 0x5b7, 0x5cf, 0x5e7, + 0x600, 0x619, 0x633, 0x64d, 0x669, 0x684, 0x6a0, 0x6bd, 0x6db, 0x6f9, + 0x718, 0x738, 0x758, 0x779, 0x79b, 0x7bd, 0x7e0, 0x804, 0x829, 0x84f, + 0x875, 0x89c, 0x8c4, 0x8ed, 0x917, 0x942, 0x96e, 0x99b, 0x9c9, 0x9f7, + 0xa27, 0xa58, 0xa8a, 0xabd, 0xaf1, 0xb27, 0xb5d, 0xb95, 0xbce, 0xc08, + 0xc44, 0xc80, 0xcbf, 0xcfe, 0xd3f, 0xd82, 0xdc5, 0xe0b, 0xe52, 0xe9a, + 0xee4, 0xf30, 0xf7d, 0xfcc, 0x101d, 0x1070, 0x10c4, 0x111a, 0x1173, 0x11cd, + 0x1229, 0x1287, 0x12e7, 0x134a, 0x13ae, 0x1415, 0x147e, 0x14ea, 0x1557, + 0x15c7, 0x163a, 0x16af, 0x1727, 0x17a2, 0x181f, 0x189f, 0x1921, 0x19a7, + 0x1a30, 0x1abb, 0x1b4a, 0x1bdc, 0x1c71, 0x1d09, 0x1da5, 0x1e44, 0x1ee7, + 0x1f8d, 0x2037, 0x20e5, 0x2196, 0x224c, 0x2305, 0x23c3, 0x2485, 0x254b, + 0x2615, 0x26e4, 0x27b8, 0x2890, 0x296d, 0x2a4f, 0x2b35, 0x2c21, 0x2d13, + 0x2e09, 0x2f05, 0x3006, 0x310e, 0x321b, 0x332e, 0x3447, 0x3566, 0x368b, + 0x37b8, 0x38ea, 0x3a24, 0x3b64, 0x3cac, 0x3dfa, 0x3f51, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x2, 0x2, 0x3, 0x3, 0x4, 0x4, 0x5, 0x6, 0x6, + 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x14, 0x15, + 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e, 0x1f, 0x21, 0x23, 0x24, 0x26, 0x28, + 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3f, 0x41, + 0x43, 0x46, 0x48, 0x4b, 0x4d, 0x50, 0x52, 0x55, 0x58, 0x5a, 0x5d, 0x60, + 0x63, 0x66, 0x69, 0x6c, 0x6f, 0x72, 0x75, 0x78, 0x7b, 0x7e, 0x82, 0x85, + 0x88, 0x8c, 0x8f, 0x93, 0x96, 0x9a, 0x9d, 0xa1, 0xa5, 0xa9, 0xac, 0xb0, + 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd9, 0xdd, 0xe1, + 0xe6, 0xea, 0xee, 0xf3, 0xf7, 0xfc, 0x101, 0x105, 0x10a, 0x10f, 0x114, + 0x118, 0x11d, 0x122, 0x127, 0x12c, 0x131, 0x136, 0x13b, 0x140, 0x146, + 0x14b, 0x150, 0x155, 0x15b, 0x160, 0x166, 0x16b, 0x171, 0x176, 0x17c, + 0x182, 0x187, 0x18d, 0x193, 0x199, 0x19e, 0x1a4, 0x1aa, 0x1b0, 0x1b6, + 0x1bc, 0x1c3, 0x1c9, 0x1cf, 0x1d5, 0x1db, 0x1e2, 0x1e8, 0x1ef, 0x1f5, + 0x1fb, 0x202, 0x209, 0x20f, 0x216, 0x21d, 0x223, 0x22a, 0x231, 0x238, + 0x23f, 0x246, 0x24d, 0x254, 0x25b, 0x262, 0x269, 0x270, 0x277, 0x27f, + 0x286, 0x28d, 0x295, 0x29c, 0x2a4, 0x2ab, 0x2b3, 0x2bb, 0x2c2, 0x2ca, + 0x2d2, 0x2d9, 0x2e1, 0x2e9, 0x2f1, 0x2f9, 0x301, 0x309, 0x311, 0x319, + 0x321, 0x32a, 0x332, 0x33a, 0x342, 0x34b, 0x353, 0x35c, 0x364, 0x36d, + 0x375, 0x37e, 0x387, 0x38f, 0x398, 0x3a1, 0x3aa, 0x3b2, 0x3bb, 0x3c4, + 0x3cd, 0x3d6, 0x3df, 0x3e9, 0x3f2, 0x3fb, 0x404, 0x40d, 0x417, 0x420, + 0x42a, 0x433, 0x43d, 0x446, 0x450, 0x459, 0x463, 0x46d, 0x476, 0x480, + 0x48a, 0x494, 0x49e, 0x4a8, 0x4b2, 0x4bc, 0x4c6, 0x4d0, 0x4da, 0x4e4, + 0x4ef, 0x4f9, 0x503, 0x50e, 0x518, 0x523, 0x52d, 0x538, 0x542, 0x54d, + 0x557, 0x562, 0x56d, 0x578, 0x583, 0x58e, 0x59a, 0x5a5, 0x5b1, 0x5bd, + 0x5c9, 0x5d5, 0x5e1, 0x5ed, 0x5f9, 0x606, 0x613, 0x61f, 0x62c, 0x639, + 0x647, 0x654, 0x662, 0x66f, 0x67d, 0x68b, 0x699, 0x6a8, 0x6b6, 0x6c5, + 0x6d4, 0x6e2, 0x6f2, 0x701, 0x710, 0x720, 0x730, 0x740, 0x750, 0x760, + 0x771, 0x781, 0x792, 0x7a3, 0x7b4, 0x7c6, 0x7d7, 0x7e9, 0x7fb, 0x80d, + 0x820, 0x832, 0x845, 0x858, 0x86b, 0x87f, 0x892, 0x8a6, 0x8ba, 0x8cf, + 0x8e3, 0x8f8, 0x90d, 0x922, 0x937, 0x94d, 0x963, 0x979, 0x990, 0x9a6, + 0x9bd, 0x9d4, 0x9ec, 0xa03, 0xa1b, 0xa33, 0xa4c, 0xa65, 0xa7e, 0xa97, + 0xab0, 0xaca, 0xae4, 0xaff, 0xb19, 0xb34, 0xb50, 0xb6b, 0xb87, 0xba3, + 0xbc0, 0xbdc, 0xbf9, 0xc17, 0xc35, 0xc53, 0xc71, 0xc90, 0xcaf, 0xcce, + 0xcee, 0xd0e, 0xd2f, 0xd50, 0xd71, 0xd92, 0xdb4, 0xdd7, 0xdf9, 0xe1c, + 0xe40, 0xe64, 0xe88, 0xeac, 0xed1, 0xef7, 0xf1d, 0xf43, 0xf6a, 0xf91, + 0xfb8, 0xfe0, 0x1009, 0x1032, 0x105b, 0x1085, 0x10af, 0x10d9, 0x1105, + 0x1130, 0x115c, 0x1189, 0x11b6, 0x11e4, 0x1212, 0x1240, 0x126f, 0x129f, + 0x12cf, 0x1300, 0x1331, 0x1363, 0x1395, 0x13c8, 0x13fb, 0x142f, 0x1464, + 0x1499, 0x14ce, 0x1505, 0x153c, 0x1573, 0x15ab, 0x15e4, 0x161d, 0x1657, + 0x1692, 0x16cd, 0x1709, 0x1745, 0x1783, 0x17c1, 0x17ff, 0x183e, 0x187e, + 0x18bf, 0x1900, 0x1943, 0x1985, 0x19c9, 0x1a0d, 0x1a52, 0x1a98, 0x1adf, + 0x1b26, 0x1b6e, 0x1bb7, 0x1c01, 0x1c4b, 0x1c97, 0x1ce3, 0x1d30, 0x1d7e, + 0x1dcc, 0x1e1c, 0x1e6c, 0x1ebe, 0x1f10, 0x1f63, 0x1fb7, 0x200c, 0x2062, + 0x20b9, 0x2111, 0x216a, 0x21c3, 0x221e, 0x227a, 0x22d7, 0x2334, 0x2393, + 0x23f3, 0x2454, 0x24b6, 0x2519, 0x257d, 0x25e2, 0x2649, 0x26b0, 0x2719, + 0x2782, 0x27ed, 0x2859, 0x28c7, 0x2935, 0x29a5, 0x2a16, 0x2a88, 0x2afb, + 0x2b70, 0x2be6, 0x2c5d, 0x2cd6, 0x2d50, 0x2dcb, 0x2e47, 0x2ec5, 0x2f45, + 0x2fc6, 0x3048, 0x30cb, 0x3150, 0x31d7, 0x325f, 0x32e8, 0x3373, 0x3400, + 0x348e, 0x351d, 0x35af, 0x3641, 0x36d6, 0x376c, 0x3804, 0x389d, 0x3938, + 0x39d5, 0x3a73, 0x3b13, 0x3bb5, 0x3c59, 0x3cff, 0x3da6, 0x3e4f, 0x3efa, + 0x3fa7, 0x6, + /* table descriptor */ + 0xe8effbe9, 0x0, + /* table length */ + 0x400, + /* table data */ + 0x32a8, 0x2ea6, 0x361d, 0x2aa0, 0x30fc, 0x344a, 0x3808, 0x2696, 0x2cf8, + 0x3012, 0x3208, 0x3374, 0x3517, 0x36eb, 0x38d8, 0x2281, 0x28f0, 0x2c0f, + 0x2e06, 0x2f5a, 0x3082, 0x3181, 0x3256, 0x3305, 0x33f8, 0x34a8, 0x359c, + 0x367b, 0x3771, 0x3867, 0x395e, 0x1e58, 0x24e1, 0x2808, 0x2a01, 0x2b54, + 0x2c7e, 0x2d7d, 0x2e53, 0x2efd, 0x2fbd, 0x3049, 0x30be, 0x313d, 0x31c8, + 0x322e, 0x327e, 0x32d5, 0x333a, 0x33b3, 0x3421, 0x3477, 0x34dd, 0x3557, + 0x35e8, 0x364a, 0x36b1, 0x372b, 0x37bd, 0x3835, 0x389d, 0x3918, 0x39ab, + 0x1a0b, 0x20c2, 0x23f6, 0x25f1, 0x2748, 0x2877, 0x2974, 0x2a4e, 0x2af8, + 0x2bb6, 0x2c45, 0x2cba, 0x2d39, 0x2dc3, 0x2e2c, 0x2e7c, 0x2ed1, 0x2f2b, + 0x2f8b, 0x2ff0, 0x302d, 0x3065, 0x309f, 0x30dd, 0x311d, 0x315f, 0x31a4, + 0x31ec, 0x321b, 0x3242, 0x326a, 0x3293, 0x32be, 0x32ec, 0x331f, 0x3356, + 0x3393, 0x33d5, 0x340e, 0x3435, 0x3460, 0x348f, 0x34c2, 0x34fa, 0x3536, + 0x3579, 0x35c1, 0x3608, 0x3633, 0x3662, 0x3695, 0x36cd, 0x370b, 0x374d, + 0x3796, 0x37e6, 0x381e, 0x384d, 0x3881, 0x38ba, 0x38f7, 0x393a, 0x3984, + 0x39d4, 0x1500, 0x1c85, 0x1fc3, 0x21cc, 0x2331, 0x2468, 0x2563, 0x2644, + 0x26ec, 0x27aa, 0x283e, 0x28b2, 0x2931, 0x29bb, 0x2a27, 0x2a76, 0x2acb, + 0x2b25, 0x2b85, 0x2bea, 0x2c2a, 0x2c61, 0x2c9c, 0x2cd9, 0x2d18, 0x2d5b, + 0x2da0, 0x2de8, 0x2e19, 0x2e3f, 0x2e67, 0x2e90, 0x2ebb, 0x2ee7, 0x2f14, + 0x2f43, 0x2f72, 0x2fa4, 0x2fd6, 0x3005, 0x301f, 0x303b, 0x3057, 0x3073, + 0x3091, 0x30ae, 0x30cd, 0x30ec, 0x310c, 0x312d, 0x314e, 0x3170, 0x3193, + 0x31b6, 0x31da, 0x31ff, 0x3212, 0x3225, 0x3238, 0x324c, 0x3260, 0x3274, + 0x3289, 0x329e, 0x32b3, 0x32c9, 0x32e0, 0x32f9, 0x3312, 0x332c, 0x3348, + 0x3365, 0x3383, 0x33a3, 0x33c4, 0x33e6, 0x3405, 0x3418, 0x342b, 0x3440, + 0x3455, 0x346b, 0x3483, 0x349b, 0x34b5, 0x34cf, 0x34eb, 0x3508, 0x3527, + 0x3546, 0x3568, 0x358a, 0x35ae, 0x35d4, 0x35fc, 0x3612, 0x3628, 0x363e, + 0x3656, 0x366e, 0x3688, 0x36a3, 0x36bf, 0x36dc, 0x36fb, 0x371b, 0x373c, + 0x375f, 0x3783, 0x37aa, 0x37d1, 0x37fb, 0x3813, 0x382a, 0x3841, 0x385a, + 0x3874, 0x388f, 0x38ab, 0x38c8, 0x38e7, 0x3907, 0x3929, 0x394c, 0x3971, + 0x3997, 0x39bf, 0x39e9, 0xea8, 0x1815, 0x1b60, 0x1d86, 0x1f03, 0x204c, + 0x2142, 0x2231, 0x22d6, 0x2391, 0x2430, 0x24a3, 0x2521, 0x25a9, 0x261e, + 0x266c, 0x26c0, 0x271a, 0x2779, 0x27dd, 0x2823, 0x285a, 0x2894, 0x28d1, + 0x2910, 0x2952, 0x2997, 0x29df, 0x2a14, 0x2a3a, 0x2a62, 0x2a8b, 0x2ab6, + 0x2ae1, 0x2b0e, 0x2b3d, 0x2b6c, 0x2b9d, 0x2bd0, 0x2c02, 0x2c1c, 0x2c37, + 0x2c53, 0x2c70, 0x2c8d, 0x2cab, 0x2cc9, 0x2ce8, 0x2d08, 0x2d29, 0x2d4a, + 0x2d6c, 0x2d8e, 0x2db2, 0x2dd5, 0x2dfa, 0x2e0f, 0x2e22, 0x2e36, 0x2e49, + 0x2e5d, 0x2e71, 0x2e86, 0x2e9b, 0x2eb0, 0x2ec6, 0x2edc, 0x2ef2, 0x2f09, + 0x2f20, 0x2f37, 0x2f4e, 0x2f66, 0x2f7f, 0x2f97, 0x2fb0, 0x2fc9, 0x2fe3, + 0x2ffd, 0x300b, 0x3019, 0x3026, 0x3034, 0x3042, 0x3050, 0x305e, 0x306c, + 0x307b, 0x3089, 0x3098, 0x30a7, 0x30b6, 0x30c5, 0x30d5, 0x30e4, 0x30f4, + 0x3104, 0x3114, 0x3125, 0x3135, 0x3146, 0x3157, 0x3168, 0x3179, 0x318a, + 0x319c, 0x31ad, 0x31bf, 0x31d1, 0x31e3, 0x31f5, 0x3204, 0x320d, 0x3216, + 0x3220, 0x3229, 0x3233, 0x323d, 0x3247, 0x3251, 0x325b, 0x3265, 0x326f, + 0x3279, 0x3283, 0x328e, 0x3298, 0x32a3, 0x32ae, 0x32b8, 0x32c4, 0x32cf, + 0x32da, 0x32e6, 0x32f2, 0x32ff, 0x330c, 0x3318, 0x3326, 0x3333, 0x3341, + 0x334f, 0x335e, 0x336c, 0x337c, 0x338b, 0x339b, 0x33ab, 0x33bb, 0x33cc, + 0x33dd, 0x33ef, 0x3400, 0x3409, 0x3413, 0x341c, 0x3426, 0x3430, 0x343a, + 0x3445, 0x3450, 0x345b, 0x3466, 0x3471, 0x347d, 0x3489, 0x3495, 0x34a2, + 0x34ae, 0x34bb, 0x34c9, 0x34d6, 0x34e4, 0x34f2, 0x3501, 0x3510, 0x351f, + 0x352f, 0x353e, 0x354f, 0x355f, 0x3570, 0x3581, 0x3593, 0x35a5, 0x35b8, + 0x35cb, 0x35de, 0x35f2, 0x3603, 0x360d, 0x3618, 0x3622, 0x362d, 0x3639, + 0x3644, 0x3650, 0x365c, 0x3668, 0x3675, 0x3682, 0x368f, 0x369c, 0x36aa, + 0x36b8, 0x36c6, 0x36d5, 0x36e4, 0x36f3, 0x3703, 0x3713, 0x3723, 0x3734, + 0x3745, 0x3756, 0x3768, 0x377a, 0x378d, 0x37a0, 0x37b3, 0x37c7, 0x37dc, + 0x37f0, 0x3802, 0x380d, 0x3818, 0x3824, 0x382f, 0x383b, 0x3847, 0x3854, + 0x3860, 0x386d, 0x387a, 0x3888, 0x3896, 0x38a4, 0x38b2, 0x38c1, 0x38d0, + 0x38df, 0x38ef, 0x38ff, 0x3910, 0x3920, 0x3932, 0x3943, 0x3955, 0x3967, + 0x397a, 0x398d, 0x39a1, 0x39b5, 0x39c9, 0x39de, 0x39f4, 0x0, 0x12aa, + 0x16ab, 0x1900, 0x1aab, 0x1c15, 0x1d00, 0x1e0b, 0x1eab, 0x1f60, 0x2015, + 0x2085, 0x2100, 0x2186, 0x220b, 0x2258, 0x22ab, 0x2303, 0x2360, 0x23c3, + 0x2415, 0x244c, 0x2485, 0x24c2, 0x2500, 0x2542, 0x2586, 0x25cc, 0x260b, + 0x2631, 0x2658, 0x2681, 0x26ab, 0x26d6, 0x2703, 0x2731, 0x2760, 0x2791, + 0x27c3, 0x27f6, 0x2815, 0x2830, 0x284c, 0x2868, 0x2885, 0x28a3, 0x28c2, + 0x28e1, 0x2900, 0x2921, 0x2942, 0x2963, 0x2986, 0x29a9, 0x29cc, 0x29f1, + 0x2a0b, 0x2a1e, 0x2a31, 0x2a44, 0x2a58, 0x2a6c, 0x2a81, 0x2a96, 0x2aab, + 0x2ac0, 0x2ad6, 0x2aec, 0x2b03, 0x2b1a, 0x2b31, 0x2b48, 0x2b60, 0x2b79, + 0x2b91, 0x2baa, 0x2bc3, 0x2bdd, 0x2bf6, 0x2c08, 0x2c15, 0x2c23, 0x2c30, + 0x2c3e, 0x2c4c, 0x2c5a, 0x2c68, 0x2c77, 0x2c85, 0x2c94, 0x2ca3, 0x2cb2, + 0x2cc2, 0x2cd1, 0x2ce1, 0x2cf0, 0x2d00, 0x2d10, 0x2d21, 0x2d31, 0x2d42, + 0x2d52, 0x2d63, 0x2d74, 0x2d86, 0x2d97, 0x2da9, 0x2dbb, 0x2dcc, 0x2ddf, + 0x2df1, 0x2e01, 0x2e0b, 0x2e14, 0x2e1e, 0x2e27, 0x2e31, 0x2e3a, 0x2e44, + 0x2e4e, 0x2e58, 0x2e62, 0x2e6c, 0x2e76, 0x2e81, 0x2e8b, 0x2e96, 0x2ea0, + 0x2eab, 0x2eb6, 0x2ec0, 0x2ecb, 0x2ed6, 0x2ee1, 0x2eec, 0x2ef8, 0x2f03, + 0x2f0e, 0x2f1a, 0x2f25, 0x2f31, 0x2f3d, 0x2f48, 0x2f54, 0x2f60, 0x2f6c, + 0x2f79, 0x2f85, 0x2f91, 0x2f9d, 0x2faa, 0x2fb6, 0x2fc3, 0x2fd0, 0x2fdd, + 0x2fea, 0x2ff6, 0x3002, 0x3008, 0x300f, 0x3015, 0x301c, 0x3023, 0x302a, + 0x3030, 0x3037, 0x303e, 0x3045, 0x304c, 0x3053, 0x305a, 0x3061, 0x3068, + 0x3070, 0x3077, 0x307e, 0x3085, 0x308d, 0x3094, 0x309c, 0x30a3, 0x30ab, + 0x30b2, 0x30ba, 0x30c2, 0x30c9, 0x30d1, 0x30d9, 0x30e1, 0x30e8, 0x30f0, + 0x30f8, 0x3100, 0x3108, 0x3110, 0x3118, 0x3121, 0x3129, 0x3131, 0x3139, + 0x3142, 0x314a, 0x3152, 0x315b, 0x3163, 0x316c, 0x3174, 0x317d, 0x3186, + 0x318e, 0x3197, 0x31a0, 0x31a9, 0x31b2, 0x31bb, 0x31c3, 0x31cc, 0x31d5, + 0x31df, 0x31e8, 0x31f1, 0x31fa, 0x3201, 0x3206, 0x320b, 0x320f, 0x3214, + 0x3219, 0x321e, 0x3222, 0x3227, 0x322c, 0x3231, 0x3236, 0x323a, 0x323f, + 0x3244, 0x3249, 0x324e, 0x3253, 0x3258, 0x325d, 0x3262, 0x3267, 0x326c, + 0x3271, 0x3276, 0x327c, 0x3281, 0x3286, 0x328b, 0x3290, 0x3296, 0x329b, + 0x32a0, 0x32a6, 0x32ab, 0x32b0, 0x32b6, 0x32bb, 0x32c1, 0x32c6, 0x32cc, + 0x32d2, 0x32d8, 0x32dd, 0x32e3, 0x32e9, 0x32ef, 0x32f6, 0x32fc, 0x3302, + 0x3308, 0x330f, 0x3315, 0x331c, 0x3322, 0x3329, 0x3330, 0x3337, 0x333e, + 0x3345, 0x334c, 0x3353, 0x335a, 0x3361, 0x3369, 0x3370, 0x3378, 0x337f, + 0x3387, 0x338f, 0x3397, 0x339f, 0x33a7, 0x33af, 0x33b7, 0x33bf, 0x33c8, + 0x33d0, 0x33d9, 0x33e2, 0x33eb, 0x33f3, 0x33fc, 0x3402, 0x3407, 0x340c, + 0x3410, 0x3415, 0x341a, 0x341f, 0x3424, 0x3429, 0x342e, 0x3433, 0x3438, + 0x343d, 0x3442, 0x3448, 0x344d, 0x3452, 0x3458, 0x345d, 0x3463, 0x3469, + 0x346e, 0x3474, 0x347a, 0x3480, 0x3486, 0x348c, 0x3492, 0x3498, 0x349e, + 0x34a5, 0x34ab, 0x34b2, 0x34b8, 0x34bf, 0x34c5, 0x34cc, 0x34d3, 0x34da, + 0x34e1, 0x34e8, 0x34ef, 0x34f6, 0x34fd, 0x3505, 0x350c, 0x3514, 0x351b, + 0x3523, 0x352b, 0x3532, 0x353a, 0x3542, 0x354b, 0x3553, 0x355b, 0x3563, + 0x356c, 0x3574, 0x357d, 0x3586, 0x358f, 0x3598, 0x35a1, 0x35aa, 0x35b3, + 0x35bc, 0x35c6, 0x35cf, 0x35d9, 0x35e3, 0x35ed, 0x35f7, 0x3600, 0x3605, + 0x360a, 0x3610, 0x3615, 0x361a, 0x3620, 0x3625, 0x362b, 0x3630, 0x3636, + 0x363b, 0x3641, 0x3647, 0x364d, 0x3653, 0x3659, 0x365f, 0x3665, 0x366b, + 0x3672, 0x3678, 0x367e, 0x3685, 0x368b, 0x3692, 0x3699, 0x36a0, 0x36a6, + 0x36ad, 0x36b4, 0x36bb, 0x36c3, 0x36ca, 0x36d1, 0x36d9, 0x36e0, 0x36e8, + 0x36ef, 0x36f7, 0x36ff, 0x3707, 0x370f, 0x3717, 0x371f, 0x3727, 0x372f, + 0x3738, 0x3740, 0x3749, 0x3752, 0x375b, 0x3764, 0x376d, 0x3776, 0x377f, + 0x3788, 0x3792, 0x379b, 0x37a5, 0x37ae, 0x37b8, 0x37c2, 0x37cc, 0x37d6, + 0x37e1, 0x37eb, 0x37f6, 0x3800, 0x3805, 0x380b, 0x3810, 0x3816, 0x381b, + 0x3821, 0x3827, 0x382c, 0x3832, 0x3838, 0x383e, 0x3844, 0x384a, 0x3851, + 0x3857, 0x385d, 0x3864, 0x386a, 0x3870, 0x3877, 0x387e, 0x3885, 0x388b, + 0x3892, 0x3899, 0x38a0, 0x38a7, 0x38af, 0x38b6, 0x38bd, 0x38c5, 0x38cc, + 0x38d4, 0x38dc, 0x38e3, 0x38eb, 0x38f3, 0x38fb, 0x3903, 0x390c, 0x3914, + 0x391c, 0x3925, 0x392d, 0x3936, 0x393f, 0x3948, 0x3951, 0x395a, 0x3963, + 0x396c, 0x3975, 0x397f, 0x3989, 0x3992, 0x399c, 0x39a6, 0x39b0, 0x39ba, + 0x39c4, 0x39cf, 0x39d9, 0x39e4, 0x39ee, 0x39f9, 0xc, + /* table descriptor */ + 0xe1effbe9, 0x0, + /* table length */ + 0x400, + /* table data */ + 0x2c59, 0x2416, 0x3322, 0x1dd0, 0x287e, 0x2ff0, 0x3679, 0x188b, 0x2131, + 0x2661, 0x2a78, 0x2e2b, 0x3186, 0x34c8, 0x3836, 0x141d, 0x1b81, 0x1fa3, + 0x22a3, 0x2531, 0x277a, 0x2988, 0x2b6b, 0x2d33, 0x2eed, 0x30a3, 0x325a, + 0x3416, 0x35b5, 0x374e, 0x38f8, 0xff8, 0x1698, 0x1a32, 0x1ca3, 0x1eab, + 0x206e, 0x2210, 0x2355, 0x2497, 0x25e9, 0x26e2, 0x2816, 0x28f9, 0x2a17, + 0x2ae8, 0x2c01, 0x2cbe, 0x2dbb, 0x2e85, 0x2f65, 0x3047, 0x310d, 0x3209, + 0x32b7, 0x339e, 0x3469, 0x3536, 0x3624, 0x36db, 0x37d2, 0x388f, 0x3971, + 0xbe0, 0x124a, 0x1572, 0x17b5, 0x1962, 0x1acc, 0x1c29, 0x1d30, 0x1e43, + 0x1f20, 0x201b, 0x20ca, 0x21a2, 0x2256, 0x22f8, 0x23bc, 0x2454, 0x24e1, + 0x2589, 0x2629, 0x269f, 0x272b, 0x27d0, 0x2848, 0x28b9, 0x293d, 0x29d8, + 0x2a45, 0x2aae, 0x2b27, 0x2bb4, 0x2c2b, 0x2c89, 0x2cf6, 0x2d74, 0x2e03, + 0x2e57, 0x2eb8, 0x2f27, 0x2fa8, 0x301e, 0x3073, 0x30d6, 0x3147, 0x31ca, + 0x3230, 0x3287, 0x32eb, 0x335e, 0x33e3, 0x343e, 0x3497, 0x34fd, 0x3573, + 0x35fc, 0x364d, 0x36a8, 0x3712, 0x378d, 0x380e, 0x3861, 0x38c1, 0x3932, + 0x39b5, 0x700, 0xe30, 0x1120, 0x132c, 0x14bb, 0x1621, 0x171f, 0x182e, + 0x18f2, 0x19de, 0x1a7c, 0x1b23, 0x1be6, 0x1c64, 0x1ce7, 0x1d7d, 0x1e14, + 0x1e75, 0x1ee3, 0x1f60, 0x1feb, 0x2043, 0x209b, 0x20fc, 0x2168, 0x21e0, + 0x2232, 0x227b, 0x22cc, 0x2325, 0x2387, 0x23f3, 0x2434, 0x2475, 0x24bb, + 0x2508, 0x255d, 0x25b8, 0x260e, 0x2644, 0x2680, 0x26c0, 0x2706, 0x2752, + 0x27a4, 0x27fd, 0x282f, 0x2863, 0x289b, 0x28d8, 0x291a, 0x2962, 0x29af, + 0x2a01, 0x2a2e, 0x2a5e, 0x2a92, 0x2aca, 0x2b07, 0x2b48, 0x2b8f, 0x2bda, + 0x2c16, 0x2c41, 0x2c70, 0x2ca3, 0x2cda, 0x2d14, 0x2d53, 0x2d97, 0x2de0, + 0x2e17, 0x2e41, 0x2e6e, 0x2e9e, 0x2ed2, 0x2f0a, 0x2f46, 0x2f86, 0x2fcb, + 0x300a, 0x3032, 0x305d, 0x308b, 0x30bc, 0x30f1, 0x3129, 0x3166, 0x31a8, + 0x31ee, 0x321c, 0x3245, 0x3270, 0x329f, 0x32d1, 0x3306, 0x3340, 0x337e, + 0x33c0, 0x3404, 0x342a, 0x3453, 0x347f, 0x34af, 0x34e2, 0x3519, 0x3554, + 0x3593, 0x35d8, 0x3610, 0x3638, 0x3662, 0x3690, 0x36c1, 0x36f6, 0x372f, + 0x376d, 0x37af, 0x37f7, 0x3821, 0x384b, 0x3878, 0x38a8, 0x38dc, 0x3914, + 0x3951, 0x3992, 0x39d9, 0x200, 0xa20, 0xd00, 0xf00, 0x1084, 0x11d0, 0x12b6, + 0x13ae, 0x1469, 0x1513, 0x15d7, 0x165b, 0x16da, 0x1768, 0x1803, 0x185b, + 0x18bd, 0x1929, 0x199f, 0x1a10, 0x1a56, 0x1aa3, 0x1af6, 0x1b51, 0x1bb2, + 0x1c0e, 0x1c46, 0x1c83, 0x1cc5, 0x1d0b, 0x1d56, 0x1da6, 0x1dfc, 0x1e2b, + 0x1e5c, 0x1e90, 0x1ec7, 0x1f01, 0x1f3f, 0x1f81, 0x1fc7, 0x2008, 0x202f, + 0x2058, 0x2084, 0x20b2, 0x20e3, 0x2116, 0x214c, 0x2185, 0x21c1, 0x2200, + 0x2221, 0x2244, 0x2268, 0x228f, 0x22b7, 0x22e2, 0x230e, 0x233d, 0x236e, + 0x23a1, 0x23d7, 0x2407, 0x2425, 0x2444, 0x2464, 0x2486, 0x24a9, 0x24ce, + 0x24f4, 0x251d, 0x2547, 0x2573, 0x25a1, 0x25d1, 0x2601, 0x261b, 0x2636, + 0x2653, 0x2670, 0x268f, 0x26af, 0x26d1, 0x26f4, 0x2718, 0x273e, 0x2766, + 0x278f, 0x27b9, 0x27e6, 0x280a, 0x2822, 0x283b, 0x2855, 0x2870, 0x288d, + 0x28aa, 0x28c9, 0x28e8, 0x2909, 0x292c, 0x294f, 0x2975, 0x299b, 0x29c3, + 0x29ed, 0x2a0c, 0x2a22, 0x2a39, 0x2a52, 0x2a6b, 0x2a85, 0x2aa0, 0x2abc, + 0x2ad9, 0x2af7, 0x2b17, 0x2b38, 0x2b59, 0x2b7d, 0x2ba1, 0x2bc7, 0x2bee, + 0x2c0b, 0x2c20, 0x2c36, 0x2c4d, 0x2c64, 0x2c7d, 0x2c96, 0x2cb0, 0x2ccc, + 0x2ce8, 0x2d05, 0x2d24, 0x2d43, 0x2d64, 0x2d85, 0x2da9, 0x2dcd, 0x2df3, + 0x2e0d, 0x2e21, 0x2e36, 0x2e4c, 0x2e62, 0x2e79, 0x2e92, 0x2eab, 0x2ec5, + 0x2ee0, 0x2efb, 0x2f18, 0x2f36, 0x2f55, 0x2f76, 0x2f97, 0x2fb9, 0x2fdd, + 0x3001, 0x3014, 0x3028, 0x303d, 0x3052, 0x3068, 0x307f, 0x3097, 0x30af, + 0x30c9, 0x30e3, 0x30ff, 0x311b, 0x3138, 0x3157, 0x3176, 0x3197, 0x31b9, + 0x31dc, 0x3200, 0x3213, 0x3226, 0x323a, 0x324f, 0x3265, 0x327b, 0x3293, + 0x32ab, 0x32c4, 0x32de, 0x32f8, 0x3314, 0x3331, 0x334f, 0x336e, 0x338e, + 0x33af, 0x33d2, 0x33f5, 0x340d, 0x3420, 0x3434, 0x3448, 0x345e, 0x3474, + 0x348b, 0x34a3, 0x34bb, 0x34d5, 0x34ef, 0x350b, 0x3527, 0x3545, 0x3563, + 0x3583, 0x35a4, 0x35c6, 0x35ea, 0x3607, 0x361a, 0x362e, 0x3642, 0x3657, + 0x366d, 0x3684, 0x369c, 0x36b5, 0x36ce, 0x36e9, 0x3704, 0x3721, 0x373e, + 0x375d, 0x377d, 0x379e, 0x37c1, 0x37e4, 0x3804, 0x3818, 0x382c, 0x3840, + 0x3856, 0x386c, 0x3883, 0x389b, 0x38b5, 0x38cf, 0x38ea, 0x3906, 0x3923, + 0x3941, 0x3961, 0x3981, 0x39a3, 0x39c7, 0x39ec, 0x0, 0x500, 0x8c0, 0xae0, + 0xc70, 0xdb0, 0xe98, 0xf78, 0x103c, 0x10d0, 0x1178, 0x1218, 0x127e, 0x12f0, + 0x136c, 0x13f2, 0x1442, 0x1491, 0x14e6, 0x1542, 0x15a4, 0x1606, 0x163e, + 0x1679, 0x16b9, 0x16fc, 0x1743, 0x178e, 0x17dd, 0x1818, 0x1844, 0x1873, + 0x18a4, 0x18d7, 0x190d, 0x1945, 0x1980, 0x19be, 0x19ff, 0x1a21, 0x1a44, + 0x1a69, 0x1a8f, 0x1ab7, 0x1ae1, 0x1b0c, 0x1b39, 0x1b68, 0x1b99, 0x1bcc, + 0x1c00, 0x1c1b, 0x1c38, 0x1c55, 0x1c74, 0x1c93, 0x1cb4, 0x1cd6, 0x1cf9, + 0x1d1d, 0x1d42, 0x1d69, 0x1d91, 0x1dbb, 0x1de6, 0x1e09, 0x1e20, 0x1e37, + 0x1e4f, 0x1e69, 0x1e82, 0x1e9d, 0x1eb9, 0x1ed5, 0x1ef2, 0x1f10, 0x1f2f, + 0x1f4f, 0x1f70, 0x1f92, 0x1fb5, 0x1fd9, 0x1ffe, 0x2012, 0x2025, 0x2039, + 0x204e, 0x2063, 0x2079, 0x208f, 0x20a6, 0x20be, 0x20d6, 0x20ef, 0x2109, + 0x2123, 0x213e, 0x215a, 0x2176, 0x2194, 0x21b2, 0x21d0, 0x21f0, 0x2208, + 0x2218, 0x2229, 0x223b, 0x224d, 0x225f, 0x2272, 0x2285, 0x2299, 0x22ad, + 0x22c2, 0x22d7, 0x22ed, 0x2303, 0x231a, 0x2331, 0x2349, 0x2361, 0x237a, + 0x2394, 0x23ae, 0x23c9, 0x23e5, 0x2400, 0x240f, 0x241d, 0x242c, 0x243c, + 0x244c, 0x245c, 0x246c, 0x247d, 0x248e, 0x24a0, 0x24b2, 0x24c4, 0x24d7, + 0x24eb, 0x24fe, 0x2512, 0x2527, 0x253c, 0x2552, 0x2568, 0x257e, 0x2595, + 0x25ac, 0x25c4, 0x25dd, 0x25f6, 0x2607, 0x2614, 0x2622, 0x262f, 0x263d, + 0x264c, 0x265a, 0x2669, 0x2678, 0x2687, 0x2697, 0x26a7, 0x26b8, 0x26c8, + 0x26d9, 0x26eb, 0x26fd, 0x270f, 0x2722, 0x2734, 0x2748, 0x275c, 0x2770, + 0x2784, 0x2799, 0x27af, 0x27c4, 0x27db, 0x27f1, 0x2804, 0x2810, 0x281c, + 0x2828, 0x2835, 0x2842, 0x284f, 0x285c, 0x286a, 0x2877, 0x2886, 0x2894, + 0x28a3, 0x28b2, 0x28c1, 0x28d0, 0x28e0, 0x28f1, 0x2901, 0x2912, 0x2923, + 0x2935, 0x2946, 0x2959, 0x296b, 0x297e, 0x2991, 0x29a5, 0x29b9, 0x29cd, + 0x29e2, 0x29f7, 0x2a06, 0x2a11, 0x2a1c, 0x2a28, 0x2a33, 0x2a3f, 0x2a4b, + 0x2a58, 0x2a64, 0x2a71, 0x2a7e, 0x2a8b, 0x2a99, 0x2aa7, 0x2ab5, 0x2ac3, + 0x2ad2, 0x2ae1, 0x2af0, 0x2aff, 0x2b0f, 0x2b1f, 0x2b2f, 0x2b40, 0x2b51, + 0x2b62, 0x2b74, 0x2b86, 0x2b98, 0x2baa, 0x2bbd, 0x2bd0, 0x2be4, 0x2bf8, + 0x2c06, 0x2c10, 0x2c1b, 0x2c26, 0x2c31, 0x2c3c, 0x2c47, 0x2c53, 0x2c5e, + 0x2c6a, 0x2c77, 0x2c83, 0x2c90, 0x2c9d, 0x2caa, 0x2cb7, 0x2cc5, 0x2cd3, + 0x2ce1, 0x2cef, 0x2cfe, 0x2d0d, 0x2d1c, 0x2d2b, 0x2d3b, 0x2d4b, 0x2d5b, + 0x2d6c, 0x2d7d, 0x2d8e, 0x2da0, 0x2db2, 0x2dc4, 0x2dd6, 0x2de9, 0x2dfc, + 0x2e08, 0x2e12, 0x2e1c, 0x2e26, 0x2e30, 0x2e3b, 0x2e46, 0x2e51, 0x2e5c, + 0x2e68, 0x2e73, 0x2e7f, 0x2e8b, 0x2e98, 0x2ea4, 0x2eb1, 0x2ebe, 0x2ecb, + 0x2ed9, 0x2ee6, 0x2ef4, 0x2f03, 0x2f11, 0x2f20, 0x2f2f, 0x2f3e, 0x2f4e, + 0x2f5d, 0x2f6d, 0x2f7e, 0x2f8e, 0x2f9f, 0x2fb1, 0x2fc2, 0x2fd4, 0x2fe6, + 0x2ff9, 0x3006, 0x300f, 0x3019, 0x3023, 0x302d, 0x3037, 0x3042, 0x304d, + 0x3057, 0x3062, 0x306e, 0x3079, 0x3085, 0x3091, 0x309d, 0x30a9, 0x30b6, + 0x30c2, 0x30cf, 0x30dd, 0x30ea, 0x30f8, 0x3106, 0x3114, 0x3122, 0x3131, + 0x3140, 0x314f, 0x315e, 0x316e, 0x317e, 0x318f, 0x319f, 0x31b0, 0x31c1, + 0x31d3, 0x31e5, 0x31f7, 0x3204, 0x320e, 0x3217, 0x3221, 0x322b, 0x3235, + 0x323f, 0x324a, 0x3255, 0x325f, 0x326a, 0x3276, 0x3281, 0x328d, 0x3299, + 0x32a5, 0x32b1, 0x32bd, 0x32ca, 0x32d7, 0x32e4, 0x32f2, 0x32ff, 0x330d, + 0x331b, 0x332a, 0x3338, 0x3347, 0x3357, 0x3366, 0x3376, 0x3386, 0x3396, + 0x33a7, 0x33b8, 0x33c9, 0x33da, 0x33ec, 0x33fe, 0x3408, 0x3412, 0x341b, + 0x3425, 0x342f, 0x3439, 0x3443, 0x344e, 0x3458, 0x3463, 0x346e, 0x347a, + 0x3485, 0x3491, 0x349d, 0x34a9, 0x34b5, 0x34c2, 0x34ce, 0x34db, 0x34e9, + 0x34f6, 0x3504, 0x3512, 0x3520, 0x352f, 0x353d, 0x354c, 0x355c, 0x356b, + 0x357b, 0x358b, 0x359c, 0x35ad, 0x35be, 0x35cf, 0x35e1, 0x35f3, 0x3602, + 0x360c, 0x3615, 0x361f, 0x3629, 0x3633, 0x363d, 0x3647, 0x3652, 0x365d, + 0x3668, 0x3673, 0x367f, 0x368a, 0x3696, 0x36a2, 0x36ae, 0x36bb, 0x36c8, + 0x36d5, 0x36e2, 0x36f0, 0x36fd, 0x370b, 0x371a, 0x3728, 0x3737, 0x3746, + 0x3755, 0x3765, 0x3775, 0x3785, 0x3796, 0x37a7, 0x37b8, 0x37c9, 0x37db, + 0x37ed, 0x3800, 0x3809, 0x3813, 0x381d, 0x3826, 0x3831, 0x383b, 0x3846, + 0x3850, 0x385b, 0x3866, 0x3872, 0x387d, 0x3889, 0x3895, 0x38a2, 0x38ae, + 0x38bb, 0x38c8, 0x38d5, 0x38e3, 0x38f1, 0x38ff, 0x390d, 0x391b, 0x392a, + 0x3939, 0x3949, 0x3959, 0x3969, 0x3979, 0x398a, 0x399b, 0x39ac, 0x39be, + 0x39d0, 0x39e2, 0x39f5, 0xc, + /* table descriptor */ + 0xe2effbe9, 0xf, + /* table length */ + 0x400, + /* table data */ + 0x1095, 0x4fa, 0x240a, 0x1d9, 0x9d8, 0x1949, 0x30eb, 0xe0, 0x334, 0x72f, + 0xcf9, 0x14ae, 0x1e66, 0x2a35, 0x382e, 0x6e, 0x152, 0x27a, 0x409, 0x606, + 0x875, 0xb5a, 0xeb7, 0x1291, 0x16eb, 0x1bc7, 0x2127, 0x270e, 0x2d7f, + 0x347b, 0x3c04, 0x35, 0xa7, 0x119, 0x193, 0x226, 0x2d4, 0x39b, 0x47e, + 0x57d, 0x697, 0x7cf, 0x923, 0xa95, 0xc25, 0xdd4, 0xfa2, 0x118f, 0x139c, + 0x15c9, 0x1816, 0x1a84, 0x1d12, 0x1fc2, 0x2294, 0x2588, 0x289e, 0x2bd6, + 0x2f31, 0x32af, 0x3650, 0x3a15, 0x3dfd, 0x18, 0x51, 0x8a, 0xc3, 0xfc, + 0x134, 0x172, 0x1b5, 0x1ff, 0x24f, 0x2a6, 0x303, 0x367, 0x3d2, 0x443, + 0x4bb, 0x53a, 0x5c1, 0x64e, 0x6e2, 0x77e, 0x821, 0x8cb, 0x97d, 0xa36, + 0xaf6, 0xbbf, 0xc8e, 0xd66, 0xe45, 0xf2c, 0x101a, 0x1111, 0x120f, 0x1316, + 0x1424, 0x153a, 0x1659, 0x177f, 0x18ae, 0x19e5, 0x1b24, 0x1c6c, 0x1dbb, + 0x1f13, 0x2074, 0x21dd, 0x234e, 0x24c8, 0x264a, 0x27d5, 0x2968, 0x2b05, + 0x2ca9, 0x2e57, 0x300d, 0x31cc, 0x3394, 0x3564, 0x373e, 0x3920, 0x3b0b, + 0x3cff, 0x3efd, 0xa, 0x27, 0x43, 0x60, 0x7c, 0x99, 0xb5, 0xd1, 0xee, 0x10a, + 0x126, 0x143, 0x162, 0x182, 0x1a4, 0x1c7, 0x1ec, 0x213, 0x23b, 0x264, + 0x290, 0x2bd, 0x2eb, 0x31c, 0x34d, 0x381, 0x3b6, 0x3ed, 0x426, 0x460, + 0x49c, 0x4da, 0x51a, 0x55b, 0x59e, 0x5e3, 0x62a, 0x672, 0x6bd, 0x709, + 0x756, 0x7a6, 0x7f7, 0x84b, 0x8a0, 0x8f7, 0x950, 0x9aa, 0xa07, 0xa65, + 0xac6, 0xb28, 0xb8c, 0xbf2, 0xc5a, 0xcc3, 0xd2f, 0xd9d, 0xe0c, 0xe7e, + 0xef1, 0xf67, 0xfde, 0x1057, 0x10d3, 0x1150, 0x11cf, 0x1250, 0x12d3, + 0x1359, 0x13e0, 0x1469, 0x14f4, 0x1581, 0x1610, 0x16a2, 0x1735, 0x17ca, + 0x1862, 0x18fb, 0x1996, 0x1a34, 0x1ad4, 0x1b75, 0x1c19, 0x1cbf, 0x1d67, + 0x1e10, 0x1ebd, 0x1f6b, 0x201b, 0x20cd, 0x2182, 0x2238, 0x22f1, 0x23ac, + 0x2469, 0x2528, 0x25e9, 0x26ac, 0x2771, 0x2839, 0x2903, 0x29cf, 0x2a9d, + 0x2b6d, 0x2c3f, 0x2d14, 0x2deb, 0x2ec4, 0x2f9f, 0x307c, 0x315b, 0x323d, + 0x3321, 0x3407, 0x34ef, 0x35da, 0x36c7, 0x37b6, 0x38a7, 0x399a, 0x3a90, + 0x3b87, 0x3c82, 0x3d7e, 0x3e7c, 0x3f7d, 0x3, 0x11, 0x20, 0x2e, 0x3c, 0x4a, + 0x58, 0x67, 0x75, 0x83, 0x91, 0xa0, 0xae, 0xbc, 0xca, 0xd9, 0xe7, 0xf5, + 0x103, 0x112, 0x120, 0x12d, 0x13c, 0x14b, 0x15a, 0x16a, 0x17a, 0x18a, + 0x19b, 0x1ad, 0x1be, 0x1d0, 0x1e3, 0x1f6, 0x209, 0x21c, 0x230, 0x245, + 0x25a, 0x26f, 0x285, 0x29b, 0x2b1, 0x2c8, 0x2df, 0x2f7, 0x30f, 0x328, + 0x341, 0x35a, 0x374, 0x38e, 0x3a9, 0x3c4, 0x3df, 0x3fb, 0x418, 0x434, + 0x452, 0x46f, 0x48d, 0x4ac, 0x4cb, 0x4ea, 0x50a, 0x52a, 0x54b, 0x56c, + 0x58d, 0x5af, 0x5d2, 0x5f5, 0x618, 0x63c, 0x660, 0x685, 0x6aa, 0x6cf, + 0x6f5, 0x71c, 0x743, 0x76a, 0x792, 0x7ba, 0x7e3, 0x80c, 0x836, 0x860, + 0x88a, 0x8b5, 0x8e1, 0x90d, 0x939, 0x966, 0x993, 0x9c1, 0x9f0, 0xa1e, + 0xa4e, 0xa7d, 0xaad, 0xade, 0xb0f, 0xb41, 0xb73, 0xba5, 0xbd8, 0xc0c, + 0xc40, 0xc74, 0xca9, 0xcde, 0xd14, 0xd4a, 0xd81, 0xdb8, 0xdf0, 0xe29, + 0xe61, 0xe9a, 0xed4, 0xf0e, 0xf49, 0xf84, 0xfc0, 0xffc, 0x1039, 0x1076, + 0x10b4, 0x10f2, 0x1130, 0x116f, 0x11af, 0x11ef, 0x1230, 0x1271, 0x12b2, + 0x12f4, 0x1337, 0x137a, 0x13be, 0x1402, 0x1446, 0x148b, 0x14d1, 0x1517, + 0x155e, 0x15a5, 0x15ec, 0x1635, 0x167d, 0x16c6, 0x1710, 0x175a, 0x17a5, + 0x17f0, 0x183c, 0x1888, 0x18d5, 0x1922, 0x196f, 0x19be, 0x1a0c, 0x1a5c, + 0x1aab, 0x1afc, 0x1b4d, 0x1b9e, 0x1bf0, 0x1c42, 0x1c95, 0x1ce8, 0x1d3c, + 0x1d91, 0x1de6, 0x1e3b, 0x1e91, 0x1ee8, 0x1f3f, 0x1f96, 0x1fef, 0x2047, + 0x20a0, 0x20fa, 0x2154, 0x21af, 0x220a, 0x2266, 0x22c2, 0x231f, 0x237d, + 0x23db, 0x2439, 0x2498, 0x24f8, 0x2558, 0x25b8, 0x2619, 0x267b, 0x26dd, + 0x2740, 0x27a3, 0x2807, 0x286b, 0x28d0, 0x2936, 0x299c, 0x2a02, 0x2a69, + 0x2ad1, 0x2b39, 0x2ba1, 0x2c0b, 0x2c74, 0x2cdf, 0x2d49, 0x2db5, 0x2e21, + 0x2e8d, 0x2efa, 0x2f68, 0x2fd6, 0x3044, 0x30b4, 0x3123, 0x3194, 0x3205, + 0x3276, 0x32e8, 0x335a, 0x33cd, 0x3441, 0x34b5, 0x352a, 0x359f, 0x3615, + 0x368b, 0x3702, 0x377a, 0x37f2, 0x386a, 0x38e3, 0x395d, 0x39d7, 0x3a52, + 0x3acd, 0x3b49, 0x3bc6, 0x3c43, 0x3cc0, 0x3d3f, 0x3dbd, 0x3e3d, 0x3ebc, + 0x3f3d, 0x3fbe, 0x0, 0x7, 0xe, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x40, + 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x80, 0x87, 0x8e, 0x95, + 0x9c, 0xa3, 0xaa, 0xb1, 0xb9, 0xc0, 0xc7, 0xce, 0xd5, 0xdc, 0xe3, 0xea, + 0xf1, 0xf9, 0x100, 0x107, 0x10e, 0x115, 0x11c, 0x123, 0x12a, 0x131, 0x138, + 0x13f, 0x147, 0x14e, 0x156, 0x15e, 0x166, 0x16e, 0x176, 0x17e, 0x186, + 0x18f, 0x197, 0x1a0, 0x1a8, 0x1b1, 0x1ba, 0x1c3, 0x1cc, 0x1d5, 0x1de, + 0x1e7, 0x1f1, 0x1fa, 0x204, 0x20e, 0x217, 0x221, 0x22b, 0x236, 0x240, + 0x24a, 0x255, 0x25f, 0x26a, 0x274, 0x27f, 0x28a, 0x295, 0x2a0, 0x2ac, + 0x2b7, 0x2c2, 0x2ce, 0x2da, 0x2e5, 0x2f1, 0x2fd, 0x309, 0x315, 0x322, + 0x32e, 0x33b, 0x347, 0x354, 0x361, 0x36d, 0x37a, 0x388, 0x395, 0x3a2, + 0x3b0, 0x3bd, 0x3cb, 0x3d8, 0x3e6, 0x3f4, 0x402, 0x411, 0x41f, 0x42d, + 0x43c, 0x44a, 0x459, 0x468, 0x477, 0x486, 0x495, 0x4a4, 0x4b3, 0x4c3, + 0x4d3, 0x4e2, 0x4f2, 0x502, 0x512, 0x522, 0x532, 0x543, 0x553, 0x564, + 0x574, 0x585, 0x596, 0x5a7, 0x5b8, 0x5c9, 0x5db, 0x5ec, 0x5fe, 0x60f, + 0x621, 0x633, 0x645, 0x657, 0x669, 0x67b, 0x68e, 0x6a0, 0x6b3, 0x6c6, + 0x6d9, 0x6ec, 0x6ff, 0x712, 0x726, 0x739, 0x74d, 0x760, 0x774, 0x788, + 0x79c, 0x7b0, 0x7c4, 0x7d9, 0x7ed, 0x802, 0x816, 0x82b, 0x840, 0x855, + 0x86a, 0x880, 0x895, 0x8ab, 0x8c0, 0x8d6, 0x8ec, 0x902, 0x918, 0x92e, + 0x944, 0x95b, 0x971, 0x988, 0x99f, 0x9b6, 0x9cd, 0x9e4, 0x9fb, 0xa13, + 0xa2a, 0xa42, 0xa59, 0xa71, 0xa89, 0xaa1, 0xab9, 0xad2, 0xaea, 0xb03, + 0xb1b, 0xb34, 0xb4d, 0xb66, 0xb7f, 0xb98, 0xbb2, 0xbcb, 0xbe5, 0xbff, + 0xc19, 0xc32, 0xc4d, 0xc67, 0xc81, 0xc9c, 0xcb6, 0xcd1, 0xcec, 0xd07, + 0xd22, 0xd3d, 0xd58, 0xd73, 0xd8f, 0xdab, 0xdc6, 0xde2, 0xdfe, 0xe1a, + 0xe37, 0xe53, 0xe70, 0xe8c, 0xea9, 0xec6, 0xee3, 0xf00, 0xf1d, 0xf3a, + 0xf58, 0xf75, 0xf93, 0xfb1, 0xfcf, 0xfed, 0x100b, 0x102a, 0x1048, 0x1067, + 0x1085, 0x10a4, 0x10c3, 0x10e2, 0x1101, 0x1121, 0x1140, 0x1160, 0x117f, + 0x119f, 0x11bf, 0x11df, 0x11ff, 0x121f, 0x1240, 0x1260, 0x1281, 0x12a2, + 0x12c3, 0x12e4, 0x1305, 0x1326, 0x1348, 0x1369, 0x138b, 0x13ad, 0x13cf, + 0x13f1, 0x1413, 0x1435, 0x1458, 0x147a, 0x149d, 0x14c0, 0x14e3, 0x1506, + 0x1529, 0x154c, 0x156f, 0x1593, 0x15b7, 0x15db, 0x15fe, 0x1623, 0x1647, + 0x166b, 0x168f, 0x16b4, 0x16d9, 0x16fe, 0x1722, 0x1748, 0x176d, 0x1792, + 0x17b8, 0x17dd, 0x1803, 0x1829, 0x184f, 0x1875, 0x189b, 0x18c1, 0x18e8, + 0x190e, 0x1935, 0x195c, 0x1983, 0x19aa, 0x19d1, 0x19f9, 0x1a20, 0x1a48, + 0x1a70, 0x1a97, 0x1ac0, 0x1ae8, 0x1b10, 0x1b38, 0x1b61, 0x1b8a, 0x1bb2, + 0x1bdb, 0x1c04, 0x1c2e, 0x1c57, 0x1c80, 0x1caa, 0x1cd4, 0x1cfd, 0x1d27, + 0x1d51, 0x1d7c, 0x1da6, 0x1dd1, 0x1dfb, 0x1e26, 0x1e51, 0x1e7c, 0x1ea7, + 0x1ed2, 0x1efe, 0x1f29, 0x1f55, 0x1f81, 0x1fac, 0x1fd9, 0x2005, 0x2031, + 0x205d, 0x208a, 0x20b7, 0x20e4, 0x2111, 0x213e, 0x216b, 0x2198, 0x21c6, + 0x21f3, 0x2221, 0x224f, 0x227d, 0x22ab, 0x22da, 0x2308, 0x2337, 0x2365, + 0x2394, 0x23c3, 0x23f2, 0x2421, 0x2451, 0x2480, 0x24b0, 0x24e0, 0x2510, + 0x2540, 0x2570, 0x25a0, 0x25d0, 0x2601, 0x2632, 0x2663, 0x2693, 0x26c5, + 0x26f6, 0x2727, 0x2759, 0x278a, 0x27bc, 0x27ee, 0x2820, 0x2852, 0x2884, + 0x28b7, 0x28e9, 0x291c, 0x294f, 0x2982, 0x29b5, 0x29e8, 0x2a1c, 0x2a4f, + 0x2a83, 0x2ab7, 0x2aeb, 0x2b1f, 0x2b53, 0x2b87, 0x2bbc, 0x2bf0, 0x2c25, + 0x2c5a, 0x2c8f, 0x2cc4, 0x2cf9, 0x2d2f, 0x2d64, 0x2d9a, 0x2dd0, 0x2e06, + 0x2e3c, 0x2e72, 0x2ea8, 0x2edf, 0x2f16, 0x2f4c, 0x2f83, 0x2fba, 0x2ff1, + 0x3029, 0x3060, 0x3098, 0x30d0, 0x3107, 0x313f, 0x3178, 0x31b0, 0x31e8, + 0x3221, 0x325a, 0x3292, 0x32cb, 0x3304, 0x333e, 0x3377, 0x33b1, 0x33ea, + 0x3424, 0x345e, 0x3498, 0x34d2, 0x350d, 0x3547, 0x3582, 0x35bc, 0x35f7, + 0x3632, 0x366e, 0x36a9, 0x36e4, 0x3720, 0x375c, 0x3798, 0x37d4, 0x3810, + 0x384c, 0x3888, 0x38c5, 0x3902, 0x393f, 0x397c, 0x39b9, 0x39f6, 0x3a33, + 0x3a71, 0x3aaf, 0x3aec, 0x3b2a, 0x3b68, 0x3ba7, 0x3be5, 0x3c24, 0x3c62, + 0x3ca1, 0x3ce0, 0x3d1f, 0x3d5e, 0x3d9e, 0x3ddd, 0x3e1d, 0x3e5d, 0x3e9c, + 0x3edc, 0x3f1d, 0x3f5d, 0x3f9e, 0x3fde, 0x6, + /* table descriptor */ + 0x61effbe9, 0xf, + /* table length */ + 0x400, + /* table data */ + 0x96, 0x8, 0x647, 0x0, 0x27, 0x1f8, 0x13cf, 0x0, 0x3, 0x13, 0x4f, 0x116, + 0x387, 0xb24, 0x236d, 0x0, 0x0, 0x1, 0x5, 0xc, 0x1b, 0x38, 0x6d, 0xcd, + 0x177, 0x2a4, 0x4b5, 0x85d, 0xed9, 0x1a77, 0x2f8b, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x2, 0x4, 0x6, 0xa, 0xf, 0x17, 0x21, 0x2f, 0x42, 0x5d, 0x80, 0xaf, 0xee, + 0x143, 0x1b3, 0x248, 0x30d, 0x414, 0x570, 0x73f, 0x9a7, 0xcdc, 0x1126, + 0x16e4, 0x1e9c, 0x2906, 0x3723, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1, 0x2, 0x2, 0x3, 0x4, 0x5, 0x7, 0x9, 0xb, 0xe, 0x11, 0x15, 0x19, + 0x1e, 0x24, 0x2b, 0x33, 0x3d, 0x48, 0x55, 0x65, 0x76, 0x8b, 0xa2, 0xbd, + 0xdd, 0x101, 0x12b, 0x15c, 0x194, 0x1d4, 0x21e, 0x274, 0x2d7, 0x348, 0x3cb, + 0x462, 0x50f, 0x5d8, 0x6be, 0x7c9, 0x8fc, 0xa5f, 0xbf8, 0xdd1, 0xff5, + 0x126e, 0x154b, 0x189c, 0x1c76, 0x20ed, 0x261f, 0x2c29, 0x3331, 0x3b65, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x3, 0x3, 0x4, 0x4, 0x5, 0x6, + 0x7, 0x7, 0x8, 0x9, 0xa, 0xc, 0xd, 0xe, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, + 0x1d, 0x1f, 0x22, 0x26, 0x29, 0x2d, 0x31, 0x36, 0x3b, 0x40, 0x45, 0x4b, + 0x52, 0x59, 0x61, 0x69, 0x71, 0x7b, 0x85, 0x90, 0x9c, 0xa9, 0xb6, 0xc5, + 0xd5, 0xe6, 0xf8, 0x10b, 0x120, 0x137, 0x14f, 0x169, 0x185, 0x1a3, 0x1c3, + 0x1e6, 0x20b, 0x233, 0x25d, 0x28b, 0x2bd, 0x2f1, 0x32a, 0x367, 0x3a8, + 0x3ef, 0x43a, 0x48b, 0x4e2, 0x53f, 0x5a3, 0x60e, 0x682, 0x6fe, 0x783, + 0x811, 0x8ab, 0x950, 0xa01, 0xabf, 0xb8c, 0xc68, 0xd55, 0xe53, 0xf64, + 0x108a, 0x11c7, 0x131b, 0x1489, 0x1613, 0x17bc, 0x1985, 0x1b71, 0x1d84, + 0x1fbf, 0x2227, 0x24bf, 0x278b, 0x2a90, 0x2dd1, 0x3155, 0x3520, 0x3939, + 0x3da6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, + 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, + 0x6, 0x6, 0x6, 0x7, 0x7, 0x8, 0x8, 0x9, 0x9, 0xa, 0xa, 0xb, 0xb, 0xc, 0xd, + 0xd, 0xe, 0xf, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1b, 0x1c, 0x1d, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x27, 0x28, 0x2a, + 0x2c, 0x2e, 0x30, 0x32, 0x35, 0x37, 0x39, 0x3c, 0x3e, 0x41, 0x44, 0x47, + 0x4a, 0x4d, 0x50, 0x54, 0x57, 0x5b, 0x5f, 0x63, 0x67, 0x6b, 0x6f, 0x74, + 0x79, 0x7d, 0x83, 0x88, 0x8d, 0x93, 0x99, 0x9f, 0xa5, 0xac, 0xb3, 0xba, + 0xc1, 0xc9, 0xd1, 0xd9, 0xe1, 0xea, 0xf3, 0xfc, 0x106, 0x110, 0x11b, 0x126, + 0x131, 0x13d, 0x149, 0x155, 0x162, 0x170, 0x17e, 0x18c, 0x19b, 0x1ab, + 0x1bb, 0x1cc, 0x1dd, 0x1ef, 0x201, 0x215, 0x228, 0x23d, 0x252, 0x269, + 0x280, 0x297, 0x2b0, 0x2c9, 0x2e4, 0x2ff, 0x31c, 0x339, 0x357, 0x377, + 0x398, 0x3ba, 0x3dd, 0x401, 0x427, 0x44e, 0x476, 0x4a0, 0x4cb, 0x4f8, + 0x527, 0x557, 0x589, 0x5bd, 0x5f3, 0x62a, 0x664, 0x6a0, 0x6de, 0x71e, + 0x760, 0x7a5, 0x7ed, 0x837, 0x884, 0x8d3, 0x926, 0x97b, 0x9d4, 0xa2f, + 0xa8f, 0xaf1, 0xb58, 0xbc2, 0xc30, 0xca2, 0xd18, 0xd92, 0xe11, 0xe95, + 0xf1e, 0xfac, 0x103f, 0x10d7, 0x1175, 0x1219, 0x12c3, 0x1374, 0x142b, + 0x14e9, 0x15ae, 0x167b, 0x174f, 0x182b, 0x1910, 0x19fd, 0x1af3, 0x1bf2, + 0x1cfb, 0x1e0f, 0x1f2c, 0x2055, 0x2189, 0x22c9, 0x2415, 0x256d, 0x26d3, + 0x2847, 0x29c9, 0x2b5a, 0x2cfb, 0x2eac, 0x306e, 0x3241, 0x3426, 0x361f, + 0x382b, 0x3a4c, 0x3c83, 0x3ecf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, + 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, + 0x5, 0x5, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x8, 0x8, + 0x8, 0x8, 0x8, 0x9, 0x9, 0x9, 0x9, 0xa, 0xa, 0xa, 0xb, 0xb, 0xb, 0xb, 0xc, + 0xc, 0xc, 0xd, 0xd, 0xd, 0xe, 0xe, 0xf, 0xf, 0xf, 0x10, 0x10, 0x11, 0x11, + 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1c, 0x1c, 0x1d, 0x1e, 0x1e, + 0x1f, 0x20, 0x21, 0x21, 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x39, 0x3a, 0x3b, 0x3c, 0x3e, 0x3f, 0x40, 0x42, 0x43, + 0x45, 0x46, 0x48, 0x49, 0x4b, 0x4c, 0x4e, 0x4f, 0x51, 0x53, 0x54, 0x56, + 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, + 0x70, 0x73, 0x75, 0x77, 0x7a, 0x7c, 0x7f, 0x81, 0x84, 0x86, 0x89, 0x8c, + 0x8f, 0x92, 0x94, 0x97, 0x9a, 0x9d, 0xa1, 0xa4, 0xa7, 0xaa, 0xae, 0xb1, + 0xb4, 0xb8, 0xbc, 0xbf, 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7, 0xdb, 0xdf, + 0xe3, 0xe8, 0xec, 0xf1, 0xf5, 0xfa, 0xff, 0x104, 0x109, 0x10e, 0x113, + 0x118, 0x11e, 0x123, 0x129, 0x12e, 0x134, 0x13a, 0x140, 0x146, 0x14c, + 0x152, 0x159, 0x15f, 0x166, 0x16c, 0x173, 0x17a, 0x181, 0x189, 0x190, + 0x197, 0x19f, 0x1a7, 0x1af, 0x1b7, 0x1bf, 0x1c7, 0x1d0, 0x1d8, 0x1e1, + 0x1ea, 0x1f3, 0x1fd, 0x206, 0x210, 0x219, 0x223, 0x22e, 0x238, 0x242, + 0x24d, 0x258, 0x263, 0x26e, 0x27a, 0x285, 0x291, 0x29d, 0x2aa, 0x2b6, + 0x2c3, 0x2d0, 0x2dd, 0x2eb, 0x2f8, 0x306, 0x314, 0x323, 0x332, 0x341, + 0x350, 0x35f, 0x36f, 0x37f, 0x38f, 0x3a0, 0x3b1, 0x3c2, 0x3d4, 0x3e6, + 0x3f8, 0x40a, 0x41d, 0x430, 0x444, 0x458, 0x46c, 0x480, 0x495, 0x4ab, + 0x4c0, 0x4d6, 0x4ed, 0x504, 0x51b, 0x533, 0x54b, 0x564, 0x57d, 0x596, + 0x5b0, 0x5ca, 0x5e5, 0x600, 0x61c, 0x639, 0x655, 0x673, 0x691, 0x6af, + 0x6ce, 0x6ee, 0x70e, 0x72e, 0x750, 0x771, 0x794, 0x7b7, 0x7db, 0x7ff, + 0x824, 0x84a, 0x870, 0x897, 0x8bf, 0x8e7, 0x911, 0x93b, 0x965, 0x991, + 0x9bd, 0x9ea, 0xa18, 0xa47, 0xa76, 0xaa7, 0xad8, 0xb0a, 0xb3e, 0xb72, + 0xba7, 0xbdd, 0xc14, 0xc4c, 0xc85, 0xcbf, 0xcfa, 0xd36, 0xd73, 0xdb2, + 0xdf1, 0xe32, 0xe74, 0xeb7, 0xefb, 0xf41, 0xf88, 0xfd0, 0x1019, 0x1064, + 0x10b1, 0x10fe, 0x114d, 0x119e, 0x11f0, 0x1243, 0x1298, 0x12ef, 0x1347, + 0x13a1, 0x13fd, 0x145a, 0x14b9, 0x151a, 0x157c, 0x15e1, 0x1647, 0x16af, + 0x1719, 0x1785, 0x17f3, 0x1863, 0x18d6, 0x194a, 0x19c1, 0x1a39, 0x1ab4, + 0x1b32, 0x1bb1, 0x1c34, 0x1cb8, 0x1d3f, 0x1dc9, 0x1e55, 0x1ee4, 0x1f75, + 0x200a, 0x20a1, 0x213b, 0x21d8, 0x2277, 0x231a, 0x23c0, 0x2469, 0x2516, + 0x25c6, 0x2679, 0x272f, 0x27e9, 0x28a6, 0x2967, 0x2a2c, 0x2af5, 0x2bc1, + 0x2c91, 0x2d66, 0x2e3e, 0x2f1b, 0x2ffc, 0x30e1, 0x31ca, 0x32b9, 0x33ab, + 0x34a3, 0x359f, 0x36a0, 0x37a6, 0x38b2, 0x39c2, 0x3ad8, 0x3bf3, 0x3d14, + 0x3e3a, 0x3f66, 0x6, + /* table descriptor */ + 0xf0effbe9, 0xf, + /* table length */ + 0x400, + /* table data */ + 0xdab, 0x33c, 0x216a, 0xe8, 0x766, 0x1646, 0x2f44, 0x52, 0x1dc, 0x512, + 0xa41, 0x11aa, 0x1b84, 0x27fd, 0x3743, 0x26, 0x92, 0x155, 0x27e, 0x418, + 0x62b, 0x8c2, 0xbe4, 0xf97, 0x13e4, 0x18d0, 0x1e61, 0x249e, 0x2b8a, 0x332c, + 0x3b89, 0x12, 0x3a, 0x70, 0xba, 0x11c, 0x196, 0x22a, 0x2da, 0x3a6, 0x491, + 0x59a, 0x6c4, 0x810, 0x97d, 0xb0e, 0xcc3, 0xe9c, 0x109c, 0x12c2, 0x1510, + 0x1786, 0x1a25, 0x1ced, 0x1fe0, 0x22fe, 0x2648, 0x29be, 0x2d61, 0x3132, + 0x3532, 0x3960, 0x3dbe, 0x8, 0x1c, 0x30, 0x46, 0x60, 0x80, 0xa5, 0xd0, + 0x101, 0x138, 0x175, 0x1b8, 0x202, 0x253, 0x2ab, 0x30a, 0x370, 0x3de, + 0x453, 0x4d0, 0x555, 0x5e2, 0x677, 0x714, 0x7ba, 0x868, 0x91e, 0x9de, + 0xaa6, 0xb77, 0xc52, 0xd35, 0xe22, 0xf19, 0x1018, 0x1122, 0x1235, 0x1352, + 0x1479, 0x15aa, 0x16e4, 0x182a, 0x1979, 0x1ad3, 0x1c37, 0x1da6, 0x1f1f, + 0x20a4, 0x2233, 0x23cc, 0x2571, 0x2721, 0x28dc, 0x2aa3, 0x2c74, 0x2e51, + 0x303a, 0x322e, 0x342e, 0x3639, 0x3850, 0x3a73, 0x3ca2, 0x3edd, 0x3, 0xd, + 0x17, 0x21, 0x2b, 0x35, 0x40, 0x4c, 0x59, 0x68, 0x78, 0x89, 0x9c, 0xb0, + 0xc5, 0xdc, 0xf4, 0x10e, 0x12a, 0x146, 0x165, 0x185, 0x1a7, 0x1ca, 0x1ef, + 0x216, 0x23e, 0x269, 0x294, 0x2c2, 0x2f2, 0x323, 0x356, 0x38b, 0x3c2, + 0x3fb, 0x435, 0x472, 0x4b0, 0x4f1, 0x533, 0x577, 0x5be, 0x606, 0x651, + 0x69d, 0x6ec, 0x73d, 0x78f, 0x7e4, 0x83b, 0x895, 0x8f0, 0x94d, 0x9ad, + 0xa0f, 0xa73, 0xada, 0xb42, 0xbad, 0xc1a, 0xc8a, 0xcfc, 0xd70, 0xde6, + 0xe5f, 0xeda, 0xf58, 0xfd8, 0x105a, 0x10df, 0x1166, 0x11ef, 0x127b, 0x130a, + 0x139b, 0x142e, 0x14c4, 0x155c, 0x15f7, 0x1695, 0x1735, 0x17d7, 0x187d, + 0x1924, 0x19cf, 0x1a7b, 0x1b2b, 0x1bdd, 0x1c92, 0x1d49, 0x1e03, 0x1ec0, + 0x1f7f, 0x2042, 0x2106, 0x21ce, 0x2298, 0x2365, 0x2435, 0x2507, 0x25dc, + 0x26b4, 0x278f, 0x286d, 0x294d, 0x2a30, 0x2b16, 0x2bff, 0x2ceb, 0x2dd9, + 0x2eca, 0x2fbf, 0x30b6, 0x31b0, 0x32ad, 0x33ac, 0x34af, 0x35b5, 0x36bd, + 0x37c9, 0x38d8, 0x39e9, 0x3afd, 0x3c15, 0x3d2f, 0x3e4d, 0x3f6d, 0x1, 0x6, + 0xb, 0x10, 0x15, 0x1a, 0x1e, 0x23, 0x28, 0x2d, 0x32, 0x37, 0x3d, 0x43, + 0x49, 0x4f, 0x56, 0x5d, 0x64, 0x6c, 0x74, 0x7c, 0x85, 0x8e, 0x97, 0xa0, + 0xaa, 0xb5, 0xc0, 0xcb, 0xd6, 0xe2, 0xee, 0xfb, 0x108, 0x115, 0x123, 0x131, + 0x13f, 0x14e, 0x15d, 0x16d, 0x17d, 0x18d, 0x19e, 0x1af, 0x1c1, 0x1d3, + 0x1e6, 0x1f9, 0x20c, 0x220, 0x234, 0x249, 0x25e, 0x273, 0x289, 0x2a0, + 0x2b7, 0x2ce, 0x2e6, 0x2fe, 0x316, 0x32f, 0x349, 0x363, 0x37e, 0x398, + 0x3b4, 0x3d0, 0x3ec, 0x409, 0x426, 0x444, 0x462, 0x481, 0x4a0, 0x4c0, + 0x4e0, 0x501, 0x522, 0x544, 0x566, 0x589, 0x5ac, 0x5d0, 0x5f4, 0x619, + 0x63e, 0x664, 0x68a, 0x6b1, 0x6d8, 0x700, 0x728, 0x751, 0x77a, 0x7a4, + 0x7cf, 0x7fa, 0x825, 0x851, 0x87e, 0x8ab, 0x8d9, 0x907, 0x936, 0x965, + 0x995, 0x9c5, 0x9f6, 0xa28, 0xa5a, 0xa8d, 0xac0, 0xaf4, 0xb28, 0xb5d, + 0xb92, 0xbc8, 0xbff, 0xc36, 0xc6e, 0xca6, 0xcdf, 0xd19, 0xd53, 0xd8d, + 0xdc8, 0xe04, 0xe41, 0xe7e, 0xebb, 0xef9, 0xf38, 0xf77, 0xfb7, 0xff8, + 0x1039, 0x107b, 0x10bd, 0x1100, 0x1144, 0x1188, 0x11cd, 0x1212, 0x1258, + 0x129f, 0x12e6, 0x132e, 0x1376, 0x13bf, 0x1409, 0x1453, 0x149e, 0x14ea, + 0x1536, 0x1583, 0x15d0, 0x161e, 0x166d, 0x16bd, 0x170d, 0x175d, 0x17af, + 0x1800, 0x1853, 0x18a6, 0x18fa, 0x194f, 0x19a4, 0x19fa, 0x1a50, 0x1aa7, + 0x1aff, 0x1b57, 0x1bb0, 0x1c0a, 0x1c64, 0x1cbf, 0x1d1b, 0x1d78, 0x1dd5, + 0x1e32, 0x1e91, 0x1ef0, 0x1f4f, 0x1fb0, 0x2011, 0x2072, 0x20d5, 0x2138, + 0x219c, 0x2200, 0x2265, 0x22cb, 0x2331, 0x2399, 0x2401, 0x2469, 0x24d2, + 0x253c, 0x25a7, 0x2612, 0x267e, 0x26eb, 0x2758, 0x27c6, 0x2835, 0x28a4, + 0x2915, 0x2985, 0x29f7, 0x2a69, 0x2adc, 0x2b50, 0x2bc4, 0x2c3a, 0x2caf, + 0x2d26, 0x2d9d, 0x2e15, 0x2e8e, 0x2f07, 0x2f81, 0x2ffc, 0x3078, 0x30f4, + 0x3171, 0x31ef, 0x326d, 0x32ec, 0x336c, 0x33ed, 0x346e, 0x34f0, 0x3573, + 0x35f7, 0x367b, 0x3700, 0x3786, 0x380c, 0x3894, 0x391c, 0x39a4, 0x3a2e, + 0x3ab8, 0x3b43, 0x3bcf, 0x3c5b, 0x3ce8, 0x3d76, 0x3e05, 0x3e95, 0x3f25, + 0x3fb6, 0x0, 0x2, 0x4, 0x7, 0x9, 0xc, 0xe, 0x11, 0x13, 0x16, 0x18, 0x1b, + 0x1d, 0x20, 0x22, 0x25, 0x27, 0x2a, 0x2c, 0x2f, 0x31, 0x34, 0x36, 0x39, + 0x3c, 0x3e, 0x41, 0x44, 0x47, 0x4a, 0x4d, 0x51, 0x54, 0x57, 0x5b, 0x5f, + 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76, 0x7a, 0x7e, 0x82, 0x87, 0x8b, 0x90, + 0x94, 0x99, 0x9e, 0xa3, 0xa8, 0xad, 0xb2, 0xb7, 0xbd, 0xc2, 0xc8, 0xcd, + 0xd3, 0xd9, 0xdf, 0xe5, 0xeb, 0xf1, 0xf7, 0xfe, 0x104, 0x10b, 0x111, 0x118, + 0x11f, 0x126, 0x12d, 0x134, 0x13b, 0x143, 0x14a, 0x152, 0x159, 0x161, + 0x169, 0x171, 0x179, 0x181, 0x189, 0x192, 0x19a, 0x1a2, 0x1ab, 0x1b4, + 0x1bd, 0x1c6, 0x1cf, 0x1d8, 0x1e1, 0x1ea, 0x1f4, 0x1fe, 0x207, 0x211, + 0x21b, 0x225, 0x22f, 0x239, 0x244, 0x24e, 0x258, 0x263, 0x26e, 0x279, + 0x284, 0x28f, 0x29a, 0x2a5, 0x2b1, 0x2bc, 0x2c8, 0x2d4, 0x2e0, 0x2ec, + 0x2f8, 0x304, 0x310, 0x31d, 0x329, 0x336, 0x343, 0x350, 0x35d, 0x36a, + 0x377, 0x384, 0x392, 0x39f, 0x3ad, 0x3bb, 0x3c9, 0x3d7, 0x3e5, 0x3f3, + 0x402, 0x410, 0x41f, 0x42e, 0x43d, 0x44c, 0x45b, 0x46a, 0x479, 0x489, + 0x499, 0x4a8, 0x4b8, 0x4c8, 0x4d8, 0x4e8, 0x4f9, 0x509, 0x51a, 0x52b, + 0x53b, 0x54c, 0x55e, 0x56f, 0x580, 0x592, 0x5a3, 0x5b5, 0x5c7, 0x5d9, + 0x5eb, 0x5fd, 0x60f, 0x622, 0x635, 0x647, 0x65a, 0x66d, 0x680, 0x694, + 0x6a7, 0x6bb, 0x6ce, 0x6e2, 0x6f6, 0x70a, 0x71e, 0x732, 0x747, 0x75b, + 0x770, 0x785, 0x79a, 0x7af, 0x7c4, 0x7da, 0x7ef, 0x805, 0x81a, 0x830, + 0x846, 0x85d, 0x873, 0x889, 0x8a0, 0x8b7, 0x8cd, 0x8e4, 0x8fb, 0x913, + 0x92a, 0x942, 0x959, 0x971, 0x989, 0x9a1, 0x9b9, 0x9d2, 0x9ea, 0xa03, + 0xa1c, 0xa34, 0xa4d, 0xa67, 0xa80, 0xa99, 0xab3, 0xacd, 0xae7, 0xb01, + 0xb1b, 0xb35, 0xb50, 0xb6a, 0xb85, 0xba0, 0xbbb, 0xbd6, 0xbf1, 0xc0d, + 0xc28, 0xc44, 0xc60, 0xc7c, 0xc98, 0xcb4, 0xcd1, 0xced, 0xd0a, 0xd27, + 0xd44, 0xd61, 0xd7e, 0xd9c, 0xdba, 0xdd7, 0xdf5, 0xe13, 0xe31, 0xe50, + 0xe6e, 0xe8d, 0xeac, 0xecb, 0xeea, 0xf09, 0xf28, 0xf48, 0xf68, 0xf87, + 0xfa7, 0xfc7, 0xfe8, 0x1008, 0x1029, 0x1049, 0x106a, 0x108b, 0x10ad, + 0x10ce, 0x10ef, 0x1111, 0x1133, 0x1155, 0x1177, 0x1199, 0x11bb, 0x11de, + 0x1201, 0x1223, 0x1246, 0x126a, 0x128d, 0x12b0, 0x12d4, 0x12f8, 0x131c, + 0x1340, 0x1364, 0x1388, 0x13ad, 0x13d2, 0x13f6, 0x141c, 0x1441, 0x1466, + 0x148b, 0x14b1, 0x14d7, 0x14fd, 0x1523, 0x1549, 0x1570, 0x1596, 0x15bd, + 0x15e4, 0x160b, 0x1632, 0x1659, 0x1681, 0x16a9, 0x16d1, 0x16f9, 0x1721, + 0x1749, 0x1771, 0x179a, 0x17c3, 0x17ec, 0x1815, 0x183e, 0x1868, 0x1891, + 0x18bb, 0x18e5, 0x190f, 0x1939, 0x1964, 0x198e, 0x19b9, 0x19e4, 0x1a0f, + 0x1a3a, 0x1a66, 0x1a91, 0x1abd, 0x1ae9, 0x1b15, 0x1b41, 0x1b6d, 0x1b9a, + 0x1bc7, 0x1bf4, 0x1c21, 0x1c4e, 0x1c7b, 0x1ca9, 0x1cd6, 0x1d04, 0x1d32, + 0x1d60, 0x1d8f, 0x1dbd, 0x1dec, 0x1e1b, 0x1e4a, 0x1e79, 0x1ea8, 0x1ed8, + 0x1f07, 0x1f37, 0x1f67, 0x1f98, 0x1fc8, 0x1ff8, 0x2029, 0x205a, 0x208b, + 0x20bc, 0x20ee, 0x211f, 0x2151, 0x2183, 0x21b5, 0x21e7, 0x2219, 0x224c, + 0x227f, 0x22b2, 0x22e5, 0x2318, 0x234b, 0x237f, 0x23b3, 0x23e6, 0x241b, + 0x244f, 0x2483, 0x24b8, 0x24ed, 0x2522, 0x2557, 0x258c, 0x25c2, 0x25f7, + 0x262d, 0x2663, 0x2699, 0x26cf, 0x2706, 0x273d, 0x2774, 0x27ab, 0x27e2, + 0x2819, 0x2851, 0x2888, 0x28c0, 0x28f8, 0x2931, 0x2969, 0x29a2, 0x29db, + 0x2a14, 0x2a4d, 0x2a86, 0x2ac0, 0x2af9, 0x2b33, 0x2b6d, 0x2ba7, 0x2be2, + 0x2c1c, 0x2c57, 0x2c92, 0x2ccd, 0x2d08, 0x2d44, 0x2d7f, 0x2dbb, 0x2df7, + 0x2e33, 0x2e70, 0x2eac, 0x2ee9, 0x2f26, 0x2f63, 0x2fa0, 0x2fdd, 0x301b, + 0x3059, 0x3097, 0x30d5, 0x3113, 0x3152, 0x3190, 0x31cf, 0x320e, 0x324e, + 0x328d, 0x32cd, 0x330c, 0x334c, 0x338c, 0x33cd, 0x340d, 0x344e, 0x348f, + 0x34d0, 0x3511, 0x3552, 0x3594, 0x35d6, 0x3618, 0x365a, 0x369c, 0x36df, + 0x3721, 0x3764, 0x37a7, 0x37eb, 0x382e, 0x3872, 0x38b6, 0x38fa, 0x393e, + 0x3982, 0x39c7, 0x3a0b, 0x3a50, 0x3a95, 0x3adb, 0x3b20, 0x3b66, 0x3bac, + 0x3bf2, 0x3c38, 0x3c7e, 0x3cc5, 0x3d0c, 0x3d53, 0x3d9a, 0x3de1, 0x3e29, + 0x3e71, 0x3eb9, 0x3f01, 0x3f49, 0x3f91, 0x3fda, 0x6, + /* table descriptor */ + 0x7baffbea, 0x6, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x2646, 0x4b23, 0xe98, 0xffffea68, 0xffffd59b, 0x4000, 0x4000, + 0xffffca6a, 0xfffff599, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + 0xf, 0x0, 0x200, 0x200, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0x7baffbea, 0x8, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4000, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, 0xe, 0x0, 0x200, 0x200, 0x0, 0x0, 0x0, + 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbaffbea, 0x0, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x21a1, 0x56c9, 0x798, 0xffffee22, 0xffffd1e1, 0x4000, 0x4000, + 0xffffc527, 0xfffffadc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + 0xf, 0x0, 0x200, 0x200, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0x7baffbea, 0x1, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x1b37, 0x5b8c, 0x93e, 0xfffff157, 0xffffceac, 0x4000, 0x4000, + 0xffffc5e0, 0xfffffa23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + 0xf, 0x0, 0x200, 0x200, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfb6ffbea, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x8000, 0x4000, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbefe8e1, 0xf, + /* table length */ + 0x402, + /* table data */ + 0x3, 0x0, 0xea8, 0x12aa, 0x1500, 0x16ab, 0x1815, 0x1900, 0x1a0b, 0x1aab, + 0x1b60, 0x1c15, 0x1c85, 0x1d00, 0x1d86, 0x1e0b, 0x1e58, 0x1eab, 0x1f03, + 0x1f60, 0x1fc3, 0x2015, 0x204c, 0x2085, 0x20c2, 0x2100, 0x2142, 0x2186, + 0x21cc, 0x220b, 0x2231, 0x2258, 0x2281, 0x22ab, 0x22d6, 0x2303, 0x2331, + 0x2360, 0x2391, 0x23c3, 0x23f6, 0x2415, 0x2430, 0x244c, 0x2468, 0x2485, + 0x24a3, 0x24c2, 0x24e1, 0x2500, 0x2521, 0x2542, 0x2563, 0x2586, 0x25a9, + 0x25cc, 0x25f1, 0x260b, 0x261e, 0x2631, 0x2644, 0x2658, 0x266c, 0x2681, + 0x2696, 0x26ab, 0x26c0, 0x26d6, 0x26ec, 0x2703, 0x271a, 0x2731, 0x2748, + 0x2760, 0x2779, 0x2791, 0x27aa, 0x27c3, 0x27dd, 0x27f6, 0x2808, 0x2815, + 0x2823, 0x2830, 0x283e, 0x284c, 0x285a, 0x2868, 0x2877, 0x2885, 0x2894, + 0x28a3, 0x28b2, 0x28c2, 0x28d1, 0x28e1, 0x28f0, 0x2900, 0x2910, 0x2921, + 0x2931, 0x2942, 0x2952, 0x2963, 0x2974, 0x2986, 0x2997, 0x29a9, 0x29bb, + 0x29cc, 0x29df, 0x29f1, 0x2a01, 0x2a0b, 0x2a14, 0x2a1e, 0x2a27, 0x2a31, + 0x2a3a, 0x2a44, 0x2a4e, 0x2a58, 0x2a62, 0x2a6c, 0x2a76, 0x2a81, 0x2a8b, + 0x2a96, 0x2aa0, 0x2aab, 0x2ab6, 0x2ac0, 0x2acb, 0x2ad6, 0x2ae1, 0x2aec, + 0x2af8, 0x2b03, 0x2b0e, 0x2b1a, 0x2b25, 0x2b31, 0x2b3d, 0x2b48, 0x2b54, + 0x2b60, 0x2b6c, 0x2b79, 0x2b85, 0x2b91, 0x2b9d, 0x2baa, 0x2bb6, 0x2bc3, + 0x2bd0, 0x2bdd, 0x2bea, 0x2bf6, 0x2c02, 0x2c08, 0x2c0f, 0x2c15, 0x2c1c, + 0x2c23, 0x2c2a, 0x2c30, 0x2c37, 0x2c3e, 0x2c45, 0x2c4c, 0x2c53, 0x2c5a, + 0x2c61, 0x2c68, 0x2c70, 0x2c77, 0x2c7e, 0x2c85, 0x2c8d, 0x2c94, 0x2c9c, + 0x2ca3, 0x2cab, 0x2cb2, 0x2cba, 0x2cc2, 0x2cc9, 0x2cd1, 0x2cd9, 0x2ce1, + 0x2ce8, 0x2cf0, 0x2cf8, 0x2d00, 0x2d08, 0x2d10, 0x2d18, 0x2d21, 0x2d29, + 0x2d31, 0x2d39, 0x2d42, 0x2d4a, 0x2d52, 0x2d5b, 0x2d63, 0x2d6c, 0x2d74, + 0x2d7d, 0x2d86, 0x2d8e, 0x2d97, 0x2da0, 0x2da9, 0x2db2, 0x2dbb, 0x2dc3, + 0x2dcc, 0x2dd5, 0x2ddf, 0x2de8, 0x2df1, 0x2dfa, 0x2e01, 0x2e06, 0x2e0b, + 0x2e0f, 0x2e14, 0x2e19, 0x2e1e, 0x2e22, 0x2e27, 0x2e2c, 0x2e31, 0x2e36, + 0x2e3a, 0x2e3f, 0x2e44, 0x2e49, 0x2e4e, 0x2e53, 0x2e58, 0x2e5d, 0x2e62, + 0x2e67, 0x2e6c, 0x2e71, 0x2e76, 0x2e7c, 0x2e81, 0x2e86, 0x2e8b, 0x2e90, + 0x2e96, 0x2e9b, 0x2ea0, 0x2ea6, 0x2eab, 0x2eb0, 0x2eb6, 0x2ebb, 0x2ec0, + 0x2ec6, 0x2ecb, 0x2ed1, 0x2ed6, 0x2edc, 0x2ee1, 0x2ee7, 0x2eec, 0x2ef2, + 0x2ef8, 0x2efd, 0x2f03, 0x2f09, 0x2f0e, 0x2f14, 0x2f1a, 0x2f20, 0x2f25, + 0x2f2b, 0x2f31, 0x2f37, 0x2f3d, 0x2f43, 0x2f48, 0x2f4e, 0x2f54, 0x2f5a, + 0x2f60, 0x2f66, 0x2f6c, 0x2f72, 0x2f79, 0x2f7f, 0x2f85, 0x2f8b, 0x2f91, + 0x2f97, 0x2f9d, 0x2fa4, 0x2faa, 0x2fb0, 0x2fb6, 0x2fbd, 0x2fc3, 0x2fc9, + 0x2fd0, 0x2fd6, 0x2fdd, 0x2fe3, 0x2fea, 0x2ff0, 0x2ff6, 0x2ffd, 0x3002, + 0x3005, 0x3008, 0x300b, 0x300f, 0x3012, 0x3015, 0x3019, 0x301c, 0x301f, + 0x3023, 0x3026, 0x302a, 0x302d, 0x3030, 0x3034, 0x3037, 0x303b, 0x303e, + 0x3042, 0x3045, 0x3049, 0x304c, 0x3050, 0x3053, 0x3057, 0x305a, 0x305e, + 0x3061, 0x3065, 0x3068, 0x306c, 0x3070, 0x3073, 0x3077, 0x307b, 0x307e, + 0x3082, 0x3085, 0x3089, 0x308d, 0x3091, 0x3094, 0x3098, 0x309c, 0x309f, + 0x30a3, 0x30a7, 0x30ab, 0x30ae, 0x30b2, 0x30b6, 0x30ba, 0x30be, 0x30c2, + 0x30c5, 0x30c9, 0x30cd, 0x30d1, 0x30d5, 0x30d9, 0x30dd, 0x30e1, 0x30e4, + 0x30e8, 0x30ec, 0x30f0, 0x30f4, 0x30f8, 0x30fc, 0x3100, 0x3104, 0x3108, + 0x310c, 0x3110, 0x3114, 0x3118, 0x311d, 0x3121, 0x3125, 0x3129, 0x312d, + 0x3131, 0x3135, 0x3139, 0x313d, 0x3142, 0x3146, 0x314a, 0x314e, 0x3152, + 0x3157, 0x315b, 0x315f, 0x3163, 0x3168, 0x316c, 0x3170, 0x3174, 0x3179, + 0x317d, 0x3181, 0x3186, 0x318a, 0x318e, 0x3193, 0x3197, 0x319c, 0x31a0, + 0x31a4, 0x31a9, 0x31ad, 0x31b2, 0x31b6, 0x31bb, 0x31bf, 0x31c3, 0x31c8, + 0x31cc, 0x31d1, 0x31d5, 0x31da, 0x31df, 0x31e3, 0x31e8, 0x31ec, 0x31f1, + 0x31f5, 0x31fa, 0x31ff, 0x3201, 0x3204, 0x3206, 0x3208, 0x320b, 0x320d, + 0x320f, 0x3212, 0x3214, 0x3216, 0x3219, 0x321b, 0x321e, 0x3220, 0x3222, + 0x3225, 0x3227, 0x3229, 0x322c, 0x322e, 0x3231, 0x3233, 0x3236, 0x3238, + 0x323a, 0x323d, 0x323f, 0x3242, 0x3244, 0x3247, 0x3249, 0x324c, 0x324e, + 0x3251, 0x3253, 0x3256, 0x3258, 0x325b, 0x325d, 0x3260, 0x3262, 0x3265, + 0x3267, 0x326a, 0x326c, 0x326f, 0x3271, 0x3274, 0x3276, 0x3279, 0x327c, + 0x327e, 0x3281, 0x3283, 0x3286, 0x3289, 0x328b, 0x328e, 0x3290, 0x3293, + 0x3296, 0x3298, 0x329b, 0x329e, 0x32a0, 0x32a3, 0x32a6, 0x32a8, 0x32ab, + 0x32ae, 0x32b0, 0x32b3, 0x32b6, 0x32b8, 0x32bb, 0x32be, 0x32c1, 0x32c4, + 0x32c6, 0x32c9, 0x32cc, 0x32cf, 0x32d2, 0x32d5, 0x32d8, 0x32da, 0x32dd, + 0x32e0, 0x32e3, 0x32e6, 0x32e9, 0x32ec, 0x32ef, 0x32f2, 0x32f6, 0x32f9, + 0x32fc, 0x32ff, 0x3302, 0x3305, 0x3308, 0x330c, 0x330f, 0x3312, 0x3315, + 0x3318, 0x331c, 0x331f, 0x3322, 0x3326, 0x3329, 0x332c, 0x3330, 0x3333, + 0x3337, 0x333a, 0x333e, 0x3341, 0x3345, 0x3348, 0x334c, 0x334f, 0x3353, + 0x3356, 0x335a, 0x335e, 0x3361, 0x3365, 0x3369, 0x336c, 0x3370, 0x3374, + 0x3378, 0x337c, 0x337f, 0x3383, 0x3387, 0x338b, 0x338f, 0x3393, 0x3397, + 0x339b, 0x339f, 0x33a3, 0x33a7, 0x33ab, 0x33af, 0x33b3, 0x33b7, 0x33bb, + 0x33bf, 0x33c4, 0x33c8, 0x33cc, 0x33d0, 0x33d5, 0x33d9, 0x33dd, 0x33e2, + 0x33e6, 0x33eb, 0x33ef, 0x33f3, 0x33f8, 0x33fc, 0x3400, 0x3402, 0x3405, + 0x3407, 0x3409, 0x340c, 0x340e, 0x3410, 0x3413, 0x3415, 0x3418, 0x341a, + 0x341c, 0x341f, 0x3421, 0x3424, 0x3426, 0x3429, 0x342b, 0x342e, 0x3430, + 0x3433, 0x3435, 0x3438, 0x343a, 0x343d, 0x3440, 0x3442, 0x3445, 0x3448, + 0x344a, 0x344d, 0x3450, 0x3452, 0x3455, 0x3458, 0x345b, 0x345d, 0x3460, + 0x3463, 0x3466, 0x3469, 0x346b, 0x346e, 0x3471, 0x3474, 0x3477, 0x347a, + 0x347d, 0x3480, 0x3483, 0x3486, 0x3489, 0x348c, 0x348f, 0x3492, 0x3495, + 0x3498, 0x349b, 0x349e, 0x34a2, 0x34a5, 0x34a8, 0x34ab, 0x34ae, 0x34b2, + 0x34b5, 0x34b8, 0x34bb, 0x34bf, 0x34c2, 0x34c5, 0x34c9, 0x34cc, 0x34cf, + 0x34d3, 0x34d6, 0x34da, 0x34dd, 0x34e1, 0x34e4, 0x34e8, 0x34eb, 0x34ef, + 0x34f2, 0x34f6, 0x34fa, 0x34fd, 0x3501, 0x3505, 0x3508, 0x350c, 0x3510, + 0x3514, 0x3517, 0x351b, 0x351f, 0x3523, 0x3527, 0x352b, 0x352f, 0x3532, + 0x3536, 0x353a, 0x353e, 0x3542, 0x3546, 0x354b, 0x354f, 0x3553, 0x3557, + 0x355b, 0x355f, 0x3563, 0x3568, 0x356c, 0x3570, 0x3574, 0x3579, 0x357d, + 0x3581, 0x3586, 0x358a, 0x358f, 0x3593, 0x3598, 0x359c, 0x35a1, 0x35a5, + 0x35aa, 0x35ae, 0x35b3, 0x35b8, 0x35bc, 0x35c1, 0x35c6, 0x35cb, 0x35cf, + 0x35d4, 0x35d9, 0x35de, 0x35e3, 0x35e8, 0x35ed, 0x35f2, 0x35f7, 0x35fc, + 0x3600, 0x3603, 0x3605, 0x3608, 0x360a, 0x360d, 0x3610, 0x3612, 0x3615, + 0x3618, 0x361a, 0x361d, 0x3620, 0x3622, 0x3625, 0x3628, 0x362b, 0x362d, + 0x3630, 0x3633, 0x3636, 0x3639, 0x363b, 0x363e, 0x3641, 0x3644, 0x3647, + 0x364a, 0x364d, 0x3650, 0x3653, 0x3656, 0x3659, 0x365c, 0x365f, 0x3662, + 0x3665, 0x3668, 0x366b, 0x366e, 0x3672, 0x3675, 0x3678, 0x367b, 0x367e, + 0x3682, 0x3685, 0x3688, 0x368b, 0x368f, 0x3692, 0x3695, 0x3699, 0x369c, + 0x36a0, 0x36a3, 0x36a6, 0x36aa, 0x36ad, 0x36b1, 0x36b4, 0x36b8, 0x36bb, + 0x36bf, 0x36c3, 0x36c6, 0x36ca, 0x36cd, 0x36d1, 0x36d5, 0x36d9, 0x36dc, + 0x36e0, 0x36e4, 0x36e8, 0x36eb, 0x36ef, 0x36f3, 0x36f7, 0x36fb, 0x36ff, + 0x3703, 0x3707, 0x370b, 0x370f, 0x3713, 0x3717, 0x371b, 0x371f, 0x3723, + 0x3727, 0x372b, 0x372f, 0x3734, 0x3738, 0x373c, 0x3740, 0x3745, 0x3749, + 0x374d, 0x3752, 0x3756, 0x375b, 0x375f, 0x3764, 0x3768, 0x376d, 0x3771, + 0x3776, 0x377a, 0x377f, 0x3783, 0x3788, 0x378d, 0x3792, 0x3796, 0x379b, + 0x37a0, 0x37a5, 0x37aa, 0x37ae, 0x37b3, 0x37b8, 0x37bd, 0x37c2, 0x37c7, + 0x37cc, 0x37d1, 0x37d6, 0x37dc, 0x37e1, 0x37e6, 0x37eb, 0x37f0, 0x37f6, + 0x37fb, 0x3800, 0x3802, 0x3805, 0x3808, 0x380b, 0x380d, 0x3810, 0x3813, + 0x3816, 0x3818, 0x381b, 0x381e, 0x3821, 0x3824, 0x3827, 0x382a, 0x382c, + 0x382f, 0x3832, 0x3835, 0x3838, 0x383b, 0x383e, 0x3841, 0x3844, 0x3847, + 0x384a, 0x384d, 0x3851, 0x3854, 0x3857, 0x385a, 0x385d, 0x3860, 0x3864, + 0x3867, 0x386a, 0x386d, 0x3870, 0x3874, 0x3877, 0x387a, 0x387e, 0x3881, + 0x3885, 0x3888, 0x388b, 0x388f, 0x3892, 0x3896, 0x3899, 0x389d, 0x38a0, + 0x38a4, 0x38a7, 0x38ab, 0x38af, 0x38b2, 0x38b6, 0x38ba, 0x38bd, 0x38c1, + 0x38c5, 0x38c8, 0x38cc, 0x38d0, 0x38d4, 0x38d8, 0x38dc, 0x38df, 0x38e3, + 0x38e7, 0x38eb, 0x38ef, 0x38f3, 0x38f7, 0x38fb, 0x38ff, 0x3903, 0x3907, + 0x390c, 0x3910, 0x3914, 0x3918, 0x391c, 0x3920, 0x3925, 0x3929, 0x392d, + 0x3932, 0x3936, 0x393a, 0x393f, 0x3943, 0x3948, 0x394c, 0x3951, 0x3955, + 0x395a, 0x395e, 0x3963, 0x3967, 0x396c, 0x3971, 0x3975, 0x397a, 0x397f, + 0x3984, 0x3989, 0x398d, 0x3992, 0x3997, 0x399c, 0x39a1, 0x39a6, 0x39ab, + 0x39b0, 0x39b5, 0x39ba, 0x39bf, 0x39c4, 0x39c9, 0x39cf, 0x39d4, 0x39d9, + 0x39de, 0x39e4, 0x39e9, 0x39ee, 0x39f4, 0x39f9, 0x39ff, 0x3, + /* table descriptor */ + 0xfbeff0e1, 0xf, + /* table length */ + 0x402, + /* table data */ + 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x6, 0x7, 0x8, 0x9, 0xb, 0xc, 0xd, 0xe, 0x10, + 0x11, 0x12, 0x13, 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x20, 0x21, 0x22, 0x23, 0x25, 0x26, 0x27, 0x28, 0x2a, 0x2b, 0x2c, 0x2d, + 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36, 0x37, 0x39, 0x3a, 0x3c, 0x3d, + 0x3e, 0x40, 0x41, 0x43, 0x44, 0x46, 0x47, 0x49, 0x4a, 0x4c, 0x4d, 0x4f, + 0x51, 0x52, 0x54, 0x56, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x60, 0x62, 0x64, + 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, + 0x7e, 0x80, 0x82, 0x85, 0x87, 0x89, 0x8b, 0x8e, 0x90, 0x92, 0x94, 0x97, + 0x99, 0x9c, 0x9e, 0xa0, 0xa3, 0xa5, 0xa8, 0xaa, 0xad, 0xb0, 0xb2, 0xb5, + 0xb7, 0xba, 0xbd, 0xc0, 0xc2, 0xc5, 0xc8, 0xcb, 0xcd, 0xd0, 0xd3, 0xd6, + 0xd9, 0xdc, 0xdf, 0xe2, 0xe5, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf7, 0xfb, + 0xfe, 0x101, 0x104, 0x108, 0x10b, 0x10e, 0x111, 0x115, 0x118, 0x11c, 0x11f, + 0x123, 0x126, 0x12a, 0x12d, 0x131, 0x134, 0x138, 0x13b, 0x13f, 0x143, + 0x146, 0x14a, 0x14e, 0x152, 0x155, 0x159, 0x15d, 0x161, 0x165, 0x169, + 0x16d, 0x171, 0x175, 0x179, 0x17d, 0x181, 0x185, 0x189, 0x18d, 0x192, + 0x196, 0x19a, 0x19e, 0x1a2, 0x1a7, 0x1ab, 0x1af, 0x1b4, 0x1b8, 0x1bd, + 0x1c1, 0x1c6, 0x1ca, 0x1cf, 0x1d3, 0x1d8, 0x1dc, 0x1e1, 0x1e6, 0x1ea, + 0x1ef, 0x1f4, 0x1f9, 0x1fe, 0x202, 0x207, 0x20c, 0x211, 0x216, 0x21b, + 0x220, 0x225, 0x22a, 0x22f, 0x234, 0x239, 0x23e, 0x244, 0x249, 0x24e, + 0x253, 0x258, 0x25e, 0x263, 0x269, 0x26e, 0x273, 0x279, 0x27e, 0x284, + 0x289, 0x28f, 0x294, 0x29a, 0x2a0, 0x2a5, 0x2ab, 0x2b1, 0x2b7, 0x2bc, + 0x2c2, 0x2c8, 0x2ce, 0x2d4, 0x2da, 0x2e0, 0x2e6, 0x2ec, 0x2f2, 0x2f8, + 0x2fe, 0x304, 0x30a, 0x310, 0x316, 0x31d, 0x323, 0x329, 0x32f, 0x336, + 0x33c, 0x343, 0x349, 0x350, 0x356, 0x35d, 0x363, 0x36a, 0x370, 0x377, + 0x37e, 0x384, 0x38b, 0x392, 0x398, 0x39f, 0x3a6, 0x3ad, 0x3b4, 0x3bb, + 0x3c2, 0x3c9, 0x3d0, 0x3d7, 0x3de, 0x3e5, 0x3ec, 0x3f3, 0x3fb, 0x402, + 0x409, 0x410, 0x418, 0x41f, 0x426, 0x42e, 0x435, 0x43d, 0x444, 0x44c, + 0x453, 0x45b, 0x462, 0x46a, 0x472, 0x479, 0x481, 0x489, 0x491, 0x499, + 0x4a0, 0x4a8, 0x4b0, 0x4b8, 0x4c0, 0x4c8, 0x4d0, 0x4d8, 0x4e0, 0x4e8, + 0x4f1, 0x4f9, 0x501, 0x509, 0x512, 0x51a, 0x522, 0x52b, 0x533, 0x53b, + 0x544, 0x54c, 0x555, 0x55e, 0x566, 0x56f, 0x577, 0x580, 0x589, 0x592, + 0x59a, 0x5a3, 0x5ac, 0x5b5, 0x5be, 0x5c7, 0x5d0, 0x5d9, 0x5e2, 0x5eb, + 0x5f4, 0x5fd, 0x606, 0x60f, 0x619, 0x622, 0x62b, 0x635, 0x63e, 0x647, + 0x651, 0x65a, 0x664, 0x66d, 0x677, 0x680, 0x68a, 0x694, 0x69d, 0x6a7, + 0x6b1, 0x6bb, 0x6c4, 0x6ce, 0x6d8, 0x6e2, 0x6ec, 0x6f6, 0x700, 0x70a, + 0x714, 0x71e, 0x728, 0x732, 0x73d, 0x747, 0x751, 0x75b, 0x766, 0x770, + 0x77a, 0x785, 0x78f, 0x79a, 0x7a4, 0x7af, 0x7ba, 0x7c4, 0x7cf, 0x7da, + 0x7e4, 0x7ef, 0x7fa, 0x805, 0x810, 0x81a, 0x825, 0x830, 0x83b, 0x846, + 0x851, 0x85d, 0x868, 0x873, 0x87e, 0x889, 0x895, 0x8a0, 0x8ab, 0x8b7, + 0x8c2, 0x8cd, 0x8d9, 0x8e4, 0x8f0, 0x8fb, 0x907, 0x913, 0x91e, 0x92a, + 0x936, 0x942, 0x94d, 0x959, 0x965, 0x971, 0x97d, 0x989, 0x995, 0x9a1, + 0x9ad, 0x9b9, 0x9c5, 0x9d2, 0x9de, 0x9ea, 0x9f6, 0xa03, 0xa0f, 0xa1c, + 0xa28, 0xa34, 0xa41, 0xa4d, 0xa5a, 0xa67, 0xa73, 0xa80, 0xa8d, 0xa99, + 0xaa6, 0xab3, 0xac0, 0xacd, 0xada, 0xae7, 0xaf4, 0xb01, 0xb0e, 0xb1b, + 0xb28, 0xb35, 0xb42, 0xb50, 0xb5d, 0xb6a, 0xb77, 0xb85, 0xb92, 0xba0, + 0xbad, 0xbbb, 0xbc8, 0xbd6, 0xbe4, 0xbf1, 0xbff, 0xc0d, 0xc1a, 0xc28, + 0xc36, 0xc44, 0xc52, 0xc60, 0xc6e, 0xc7c, 0xc8a, 0xc98, 0xca6, 0xcb4, + 0xcc3, 0xcd1, 0xcdf, 0xced, 0xcfc, 0xd0a, 0xd19, 0xd27, 0xd35, 0xd44, + 0xd53, 0xd61, 0xd70, 0xd7e, 0xd8d, 0xd9c, 0xdab, 0xdba, 0xdc8, 0xdd7, + 0xde6, 0xdf5, 0xe04, 0xe13, 0xe22, 0xe31, 0xe41, 0xe50, 0xe5f, 0xe6e, + 0xe7e, 0xe8d, 0xe9c, 0xeac, 0xebb, 0xecb, 0xeda, 0xeea, 0xef9, 0xf09, + 0xf19, 0xf28, 0xf38, 0xf48, 0xf58, 0xf68, 0xf77, 0xf87, 0xf97, 0xfa7, + 0xfb7, 0xfc7, 0xfd8, 0xfe8, 0xff8, 0x1008, 0x1018, 0x1029, 0x1039, 0x1049, + 0x105a, 0x106a, 0x107b, 0x108b, 0x109c, 0x10ad, 0x10bd, 0x10ce, 0x10df, + 0x10ef, 0x1100, 0x1111, 0x1122, 0x1133, 0x1144, 0x1155, 0x1166, 0x1177, + 0x1188, 0x1199, 0x11aa, 0x11bb, 0x11cd, 0x11de, 0x11ef, 0x1201, 0x1212, + 0x1223, 0x1235, 0x1246, 0x1258, 0x126a, 0x127b, 0x128d, 0x129f, 0x12b0, + 0x12c2, 0x12d4, 0x12e6, 0x12f8, 0x130a, 0x131c, 0x132e, 0x1340, 0x1352, + 0x1364, 0x1376, 0x1388, 0x139b, 0x13ad, 0x13bf, 0x13d2, 0x13e4, 0x13f6, + 0x1409, 0x141c, 0x142e, 0x1441, 0x1453, 0x1466, 0x1479, 0x148b, 0x149e, + 0x14b1, 0x14c4, 0x14d7, 0x14ea, 0x14fd, 0x1510, 0x1523, 0x1536, 0x1549, + 0x155c, 0x1570, 0x1583, 0x1596, 0x15aa, 0x15bd, 0x15d0, 0x15e4, 0x15f7, + 0x160b, 0x161e, 0x1632, 0x1646, 0x1659, 0x166d, 0x1681, 0x1695, 0x16a9, + 0x16bd, 0x16d1, 0x16e4, 0x16f9, 0x170d, 0x1721, 0x1735, 0x1749, 0x175d, + 0x1771, 0x1786, 0x179a, 0x17af, 0x17c3, 0x17d7, 0x17ec, 0x1800, 0x1815, + 0x182a, 0x183e, 0x1853, 0x1868, 0x187d, 0x1891, 0x18a6, 0x18bb, 0x18d0, + 0x18e5, 0x18fa, 0x190f, 0x1924, 0x1939, 0x194f, 0x1964, 0x1979, 0x198e, + 0x19a4, 0x19b9, 0x19cf, 0x19e4, 0x19fa, 0x1a0f, 0x1a25, 0x1a3a, 0x1a50, + 0x1a66, 0x1a7b, 0x1a91, 0x1aa7, 0x1abd, 0x1ad3, 0x1ae9, 0x1aff, 0x1b15, + 0x1b2b, 0x1b41, 0x1b57, 0x1b6d, 0x1b84, 0x1b9a, 0x1bb0, 0x1bc7, 0x1bdd, + 0x1bf4, 0x1c0a, 0x1c21, 0x1c37, 0x1c4e, 0x1c64, 0x1c7b, 0x1c92, 0x1ca9, + 0x1cbf, 0x1cd6, 0x1ced, 0x1d04, 0x1d1b, 0x1d32, 0x1d49, 0x1d60, 0x1d78, + 0x1d8f, 0x1da6, 0x1dbd, 0x1dd5, 0x1dec, 0x1e03, 0x1e1b, 0x1e32, 0x1e4a, + 0x1e61, 0x1e79, 0x1e91, 0x1ea8, 0x1ec0, 0x1ed8, 0x1ef0, 0x1f07, 0x1f1f, + 0x1f37, 0x1f4f, 0x1f67, 0x1f7f, 0x1f98, 0x1fb0, 0x1fc8, 0x1fe0, 0x1ff8, + 0x2011, 0x2029, 0x2042, 0x205a, 0x2072, 0x208b, 0x20a4, 0x20bc, 0x20d5, + 0x20ee, 0x2106, 0x211f, 0x2138, 0x2151, 0x216a, 0x2183, 0x219c, 0x21b5, + 0x21ce, 0x21e7, 0x2200, 0x2219, 0x2233, 0x224c, 0x2265, 0x227f, 0x2298, + 0x22b2, 0x22cb, 0x22e5, 0x22fe, 0x2318, 0x2331, 0x234b, 0x2365, 0x237f, + 0x2399, 0x23b3, 0x23cc, 0x23e6, 0x2401, 0x241b, 0x2435, 0x244f, 0x2469, + 0x2483, 0x249e, 0x24b8, 0x24d2, 0x24ed, 0x2507, 0x2522, 0x253c, 0x2557, + 0x2571, 0x258c, 0x25a7, 0x25c2, 0x25dc, 0x25f7, 0x2612, 0x262d, 0x2648, + 0x2663, 0x267e, 0x2699, 0x26b4, 0x26cf, 0x26eb, 0x2706, 0x2721, 0x273d, + 0x2758, 0x2774, 0x278f, 0x27ab, 0x27c6, 0x27e2, 0x27fd, 0x2819, 0x2835, + 0x2851, 0x286d, 0x2888, 0x28a4, 0x28c0, 0x28dc, 0x28f8, 0x2915, 0x2931, + 0x294d, 0x2969, 0x2985, 0x29a2, 0x29be, 0x29db, 0x29f7, 0x2a14, 0x2a30, + 0x2a4d, 0x2a69, 0x2a86, 0x2aa3, 0x2ac0, 0x2adc, 0x2af9, 0x2b16, 0x2b33, + 0x2b50, 0x2b6d, 0x2b8a, 0x2ba7, 0x2bc4, 0x2be2, 0x2bff, 0x2c1c, 0x2c3a, + 0x2c57, 0x2c74, 0x2c92, 0x2caf, 0x2ccd, 0x2ceb, 0x2d08, 0x2d26, 0x2d44, + 0x2d61, 0x2d7f, 0x2d9d, 0x2dbb, 0x2dd9, 0x2df7, 0x2e15, 0x2e33, 0x2e51, + 0x2e70, 0x2e8e, 0x2eac, 0x2eca, 0x2ee9, 0x2f07, 0x2f26, 0x2f44, 0x2f63, + 0x2f81, 0x2fa0, 0x2fbf, 0x2fdd, 0x2ffc, 0x301b, 0x303a, 0x3059, 0x3078, + 0x3097, 0x30b6, 0x30d5, 0x30f4, 0x3113, 0x3132, 0x3152, 0x3171, 0x3190, + 0x31b0, 0x31cf, 0x31ef, 0x320e, 0x322e, 0x324e, 0x326d, 0x328d, 0x32ad, + 0x32cd, 0x32ec, 0x330c, 0x332c, 0x334c, 0x336c, 0x338c, 0x33ac, 0x33cd, + 0x33ed, 0x340d, 0x342e, 0x344e, 0x346e, 0x348f, 0x34af, 0x34d0, 0x34f0, + 0x3511, 0x3532, 0x3552, 0x3573, 0x3594, 0x35b5, 0x35d6, 0x35f7, 0x3618, + 0x3639, 0x365a, 0x367b, 0x369c, 0x36bd, 0x36df, 0x3700, 0x3721, 0x3743, + 0x3764, 0x3786, 0x37a7, 0x37c9, 0x37eb, 0x380c, 0x382e, 0x3850, 0x3872, + 0x3894, 0x38b6, 0x38d8, 0x38fa, 0x391c, 0x393e, 0x3960, 0x3982, 0x39a4, + 0x39c7, 0x39e9, 0x3a0b, 0x3a2e, 0x3a50, 0x3a73, 0x3a95, 0x3ab8, 0x3adb, + 0x3afd, 0x3b20, 0x3b43, 0x3b66, 0x3b89, 0x3bac, 0x3bcf, 0x3bf2, 0x3c15, + 0x3c38, 0x3c5b, 0x3c7e, 0x3ca2, 0x3cc5, 0x3ce8, 0x3d0c, 0x3d2f, 0x3d53, + 0x3d76, 0x3d9a, 0x3dbe, 0x3de1, 0x3e05, 0x3e29, 0x3e4d, 0x3e71, 0x3e95, + 0x3eb9, 0x3edd, 0x3f01, 0x3f25, 0x3f49, 0x3f6d, 0x3f91, 0x3fb6, 0x3fda, + 0x3fff, 0x0, + /* table descriptor */ + 0xfbefe1e1, 0xf, + /* table length */ + 0x402, + /* table data */ + 0x3, 0x0, 0x200, 0x500, 0x700, 0x8c0, 0xa20, 0xae0, 0xbe0, 0xc70, 0xd00, + 0xdb0, 0xe30, 0xe98, 0xf00, 0xf78, 0xff8, 0x103c, 0x1084, 0x10d0, 0x1120, + 0x1178, 0x11d0, 0x1218, 0x124a, 0x127e, 0x12b6, 0x12f0, 0x132c, 0x136c, + 0x13ae, 0x13f2, 0x141d, 0x1442, 0x1469, 0x1491, 0x14bb, 0x14e6, 0x1513, + 0x1542, 0x1572, 0x15a4, 0x15d7, 0x1606, 0x1621, 0x163e, 0x165b, 0x1679, + 0x1698, 0x16b9, 0x16da, 0x16fc, 0x171f, 0x1743, 0x1768, 0x178e, 0x17b5, + 0x17dd, 0x1803, 0x1818, 0x182e, 0x1844, 0x185b, 0x1873, 0x188b, 0x18a4, + 0x18bd, 0x18d7, 0x18f2, 0x190d, 0x1929, 0x1945, 0x1962, 0x1980, 0x199f, + 0x19be, 0x19de, 0x19ff, 0x1a10, 0x1a21, 0x1a32, 0x1a44, 0x1a56, 0x1a69, + 0x1a7c, 0x1a8f, 0x1aa3, 0x1ab7, 0x1acc, 0x1ae1, 0x1af6, 0x1b0c, 0x1b23, + 0x1b39, 0x1b51, 0x1b68, 0x1b81, 0x1b99, 0x1bb2, 0x1bcc, 0x1be6, 0x1c00, + 0x1c0e, 0x1c1b, 0x1c29, 0x1c38, 0x1c46, 0x1c55, 0x1c64, 0x1c74, 0x1c83, + 0x1c93, 0x1ca3, 0x1cb4, 0x1cc5, 0x1cd6, 0x1ce7, 0x1cf9, 0x1d0b, 0x1d1d, + 0x1d30, 0x1d42, 0x1d56, 0x1d69, 0x1d7d, 0x1d91, 0x1da6, 0x1dbb, 0x1dd0, + 0x1de6, 0x1dfc, 0x1e09, 0x1e14, 0x1e20, 0x1e2b, 0x1e37, 0x1e43, 0x1e4f, + 0x1e5c, 0x1e69, 0x1e75, 0x1e82, 0x1e90, 0x1e9d, 0x1eab, 0x1eb9, 0x1ec7, + 0x1ed5, 0x1ee3, 0x1ef2, 0x1f01, 0x1f10, 0x1f20, 0x1f2f, 0x1f3f, 0x1f4f, + 0x1f60, 0x1f70, 0x1f81, 0x1f92, 0x1fa3, 0x1fb5, 0x1fc7, 0x1fd9, 0x1feb, + 0x1ffe, 0x2008, 0x2012, 0x201b, 0x2025, 0x202f, 0x2039, 0x2043, 0x204e, + 0x2058, 0x2063, 0x206e, 0x2079, 0x2084, 0x208f, 0x209b, 0x20a6, 0x20b2, + 0x20be, 0x20ca, 0x20d6, 0x20e3, 0x20ef, 0x20fc, 0x2109, 0x2116, 0x2123, + 0x2131, 0x213e, 0x214c, 0x215a, 0x2168, 0x2176, 0x2185, 0x2194, 0x21a2, + 0x21b2, 0x21c1, 0x21d0, 0x21e0, 0x21f0, 0x2200, 0x2208, 0x2210, 0x2218, + 0x2221, 0x2229, 0x2232, 0x223b, 0x2244, 0x224d, 0x2256, 0x225f, 0x2268, + 0x2272, 0x227b, 0x2285, 0x228f, 0x2299, 0x22a3, 0x22ad, 0x22b7, 0x22c2, + 0x22cc, 0x22d7, 0x22e2, 0x22ed, 0x22f8, 0x2303, 0x230e, 0x231a, 0x2325, + 0x2331, 0x233d, 0x2349, 0x2355, 0x2361, 0x236e, 0x237a, 0x2387, 0x2394, + 0x23a1, 0x23ae, 0x23bc, 0x23c9, 0x23d7, 0x23e5, 0x23f3, 0x2400, 0x2407, + 0x240f, 0x2416, 0x241d, 0x2425, 0x242c, 0x2434, 0x243c, 0x2444, 0x244c, + 0x2454, 0x245c, 0x2464, 0x246c, 0x2475, 0x247d, 0x2486, 0x248e, 0x2497, + 0x24a0, 0x24a9, 0x24b2, 0x24bb, 0x24c4, 0x24ce, 0x24d7, 0x24e1, 0x24eb, + 0x24f4, 0x24fe, 0x2508, 0x2512, 0x251d, 0x2527, 0x2531, 0x253c, 0x2547, + 0x2552, 0x255d, 0x2568, 0x2573, 0x257e, 0x2589, 0x2595, 0x25a1, 0x25ac, + 0x25b8, 0x25c4, 0x25d1, 0x25dd, 0x25e9, 0x25f6, 0x2601, 0x2607, 0x260e, + 0x2614, 0x261b, 0x2622, 0x2629, 0x262f, 0x2636, 0x263d, 0x2644, 0x264c, + 0x2653, 0x265a, 0x2661, 0x2669, 0x2670, 0x2678, 0x2680, 0x2687, 0x268f, + 0x2697, 0x269f, 0x26a7, 0x26af, 0x26b8, 0x26c0, 0x26c8, 0x26d1, 0x26d9, + 0x26e2, 0x26eb, 0x26f4, 0x26fd, 0x2706, 0x270f, 0x2718, 0x2722, 0x272b, + 0x2734, 0x273e, 0x2748, 0x2752, 0x275c, 0x2766, 0x2770, 0x277a, 0x2784, + 0x278f, 0x2799, 0x27a4, 0x27af, 0x27b9, 0x27c4, 0x27d0, 0x27db, 0x27e6, + 0x27f1, 0x27fd, 0x2804, 0x280a, 0x2810, 0x2816, 0x281c, 0x2822, 0x2828, + 0x282f, 0x2835, 0x283b, 0x2842, 0x2848, 0x284f, 0x2855, 0x285c, 0x2863, + 0x286a, 0x2870, 0x2877, 0x287e, 0x2886, 0x288d, 0x2894, 0x289b, 0x28a3, + 0x28aa, 0x28b2, 0x28b9, 0x28c1, 0x28c9, 0x28d0, 0x28d8, 0x28e0, 0x28e8, + 0x28f1, 0x28f9, 0x2901, 0x2909, 0x2912, 0x291a, 0x2923, 0x292c, 0x2935, + 0x293d, 0x2946, 0x294f, 0x2959, 0x2962, 0x296b, 0x2975, 0x297e, 0x2988, + 0x2991, 0x299b, 0x29a5, 0x29af, 0x29b9, 0x29c3, 0x29cd, 0x29d8, 0x29e2, + 0x29ed, 0x29f7, 0x2a01, 0x2a06, 0x2a0c, 0x2a11, 0x2a17, 0x2a1c, 0x2a22, + 0x2a28, 0x2a2e, 0x2a33, 0x2a39, 0x2a3f, 0x2a45, 0x2a4b, 0x2a52, 0x2a58, + 0x2a5e, 0x2a64, 0x2a6b, 0x2a71, 0x2a78, 0x2a7e, 0x2a85, 0x2a8b, 0x2a92, + 0x2a99, 0x2aa0, 0x2aa7, 0x2aae, 0x2ab5, 0x2abc, 0x2ac3, 0x2aca, 0x2ad2, + 0x2ad9, 0x2ae1, 0x2ae8, 0x2af0, 0x2af7, 0x2aff, 0x2b07, 0x2b0f, 0x2b17, + 0x2b1f, 0x2b27, 0x2b2f, 0x2b38, 0x2b40, 0x2b48, 0x2b51, 0x2b59, 0x2b62, + 0x2b6b, 0x2b74, 0x2b7d, 0x2b86, 0x2b8f, 0x2b98, 0x2ba1, 0x2baa, 0x2bb4, + 0x2bbd, 0x2bc7, 0x2bd0, 0x2bda, 0x2be4, 0x2bee, 0x2bf8, 0x2c01, 0x2c06, + 0x2c0b, 0x2c10, 0x2c16, 0x2c1b, 0x2c20, 0x2c26, 0x2c2b, 0x2c31, 0x2c36, + 0x2c3c, 0x2c41, 0x2c47, 0x2c4d, 0x2c53, 0x2c59, 0x2c5e, 0x2c64, 0x2c6a, + 0x2c70, 0x2c77, 0x2c7d, 0x2c83, 0x2c89, 0x2c90, 0x2c96, 0x2c9d, 0x2ca3, + 0x2caa, 0x2cb0, 0x2cb7, 0x2cbe, 0x2cc5, 0x2ccc, 0x2cd3, 0x2cda, 0x2ce1, + 0x2ce8, 0x2cef, 0x2cf6, 0x2cfe, 0x2d05, 0x2d0d, 0x2d14, 0x2d1c, 0x2d24, + 0x2d2b, 0x2d33, 0x2d3b, 0x2d43, 0x2d4b, 0x2d53, 0x2d5b, 0x2d64, 0x2d6c, + 0x2d74, 0x2d7d, 0x2d85, 0x2d8e, 0x2d97, 0x2da0, 0x2da9, 0x2db2, 0x2dbb, + 0x2dc4, 0x2dcd, 0x2dd6, 0x2de0, 0x2de9, 0x2df3, 0x2dfc, 0x2e03, 0x2e08, + 0x2e0d, 0x2e12, 0x2e17, 0x2e1c, 0x2e21, 0x2e26, 0x2e2b, 0x2e30, 0x2e36, + 0x2e3b, 0x2e41, 0x2e46, 0x2e4c, 0x2e51, 0x2e57, 0x2e5c, 0x2e62, 0x2e68, + 0x2e6e, 0x2e73, 0x2e79, 0x2e7f, 0x2e85, 0x2e8b, 0x2e92, 0x2e98, 0x2e9e, + 0x2ea4, 0x2eab, 0x2eb1, 0x2eb8, 0x2ebe, 0x2ec5, 0x2ecb, 0x2ed2, 0x2ed9, + 0x2ee0, 0x2ee6, 0x2eed, 0x2ef4, 0x2efb, 0x2f03, 0x2f0a, 0x2f11, 0x2f18, + 0x2f20, 0x2f27, 0x2f2f, 0x2f36, 0x2f3e, 0x2f46, 0x2f4e, 0x2f55, 0x2f5d, + 0x2f65, 0x2f6d, 0x2f76, 0x2f7e, 0x2f86, 0x2f8e, 0x2f97, 0x2f9f, 0x2fa8, + 0x2fb1, 0x2fb9, 0x2fc2, 0x2fcb, 0x2fd4, 0x2fdd, 0x2fe6, 0x2ff0, 0x2ff9, + 0x3001, 0x3006, 0x300a, 0x300f, 0x3014, 0x3019, 0x301e, 0x3023, 0x3028, + 0x302d, 0x3032, 0x3037, 0x303d, 0x3042, 0x3047, 0x304d, 0x3052, 0x3057, + 0x305d, 0x3062, 0x3068, 0x306e, 0x3073, 0x3079, 0x307f, 0x3085, 0x308b, + 0x3091, 0x3097, 0x309d, 0x30a3, 0x30a9, 0x30af, 0x30b6, 0x30bc, 0x30c2, + 0x30c9, 0x30cf, 0x30d6, 0x30dd, 0x30e3, 0x30ea, 0x30f1, 0x30f8, 0x30ff, + 0x3106, 0x310d, 0x3114, 0x311b, 0x3122, 0x3129, 0x3131, 0x3138, 0x3140, + 0x3147, 0x314f, 0x3157, 0x315e, 0x3166, 0x316e, 0x3176, 0x317e, 0x3186, + 0x318f, 0x3197, 0x319f, 0x31a8, 0x31b0, 0x31b9, 0x31c1, 0x31ca, 0x31d3, + 0x31dc, 0x31e5, 0x31ee, 0x31f7, 0x3200, 0x3204, 0x3209, 0x320e, 0x3213, + 0x3217, 0x321c, 0x3221, 0x3226, 0x322b, 0x3230, 0x3235, 0x323a, 0x323f, + 0x3245, 0x324a, 0x324f, 0x3255, 0x325a, 0x325f, 0x3265, 0x326a, 0x3270, + 0x3276, 0x327b, 0x3281, 0x3287, 0x328d, 0x3293, 0x3299, 0x329f, 0x32a5, + 0x32ab, 0x32b1, 0x32b7, 0x32bd, 0x32c4, 0x32ca, 0x32d1, 0x32d7, 0x32de, + 0x32e4, 0x32eb, 0x32f2, 0x32f8, 0x32ff, 0x3306, 0x330d, 0x3314, 0x331b, + 0x3322, 0x332a, 0x3331, 0x3338, 0x3340, 0x3347, 0x334f, 0x3357, 0x335e, + 0x3366, 0x336e, 0x3376, 0x337e, 0x3386, 0x338e, 0x3396, 0x339e, 0x33a7, + 0x33af, 0x33b8, 0x33c0, 0x33c9, 0x33d2, 0x33da, 0x33e3, 0x33ec, 0x33f5, + 0x33fe, 0x3404, 0x3408, 0x340d, 0x3412, 0x3416, 0x341b, 0x3420, 0x3425, + 0x342a, 0x342f, 0x3434, 0x3439, 0x343e, 0x3443, 0x3448, 0x344e, 0x3453, + 0x3458, 0x345e, 0x3463, 0x3469, 0x346e, 0x3474, 0x347a, 0x347f, 0x3485, + 0x348b, 0x3491, 0x3497, 0x349d, 0x34a3, 0x34a9, 0x34af, 0x34b5, 0x34bb, + 0x34c2, 0x34c8, 0x34ce, 0x34d5, 0x34db, 0x34e2, 0x34e9, 0x34ef, 0x34f6, + 0x34fd, 0x3504, 0x350b, 0x3512, 0x3519, 0x3520, 0x3527, 0x352f, 0x3536, + 0x353d, 0x3545, 0x354c, 0x3554, 0x355c, 0x3563, 0x356b, 0x3573, 0x357b, + 0x3583, 0x358b, 0x3593, 0x359c, 0x35a4, 0x35ad, 0x35b5, 0x35be, 0x35c6, + 0x35cf, 0x35d8, 0x35e1, 0x35ea, 0x35f3, 0x35fc, 0x3602, 0x3607, 0x360c, + 0x3610, 0x3615, 0x361a, 0x361f, 0x3624, 0x3629, 0x362e, 0x3633, 0x3638, + 0x363d, 0x3642, 0x3647, 0x364d, 0x3652, 0x3657, 0x365d, 0x3662, 0x3668, + 0x366d, 0x3673, 0x3679, 0x367f, 0x3684, 0x368a, 0x3690, 0x3696, 0x369c, + 0x36a2, 0x36a8, 0x36ae, 0x36b5, 0x36bb, 0x36c1, 0x36c8, 0x36ce, 0x36d5, + 0x36db, 0x36e2, 0x36e9, 0x36f0, 0x36f6, 0x36fd, 0x3704, 0x370b, 0x3712, + 0x371a, 0x3721, 0x3728, 0x372f, 0x3737, 0x373e, 0x3746, 0x374e, 0x3755, + 0x375d, 0x3765, 0x376d, 0x3775, 0x377d, 0x3785, 0x378d, 0x3796, 0x379e, + 0x37a7, 0x37af, 0x37b8, 0x37c1, 0x37c9, 0x37d2, 0x37db, 0x37e4, 0x37ed, + 0x37f7, 0x3800, 0x3804, 0x3809, 0x380e, 0x3813, 0x3818, 0x381d, 0x3821, + 0x3826, 0x382c, 0x3831, 0x3836, 0x383b, 0x3840, 0x3846, 0x384b, 0x3850, + 0x3856, 0x385b, 0x3861, 0x3866, 0x386c, 0x3872, 0x3878, 0x387d, 0x3883, + 0x3889, 0x388f, 0x3895, 0x389b, 0x38a2, 0x38a8, 0x38ae, 0x38b5, 0x38bb, + 0x38c1, 0x38c8, 0x38cf, 0x38d5, 0x38dc, 0x38e3, 0x38ea, 0x38f1, 0x38f8, + 0x38ff, 0x3906, 0x390d, 0x3914, 0x391b, 0x3923, 0x392a, 0x3932, 0x3939, + 0x3941, 0x3949, 0x3951, 0x3959, 0x3961, 0x3969, 0x3971, 0x3979, 0x3981, + 0x398a, 0x3992, 0x399b, 0x39a3, 0x39ac, 0x39b5, 0x39be, 0x39c7, 0x39d0, + 0x39d9, 0x39e2, 0x39ec, 0x39f5, 0x39ff, 0x3, + /* table descriptor */ + 0xfbefe2e1, 0xf, + /* table length */ + 0x402, + /* table data */ + 0x3, 0x0, 0x3, 0x7, 0xa, 0xe, 0x11, 0x15, 0x18, 0x1c, 0x20, 0x23, 0x27, + 0x2a, 0x2e, 0x31, 0x35, 0x38, 0x3c, 0x40, 0x43, 0x47, 0x4a, 0x4e, 0x51, + 0x55, 0x58, 0x5c, 0x60, 0x63, 0x67, 0x6a, 0x6e, 0x71, 0x75, 0x78, 0x7c, + 0x80, 0x83, 0x87, 0x8a, 0x8e, 0x91, 0x95, 0x99, 0x9c, 0xa0, 0xa3, 0xa7, + 0xaa, 0xae, 0xb1, 0xb5, 0xb9, 0xbc, 0xc0, 0xc3, 0xc7, 0xca, 0xce, 0xd1, + 0xd5, 0xd9, 0xdc, 0xe0, 0xe3, 0xe7, 0xea, 0xee, 0xf1, 0xf5, 0xf9, 0xfc, + 0x100, 0x103, 0x107, 0x10a, 0x10e, 0x112, 0x115, 0x119, 0x11c, 0x120, + 0x123, 0x126, 0x12a, 0x12d, 0x131, 0x134, 0x138, 0x13c, 0x13f, 0x143, + 0x147, 0x14b, 0x14e, 0x152, 0x156, 0x15a, 0x15e, 0x162, 0x166, 0x16a, + 0x16e, 0x172, 0x176, 0x17a, 0x17e, 0x182, 0x186, 0x18a, 0x18f, 0x193, + 0x197, 0x19b, 0x1a0, 0x1a4, 0x1a8, 0x1ad, 0x1b1, 0x1b5, 0x1ba, 0x1be, + 0x1c3, 0x1c7, 0x1cc, 0x1d0, 0x1d5, 0x1d9, 0x1de, 0x1e3, 0x1e7, 0x1ec, + 0x1f1, 0x1f6, 0x1fa, 0x1ff, 0x204, 0x209, 0x20e, 0x213, 0x217, 0x21c, + 0x221, 0x226, 0x22b, 0x230, 0x236, 0x23b, 0x240, 0x245, 0x24a, 0x24f, + 0x255, 0x25a, 0x25f, 0x264, 0x26a, 0x26f, 0x274, 0x27a, 0x27f, 0x285, + 0x28a, 0x290, 0x295, 0x29b, 0x2a0, 0x2a6, 0x2ac, 0x2b1, 0x2b7, 0x2bd, + 0x2c2, 0x2c8, 0x2ce, 0x2d4, 0x2da, 0x2df, 0x2e5, 0x2eb, 0x2f1, 0x2f7, + 0x2fd, 0x303, 0x309, 0x30f, 0x315, 0x31c, 0x322, 0x328, 0x32e, 0x334, + 0x33b, 0x341, 0x347, 0x34d, 0x354, 0x35a, 0x361, 0x367, 0x36d, 0x374, + 0x37a, 0x381, 0x388, 0x38e, 0x395, 0x39b, 0x3a2, 0x3a9, 0x3b0, 0x3b6, + 0x3bd, 0x3c4, 0x3cb, 0x3d2, 0x3d8, 0x3df, 0x3e6, 0x3ed, 0x3f4, 0x3fb, + 0x402, 0x409, 0x411, 0x418, 0x41f, 0x426, 0x42d, 0x434, 0x43c, 0x443, + 0x44a, 0x452, 0x459, 0x460, 0x468, 0x46f, 0x477, 0x47e, 0x486, 0x48d, + 0x495, 0x49c, 0x4a4, 0x4ac, 0x4b3, 0x4bb, 0x4c3, 0x4cb, 0x4d3, 0x4da, + 0x4e2, 0x4ea, 0x4f2, 0x4fa, 0x502, 0x50a, 0x512, 0x51a, 0x522, 0x52a, + 0x532, 0x53a, 0x543, 0x54b, 0x553, 0x55b, 0x564, 0x56c, 0x574, 0x57d, + 0x585, 0x58d, 0x596, 0x59e, 0x5a7, 0x5af, 0x5b8, 0x5c1, 0x5c9, 0x5d2, + 0x5db, 0x5e3, 0x5ec, 0x5f5, 0x5fe, 0x606, 0x60f, 0x618, 0x621, 0x62a, + 0x633, 0x63c, 0x645, 0x64e, 0x657, 0x660, 0x669, 0x672, 0x67b, 0x685, + 0x68e, 0x697, 0x6a0, 0x6aa, 0x6b3, 0x6bd, 0x6c6, 0x6cf, 0x6d9, 0x6e2, + 0x6ec, 0x6f5, 0x6ff, 0x709, 0x712, 0x71c, 0x726, 0x72f, 0x739, 0x743, + 0x74d, 0x756, 0x760, 0x76a, 0x774, 0x77e, 0x788, 0x792, 0x79c, 0x7a6, + 0x7b0, 0x7ba, 0x7c4, 0x7cf, 0x7d9, 0x7e3, 0x7ed, 0x7f7, 0x802, 0x80c, + 0x816, 0x821, 0x82b, 0x836, 0x840, 0x84b, 0x855, 0x860, 0x86a, 0x875, + 0x880, 0x88a, 0x895, 0x8a0, 0x8ab, 0x8b5, 0x8c0, 0x8cb, 0x8d6, 0x8e1, + 0x8ec, 0x8f7, 0x902, 0x90d, 0x918, 0x923, 0x92e, 0x939, 0x944, 0x950, + 0x95b, 0x966, 0x971, 0x97d, 0x988, 0x993, 0x99f, 0x9aa, 0x9b6, 0x9c1, + 0x9cd, 0x9d8, 0x9e4, 0x9f0, 0x9fb, 0xa07, 0xa13, 0xa1e, 0xa2a, 0xa36, + 0xa42, 0xa4e, 0xa59, 0xa65, 0xa71, 0xa7d, 0xa89, 0xa95, 0xaa1, 0xaad, + 0xab9, 0xac6, 0xad2, 0xade, 0xaea, 0xaf6, 0xb03, 0xb0f, 0xb1b, 0xb28, + 0xb34, 0xb41, 0xb4d, 0xb5a, 0xb66, 0xb73, 0xb7f, 0xb8c, 0xb98, 0xba5, + 0xbb2, 0xbbf, 0xbcb, 0xbd8, 0xbe5, 0xbf2, 0xbff, 0xc0c, 0xc19, 0xc25, + 0xc32, 0xc40, 0xc4d, 0xc5a, 0xc67, 0xc74, 0xc81, 0xc8e, 0xc9c, 0xca9, + 0xcb6, 0xcc3, 0xcd1, 0xcde, 0xcec, 0xcf9, 0xd07, 0xd14, 0xd22, 0xd2f, + 0xd3d, 0xd4a, 0xd58, 0xd66, 0xd73, 0xd81, 0xd8f, 0xd9d, 0xdab, 0xdb8, + 0xdc6, 0xdd4, 0xde2, 0xdf0, 0xdfe, 0xe0c, 0xe1a, 0xe29, 0xe37, 0xe45, + 0xe53, 0xe61, 0xe70, 0xe7e, 0xe8c, 0xe9a, 0xea9, 0xeb7, 0xec6, 0xed4, + 0xee3, 0xef1, 0xf00, 0xf0e, 0xf1d, 0xf2c, 0xf3a, 0xf49, 0xf58, 0xf67, + 0xf75, 0xf84, 0xf93, 0xfa2, 0xfb1, 0xfc0, 0xfcf, 0xfde, 0xfed, 0xffc, + 0x100b, 0x101a, 0x102a, 0x1039, 0x1048, 0x1057, 0x1067, 0x1076, 0x1085, + 0x1095, 0x10a4, 0x10b4, 0x10c3, 0x10d3, 0x10e2, 0x10f2, 0x1101, 0x1111, + 0x1121, 0x1130, 0x1140, 0x1150, 0x1160, 0x116f, 0x117f, 0x118f, 0x119f, + 0x11af, 0x11bf, 0x11cf, 0x11df, 0x11ef, 0x11ff, 0x120f, 0x121f, 0x1230, + 0x1240, 0x1250, 0x1260, 0x1271, 0x1281, 0x1291, 0x12a2, 0x12b2, 0x12c3, + 0x12d3, 0x12e4, 0x12f4, 0x1305, 0x1316, 0x1326, 0x1337, 0x1348, 0x1359, + 0x1369, 0x137a, 0x138b, 0x139c, 0x13ad, 0x13be, 0x13cf, 0x13e0, 0x13f1, + 0x1402, 0x1413, 0x1424, 0x1435, 0x1446, 0x1458, 0x1469, 0x147a, 0x148b, + 0x149d, 0x14ae, 0x14c0, 0x14d1, 0x14e3, 0x14f4, 0x1506, 0x1517, 0x1529, + 0x153a, 0x154c, 0x155e, 0x156f, 0x1581, 0x1593, 0x15a5, 0x15b7, 0x15c9, + 0x15db, 0x15ec, 0x15fe, 0x1610, 0x1623, 0x1635, 0x1647, 0x1659, 0x166b, + 0x167d, 0x168f, 0x16a2, 0x16b4, 0x16c6, 0x16d9, 0x16eb, 0x16fe, 0x1710, + 0x1722, 0x1735, 0x1748, 0x175a, 0x176d, 0x177f, 0x1792, 0x17a5, 0x17b8, + 0x17ca, 0x17dd, 0x17f0, 0x1803, 0x1816, 0x1829, 0x183c, 0x184f, 0x1862, + 0x1875, 0x1888, 0x189b, 0x18ae, 0x18c1, 0x18d5, 0x18e8, 0x18fb, 0x190e, + 0x1922, 0x1935, 0x1949, 0x195c, 0x196f, 0x1983, 0x1996, 0x19aa, 0x19be, + 0x19d1, 0x19e5, 0x19f9, 0x1a0c, 0x1a20, 0x1a34, 0x1a48, 0x1a5c, 0x1a70, + 0x1a84, 0x1a97, 0x1aab, 0x1ac0, 0x1ad4, 0x1ae8, 0x1afc, 0x1b10, 0x1b24, + 0x1b38, 0x1b4d, 0x1b61, 0x1b75, 0x1b8a, 0x1b9e, 0x1bb2, 0x1bc7, 0x1bdb, + 0x1bf0, 0x1c04, 0x1c19, 0x1c2e, 0x1c42, 0x1c57, 0x1c6c, 0x1c80, 0x1c95, + 0x1caa, 0x1cbf, 0x1cd4, 0x1ce8, 0x1cfd, 0x1d12, 0x1d27, 0x1d3c, 0x1d51, + 0x1d67, 0x1d7c, 0x1d91, 0x1da6, 0x1dbb, 0x1dd1, 0x1de6, 0x1dfb, 0x1e10, + 0x1e26, 0x1e3b, 0x1e51, 0x1e66, 0x1e7c, 0x1e91, 0x1ea7, 0x1ebd, 0x1ed2, + 0x1ee8, 0x1efe, 0x1f13, 0x1f29, 0x1f3f, 0x1f55, 0x1f6b, 0x1f81, 0x1f96, + 0x1fac, 0x1fc2, 0x1fd9, 0x1fef, 0x2005, 0x201b, 0x2031, 0x2047, 0x205d, + 0x2074, 0x208a, 0x20a0, 0x20b7, 0x20cd, 0x20e4, 0x20fa, 0x2111, 0x2127, + 0x213e, 0x2154, 0x216b, 0x2182, 0x2198, 0x21af, 0x21c6, 0x21dd, 0x21f3, + 0x220a, 0x2221, 0x2238, 0x224f, 0x2266, 0x227d, 0x2294, 0x22ab, 0x22c2, + 0x22da, 0x22f1, 0x2308, 0x231f, 0x2337, 0x234e, 0x2365, 0x237d, 0x2394, + 0x23ac, 0x23c3, 0x23db, 0x23f2, 0x240a, 0x2421, 0x2439, 0x2451, 0x2469, + 0x2480, 0x2498, 0x24b0, 0x24c8, 0x24e0, 0x24f8, 0x2510, 0x2528, 0x2540, + 0x2558, 0x2570, 0x2588, 0x25a0, 0x25b8, 0x25d0, 0x25e9, 0x2601, 0x2619, + 0x2632, 0x264a, 0x2663, 0x267b, 0x2693, 0x26ac, 0x26c5, 0x26dd, 0x26f6, + 0x270e, 0x2727, 0x2740, 0x2759, 0x2771, 0x278a, 0x27a3, 0x27bc, 0x27d5, + 0x27ee, 0x2807, 0x2820, 0x2839, 0x2852, 0x286b, 0x2884, 0x289e, 0x28b7, + 0x28d0, 0x28e9, 0x2903, 0x291c, 0x2936, 0x294f, 0x2968, 0x2982, 0x299c, + 0x29b5, 0x29cf, 0x29e8, 0x2a02, 0x2a1c, 0x2a35, 0x2a4f, 0x2a69, 0x2a83, + 0x2a9d, 0x2ab7, 0x2ad1, 0x2aeb, 0x2b05, 0x2b1f, 0x2b39, 0x2b53, 0x2b6d, + 0x2b87, 0x2ba1, 0x2bbc, 0x2bd6, 0x2bf0, 0x2c0b, 0x2c25, 0x2c3f, 0x2c5a, + 0x2c74, 0x2c8f, 0x2ca9, 0x2cc4, 0x2cdf, 0x2cf9, 0x2d14, 0x2d2f, 0x2d49, + 0x2d64, 0x2d7f, 0x2d9a, 0x2db5, 0x2dd0, 0x2deb, 0x2e06, 0x2e21, 0x2e3c, + 0x2e57, 0x2e72, 0x2e8d, 0x2ea8, 0x2ec4, 0x2edf, 0x2efa, 0x2f16, 0x2f31, + 0x2f4c, 0x2f68, 0x2f83, 0x2f9f, 0x2fba, 0x2fd6, 0x2ff1, 0x300d, 0x3029, + 0x3044, 0x3060, 0x307c, 0x3098, 0x30b4, 0x30d0, 0x30eb, 0x3107, 0x3123, + 0x313f, 0x315b, 0x3178, 0x3194, 0x31b0, 0x31cc, 0x31e8, 0x3205, 0x3221, + 0x323d, 0x325a, 0x3276, 0x3292, 0x32af, 0x32cb, 0x32e8, 0x3304, 0x3321, + 0x333e, 0x335a, 0x3377, 0x3394, 0x33b1, 0x33cd, 0x33ea, 0x3407, 0x3424, + 0x3441, 0x345e, 0x347b, 0x3498, 0x34b5, 0x34d2, 0x34ef, 0x350d, 0x352a, + 0x3547, 0x3564, 0x3582, 0x359f, 0x35bc, 0x35da, 0x35f7, 0x3615, 0x3632, + 0x3650, 0x366e, 0x368b, 0x36a9, 0x36c7, 0x36e4, 0x3702, 0x3720, 0x373e, + 0x375c, 0x377a, 0x3798, 0x37b6, 0x37d4, 0x37f2, 0x3810, 0x382e, 0x384c, + 0x386a, 0x3888, 0x38a7, 0x38c5, 0x38e3, 0x3902, 0x3920, 0x393f, 0x395d, + 0x397c, 0x399a, 0x39b9, 0x39d7, 0x39f6, 0x3a15, 0x3a33, 0x3a52, 0x3a71, + 0x3a90, 0x3aaf, 0x3acd, 0x3aec, 0x3b0b, 0x3b2a, 0x3b49, 0x3b68, 0x3b87, + 0x3ba7, 0x3bc6, 0x3be5, 0x3c04, 0x3c24, 0x3c43, 0x3c62, 0x3c82, 0x3ca1, + 0x3cc0, 0x3ce0, 0x3cff, 0x3d1f, 0x3d3f, 0x3d5e, 0x3d7e, 0x3d9e, 0x3dbd, + 0x3ddd, 0x3dfd, 0x3e1d, 0x3e3d, 0x3e5d, 0x3e7c, 0x3e9c, 0x3ebc, 0x3edc, + 0x3efd, 0x3f1d, 0x3f3d, 0x3f5d, 0x3f7d, 0x3f9e, 0x3fbe, 0x3fde, 0x3fff, + 0x0, + /* table descriptor */ + 0xfbe83ba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4031, 0x0, 0x0, 0x0, 0x4031, 0x0, 0x0, 0x0, 0x4031, 0x0, 0xffffff80, + 0xffffff80, 0x0, 0xffffff80, 0xffffff80, 0xff, 0x7f, 0x7f, 0xc, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbe65ba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4000, 0x0, 0x59bb, 0x4000, 0xffffe9fb, 0xffffd24d, 0x4000, 0x7169, + 0x0, 0x0, 0xfffffe00, 0xfffffe00, 0x0, 0xfffffe00, 0xfffffe00, 0x3ff, + 0x1ff, 0x1ff, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbefbb62, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x1, 0x4031, 0x0, 0x0, 0x0, 0x4031, 0x0, 0x0, 0x0, 0x4031, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, + 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbefdb62, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* table descriptor */ + 0xfbe0bba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4031, 0x0, 0x5ea7, 0x4031, 0xfffff571, 0xffffdb55, 0x4031, 0x78c4, + 0x0, 0x0, 0xffffff80, 0xffffff80, 0x0, 0xffffff80, 0xffffff80, 0xff, 0x7f, + 0x7f, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbe63ba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4031, 0x0, 0x59fe, 0x4031, 0xffffe9ea, 0xffffd22b, 0x4031, 0x71be, + 0x0, 0x0, 0xffffff80, 0xffffff80, 0x0, 0xffffff80, 0xffffff80, 0xff, 0x7f, + 0x7f, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbe15ba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4000, 0x0, 0x64ca, 0x4000, 0xfffff404, 0xffffe20c, 0x4000, 0x76c3, + 0x0, 0x0, 0xfffffe00, 0xfffffe00, 0x0, 0xfffffe00, 0xfffffe00, 0x3ff, + 0x1ff, 0x1ff, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbe13ba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4031, 0x0, 0x6516, 0x4031, 0xfffff3fb, 0xffffe1f5, 0x4031, 0x771c, + 0x0, 0x0, 0xffffff80, 0xffffff80, 0x0, 0xffffff80, 0xffffff80, 0xff, 0x7f, + 0x7f, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbe0dba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4000, 0x0, 0x5e60, 0x4000, 0xfffff579, 0xffffdb70, 0x4000, 0x7869, + 0x0, 0x0, 0xfffffe00, 0xfffffe00, 0x0, 0xfffffe00, 0xfffffe00, 0x3ff, + 0x1ff, 0x1ff, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbe85ba2, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x3, 0x4000, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, 0x4000, 0x0, 0xfffffe00, + 0xfffffe00, 0x0, 0xfffffe00, 0xfffffe00, 0x3ff, 0x1ff, 0x1ff, 0xe, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x3ff, 0x3ff, + /* table descriptor */ + 0xfbeffbe4, 0xf, + /* table length */ + 0x1d, + /* table data */ + 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +}; + +#endif /* __DCSS_HDR10_TABLES_H__ */ diff --git a/drivers/gpu/drm/imx/dcss/dcss-hdr10.c b/drivers/gpu/drm/imx/dcss/dcss-hdr10.c new file mode 100644 index 000000000000..0c8e2478d2f4 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-hdr10.c @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/device.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/seq_file.h> +#include <linux/firmware.h> +#include <drm/drm_fourcc.h> + +#include "dcss-dev.h" + +#define USE_TBL_HEADER + +#ifdef USE_TBL_HEADER +#include "dcss-hdr10-tables.h" +#endif + +#define USE_CTXLD + +#define DCSS_HDR10_A0_LUT 0x0000 +#define DCSS_HDR10_A1_LUT 0x1000 +#define DCSS_HDR10_A2_LUT 0x2000 +/* one CSCA and CSCB for each channel(pipe) */ +#define DCSS_HDR10_CSCA_BASE 0x3000 +#define DCSS_HDR10_CSCB_BASE 0x3800 + +/* one CSCO for all channels(pipes) */ +#define DCSS_HDR10_CSCO_BASE 0x3000 + +#define DCSS_HDR10_LUT_CONTROL (DCSS_HDR10_CSCA_BASE + 0x80) +#define LUT_ENABLE BIT(0) +#define LUT_EN_FOR_ALL_PELS BIT(1) +#define LUT_BYPASS BIT(15) +#define DCSS_HDR10_FL2FX (DCSS_HDR10_CSCB_BASE + 0x74) +#define DCSS_HDR10_LTNL (DCSS_HDR10_CSCO_BASE + 0x74) +#define LTNL_PASS_THRU BIT(0) +#define FIX2FLT_DISABLE BIT(1) +#define LTNL_EN_FOR_ALL_PELS BIT(2) +#define FIX2FLT_EN_FOR_ALL_PELS BIT(3) + +/* following offsets are relative to CSC(A|B|O)_BASE */ +#define DCSS_HDR10_CSC_CONTROL 0x00 +#define CSC_EN BIT(0) +#define CSC_ALL_PIX_EN BIT(1) +#define CSC_BYPASS BIT(15) +#define DCSS_HDR10_CSC_H00 0x04 +#define DCSS_HDR10_CSC_H10 0x08 +#define DCSS_HDR10_CSC_H20 0x0C +#define DCSS_HDR10_CSC_H01 0x10 +#define DCSS_HDR10_CSC_H11 0x14 +#define DCSS_HDR10_CSC_H21 0x18 +#define DCSS_HDR10_CSC_H02 0x1C +#define DCSS_HDR10_CSC_H12 0x20 +#define DCSS_HDR10_CSC_H22 0x24 +#define H_COEF_MASK GENMASK(15, 0) +#define DCSS_HDR10_CSC_IO0 0x28 +#define DCSS_HDR10_CSC_IO1 0x2C +#define DCSS_HDR10_CSC_IO2 0x30 +#define PRE_OFFSET_MASK GENMASK(9, 0) +#define DCSS_HDR10_CSC_IO_MIN0 0x34 +#define DCSS_HDR10_CSC_IO_MIN1 0x38 +#define DCSS_HDR10_CSC_IO_MIN2 0x3C +#define DCSS_HDR10_CSC_IO_MAX0 0x40 +#define DCSS_HDR10_CSC_IO_MAX1 0x44 +#define DCSS_HDR10_CSC_IO_MAX2 0x48 +#define IO_CLIP_MASK GENMASK(9, 0) +#define DCSS_HDR10_CSC_NORM 0x4C +#define NORM_MASK GENMASK(4, 0) +#define DCSS_HDR10_CSC_OO0 0x50 +#define DCSS_HDR10_CSC_OO1 0x54 +#define DCSS_HDR10_CSC_OO2 0x58 +#define POST_OFFSET_MASK GENMASK(27, 0) +#define DCSS_HDR10_CSC_OMIN0 0x5C +#define DCSS_HDR10_CSC_OMIN1 0x60 +#define DCSS_HDR10_CSC_OMIN2 0x64 +#define DCSS_HDR10_CSC_OMAX0 0x68 +#define DCSS_HDR10_CSC_OMAX1 0x6C +#define DCSS_HDR10_CSC_OMAX2 0x70 +#define POST_CLIP_MASK GENMASK(9, 0) + +#define HDR10_IPIPE_LUT_MAX_ENTRIES 1024 +#define HDR10_OPIPE_LUT_MAX_ENTRIES 1023 +#define HDR10_CSC_MAX_REGS 29 + +#define OPIPE_CH_NO 3 + +/* Pipe config descriptor */ + +/* bits per component */ +#define HDR10_BPC_POS 0 +#define HDR10_BPC_MASK GENMASK(1, 0) +/* colorspace */ +#define HDR10_CS_POS 2 +#define HDR10_CS_MASK GENMASK(3, 2) +/* nonlinearity type */ +#define HDR10_NL_POS 4 +#define HDR10_NL_MASK GENMASK(8, 4) +/* pixel range */ +#define HDR10_PR_POS 9 +#define HDR10_PR_MASK GENMASK(10, 9) +/* gamut type */ +#define HDR10_G_POS 11 +#define HDR10_G_MASK GENMASK(15, 11) + +/* FW Table Type Descriptor */ +#define HDR10_TT_LUT BIT(0) +#define HDR10_TT_CSCA BIT(1) +#define HDR10_TT_CSCB BIT(2) +/* Pipe type */ +#define HDR10_PT_OUTPUT BIT(3) +/* Output pipe config descriptor */ +#define HDR10_IPIPE_DESC_POS 4 +#define HDR10_IPIPE_DESC_MASK GENMASK(19, 4) +/* Input pipe config descriptor */ +#define HDR10_OPIPE_DESC_POS 20 +#define HDR10_OPIPE_DESC_MASK GENMASK(35, 20) + +/* config invalid */ +#define HDR10_DESC_INVALID BIT(63) + +enum dcss_hdr10_csc { + HDR10_CSCA, + HDR10_CSCB, +}; + +struct dcss_hdr10_tbl_node { + struct list_head node; + + u64 tbl_descriptor; + u32 *tbl_data; +}; + +struct dcss_hdr10_opipe_tbls { + struct list_head lut; + struct list_head csc; +}; + +struct dcss_hdr10_ipipe_tbls { + struct list_head lut; + struct list_head csca; + struct list_head cscb; +}; + +struct dcss_hdr10_ch { + struct dcss_hdr10 *hdr10; + void __iomem *base_reg; + u32 base_ofs; + + u64 old_cfg_desc; + + u32 id; +}; + +struct dcss_hdr10 { + struct device *dev; + struct dcss_ctxld *ctxld; + + u32 ctx_id; + + struct dcss_hdr10_ch ch[4]; /* 4th channel is, actually, OPIPE */ + + struct dcss_hdr10_ipipe_tbls *ipipe_tbls; + struct dcss_hdr10_opipe_tbls *opipe_tbls; + + u8 *fw_data; + u32 fw_size; +}; + +static void dcss_hdr10_write(struct dcss_hdr10_ch *ch, u32 val, u32 ofs) +{ + struct dcss_hdr10 *hdr10 = ch->hdr10; + + dcss_ctxld_write(hdr10->ctxld, hdr10->ctx_id, val, ch->base_ofs + ofs); +} + +static void dcss_hdr10_csc_fill(struct dcss_hdr10_ch *ch, + enum dcss_hdr10_csc csc_to_use, + u32 *map) +{ + int i; + u32 csc_base_ofs[] = { + DCSS_HDR10_CSCA_BASE + DCSS_HDR10_CSC_CONTROL, + DCSS_HDR10_CSCB_BASE + DCSS_HDR10_CSC_CONTROL, + }; + + for (i = 0; i < HDR10_CSC_MAX_REGS; i++) { + u32 reg_ofs = csc_base_ofs[csc_to_use] + i * sizeof(u32); + + dcss_hdr10_write(ch, map[i], reg_ofs); + } +} + +static void dcss_hdr10_lut_fill(struct dcss_hdr10_ch *ch, u32 *map) +{ + int i, comp; + u32 lut_base_ofs, ctrl_ofs, lut_entries; + + if (ch->id == OPIPE_CH_NO) { + ctrl_ofs = DCSS_HDR10_LTNL; + lut_entries = HDR10_OPIPE_LUT_MAX_ENTRIES; + } else { + ctrl_ofs = DCSS_HDR10_LUT_CONTROL; + lut_entries = HDR10_IPIPE_LUT_MAX_ENTRIES; + } + + if (ch->id != OPIPE_CH_NO) + dcss_hdr10_write(ch, *map++, ctrl_ofs); + + for (comp = 0; comp < 3; comp++) { + lut_base_ofs = DCSS_HDR10_A0_LUT + comp * 0x1000; + + if (ch->id == OPIPE_CH_NO) { + dcss_hdr10_write(ch, map[0], lut_base_ofs); + lut_base_ofs += 4; + } + + for (i = 0; i < lut_entries; i++) { + u32 reg_ofs = lut_base_ofs + i * sizeof(u32); + + dcss_hdr10_write(ch, map[i], reg_ofs); + } + } + + map += lut_entries; + + if (ch->id != OPIPE_CH_NO) + dcss_hdr10_write(ch, *map, DCSS_HDR10_FL2FX); + else + dcss_hdr10_write(ch, *map, ctrl_ofs); +} + +static int dcss_hdr10_ch_init_all(struct dcss_hdr10 *hdr10, + unsigned long hdr10_base) +{ + struct dcss_hdr10_ch *ch; + int i; + + for (i = 0; i < 4; i++) { + ch = &hdr10->ch[i]; + + ch->base_ofs = hdr10_base + i * 0x4000; + + ch->base_reg = devm_ioremap(hdr10->dev, ch->base_ofs, SZ_16K); + if (!ch->base_reg) { + dev_err(hdr10->dev, "hdr10: unable to remap ch base\n"); + return -ENOMEM; + } + + ch->old_cfg_desc = HDR10_DESC_INVALID; + + ch->id = i; + ch->hdr10 = hdr10; + } + + return 0; +} + +static u32 *dcss_hdr10_find_tbl(u64 desc, struct list_head *head) +{ + struct list_head *node; + struct dcss_hdr10_tbl_node *tbl_node; + + list_for_each(node, head) { + tbl_node = container_of(node, struct dcss_hdr10_tbl_node, node); + + if ((tbl_node->tbl_descriptor & desc) == desc) + return tbl_node->tbl_data; + } + + return NULL; +} + +static int dcss_hdr10_get_tbls(struct dcss_hdr10 *hdr10, bool input, + u64 desc, u32 **lut, u32 **csca, u32 **cscb) +{ + struct list_head *lut_list, *csca_list, *cscb_list; + + lut_list = input ? &hdr10->ipipe_tbls->lut : &hdr10->opipe_tbls->lut; + csca_list = input ? &hdr10->ipipe_tbls->csca : &hdr10->opipe_tbls->csc; + cscb_list = input ? &hdr10->ipipe_tbls->cscb : NULL; + + *lut = dcss_hdr10_find_tbl(desc, lut_list); + *csca = dcss_hdr10_find_tbl(desc, csca_list); + + *cscb = NULL; + if (cscb_list) + *cscb = dcss_hdr10_find_tbl(desc, cscb_list); + + return 0; +} + +static void dcss_hdr10_write_pipe_tbls(struct dcss_hdr10_ch *ch, + u32 *lut, u32 *csca, u32 *cscb) +{ + if (csca) + dcss_hdr10_csc_fill(ch, HDR10_CSCA, csca); + + if (ch->id != OPIPE_CH_NO && cscb) + dcss_hdr10_csc_fill(ch, HDR10_CSCB, cscb); + + if (lut) + dcss_hdr10_lut_fill(ch, lut); +} + +static int dcss_hdr10_tbl_add(struct dcss_hdr10 *hdr10, u64 desc, u32 sz, + u32 *data) +{ + struct device *dev = hdr10->dev; + struct dcss_hdr10_tbl_node *node; + + node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + /* we don't need to store the table type and pipe type */ + node->tbl_descriptor = desc >> 4; + node->tbl_data = data; + + if (!(desc & HDR10_PT_OUTPUT)) { + if (desc & HDR10_TT_LUT) + list_add(&node->node, &hdr10->ipipe_tbls->lut); + else if (desc & HDR10_TT_CSCA) + list_add(&node->node, &hdr10->ipipe_tbls->csca); + else if (desc & HDR10_TT_CSCB) + list_add(&node->node, &hdr10->ipipe_tbls->cscb); + + return 0; + } + + if (desc & HDR10_TT_LUT) + list_add(&node->node, &hdr10->opipe_tbls->lut); + else if (desc & HDR10_TT_CSCA) + list_add(&node->node, &hdr10->opipe_tbls->csc); + + return 0; +} + +static int dcss_hdr10_parse_fw_data(struct dcss_hdr10 *hdr10) +{ + u32 *data = (u32 *)hdr10->fw_data; + u32 remaining = hdr10->fw_size / sizeof(u32); + u64 tbl_desc; + u32 tbl_size; + int ret; + + while (remaining) { + tbl_desc = *((u64 *)data); + data += 2; + tbl_size = *data++; + + ret = dcss_hdr10_tbl_add(hdr10, tbl_desc, tbl_size, data); + if (ret) + return ret; + + data += tbl_size; + remaining -= tbl_size + 3; + } + + return 0; +} + +static void dcss_hdr10_cleanup_tbls(struct dcss_hdr10 *hdr10) +{ + int i; + struct dcss_hdr10_tbl_node *tbl_node, *next; + struct list_head *tbls[] = { + &hdr10->ipipe_tbls->lut, + &hdr10->ipipe_tbls->csca, + &hdr10->ipipe_tbls->cscb, + &hdr10->opipe_tbls->lut, + &hdr10->opipe_tbls->csc, + }; + + for (i = 0; i < 5; i++) { + list_for_each_entry_safe(tbl_node, next, tbls[i], node) { + list_del(&tbl_node->node); + devm_kfree(hdr10->dev, tbl_node); + } + } + + devm_kfree(hdr10->dev, hdr10->opipe_tbls); + devm_kfree(hdr10->dev, hdr10->ipipe_tbls); +} + +#ifndef USE_TBL_HEADER +static void dcss_hdr10_fw_handler(const struct firmware *fw, void *context) +{ + struct dcss_hdr10 *hdr10 = context; + int i; + + if (!fw) { + dev_err(hdr10->dev, "hdr10: DCSS FW load failed.\n"); + return; + } + + /* we need to keep the tables for the entire life of the driver */ + hdr10->fw_data = devm_kzalloc(hdr10->dev, fw->size, GFP_KERNEL); + if (!hdr10->fw_data) + return; + + memcpy(hdr10->fw_data, fw->data, fw->size); + hdr10->fw_size = fw->size; + + release_firmware(fw); + + if (dcss_hdr10_parse_fw_data(hdr10)) { + dcss_hdr10_cleanup_tbls(hdr10); + return; + } + + for (i = 0; i < 4; i++) { + u32 *lut, *csca, *cscb; + struct dcss_hdr10_ch *ch = &hdr10->ch[i]; + bool is_input_pipe = i != OPIPE_CH_NO ? true : false; + + if (ch->old_cfg_desc != HDR10_DESC_INVALID) { + dcss_hdr10_get_tbls(hdr10, is_input_pipe, + ch->old_cfg_desc, &lut, + &csca, &cscb); + dcss_hdr10_write_pipe_tbls(ch, lut, csca, cscb); + } + } + + dev_info(hdr10->dev, "hdr10: DCSS FW loaded successfully\n"); +} +#endif + +static int dcss_hdr10_tbls_init(struct dcss_hdr10 *hdr10) +{ + struct device *dev = hdr10->dev; + + hdr10->ipipe_tbls = devm_kzalloc(dev, sizeof(*hdr10->ipipe_tbls), + GFP_KERNEL); + if (!hdr10->ipipe_tbls) + return -ENOMEM; + + INIT_LIST_HEAD(&hdr10->ipipe_tbls->lut); + INIT_LIST_HEAD(&hdr10->ipipe_tbls->csca); + INIT_LIST_HEAD(&hdr10->ipipe_tbls->cscb); + + hdr10->opipe_tbls = devm_kzalloc(dev, sizeof(*hdr10->opipe_tbls), + GFP_KERNEL); + if (!hdr10->opipe_tbls) { + devm_kfree(dev, hdr10->ipipe_tbls); + return -ENOMEM; + } + + INIT_LIST_HEAD(&hdr10->opipe_tbls->lut); + INIT_LIST_HEAD(&hdr10->opipe_tbls->csc); + + return 0; +} + +int dcss_hdr10_init(struct dcss_dev *dcss, unsigned long hdr10_base) +{ + int ret; + struct dcss_hdr10 *hdr10; + + hdr10 = devm_kzalloc(dcss->dev, sizeof(*hdr10), GFP_KERNEL); + if (!hdr10) + return -ENOMEM; + + dcss->hdr10 = hdr10; + hdr10->dev = dcss->dev; + hdr10->ctx_id = CTX_SB_HP; + hdr10->ctxld = dcss->ctxld; + + ret = dcss_hdr10_tbls_init(hdr10); + if (ret < 0) { + dev_err(dcss->dev, "hdr10: Cannot init table lists.\n"); + goto cleanup; + } + +#ifndef USE_TBL_HEADER + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, "dcss.fw", + dcss->dev, GFP_KERNEL, hdr10, + dcss_hdr10_fw_handler); + if (ret < 0) { + dev_err(dcss->dev, "hdr10: Cannot async load DCSS FW.\n"); + goto cleanup_tbls; + } +#else + hdr10->fw_data = (u8 *)dcss_hdr10_tables; + hdr10->fw_size = sizeof(dcss_hdr10_tables); + + ret = dcss_hdr10_parse_fw_data(hdr10); + if (ret) + goto cleanup_tbls; +#endif + + ret = dcss_hdr10_ch_init_all(hdr10, hdr10_base); + if (ret) { + int i; + + for (i = 0; i < 4; i++) { + if (hdr10->ch[i].base_reg) + devm_iounmap(hdr10->dev, hdr10->ch[i].base_reg); + } + + goto cleanup_tbls; + } + + return 0; + +cleanup_tbls: + dcss_hdr10_cleanup_tbls(hdr10); + +cleanup: + devm_kfree(hdr10->dev, hdr10); + + return ret; +} + +void dcss_hdr10_exit(struct dcss_hdr10 *hdr10) +{ + int i; + + for (i = 0; i < 4; i++) { + if (hdr10->ch[i].base_reg) + devm_iounmap(hdr10->dev, hdr10->ch[i].base_reg); + } + + dcss_hdr10_cleanup_tbls(hdr10); + + devm_kfree(hdr10->dev, hdr10); +} + +static u32 dcss_hdr10_pipe_desc(struct dcss_hdr10_pipe_cfg *pipe_cfg) +{ + u32 desc; + + desc = 2 << HDR10_BPC_POS; + desc |= pipe_cfg->is_yuv ? 2 << HDR10_CS_POS : 1 << HDR10_CS_POS; + desc |= ((1 << pipe_cfg->nl) << HDR10_NL_POS) & HDR10_NL_MASK; + desc |= ((1 << pipe_cfg->pr) << HDR10_PR_POS) & HDR10_PR_MASK; + desc |= ((1 << pipe_cfg->g) << HDR10_G_POS) & HDR10_G_MASK; + + return desc; +} + +static u64 dcss_hdr10_get_desc(struct dcss_hdr10_pipe_cfg *ipipe_cfg, + struct dcss_hdr10_pipe_cfg *opipe_cfg) +{ + u32 ipipe_desc, opipe_desc; + + ipipe_desc = dcss_hdr10_pipe_desc(ipipe_cfg); + opipe_desc = dcss_hdr10_pipe_desc(opipe_cfg); + + return (ipipe_desc & 0xFFFF) | ((opipe_desc & 0xFFFF) << 16); +} + +static void dcss_hdr10_pipe_setup(struct dcss_hdr10_ch *ch, u64 desc) +{ + bool pipe_cfg_chgd; + u32 *csca, *cscb, *lut; + + pipe_cfg_chgd = ch->old_cfg_desc != desc; + + if (!pipe_cfg_chgd) + return; + + dcss_hdr10_get_tbls(ch->hdr10, ch->id != OPIPE_CH_NO, + desc, &lut, &csca, &cscb); + dcss_hdr10_write_pipe_tbls(ch, lut, csca, cscb); + + ch->old_cfg_desc = desc; +} + +void dcss_hdr10_setup(struct dcss_hdr10 *hdr10, int ch_num, + struct dcss_hdr10_pipe_cfg *ipipe_cfg, + struct dcss_hdr10_pipe_cfg *opipe_cfg) +{ + u64 desc = dcss_hdr10_get_desc(ipipe_cfg, opipe_cfg); + + dcss_hdr10_pipe_setup(&hdr10->ch[ch_num], desc); + + /* + * Input pipe configuration doesn't matter for configuring the output + * pipe. So, will just mask off the input part of the descriptor. + */ + dcss_hdr10_pipe_setup(&hdr10->ch[OPIPE_CH_NO], desc & ~0xffff); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c new file mode 100644 index 000000000000..6e5c5ad13304 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drmP.h> +#include <linux/component.h> + +#include "dcss-dev.h" +#include "dcss-kms.h" + +DEFINE_DRM_GEM_CMA_FOPS(dcss_cma_fops); + +struct dcss_drm_commit { + struct work_struct work; + struct drm_device *drm; + struct drm_atomic_state *state; +}; + +static void dcss_kms_setup_opipe_gamut(u32 colorspace, + const struct drm_display_mode *mode, + enum dcss_hdr10_gamut *g, + enum dcss_hdr10_nonlinearity *nl) +{ + u8 vic; + + switch (colorspace) { + case DRM_MODE_COLORIMETRY_BT709_YCC: + case DRM_MODE_COLORIMETRY_XVYCC_709: + *g = G_REC709; + *nl = NL_REC709; + return; + case DRM_MODE_COLORIMETRY_SMPTE_170M_YCC: + case DRM_MODE_COLORIMETRY_XVYCC_601: + case DRM_MODE_COLORIMETRY_SYCC_601: + case DRM_MODE_COLORIMETRY_OPYCC_601: + *g = G_REC601_NTSC; + *nl = NL_REC709; + return; + case DRM_MODE_COLORIMETRY_BT2020_CYCC: + case DRM_MODE_COLORIMETRY_BT2020_RGB: + case DRM_MODE_COLORIMETRY_BT2020_YCC: + *g = G_REC2020; + *nl = NL_REC2084; + return; + case DRM_MODE_COLORIMETRY_OPRGB: + *g = G_ADOBE_ARGB; + *nl = NL_SRGB; + return; + default: + break; + } + + /* + * If we reached this point, it means the default colorimetry is used. + */ + + /* non-CEA mode, sRGB is used */ + vic = drm_match_cea_mode(mode); + if (vic == 0) { + *g = G_ADOBE_ARGB; + *nl = NL_SRGB; + return; + } + + if (mode->vdisplay == 480 || mode->vdisplay == 576 || + mode->vdisplay == 240 || mode->vdisplay == 288) { + *g = G_REC601_NTSC; + *nl = NL_REC709; + return; + } + + /* 2160p, 1080p, 720p */ + *g = G_REC709; + *nl = NL_REC709; +} + +#define YUV_MODE BIT(0) + +void dcss_kms_setup_opipe(struct drm_connector_state *conn_state) +{ + struct drm_crtc *crtc = conn_state->crtc; + struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, + base); + int mode_flags = crtc->state->adjusted_mode.private_flags; + enum hdmi_quantization_range qr; + + qr = drm_default_rgb_quant_range(&crtc->state->adjusted_mode); + + dcss_kms_setup_opipe_gamut(conn_state->colorspace, + &crtc->state->adjusted_mode, + &dcss_crtc->opipe_g, + &dcss_crtc->opipe_nl); + + dcss_crtc->opipe_pr = qr == HDMI_QUANTIZATION_RANGE_FULL ? PR_FULL : + PR_LIMITED; + + dcss_crtc->output_is_yuv = !!(mode_flags & YUV_MODE); +} + +static void dcss_kms_setup_output_pipe(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_display_info *di; + int i; + + for_each_new_connector_in_state(state, connector, conn_state, i) { + if (!conn_state->best_encoder) + continue; + + if (!conn_state->crtc->state->active || + !drm_atomic_crtc_needs_modeset(conn_state->crtc->state)) + continue; + + crtc = connector->state->crtc; + di = &connector->display_info; + + dcss_kms_setup_opipe(conn_state); + } +} + +static void dcss_drm_atomic_commit_tail(struct dcss_drm_commit *commit) +{ + struct drm_atomic_state *state = commit->state; + struct drm_device *drm = commit->drm; + struct dcss_kms_dev *kms = container_of(drm, struct dcss_kms_dev, base); + + drm_atomic_helper_wait_for_fences(drm, state, false); + + drm_atomic_helper_wait_for_dependencies(state); + + drm_atomic_helper_commit_modeset_disables(drm, state); + + dcss_kms_setup_output_pipe(state); + + drm_atomic_helper_commit_modeset_enables(drm, state); + + drm_atomic_helper_commit_planes(drm, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); + + drm_atomic_helper_commit_hw_done(state); + + drm_atomic_helper_wait_for_vblanks(drm, state); + + drm_atomic_helper_cleanup_planes(drm, state); + + drm_atomic_helper_commit_cleanup_done(state); + + drm_atomic_state_put(state); + + spin_lock(&kms->commit.wait.lock); + kms->commit.pending = false; + wake_up_all_locked(&kms->commit.wait); + spin_unlock(&kms->commit.wait.lock); + + kfree(commit); +} + +static void dcss_commit_work(struct work_struct *work) +{ + struct dcss_drm_commit *commit = container_of(work, + struct dcss_drm_commit, + work); + + dcss_drm_atomic_commit_tail(commit); +} + +static int dcss_drm_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *state, + bool nonblock) +{ + int ret; + struct dcss_kms_dev *kms = container_of(drm, struct dcss_kms_dev, base); + struct dcss_drm_commit *commit; + + if (state->async_update) { + ret = drm_atomic_helper_prepare_planes(drm, state); + if (ret) + return ret; + + drm_atomic_helper_async_commit(drm, state); + drm_atomic_helper_cleanup_planes(drm, state); + + return 0; + } + + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (!commit) + return -ENOMEM; + + commit->drm = drm; + commit->state = state; + + ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + goto err_free; + + INIT_WORK(&commit->work, dcss_commit_work); + + ret = drm_atomic_helper_prepare_planes(drm, state); + if (ret) + goto err_free; + + if (!nonblock) { + ret = drm_atomic_helper_wait_for_fences(drm, state, true); + if (ret) + goto err; + } + + spin_lock(&kms->commit.wait.lock); + ret = wait_event_interruptible_locked(kms->commit.wait, + !kms->commit.pending); + if (ret == 0) + kms->commit.pending = true; + spin_unlock(&kms->commit.wait.lock); + + if (ret) + goto err; + + ret = drm_atomic_helper_swap_state(state, true); + if (ret) + goto err; + + drm_atomic_state_get(state); + if (nonblock) + queue_work(kms->commit_wq, &commit->work); + else + dcss_drm_atomic_commit_tail(commit); + + return 0; + +err: + drm_atomic_helper_cleanup_planes(drm, state); + +err_free: + kfree(commit); + return ret; +} + +const struct drm_mode_config_funcs dcss_drm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .output_poll_changed = drm_fb_helper_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = dcss_drm_atomic_commit, +}; + +static struct drm_driver dcss_kms_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .fops = &dcss_cma_fops, + .name = "imx-dcss", + .desc = "i.MX8MQ Display Subsystem", + .date = "20190917", + .major = 1, + .minor = 0, + .patchlevel = 0, +}; + +static const struct drm_mode_config_helper_funcs dcss_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +static void dcss_kms_mode_config_init(struct dcss_kms_dev *kms) +{ + struct drm_mode_config *config = &kms->base.mode_config; + + drm_mode_config_init(&kms->base); + + config->min_width = 1; + config->min_height = 1; + config->max_width = 4096; + config->max_height = 4096; + config->allow_fb_modifiers = true; + config->normalize_zpos = true; + + config->funcs = &dcss_drm_mode_config_funcs; + config->helper_private = &dcss_mode_config_helpers; +} + +static const struct drm_encoder_funcs dcss_kms_simple_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int dcss_kms_setup_encoder(struct dcss_kms_dev *kms) +{ + struct drm_device *ddev = &kms->base; + struct drm_encoder *encoder = &kms->encoder; + struct drm_crtc *crtc = (struct drm_crtc *)&kms->crtc; + struct drm_panel *panel; + struct drm_bridge *bridge; + int ret; + + ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0, + &panel, &bridge); + if (ret) + return ret; + + if (!bridge) { + dev_err(ddev->dev, "No bridge found %d.\n", ret); + return -ENODEV; + } + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + ret = drm_encoder_init(&kms->base, encoder, + &dcss_kms_simple_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) { + dev_err(ddev->dev, "Failed initializing encoder %d.\n", ret); + return ret; + } + + return drm_bridge_attach(encoder, bridge, NULL); +} + +struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss, bool componentized) +{ + struct dcss_kms_dev *kms = kzalloc(sizeof(*kms), GFP_KERNEL); + struct drm_device *drm; + struct dcss_crtc *crtc; + int ret; + + if (!kms) + return ERR_PTR(-ENOMEM); + + drm = &kms->base; + crtc = &kms->crtc; + ret = drm_dev_init(drm, &dcss_kms_driver, dcss->dev); + if (ret) + goto free_kms; + + drm->dev_private = dcss; + + dcss_kms_mode_config_init(kms); + + ret = drm_vblank_init(drm, 1); + if (ret) + goto cleanup_mode_config; + + drm->irq_enabled = true; + + ret = dcss_crtc_init(crtc, drm); + if (ret) + goto cleanup_mode_config; + + kms->commit_wq = alloc_ordered_workqueue("dcss_nonblock_commit_wq", 0); + if (!kms->commit_wq) { + ret = -ENOMEM; + goto cleanup_crtc; + } + + init_waitqueue_head(&kms->commit.wait); + + if (componentized) + ret = component_bind_all(dcss->dev, kms); + else + ret = dcss_kms_setup_encoder(kms); + + if (ret) + goto cleanup_wq; + + drm_mode_config_reset(drm); + + dcss_crtc_attach_color_mgmt_properties(crtc); + + drm_kms_helper_poll_init(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + goto cleanup_wq; + + drm_fbdev_generic_setup(drm, 32); + + return kms; + +cleanup_wq: + drm_kms_helper_poll_fini(drm); + destroy_workqueue(kms->commit_wq); + +cleanup_crtc: + dcss_crtc_deinit(crtc, drm); + +cleanup_mode_config: + drm_mode_config_cleanup(drm); + +free_kms: + kfree(kms); + return ERR_PTR(ret); +} + +void dcss_kms_detach(struct dcss_kms_dev *kms, bool componentized) +{ + struct drm_device *drm = &kms->base; + struct dcss_dev *dcss = drm->dev_private; + + drm_dev_unregister(drm); + drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); + drm_crtc_vblank_off(&kms->crtc.base); + drm->irq_enabled = false; + drm_mode_config_cleanup(drm); + destroy_workqueue(kms->commit_wq); + dcss_crtc_deinit(&kms->crtc, drm); + if (componentized) + component_unbind_all(dcss->dev, drm); + drm->dev_private = NULL; + drm_dev_put(drm); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.h b/drivers/gpu/drm/imx/dcss/dcss-kms.h new file mode 100644 index 000000000000..0c4c49ce2e99 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 NXP. + */ + +#ifndef _DCSS_KMS_H_ +#define _DCSS_KMS_H_ + +#include <drm/drm_encoder.h> + +struct dcss_plane { + struct drm_plane base; + + uint64_t dtrc_table_ofs_val; + struct drm_property *dtrc_table_ofs_prop; + + int ch_num; + + enum drm_plane_type type; + bool use_dtrc; +}; + +struct dcss_crtc { + struct drm_crtc base; + struct drm_crtc_state *state; + + struct dcss_plane *plane[3]; + + int irq; + bool irq_enabled; + + struct completion en_completion; + struct completion dis_completion; + + bool output_is_yuv; + enum dcss_hdr10_nonlinearity opipe_nl; + enum dcss_hdr10_gamut opipe_g; + enum dcss_hdr10_pixel_range opipe_pr; +}; + +struct commit { + wait_queue_head_t wait; + bool pending; +}; + +struct dcss_kms_dev { + struct drm_device base; + struct dcss_crtc crtc; + struct drm_encoder encoder; + struct workqueue_struct *commit_wq; + struct commit commit; +}; + +struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss, bool componentized); +void dcss_kms_detach(struct dcss_kms_dev *kms, bool componentized); +int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm); +void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm); +struct dcss_plane *dcss_plane_init(struct drm_device *drm, + unsigned int possible_crtcs, + enum drm_plane_type type, + unsigned int zpos); +void dcss_crtc_attach_color_mgmt_properties(struct dcss_crtc *crtc); + +#endif diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c new file mode 100644 index 000000000000..80e8f91c1d0c --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c @@ -0,0 +1,673 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <linux/dma-buf.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_cma_helper.h> + +#include "dcss-dev.h" +#include "dcss-kms.h" + +static const u32 dcss_graphics_formats[] = { + /* RGB */ + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_RGBX1010102, + DRM_FORMAT_BGRX1010102, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, +}; + +static const u32 dcss_video_formats[] = { + /* YUV444 */ + DRM_FORMAT_AYUV, + + /* YUV422 */ + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + + /* YUV420 */ + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV12_10LE40, +}; + +static const u64 dcss_video_format_modifiers[] = { + DRM_FORMAT_MOD_VSI_G1_TILED, + DRM_FORMAT_MOD_VSI_G2_TILED, + DRM_FORMAT_MOD_VSI_G2_TILED_COMPRESSED, + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, +}; + +static const u64 dcss_graphics_format_modifiers[] = { + DRM_FORMAT_MOD_VIVANTE_TILED, + DRM_FORMAT_MOD_VIVANTE_SUPER_TILED, + DRM_FORMAT_MOD_VIVANTE_SUPER_TILED_FC, + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, +}; + +static inline struct dcss_plane *to_dcss_plane(struct drm_plane *p) +{ + return container_of(p, struct dcss_plane, base); +} + +static inline bool dcss_plane_fb_is_linear(const struct drm_framebuffer *fb) +{ + return ((fb->flags & DRM_MODE_FB_MODIFIERS) == 0) || + ((fb->flags & DRM_MODE_FB_MODIFIERS) != 0 && + fb->modifier == DRM_FORMAT_MOD_LINEAR); +} + +static void dcss_plane_destroy(struct drm_plane *plane) +{ + struct dcss_plane *dcss_plane = container_of(plane, struct dcss_plane, + base); + + drm_plane_cleanup(plane); + kfree(dcss_plane); +} + +static int dcss_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t val) +{ + struct dcss_plane *dcss_plane = to_dcss_plane(plane); + + if (property == dcss_plane->dtrc_table_ofs_prop) + dcss_plane->dtrc_table_ofs_val = val; + else + return -EINVAL; + + return 0; +} + +static int dcss_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct dcss_plane *dcss_plane = to_dcss_plane(plane); + + if (property == dcss_plane->dtrc_table_ofs_prop) + *val = dcss_plane->dtrc_table_ofs_val; + else + return -EINVAL; + + return 0; +} + +static bool dcss_plane_format_mod_supported(struct drm_plane *plane, + u32 format, + u64 modifier) +{ + switch (plane->type) { + case DRM_PLANE_TYPE_PRIMARY: + switch (format) { + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB2101010: + return modifier == DRM_FORMAT_MOD_LINEAR || + modifier == DRM_FORMAT_MOD_VIVANTE_TILED || + modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED || + modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED_FC; + default: + return modifier == DRM_FORMAT_MOD_LINEAR; + } + break; + case DRM_PLANE_TYPE_OVERLAY: + switch (format) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV12_10LE40: + return modifier == DRM_FORMAT_MOD_LINEAR || + modifier == DRM_FORMAT_MOD_VSI_G1_TILED || + modifier == DRM_FORMAT_MOD_VSI_G2_TILED || + modifier == DRM_FORMAT_MOD_VSI_G2_TILED_COMPRESSED; + default: + return modifier == DRM_FORMAT_MOD_LINEAR; + } + break; + default: + return false; + } + + return false; +} + +static const struct drm_plane_funcs dcss_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = dcss_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_set_property = dcss_plane_atomic_set_property, + .atomic_get_property = dcss_plane_atomic_get_property, + .format_mod_supported = dcss_plane_format_mod_supported, +}; + +static bool dcss_plane_can_rotate(const struct drm_format_info *format, + bool mod_present, u64 modifier, + unsigned int rotation) +{ + bool linear_format = !mod_present || + (mod_present && modifier == DRM_FORMAT_MOD_LINEAR); + u32 supported_rotation = DRM_MODE_ROTATE_0; + + if (!format->is_yuv && linear_format) + supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | + DRM_MODE_REFLECT_MASK; + else if (!format->is_yuv && + modifier == DRM_FORMAT_MOD_VIVANTE_TILED) + supported_rotation = DRM_MODE_ROTATE_MASK | + DRM_MODE_REFLECT_MASK; + else if (format->is_yuv && linear_format && + (format->format == DRM_FORMAT_NV12 || + format->format == DRM_FORMAT_NV21)) + supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | + DRM_MODE_REFLECT_MASK; + else if (format->is_yuv && linear_format && + format->format == DRM_FORMAT_NV12_10LE40) + supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y; + + return !!(rotation & supported_rotation); +} + +static bool dcss_plane_is_source_size_allowed(u16 src_w, u16 src_h, u32 pix_fmt) +{ + if (src_w < 64 && + (pix_fmt == DRM_FORMAT_NV12 || pix_fmt == DRM_FORMAT_NV21 || + pix_fmt == DRM_FORMAT_NV12_10LE40)) + return false; + else if (src_w < 32 && + (pix_fmt == DRM_FORMAT_UYVY || pix_fmt == DRM_FORMAT_VYUY || + pix_fmt == DRM_FORMAT_YUYV || pix_fmt == DRM_FORMAT_YVYU)) + return false; + + return src_w >= 16 && src_h >= 8; +} + +static inline bool dcss_plane_use_dtrc(struct drm_framebuffer *fb, + enum drm_plane_type type) +{ + u64 pix_format = fb->format->format; + + return !dcss_plane_fb_is_linear(fb) && + type == DRM_PLANE_TYPE_OVERLAY && + (pix_format == DRM_FORMAT_NV12 || + pix_format == DRM_FORMAT_NV21 || + pix_format == DRM_FORMAT_NV12_10LE40); +} + +static int dcss_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct dcss_plane *dcss_plane = to_dcss_plane(plane); + struct dcss_dev *dcss = plane->dev->dev_private; + struct drm_framebuffer *fb = state->fb; + bool is_primary_plane = plane->type == DRM_PLANE_TYPE_PRIMARY; + struct drm_gem_cma_object *cma_obj; + struct drm_crtc_state *crtc_state; + int hdisplay, vdisplay; + int min, max; + int ret; + + if (!fb || !state->crtc) + return 0; + + cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + WARN_ON(!cma_obj); + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, + state->crtc); + + hdisplay = crtc_state->adjusted_mode.hdisplay; + vdisplay = crtc_state->adjusted_mode.vdisplay; + + if (!dcss_plane_is_source_size_allowed(state->src_w >> 16, + state->src_h >> 16, + fb->format->format)) { + DRM_DEBUG_KMS("Source plane size is not allowed!\n"); + return -EINVAL; + } + + dcss_scaler_get_min_max_ratios(dcss->scaler, dcss_plane->ch_num, + &min, &max); + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + min, max, !is_primary_plane, + false); + if (ret) + return ret; + + if (!state->visible) + return 0; + + if (!dcss_plane_can_rotate(fb->format, + !!(fb->flags & DRM_MODE_FB_MODIFIERS), + fb->modifier, + state->rotation)) { + DRM_DEBUG_KMS("requested rotation is not allowed!\n"); + return -EINVAL; + } + + if ((fb->flags & DRM_MODE_FB_MODIFIERS) && + !plane->funcs->format_mod_supported(plane, + fb->format->format, + fb->modifier)) { + DRM_DEBUG_KMS("Invalid modifier: %llx", fb->modifier); + return -EINVAL; + } + + dcss_plane->use_dtrc = dcss_plane_use_dtrc(fb, plane->type); + + return 0; +} + +static struct drm_gem_object *dcss_plane_gem_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj; + + if (IS_ERR(dma_buf)) + return ERR_CAST(dma_buf); + + mutex_lock(&dev->object_name_lock); + + obj = dev->driver->gem_prime_import(dev, dma_buf); + + mutex_unlock(&dev->object_name_lock); + + return obj; +} + +static void dcss_plane_set_primary_base(struct dcss_plane *dcss_plane, + u32 baddr) +{ + struct drm_plane *plane = &dcss_plane->base; + struct dcss_dev *dcss = plane->dev->dev_private; + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + struct dma_buf *dma_buf = cma_obj->base.dma_buf; + struct drm_gem_object *gem_obj; + dma_addr_t caddr; + bool compressed = true; + u32 compressed_format = _VIV_CFMT_ARGB8; + _VIV_VIDMEM_METADATA *mdata; + + if (dcss_plane_fb_is_linear(fb) || + ((fb->flags & DRM_MODE_FB_MODIFIERS) && + (fb->modifier == DRM_FORMAT_MOD_VIVANTE_TILED || + fb->modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED))) { + dcss_dec400d_bypass(dcss->dec400d); + return; + } + + if (!dma_buf) { + caddr = cma_obj->paddr + ALIGN(fb->height, 64) * fb->pitches[0]; + } else { + mdata = dma_buf->priv; + if (!mdata || mdata->magic != VIV_VIDMEM_METADATA_MAGIC) + return; + + gem_obj = dcss_plane_gem_import(plane->dev, mdata->ts_dma_buf); + if (IS_ERR(gem_obj)) + return; + + caddr = to_drm_gem_cma_obj(gem_obj)->paddr; + + /* release gem_obj */ + drm_gem_object_put_unlocked(gem_obj); + + dcss_dec400d_fast_clear_config(dcss->dec400d, mdata->fc_value, + mdata->fc_enabled); + + compressed = !!mdata->compressed; + compressed_format = mdata->compress_format; + } + + dcss_dec400d_read_config(dcss->dec400d, 0, compressed, + compressed_format); + dcss_dec400d_addr_set(dcss->dec400d, baddr, caddr); +} + +static void dcss_plane_set_dtrc_base(struct dcss_plane *dcss_plane, + u32 p1_ba, u32 p2_ba) +{ + struct drm_plane *plane = &dcss_plane->base; + struct dcss_dev *dcss = plane->dev->dev_private; + + if (!dcss_plane->use_dtrc) { + dcss_dtrc_bypass(dcss->dtrc, dcss_plane->ch_num); + return; + } + + dcss_dtrc_addr_set(dcss->dtrc, dcss_plane->ch_num, + p1_ba, p2_ba, dcss_plane->dtrc_table_ofs_val); +} + +static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane) +{ + struct drm_plane *plane = &dcss_plane->base; + struct drm_plane_state *state = plane->state; + struct dcss_dev *dcss = plane->dev->dev_private; + struct drm_framebuffer *fb = state->fb; + const struct drm_format_info *format = fb->format; + struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + unsigned long p1_ba = 0, p2_ba = 0; + u16 x1, y1; + + x1 = state->src.x1 >> 16; + y1 = state->src.y1 >> 16; + + if (!format->is_yuv || + format->format == DRM_FORMAT_NV12 || + format->format == DRM_FORMAT_NV21) + p1_ba = cma_obj->paddr + fb->offsets[0] + + fb->pitches[0] * y1 + + format->char_per_block[0] * x1; + else if (format->format == DRM_FORMAT_NV12_10LE40) + p1_ba = cma_obj->paddr + fb->offsets[0] + + fb->pitches[0] * y1 + + format->char_per_block[0] * (x1 >> 2); + else if (format->format == DRM_FORMAT_UYVY || + format->format == DRM_FORMAT_VYUY || + format->format == DRM_FORMAT_YUYV || + format->format == DRM_FORMAT_YVYU) + p1_ba = cma_obj->paddr + fb->offsets[0] + + fb->pitches[0] * y1 + + 2 * format->char_per_block[0] * (x1 >> 1); + + if (format->format == DRM_FORMAT_NV12 || + format->format == DRM_FORMAT_NV21) + p2_ba = cma_obj->paddr + fb->offsets[1] + + (((fb->pitches[1] >> 1) * (y1 >> 1) + + (x1 >> 1)) << 1); + else if (format->format == DRM_FORMAT_NV12_10LE40) + p2_ba = cma_obj->paddr + fb->offsets[1] + + (((fb->pitches[1] >> 1) * (y1 >> 1)) << 1) + + format->char_per_block[1] * (x1 >> 2); + + dcss_dpr_addr_set(dcss->dpr, dcss_plane->ch_num, p1_ba, p2_ba, + fb->pitches[0]); + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + dcss_plane_set_primary_base(dcss_plane, p1_ba); + else + dcss_plane_set_dtrc_base(dcss_plane, + cma_obj->paddr + fb->offsets[0], + cma_obj->paddr + fb->offsets[1]); +} + +static bool dcss_plane_needs_setup(struct drm_plane_state *state, + struct drm_plane_state *old_state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_framebuffer *old_fb = old_state->fb; + + return state->crtc_x != old_state->crtc_x || + state->crtc_y != old_state->crtc_y || + state->crtc_w != old_state->crtc_w || + state->crtc_h != old_state->crtc_h || + state->src_x != old_state->src_x || + state->src_y != old_state->src_y || + state->src_w != old_state->src_w || + state->src_h != old_state->src_h || + fb->format->format != old_fb->format->format || + fb->modifier != old_fb->modifier || + state->rotation != old_state->rotation; +} + +static void dcss_plane_setup_hdr10_pipes(struct drm_plane *plane) +{ + struct dcss_dev *dcss = plane->dev->dev_private; + struct dcss_plane *dcss_plane = to_dcss_plane(plane); + struct drm_plane_state *state = plane->state; + struct drm_crtc *crtc = state->crtc; + struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, + base); + struct drm_framebuffer *fb = state->fb; + struct dcss_hdr10_pipe_cfg ipipe_cfg, opipe_cfg; + + opipe_cfg.is_yuv = dcss_crtc->output_is_yuv; + opipe_cfg.g = dcss_crtc->opipe_g; + opipe_cfg.nl = dcss_crtc->opipe_nl; + opipe_cfg.pr = dcss_crtc->opipe_pr; + + ipipe_cfg.is_yuv = fb->format->is_yuv; + + if (!fb->format->is_yuv) { + ipipe_cfg.nl = NL_SRGB; + ipipe_cfg.pr = PR_FULL; + ipipe_cfg.g = G_ADOBE_ARGB; + goto setup; + } + + switch (state->color_encoding) { + case DRM_COLOR_YCBCR_BT709: + ipipe_cfg.nl = NL_REC709; + ipipe_cfg.g = G_REC709; + break; + + case DRM_COLOR_YCBCR_BT2020: + ipipe_cfg.nl = NL_REC2084; + ipipe_cfg.g = G_REC2020; + break; + + default: + ipipe_cfg.nl = NL_REC709; + ipipe_cfg.g = G_REC601_PAL; + break; + } + + ipipe_cfg.pr = state->color_range; + +setup: + dcss_hdr10_setup(dcss->hdr10, dcss_plane->ch_num, + &ipipe_cfg, &opipe_cfg); +} + +static void dcss_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct dcss_plane *dcss_plane = to_dcss_plane(plane); + struct dcss_dev *dcss = plane->dev->dev_private; + struct drm_framebuffer *fb = state->fb; + u32 pixel_format; + struct drm_crtc_state *crtc_state; + bool modifiers_present; + u32 src_w, src_h, dst_w, dst_h; + struct drm_rect src, dst; + bool enable = true; + + if (!fb || !state->crtc || !state->visible) + return; + + pixel_format = fb->format->format; + crtc_state = state->crtc->state; + modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS); + + if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) && + !dcss_plane_needs_setup(state, old_state) && + !dcss_dtg_global_alpha_changed(dcss->dtg, dcss_plane->ch_num, + state->alpha >> 8)) { + dcss_plane_atomic_set_base(dcss_plane); + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + dcss_dec400d_shadow_trig(dcss->dec400d); + return; + } + + src = plane->state->src; + dst = plane->state->dst; + + /* + * The width and height after clipping. + */ + src_w = drm_rect_width(&src) >> 16; + src_h = drm_rect_height(&src) >> 16; + dst_w = drm_rect_width(&dst); + dst_h = drm_rect_height(&dst); + + dcss_dpr_format_set(dcss->dpr, dcss_plane->ch_num, state->fb->format, + modifiers_present ? fb->modifier : + DRM_FORMAT_MOD_LINEAR); + + if (dcss_plane->use_dtrc) { + u32 dtrc_w, dtrc_h; + + dcss_dtrc_set_res(dcss->dtrc, dcss_plane->ch_num, state, + &dtrc_w, &dtrc_h); + dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, dtrc_w, dtrc_h); + } else { + dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, src_w, src_h); + } + + dcss_dpr_set_rotation(dcss->dpr, dcss_plane->ch_num, + state->rotation); + + dcss_plane_atomic_set_base(dcss_plane); + + dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num, + state->fb->format, src_w, src_h, + dst_w, dst_h, + drm_mode_vrefresh(&crtc_state->mode)); + + dcss_plane_setup_hdr10_pipes(plane); + + dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, + dst.x1, dst.y1, dst_w, dst_h); + dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num, + fb->format, state->alpha >> 8); + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + dcss_dec400d_enable(dcss->dec400d); + else if (dcss_plane->use_dtrc) + dcss_dtrc_enable(dcss->dtrc, dcss_plane->ch_num, true); + + if (!dcss_plane->ch_num && (state->alpha >> 8) == 0) + enable = false; + + dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable); + dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable); + + if (!enable) + dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, + 0, 0, 0, 0); + + dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable); +} + +static void dcss_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct dcss_plane *dcss_plane = to_dcss_plane(plane); + struct dcss_dev *dcss = plane->dev->dev_private; + + if (dcss_plane->use_dtrc) + dcss_dtrc_enable(dcss->dtrc, dcss_plane->ch_num, false); + dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false); + dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false); + dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0); + dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false); +} + +static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, + .atomic_check = dcss_plane_atomic_check, + .atomic_update = dcss_plane_atomic_update, + .atomic_disable = dcss_plane_atomic_disable, +}; + +struct dcss_plane *dcss_plane_init(struct drm_device *drm, + unsigned int possible_crtcs, + enum drm_plane_type type, + unsigned int zpos) +{ + struct dcss_plane *dcss_plane; + const u64 *format_modifiers = dcss_video_format_modifiers; + const u32 *formats = dcss_video_formats; + u32 formats_size = ARRAY_SIZE(dcss_video_formats); + struct drm_property *prop; + int ret; + + if (zpos > 2) + return ERR_PTR(-EINVAL); + + dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL); + if (!dcss_plane) { + DRM_ERROR("failed to allocate plane\n"); + return ERR_PTR(-ENOMEM); + } + + if (type == DRM_PLANE_TYPE_PRIMARY) { + formats = dcss_graphics_formats; + formats_size = ARRAY_SIZE(dcss_graphics_formats); + format_modifiers = dcss_graphics_format_modifiers; + } + + ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs, + &dcss_plane_funcs, formats, + formats_size, + format_modifiers, type, NULL); + if (ret) { + DRM_ERROR("failed to initialize plane\n"); + kfree(dcss_plane); + return ERR_PTR(ret); + } + + drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs); + + ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos); + if (ret) + return ERR_PTR(ret); + + drm_plane_create_rotation_property(&dcss_plane->base, + DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y); + + dcss_plane->ch_num = 2 - zpos; + dcss_plane->type = type; + + if (type == DRM_PLANE_TYPE_PRIMARY) + return dcss_plane; + + prop = drm_property_create_range(drm, 0, "dtrc_table_ofs", + 0, ULLONG_MAX); + if (!prop) { + DRM_ERROR("cannot create dtrc_table_ofs property\n"); + return ERR_PTR(-ENOMEM); + } + + dcss_plane->dtrc_table_ofs_prop = prop; + drm_object_attach_property(&dcss_plane->base.base, prop, 0); + + return dcss_plane; +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-rdsrc.c b/drivers/gpu/drm/imx/dcss/dcss-rdsrc.c new file mode 100644 index 000000000000..ef695e584acf --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-rdsrc.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/seq_file.h> + +#include "dcss-dev.h" + +#define DCSS_RDSRC_CTRL_STATUS 0x00 +#define RDSRC_RD_ERR BIT(31) +#define RDSRC_FRAME_COMP BIT(30) +#define RDSRC_FIFO_SIZE_POS 16 +#define RDSRC_FIFO_SIZE_MASK GENMASK(22, 16) +#define RDSRC_RD_ERR_EN BIT(15) +#define RDSRC_FRAME_COMP_EN BIT(14) +#define RDSRC_P_SIZE_POS 7 +#define RDSRC_P_SIZE_MASK GENMASK(9, 7) +#define RDSRC_T_SIZE_POS 5 +#define RDSRC_T_SIZE_MASK GENMASK(6, 5) +#define RDSRC_BPP_POS 2 +#define RDSRC_BPP_MASK GENMASK(4, 2) +#define RDSRC_ENABLE BIT(0) +#define DCSS_RDSRC_BASE_ADDR 0x10 +#define DCSS_RDSRC_PITCH 0x14 +#define DCSS_RDSRC_WIDTH 0x18 +#define DCSS_RDSRC_HEIGHT 0x1C + +struct dcss_rdsrc { + struct device *dev; + + void __iomem *base_reg; + u32 base_ofs; + + struct dcss_ctxld *ctxld; + u32 ctx_id; + + u32 buf_addr; + + u32 ctrl_status; +}; + +static void dcss_rdsrc_write(struct dcss_rdsrc *rdsrc, u32 val, u32 ofs) +{ + dcss_ctxld_write(rdsrc->ctxld, rdsrc->ctx_id, val, + rdsrc->base_ofs + ofs); +} + +int dcss_rdsrc_init(struct dcss_dev *dcss, unsigned long rdsrc_base) +{ + struct dcss_rdsrc *rdsrc; + + rdsrc = devm_kzalloc(dcss->dev, sizeof(*rdsrc), GFP_KERNEL); + if (!rdsrc) + return -ENOMEM; + + rdsrc->base_reg = devm_ioremap(dcss->dev, rdsrc_base, SZ_4K); + if (!rdsrc->base_reg) { + dev_err(dcss->dev, "rdsrc: unable to remap base\n"); + devm_kfree(dcss->dev, rdsrc); + return -ENOMEM; + } + + dcss->rdsrc = rdsrc; + + rdsrc->dev = dcss->dev; + rdsrc->base_ofs = rdsrc_base; + rdsrc->ctxld = dcss->ctxld; + rdsrc->ctx_id = CTX_SB_HP; + + return 0; +} + +void dcss_rdsrc_exit(struct dcss_rdsrc *rdsrc) +{ + devm_iounmap(rdsrc->dev, rdsrc->base_reg); + devm_kfree(rdsrc->dev, rdsrc); +} + +void dcss_rdsrc_setup(struct dcss_rdsrc *rdsrc, u32 pix_format, u32 dst_xres, + u32 dst_yres, u32 base_addr) +{ + u32 buf_size, pitch, bpp; + + /* since the scaler output is YUV444, the RDSRC output has to match */ + bpp = 4; + + rdsrc->ctrl_status = FIFO_512 << RDSRC_FIFO_SIZE_POS; + rdsrc->ctrl_status |= PSIZE_256 << RDSRC_P_SIZE_POS; + rdsrc->ctrl_status |= TSIZE_256 << RDSRC_T_SIZE_POS; + rdsrc->ctrl_status |= BPP_32_10BIT_OUTPUT << RDSRC_BPP_POS; + + buf_size = dst_xres * dst_yres * bpp; + pitch = dst_xres * bpp; + + rdsrc->buf_addr = base_addr; + + dcss_rdsrc_write(rdsrc, rdsrc->buf_addr, DCSS_RDSRC_BASE_ADDR); + dcss_rdsrc_write(rdsrc, pitch, DCSS_RDSRC_PITCH); + dcss_rdsrc_write(rdsrc, dst_xres, DCSS_RDSRC_WIDTH); + dcss_rdsrc_write(rdsrc, dst_yres, DCSS_RDSRC_HEIGHT); +} + +void dcss_rdsrc_enable(struct dcss_rdsrc *rdsrc) +{ + dcss_rdsrc_write(rdsrc, rdsrc->ctrl_status, DCSS_RDSRC_CTRL_STATUS); +} + +void dcss_rdsrc_disable(struct dcss_rdsrc *rdsrc) +{ + /* RDSRC is turned off by setting the width and height to 0 */ + dcss_rdsrc_write(rdsrc, 0, DCSS_RDSRC_WIDTH); + dcss_rdsrc_write(rdsrc, 0, DCSS_RDSRC_HEIGHT); + + dcss_rdsrc_write(rdsrc, rdsrc->ctrl_status, DCSS_RDSRC_CTRL_STATUS); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c new file mode 100644 index 000000000000..d1ff5443c74d --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c @@ -0,0 +1,911 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + * + * Scaling algorithms were contributed by Dzung Hoang <dzung.hoang@nxp.com> + */ + +#include <linux/device.h> + +#include "dcss-dev.h" + +#define DCSS_SCALER_CTRL 0x00 +#define SCALER_EN BIT(0) +#define REPEAT_EN BIT(4) +#define SCALE2MEM_EN BIT(8) +#define MEM2OFIFO_EN BIT(12) +#define DCSS_SCALER_OFIFO_CTRL 0x04 +#define OFIFO_LOW_THRES_POS 0 +#define OFIFO_LOW_THRES_MASK GENMASK(9, 0) +#define OFIFO_HIGH_THRES_POS 16 +#define OFIFO_HIGH_THRES_MASK GENMASK(25, 16) +#define UNDERRUN_DETECT_CLR BIT(26) +#define LOW_THRES_DETECT_CLR BIT(27) +#define HIGH_THRES_DETECT_CLR BIT(28) +#define UNDERRUN_DETECT_EN BIT(29) +#define LOW_THRES_DETECT_EN BIT(30) +#define HIGH_THRES_DETECT_EN BIT(31) +#define DCSS_SCALER_SDATA_CTRL 0x08 +#define YUV_EN BIT(0) +#define RTRAM_8LINES BIT(1) +#define Y_UV_BYTE_SWAP BIT(4) +#define A2R10G10B10_FORMAT_POS 8 +#define A2R10G10B10_FORMAT_MASK GENMASK(11, 8) +#define DCSS_SCALER_BIT_DEPTH 0x0C +#define LUM_BIT_DEPTH_POS 0 +#define LUM_BIT_DEPTH_MASK GENMASK(1, 0) +#define CHR_BIT_DEPTH_POS 4 +#define CHR_BIT_DEPTH_MASK GENMASK(5, 4) +#define DCSS_SCALER_SRC_FORMAT 0x10 +#define DCSS_SCALER_DST_FORMAT 0x14 +#define FORMAT_MASK GENMASK(1, 0) +#define DCSS_SCALER_SRC_LUM_RES 0x18 +#define DCSS_SCALER_SRC_CHR_RES 0x1C +#define DCSS_SCALER_DST_LUM_RES 0x20 +#define DCSS_SCALER_DST_CHR_RES 0x24 +#define WIDTH_POS 0 +#define WIDTH_MASK GENMASK(11, 0) +#define HEIGHT_POS 16 +#define HEIGHT_MASK GENMASK(27, 16) +#define DCSS_SCALER_V_LUM_START 0x48 +#define V_START_MASK GENMASK(15, 0) +#define DCSS_SCALER_V_LUM_INC 0x4C +#define V_INC_MASK GENMASK(15, 0) +#define DCSS_SCALER_H_LUM_START 0x50 +#define H_START_MASK GENMASK(18, 0) +#define DCSS_SCALER_H_LUM_INC 0x54 +#define H_INC_MASK GENMASK(15, 0) +#define DCSS_SCALER_V_CHR_START 0x58 +#define DCSS_SCALER_V_CHR_INC 0x5C +#define DCSS_SCALER_H_CHR_START 0x60 +#define DCSS_SCALER_H_CHR_INC 0x64 +#define DCSS_SCALER_COEF_VLUM 0x80 +#define DCSS_SCALER_COEF_HLUM 0x140 +#define DCSS_SCALER_COEF_VCHR 0x200 +#define DCSS_SCALER_COEF_HCHR 0x300 + +struct dcss_scaler_ch { + void __iomem *base_reg; + u32 base_ofs; + struct dcss_scaler *scl; + + u32 sdata_ctrl; + u32 scaler_ctrl; + + bool scaler_ctrl_chgd; + + u32 c_vstart; + u32 c_hstart; + + int ch_num; +}; + +struct dcss_scaler { + struct device *dev; + + struct dcss_ctxld *ctxld; + u32 ctx_id; + + struct dcss_scaler_ch ch[3]; + + struct dcss_wrscl *wrscl; + struct dcss_rdsrc *rdsrc; + int ch_using_wrscl; +}; + +/* scaler coefficients generator */ +#define PSC_FRAC_BITS 30 +#define PSC_FRAC_SCALE BIT(PSC_FRAC_BITS) +#define PSC_BITS_FOR_PHASE 4 +#define PSC_NUM_PHASES 16 +#define PSC_STORED_PHASES (PSC_NUM_PHASES / 2 + 1) +#define PSC_NUM_TAPS 7 +#define PSC_NUM_TAPS_RGBA 5 +#define PSC_COEFF_PRECISION 10 +#define PSC_PHASE_FRACTION_BITS 13 +#define PSC_PHASE_MASK (PSC_NUM_PHASES - 1) +#define PSC_Q_FRACTION 19 +#define PSC_Q_ROUND_OFFSET (1 << (PSC_Q_FRACTION - 1)) + +/** + * mult_q() - Performs fixed-point multiplication. + * @A: multiplier + * @B: multiplicand + */ +static int mult_q(int A, int B) +{ + int result; + s64 temp; + + temp = (int64_t)A * (int64_t)B; + temp += PSC_Q_ROUND_OFFSET; + result = (int)(temp >> PSC_Q_FRACTION); + return result; +} + +/** + * div_q() - Performs fixed-point division. + * @A: dividend + * @B: divisor + */ +static int div_q(int A, int B) +{ + int result; + s64 temp; + + temp = (int64_t)A << PSC_Q_FRACTION; + if ((temp >= 0 && B >= 0) || (temp < 0 && B < 0)) + temp += B / 2; + else + temp -= B / 2; + + result = (int)(temp / B); + return result; +} + +/** + * exp_approx_q() - Compute approximation to exp(x) function using Taylor + * series. + * @x: fixed-point argument of exp function + */ +static int exp_approx_q(int x) +{ + int sum = 1 << PSC_Q_FRACTION; + int term = 1 << PSC_Q_FRACTION; + + term = mult_q(term, div_q(x, 1 << PSC_Q_FRACTION)); + sum += term; + term = mult_q(term, div_q(x, 2 << PSC_Q_FRACTION)); + sum += term; + term = mult_q(term, div_q(x, 3 << PSC_Q_FRACTION)); + sum += term; + term = mult_q(term, div_q(x, 4 << PSC_Q_FRACTION)); + sum += term; + + return sum; +} + +/** + * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. + * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] + * @use_5_taps: indicates whether to use 5 taps or 7 taps + * @coef: output filter coefficients + */ +static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps, + bool phase0_identity, + int coef[][PSC_NUM_TAPS]) +{ + int sigma_q, g0_q, g1_q, g2_q; + int tap_cnt1, tap_cnt2, tap_idx, phase_cnt; + int mid; + int phase; + int i; + int taps; + + if (use_5_taps) + for (phase = 0; phase < PSC_STORED_PHASES; phase++) { + coef[phase][0] = 0; + coef[phase][PSC_NUM_TAPS - 1] = 0; + } + + /* seed coefficient scanner */ + taps = use_5_taps ? PSC_NUM_TAPS_RGBA : PSC_NUM_TAPS; + mid = (PSC_NUM_PHASES * taps) / 2 - 1; + phase_cnt = (PSC_NUM_PHASES * (PSC_NUM_TAPS + 1)) / 2; + tap_cnt1 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2; + tap_cnt2 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2; + + /* seed gaussian filter generator */ + sigma_q = div_q(PSC_Q_ROUND_OFFSET, fc_q); + g0_q = 1 << PSC_Q_FRACTION; + g1_q = exp_approx_q(div_q(-PSC_Q_ROUND_OFFSET, + mult_q(sigma_q, sigma_q))); + g2_q = mult_q(g1_q, g1_q); + coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = g0_q; + + for (i = 0; i < mid; i++) { + phase_cnt++; + tap_cnt1--; + tap_cnt2++; + + g0_q = mult_q(g0_q, g1_q); + g1_q = mult_q(g1_q, g2_q); + + if ((phase_cnt & PSC_PHASE_MASK) <= 8) { + tap_idx = tap_cnt1 >> PSC_BITS_FOR_PHASE; + coef[phase_cnt & PSC_PHASE_MASK][tap_idx] = g0_q; + } + if (((-phase_cnt) & PSC_PHASE_MASK) <= 8) { + tap_idx = tap_cnt2 >> PSC_BITS_FOR_PHASE; + coef[(-phase_cnt) & PSC_PHASE_MASK][tap_idx] = g0_q; + } + } + + phase_cnt++; + tap_cnt1--; + coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = 0; + + /* override phase 0 with identity filter if specified */ + if (phase0_identity) + for (i = 0; i < PSC_NUM_TAPS; i++) + coef[0][i] = i == (PSC_NUM_TAPS >> 1) ? + (1 << PSC_COEFF_PRECISION) : 0; + + /* normalize coef */ + for (phase = 0; phase < PSC_STORED_PHASES; phase++) { + int sum = 0; + s64 ll_temp; + + for (i = 0; i < PSC_NUM_TAPS; i++) + sum += coef[phase][i]; + for (i = 0; i < PSC_NUM_TAPS; i++) { + ll_temp = coef[phase][i]; + ll_temp <<= PSC_COEFF_PRECISION; + ll_temp += sum >> 1; + ll_temp /= sum; + coef[phase][i] = (int)ll_temp; + } + } +} + +/** + * dcss_scaler_filter_design() - Compute filter coefficients using + * Gaussian filter. + * @src_length: length of input + * @dst_length: length of output + * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps + * @coef: output coefficients + */ +static void dcss_scaler_filter_design(int src_length, int dst_length, + bool use_5_taps, bool phase0_identity, + int coef[][PSC_NUM_TAPS]) +{ + int fc_q; + + /* compute cutoff frequency */ + if (dst_length >= src_length) + fc_q = div_q(1, PSC_NUM_PHASES); + else + fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES); + + /* compute gaussian filter coefficients */ + dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef); +} + +static void dcss_scaler_write(struct dcss_scaler_ch *ch, u32 val, u32 ofs) +{ + struct dcss_scaler *scl = ch->scl; + + dcss_ctxld_write(scl->ctxld, scl->ctx_id, val, ch->base_ofs + ofs); +} + +static int dcss_scaler_ch_init_all(struct dcss_scaler *scl, + unsigned long scaler_base) +{ + struct dcss_scaler_ch *ch; + int i; + + for (i = 0; i < 3; i++) { + ch = &scl->ch[i]; + + ch->base_ofs = scaler_base + i * 0x400; + + ch->base_reg = devm_ioremap(scl->dev, ch->base_ofs, SZ_4K); + if (!ch->base_reg) { + dev_err(scl->dev, "scaler: unable to remap ch base\n"); + return -ENOMEM; + } + + ch->scl = scl; + ch->ch_num = i; + } + + return 0; +} + +int dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base) +{ + struct dcss_scaler *scaler; + + scaler = devm_kzalloc(dcss->dev, sizeof(*scaler), GFP_KERNEL); + if (!scaler) + return -ENOMEM; + + dcss->scaler = scaler; + scaler->dev = dcss->dev; + scaler->ctxld = dcss->ctxld; + scaler->ctx_id = CTX_SB_HP; + scaler->wrscl = dcss->wrscl; + scaler->rdsrc = dcss->rdsrc; + scaler->ch_using_wrscl = -1; + + if (dcss_scaler_ch_init_all(scaler, scaler_base)) { + int i; + + for (i = 0; i < 3; i++) { + if (scaler->ch[i].base_reg) + devm_iounmap(scaler->dev, + scaler->ch[i].base_reg); + } + + devm_kfree(scaler->dev, scaler); + + return -ENOMEM; + } + + return 0; +} + +void dcss_scaler_exit(struct dcss_scaler *scl) +{ + int ch_no; + + for (ch_no = 0; ch_no < 3; ch_no++) { + struct dcss_scaler_ch *ch = &scl->ch[ch_no]; + + dcss_writel(0, ch->base_reg + DCSS_SCALER_CTRL); + + if (ch->base_reg) + devm_iounmap(scl->dev, ch->base_reg); + } + + devm_kfree(scl->dev, scl); +} + +void dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en) +{ + struct dcss_scaler_ch *ch = &scl->ch[ch_num]; + u32 scaler_ctrl; + + if (scl->ch_using_wrscl == ch_num) { + if (en) { + scaler_ctrl = SCALE2MEM_EN | MEM2OFIFO_EN | REPEAT_EN; + } else { + dcss_wrscl_disable(scl->wrscl); + dcss_rdsrc_disable(scl->rdsrc); + + scl->ch_using_wrscl = -1; + scaler_ctrl = 0; + } + } else { + scaler_ctrl = en ? SCALER_EN | REPEAT_EN : 0; + } + + if (en) + dcss_scaler_write(ch, ch->sdata_ctrl, DCSS_SCALER_SDATA_CTRL); + + if (ch->scaler_ctrl != scaler_ctrl) + ch->scaler_ctrl_chgd = true; + + ch->scaler_ctrl = scaler_ctrl; +} + +static void dcss_scaler_yuv_enable(struct dcss_scaler_ch *ch, bool en) +{ + ch->sdata_ctrl &= ~YUV_EN; + ch->sdata_ctrl |= en ? YUV_EN : 0; +} + +static void dcss_scaler_rtr_8lines_enable(struct dcss_scaler_ch *ch, bool en) +{ + ch->sdata_ctrl &= ~RTRAM_8LINES; + ch->sdata_ctrl |= en ? RTRAM_8LINES : 0; +} + +static void dcss_scaler_bit_depth_set(struct dcss_scaler_ch *ch, int depth) +{ + u32 val; + + val = depth == 30 ? 2 : 0; + + dcss_scaler_write(ch, + ((val << CHR_BIT_DEPTH_POS) & CHR_BIT_DEPTH_MASK) | + ((val << LUM_BIT_DEPTH_POS) & LUM_BIT_DEPTH_MASK), + DCSS_SCALER_BIT_DEPTH); +} + +enum buffer_format { + BUF_FMT_YUV420, + BUF_FMT_YUV422, + BUF_FMT_ARGB8888_YUV444, +}; + +enum chroma_location { + PSC_LOC_HORZ_0_VERT_1_OVER_4 = 0, + PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4 = 1, + PSC_LOC_HORZ_0_VERT_0 = 2, + PSC_LOC_HORZ_1_OVER_4_VERT_0 = 3, + PSC_LOC_HORZ_0_VERT_1_OVER_2 = 4, + PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2 = 5 +}; + +static void dcss_scaler_format_set(struct dcss_scaler_ch *ch, + enum buffer_format src_fmt, + enum buffer_format dst_fmt) +{ + dcss_scaler_write(ch, src_fmt, DCSS_SCALER_SRC_FORMAT); + dcss_scaler_write(ch, dst_fmt, DCSS_SCALER_DST_FORMAT); +} + +static void dcss_scaler_res_set(struct dcss_scaler_ch *ch, + int src_xres, int src_yres, + int dst_xres, int dst_yres, + u32 pix_format, enum buffer_format dst_format) +{ + u32 lsrc_xres, lsrc_yres, csrc_xres, csrc_yres; + u32 ldst_xres, ldst_yres, cdst_xres, cdst_yres; + bool src_is_444 = true; + + lsrc_xres = src_xres; + csrc_xres = src_xres; + lsrc_yres = src_yres; + csrc_yres = src_yres; + ldst_xres = dst_xres; + cdst_xres = dst_xres; + ldst_yres = dst_yres; + cdst_yres = dst_yres; + + if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY || + pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) { + csrc_xres >>= 1; + src_is_444 = false; + } else if (pix_format == DRM_FORMAT_NV12 || + pix_format == DRM_FORMAT_NV21 || + pix_format == DRM_FORMAT_NV12_10LE40) { + csrc_xres >>= 1; + csrc_yres >>= 1; + src_is_444 = false; + } + + if (dst_format == BUF_FMT_YUV422) + cdst_xres >>= 1; + + /* for 4:4:4 to 4:2:2 conversion, source height should be 1 less */ + if (src_is_444 && dst_format == BUF_FMT_YUV422) { + lsrc_yres--; + csrc_yres--; + } + + dcss_scaler_write(ch, (((lsrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | + (((lsrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), + DCSS_SCALER_SRC_LUM_RES); + dcss_scaler_write(ch, (((csrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | + (((csrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), + DCSS_SCALER_SRC_CHR_RES); + dcss_scaler_write(ch, (((ldst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | + (((ldst_xres - 1) << WIDTH_POS) & WIDTH_MASK), + DCSS_SCALER_DST_LUM_RES); + dcss_scaler_write(ch, (((cdst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | + (((cdst_xres - 1) << WIDTH_POS) & WIDTH_MASK), + DCSS_SCALER_DST_CHR_RES); +} + +#define downscale_fp(factor, fp_pos) ((factor) << (fp_pos)) +#define upscale_fp(factor, fp_pos) ((1 << (fp_pos)) / (factor)) + +struct dcss_scaler_factors { + int downscale; + int upscale; +}; + +static const struct dcss_scaler_factors dcss_scaler_factors[] = { + {3, 8}, {5, 8}, {5, 8}, +}; + +static const struct dcss_scaler_factors dcss_scaler_wrscl_factors[] = { + {5, 8}, {7, 8}, {7, 8}, +}; + +static bool dcss_scaler_fractions_set(struct dcss_scaler_ch *ch, + int src_xres, int src_yres, + int dst_xres, int dst_yres, + u32 src_format, u32 dst_format, + enum chroma_location src_chroma_loc) +{ + int src_c_xres, src_c_yres, dst_c_xres, dst_c_yres; + u32 l_vinc, l_hinc, c_vinc, c_hinc; + u32 c_vstart, c_hstart; + u8 upscale_factor, downscale_factor; + + src_c_xres = src_xres; + src_c_yres = src_yres; + dst_c_xres = dst_xres; + dst_c_yres = dst_yres; + + c_vstart = 0; + c_hstart = 0; + + /* adjustments for source chroma location */ + if (src_format == BUF_FMT_YUV420) { + /* vertical input chroma position adjustment */ + switch (src_chroma_loc) { + case PSC_LOC_HORZ_0_VERT_1_OVER_4: + case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4: + /* + * move chroma up to first luma line + * (1/4 chroma input line spacing) + */ + c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2)); + break; + case PSC_LOC_HORZ_0_VERT_1_OVER_2: + case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2: + /* + * move chroma up to first luma line + * (1/2 chroma input line spacing) + */ + c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 1)); + break; + default: + break; + } + /* horizontal input chroma position adjustment */ + switch (src_chroma_loc) { + case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4: + case PSC_LOC_HORZ_1_OVER_4_VERT_0: + case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2: + /* move chroma left 1/4 chroma input sample spacing */ + c_hstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2)); + break; + default: + break; + } + } + + /* adjustments to chroma resolution */ + if (src_format == BUF_FMT_YUV420) { + src_c_xres >>= 1; + src_c_yres >>= 1; + } else if (src_format == BUF_FMT_YUV422) { + src_c_xres >>= 1; + } + + if (dst_format == BUF_FMT_YUV422) + dst_c_xres >>= 1; + + l_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres; + c_vinc = ((src_c_yres << 13) + (dst_c_yres >> 1)) / dst_c_yres; + l_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres; + c_hinc = ((src_c_xres << 13) + (dst_c_xres >> 1)) / dst_c_xres; + + /* save chroma start phase */ + ch->c_vstart = c_vstart; + ch->c_hstart = c_hstart; + + dcss_scaler_write(ch, 0, DCSS_SCALER_V_LUM_START); + dcss_scaler_write(ch, l_vinc, DCSS_SCALER_V_LUM_INC); + + dcss_scaler_write(ch, 0, DCSS_SCALER_H_LUM_START); + dcss_scaler_write(ch, l_hinc, DCSS_SCALER_H_LUM_INC); + + dcss_scaler_write(ch, c_vstart, DCSS_SCALER_V_CHR_START); + dcss_scaler_write(ch, c_vinc, DCSS_SCALER_V_CHR_INC); + + dcss_scaler_write(ch, c_hstart, DCSS_SCALER_H_CHR_START); + dcss_scaler_write(ch, c_hinc, DCSS_SCALER_H_CHR_INC); + + downscale_factor = dcss_scaler_factors[ch->ch_num].downscale; + upscale_factor = dcss_scaler_factors[ch->ch_num].upscale; + + /* return if WR_SCL/RD_SRC is needed to scale */ + return l_vinc > downscale_fp(downscale_factor, 13) || + l_vinc < upscale_fp(upscale_factor, 13) || + l_hinc > downscale_fp(downscale_factor, 13) || + l_hinc < upscale_fp(upscale_factor, 13); +} + +int dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num, + int *min, int *max) +{ + const struct dcss_scaler_factors *factors_map = dcss_scaler_factors; + + if (scl->ch_using_wrscl == -1 || scl->ch_using_wrscl == ch_num) + factors_map = dcss_scaler_wrscl_factors; + + *min = upscale_fp(factors_map[ch_num].upscale, 16); + *max = downscale_fp(factors_map[ch_num].downscale, 16); + + return 0; +} + +static void dcss_scaler_program_5_coef_set(struct dcss_scaler_ch *ch, + int base_addr, + int coef[][PSC_NUM_TAPS]) +{ + int i, phase; + + for (i = 0; i < PSC_STORED_PHASES; i++) { + dcss_scaler_write(ch, ((coef[i][1] & 0xfff) << 16 | + (coef[i][2] & 0xfff) << 4 | + (coef[i][3] & 0xf00) >> 8), + base_addr + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[i][3] & 0x0ff) << 20 | + (coef[i][4] & 0xfff) << 8 | + (coef[i][5] & 0xff0) >> 4), + base_addr + 0x40 + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[i][5] & 0x00f) << 24), + base_addr + 0x80 + i * sizeof(u32)); + } + + /* reverse both phase and tap orderings */ + for (phase = (PSC_NUM_PHASES >> 1) - 1; + i < PSC_NUM_PHASES; i++, phase--) { + dcss_scaler_write(ch, ((coef[phase][5] & 0xfff) << 16 | + (coef[phase][4] & 0xfff) << 4 | + (coef[phase][3] & 0xf00) >> 8), + base_addr + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[phase][3] & 0x0ff) << 20 | + (coef[phase][2] & 0xfff) << 8 | + (coef[phase][1] & 0xff0) >> 4), + base_addr + 0x40 + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[phase][1] & 0x00f) << 24), + base_addr + 0x80 + i * sizeof(u32)); + } +} + +static void dcss_scaler_program_7_coef_set(struct dcss_scaler_ch *ch, + int base_addr, + int coef[][PSC_NUM_TAPS]) +{ + int i, phase; + + for (i = 0; i < PSC_STORED_PHASES; i++) { + dcss_scaler_write(ch, ((coef[i][0] & 0xfff) << 16 | + (coef[i][1] & 0xfff) << 4 | + (coef[i][2] & 0xf00) >> 8), + base_addr + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[i][2] & 0x0ff) << 20 | + (coef[i][3] & 0xfff) << 8 | + (coef[i][4] & 0xff0) >> 4), + base_addr + 0x40 + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[i][4] & 0x00f) << 24 | + (coef[i][5] & 0xfff) << 12 | + (coef[i][6] & 0xfff)), + base_addr + 0x80 + i * sizeof(u32)); + } + + /* reverse both phase and tap orderings */ + for (phase = (PSC_NUM_PHASES >> 1) - 1; + i < PSC_NUM_PHASES; i++, phase--) { + dcss_scaler_write(ch, ((coef[phase][6] & 0xfff) << 16 | + (coef[phase][5] & 0xfff) << 4 | + (coef[phase][4] & 0xf00) >> 8), + base_addr + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[phase][4] & 0x0ff) << 20 | + (coef[phase][3] & 0xfff) << 8 | + (coef[phase][2] & 0xff0) >> 4), + base_addr + 0x40 + i * sizeof(u32)); + dcss_scaler_write(ch, ((coef[phase][2] & 0x00f) << 24 | + (coef[phase][1] & 0xfff) << 12 | + (coef[phase][0] & 0xfff)), + base_addr + 0x80 + i * sizeof(u32)); + } +} + +static void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch, + enum buffer_format src_format, + enum buffer_format dst_format, + bool use_5_taps, + int src_xres, int src_yres, int dst_xres, + int dst_yres) +{ + int coef[PSC_STORED_PHASES][PSC_NUM_TAPS]; + bool program_5_taps = use_5_taps || + (dst_format == BUF_FMT_YUV422 && + src_format == BUF_FMT_ARGB8888_YUV444); + + /* horizontal luma */ + dcss_scaler_filter_design(src_xres, dst_xres, false, + src_xres == dst_xres, coef); + dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef); + + /* vertical luma */ + dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps, + src_yres == dst_yres, coef); + + if (program_5_taps) + dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); + else + dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); + + /* adjust chroma resolution */ + if (src_format != BUF_FMT_ARGB8888_YUV444) + src_xres >>= 1; + if (src_format == BUF_FMT_YUV420) + src_yres >>= 1; + if (dst_format != BUF_FMT_ARGB8888_YUV444) + dst_xres >>= 1; + if (dst_format == BUF_FMT_YUV420) /* should not happen */ + dst_yres >>= 1; + + /* horizontal chroma */ + dcss_scaler_filter_design(src_xres, dst_xres, false, + (src_xres == dst_xres) && (ch->c_hstart == 0), + coef); + + dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HCHR, coef); + + /* vertical chroma */ + dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps, + (src_yres == dst_yres) && (ch->c_vstart == 0), + coef); + if (program_5_taps) + dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef); + else + dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef); +} + +static void dcss_scaler_rgb_coef_set(struct dcss_scaler_ch *ch, + int src_xres, int src_yres, int dst_xres, + int dst_yres) +{ + int coef[PSC_STORED_PHASES][PSC_NUM_TAPS]; + + /* horizontal RGB */ + dcss_scaler_filter_design(src_xres, dst_xres, false, + src_xres == dst_xres, coef); + dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef); + + /* vertical RGB */ + dcss_scaler_filter_design(src_yres, dst_yres, false, + src_yres == dst_yres, coef); + dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); +} + +static void dcss_scaler_set_rgb10_order(struct dcss_scaler_ch *ch, + const struct drm_format_info *format) +{ + u32 a2r10g10b10_format; + + if (format->is_yuv) + return; + + ch->sdata_ctrl &= ~A2R10G10B10_FORMAT_MASK; + + if (format->depth != 30) + return; + + switch (format->format) { + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + a2r10g10b10_format = 0; + break; + + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XBGR2101010: + a2r10g10b10_format = 5; + break; + + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_RGBX1010102: + a2r10g10b10_format = 6; + break; + + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_BGRX1010102: + a2r10g10b10_format = 11; + break; + + default: + a2r10g10b10_format = 0; + break; + } + + ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS; +} + +static void dcss_scaler_setup_path(struct dcss_scaler_ch *ch, + u32 pix_format, int dst_xres, + int dst_yres, u32 vrefresh_hz, + bool wrscl_needed) +{ + struct dcss_scaler *scl = ch->scl; + u32 base_addr; + + /* nothing to do if WRSCL path is needed but it's already used */ + if (wrscl_needed && scl->ch_using_wrscl != -1 && + scl->ch_using_wrscl != ch->ch_num) + return; + + if (!wrscl_needed) { + /* Channel has finished using WRSCL. Release WRSCL/RDSRC. */ + if (scl->ch_using_wrscl == ch->ch_num) { + dcss_wrscl_disable(scl->wrscl); + dcss_rdsrc_disable(scl->rdsrc); + + scl->ch_using_wrscl = -1; + } + + return; + } + + base_addr = dcss_wrscl_setup(scl->wrscl, pix_format, vrefresh_hz, + dst_xres, dst_yres); + + dcss_rdsrc_setup(scl->rdsrc, pix_format, dst_xres, dst_yres, + base_addr); + + dcss_wrscl_enable(scl->wrscl); + dcss_rdsrc_enable(scl->rdsrc); + + scl->ch_using_wrscl = ch->ch_num; +} + +void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num, + const struct drm_format_info *format, + int src_xres, int src_yres, int dst_xres, int dst_yres, + u32 vrefresh_hz) +{ + struct dcss_scaler_ch *ch = &scl->ch[ch_num]; + unsigned int pixel_depth = 0; + bool rtr_8line_en = false; + bool use_5_taps = false; + enum buffer_format src_format = BUF_FMT_ARGB8888_YUV444; + enum buffer_format dst_format = BUF_FMT_ARGB8888_YUV444; + u32 pix_format = format->format; + bool use_wrscl; + + if (format->is_yuv) { + dcss_scaler_yuv_enable(ch, true); + + if (pix_format == DRM_FORMAT_NV12 || + pix_format == DRM_FORMAT_NV21 || + pix_format == DRM_FORMAT_NV12_10LE40) { + rtr_8line_en = true; + src_format = BUF_FMT_YUV420; + } else if (pix_format == DRM_FORMAT_UYVY || + pix_format == DRM_FORMAT_VYUY || + pix_format == DRM_FORMAT_YUYV || + pix_format == DRM_FORMAT_YVYU) { + src_format = BUF_FMT_YUV422; + } + + use_5_taps = !rtr_8line_en; + + if (pix_format == DRM_FORMAT_NV12_10LE40) + pixel_depth = 30; + } else { + dcss_scaler_yuv_enable(ch, false); + + pixel_depth = format->depth; + } + + use_wrscl = dcss_scaler_fractions_set(ch, src_xres, src_yres, dst_xres, + dst_yres, src_format, dst_format, + PSC_LOC_HORZ_0_VERT_1_OVER_4); + + if (format->is_yuv) + dcss_scaler_yuv_coef_set(ch, src_format, dst_format, + use_5_taps, src_xres, src_yres, + dst_xres, dst_yres); + else + dcss_scaler_rgb_coef_set(ch, src_xres, src_yres, + dst_xres, dst_yres); + + dcss_scaler_rtr_8lines_enable(ch, rtr_8line_en); + dcss_scaler_bit_depth_set(ch, pixel_depth); + dcss_scaler_set_rgb10_order(ch, format); + dcss_scaler_format_set(ch, src_format, dst_format); + dcss_scaler_res_set(ch, src_xres, src_yres, dst_xres, dst_yres, + pix_format, dst_format); + + dcss_scaler_setup_path(ch, pix_format, dst_xres, + dst_yres, vrefresh_hz, use_wrscl); +} + +/* This function will be called from interrupt context. */ +void dcss_scaler_write_sclctrl(struct dcss_scaler *scl) +{ + int chnum; + + for (chnum = 0; chnum < 3; chnum++) { + struct dcss_scaler_ch *ch = &scl->ch[chnum]; + + if (ch->scaler_ctrl_chgd) { + dcss_ctxld_write_irqsafe(scl->ctxld, scl->ctx_id, + ch->scaler_ctrl, + ch->base_ofs + + DCSS_SCALER_CTRL); + ch->scaler_ctrl_chgd = false; + } + } +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-ss.c b/drivers/gpu/drm/imx/dcss/dcss-ss.c new file mode 100644 index 000000000000..39b27f0cde79 --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-ss.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/device.h> + +#include "dcss-dev.h" + +#define DCSS_SS_SYS_CTRL 0x00 +#define RUN_EN BIT(0) +#define DCSS_SS_DISPLAY 0x10 +#define LRC_X_POS 0 +#define LRC_X_MASK GENMASK(12, 0) +#define LRC_Y_POS 16 +#define LRC_Y_MASK GENMASK(28, 16) +#define DCSS_SS_HSYNC 0x20 +#define DCSS_SS_VSYNC 0x30 +#define SYNC_START_POS 0 +#define SYNC_START_MASK GENMASK(12, 0) +#define SYNC_END_POS 16 +#define SYNC_END_MASK GENMASK(28, 16) +#define SYNC_POL BIT(31) +#define DCSS_SS_DE_ULC 0x40 +#define ULC_X_POS 0 +#define ULC_X_MASK GENMASK(12, 0) +#define ULC_Y_POS 16 +#define ULC_Y_MASK GENMASK(28, 16) +#define ULC_POL BIT(31) +#define DCSS_SS_DE_LRC 0x50 +#define DCSS_SS_MODE 0x60 +#define PIPE_MODE_POS 0 +#define PIPE_MODE_MASK GENMASK(1, 0) +#define DCSS_SS_COEFF 0x70 +#define HORIZ_A_POS 0 +#define HORIZ_A_MASK GENMASK(3, 0) +#define HORIZ_B_POS 4 +#define HORIZ_B_MASK GENMASK(7, 4) +#define HORIZ_C_POS 8 +#define HORIZ_C_MASK GENMASK(11, 8) +#define HORIZ_H_NORM_POS 12 +#define HORIZ_H_NORM_MASK GENMASK(14, 12) +#define VERT_A_POS 16 +#define VERT_A_MASK GENMASK(19, 16) +#define VERT_B_POS 20 +#define VERT_B_MASK GENMASK(23, 20) +#define VERT_C_POS 24 +#define VERT_C_MASK GENMASK(27, 24) +#define VERT_H_NORM_POS 28 +#define VERT_H_NORM_MASK GENMASK(30, 28) +#define DCSS_SS_CLIP_CB 0x80 +#define DCSS_SS_CLIP_CR 0x90 +#define CLIP_MIN_POS 0 +#define CLIP_MIN_MASK GENMASK(9, 0) +#define CLIP_MAX_POS 0 +#define CLIP_MAX_MASK GENMASK(23, 16) +#define DCSS_SS_INTER_MODE 0xA0 +#define INT_EN BIT(0) +#define VSYNC_SHIFT BIT(1) + +struct dcss_ss { + struct device *dev; + void __iomem *base_reg; + u32 base_ofs; + + struct dcss_ctxld *ctxld; + u32 ctx_id; + + bool in_use; +}; + +static void dcss_ss_write(struct dcss_ss *ss, u32 val, u32 ofs) +{ + if (!ss->in_use) + dcss_writel(val, ss->base_reg + ofs); + + dcss_ctxld_write(ss->ctxld, ss->ctx_id, val, + ss->base_ofs + ofs); +} + +int dcss_ss_init(struct dcss_dev *dcss, unsigned long ss_base) +{ + struct dcss_ss *ss; + + ss = devm_kzalloc(dcss->dev, sizeof(*ss), GFP_KERNEL); + if (!ss) + return -ENOMEM; + + dcss->ss = ss; + ss->dev = dcss->dev; + ss->ctxld = dcss->ctxld; + + ss->base_reg = devm_ioremap(dcss->dev, ss_base, SZ_4K); + if (!ss->base_reg) { + dev_err(dcss->dev, "ss: unable to remap ss base\n"); + devm_kfree(ss->dev, ss); + return -ENOMEM; + } + + ss->base_ofs = ss_base; + ss->ctx_id = CTX_SB_HP; + + return 0; +} + +void dcss_ss_exit(struct dcss_ss *ss) +{ + /* stop SS */ + dcss_writel(0, ss->base_reg + DCSS_SS_SYS_CTRL); + + if (ss->base_reg) + devm_iounmap(ss->dev, ss->base_reg); + + devm_kfree(ss->dev, ss); +} + +void dcss_ss_subsam_set(struct dcss_ss *ss, bool out_is_yuv) +{ + dcss_ss_write(ss, out_is_yuv ? 0x21612161 : 0x41614161, DCSS_SS_COEFF); + dcss_ss_write(ss, out_is_yuv ? 2 : 0, DCSS_SS_MODE); + dcss_ss_write(ss, 0x03ff0000, DCSS_SS_CLIP_CB); + dcss_ss_write(ss, 0x03ff0000, DCSS_SS_CLIP_CR); +} + +void dcss_ss_sync_set(struct dcss_ss *ss, struct videomode *vm, + bool phsync, bool pvsync) +{ + u16 lrc_x, lrc_y; + u16 hsync_start, hsync_end; + u16 vsync_start, vsync_end; + u16 de_ulc_x, de_ulc_y; + u16 de_lrc_x, de_lrc_y; + + lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len + + vm->hactive - 1; + lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len + + vm->vactive - 1; + + dcss_ss_write(ss, (lrc_y << LRC_Y_POS) | lrc_x, DCSS_SS_DISPLAY); + + hsync_start = vm->hfront_porch + vm->hback_porch + vm->hsync_len + + vm->hactive - 1; + hsync_end = vm->hsync_len - 1; + + dcss_ss_write(ss, (phsync ? SYNC_POL : 0) | + ((u32)hsync_end << SYNC_END_POS) | hsync_start, + DCSS_SS_HSYNC); + + vsync_start = vm->vfront_porch - 1; + vsync_end = vm->vfront_porch + vm->vsync_len - 1; + + dcss_ss_write(ss, (pvsync ? SYNC_POL : 0) | + ((u32)vsync_end << SYNC_END_POS) | vsync_start, + DCSS_SS_VSYNC); + + de_ulc_x = vm->hsync_len + vm->hback_porch - 1; + de_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch; + + dcss_ss_write(ss, SYNC_POL | ((u32)de_ulc_y << ULC_Y_POS) | de_ulc_x, + DCSS_SS_DE_ULC); + + de_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1; + de_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch + + vm->vactive - 1; + + dcss_ss_write(ss, (de_lrc_y << LRC_Y_POS) | de_lrc_x, DCSS_SS_DE_LRC); +} + +void dcss_ss_enable(struct dcss_ss *ss) +{ + dcss_ss_write(ss, RUN_EN, DCSS_SS_SYS_CTRL); + ss->in_use = true; +} + +void dcss_ss_disable(struct dcss_ss *ss) +{ + dcss_ss_write(ss, 0, DCSS_SS_SYS_CTRL); + ss->in_use = false; +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-wrscl.c b/drivers/gpu/drm/imx/dcss/dcss-wrscl.c new file mode 100644 index 000000000000..8228f8f46cae --- /dev/null +++ b/drivers/gpu/drm/imx/dcss/dcss-wrscl.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/seq_file.h> + +#include "dcss-dev.h" + +#define DCSS_WRSCL_CTRL_STATUS 0x00 +#define WRSCL_ERR BIT(31) +#define WRSCL_ERR_EN BIT(30) +#define WRSCL_FRAME_COMP BIT(29) +#define WRSCL_FRAME_COMP_EN BIT(28) +#define WRSCL_FIFO_SIZE_POS 18 +#define WRSCL_FIFO_SIZE_MASK GENMAK(24, 18) +#define WRSCL_P_FREQ_POS 10 +#define WRSCL_P_FREQ_MASK GENMASK(17, 10) +#define WRSCL_P_SIZE_POS 7 +#define WRSCL_P_SIZE_MASK GENMASK(9, 7) +#define WRSCL_T_SIZE_POS 5 +#define WRSCL_T_SIZE_MASK GENMASK(6, 5) +#define WRSCL_BPP_POS 2 +#define WRSCL_BPP_MASK GENMASK(4, 2) +#define WRSCL_REPEAT BIT(1) +#define WRSCL_ENABLE BIT(0) +#define DCSS_WRSCL_BASE_ADDR 0x10 +#define DCSS_WRSCL_PITCH 0x14 + +struct dcss_wrscl { + struct device *dev; + + void __iomem *base_reg; + u32 base_ofs; + + struct dcss_ctxld *ctxld; + u32 ctx_id; + + u32 buf_size; + u32 buf_addr; + void *buf_vaddr; + + struct clk *bclk; + + u32 ctrl_status; +}; + +static void dcss_wrscl_write(struct dcss_wrscl *wrscl, u32 val, u32 ofs) +{ + dcss_ctxld_write(wrscl->ctxld, wrscl->ctx_id, + val, wrscl->base_ofs + ofs); +} + +int dcss_wrscl_init(struct dcss_dev *dcss, unsigned long wrscl_base) +{ + struct dcss_wrscl *wrscl; + + wrscl = devm_kzalloc(dcss->dev, sizeof(*wrscl), GFP_KERNEL); + if (!wrscl) + return -ENOMEM; + + wrscl->base_reg = devm_ioremap(dcss->dev, wrscl_base, SZ_4K); + if (!wrscl->base_reg) { + dev_err(dcss->dev, "wrscl: unable to remap base\n"); + devm_kfree(dcss->dev, wrscl); + return -ENOMEM; + } + + dcss->wrscl = wrscl; + + wrscl->dev = dcss->dev; + wrscl->base_ofs = wrscl_base; + wrscl->ctxld = dcss->ctxld; + wrscl->ctx_id = CTX_SB_HP; + wrscl->bclk = dcss->axi_clk; + + return 0; +} + +void dcss_wrscl_exit(struct dcss_wrscl *wrscl) +{ + devm_iounmap(wrscl->dev, wrscl->base_reg); + devm_kfree(wrscl->dev, wrscl); +} + +static const u16 dcss_wrscl_psize_map[] = {64, 128, 256, 512, 1024, 2048, 4096}; + +u32 dcss_wrscl_setup(struct dcss_wrscl *wrscl, u32 pix_format, u32 vrefresh_hz, + u32 dst_xres, u32 dst_yres) +{ + u32 pitch, p_size, p_freq, bpp; + dma_addr_t dma_handle; + u32 bclk_rate = clk_get_rate(wrscl->bclk); + + /* we'd better release the old buffer */ + if (wrscl->buf_addr) + dmam_free_coherent(wrscl->dev, wrscl->buf_size, + wrscl->buf_vaddr, wrscl->buf_addr); + + p_size = PSIZE_256; + + /* scaler output is YUV444 */ + bpp = 4; + + /* spread the load over the entire frame */ + p_freq = ((u64)bclk_rate * dcss_wrscl_psize_map[p_size]) / + ((u64)dst_xres * dst_yres * vrefresh_hz * bpp * 8); + + /* choose a slightly smaller p_freq */ + p_freq = p_freq - 3 > 255 ? 255 : p_freq - 3; + + wrscl->ctrl_status = FIFO_512 << WRSCL_FIFO_SIZE_POS; + wrscl->ctrl_status |= p_size << WRSCL_P_SIZE_POS; + wrscl->ctrl_status |= TSIZE_256 << WRSCL_T_SIZE_POS; + wrscl->ctrl_status |= BPP_32_10BIT_OUTPUT << WRSCL_BPP_POS; + wrscl->ctrl_status |= p_freq << WRSCL_P_FREQ_POS; + + wrscl->buf_size = dst_xres * dst_yres * bpp; + pitch = dst_xres * bpp; + + wrscl->buf_vaddr = dmam_alloc_coherent(wrscl->dev, wrscl->buf_size, + &dma_handle, GFP_KERNEL); + if (!wrscl->buf_vaddr) { + dev_err(wrscl->dev, "wrscl: cannot alloc buf mem\n"); + return 0; + } + + wrscl->buf_addr = dma_handle; + + dcss_wrscl_write(wrscl, wrscl->buf_addr, DCSS_WRSCL_BASE_ADDR); + dcss_wrscl_write(wrscl, pitch, DCSS_WRSCL_PITCH); + + return wrscl->buf_addr; +} + +void dcss_wrscl_enable(struct dcss_wrscl *wrscl) +{ + wrscl->ctrl_status |= WRSCL_ENABLE | WRSCL_REPEAT; + + dcss_wrscl_write(wrscl, wrscl->ctrl_status, DCSS_WRSCL_CTRL_STATUS); +} + +void dcss_wrscl_disable(struct dcss_wrscl *wrscl) +{ + wrscl->ctrl_status &= ~(WRSCL_ENABLE | WRSCL_REPEAT); + + dcss_wrscl_write(wrscl, wrscl->ctrl_status, DCSS_WRSCL_CTRL_STATUS); + + if (wrscl->buf_addr) { + dmam_free_coherent(wrscl->dev, wrscl->buf_size, + wrscl->buf_vaddr, wrscl->buf_addr); + wrscl->buf_addr = 0; + } +} diff --git a/include/linux/busfreq-imx.h b/include/linux/busfreq-imx.h new file mode 100644 index 000000000000..39c71a9f55eb --- /dev/null +++ b/include/linux/busfreq-imx.h @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2016 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARCH_MXC_BUSFREQ_H__ +#define __ASM_ARCH_MXC_BUSFREQ_H__ + +#include <linux/notifier.h> +#include <linux/regulator/consumer.h> + +/* + * This enumerates busfreq low power mode entry and exit. + */ +enum busfreq_event { + LOW_BUSFREQ_ENTER, + LOW_BUSFREQ_EXIT, +}; + +/* + * This enumerates the system bus and ddr frequencies in various modes. + * BUS_FREQ_HIGH - DDR @ 528MHz, AHB @ 132MHz. + * BUS_FREQ_MED - DDR @ 400MHz, AHB @ 132MHz + * BUS_FREQ_AUDIO - DDR @ 50MHz/100MHz, AHB @ 24MHz. + * BUS_FREQ_LOW - DDR @ 24MHz, AHB @ 24MHz. + * BUS_FREQ_ULTRA_LOW - DDR @ 1MHz, AHB - 3MHz. + * + * Drivers need to request/release the bus/ddr frequencies based on + * their performance requirements. Drivers cannot request/release + * BUS_FREQ_ULTRA_LOW mode as this mode is automatically entered from + * either BUS_FREQ_AUDIO or BUS_FREQ_LOW + * modes. + */ +enum bus_freq_mode { + BUS_FREQ_HIGH, + BUS_FREQ_MED, + BUS_FREQ_AUDIO, + BUS_FREQ_LOW, + BUS_FREQ_ULTRA_LOW, +}; + +#if defined(CONFIG_HAVE_IMX_BUSFREQ) && !defined(CONFIG_ARM64) +extern struct regulator *arm_reg; +extern struct regulator *soc_reg; +void request_bus_freq(enum bus_freq_mode mode); +void release_bus_freq(enum bus_freq_mode mode); +int register_busfreq_notifier(struct notifier_block *nb); +int unregister_busfreq_notifier(struct notifier_block *nb); +int get_bus_freq_mode(void); +#elif defined(CONFIG_HAVE_IMX_BUSFREQ) +void request_bus_freq(enum bus_freq_mode mode); +void release_bus_freq(enum bus_freq_mode mode); +int get_bus_freq_mode(void); +#else +static inline void request_bus_freq(enum bus_freq_mode mode) +{ +} +static inline void release_bus_freq(enum bus_freq_mode mode) +{ +} +static inline int register_busfreq_notifier(struct notifier_block *nb) +{ + return 0; +} +static inline int unregister_busfreq_notifier(struct notifier_block *nb) +{ + return 0; +} +static inline int get_bus_freq_mode(void) +{ + return BUS_FREQ_HIGH; +} +#endif +#endif |