Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
0004  *
0005  * Based on sun4i_backend.c, which is:
0006  *   Copyright (C) 2015 Free Electrons
0007  *   Copyright (C) 2015 NextThing Co
0008  */
0009 
0010 #include <linux/component.h>
0011 #include <linux/dma-mapping.h>
0012 #include <linux/module.h>
0013 #include <linux/of_device.h>
0014 #include <linux/of_graph.h>
0015 #include <linux/reset.h>
0016 
0017 #include <drm/drm_atomic_helper.h>
0018 #include <drm/drm_crtc.h>
0019 #include <drm/drm_fb_cma_helper.h>
0020 #include <drm/drm_framebuffer.h>
0021 #include <drm/drm_gem_cma_helper.h>
0022 #include <drm/drm_plane_helper.h>
0023 #include <drm/drm_probe_helper.h>
0024 
0025 #include "sun4i_drv.h"
0026 #include "sun8i_mixer.h"
0027 #include "sun8i_ui_layer.h"
0028 #include "sun8i_vi_layer.h"
0029 #include "sunxi_engine.h"
0030 
0031 struct de2_fmt_info {
0032     u32 drm_fmt;
0033     u32 de2_fmt;
0034 };
0035 
0036 static const struct de2_fmt_info de2_formats[] = {
0037     {
0038         .drm_fmt = DRM_FORMAT_ARGB8888,
0039         .de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888,
0040     },
0041     {
0042         .drm_fmt = DRM_FORMAT_ABGR8888,
0043         .de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888,
0044     },
0045     {
0046         .drm_fmt = DRM_FORMAT_RGBA8888,
0047         .de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888,
0048     },
0049     {
0050         .drm_fmt = DRM_FORMAT_BGRA8888,
0051         .de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888,
0052     },
0053     {
0054         .drm_fmt = DRM_FORMAT_XRGB8888,
0055         .de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888,
0056     },
0057     {
0058         .drm_fmt = DRM_FORMAT_XBGR8888,
0059         .de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888,
0060     },
0061     {
0062         .drm_fmt = DRM_FORMAT_RGBX8888,
0063         .de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888,
0064     },
0065     {
0066         .drm_fmt = DRM_FORMAT_BGRX8888,
0067         .de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888,
0068     },
0069     {
0070         .drm_fmt = DRM_FORMAT_RGB888,
0071         .de2_fmt = SUN8I_MIXER_FBFMT_RGB888,
0072     },
0073     {
0074         .drm_fmt = DRM_FORMAT_BGR888,
0075         .de2_fmt = SUN8I_MIXER_FBFMT_BGR888,
0076     },
0077     {
0078         .drm_fmt = DRM_FORMAT_RGB565,
0079         .de2_fmt = SUN8I_MIXER_FBFMT_RGB565,
0080     },
0081     {
0082         .drm_fmt = DRM_FORMAT_BGR565,
0083         .de2_fmt = SUN8I_MIXER_FBFMT_BGR565,
0084     },
0085     {
0086         .drm_fmt = DRM_FORMAT_ARGB4444,
0087         .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
0088     },
0089     {
0090         /* for DE2 VI layer which ignores alpha */
0091         .drm_fmt = DRM_FORMAT_XRGB4444,
0092         .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
0093     },
0094     {
0095         .drm_fmt = DRM_FORMAT_ABGR4444,
0096         .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
0097     },
0098     {
0099         /* for DE2 VI layer which ignores alpha */
0100         .drm_fmt = DRM_FORMAT_XBGR4444,
0101         .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
0102     },
0103     {
0104         .drm_fmt = DRM_FORMAT_RGBA4444,
0105         .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
0106     },
0107     {
0108         /* for DE2 VI layer which ignores alpha */
0109         .drm_fmt = DRM_FORMAT_RGBX4444,
0110         .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
0111     },
0112     {
0113         .drm_fmt = DRM_FORMAT_BGRA4444,
0114         .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
0115     },
0116     {
0117         /* for DE2 VI layer which ignores alpha */
0118         .drm_fmt = DRM_FORMAT_BGRX4444,
0119         .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
0120     },
0121     {
0122         .drm_fmt = DRM_FORMAT_ARGB1555,
0123         .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
0124     },
0125     {
0126         /* for DE2 VI layer which ignores alpha */
0127         .drm_fmt = DRM_FORMAT_XRGB1555,
0128         .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
0129     },
0130     {
0131         .drm_fmt = DRM_FORMAT_ABGR1555,
0132         .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
0133     },
0134     {
0135         /* for DE2 VI layer which ignores alpha */
0136         .drm_fmt = DRM_FORMAT_XBGR1555,
0137         .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
0138     },
0139     {
0140         .drm_fmt = DRM_FORMAT_RGBA5551,
0141         .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
0142     },
0143     {
0144         /* for DE2 VI layer which ignores alpha */
0145         .drm_fmt = DRM_FORMAT_RGBX5551,
0146         .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
0147     },
0148     {
0149         .drm_fmt = DRM_FORMAT_BGRA5551,
0150         .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
0151     },
0152     {
0153         /* for DE2 VI layer which ignores alpha */
0154         .drm_fmt = DRM_FORMAT_BGRX5551,
0155         .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
0156     },
0157     {
0158         .drm_fmt = DRM_FORMAT_ARGB2101010,
0159         .de2_fmt = SUN8I_MIXER_FBFMT_ARGB2101010,
0160     },
0161     {
0162         .drm_fmt = DRM_FORMAT_ABGR2101010,
0163         .de2_fmt = SUN8I_MIXER_FBFMT_ABGR2101010,
0164     },
0165     {
0166         .drm_fmt = DRM_FORMAT_RGBA1010102,
0167         .de2_fmt = SUN8I_MIXER_FBFMT_RGBA1010102,
0168     },
0169     {
0170         .drm_fmt = DRM_FORMAT_BGRA1010102,
0171         .de2_fmt = SUN8I_MIXER_FBFMT_BGRA1010102,
0172     },
0173     {
0174         .drm_fmt = DRM_FORMAT_UYVY,
0175         .de2_fmt = SUN8I_MIXER_FBFMT_UYVY,
0176     },
0177     {
0178         .drm_fmt = DRM_FORMAT_VYUY,
0179         .de2_fmt = SUN8I_MIXER_FBFMT_VYUY,
0180     },
0181     {
0182         .drm_fmt = DRM_FORMAT_YUYV,
0183         .de2_fmt = SUN8I_MIXER_FBFMT_YUYV,
0184     },
0185     {
0186         .drm_fmt = DRM_FORMAT_YVYU,
0187         .de2_fmt = SUN8I_MIXER_FBFMT_YVYU,
0188     },
0189     {
0190         .drm_fmt = DRM_FORMAT_NV16,
0191         .de2_fmt = SUN8I_MIXER_FBFMT_NV16,
0192     },
0193     {
0194         .drm_fmt = DRM_FORMAT_NV61,
0195         .de2_fmt = SUN8I_MIXER_FBFMT_NV61,
0196     },
0197     {
0198         .drm_fmt = DRM_FORMAT_NV12,
0199         .de2_fmt = SUN8I_MIXER_FBFMT_NV12,
0200     },
0201     {
0202         .drm_fmt = DRM_FORMAT_NV21,
0203         .de2_fmt = SUN8I_MIXER_FBFMT_NV21,
0204     },
0205     {
0206         .drm_fmt = DRM_FORMAT_YUV422,
0207         .de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
0208     },
0209     {
0210         .drm_fmt = DRM_FORMAT_YUV420,
0211         .de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
0212     },
0213     {
0214         .drm_fmt = DRM_FORMAT_YUV411,
0215         .de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
0216     },
0217     {
0218         .drm_fmt = DRM_FORMAT_YVU422,
0219         .de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
0220     },
0221     {
0222         .drm_fmt = DRM_FORMAT_YVU420,
0223         .de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
0224     },
0225     {
0226         .drm_fmt = DRM_FORMAT_YVU411,
0227         .de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
0228     },
0229     {
0230         .drm_fmt = DRM_FORMAT_P010,
0231         .de2_fmt = SUN8I_MIXER_FBFMT_P010_YUV,
0232     },
0233     {
0234         .drm_fmt = DRM_FORMAT_P210,
0235         .de2_fmt = SUN8I_MIXER_FBFMT_P210_YUV,
0236     },
0237 };
0238 
0239 int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format)
0240 {
0241     unsigned int i;
0242 
0243     for (i = 0; i < ARRAY_SIZE(de2_formats); ++i)
0244         if (de2_formats[i].drm_fmt == format) {
0245             *hw_format = de2_formats[i].de2_fmt;
0246             return 0;
0247         }
0248 
0249     return -EINVAL;
0250 }
0251 
0252 static void sun8i_mixer_commit(struct sunxi_engine *engine)
0253 {
0254     DRM_DEBUG_DRIVER("Committing changes\n");
0255 
0256     regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
0257              SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
0258 }
0259 
0260 static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
0261                         struct sunxi_engine *engine)
0262 {
0263     struct drm_plane **planes;
0264     struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
0265     int i;
0266 
0267     planes = devm_kcalloc(drm->dev,
0268                   mixer->cfg->vi_num + mixer->cfg->ui_num + 1,
0269                   sizeof(*planes), GFP_KERNEL);
0270     if (!planes)
0271         return ERR_PTR(-ENOMEM);
0272 
0273     for (i = 0; i < mixer->cfg->vi_num; i++) {
0274         struct sun8i_vi_layer *layer;
0275 
0276         layer = sun8i_vi_layer_init_one(drm, mixer, i);
0277         if (IS_ERR(layer)) {
0278             dev_err(drm->dev,
0279                 "Couldn't initialize overlay plane\n");
0280             return ERR_CAST(layer);
0281         }
0282 
0283         planes[i] = &layer->plane;
0284     }
0285 
0286     for (i = 0; i < mixer->cfg->ui_num; i++) {
0287         struct sun8i_ui_layer *layer;
0288 
0289         layer = sun8i_ui_layer_init_one(drm, mixer, i);
0290         if (IS_ERR(layer)) {
0291             dev_err(drm->dev, "Couldn't initialize %s plane\n",
0292                 i ? "overlay" : "primary");
0293             return ERR_CAST(layer);
0294         }
0295 
0296         planes[mixer->cfg->vi_num + i] = &layer->plane;
0297     }
0298 
0299     return planes;
0300 }
0301 
0302 static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
0303                  const struct drm_display_mode *mode)
0304 {
0305     struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
0306     u32 bld_base, size, val;
0307     bool interlaced;
0308 
0309     bld_base = sun8i_blender_base(mixer);
0310     interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
0311     size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay);
0312 
0313     DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
0314              mode->hdisplay, mode->vdisplay);
0315 
0316     regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size);
0317     regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
0318 
0319     if (interlaced)
0320         val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
0321     else
0322         val = 0;
0323 
0324     regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
0325                SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val);
0326 
0327     DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
0328              interlaced ? "on" : "off");
0329 }
0330 
0331 static const struct sunxi_engine_ops sun8i_engine_ops = {
0332     .commit     = sun8i_mixer_commit,
0333     .layers_init    = sun8i_layers_init,
0334     .mode_set   = sun8i_mixer_mode_set,
0335 };
0336 
0337 static const struct regmap_config sun8i_mixer_regmap_config = {
0338     .reg_bits   = 32,
0339     .val_bits   = 32,
0340     .reg_stride = 4,
0341     .max_register   = 0xffffc, /* guessed */
0342 };
0343 
0344 static int sun8i_mixer_of_get_id(struct device_node *node)
0345 {
0346     struct device_node *ep, *remote;
0347     struct of_endpoint of_ep;
0348 
0349     /* Output port is 1, and we want the first endpoint. */
0350     ep = of_graph_get_endpoint_by_regs(node, 1, -1);
0351     if (!ep)
0352         return -EINVAL;
0353 
0354     remote = of_graph_get_remote_endpoint(ep);
0355     of_node_put(ep);
0356     if (!remote)
0357         return -EINVAL;
0358 
0359     of_graph_parse_endpoint(remote, &of_ep);
0360     of_node_put(remote);
0361     return of_ep.id;
0362 }
0363 
0364 static int sun8i_mixer_bind(struct device *dev, struct device *master,
0365                   void *data)
0366 {
0367     struct platform_device *pdev = to_platform_device(dev);
0368     struct drm_device *drm = data;
0369     struct sun4i_drv *drv = drm->dev_private;
0370     struct sun8i_mixer *mixer;
0371     void __iomem *regs;
0372     unsigned int base;
0373     int plane_cnt;
0374     int i, ret;
0375 
0376     /*
0377      * The mixer uses single 32-bit register to store memory
0378      * addresses, so that it cannot deal with 64-bit memory
0379      * addresses.
0380      * Restrict the DMA mask so that the mixer won't be
0381      * allocated some memory that is too high.
0382      */
0383     ret = dma_set_mask(dev, DMA_BIT_MASK(32));
0384     if (ret) {
0385         dev_err(dev, "Cannot do 32-bit DMA.\n");
0386         return ret;
0387     }
0388 
0389     mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
0390     if (!mixer)
0391         return -ENOMEM;
0392     dev_set_drvdata(dev, mixer);
0393     mixer->engine.ops = &sun8i_engine_ops;
0394     mixer->engine.node = dev->of_node;
0395 
0396     if (of_find_property(dev->of_node, "iommus", NULL)) {
0397         /*
0398          * This assume we have the same DMA constraints for
0399          * all our the mixers in our pipeline. This sounds
0400          * bad, but it has always been the case for us, and
0401          * DRM doesn't do per-device allocation either, so we
0402          * would need to fix DRM first...
0403          */
0404         ret = of_dma_configure(drm->dev, dev->of_node, true);
0405         if (ret)
0406             return ret;
0407     }
0408 
0409     /*
0410      * While this function can fail, we shouldn't do anything
0411      * if this happens. Some early DE2 DT entries don't provide
0412      * mixer id but work nevertheless because matching between
0413      * TCON and mixer is done by comparing node pointers (old
0414      * way) instead comparing ids. If this function fails and
0415      * id is needed, it will fail during id matching anyway.
0416      */
0417     mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node);
0418 
0419     mixer->cfg = of_device_get_match_data(dev);
0420     if (!mixer->cfg)
0421         return -EINVAL;
0422 
0423     regs = devm_platform_ioremap_resource(pdev, 0);
0424     if (IS_ERR(regs))
0425         return PTR_ERR(regs);
0426 
0427     mixer->engine.regs = devm_regmap_init_mmio(dev, regs,
0428                            &sun8i_mixer_regmap_config);
0429     if (IS_ERR(mixer->engine.regs)) {
0430         dev_err(dev, "Couldn't create the mixer regmap\n");
0431         return PTR_ERR(mixer->engine.regs);
0432     }
0433 
0434     mixer->reset = devm_reset_control_get(dev, NULL);
0435     if (IS_ERR(mixer->reset)) {
0436         dev_err(dev, "Couldn't get our reset line\n");
0437         return PTR_ERR(mixer->reset);
0438     }
0439 
0440     ret = reset_control_deassert(mixer->reset);
0441     if (ret) {
0442         dev_err(dev, "Couldn't deassert our reset line\n");
0443         return ret;
0444     }
0445 
0446     mixer->bus_clk = devm_clk_get(dev, "bus");
0447     if (IS_ERR(mixer->bus_clk)) {
0448         dev_err(dev, "Couldn't get the mixer bus clock\n");
0449         ret = PTR_ERR(mixer->bus_clk);
0450         goto err_assert_reset;
0451     }
0452     clk_prepare_enable(mixer->bus_clk);
0453 
0454     mixer->mod_clk = devm_clk_get(dev, "mod");
0455     if (IS_ERR(mixer->mod_clk)) {
0456         dev_err(dev, "Couldn't get the mixer module clock\n");
0457         ret = PTR_ERR(mixer->mod_clk);
0458         goto err_disable_bus_clk;
0459     }
0460 
0461     /*
0462      * It seems that we need to enforce that rate for whatever
0463      * reason for the mixer to be functional. Make sure it's the
0464      * case.
0465      */
0466     if (mixer->cfg->mod_rate)
0467         clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate);
0468 
0469     clk_prepare_enable(mixer->mod_clk);
0470 
0471     list_add_tail(&mixer->engine.list, &drv->engine_list);
0472 
0473     base = sun8i_blender_base(mixer);
0474 
0475     /* Reset registers and disable unused sub-engines */
0476     if (mixer->cfg->is_de3) {
0477         for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4)
0478             regmap_write(mixer->engine.regs, i, 0);
0479 
0480         regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0);
0481         regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0);
0482         regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0);
0483         regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0);
0484         regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0);
0485         regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0);
0486         regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0);
0487         regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0);
0488         regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0);
0489         regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0);
0490     } else {
0491         for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4)
0492             regmap_write(mixer->engine.regs, i, 0);
0493 
0494         regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0);
0495         regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0);
0496         regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0);
0497         regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0);
0498         regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0);
0499         regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0);
0500         regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0);
0501     }
0502 
0503     /* Enable the mixer */
0504     regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
0505              SUN8I_MIXER_GLOBAL_CTL_RT_EN);
0506 
0507     /* Set background color to black */
0508     regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
0509              SUN8I_MIXER_BLEND_COLOR_BLACK);
0510 
0511     /*
0512      * Set fill color of bottom plane to black. Generally not needed
0513      * except when VI plane is at bottom (zpos = 0) and enabled.
0514      */
0515     regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
0516              SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
0517     regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
0518              SUN8I_MIXER_BLEND_COLOR_BLACK);
0519 
0520     plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
0521     for (i = 0; i < plane_cnt; i++)
0522         regmap_write(mixer->engine.regs,
0523                  SUN8I_MIXER_BLEND_MODE(base, i),
0524                  SUN8I_MIXER_BLEND_MODE_DEF);
0525 
0526     regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
0527                SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0);
0528 
0529     return 0;
0530 
0531 err_disable_bus_clk:
0532     clk_disable_unprepare(mixer->bus_clk);
0533 err_assert_reset:
0534     reset_control_assert(mixer->reset);
0535     return ret;
0536 }
0537 
0538 static void sun8i_mixer_unbind(struct device *dev, struct device *master,
0539                  void *data)
0540 {
0541     struct sun8i_mixer *mixer = dev_get_drvdata(dev);
0542 
0543     list_del(&mixer->engine.list);
0544 
0545     clk_disable_unprepare(mixer->mod_clk);
0546     clk_disable_unprepare(mixer->bus_clk);
0547     reset_control_assert(mixer->reset);
0548 }
0549 
0550 static const struct component_ops sun8i_mixer_ops = {
0551     .bind   = sun8i_mixer_bind,
0552     .unbind = sun8i_mixer_unbind,
0553 };
0554 
0555 static int sun8i_mixer_probe(struct platform_device *pdev)
0556 {
0557     return component_add(&pdev->dev, &sun8i_mixer_ops);
0558 }
0559 
0560 static int sun8i_mixer_remove(struct platform_device *pdev)
0561 {
0562     component_del(&pdev->dev, &sun8i_mixer_ops);
0563 
0564     return 0;
0565 }
0566 
0567 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
0568     .ccsc       = CCSC_MIXER0_LAYOUT,
0569     .scaler_mask    = 0xf,
0570     .scanline_yuv   = 2048,
0571     .ui_num     = 3,
0572     .vi_num     = 1,
0573 };
0574 
0575 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
0576     .ccsc       = CCSC_MIXER1_LAYOUT,
0577     .scaler_mask    = 0x3,
0578     .scanline_yuv   = 2048,
0579     .ui_num     = 1,
0580     .vi_num     = 1,
0581 };
0582 
0583 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
0584     .ccsc       = CCSC_MIXER0_LAYOUT,
0585     .mod_rate   = 432000000,
0586     .scaler_mask    = 0xf,
0587     .scanline_yuv   = 2048,
0588     .ui_num     = 3,
0589     .vi_num     = 1,
0590 };
0591 
0592 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
0593     .ccsc       = CCSC_MIXER0_LAYOUT,
0594     .mod_rate   = 297000000,
0595     .scaler_mask    = 0xf,
0596     .scanline_yuv   = 2048,
0597     .ui_num     = 3,
0598     .vi_num     = 1,
0599 };
0600 
0601 static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
0602     .ccsc       = CCSC_MIXER1_LAYOUT,
0603     .mod_rate   = 297000000,
0604     .scaler_mask    = 0x3,
0605     .scanline_yuv   = 2048,
0606     .ui_num     = 1,
0607     .vi_num     = 1,
0608 };
0609 
0610 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
0611     .vi_num = 2,
0612     .ui_num = 1,
0613     .scaler_mask = 0x3,
0614     .scanline_yuv = 2048,
0615     .ccsc = CCSC_MIXER0_LAYOUT,
0616     .mod_rate = 150000000,
0617 };
0618 
0619 static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = {
0620     .ccsc       = CCSC_D1_MIXER0_LAYOUT,
0621     .mod_rate   = 297000000,
0622     .scaler_mask    = 0x3,
0623     .scanline_yuv   = 2048,
0624     .ui_num     = 1,
0625     .vi_num     = 1,
0626 };
0627 
0628 static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = {
0629     .ccsc       = CCSC_MIXER1_LAYOUT,
0630     .mod_rate   = 297000000,
0631     .scaler_mask    = 0x1,
0632     .scanline_yuv   = 1024,
0633     .ui_num     = 0,
0634     .vi_num     = 1,
0635 };
0636 
0637 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
0638     .ccsc       = CCSC_MIXER0_LAYOUT,
0639     .mod_rate   = 297000000,
0640     .scaler_mask    = 0xf,
0641     .scanline_yuv   = 4096,
0642     .ui_num     = 3,
0643     .vi_num     = 1,
0644 };
0645 
0646 static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
0647     .ccsc       = CCSC_MIXER1_LAYOUT,
0648     .mod_rate   = 297000000,
0649     .scaler_mask    = 0x3,
0650     .scanline_yuv   = 2048,
0651     .ui_num     = 1,
0652     .vi_num     = 1,
0653 };
0654 
0655 static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
0656     .ccsc       = CCSC_MIXER0_LAYOUT,
0657     .is_de3     = true,
0658     .mod_rate   = 600000000,
0659     .scaler_mask    = 0xf,
0660     .scanline_yuv   = 4096,
0661     .ui_num     = 3,
0662     .vi_num     = 1,
0663 };
0664 
0665 static const struct of_device_id sun8i_mixer_of_table[] = {
0666     {
0667         .compatible = "allwinner,sun8i-a83t-de2-mixer-0",
0668         .data = &sun8i_a83t_mixer0_cfg,
0669     },
0670     {
0671         .compatible = "allwinner,sun8i-a83t-de2-mixer-1",
0672         .data = &sun8i_a83t_mixer1_cfg,
0673     },
0674     {
0675         .compatible = "allwinner,sun8i-h3-de2-mixer-0",
0676         .data = &sun8i_h3_mixer0_cfg,
0677     },
0678     {
0679         .compatible = "allwinner,sun8i-r40-de2-mixer-0",
0680         .data = &sun8i_r40_mixer0_cfg,
0681     },
0682     {
0683         .compatible = "allwinner,sun8i-r40-de2-mixer-1",
0684         .data = &sun8i_r40_mixer1_cfg,
0685     },
0686     {
0687         .compatible = "allwinner,sun8i-v3s-de2-mixer",
0688         .data = &sun8i_v3s_mixer_cfg,
0689     },
0690     {
0691         .compatible = "allwinner,sun20i-d1-de2-mixer-0",
0692         .data = &sun20i_d1_mixer0_cfg,
0693     },
0694     {
0695         .compatible = "allwinner,sun20i-d1-de2-mixer-1",
0696         .data = &sun20i_d1_mixer1_cfg,
0697     },
0698     {
0699         .compatible = "allwinner,sun50i-a64-de2-mixer-0",
0700         .data = &sun50i_a64_mixer0_cfg,
0701     },
0702     {
0703         .compatible = "allwinner,sun50i-a64-de2-mixer-1",
0704         .data = &sun50i_a64_mixer1_cfg,
0705     },
0706     {
0707         .compatible = "allwinner,sun50i-h6-de3-mixer-0",
0708         .data = &sun50i_h6_mixer0_cfg,
0709     },
0710     { }
0711 };
0712 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
0713 
0714 static struct platform_driver sun8i_mixer_platform_driver = {
0715     .probe      = sun8i_mixer_probe,
0716     .remove     = sun8i_mixer_remove,
0717     .driver     = {
0718         .name       = "sun8i-mixer",
0719         .of_match_table = sun8i_mixer_of_table,
0720     },
0721 };
0722 module_platform_driver(sun8i_mixer_platform_driver);
0723 
0724 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
0725 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
0726 MODULE_LICENSE("GPL");