@@ -2,6 +2,7 @@
#include <linux/aperture.h>
#include <linux/clk.h>
+#include <linux/interconnect.h>
#include <linux/minmax.h>
#include <linux/of_address.h>
#include <linux/of_clk.h>
@@ -225,6 +226,10 @@ struct simpledrm_device {
struct device **pwr_dom_devs;
struct device_link **pwr_dom_links;
#endif
+#if defined CONFIG_OF && defined CONFIG_INTERCONNECT
+ unsigned int icc_count;
+ struct icc_path **icc_paths;
+#endif
/* modesetting */
u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)];
@@ -547,6 +552,81 @@ static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev)
}
#endif
+#if defined CONFIG_OF && defined CONFIG_INTERCONNECT
+/*
+ * Generic interconnect path handling code.
+ */
+static void simpledrm_device_detach_icc(void *res)
+{
+ struct simpledrm_device *sdev = res;
+ int i;
+
+ for (i = sdev->icc_count - 1; i >= 0; i--) {
+ if (!IS_ERR_OR_NULL(sdev->icc_paths[i]))
+ icc_put(sdev->icc_paths[i]);
+ }
+}
+
+static int simpledrm_device_attach_icc(struct simpledrm_device *sdev)
+{
+ struct device *dev = sdev->sysfb.dev.dev;
+ int ret, count, i;
+
+ count = of_count_phandle_with_args(dev->of_node, "interconnects",
+ "#interconnect-cells");
+ if (count < 0)
+ return 0;
+
+ /* An interconnect path consists of two elements */
+ if (count % 2) {
+ drm_err(&sdev->sysfb.dev,
+ "invalid interconnects value\n");
+ return -EINVAL;
+ }
+ sdev->icc_count = count / 2;
+
+ sdev->icc_paths = devm_kcalloc(dev, sdev->icc_count,
+ sizeof(*sdev->icc_paths),
+ GFP_KERNEL);
+ if (!sdev->icc_paths)
+ return -ENOMEM;
+
+ for (i = 0; i < sdev->icc_count; i++) {
+ sdev->icc_paths[i] = of_icc_get_by_index(dev, i);
+ if (IS_ERR_OR_NULL(sdev->icc_paths[i])) {
+ ret = PTR_ERR(sdev->icc_paths[i]);
+ if (ret == -EPROBE_DEFER)
+ goto err;
+ drm_err(&sdev->sysfb.dev, "failed to get interconnect path %u: %d\n",
+ i, ret);
+ continue;
+ }
+
+ ret = icc_set_bw(sdev->icc_paths[i], 0, UINT_MAX);
+ if (ret) {
+ drm_err(&sdev->sysfb.dev, "failed to set interconnect bandwidth %u: %d\n",
+ i, ret);
+ continue;
+ }
+ }
+
+ return devm_add_action_or_reset(dev, simpledrm_device_detach_icc, sdev);
+
+err:
+ while (i) {
+ --i;
+ if (!IS_ERR_OR_NULL(sdev->icc_paths[i]))
+ icc_put(sdev->icc_paths[i]);
+ }
+ return ret;
+}
+#else
+static int simpledrm_device_attach_icc(struct simpledrm_device *sdev)
+{
+ return 0;
+}
+#endif
+
/*
* Modesetting
*/
@@ -633,6 +713,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
if (ret)
return ERR_PTR(ret);
ret = simpledrm_device_attach_genpd(sdev);
+ if (ret)
+ return ERR_PTR(ret);
+ ret = simpledrm_device_attach_icc(sdev);
if (ret)
return ERR_PTR(ret);