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/platform_device.h>
9  #include <drm/drm_of.h>
10  
11  #include "dcss-dev.h"
12  #include "dcss-kms.h"
13  
14  struct dcss_drv {
15  	struct dcss_dev *dcss;
16  	struct dcss_kms_dev *kms;
17  };
18  
dcss_drv_dev_to_dcss(struct device * dev)19  struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev)
20  {
21  	struct dcss_drv *mdrv = dev_get_drvdata(dev);
22  
23  	return mdrv ? mdrv->dcss : NULL;
24  }
25  
dcss_drv_dev_to_drm(struct device * dev)26  struct drm_device *dcss_drv_dev_to_drm(struct device *dev)
27  {
28  	struct dcss_drv *mdrv = dev_get_drvdata(dev);
29  
30  	return mdrv ? &mdrv->kms->base : NULL;
31  }
32  
dcss_drv_platform_probe(struct platform_device * pdev)33  static int dcss_drv_platform_probe(struct platform_device *pdev)
34  {
35  	struct device *dev = &pdev->dev;
36  	struct device_node *remote;
37  	struct dcss_drv *mdrv;
38  	int err = 0;
39  	bool hdmi_output = true;
40  
41  	if (!dev->of_node)
42  		return -ENODEV;
43  
44  	remote = of_graph_get_remote_node(dev->of_node, 0, 0);
45  	if (!remote)
46  		return -ENODEV;
47  
48  	hdmi_output = !of_device_is_compatible(remote, "fsl,imx8mq-nwl-dsi");
49  
50  	of_node_put(remote);
51  
52  	mdrv = kzalloc(sizeof(*mdrv), GFP_KERNEL);
53  	if (!mdrv)
54  		return -ENOMEM;
55  
56  	mdrv->dcss = dcss_dev_create(dev, hdmi_output);
57  	if (IS_ERR(mdrv->dcss)) {
58  		err = PTR_ERR(mdrv->dcss);
59  		goto err;
60  	}
61  
62  	dev_set_drvdata(dev, mdrv);
63  
64  	mdrv->kms = dcss_kms_attach(mdrv->dcss);
65  	if (IS_ERR(mdrv->kms)) {
66  		err = PTR_ERR(mdrv->kms);
67  		goto dcss_shutoff;
68  	}
69  
70  	return 0;
71  
72  dcss_shutoff:
73  	dcss_dev_destroy(mdrv->dcss);
74  
75  	dev_set_drvdata(dev, NULL);
76  
77  err:
78  	kfree(mdrv);
79  	return err;
80  }
81  
dcss_drv_platform_remove(struct platform_device * pdev)82  static int dcss_drv_platform_remove(struct platform_device *pdev)
83  {
84  	struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev);
85  
86  	if (!mdrv)
87  		return 0;
88  
89  	dcss_kms_detach(mdrv->kms);
90  	dcss_dev_destroy(mdrv->dcss);
91  
92  	dev_set_drvdata(&pdev->dev, NULL);
93  
94  	kfree(mdrv);
95  
96  	return 0;
97  }
98  
99  static struct dcss_type_data dcss_types[] = {
100  	[DCSS_IMX8MQ] = {
101  		.name = "DCSS_IMX8MQ",
102  		.blkctl_ofs = 0x2F000,
103  		.ctxld_ofs = 0x23000,
104  		.dtg_ofs = 0x20000,
105  		.scaler_ofs = 0x1C000,
106  		.ss_ofs = 0x1B000,
107  		.dpr_ofs = 0x18000,
108  	},
109  };
110  
111  static const struct of_device_id dcss_of_match[] = {
112  	{ .compatible = "nxp,imx8mq-dcss", .data = &dcss_types[DCSS_IMX8MQ], },
113  	{},
114  };
115  
116  MODULE_DEVICE_TABLE(of, dcss_of_match);
117  
118  static const struct dev_pm_ops dcss_dev_pm = {
119  	SET_SYSTEM_SLEEP_PM_OPS(dcss_dev_suspend, dcss_dev_resume)
120  	SET_RUNTIME_PM_OPS(dcss_dev_runtime_suspend,
121  			   dcss_dev_runtime_resume, NULL)
122  };
123  
124  static struct platform_driver dcss_platform_driver = {
125  	.probe	= dcss_drv_platform_probe,
126  	.remove	= dcss_drv_platform_remove,
127  	.driver	= {
128  		.name = "imx-dcss",
129  		.of_match_table	= dcss_of_match,
130  		.pm = &dcss_dev_pm,
131  	},
132  };
133  
134  module_platform_driver(dcss_platform_driver);
135  
136  MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@nxp.com>");
137  MODULE_DESCRIPTION("DCSS driver for i.MX8MQ");
138  MODULE_LICENSE("GPL v2");
139