Driver registration and OF binding.

Signed-off-by: Davor Joja <[email protected]>
---
 drivers/video/fbdev/xylon/xylonfb_main.c | 526 +++++++++++++++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/xylonfb_main.c

diff --git a/drivers/video/fbdev/xylon/xylonfb_main.c 
b/drivers/video/fbdev/xylon/xylonfb_main.c
new file mode 100644
index 0000000..5777360
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_main.c
@@ -0,0 +1,526 @@
+/*
+ * Xylon logiCVC frame buffer Open Firmware driver
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "xylonfb_core.h"
+#include "logicvc.h"
+
+static void xylonfb_init_ctrl(struct device_node *dn, enum display_flags flags,
+                             u32 *ctrl)
+{
+       u32 ctrl_reg = (LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+                       LOGICVC_CTRL_DATA_ENABLE);
+
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       if (of_property_read_bool(dn, "hsync-active-low") ||
+           (flags & DISPLAY_FLAGS_HSYNC_LOW))
+               ctrl_reg |= LOGICVC_CTRL_HSYNC_INVERT;
+       if (of_property_read_bool(dn, "vsync-active-low") ||
+           (flags & DISPLAY_FLAGS_VSYNC_LOW))
+               ctrl_reg |= LOGICVC_CTRL_VSYNC_INVERT;
+       if (of_property_read_bool(dn, "data-enable-active-low") ||
+           (flags & DISPLAY_FLAGS_DE_LOW))
+               ctrl_reg |= LOGICVC_CTRL_DATA_ENABLE_INVERT;
+       if (of_property_read_bool(dn, "pixel-data-invert"))
+               ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_INVERT;
+       if (of_property_read_bool(dn, "pixel-data-output-trigger-high") ||
+           (flags & DISPLAY_FLAGS_PIXDATA_POSEDGE))
+               ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_TRIGGER_INVERT;
+
+       *ctrl = ctrl_reg;
+}
+
+static int xylonfb_layer_set_format(struct xylonfb_layer_fix_data *fd,
+                                   struct device *dev)
+{
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       switch (fd->type) {
+       case LOGICVC_LAYER_ALPHA:
+               fd->format = XYLONFB_FORMAT_A8;
+               break;
+
+       case LOGICVC_LAYER_RGB:
+               switch (fd->bpp) {
+               case 8:
+                       switch (fd->transparency) {
+                       case LOGICVC_ALPHA_CLUT_16BPP:
+                               fd->format = XYLONFB_FORMAT_C8;
+                               fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB6565;
+                               break;
+                       case LOGICVC_ALPHA_CLUT_32BPP:
+                               fd->format = XYLONFB_FORMAT_C8;
+                               fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB8888;
+                               break;
+                       case LOGICVC_ALPHA_LAYER:
+                               fd->format = XYLONFB_FORMAT_RGB332;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               case 16:
+                       if (fd->transparency != LOGICVC_ALPHA_LAYER)
+                               return -EINVAL;
+
+                       fd->format = XYLONFB_FORMAT_RGB565;
+                       break;
+               case 32:
+                       switch (fd->transparency) {
+                       case LOGICVC_ALPHA_LAYER:
+                               fd->format = XYLONFB_FORMAT_XRGB8888;
+                               break;
+                       case LOGICVC_ALPHA_PIXEL:
+                               fd->format = XYLONFB_FORMAT_ARGB8888;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               }
+               break;
+
+       case LOGICVC_LAYER_YUV:
+               switch (fd->bpp) {
+               case 8:
+                       if (fd->transparency != LOGICVC_ALPHA_CLUT_32BPP)
+                               return -EINVAL;
+
+                       fd->format = XYLONFB_FORMAT_C8;
+                       fd->format_clut = XYLONFB_FORMAT_CLUT_AYUV8888;
+                       break;
+               case 16:
+                       if (fd->transparency != LOGICVC_ALPHA_LAYER)
+                               return -EINVAL;
+
+                       fd->format = XYLONFB_FORMAT_YUYV;
+                       break;
+               case 32:
+                       if (fd->transparency != LOGICVC_ALPHA_PIXEL)
+                               return -EINVAL;
+
+                       fd->format = XYLONFB_FORMAT_AYUV;
+                       break;
+               }
+               break;
+
+       default:
+               dev_err(dev, "unsupported layer type\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int xylonfb_parse_layer_info(struct device_node *parent_dn,
+                                   struct xylonfb_data *data, int id)
+{
+       struct device *dev = &data->pdev->dev;
+       struct device_node *dn;
+       struct xylonfb_layer_fix_data *fd;
+       int ret;
+       char layer_name[10];
+       const char *string;
+
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       snprintf(layer_name, sizeof(layer_name), "layer_%d", id);
+       dn = of_get_child_by_name(parent_dn, layer_name);
+       if (!dn)
+               return 0;
+
+       data->layers++;
+
+       fd = devm_kzalloc(&data->pdev->dev,
+                         sizeof(struct xylonfb_layer_fix_data), GFP_KERNEL);
+       if (!fd) {
+               dev_err(dev, "failed allocate layer fix data (%d)\n", id);
+               return -ENOMEM;
+       }
+
+       data->fd[id] = fd;
+
+       fd->id = id;
+
+       ret = of_property_read_u32(dn, "address", &fd->address);
+       if (ret && (ret != -EINVAL)) {
+               dev_err(dev, "failed get address\n");
+               return ret;
+       }
+       ret = of_property_read_u32_index(dn, "address", 1, &fd->address_range);
+
+       ret = of_property_read_u32(dn, "buffer-offset", &fd->buffer_offset);
+       if (ret && (ret != -EINVAL)) {
+               dev_err(dev, "failed get buffer-offset\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(dn, "bits-per-pixel", &fd->bpp);
+       if (ret) {
+               dev_err(dev, "failed get bits-per-pixel\n");
+               return ret;
+       }
+       switch (fd->bpp) {
+       case 8:
+       case 16:
+       case 32:
+               break;
+       default:
+               dev_err(dev, "invalid bits-per-pixel value\n");
+               return -EINVAL;
+       }
+
+       ret = of_property_read_string(dn, "type", &string);
+       if (ret) {
+               dev_err(dev, "failed get type\n");
+               return ret;
+       }
+       if (!strcmp(string, "alpha")) {
+               fd->type = LOGICVC_LAYER_ALPHA;
+       } else if (!strcmp(string, "rgb")) {
+               fd->type = LOGICVC_LAYER_RGB;
+       } else if (!strcmp(string, "yuv")) {
+               fd->type = LOGICVC_LAYER_YUV;
+       } else {
+               dev_err(dev, "unsupported layer type\n");
+               return -EINVAL;
+       }
+
+       if (fd->type != LOGICVC_LAYER_ALPHA) {
+               ret = of_property_read_string(dn, "transparency", &string);
+               if (ret) {
+                       dev_err(dev, "failed get transparency\n");
+                       return ret;
+               }
+               if (!strcmp(string, "clut16")) {
+                       fd->transparency = LOGICVC_ALPHA_CLUT_16BPP;
+               } else if (!strcmp(string, "clut32")) {
+                       fd->transparency = LOGICVC_ALPHA_CLUT_32BPP;
+               } else if (!strcmp(string, "layer")) {
+                       fd->transparency = LOGICVC_ALPHA_LAYER;
+               } else if (!strcmp(string, "pixel")) {
+                       fd->transparency = LOGICVC_ALPHA_PIXEL;
+               } else {
+                       dev_err(dev, "unsupported layer transparency\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (of_property_read_bool(dn, "component-swap"))
+               fd->component_swap = true;
+
+       fd->width = data->pixel_stride;
+
+       ret = xylonfb_layer_set_format(fd, dev);
+       if (ret) {
+               dev_err(dev, "failed set layer format\n");
+               return ret;
+       }
+
+       of_node_put(dn);
+
+       return id + 1;
+}
+
+static int xylon_parse_hw_info(struct device_node *dn,
+                              struct xylonfb_data *data)
+{
+       struct device *dev = &data->pdev->dev;
+       int ret;
+       const char *string;
+
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       ret = of_property_read_u32(dn, "background-layer-bits-per-pixel",
+                                  &data->bg_layer_bpp);
+       if (ret && (ret != -EINVAL)) {
+               dev_err(dev, "failed get bg-layer-bits-per-pixel\n");
+               return ret;
+       } else if (ret == 0) {
+               data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER;
+
+               ret = of_property_read_string(dn, "background-layer-type",
+                                             &string);
+               if (ret) {
+                       dev_err(dev, "failed get bg-layer-type\n");
+                       return ret;
+               }
+               if (!strcmp(string, "rgb")) {
+                       data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_RGB;
+               } else if (!strcmp(string, "yuv")) {
+                       data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_YUV;
+               } else {
+                       dev_err(dev, "unsupported bg layer type\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (of_property_read_bool(dn, "display-interface-itu656"))
+               data->flags |= XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656;
+
+       if (of_property_read_bool(dn, "readable-regs"))
+               data->flags |= XYLONFB_FLAGS_READABLE_REGS;
+       else
+               dev_warn(dev, "logicvc registers not readable\n");
+
+       if (of_property_read_bool(dn, "size-position"))
+               data->flags |= XYLONFB_FLAGS_SIZE_POSITION;
+       else
+               dev_warn(dev, "logicvc size-position disabled\n");
+
+       ret = of_property_read_u32(dn, "pixel-stride", &data->pixel_stride);
+       if (ret) {
+               dev_err(dev, "failed get pixel-stride\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(dn, "power-delay", &data->pwr_delay);
+       if (ret && (ret != -EINVAL)) {
+               dev_err(dev, "failed get power-delay\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(dn, "signal-delay", &data->sig_delay);
+       if (ret && (ret != -EINVAL)) {
+               dev_err(dev, "failed get signal\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id logicvc_of_match[] = {
+       { .compatible = "xylon,logicvc-3.02.b" },
+       { .compatible = "xylon,logicvc-4.01.b" },
+       {/* end of table */}
+};
+
+static int xylonfb_get_logicvc_configuration(struct xylonfb_data *data)
+{
+       struct device *dev = &data->pdev->dev;
+       struct device_node *dn = data->device;
+       const struct of_device_id *match;
+       struct videomode vm;
+       int i, ret;
+
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       match = of_match_node(logicvc_of_match, dn);
+       if (!match) {
+               dev_err(dev, "failed match logicvc\n");
+               return -ENODEV;
+       }
+
+       ret = of_address_to_resource(dn, 0, &data->resource_mem);
+       if (ret) {
+               dev_err(dev, "failed get mem resource\n");
+               return ret;
+       }
+       data->irq = of_irq_to_resource(dn, 0, &data->resource_irq);
+       if (data->irq == 0) {
+               dev_err(dev, "failed get irq resource\n");
+               return ret;
+       }
+
+       ret = xylon_parse_hw_info(dn, data);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < LOGICVC_MAX_LAYERS; i++) {
+               ret = xylonfb_parse_layer_info(dn, data, i);
+               if (ret < 0)
+                       return ret;
+               if (ret == 0)
+                       break;
+       }
+
+       if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER &&
+           data->layers == LOGICVC_MAX_LAYERS) {
+               data->flags &= ~XYLONFB_FLAGS_BACKGROUND_LAYER;
+               data->layers--;
+               if (data->console_layer == data->layers)
+                       data->console_layer--;
+
+               dev_warn(dev, "invalid last layer configuration\n");
+       }
+
+       if (data->vm.name[0] == 0) {
+               ret = of_get_videomode(dn, &vm, OF_USE_NATIVE_MODE);
+               if (!ret) {
+                       fb_videomode_from_videomode(&vm, &data->vm.vmode);
+
+                       sprintf(data->vm.name, "%dx%d",
+                               data->vm.vmode.xres, data->vm.vmode.yres);
+
+                       data->flags |= XYLONFB_FLAGS_VMODE_CUSTOM;
+               }
+       }
+
+       xylonfb_init_ctrl(dn, vm.flags, &data->vm.ctrl);
+
+       return 0;
+}
+
+static int xylonfb_get_driver_configuration(struct xylonfb_data *data)
+{
+       struct device *dev = &data->pdev->dev;
+       struct device_node *dn = data->pdev->dev.of_node;
+       int ret;
+       const char *string;
+
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       data->device = of_parse_phandle(dn, "device", 0);
+       if (!data->device) {
+               dev_err(dev, "failed get device\n");
+               return -ENODEV;
+       }
+
+       data->pixel_clock = of_parse_phandle(dn, "clocks", 0);
+
+       ret = of_property_read_u32(dn, "console-layer", &data->console_layer);
+       if (ret && (ret != -EINVAL)) {
+                       dev_err(dev, "failed get console-layer\n");
+                       return ret;
+       } else {
+               data->flags |= XYLONFB_FLAGS_CHECK_CONSOLE_LAYER;
+       }
+
+       if (of_property_read_bool(dn, "vsync-irq"))
+               data->flags |= XYLONFB_FLAGS_VSYNC_IRQ;
+
+       ret = of_property_read_string(dn, "video-mode", &string);
+       if (ret && (ret != -EINVAL)) {
+               dev_err(dev, "failed get video-mode\n");
+               return ret;
+       } else if (ret == 0) {
+               strcpy(data->vm.name, string);
+               return 0;
+       }
+
+       return 0;
+}
+
+static int xylonfb_probe(struct platform_device *pdev)
+{
+       struct xylonfb_data *data;
+       int ret;
+
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       data = devm_kzalloc(&pdev->dev, sizeof(struct xylonfb_data),
+                            GFP_KERNEL);
+       if (!data) {
+               dev_err(&pdev->dev, "failed allocate init data\n");
+               return -ENOMEM;
+       }
+
+       data->pdev = pdev;
+
+       ret = xylonfb_get_driver_configuration(data);
+       if (ret)
+               goto xylonfb_probe_error;
+
+       ret = xylonfb_get_logicvc_configuration(data);
+       if (ret)
+               goto xylonfb_probe_error;
+
+       ret = xylonfb_init_core(data);
+
+xylonfb_probe_error:
+       return ret;
+}
+
+static int xylonfb_remove(struct platform_device *pdev)
+{
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       return xylonfb_deinit_core(pdev);
+}
+
+static const struct of_device_id xylonfb_of_match[] = {
+       { .compatible = "xylon,fb-3.00.a" },
+       {/* end of table */},
+};
+MODULE_DEVICE_TABLE(of, xylonfb_of_match);
+
+static struct platform_driver xylonfb_driver = {
+       .probe = xylonfb_probe,
+       .remove = xylonfb_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = XYLONFB_DEVICE_NAME,
+               .of_match_table = xylonfb_of_match,
+       },
+};
+
+static int xylonfb_get_params(char *options)
+{
+       char *this_opt;
+
+       XYLONFB_DBG(INFO, "%s", __func__);
+
+       if (!options || !*options)
+               return 0;
+
+       while ((this_opt = strsep(&options, ",")) != NULL) {
+               if (!*this_opt)
+                       continue;
+               xylonfb_mode_option = this_opt;
+       }
+       return 0;
+}
+
+static int xylonfb_init(void)
+{
+       char *option = NULL;
+       /*
+        *  Kernel boot options (in 'video=xxxfb:<options>' format)
+        */
+       if (fb_get_options(XYLONFB_DRIVER_NAME, &option))
+               return -ENODEV;
+       /* Set internal module parameters */
+       xylonfb_get_params(option);
+
+       if (platform_driver_register(&xylonfb_driver)) {
+               pr_err("failed %s driver registration\n", XYLONFB_DRIVER_NAME);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit xylonfb_exit(void)
+{
+       platform_driver_unregister(&xylonfb_driver);
+}
+
+module_init(xylonfb_init);
+module_exit(xylonfb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(XYLONFB_DRIVER_DESCRIPTION);
+MODULE_VERSION(XYLONFB_DRIVER_VERSION);
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to