| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd |
|---|
| 3 | 4 | * Zheng Yang <zhengyang@rock-chips.com> |
|---|
| 4 | 5 | * Yakir Yang <ykk@rock-chips.com> |
|---|
| 5 | | - * |
|---|
| 6 | | - * This software is licensed under the terms of the GNU General Public |
|---|
| 7 | | - * License version 2, as published by the Free Software Foundation, and |
|---|
| 8 | | - * may be copied, distributed, and modified under those terms. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | | - * GNU General Public License for more details. |
|---|
| 14 | 6 | */ |
|---|
| 15 | 7 | |
|---|
| 16 | 8 | #include <linux/irq.h> |
|---|
| .. | .. |
|---|
| 23 | 15 | #include <linux/mutex.h> |
|---|
| 24 | 16 | #include <linux/of_device.h> |
|---|
| 25 | 17 | |
|---|
| 26 | | -#include <drm/drm_of.h> |
|---|
| 27 | | -#include <drm/drmP.h> |
|---|
| 28 | 18 | #include <drm/drm_atomic_helper.h> |
|---|
| 29 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 30 | 19 | #include <drm/drm_edid.h> |
|---|
| 20 | +#include <drm/drm_of.h> |
|---|
| 21 | +#include <drm/drm_probe_helper.h> |
|---|
| 22 | +#include <drm/drm_simple_kms_helper.h> |
|---|
| 23 | + |
|---|
| 24 | +#include <sound/hdmi-codec.h> |
|---|
| 31 | 25 | |
|---|
| 32 | 26 | #include "rockchip_drm_drv.h" |
|---|
| 33 | 27 | #include "rockchip_drm_vop.h" |
|---|
| .. | .. |
|---|
| 35 | 29 | #include "inno_hdmi.h" |
|---|
| 36 | 30 | |
|---|
| 37 | 31 | #define to_inno_hdmi(x) container_of(x, struct inno_hdmi, x) |
|---|
| 32 | + |
|---|
| 33 | +struct audio_info { |
|---|
| 34 | + int sample_rate; |
|---|
| 35 | + int channels; |
|---|
| 36 | + int sample_width; |
|---|
| 37 | +}; |
|---|
| 38 | 38 | |
|---|
| 39 | 39 | struct hdmi_data_info { |
|---|
| 40 | 40 | int vic; |
|---|
| .. | .. |
|---|
| 55 | 55 | struct completion cmp; |
|---|
| 56 | 56 | }; |
|---|
| 57 | 57 | |
|---|
| 58 | +enum inno_hdmi_dev_type { |
|---|
| 59 | + RK3036_HDMI, |
|---|
| 60 | + RK3128_HDMI, |
|---|
| 61 | +}; |
|---|
| 62 | + |
|---|
| 63 | +struct inno_hdmi_phy_config { |
|---|
| 64 | + unsigned long mpixelclock; |
|---|
| 65 | + u8 pre_emphasis; /* pre-emphasis value */ |
|---|
| 66 | + u8 vlev_ctr; /* voltage level control */ |
|---|
| 67 | +}; |
|---|
| 68 | + |
|---|
| 69 | +struct inno_hdmi_plat_data { |
|---|
| 70 | + enum inno_hdmi_dev_type dev_type; |
|---|
| 71 | + struct inno_hdmi_phy_config *phy_config; |
|---|
| 72 | +}; |
|---|
| 73 | + |
|---|
| 58 | 74 | struct inno_hdmi { |
|---|
| 59 | 75 | struct device *dev; |
|---|
| 60 | 76 | struct drm_device *drm_dev; |
|---|
| 61 | 77 | |
|---|
| 62 | 78 | int irq; |
|---|
| 79 | + struct clk *aclk; |
|---|
| 63 | 80 | struct clk *pclk; |
|---|
| 64 | 81 | void __iomem *regs; |
|---|
| 65 | 82 | |
|---|
| .. | .. |
|---|
| 70 | 87 | struct i2c_adapter *ddc; |
|---|
| 71 | 88 | |
|---|
| 72 | 89 | unsigned int tmds_rate; |
|---|
| 90 | + struct platform_device *audio_pdev; |
|---|
| 91 | + bool audio_enable; |
|---|
| 92 | + const struct inno_hdmi_plat_data *plat_data; |
|---|
| 73 | 93 | |
|---|
| 74 | 94 | struct hdmi_data_info hdmi_data; |
|---|
| 75 | 95 | struct drm_display_mode previous_mode; |
|---|
| .. | .. |
|---|
| 197 | 217 | |
|---|
| 198 | 218 | static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode) |
|---|
| 199 | 219 | { |
|---|
| 220 | + const struct inno_hdmi_phy_config *phy_config = |
|---|
| 221 | + hdmi->plat_data->phy_config; |
|---|
| 222 | + |
|---|
| 200 | 223 | switch (mode) { |
|---|
| 201 | 224 | case NORMAL: |
|---|
| 202 | 225 | inno_hdmi_sys_power(hdmi, false); |
|---|
| 203 | | - |
|---|
| 204 | | - hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f); |
|---|
| 205 | | - hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb); |
|---|
| 226 | + for (; phy_config->mpixelclock != ~0UL; phy_config++) |
|---|
| 227 | + if (hdmi->tmds_rate <= phy_config->mpixelclock) |
|---|
| 228 | + break; |
|---|
| 229 | + if (!phy_config->mpixelclock) |
|---|
| 230 | + return; |
|---|
| 231 | + hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, |
|---|
| 232 | + phy_config->pre_emphasis); |
|---|
| 233 | + hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->vlev_ctr); |
|---|
| 206 | 234 | |
|---|
| 207 | 235 | hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15); |
|---|
| 208 | 236 | hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14); |
|---|
| .. | .. |
|---|
| 289 | 317 | m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1)); |
|---|
| 290 | 318 | } |
|---|
| 291 | 319 | |
|---|
| 320 | +static int inno_hdmi_config_audio_aai(struct inno_hdmi *hdmi, |
|---|
| 321 | + struct audio_info *audio) |
|---|
| 322 | +{ |
|---|
| 323 | + struct hdmi_audio_infoframe *faudio; |
|---|
| 324 | + union hdmi_infoframe frame; |
|---|
| 325 | + int rc; |
|---|
| 326 | + |
|---|
| 327 | + rc = hdmi_audio_infoframe_init(&frame.audio); |
|---|
| 328 | + faudio = (struct hdmi_audio_infoframe *)&frame; |
|---|
| 329 | + |
|---|
| 330 | + faudio->channels = audio->channels; |
|---|
| 331 | + |
|---|
| 332 | + return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AAI, 0, 0, 0); |
|---|
| 333 | +} |
|---|
| 334 | + |
|---|
| 292 | 335 | static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi, |
|---|
| 293 | 336 | struct drm_display_mode *mode) |
|---|
| 294 | 337 | { |
|---|
| 295 | 338 | union hdmi_infoframe frame; |
|---|
| 296 | 339 | int rc; |
|---|
| 297 | 340 | |
|---|
| 298 | | - rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode, false); |
|---|
| 341 | + rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, |
|---|
| 342 | + &hdmi->connector, |
|---|
| 343 | + mode); |
|---|
| 299 | 344 | |
|---|
| 300 | 345 | if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444) |
|---|
| 301 | 346 | frame.avi.colorspace = HDMI_COLORSPACE_YUV444; |
|---|
| .. | .. |
|---|
| 389 | 434 | { |
|---|
| 390 | 435 | int value; |
|---|
| 391 | 436 | |
|---|
| 437 | + if (hdmi->plat_data->dev_type == RK3036_HDMI) { |
|---|
| 438 | + value = BIT(20) | BIT(21); |
|---|
| 439 | + value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? BIT(4) : 0; |
|---|
| 440 | + value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? BIT(5) : 0; |
|---|
| 441 | + hdmi_writeb(hdmi, 0x148, value); |
|---|
| 442 | + } |
|---|
| 392 | 443 | /* Set detail external video timing polarity and interlace mode */ |
|---|
| 393 | 444 | value = v_EXTERANL_VIDEO(1); |
|---|
| 394 | 445 | value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? |
|---|
| .. | .. |
|---|
| 479 | 530 | inno_hdmi_i2c_init(hdmi); |
|---|
| 480 | 531 | |
|---|
| 481 | 532 | /* Unmute video and audio output */ |
|---|
| 482 | | - hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, |
|---|
| 483 | | - v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); |
|---|
| 533 | + hdmi_modb(hdmi, HDMI_AV_MUTE, m_VIDEO_BLACK, v_VIDEO_MUTE(0)); |
|---|
| 534 | + if (hdmi->audio_enable) |
|---|
| 535 | + hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE, v_AUDIO_MUTE(0)); |
|---|
| 484 | 536 | |
|---|
| 485 | 537 | return 0; |
|---|
| 486 | 538 | } |
|---|
| .. | .. |
|---|
| 494 | 546 | inno_hdmi_setup(hdmi, adj_mode); |
|---|
| 495 | 547 | |
|---|
| 496 | 548 | /* Store the display mode for plugin/DPMS poweron events */ |
|---|
| 497 | | - memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode)); |
|---|
| 549 | + drm_mode_copy(&hdmi->previous_mode, adj_mode); |
|---|
| 498 | 550 | } |
|---|
| 499 | 551 | |
|---|
| 500 | 552 | static void inno_hdmi_encoder_enable(struct drm_encoder *encoder) |
|---|
| .. | .. |
|---|
| 539 | 591 | .atomic_check = inno_hdmi_encoder_atomic_check, |
|---|
| 540 | 592 | }; |
|---|
| 541 | 593 | |
|---|
| 542 | | -static struct drm_encoder_funcs inno_hdmi_encoder_funcs = { |
|---|
| 543 | | - .destroy = drm_encoder_cleanup, |
|---|
| 544 | | -}; |
|---|
| 545 | | - |
|---|
| 546 | 594 | static enum drm_connector_status |
|---|
| 547 | 595 | inno_hdmi_connector_detect(struct drm_connector *connector, bool force) |
|---|
| 548 | 596 | { |
|---|
| .. | .. |
|---|
| 556 | 604 | { |
|---|
| 557 | 605 | struct inno_hdmi *hdmi = to_inno_hdmi(connector); |
|---|
| 558 | 606 | struct edid *edid; |
|---|
| 559 | | - struct drm_display_info *info = &connector->display_info; |
|---|
| 560 | 607 | int ret = 0; |
|---|
| 561 | 608 | |
|---|
| 562 | 609 | if (!hdmi->ddc) |
|---|
| .. | .. |
|---|
| 569 | 616 | drm_connector_update_edid_property(connector, edid); |
|---|
| 570 | 617 | ret = drm_add_edid_modes(connector, edid); |
|---|
| 571 | 618 | kfree(edid); |
|---|
| 572 | | - } else { |
|---|
| 573 | | - hdmi->hdmi_data.sink_is_hdmi = true; |
|---|
| 574 | | - hdmi->hdmi_data.sink_has_audio = true; |
|---|
| 575 | | - ret = rockchip_drm_add_modes_noedid(connector); |
|---|
| 576 | | - info->edid_hdmi_dc_modes = 0; |
|---|
| 577 | | - info->hdmi.y420_dc_modes = 0; |
|---|
| 578 | | - info->color_formats = 0; |
|---|
| 579 | | - |
|---|
| 580 | | - dev_info(hdmi->dev, "failed to get edid\n"); |
|---|
| 581 | 619 | } |
|---|
| 582 | 620 | |
|---|
| 583 | 621 | return ret; |
|---|
| .. | .. |
|---|
| 617 | 655 | .mode_valid = inno_hdmi_connector_mode_valid, |
|---|
| 618 | 656 | }; |
|---|
| 619 | 657 | |
|---|
| 658 | +static int |
|---|
| 659 | +inno_hdmi_audio_config_set(struct inno_hdmi *hdmi, |
|---|
| 660 | + struct hdmi_codec_daifmt *daifmt, |
|---|
| 661 | + struct audio_info *audio) |
|---|
| 662 | +{ |
|---|
| 663 | + int rate, N, channel; |
|---|
| 664 | + |
|---|
| 665 | + if (audio->channels < 3) |
|---|
| 666 | + channel = I2S_CHANNEL_1_2; |
|---|
| 667 | + else if (audio->channels < 5) |
|---|
| 668 | + channel = I2S_CHANNEL_3_4; |
|---|
| 669 | + else if (audio->channels < 7) |
|---|
| 670 | + channel = I2S_CHANNEL_5_6; |
|---|
| 671 | + else |
|---|
| 672 | + channel = I2S_CHANNEL_7_8; |
|---|
| 673 | + |
|---|
| 674 | + switch (audio->sample_rate) { |
|---|
| 675 | + case 32000: |
|---|
| 676 | + rate = AUDIO_32K; |
|---|
| 677 | + N = N_32K; |
|---|
| 678 | + break; |
|---|
| 679 | + case 44100: |
|---|
| 680 | + rate = AUDIO_441K; |
|---|
| 681 | + N = N_441K; |
|---|
| 682 | + break; |
|---|
| 683 | + case 48000: |
|---|
| 684 | + rate = AUDIO_48K; |
|---|
| 685 | + N = N_48K; |
|---|
| 686 | + break; |
|---|
| 687 | + case 88200: |
|---|
| 688 | + rate = AUDIO_882K; |
|---|
| 689 | + N = N_882K; |
|---|
| 690 | + break; |
|---|
| 691 | + case 96000: |
|---|
| 692 | + rate = AUDIO_96K; |
|---|
| 693 | + N = N_96K; |
|---|
| 694 | + break; |
|---|
| 695 | + case 176400: |
|---|
| 696 | + rate = AUDIO_1764K; |
|---|
| 697 | + N = N_1764K; |
|---|
| 698 | + break; |
|---|
| 699 | + case 192000: |
|---|
| 700 | + rate = AUDIO_192K; |
|---|
| 701 | + N = N_192K; |
|---|
| 702 | + break; |
|---|
| 703 | + default: |
|---|
| 704 | + dev_err(hdmi->dev, "[%s] not support such sample rate %d\n", |
|---|
| 705 | + __func__, audio->sample_rate); |
|---|
| 706 | + return -ENOENT; |
|---|
| 707 | + } |
|---|
| 708 | + |
|---|
| 709 | + if (daifmt->fmt == HDMI_SPDIF) { |
|---|
| 710 | + /* set_audio source SPDIF */ |
|---|
| 711 | + hdmi_writeb(hdmi, HDMI_AUDIO_CTRL1, 0x09); |
|---|
| 712 | + } else { |
|---|
| 713 | + /* set_audio source I2S */ |
|---|
| 714 | + hdmi_writeb(hdmi, HDMI_AUDIO_CTRL1, 0x01); |
|---|
| 715 | + } |
|---|
| 716 | + hdmi_writeb(hdmi, AUDIO_SAMPLE_RATE, rate); |
|---|
| 717 | + hdmi_writeb(hdmi, AUDIO_I2S_MODE, v_I2S_MODE(I2S_STANDARD) | |
|---|
| 718 | + v_I2S_CHANNEL(channel)); |
|---|
| 719 | + |
|---|
| 720 | + hdmi_writeb(hdmi, AUDIO_I2S_MAP, 0x00); |
|---|
| 721 | + hdmi_writeb(hdmi, AUDIO_I2S_SWAPS_SPDIF, rate); |
|---|
| 722 | + |
|---|
| 723 | + /* Set N value */ |
|---|
| 724 | + hdmi_writeb(hdmi, AUDIO_N_H, (N >> 16) & 0x0F); |
|---|
| 725 | + hdmi_writeb(hdmi, AUDIO_N_M, (N >> 8) & 0xFF); |
|---|
| 726 | + hdmi_writeb(hdmi, AUDIO_N_L, N & 0xFF); |
|---|
| 727 | + |
|---|
| 728 | + /* Set hdmi nlpcm mode to support hdmi bitstream */ |
|---|
| 729 | + hdmi_writeb(hdmi, HDMI_AUDIO_CHANNEL_STATUS, v_AUDIO_STATUS_NLPCM(0)); |
|---|
| 730 | + |
|---|
| 731 | + return inno_hdmi_config_audio_aai(hdmi, audio); |
|---|
| 732 | +} |
|---|
| 733 | + |
|---|
| 734 | +static int inno_hdmi_audio_prepare(struct device *dev, void *data, |
|---|
| 735 | + struct hdmi_codec_daifmt *fmt, |
|---|
| 736 | + struct hdmi_codec_params *hparms) |
|---|
| 737 | +{ |
|---|
| 738 | + struct inno_hdmi *hdmi = dev_get_drvdata(dev); |
|---|
| 739 | + |
|---|
| 740 | + if (!hdmi->hdmi_data.sink_has_audio) { |
|---|
| 741 | + dev_err(hdmi->dev, "Sink do not support audio!\n"); |
|---|
| 742 | + return -ENODEV; |
|---|
| 743 | + } |
|---|
| 744 | + |
|---|
| 745 | + hdmi->audio_enable = 0; |
|---|
| 746 | + hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_PD, v_AUDIO_PD(1)); |
|---|
| 747 | + return 0; |
|---|
| 748 | +} |
|---|
| 749 | + |
|---|
| 750 | +static int inno_hdmi_audio_hw_params(struct device *dev, void *d, |
|---|
| 751 | + struct hdmi_codec_daifmt *daifmt, |
|---|
| 752 | + struct hdmi_codec_params *params) |
|---|
| 753 | +{ |
|---|
| 754 | + struct inno_hdmi *hdmi = dev_get_drvdata(dev); |
|---|
| 755 | + struct audio_info audio = { |
|---|
| 756 | + .sample_width = params->sample_width, |
|---|
| 757 | + .sample_rate = params->sample_rate, |
|---|
| 758 | + .channels = params->channels, |
|---|
| 759 | + }; |
|---|
| 760 | + |
|---|
| 761 | + if (!hdmi->hdmi_data.sink_has_audio) { |
|---|
| 762 | + dev_err(hdmi->dev, "Sink do not support audio!\n"); |
|---|
| 763 | + return -ENODEV; |
|---|
| 764 | + } |
|---|
| 765 | + |
|---|
| 766 | + if (!hdmi->encoder.crtc) |
|---|
| 767 | + return -ENODEV; |
|---|
| 768 | + |
|---|
| 769 | + switch (daifmt->fmt) { |
|---|
| 770 | + case HDMI_I2S: |
|---|
| 771 | + break; |
|---|
| 772 | + case HDMI_SPDIF: |
|---|
| 773 | + break; |
|---|
| 774 | + default: |
|---|
| 775 | + dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt); |
|---|
| 776 | + return -EINVAL; |
|---|
| 777 | + } |
|---|
| 778 | + |
|---|
| 779 | + return inno_hdmi_audio_config_set(hdmi, daifmt, &audio); |
|---|
| 780 | +} |
|---|
| 781 | + |
|---|
| 782 | +static void inno_hdmi_audio_shutdown(struct device *dev, void *d) |
|---|
| 783 | +{ |
|---|
| 784 | + /* do nothing */ |
|---|
| 785 | +} |
|---|
| 786 | + |
|---|
| 787 | +static int inno_hdmi_audio_mute(struct device *dev, void *data, bool mute, int direction) |
|---|
| 788 | +{ |
|---|
| 789 | + struct inno_hdmi *hdmi = dev_get_drvdata(dev); |
|---|
| 790 | + |
|---|
| 791 | + if (!hdmi->hdmi_data.sink_has_audio) { |
|---|
| 792 | + dev_err(hdmi->dev, "Sink do not support audio!\n"); |
|---|
| 793 | + return -ENODEV; |
|---|
| 794 | + } |
|---|
| 795 | + |
|---|
| 796 | + hdmi->audio_enable = !mute; |
|---|
| 797 | + |
|---|
| 798 | + if (mute) |
|---|
| 799 | + hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_AUDIO_PD, |
|---|
| 800 | + v_AUDIO_MUTE(1) | v_AUDIO_PD(1)); |
|---|
| 801 | + else |
|---|
| 802 | + hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_AUDIO_PD, |
|---|
| 803 | + v_AUDIO_MUTE(0) | v_AUDIO_PD(0)); |
|---|
| 804 | + |
|---|
| 805 | + return 0; |
|---|
| 806 | +} |
|---|
| 807 | + |
|---|
| 808 | +static int inno_hdmi_audio_get_eld(struct device *dev, void *d, |
|---|
| 809 | + uint8_t *buf, size_t len) |
|---|
| 810 | +{ |
|---|
| 811 | + struct inno_hdmi *hdmi = dev_get_drvdata(dev); |
|---|
| 812 | + struct drm_mode_config *config = &hdmi->encoder.dev->mode_config; |
|---|
| 813 | + struct drm_connector *connector; |
|---|
| 814 | + int ret = -ENODEV; |
|---|
| 815 | + |
|---|
| 816 | + mutex_lock(&config->mutex); |
|---|
| 817 | + list_for_each_entry(connector, &config->connector_list, head) { |
|---|
| 818 | + if (&hdmi->encoder == connector->encoder) { |
|---|
| 819 | + memcpy(buf, connector->eld, |
|---|
| 820 | + min(sizeof(connector->eld), len)); |
|---|
| 821 | + ret = 0; |
|---|
| 822 | + } |
|---|
| 823 | + } |
|---|
| 824 | + mutex_unlock(&config->mutex); |
|---|
| 825 | + |
|---|
| 826 | + return ret; |
|---|
| 827 | +} |
|---|
| 828 | + |
|---|
| 829 | +static const struct hdmi_codec_ops audio_codec_ops = { |
|---|
| 830 | + .hw_params = inno_hdmi_audio_hw_params, |
|---|
| 831 | + .prepare = inno_hdmi_audio_prepare, |
|---|
| 832 | + .audio_shutdown = inno_hdmi_audio_shutdown, |
|---|
| 833 | + .mute_stream = inno_hdmi_audio_mute, |
|---|
| 834 | + .get_eld = inno_hdmi_audio_get_eld, |
|---|
| 835 | +}; |
|---|
| 836 | + |
|---|
| 837 | +static int inno_hdmi_audio_codec_init(struct inno_hdmi *hdmi, |
|---|
| 838 | + struct device *dev) |
|---|
| 839 | +{ |
|---|
| 840 | + const char *str = "i2s"; |
|---|
| 841 | + struct hdmi_codec_pdata codec_data = { |
|---|
| 842 | + .i2s = 1, |
|---|
| 843 | + .spdif = 0, |
|---|
| 844 | + .ops = &audio_codec_ops, |
|---|
| 845 | + .max_i2s_channels = 8, |
|---|
| 846 | + }; |
|---|
| 847 | + |
|---|
| 848 | + if (device_property_read_string(dev, "rockchip,format", &str)) |
|---|
| 849 | + dev_warn(dev, "can not get rockchip,format\n"); |
|---|
| 850 | + |
|---|
| 851 | + if (strstr(str, "spdif")) { |
|---|
| 852 | + codec_data.i2s = 0; |
|---|
| 853 | + codec_data.spdif = 1; |
|---|
| 854 | + } |
|---|
| 855 | + |
|---|
| 856 | + hdmi->audio_enable = false; |
|---|
| 857 | + hdmi->audio_pdev = platform_device_register_data( |
|---|
| 858 | + dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_NONE, |
|---|
| 859 | + &codec_data, sizeof(codec_data)); |
|---|
| 860 | + |
|---|
| 861 | + return PTR_ERR_OR_ZERO(hdmi->audio_pdev); |
|---|
| 862 | +} |
|---|
| 863 | + |
|---|
| 620 | 864 | static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi) |
|---|
| 621 | 865 | { |
|---|
| 622 | 866 | struct drm_encoder *encoder = &hdmi->encoder; |
|---|
| 623 | 867 | struct device *dev = hdmi->dev; |
|---|
| 624 | 868 | |
|---|
| 625 | | - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); |
|---|
| 869 | + encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm, dev->of_node); |
|---|
| 626 | 870 | |
|---|
| 627 | 871 | /* |
|---|
| 628 | 872 | * If we failed to find the CRTC(s) which this encoder is |
|---|
| .. | .. |
|---|
| 634 | 878 | return -EPROBE_DEFER; |
|---|
| 635 | 879 | |
|---|
| 636 | 880 | drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs); |
|---|
| 637 | | - drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs, |
|---|
| 638 | | - DRM_MODE_ENCODER_TMDS, NULL); |
|---|
| 881 | + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); |
|---|
| 639 | 882 | |
|---|
| 640 | 883 | hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; |
|---|
| 641 | 884 | |
|---|
| 642 | 885 | drm_connector_helper_add(&hdmi->connector, |
|---|
| 643 | 886 | &inno_hdmi_connector_helper_funcs); |
|---|
| 644 | | - drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs, |
|---|
| 645 | | - DRM_MODE_CONNECTOR_HDMIA); |
|---|
| 887 | + drm_connector_init_with_ddc(drm, &hdmi->connector, |
|---|
| 888 | + &inno_hdmi_connector_funcs, |
|---|
| 889 | + DRM_MODE_CONNECTOR_HDMIA, |
|---|
| 890 | + hdmi->ddc); |
|---|
| 646 | 891 | |
|---|
| 647 | 892 | drm_connector_attach_encoder(&hdmi->connector, encoder); |
|---|
| 893 | + inno_hdmi_audio_codec_init(hdmi, dev); |
|---|
| 648 | 894 | |
|---|
| 649 | 895 | return 0; |
|---|
| 650 | 896 | } |
|---|
| .. | .. |
|---|
| 823 | 1069 | return adap; |
|---|
| 824 | 1070 | } |
|---|
| 825 | 1071 | |
|---|
| 1072 | +static struct inno_hdmi_phy_config rk3036_hdmi_phy_config[] = { |
|---|
| 1073 | + /* pixelclk pre-emp vlev */ |
|---|
| 1074 | + { 74250000, 0x3f, 0xbb }, |
|---|
| 1075 | + { 165000000, 0x6f, 0xbb }, |
|---|
| 1076 | + { ~0UL, 0x00, 0x00 } |
|---|
| 1077 | +}; |
|---|
| 1078 | + |
|---|
| 1079 | +static struct inno_hdmi_phy_config rk3128_hdmi_phy_config[] = { |
|---|
| 1080 | + /* pixelclk pre-emp vlev */ |
|---|
| 1081 | + { 74250000, 0x3f, 0xaa }, |
|---|
| 1082 | + { 165000000, 0x5f, 0xaa }, |
|---|
| 1083 | + { ~0UL, 0x00, 0x00 } |
|---|
| 1084 | +}; |
|---|
| 1085 | + |
|---|
| 1086 | +static const struct inno_hdmi_plat_data rk3036_hdmi_drv_data = { |
|---|
| 1087 | + .dev_type = RK3036_HDMI, |
|---|
| 1088 | + .phy_config = rk3036_hdmi_phy_config, |
|---|
| 1089 | +}; |
|---|
| 1090 | + |
|---|
| 1091 | +static const struct inno_hdmi_plat_data rk3128_hdmi_drv_data = { |
|---|
| 1092 | + .dev_type = RK3128_HDMI, |
|---|
| 1093 | + .phy_config = rk3128_hdmi_phy_config, |
|---|
| 1094 | +}; |
|---|
| 1095 | + |
|---|
| 1096 | +static const struct of_device_id inno_hdmi_dt_ids[] = { |
|---|
| 1097 | + { .compatible = "rockchip,rk3036-inno-hdmi", |
|---|
| 1098 | + .data = &rk3036_hdmi_drv_data, |
|---|
| 1099 | + }, |
|---|
| 1100 | + { .compatible = "rockchip,rk3128-inno-hdmi", |
|---|
| 1101 | + .data = &rk3128_hdmi_drv_data, |
|---|
| 1102 | + }, |
|---|
| 1103 | + {}, |
|---|
| 1104 | +}; |
|---|
| 1105 | +MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids); |
|---|
| 1106 | + |
|---|
| 826 | 1107 | static int inno_hdmi_bind(struct device *dev, struct device *master, |
|---|
| 827 | 1108 | void *data) |
|---|
| 828 | 1109 | { |
|---|
| .. | .. |
|---|
| 839 | 1120 | |
|---|
| 840 | 1121 | hdmi->dev = dev; |
|---|
| 841 | 1122 | hdmi->drm_dev = drm; |
|---|
| 1123 | + hdmi->plat_data = device_get_match_data(hdmi->dev); |
|---|
| 842 | 1124 | |
|---|
| 843 | 1125 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 844 | 1126 | hdmi->regs = devm_ioremap_resource(dev, iores); |
|---|
| 845 | 1127 | if (IS_ERR(hdmi->regs)) |
|---|
| 846 | 1128 | return PTR_ERR(hdmi->regs); |
|---|
| 1129 | + |
|---|
| 1130 | + irq = platform_get_irq(pdev, 0); |
|---|
| 1131 | + if (irq < 0) |
|---|
| 1132 | + return irq; |
|---|
| 1133 | + |
|---|
| 1134 | + hdmi->aclk = devm_clk_get(hdmi->dev, "aclk"); |
|---|
| 1135 | + if (IS_ERR(hdmi->aclk)) { |
|---|
| 1136 | + dev_err(hdmi->dev, "Unable to get HDMI aclk clk\n"); |
|---|
| 1137 | + return PTR_ERR(hdmi->aclk); |
|---|
| 1138 | + } |
|---|
| 847 | 1139 | |
|---|
| 848 | 1140 | hdmi->pclk = devm_clk_get(hdmi->dev, "pclk"); |
|---|
| 849 | 1141 | if (IS_ERR(hdmi->pclk)) { |
|---|
| .. | .. |
|---|
| 851 | 1143 | return PTR_ERR(hdmi->pclk); |
|---|
| 852 | 1144 | } |
|---|
| 853 | 1145 | |
|---|
| 854 | | - ret = clk_prepare_enable(hdmi->pclk); |
|---|
| 1146 | + ret = clk_prepare_enable(hdmi->aclk); |
|---|
| 855 | 1147 | if (ret) { |
|---|
| 856 | 1148 | DRM_DEV_ERROR(hdmi->dev, |
|---|
| 857 | | - "Cannot enable HDMI pclk clock: %d\n", ret); |
|---|
| 1149 | + "Cannot enable HDMI aclk clock: %d\n", ret); |
|---|
| 858 | 1150 | return ret; |
|---|
| 859 | 1151 | } |
|---|
| 860 | 1152 | |
|---|
| 861 | | - irq = platform_get_irq(pdev, 0); |
|---|
| 862 | | - if (irq < 0) { |
|---|
| 863 | | - ret = irq; |
|---|
| 864 | | - goto err_disable_clk; |
|---|
| 1153 | + ret = clk_prepare_enable(hdmi->pclk); |
|---|
| 1154 | + if (ret) { |
|---|
| 1155 | + dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret); |
|---|
| 1156 | + goto err_disable_aclk; |
|---|
| 865 | 1157 | } |
|---|
| 866 | 1158 | |
|---|
| 867 | 1159 | inno_hdmi_reset(hdmi); |
|---|
| .. | .. |
|---|
| 870 | 1162 | if (IS_ERR(hdmi->ddc)) { |
|---|
| 871 | 1163 | ret = PTR_ERR(hdmi->ddc); |
|---|
| 872 | 1164 | hdmi->ddc = NULL; |
|---|
| 873 | | - goto err_disable_clk; |
|---|
| 1165 | + goto err_disable_pclk; |
|---|
| 874 | 1166 | } |
|---|
| 875 | 1167 | |
|---|
| 876 | 1168 | /* |
|---|
| .. | .. |
|---|
| 894 | 1186 | ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq, |
|---|
| 895 | 1187 | inno_hdmi_irq, IRQF_SHARED, |
|---|
| 896 | 1188 | dev_name(dev), hdmi); |
|---|
| 897 | | - if (ret < 0) |
|---|
| 1189 | + if (ret) { |
|---|
| 1190 | + dev_err(hdmi->dev, |
|---|
| 1191 | + "failed to request hdmi irq: %d\n", ret); |
|---|
| 898 | 1192 | goto err_cleanup_hdmi; |
|---|
| 1193 | + } |
|---|
| 899 | 1194 | |
|---|
| 900 | 1195 | return 0; |
|---|
| 1196 | + |
|---|
| 901 | 1197 | err_cleanup_hdmi: |
|---|
| 902 | 1198 | hdmi->connector.funcs->destroy(&hdmi->connector); |
|---|
| 903 | 1199 | hdmi->encoder.funcs->destroy(&hdmi->encoder); |
|---|
| 904 | 1200 | err_put_adapter: |
|---|
| 905 | 1201 | i2c_put_adapter(hdmi->ddc); |
|---|
| 906 | | -err_disable_clk: |
|---|
| 1202 | +err_disable_pclk: |
|---|
| 907 | 1203 | clk_disable_unprepare(hdmi->pclk); |
|---|
| 1204 | +err_disable_aclk: |
|---|
| 1205 | + clk_disable_unprepare(hdmi->aclk); |
|---|
| 908 | 1206 | return ret; |
|---|
| 909 | 1207 | } |
|---|
| 910 | 1208 | |
|---|
| .. | .. |
|---|
| 918 | 1216 | |
|---|
| 919 | 1217 | i2c_put_adapter(hdmi->ddc); |
|---|
| 920 | 1218 | clk_disable_unprepare(hdmi->pclk); |
|---|
| 1219 | + clk_disable_unprepare(hdmi->aclk); |
|---|
| 921 | 1220 | } |
|---|
| 922 | 1221 | |
|---|
| 923 | 1222 | static const struct component_ops inno_hdmi_ops = { |
|---|
| .. | .. |
|---|
| 936 | 1235 | |
|---|
| 937 | 1236 | return 0; |
|---|
| 938 | 1237 | } |
|---|
| 939 | | - |
|---|
| 940 | | -static const struct of_device_id inno_hdmi_dt_ids[] = { |
|---|
| 941 | | - { .compatible = "rockchip,rk3036-inno-hdmi", |
|---|
| 942 | | - }, |
|---|
| 943 | | - {}, |
|---|
| 944 | | -}; |
|---|
| 945 | | -MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids); |
|---|
| 946 | 1238 | |
|---|
| 947 | 1239 | struct platform_driver inno_hdmi_driver = { |
|---|
| 948 | 1240 | .probe = inno_hdmi_probe, |
|---|