Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright © 2016 Intel Corporation
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  */
0022 
0023 #include <linux/delay.h>
0024 #include <linux/errno.h>
0025 #include <linux/export.h>
0026 #include <linux/i2c.h>
0027 #include <linux/slab.h>
0028 #include <linux/string.h>
0029 
0030 #include <drm/display/drm_dp_dual_mode_helper.h>
0031 #include <drm/drm_device.h>
0032 #include <drm/drm_print.h>
0033 
0034 /**
0035  * DOC: dp dual mode helpers
0036  *
0037  * Helper functions to deal with DP dual mode (aka. DP++) adaptors.
0038  *
0039  * Type 1:
0040  * Adaptor registers (if any) and the sink DDC bus may be accessed via I2C.
0041  *
0042  * Type 2:
0043  * Adaptor registers and sink DDC bus can be accessed either via I2C or
0044  * I2C-over-AUX. Source devices may choose to implement either of these
0045  * access methods.
0046  */
0047 
0048 #define DP_DUAL_MODE_SLAVE_ADDRESS 0x40
0049 
0050 /**
0051  * drm_dp_dual_mode_read - Read from the DP dual mode adaptor register(s)
0052  * @adapter: I2C adapter for the DDC bus
0053  * @offset: register offset
0054  * @buffer: buffer for return data
0055  * @size: sizo of the buffer
0056  *
0057  * Reads @size bytes from the DP dual mode adaptor registers
0058  * starting at @offset.
0059  *
0060  * Returns:
0061  * 0 on success, negative error code on failure
0062  */
0063 ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
0064                   u8 offset, void *buffer, size_t size)
0065 {
0066     struct i2c_msg msgs[] = {
0067         {
0068             .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
0069             .flags = 0,
0070             .len = 1,
0071             .buf = &offset,
0072         },
0073         {
0074             .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
0075             .flags = I2C_M_RD,
0076             .len = size,
0077             .buf = buffer,
0078         },
0079     };
0080     int ret;
0081 
0082     ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
0083     if (ret < 0)
0084         return ret;
0085     if (ret != ARRAY_SIZE(msgs))
0086         return -EPROTO;
0087 
0088     return 0;
0089 }
0090 EXPORT_SYMBOL(drm_dp_dual_mode_read);
0091 
0092 /**
0093  * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s)
0094  * @adapter: I2C adapter for the DDC bus
0095  * @offset: register offset
0096  * @buffer: buffer for write data
0097  * @size: sizo of the buffer
0098  *
0099  * Writes @size bytes to the DP dual mode adaptor registers
0100  * starting at @offset.
0101  *
0102  * Returns:
0103  * 0 on success, negative error code on failure
0104  */
0105 ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
0106                    u8 offset, const void *buffer, size_t size)
0107 {
0108     struct i2c_msg msg = {
0109         .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
0110         .flags = 0,
0111         .len = 1 + size,
0112         .buf = NULL,
0113     };
0114     void *data;
0115     int ret;
0116 
0117     data = kmalloc(msg.len, GFP_KERNEL);
0118     if (!data)
0119         return -ENOMEM;
0120 
0121     msg.buf = data;
0122 
0123     memcpy(data, &offset, 1);
0124     memcpy(data + 1, buffer, size);
0125 
0126     ret = i2c_transfer(adapter, &msg, 1);
0127 
0128     kfree(data);
0129 
0130     if (ret < 0)
0131         return ret;
0132     if (ret != 1)
0133         return -EPROTO;
0134 
0135     return 0;
0136 }
0137 EXPORT_SYMBOL(drm_dp_dual_mode_write);
0138 
0139 static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
0140 {
0141     static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
0142         "DP-HDMI ADAPTOR\x04";
0143 
0144     return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
0145               sizeof(dp_dual_mode_hdmi_id)) == 0;
0146 }
0147 
0148 static bool is_type1_adaptor(uint8_t adaptor_id)
0149 {
0150     return adaptor_id == 0 || adaptor_id == 0xff;
0151 }
0152 
0153 static bool is_type2_adaptor(uint8_t adaptor_id)
0154 {
0155     return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
0156                   DP_DUAL_MODE_REV_TYPE2);
0157 }
0158 
0159 static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
0160                   const uint8_t adaptor_id)
0161 {
0162     return is_hdmi_adaptor(hdmi_id) &&
0163         (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
0164          DP_DUAL_MODE_TYPE_HAS_DPCD));
0165 }
0166 
0167 /**
0168  * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor
0169  * @dev: &drm_device to use
0170  * @adapter: I2C adapter for the DDC bus
0171  *
0172  * Attempt to identify the type of the DP dual mode adaptor used.
0173  *
0174  * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not
0175  * certain whether we're dealing with a native HDMI port or
0176  * a type 1 DVI dual mode adaptor. The driver will have to use
0177  * some other hardware/driver specific mechanism to make that
0178  * distinction.
0179  *
0180  * Returns:
0181  * The type of the DP dual mode adaptor used
0182  */
0183 enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
0184                            struct i2c_adapter *adapter)
0185 {
0186     char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
0187     uint8_t adaptor_id = 0x00;
0188     ssize_t ret;
0189 
0190     /*
0191      * Let's see if the adaptor is there the by reading the
0192      * HDMI ID registers.
0193      *
0194      * Note that type 1 DVI adaptors are not required to implemnt
0195      * any registers, and that presents a problem for detection.
0196      * If the i2c transfer is nacked, we may or may not be dealing
0197      * with a type 1 DVI adaptor. Some other mechanism of detecting
0198      * the presence of the adaptor is required. One way would be
0199      * to check the state of the CONFIG1 pin, Another method would
0200      * simply require the driver to know whether the port is a DP++
0201      * port or a native HDMI port. Both of these methods are entirely
0202      * hardware/driver specific so we can't deal with them here.
0203      */
0204     ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
0205                     hdmi_id, sizeof(hdmi_id));
0206     drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
0207             ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
0208     if (ret)
0209         return DRM_DP_DUAL_MODE_UNKNOWN;
0210 
0211     /*
0212      * Sigh. Some (maybe all?) type 1 adaptors are broken and ack
0213      * the offset but ignore it, and instead they just always return
0214      * data from the start of the HDMI ID buffer. So for a broken
0215      * type 1 HDMI adaptor a single byte read will always give us
0216      * 0x44, and for a type 1 DVI adaptor it should give 0x00
0217      * (assuming it implements any registers). Fortunately neither
0218      * of those values will match the type 2 signature of the
0219      * DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with
0220      * the type 2 adaptor detection safely even in the presence
0221      * of broken type 1 adaptors.
0222      */
0223     ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
0224                     &adaptor_id, sizeof(adaptor_id));
0225     drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
0226     if (ret == 0) {
0227         if (is_lspcon_adaptor(hdmi_id, adaptor_id))
0228             return DRM_DP_DUAL_MODE_LSPCON;
0229         if (is_type2_adaptor(adaptor_id)) {
0230             if (is_hdmi_adaptor(hdmi_id))
0231                 return DRM_DP_DUAL_MODE_TYPE2_HDMI;
0232             else
0233                 return DRM_DP_DUAL_MODE_TYPE2_DVI;
0234         }
0235         /*
0236          * If neither a proper type 1 ID nor a broken type 1 adaptor
0237          * as described above, assume type 1, but let the user know
0238          * that we may have misdetected the type.
0239          */
0240         if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
0241             drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
0242 
0243     }
0244 
0245     if (is_hdmi_adaptor(hdmi_id))
0246         return DRM_DP_DUAL_MODE_TYPE1_HDMI;
0247     else
0248         return DRM_DP_DUAL_MODE_TYPE1_DVI;
0249 }
0250 EXPORT_SYMBOL(drm_dp_dual_mode_detect);
0251 
0252 /**
0253  * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor
0254  * @dev: &drm_device to use
0255  * @type: DP dual mode adaptor type
0256  * @adapter: I2C adapter for the DDC bus
0257  *
0258  * Determine the max TMDS clock the adaptor supports based on the
0259  * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK
0260  * register (on type2 adaptors). As some type 1 adaptors have
0261  * problems with registers (see comments in drm_dp_dual_mode_detect())
0262  * we don't read the register on those, instead we simply assume
0263  * a 165 MHz limit based on the specification.
0264  *
0265  * Returns:
0266  * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz.
0267  */
0268 int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
0269                     struct i2c_adapter *adapter)
0270 {
0271     uint8_t max_tmds_clock;
0272     ssize_t ret;
0273 
0274     /* native HDMI so no limit */
0275     if (type == DRM_DP_DUAL_MODE_NONE)
0276         return 0;
0277 
0278     /*
0279      * Type 1 adaptors are limited to 165MHz
0280      * Type 2 adaptors can tells us their limit
0281      */
0282     if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
0283         return 165000;
0284 
0285     ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
0286                     &max_tmds_clock, sizeof(max_tmds_clock));
0287     if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
0288         drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
0289         return 165000;
0290     }
0291 
0292     return max_tmds_clock * 5000 / 2;
0293 }
0294 EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
0295 
0296 /**
0297  * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor
0298  * @dev: &drm_device to use
0299  * @type: DP dual mode adaptor type
0300  * @adapter: I2C adapter for the DDC bus
0301  * @enabled: current state of the TMDS output buffers
0302  *
0303  * Get the state of the TMDS output buffers in the adaptor. For
0304  * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN
0305  * register. As some type 1 adaptors have problems with registers
0306  * (see comments in drm_dp_dual_mode_detect()) we don't read the
0307  * register on those, instead we simply assume that the buffers
0308  * are always enabled.
0309  *
0310  * Returns:
0311  * 0 on success, negative error code on failure
0312  */
0313 int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
0314                      enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
0315                      bool *enabled)
0316 {
0317     uint8_t tmds_oen;
0318     ssize_t ret;
0319 
0320     if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
0321         *enabled = true;
0322         return 0;
0323     }
0324 
0325     ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
0326                     &tmds_oen, sizeof(tmds_oen));
0327     if (ret) {
0328         drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
0329         return ret;
0330     }
0331 
0332     *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
0333 
0334     return 0;
0335 }
0336 EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
0337 
0338 /**
0339  * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor
0340  * @dev: &drm_device to use
0341  * @type: DP dual mode adaptor type
0342  * @adapter: I2C adapter for the DDC bus
0343  * @enable: enable (as opposed to disable) the TMDS output buffers
0344  *
0345  * Set the state of the TMDS output buffers in the adaptor. For
0346  * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As
0347  * some type 1 adaptors have problems with registers (see comments
0348  * in drm_dp_dual_mode_detect()) we avoid touching the register,
0349  * making this function a no-op on type 1 adaptors.
0350  *
0351  * Returns:
0352  * 0 on success, negative error code on failure
0353  */
0354 int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
0355                      struct i2c_adapter *adapter, bool enable)
0356 {
0357     uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
0358     ssize_t ret;
0359     int retry;
0360 
0361     if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
0362         return 0;
0363 
0364     /*
0365      * LSPCON adapters in low-power state may ignore the first write, so
0366      * read back and verify the written value a few times.
0367      */
0368     for (retry = 0; retry < 3; retry++) {
0369         uint8_t tmp;
0370 
0371         ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
0372                          &tmds_oen, sizeof(tmds_oen));
0373         if (ret) {
0374             drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
0375                     enable ? "enable" : "disable", retry + 1);
0376             return ret;
0377         }
0378 
0379         ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
0380                         &tmp, sizeof(tmp));
0381         if (ret) {
0382             drm_dbg_kms(dev,
0383                     "I2C read failed during TMDS output buffer %s (%d attempts)\n",
0384                     enable ? "enabling" : "disabling", retry + 1);
0385             return ret;
0386         }
0387 
0388         if (tmp == tmds_oen)
0389             return 0;
0390     }
0391 
0392     drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
0393             enable ? "enabling" : "disabling");
0394 
0395     return -EIO;
0396 }
0397 EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
0398 
0399 /**
0400  * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string
0401  * @type: DP dual mode adaptor type
0402  *
0403  * Returns:
0404  * String representation of the DP dual mode adaptor type
0405  */
0406 const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
0407 {
0408     switch (type) {
0409     case DRM_DP_DUAL_MODE_NONE:
0410         return "none";
0411     case DRM_DP_DUAL_MODE_TYPE1_DVI:
0412         return "type 1 DVI";
0413     case DRM_DP_DUAL_MODE_TYPE1_HDMI:
0414         return "type 1 HDMI";
0415     case DRM_DP_DUAL_MODE_TYPE2_DVI:
0416         return "type 2 DVI";
0417     case DRM_DP_DUAL_MODE_TYPE2_HDMI:
0418         return "type 2 HDMI";
0419     case DRM_DP_DUAL_MODE_LSPCON:
0420         return "lspcon";
0421     default:
0422         WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
0423         return "unknown";
0424     }
0425 }
0426 EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
0427 
0428 /**
0429  * drm_lspcon_get_mode: Get LSPCON's current mode of operation by
0430  * reading offset (0x80, 0x41)
0431  * @dev: &drm_device to use
0432  * @adapter: I2C-over-aux adapter
0433  * @mode: current lspcon mode of operation output variable
0434  *
0435  * Returns:
0436  * 0 on success, sets the current_mode value to appropriate mode
0437  * -error on failure
0438  */
0439 int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
0440             enum drm_lspcon_mode *mode)
0441 {
0442     u8 data;
0443     int ret = 0;
0444     int retry;
0445 
0446     if (!mode) {
0447         drm_err(dev, "NULL input\n");
0448         return -EINVAL;
0449     }
0450 
0451     /* Read Status: i2c over aux */
0452     for (retry = 0; retry < 6; retry++) {
0453         if (retry)
0454             usleep_range(500, 1000);
0455 
0456         ret = drm_dp_dual_mode_read(adapter,
0457                         DP_DUAL_MODE_LSPCON_CURRENT_MODE,
0458                         &data, sizeof(data));
0459         if (!ret)
0460             break;
0461     }
0462 
0463     if (ret < 0) {
0464         drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
0465         return -EFAULT;
0466     }
0467 
0468     if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
0469         *mode = DRM_LSPCON_MODE_PCON;
0470     else
0471         *mode = DRM_LSPCON_MODE_LS;
0472     return 0;
0473 }
0474 EXPORT_SYMBOL(drm_lspcon_get_mode);
0475 
0476 /**
0477  * drm_lspcon_set_mode: Change LSPCON's mode of operation by
0478  * writing offset (0x80, 0x40)
0479  * @dev: &drm_device to use
0480  * @adapter: I2C-over-aux adapter
0481  * @mode: required mode of operation
0482  *
0483  * Returns:
0484  * 0 on success, -error on failure/timeout
0485  */
0486 int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
0487             enum drm_lspcon_mode mode)
0488 {
0489     u8 data = 0;
0490     int ret;
0491     int time_out = 200;
0492     enum drm_lspcon_mode current_mode;
0493 
0494     if (mode == DRM_LSPCON_MODE_PCON)
0495         data = DP_DUAL_MODE_LSPCON_MODE_PCON;
0496 
0497     /* Change mode */
0498     ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
0499                      &data, sizeof(data));
0500     if (ret < 0) {
0501         drm_err(dev, "LSPCON mode change failed\n");
0502         return ret;
0503     }
0504 
0505     /*
0506      * Confirm mode change by reading the status bit.
0507      * Sometimes, it takes a while to change the mode,
0508      * so wait and retry until time out or done.
0509      */
0510     do {
0511         ret = drm_lspcon_get_mode(dev, adapter, &current_mode);
0512         if (ret) {
0513             drm_err(dev, "can't confirm LSPCON mode change\n");
0514             return ret;
0515         } else {
0516             if (current_mode != mode) {
0517                 msleep(10);
0518                 time_out -= 10;
0519             } else {
0520                 drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
0521                         mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
0522                 return 0;
0523             }
0524         }
0525     } while (time_out);
0526 
0527     drm_err(dev, "LSPCON mode change timed out\n");
0528     return -ETIMEDOUT;
0529 }
0530 EXPORT_SYMBOL(drm_lspcon_set_mode);