1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2019 NXP. |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/kernel.h> |
8 | #include <linux/of.h> |
9 | #include <linux/platform_device.h> |
10 | #include <drm/drm_module.h> |
11 | #include <drm/drm_of.h> |
12 | |
13 | #include "dcss-dev.h" |
14 | #include "dcss-kms.h" |
15 | |
16 | struct dcss_drv { |
17 | struct dcss_dev *dcss; |
18 | struct dcss_kms_dev *kms; |
19 | }; |
20 | |
21 | struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev) |
22 | { |
23 | struct dcss_drv *mdrv = dev_get_drvdata(dev); |
24 | |
25 | return mdrv ? mdrv->dcss : NULL; |
26 | } |
27 | |
28 | struct drm_device *dcss_drv_dev_to_drm(struct device *dev) |
29 | { |
30 | struct dcss_drv *mdrv = dev_get_drvdata(dev); |
31 | |
32 | return mdrv ? &mdrv->kms->base : NULL; |
33 | } |
34 | |
35 | static int dcss_drv_platform_probe(struct platform_device *pdev) |
36 | { |
37 | struct device *dev = &pdev->dev; |
38 | struct device_node *remote; |
39 | struct dcss_drv *mdrv; |
40 | int err = 0; |
41 | bool hdmi_output = true; |
42 | |
43 | if (!dev->of_node) |
44 | return -ENODEV; |
45 | |
46 | remote = of_graph_get_remote_node(node: dev->of_node, port: 0, endpoint: 0); |
47 | if (!remote) |
48 | return -ENODEV; |
49 | |
50 | hdmi_output = !of_device_is_compatible(device: remote, "fsl,imx8mq-nwl-dsi" ); |
51 | |
52 | of_node_put(node: remote); |
53 | |
54 | mdrv = devm_kzalloc(dev, size: sizeof(*mdrv), GFP_KERNEL); |
55 | if (!mdrv) |
56 | return -ENOMEM; |
57 | |
58 | mdrv->dcss = dcss_dev_create(dev, hdmi_output); |
59 | if (IS_ERR(ptr: mdrv->dcss)) |
60 | return PTR_ERR(ptr: mdrv->dcss); |
61 | |
62 | dev_set_drvdata(dev, data: mdrv); |
63 | |
64 | mdrv->kms = dcss_kms_attach(dcss: mdrv->dcss); |
65 | if (IS_ERR(ptr: mdrv->kms)) { |
66 | err = PTR_ERR(ptr: mdrv->kms); |
67 | dev_err_probe(dev, err, fmt: "Failed to initialize KMS\n" ); |
68 | goto dcss_shutoff; |
69 | } |
70 | |
71 | return 0; |
72 | |
73 | dcss_shutoff: |
74 | dcss_dev_destroy(dcss: mdrv->dcss); |
75 | |
76 | return err; |
77 | } |
78 | |
79 | static void dcss_drv_platform_remove(struct platform_device *pdev) |
80 | { |
81 | struct dcss_drv *mdrv = dev_get_drvdata(dev: &pdev->dev); |
82 | |
83 | dcss_kms_detach(kms: mdrv->kms); |
84 | dcss_dev_destroy(dcss: mdrv->dcss); |
85 | } |
86 | |
87 | static void dcss_drv_platform_shutdown(struct platform_device *pdev) |
88 | { |
89 | struct dcss_drv *mdrv = dev_get_drvdata(dev: &pdev->dev); |
90 | |
91 | dcss_kms_shutdown(kms: mdrv->kms); |
92 | } |
93 | |
94 | static struct dcss_type_data dcss_types[] = { |
95 | [DCSS_IMX8MQ] = { |
96 | .name = "DCSS_IMX8MQ" , |
97 | .blkctl_ofs = 0x2F000, |
98 | .ctxld_ofs = 0x23000, |
99 | .dtg_ofs = 0x20000, |
100 | .scaler_ofs = 0x1C000, |
101 | .ss_ofs = 0x1B000, |
102 | .dpr_ofs = 0x18000, |
103 | }, |
104 | }; |
105 | |
106 | static const struct of_device_id dcss_of_match[] = { |
107 | { .compatible = "nxp,imx8mq-dcss" , .data = &dcss_types[DCSS_IMX8MQ], }, |
108 | {}, |
109 | }; |
110 | |
111 | MODULE_DEVICE_TABLE(of, dcss_of_match); |
112 | |
113 | static struct platform_driver dcss_platform_driver = { |
114 | .probe = dcss_drv_platform_probe, |
115 | .remove_new = dcss_drv_platform_remove, |
116 | .shutdown = dcss_drv_platform_shutdown, |
117 | .driver = { |
118 | .name = "imx-dcss" , |
119 | .of_match_table = dcss_of_match, |
120 | .pm = pm_ptr(&dcss_dev_pm_ops), |
121 | }, |
122 | }; |
123 | |
124 | drm_module_platform_driver(dcss_platform_driver); |
125 | |
126 | MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@nxp.com>" ); |
127 | MODULE_DESCRIPTION("DCSS driver for i.MX8MQ" ); |
128 | MODULE_LICENSE("GPL v2" ); |
129 | |