summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/tilcdc/tilcdc_panel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/tilcdc/tilcdc_panel.c')
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_panel.c217
1 files changed, 94 insertions, 123 deletions
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c
index 86c67329b605..262f290d85d9 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c
@@ -1,34 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Texas Instruments
* Author: Rob Clark <robdclark@gmail.com>
- *
- * 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.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <linux/pinctrl/pinmux.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+
#include <video/display_timing.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
#include "tilcdc_drv.h"
+#include "tilcdc_panel.h"
struct panel_module {
struct tilcdc_module base;
struct tilcdc_panel_info *info;
struct display_timings *timings;
struct backlight_device *backlight;
+ struct gpio_desc *enable_gpio;
};
#define to_panel_module(x) container_of(x, struct panel_module, base)
@@ -43,40 +41,26 @@ struct panel_encoder {
};
#define to_panel_encoder(x) container_of(x, struct panel_encoder, base)
-
-static void panel_encoder_destroy(struct drm_encoder *encoder)
-{
- struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
- drm_encoder_cleanup(encoder);
- kfree(panel_encoder);
-}
-
static void panel_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
struct backlight_device *backlight = panel_encoder->mod->backlight;
+ struct gpio_desc *gpio = panel_encoder->mod->enable_gpio;
- if (!backlight)
- return;
-
- backlight->props.power = mode == DRM_MODE_DPMS_ON
- ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
- backlight_update_status(backlight);
-}
+ if (backlight) {
+ backlight->props.power = mode == DRM_MODE_DPMS_ON ?
+ BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
+ backlight_update_status(backlight);
+ }
-static bool panel_encoder_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- /* nothing needed */
- return true;
+ if (gpio)
+ gpiod_set_value_cansleep(gpio,
+ mode == DRM_MODE_DPMS_ON ? 1 : 0);
}
static void panel_encoder_prepare(struct drm_encoder *encoder)
{
- struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
- tilcdc_crtc_set_panel_info(encoder->crtc, panel_encoder->mod->info);
}
static void panel_encoder_commit(struct drm_encoder *encoder)
@@ -91,13 +75,8 @@ static void panel_encoder_mode_set(struct drm_encoder *encoder,
/* nothing needed */
}
-static const struct drm_encoder_funcs panel_encoder_funcs = {
- .destroy = panel_encoder_destroy,
-};
-
static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = {
.dpms = panel_encoder_dpms,
- .mode_fixup = panel_encoder_mode_fixup,
.prepare = panel_encoder_prepare,
.commit = panel_encoder_commit,
.mode_set = panel_encoder_mode_set,
@@ -110,19 +89,17 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev,
struct drm_encoder *encoder;
int ret;
- panel_encoder = kzalloc(sizeof(*panel_encoder), GFP_KERNEL);
- if (!panel_encoder) {
- dev_err(dev->dev, "allocation failed\n");
+ panel_encoder = devm_kzalloc(dev->dev, sizeof(*panel_encoder),
+ GFP_KERNEL);
+ if (!panel_encoder)
return NULL;
- }
panel_encoder->mod = mod;
encoder = &panel_encoder->base;
encoder->possible_crtcs = 1;
- ret = drm_encoder_init(dev, encoder, &panel_encoder_funcs,
- DRM_MODE_ENCODER_LVDS);
+ ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
if (ret < 0)
goto fail;
@@ -131,7 +108,7 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev,
return encoder;
fail:
- panel_encoder_destroy(encoder);
+ drm_encoder_cleanup(encoder);
return NULL;
}
@@ -150,16 +127,8 @@ struct panel_connector {
static void panel_connector_destroy(struct drm_connector *connector)
{
- struct panel_connector *panel_connector = to_panel_connector(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
- kfree(panel_connector);
-}
-
-static enum drm_connector_status panel_connector_detect(
- struct drm_connector *connector,
- bool force)
-{
- return connector_status_connected;
}
static int panel_connector_get_modes(struct drm_connector *connector)
@@ -170,12 +139,16 @@ static int panel_connector_get_modes(struct drm_connector *connector)
int i;
for (i = 0; i < timings->num_timings; i++) {
- struct drm_display_mode *mode = drm_mode_create(dev);
+ struct drm_display_mode *mode;
struct videomode vm;
if (videomode_from_timings(timings, &vm, i))
break;
+ mode = drm_mode_create(dev);
+ if (!mode)
+ break;
+
drm_display_mode_from_videomode(&vm, mode);
mode->type = DRM_MODE_TYPE_DRIVER;
@@ -190,14 +163,6 @@ static int panel_connector_get_modes(struct drm_connector *connector)
return i;
}
-static int panel_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- struct tilcdc_drm_private *priv = connector->dev->dev_private;
- /* our only constraints are what the crtc can generate: */
- return tilcdc_crtc_mode_valid(priv->crtc, mode);
-}
-
static struct drm_encoder *panel_connector_best_encoder(
struct drm_connector *connector)
{
@@ -207,14 +172,14 @@ static struct drm_encoder *panel_connector_best_encoder(
static const struct drm_connector_funcs panel_connector_funcs = {
.destroy = panel_connector_destroy,
- .dpms = drm_helper_connector_dpms,
- .detect = panel_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static const struct drm_connector_helper_funcs panel_connector_helper_funcs = {
.get_modes = panel_connector_get_modes,
- .mode_valid = panel_connector_mode_valid,
.best_encoder = panel_connector_best_encoder,
};
@@ -225,11 +190,10 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev,
struct drm_connector *connector;
int ret;
- panel_connector = kzalloc(sizeof(*panel_connector), GFP_KERNEL);
- if (!panel_connector) {
- dev_err(dev->dev, "allocation failed\n");
+ panel_connector = devm_kzalloc(dev->dev, sizeof(*panel_connector),
+ GFP_KERNEL);
+ if (!panel_connector)
return NULL;
- }
panel_connector->encoder = encoder;
panel_connector->mod = mod;
@@ -243,12 +207,10 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev,
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
- ret = drm_mode_connector_attach_encoder(connector, encoder);
+ ret = drm_connector_attach_encoder(connector, encoder);
if (ret)
goto fail;
- drm_sysfs_connector_add(connector);
-
return connector;
fail:
@@ -278,26 +240,14 @@ static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
priv->encoders[priv->num_encoders++] = encoder;
priv->connectors[priv->num_connectors++] = connector;
- return 0;
-}
+ tilcdc_crtc_set_panel_info(priv->crtc,
+ to_panel_encoder(encoder)->mod->info);
-static void panel_destroy(struct tilcdc_module *mod)
-{
- struct panel_module *panel_mod = to_panel_module(mod);
-
- if (panel_mod->timings) {
- display_timings_release(panel_mod->timings);
- kfree(panel_mod->timings);
- }
-
- tilcdc_module_cleanup(mod);
- kfree(panel_mod->info);
- kfree(panel_mod);
+ return 0;
}
static const struct tilcdc_module_ops panel_module_ops = {
.modeset_init = panel_modeset_init,
- .destroy = panel_destroy,
};
/*
@@ -323,10 +273,8 @@ static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np)
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- pr_err("%s: allocation failed\n", __func__);
- return NULL;
- }
+ if (!info)
+ goto put_node;
ret |= of_property_read_u32(info_np, "ac-bias", &info->ac_bias);
ret |= of_property_read_u32(info_np, "ac-bias-intrpt", &info->ac_bias_intrpt);
@@ -345,22 +293,21 @@ static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np)
if (ret) {
pr_err("%s: error reading panel-info properties\n", __func__);
kfree(info);
- return NULL;
+ info = NULL;
}
+put_node:
+ of_node_put(info_np);
return info;
}
-static struct of_device_id panel_of_match[];
-
static int panel_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
+ struct backlight_device *backlight;
struct panel_module *panel_mod;
struct tilcdc_module *mod;
- struct pinctrl *pinctrl;
- int ret = -EINVAL;
-
+ int ret;
/* bail out early if no DT data: */
if (!node) {
@@ -368,60 +315,84 @@ static int panel_probe(struct platform_device *pdev)
return -ENXIO;
}
- panel_mod = kzalloc(sizeof(*panel_mod), GFP_KERNEL);
+ panel_mod = devm_kzalloc(&pdev->dev, sizeof(*panel_mod), GFP_KERNEL);
if (!panel_mod)
return -ENOMEM;
- mod = &panel_mod->base;
+ backlight = devm_of_find_backlight(&pdev->dev);
+ if (IS_ERR(backlight))
+ return PTR_ERR(backlight);
+ panel_mod->backlight = backlight;
+
+ panel_mod->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(panel_mod->enable_gpio)) {
+ ret = PTR_ERR(panel_mod->enable_gpio);
+ dev_err(&pdev->dev, "failed to request enable GPIO\n");
+ goto fail_backlight;
+ }
- tilcdc_module_init(mod, "panel", &panel_module_ops);
+ if (panel_mod->enable_gpio)
+ dev_info(&pdev->dev, "found enable GPIO\n");
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl))
- dev_warn(&pdev->dev, "pins are not configured\n");
+ mod = &panel_mod->base;
+ pdev->dev.platform_data = mod;
+ tilcdc_module_init(mod, "panel", &panel_module_ops);
panel_mod->timings = of_get_display_timings(node);
if (!panel_mod->timings) {
dev_err(&pdev->dev, "could not get panel timings\n");
- goto fail;
+ ret = -EINVAL;
+ goto fail_free;
}
panel_mod->info = of_get_panel_info(node);
if (!panel_mod->info) {
dev_err(&pdev->dev, "could not get panel info\n");
- goto fail;
+ ret = -EINVAL;
+ goto fail_timings;
}
- mod->preferred_bpp = panel_mod->info->bpp;
+ return 0;
- panel_mod->backlight = of_find_backlight_by_node(node);
- if (panel_mod->backlight)
- dev_info(&pdev->dev, "found backlight\n");
+fail_timings:
+ display_timings_release(panel_mod->timings);
- return 0;
+fail_free:
+ tilcdc_module_cleanup(mod);
-fail:
- panel_destroy(mod);
+fail_backlight:
+ if (panel_mod->backlight)
+ put_device(&panel_mod->backlight->dev);
return ret;
}
-static int panel_remove(struct platform_device *pdev)
+static void panel_remove(struct platform_device *pdev)
{
- return 0;
+ struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);
+ struct panel_module *panel_mod = to_panel_module(mod);
+ struct backlight_device *backlight = panel_mod->backlight;
+
+ if (backlight)
+ put_device(&backlight->dev);
+
+ display_timings_release(panel_mod->timings);
+
+ tilcdc_module_cleanup(mod);
+ kfree(panel_mod->info);
}
-static struct of_device_id panel_of_match[] = {
+static const struct of_device_id panel_of_match[] = {
{ .compatible = "ti,tilcdc,panel", },
{ },
};
-struct platform_driver panel_driver = {
+static struct platform_driver panel_driver = {
.probe = panel_probe,
.remove = panel_remove,
.driver = {
- .owner = THIS_MODULE,
- .name = "panel",
+ .name = "tilcdc-panel",
.of_match_table = panel_of_match,
},
};