Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
0004  */
0005 
0006 #include <linux/gpio/consumer.h>
0007 #include <linux/i2c.h>
0008 #include <linux/interrupt.h>
0009 #include <linux/media-bus-format.h>
0010 #include <linux/module.h>
0011 #include <linux/mutex.h>
0012 #include <linux/of.h>
0013 #include <linux/of_device.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/regulator/consumer.h>
0016 
0017 #include <drm/drm_atomic_helper.h>
0018 #include <drm/drm_bridge.h>
0019 #include <drm/drm_edid.h>
0020 
0021 struct display_connector {
0022     struct drm_bridge   bridge;
0023 
0024     struct gpio_desc    *hpd_gpio;
0025     int         hpd_irq;
0026 
0027     struct regulator    *dp_pwr;
0028     struct gpio_desc    *ddc_en;
0029 };
0030 
0031 static inline struct display_connector *
0032 to_display_connector(struct drm_bridge *bridge)
0033 {
0034     return container_of(bridge, struct display_connector, bridge);
0035 }
0036 
0037 static int display_connector_attach(struct drm_bridge *bridge,
0038                     enum drm_bridge_attach_flags flags)
0039 {
0040     return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
0041 }
0042 
0043 static enum drm_connector_status
0044 display_connector_detect(struct drm_bridge *bridge)
0045 {
0046     struct display_connector *conn = to_display_connector(bridge);
0047 
0048     if (conn->hpd_gpio) {
0049         if (gpiod_get_value_cansleep(conn->hpd_gpio))
0050             return connector_status_connected;
0051         else
0052             return connector_status_disconnected;
0053     }
0054 
0055     if (conn->bridge.ddc && drm_probe_ddc(conn->bridge.ddc))
0056         return connector_status_connected;
0057 
0058     switch (conn->bridge.type) {
0059     case DRM_MODE_CONNECTOR_DVIA:
0060     case DRM_MODE_CONNECTOR_DVID:
0061     case DRM_MODE_CONNECTOR_DVII:
0062     case DRM_MODE_CONNECTOR_HDMIA:
0063     case DRM_MODE_CONNECTOR_HDMIB:
0064         /*
0065          * For DVI and HDMI connectors a DDC probe failure indicates
0066          * that no cable is connected.
0067          */
0068         return connector_status_disconnected;
0069 
0070     case DRM_MODE_CONNECTOR_Composite:
0071     case DRM_MODE_CONNECTOR_SVIDEO:
0072     case DRM_MODE_CONNECTOR_VGA:
0073     default:
0074         /*
0075          * Composite and S-Video connectors have no other detection
0076          * mean than the HPD GPIO. For VGA connectors, even if we have
0077          * an I2C bus, we can't assume that the cable is disconnected
0078          * if drm_probe_ddc fails, as some cables don't wire the DDC
0079          * pins.
0080          */
0081         return connector_status_unknown;
0082     }
0083 }
0084 
0085 static struct edid *display_connector_get_edid(struct drm_bridge *bridge,
0086                            struct drm_connector *connector)
0087 {
0088     struct display_connector *conn = to_display_connector(bridge);
0089 
0090     return drm_get_edid(connector, conn->bridge.ddc);
0091 }
0092 
0093 /*
0094  * Since this bridge is tied to the connector, it acts like a passthrough,
0095  * so concerning the output bus formats, either pass the bus formats from the
0096  * previous bridge or return fallback data like done in the bridge function:
0097  * drm_atomic_bridge_chain_select_bus_fmts().
0098  * This supports negotiation if the bridge chain has all bits in place.
0099  */
0100 static u32 *display_connector_get_output_bus_fmts(struct drm_bridge *bridge,
0101                     struct drm_bridge_state *bridge_state,
0102                     struct drm_crtc_state *crtc_state,
0103                     struct drm_connector_state *conn_state,
0104                     unsigned int *num_output_fmts)
0105 {
0106     struct drm_bridge *prev_bridge = drm_bridge_get_prev_bridge(bridge);
0107     struct drm_bridge_state *prev_bridge_state;
0108 
0109     if (!prev_bridge || !prev_bridge->funcs->atomic_get_output_bus_fmts) {
0110         struct drm_connector *conn = conn_state->connector;
0111         u32 *out_bus_fmts;
0112 
0113         *num_output_fmts = 1;
0114         out_bus_fmts = kmalloc(sizeof(*out_bus_fmts), GFP_KERNEL);
0115         if (!out_bus_fmts)
0116             return NULL;
0117 
0118         if (conn->display_info.num_bus_formats &&
0119             conn->display_info.bus_formats)
0120             out_bus_fmts[0] = conn->display_info.bus_formats[0];
0121         else
0122             out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED;
0123 
0124         return out_bus_fmts;
0125     }
0126 
0127     prev_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
0128                                 prev_bridge);
0129 
0130     return prev_bridge->funcs->atomic_get_output_bus_fmts(prev_bridge, prev_bridge_state,
0131                                   crtc_state, conn_state,
0132                                   num_output_fmts);
0133 }
0134 
0135 /*
0136  * Since this bridge is tied to the connector, it acts like a passthrough,
0137  * so concerning the input bus formats, either pass the bus formats from the
0138  * previous bridge or MEDIA_BUS_FMT_FIXED (like select_bus_fmt_recursive())
0139  * when atomic_get_input_bus_fmts is not supported.
0140  * This supports negotiation if the bridge chain has all bits in place.
0141  */
0142 static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge,
0143                     struct drm_bridge_state *bridge_state,
0144                     struct drm_crtc_state *crtc_state,
0145                     struct drm_connector_state *conn_state,
0146                     u32 output_fmt,
0147                     unsigned int *num_input_fmts)
0148 {
0149     struct drm_bridge *prev_bridge = drm_bridge_get_prev_bridge(bridge);
0150     struct drm_bridge_state *prev_bridge_state;
0151 
0152     if (!prev_bridge || !prev_bridge->funcs->atomic_get_input_bus_fmts) {
0153         u32 *in_bus_fmts;
0154 
0155         *num_input_fmts = 1;
0156         in_bus_fmts = kmalloc(sizeof(*in_bus_fmts), GFP_KERNEL);
0157         if (!in_bus_fmts)
0158             return NULL;
0159 
0160         in_bus_fmts[0] = MEDIA_BUS_FMT_FIXED;
0161 
0162         return in_bus_fmts;
0163     }
0164 
0165     prev_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
0166                                 prev_bridge);
0167 
0168     return prev_bridge->funcs->atomic_get_input_bus_fmts(prev_bridge, prev_bridge_state,
0169                                  crtc_state, conn_state, output_fmt,
0170                                  num_input_fmts);
0171 }
0172 
0173 static const struct drm_bridge_funcs display_connector_bridge_funcs = {
0174     .attach = display_connector_attach,
0175     .detect = display_connector_detect,
0176     .get_edid = display_connector_get_edid,
0177     .atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts,
0178     .atomic_get_input_bus_fmts = display_connector_get_input_bus_fmts,
0179     .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
0180     .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
0181     .atomic_reset = drm_atomic_helper_bridge_reset,
0182 };
0183 
0184 static irqreturn_t display_connector_hpd_irq(int irq, void *arg)
0185 {
0186     struct display_connector *conn = arg;
0187     struct drm_bridge *bridge = &conn->bridge;
0188 
0189     drm_bridge_hpd_notify(bridge, display_connector_detect(bridge));
0190 
0191     return IRQ_HANDLED;
0192 }
0193 
0194 static int display_connector_probe(struct platform_device *pdev)
0195 {
0196     struct display_connector *conn;
0197     unsigned int type;
0198     const char *label = NULL;
0199     int ret;
0200 
0201     conn = devm_kzalloc(&pdev->dev, sizeof(*conn), GFP_KERNEL);
0202     if (!conn)
0203         return -ENOMEM;
0204 
0205     platform_set_drvdata(pdev, conn);
0206 
0207     type = (uintptr_t)of_device_get_match_data(&pdev->dev);
0208 
0209     /* Get the exact connector type. */
0210     switch (type) {
0211     case DRM_MODE_CONNECTOR_DVII: {
0212         bool analog, digital;
0213 
0214         analog = of_property_read_bool(pdev->dev.of_node, "analog");
0215         digital = of_property_read_bool(pdev->dev.of_node, "digital");
0216         if (analog && !digital) {
0217             conn->bridge.type = DRM_MODE_CONNECTOR_DVIA;
0218         } else if (!analog && digital) {
0219             conn->bridge.type = DRM_MODE_CONNECTOR_DVID;
0220         } else if (analog && digital) {
0221             conn->bridge.type = DRM_MODE_CONNECTOR_DVII;
0222         } else {
0223             dev_err(&pdev->dev, "DVI connector with no type\n");
0224             return -EINVAL;
0225         }
0226         break;
0227     }
0228 
0229     case DRM_MODE_CONNECTOR_HDMIA: {
0230         const char *hdmi_type;
0231 
0232         ret = of_property_read_string(pdev->dev.of_node, "type",
0233                           &hdmi_type);
0234         if (ret < 0) {
0235             dev_err(&pdev->dev, "HDMI connector with no type\n");
0236             return -EINVAL;
0237         }
0238 
0239         if (!strcmp(hdmi_type, "a") || !strcmp(hdmi_type, "c") ||
0240             !strcmp(hdmi_type, "d") || !strcmp(hdmi_type, "e")) {
0241             conn->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
0242         } else if (!strcmp(hdmi_type, "b")) {
0243             conn->bridge.type = DRM_MODE_CONNECTOR_HDMIB;
0244         } else {
0245             dev_err(&pdev->dev,
0246                 "Unsupported HDMI connector type '%s'\n",
0247                 hdmi_type);
0248             return -EINVAL;
0249         }
0250 
0251         break;
0252     }
0253 
0254     default:
0255         conn->bridge.type = type;
0256         break;
0257     }
0258 
0259     /* All the supported connector types support interlaced modes. */
0260     conn->bridge.interlace_allowed = true;
0261 
0262     /* Get the optional connector label. */
0263     of_property_read_string(pdev->dev.of_node, "label", &label);
0264 
0265     /*
0266      * Get the HPD GPIO for DVI, HDMI and DP connectors. If the GPIO can provide
0267      * edge interrupts, register an interrupt handler.
0268      */
0269     if (type == DRM_MODE_CONNECTOR_DVII ||
0270         type == DRM_MODE_CONNECTOR_HDMIA ||
0271         type == DRM_MODE_CONNECTOR_DisplayPort) {
0272         conn->hpd_gpio = devm_gpiod_get_optional(&pdev->dev, "hpd",
0273                              GPIOD_IN);
0274         if (IS_ERR(conn->hpd_gpio)) {
0275             if (PTR_ERR(conn->hpd_gpio) != -EPROBE_DEFER)
0276                 dev_err(&pdev->dev,
0277                     "Unable to retrieve HPD GPIO\n");
0278             return PTR_ERR(conn->hpd_gpio);
0279         }
0280 
0281         conn->hpd_irq = gpiod_to_irq(conn->hpd_gpio);
0282     } else {
0283         conn->hpd_irq = -EINVAL;
0284     }
0285 
0286     if (conn->hpd_irq >= 0) {
0287         ret = devm_request_threaded_irq(&pdev->dev, conn->hpd_irq,
0288                         NULL, display_connector_hpd_irq,
0289                         IRQF_TRIGGER_RISING |
0290                         IRQF_TRIGGER_FALLING |
0291                         IRQF_ONESHOT,
0292                         "HPD", conn);
0293         if (ret) {
0294             dev_info(&pdev->dev,
0295                  "Failed to request HPD edge interrupt, falling back to polling\n");
0296             conn->hpd_irq = -EINVAL;
0297         }
0298     }
0299 
0300     /* Retrieve the DDC I2C adapter for DVI, HDMI and VGA connectors. */
0301     if (type == DRM_MODE_CONNECTOR_DVII ||
0302         type == DRM_MODE_CONNECTOR_HDMIA ||
0303         type == DRM_MODE_CONNECTOR_VGA) {
0304         struct device_node *phandle;
0305 
0306         phandle = of_parse_phandle(pdev->dev.of_node, "ddc-i2c-bus", 0);
0307         if (phandle) {
0308             conn->bridge.ddc = of_get_i2c_adapter_by_node(phandle);
0309             of_node_put(phandle);
0310             if (!conn->bridge.ddc)
0311                 return -EPROBE_DEFER;
0312         } else {
0313             dev_dbg(&pdev->dev,
0314                 "No I2C bus specified, disabling EDID readout\n");
0315         }
0316     }
0317 
0318     /* Get the DP PWR for DP connector. */
0319     if (type == DRM_MODE_CONNECTOR_DisplayPort) {
0320         int ret;
0321 
0322         conn->dp_pwr = devm_regulator_get_optional(&pdev->dev, "dp-pwr");
0323 
0324         if (IS_ERR(conn->dp_pwr)) {
0325             ret = PTR_ERR(conn->dp_pwr);
0326 
0327             switch (ret) {
0328             case -ENODEV:
0329                 conn->dp_pwr = NULL;
0330                 break;
0331 
0332             case -EPROBE_DEFER:
0333                 return -EPROBE_DEFER;
0334 
0335             default:
0336                 dev_err(&pdev->dev, "failed to get DP PWR regulator: %d\n", ret);
0337                 return ret;
0338             }
0339         }
0340 
0341         if (conn->dp_pwr) {
0342             ret = regulator_enable(conn->dp_pwr);
0343             if (ret) {
0344                 dev_err(&pdev->dev, "failed to enable DP PWR regulator: %d\n", ret);
0345                 return ret;
0346             }
0347         }
0348     }
0349 
0350     /* enable DDC */
0351     if (type == DRM_MODE_CONNECTOR_HDMIA) {
0352         conn->ddc_en = devm_gpiod_get_optional(&pdev->dev, "ddc-en",
0353                                GPIOD_OUT_HIGH);
0354 
0355         if (IS_ERR(conn->ddc_en)) {
0356             dev_err(&pdev->dev, "Couldn't get ddc-en gpio\n");
0357             return PTR_ERR(conn->ddc_en);
0358         }
0359     }
0360 
0361     conn->bridge.funcs = &display_connector_bridge_funcs;
0362     conn->bridge.of_node = pdev->dev.of_node;
0363 
0364     if (conn->bridge.ddc)
0365         conn->bridge.ops |= DRM_BRIDGE_OP_EDID
0366                  |  DRM_BRIDGE_OP_DETECT;
0367     if (conn->hpd_gpio)
0368         conn->bridge.ops |= DRM_BRIDGE_OP_DETECT;
0369     if (conn->hpd_irq >= 0)
0370         conn->bridge.ops |= DRM_BRIDGE_OP_HPD;
0371 
0372     dev_dbg(&pdev->dev,
0373         "Found %s display connector '%s' %s DDC bus and %s HPD GPIO (ops 0x%x)\n",
0374         drm_get_connector_type_name(conn->bridge.type),
0375         label ? label : "<unlabelled>",
0376         conn->bridge.ddc ? "with" : "without",
0377         conn->hpd_gpio ? "with" : "without",
0378         conn->bridge.ops);
0379 
0380     drm_bridge_add(&conn->bridge);
0381 
0382     return 0;
0383 }
0384 
0385 static int display_connector_remove(struct platform_device *pdev)
0386 {
0387     struct display_connector *conn = platform_get_drvdata(pdev);
0388 
0389     if (conn->ddc_en)
0390         gpiod_set_value(conn->ddc_en, 0);
0391 
0392     if (conn->dp_pwr)
0393         regulator_disable(conn->dp_pwr);
0394 
0395     drm_bridge_remove(&conn->bridge);
0396 
0397     if (!IS_ERR(conn->bridge.ddc))
0398         i2c_put_adapter(conn->bridge.ddc);
0399 
0400     return 0;
0401 }
0402 
0403 static const struct of_device_id display_connector_match[] = {
0404     {
0405         .compatible = "composite-video-connector",
0406         .data = (void *)DRM_MODE_CONNECTOR_Composite,
0407     }, {
0408         .compatible = "dvi-connector",
0409         .data = (void *)DRM_MODE_CONNECTOR_DVII,
0410     }, {
0411         .compatible = "hdmi-connector",
0412         .data = (void *)DRM_MODE_CONNECTOR_HDMIA,
0413     }, {
0414         .compatible = "svideo-connector",
0415         .data = (void *)DRM_MODE_CONNECTOR_SVIDEO,
0416     }, {
0417         .compatible = "vga-connector",
0418         .data = (void *)DRM_MODE_CONNECTOR_VGA,
0419     }, {
0420         .compatible = "dp-connector",
0421         .data = (void *)DRM_MODE_CONNECTOR_DisplayPort,
0422     },
0423     {},
0424 };
0425 MODULE_DEVICE_TABLE(of, display_connector_match);
0426 
0427 static struct platform_driver display_connector_driver = {
0428     .probe  = display_connector_probe,
0429     .remove = display_connector_remove,
0430     .driver     = {
0431         .name       = "display-connector",
0432         .of_match_table = display_connector_match,
0433     },
0434 };
0435 module_platform_driver(display_connector_driver);
0436 
0437 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
0438 MODULE_DESCRIPTION("Display connector driver");
0439 MODULE_LICENSE("GPL");