root/drivers/video/fbdev/omap2/omapfb/dss/display.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/drivers/video/omap2/dss/display.c
 *
 * Copyright (C) 2009 Nokia Corporation
 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
 *
 * Some code and ideas taken from drivers/video/omap/ driver
 * by Imre Deak.
 */

#define DSS_SUBSYS_NAME "DISPLAY"

#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/of.h>

#include <video/omapfb_dss.h>
#include "dss.h"
#include "dss_features.h"

void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
                        u16 *xres, u16 *yres)
{
        *xres = dssdev->panel.timings.x_res;
        *yres = dssdev->panel.timings.y_res;
}
EXPORT_SYMBOL(omapdss_default_get_resolution);

int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
{
        switch (dssdev->type) {
        case OMAP_DISPLAY_TYPE_DPI:
                if (dssdev->phy.dpi.data_lines == 24)
                        return 24;
                else
                        return 16;

        case OMAP_DISPLAY_TYPE_DBI:
                if (dssdev->ctrl.pixel_size == 24)
                        return 24;
                else
                        return 16;
        case OMAP_DISPLAY_TYPE_DSI:
                if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16)
                        return 24;
                else
                        return 16;
        case OMAP_DISPLAY_TYPE_VENC:
        case OMAP_DISPLAY_TYPE_SDI:
        case OMAP_DISPLAY_TYPE_HDMI:
        case OMAP_DISPLAY_TYPE_DVI:
                return 24;
        default:
                BUG();
                return 0;
        }
}
EXPORT_SYMBOL(omapdss_default_get_recommended_bpp);

void omapdss_default_get_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
{
        *timings = dssdev->panel.timings;
}
EXPORT_SYMBOL(omapdss_default_get_timings);

int dss_suspend_all_devices(void)
{
        struct omap_dss_device *dssdev = NULL;

        for_each_dss_dev(dssdev) {
                if (!dssdev->driver)
                        continue;

                if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
                        dssdev->driver->disable(dssdev);
                        dssdev->activate_after_resume = true;
                } else {
                        dssdev->activate_after_resume = false;
                }
        }

        return 0;
}

int dss_resume_all_devices(void)
{
        struct omap_dss_device *dssdev = NULL;

        for_each_dss_dev(dssdev) {
                if (!dssdev->driver)
                        continue;

                if (dssdev->activate_after_resume) {
                        dssdev->driver->enable(dssdev);
                        dssdev->activate_after_resume = false;
                }
        }

        return 0;
}

void dss_disable_all_devices(void)
{
        struct omap_dss_device *dssdev = NULL;

        for_each_dss_dev(dssdev) {
                if (!dssdev->driver)
                        continue;

                if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
                        dssdev->driver->disable(dssdev);
        }
}

static LIST_HEAD(panel_list);
static DEFINE_MUTEX(panel_list_mutex);
static int disp_num_counter;

int omapdss_register_display(struct omap_dss_device *dssdev)
{
        struct omap_dss_driver *drv = dssdev->driver;
        int id;

        /*
         * Note: this presumes all the displays are either using DT or non-DT,
         * which normally should be the case. This also presumes that all
         * displays either have an DT alias, or none has.
         */

        if (dssdev->dev->of_node) {
                id = of_alias_get_id(dssdev->dev->of_node, "display");

                if (id < 0)
                        id = disp_num_counter++;
        } else {
                id = disp_num_counter++;
        }

        snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id);

        /* Use 'label' property for name, if it exists */
        if (dssdev->dev->of_node)
                of_property_read_string(dssdev->dev->of_node, "label",
                        &dssdev->name);

        if (dssdev->name == NULL)
                dssdev->name = dssdev->alias;

        if (drv && drv->get_resolution == NULL)
                drv->get_resolution = omapdss_default_get_resolution;
        if (drv && drv->get_recommended_bpp == NULL)
                drv->get_recommended_bpp = omapdss_default_get_recommended_bpp;
        if (drv && drv->get_timings == NULL)
                drv->get_timings = omapdss_default_get_timings;

        mutex_lock(&panel_list_mutex);
        list_add_tail(&dssdev->panel_list, &panel_list);
        mutex_unlock(&panel_list_mutex);
        return 0;
}
EXPORT_SYMBOL(omapdss_register_display);

void omapdss_unregister_display(struct omap_dss_device *dssdev)
{
        mutex_lock(&panel_list_mutex);
        list_del(&dssdev->panel_list);
        mutex_unlock(&panel_list_mutex);
}
EXPORT_SYMBOL(omapdss_unregister_display);

struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev)
{
        if (!try_module_get(dssdev->owner))
                return NULL;

        if (get_device(dssdev->dev) == NULL) {
                module_put(dssdev->owner);
                return NULL;
        }

        return dssdev;
}
EXPORT_SYMBOL(omap_dss_get_device);

void omap_dss_put_device(struct omap_dss_device *dssdev)
{
        put_device(dssdev->dev);
        module_put(dssdev->owner);
}
EXPORT_SYMBOL(omap_dss_put_device);

/*
 * ref count of the found device is incremented.
 * ref count of from-device is decremented.
 */
struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
{
        struct list_head *l;
        struct omap_dss_device *dssdev;

        mutex_lock(&panel_list_mutex);

        if (list_empty(&panel_list)) {
                dssdev = NULL;
                goto out;
        }

        if (from == NULL) {
                dssdev = list_first_entry(&panel_list, struct omap_dss_device,
                                panel_list);
                omap_dss_get_device(dssdev);
                goto out;
        }

        omap_dss_put_device(from);

        list_for_each(l, &panel_list) {
                dssdev = list_entry(l, struct omap_dss_device, panel_list);
                if (dssdev == from) {
                        if (list_is_last(l, &panel_list)) {
                                dssdev = NULL;
                                goto out;
                        }

                        dssdev = list_entry(l->next, struct omap_dss_device,
                                        panel_list);
                        omap_dss_get_device(dssdev);
                        goto out;
                }
        }

        WARN(1, "'from' dssdev not found\n");

        dssdev = NULL;
out:
        mutex_unlock(&panel_list_mutex);
        return dssdev;
}
EXPORT_SYMBOL(omap_dss_get_next_device);

struct omap_dss_device *omap_dss_find_device(void *data,
                int (*match)(struct omap_dss_device *dssdev, void *data))
{
        struct omap_dss_device *dssdev = NULL;

        while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) {
                if (match(dssdev, data))
                        return dssdev;
        }

        return NULL;
}
EXPORT_SYMBOL(omap_dss_find_device);

void videomode_to_omap_video_timings(const struct videomode *vm,
                struct omap_video_timings *ovt)
{
        memset(ovt, 0, sizeof(*ovt));

        ovt->pixelclock = vm->pixelclock;
        ovt->x_res = vm->hactive;
        ovt->hbp = vm->hback_porch;
        ovt->hfp = vm->hfront_porch;
        ovt->hsw = vm->hsync_len;
        ovt->y_res = vm->vactive;
        ovt->vbp = vm->vback_porch;
        ovt->vfp = vm->vfront_porch;
        ovt->vsw = vm->vsync_len;

        ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
                OMAPDSS_SIG_ACTIVE_HIGH :
                OMAPDSS_SIG_ACTIVE_LOW;
        ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
                OMAPDSS_SIG_ACTIVE_HIGH :
                OMAPDSS_SIG_ACTIVE_LOW;
        ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ?
                OMAPDSS_SIG_ACTIVE_HIGH :
                OMAPDSS_SIG_ACTIVE_LOW;
        ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ?
                OMAPDSS_DRIVE_SIG_RISING_EDGE :
                OMAPDSS_DRIVE_SIG_FALLING_EDGE;

        ovt->sync_pclk_edge = ovt->data_pclk_edge;
}
EXPORT_SYMBOL(videomode_to_omap_video_timings);

void omap_video_timings_to_videomode(const struct omap_video_timings *ovt,
                struct videomode *vm)
{
        memset(vm, 0, sizeof(*vm));

        vm->pixelclock = ovt->pixelclock;

        vm->hactive = ovt->x_res;
        vm->hback_porch = ovt->hbp;
        vm->hfront_porch = ovt->hfp;
        vm->hsync_len = ovt->hsw;
        vm->vactive = ovt->y_res;
        vm->vback_porch = ovt->vbp;
        vm->vfront_porch = ovt->vfp;
        vm->vsync_len = ovt->vsw;

        if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
                vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
        else
                vm->flags |= DISPLAY_FLAGS_HSYNC_LOW;

        if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
                vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
        else
                vm->flags |= DISPLAY_FLAGS_VSYNC_LOW;

        if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH)
                vm->flags |= DISPLAY_FLAGS_DE_HIGH;
        else
                vm->flags |= DISPLAY_FLAGS_DE_LOW;

        if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE)
                vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
        else
                vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
}
EXPORT_SYMBOL(omap_video_timings_to_videomode);