From: Linus Torvalds Date: Sat, 25 Nov 2017 17:58:25 +0000 (-1000) Subject: Merge tag 'afs-fixes-20171124' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowe... X-Git-Tag: v4.15-rc1~11 X-Git-Url: http://git.samba.org/samba.git/?p=sfrench%2Fcifs-2.6.git;a=commitdiff_plain;h=f61ec2c97cf8a517cd5714efc919138a9d8991cf;hp=43dd388b21c722616413781bd434522376e117cc Merge tag 'afs-fixes-20171124' of git://git./linux/kernel/git/dhowells/linux-fs Pull AFS fixes from David Howells: - Make AFS file locking work again. - Don't write to a page that's being written out, but wait for it to complete. - Do d_drop() and d_add() in the right places. - Put keys on error paths. - Remove some redundant code. * tag 'afs-fixes-20171124' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: afs: remove redundant assignment of dvnode to itself afs: cell: Remove unnecessary code in afs_lookup_cell afs: Fix signal handling in some file ops afs: Fix some dentry handling in dir ops and missing key_puts afs: Make afs_write_begin() avoid writing to a page that's being stored afs: Fix file locking --- diff --git a/.gitignore b/.gitignore index 6c119eab5d46..f6050b88e95b 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,11 @@ modules.builtin /System.map /Module.markers +# +# RPM spec file (make rpm-pkg) +# +/*.spec + # # Debian directory (make deb-pkg) # diff --git a/.mailmap b/.mailmap index c021f29779a7..1469ff0d3f4d 100644 --- a/.mailmap +++ b/.mailmap @@ -73,6 +73,8 @@ James E Wilson James Hogan James Hogan James Ketrenos +Jason Gunthorpe +Jason Gunthorpe Javi Merino Jean Tourrilhes diff --git a/Documentation/ABI/testing/dell-smbios-wmi b/Documentation/ABI/testing/dell-smbios-wmi new file mode 100644 index 000000000000..fc919ce16008 --- /dev/null +++ b/Documentation/ABI/testing/dell-smbios-wmi @@ -0,0 +1,41 @@ +What: /dev/wmi/dell-smbios +Date: November 2017 +KernelVersion: 4.15 +Contact: "Mario Limonciello" +Description: + Perform SMBIOS calls on supported Dell machines. + through the Dell ACPI-WMI interface. + + IOCTL's and buffer formats are defined in: + + + 1) To perform an SMBIOS call from userspace, you'll need to + first determine the minimum size of the calling interface + buffer for your machine. + Platforms that contain larger buffers can return larger + objects from the system firmware. + Commonly this size is either 4k or 32k. + + To determine the size of the buffer read() a u64 dword from + the WMI character device /dev/wmi/dell-smbios. + + 2) After you've determined the minimum size of the calling + interface buffer, you can allocate a structure that represents + the structure documented above. + + 3) In the 'length' object store the size of the buffer you + determined above and allocated. + + 4) In this buffer object, prepare as necessary for the SMBIOS + call you're interested in. Typically SMBIOS buffers have + "class", "select", and "input" defined to values that coincide + with the data you are interested in. + Documenting class/select/input values is outside of the scope + of this documentation. Check with the libsmbios project for + further documentation on these values. + + 6) Run the call by using ioctl() as described in the header. + + 7) The output will be returned in the buffer object. + + 8) Be sure to free up your allocated object. diff --git a/Documentation/ABI/testing/sysfs-platform-dell-smbios b/Documentation/ABI/testing/sysfs-platform-dell-smbios new file mode 100644 index 000000000000..205d3b6361e0 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-dell-smbios @@ -0,0 +1,21 @@ +What: /sys/devices/platform//tokens/* +Date: November 2017 +KernelVersion: 4.15 +Contact: "Mario Limonciello" +Description: + A read-only description of Dell platform tokens + available on the machine. + + Each token attribute is available as a pair of + sysfs attributes readable by a process with + CAP_SYS_ADMIN. + + For example the token ID "5" would be available + as the following attributes: + + 0005_location + 0005_value + + Tokens will vary from machine to machine, and + only tokens available on that machine will be + displayed. diff --git a/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt b/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt new file mode 100644 index 000000000000..8af65059d519 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt @@ -0,0 +1,11 @@ +What: /sys/devices/platform//force_power +Date: September 2017 +KernelVersion: 4.15 +Contact: "Mario Limonciello" +Description: + Modify the platform force power state, influencing + Thunderbolt controllers to turn on or off when no + devices are connected (write-only) + There are two available states: + * 0 -> Force power disabled + * 1 -> Force power enabled diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst index 12278a926370..fdf72429f801 100644 --- a/Documentation/admin-guide/dynamic-debug-howto.rst +++ b/Documentation/admin-guide/dynamic-debug-howto.rst @@ -18,7 +18,7 @@ shortcut for ``print_hex_dump(KERN_DEBUG)``. For ``print_hex_dump_debug()``/``print_hex_dump_bytes()``, format string is its ``prefix_str`` argument, if it is constant string; or ``hexdump`` -in case ``prefix_str`` is build dynamically. +in case ``prefix_str`` is built dynamically. Dynamic debug has even more useful features: @@ -197,8 +197,8 @@ line line number matches the callsite line number exactly. A range of line numbers matches any callsite between the first and last line number inclusive. An empty first number means - the first line in the file, an empty line number means the - last number in the file. Examples:: + the first line in the file, an empty last line number means the + last line number in the file. Examples:: line 1603 // exactly line 1603 line 1600-1605 // the six lines from line 1600 to line 1605 diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 62436bd5f34a..6571fbfdb2a1 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1890,6 +1890,10 @@ [KVM,ARM] Trap guest accesses to GICv3 common system registers + kvm-arm.vgic_v4_enable= + [KVM,ARM] Allow use of GICv4 for direct injection of + LPIs. + kvm-intel.ept= [KVM,Intel] Disable extended page tables (virtualized MMU) support on capable Intel chips. Default is 1 (enabled) @@ -3246,13 +3250,15 @@ instead using the legacy FADT method profile= [KNL] Enable kernel profiling via /proc/profile - Format: [schedule,] + Format: [,] + Param: : "schedule", "sleep", or "kvm" + [defaults to kernel profiling] Param: "schedule" - profile schedule points. - Param: - step/bucket size as a power of 2 for - statistical time based profiling. Param: "sleep" - profile D-state sleeping (millisecs). Requires CONFIG_SCHEDSTATS Param: "kvm" - profile VM exits. + Param: - step/bucket size as a power of 2 for + statistical time based profiling. prompt_ramdisk= [RAM] List of RAM disks to prompt for floppy disk before loading. diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 5c62d11d77e8..de50a8561774 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -221,3 +221,18 @@ The driver will create one virtual ethernet interface per Thunderbolt port which are named like ``thunderbolt0`` and so on. From this point you can either use standard userspace tools like ``ifconfig`` to configure the interface or let your GUI to handle it automatically. + +Forcing power +------------- +Many OEMs include a method that can be used to force the power of a +thunderbolt controller to an "On" state even if nothing is connected. +If supported by your machine this will be exposed by the WMI bus with +a sysfs attribute called "force_power". + +For example the intel-wmi-thunderbolt driver exposes this attribute in: + /sys/devices/platform/PNP0C14:00/wmi_bus/wmi_bus-PNP0C14:00/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power + + To force the power to on, write 1 to this attribute file. + To disable force power, write 0 to this attribute file. + +Note: it's currently not possible to query the force power state of a platform. diff --git a/Documentation/block/bfq-iosched.txt b/Documentation/block/bfq-iosched.txt index 3d6951d63489..8d8d8f06cab2 100644 --- a/Documentation/block/bfq-iosched.txt +++ b/Documentation/block/bfq-iosched.txt @@ -20,12 +20,27 @@ for that device, by setting low_latency to 0. See Section 3 for details on how to configure BFQ for the desired tradeoff between latency and throughput, or on how to maximize throughput. -On average CPUs, the current version of BFQ can handle devices -performing at most ~30K IOPS; at most ~50 KIOPS on faster CPUs. As a -reference, 30-50 KIOPS correspond to very high bandwidths with -sequential I/O (e.g., 8-12 GB/s if I/O requests are 256 KB large), and -to 120-200 MB/s with 4KB random I/O. BFQ is currently being tested on -multi-queue devices too. +BFQ has a non-null overhead, which limits the maximum IOPS that a CPU +can process for a device scheduled with BFQ. To give an idea of the +limits on slow or average CPUs, here are, first, the limits of BFQ for +three different CPUs, on, respectively, an average laptop, an old +desktop, and a cheap embedded system, in case full hierarchical +support is enabled (i.e., CONFIG_BFQ_GROUP_IOSCHED is set), but +CONFIG_DEBUG_BLK_CGROUP is not set (Section 4-2): +- Intel i7-4850HQ: 400 KIOPS +- AMD A8-3850: 250 KIOPS +- ARM CortexTM-A53 Octa-core: 80 KIOPS + +If CONFIG_DEBUG_BLK_CGROUP is set (and of course full hierarchical +support is enabled), then the sustainable throughput with BFQ +decreases, because all blkio.bfq* statistics are created and updated +(Section 4-2). For BFQ, this leads to the following maximum +sustainable throughputs, on the same systems as above: +- Intel i7-4850HQ: 310 KIOPS +- AMD A8-3850: 200 KIOPS +- ARM CortexTM-A53 Octa-core: 56 KIOPS + +BFQ works for multi-queue devices too. The table of contents follow. Impatients can just jump to Section 3. @@ -500,6 +515,22 @@ BFQ-specific files is "blkio.bfq." or "io.bfq." For example, the group parameter to set the weight of a group with BFQ is blkio.bfq.weight or io.bfq.weight. +As for cgroups-v1 (blkio controller), the exact set of stat files +created, and kept up-to-date by bfq, depends on whether +CONFIG_DEBUG_BLK_CGROUP is set. If it is set, then bfq creates all +the stat files documented in +Documentation/cgroup-v1/blkio-controller.txt. If, instead, +CONFIG_DEBUG_BLK_CGROUP is not set, then bfq creates only the files +blkio.bfq.io_service_bytes +blkio.bfq.io_service_bytes_recursive +blkio.bfq.io_serviced +blkio.bfq.io_serviced_recursive + +The value of CONFIG_DEBUG_BLK_CGROUP greatly influences the maximum +throughput sustainable with bfq, because updating the blkio.bfq.* +stats is rather costly, especially for some of the stats enabled by +CONFIG_DEBUG_BLK_CGROUP. + Parameters to set ----------------- diff --git a/Documentation/clearing-warn-once.txt b/Documentation/clearing-warn-once.txt new file mode 100644 index 000000000000..5b1f5d547be1 --- /dev/null +++ b/Documentation/clearing-warn-once.txt @@ -0,0 +1,7 @@ + +WARN_ONCE / WARN_ON_ONCE only print a warning once. + +echo 1 > /sys/kernel/debug/clear_warn_once + +clears the state and allows the warnings to print once again. +This can be useful after test suite runs to reproduce problems. diff --git a/Documentation/dev-tools/coccinelle.rst b/Documentation/dev-tools/coccinelle.rst index 37e474ff6911..94f41c290bfc 100644 --- a/Documentation/dev-tools/coccinelle.rst +++ b/Documentation/dev-tools/coccinelle.rst @@ -33,9 +33,6 @@ of many distributions, e.g. : You can get the latest version released from the Coccinelle homepage at http://coccinelle.lip6.fr/ -Information and tips about Coccinelle are also provided on the wiki -pages at http://cocci.ekstranet.diku.dk/wiki/doku.php - Once you have it, run the following command:: ./configure diff --git a/Documentation/dev-tools/kcov.rst b/Documentation/dev-tools/kcov.rst index 44886c91e112..c2f6452e38ed 100644 --- a/Documentation/dev-tools/kcov.rst +++ b/Documentation/dev-tools/kcov.rst @@ -12,19 +12,30 @@ To achieve this goal it does not collect coverage in soft/hard interrupts and instrumentation of some inherently non-deterministic parts of kernel is disabled (e.g. scheduler, locking). -Usage ------ +kcov is also able to collect comparison operands from the instrumented code +(this feature currently requires that the kernel is compiled with clang). + +Prerequisites +------------- Configure the kernel with:: CONFIG_KCOV=y CONFIG_KCOV requires gcc built on revision 231296 or later. + +If the comparison operands need to be collected, set:: + + CONFIG_KCOV_ENABLE_COMPARISONS=y + Profiling data will only become accessible once debugfs has been mounted:: mount -t debugfs none /sys/kernel/debug -The following program demonstrates kcov usage from within a test program: +Coverage collection +------------------- +The following program demonstrates coverage collection from within a test +program using kcov: .. code-block:: c @@ -44,6 +55,9 @@ The following program demonstrates kcov usage from within a test program: #define KCOV_DISABLE _IO('c', 101) #define COVER_SIZE (64<<10) + #define KCOV_TRACE_PC 0 + #define KCOV_TRACE_CMP 1 + int main(int argc, char **argv) { int fd; @@ -64,7 +78,7 @@ The following program demonstrates kcov usage from within a test program: if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* Enable coverage collection on the current thread. */ - if (ioctl(fd, KCOV_ENABLE, 0)) + if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC)) perror("ioctl"), exit(1); /* Reset coverage from the tail of the ioctl() call. */ __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); @@ -111,3 +125,80 @@ The interface is fine-grained to allow efficient forking of test processes. That is, a parent process opens /sys/kernel/debug/kcov, enables trace mode, mmaps coverage buffer and then forks child processes in a loop. Child processes only need to enable coverage (disable happens automatically on thread end). + +Comparison operands collection +------------------------------ +Comparison operands collection is similar to coverage collection: + +.. code-block:: c + + /* Same includes and defines as above. */ + + /* Number of 64-bit words per record. */ + #define KCOV_WORDS_PER_CMP 4 + + /* + * The format for the types of collected comparisons. + * + * Bit 0 shows whether one of the arguments is a compile-time constant. + * Bits 1 & 2 contain log2 of the argument size, up to 8 bytes. + */ + + #define KCOV_CMP_CONST (1 << 0) + #define KCOV_CMP_SIZE(n) ((n) << 1) + #define KCOV_CMP_MASK KCOV_CMP_SIZE(3) + + int main(int argc, char **argv) + { + int fd; + uint64_t *cover, type, arg1, arg2, is_const, size; + unsigned long n, i; + + fd = open("/sys/kernel/debug/kcov", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) + perror("ioctl"), exit(1); + /* + * Note that the buffer pointer is of type uint64_t*, because all + * the comparison operands are promoted to uint64_t. + */ + cover = (uint64_t *)mmap(NULL, COVER_SIZE * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void*)cover == MAP_FAILED) + perror("mmap"), exit(1); + /* Note KCOV_TRACE_CMP instead of KCOV_TRACE_PC. */ + if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_CMP)) + perror("ioctl"), exit(1); + __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); + read(-1, NULL, 0); + /* Read number of comparisons collected. */ + n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); + for (i = 0; i < n; i++) { + type = cover[i * KCOV_WORDS_PER_CMP + 1]; + /* arg1 and arg2 - operands of the comparison. */ + arg1 = cover[i * KCOV_WORDS_PER_CMP + 2]; + arg2 = cover[i * KCOV_WORDS_PER_CMP + 3]; + /* ip - caller address. */ + ip = cover[i * KCOV_WORDS_PER_CMP + 4]; + /* size of the operands. */ + size = 1 << ((type & KCOV_CMP_MASK) >> 1); + /* is_const - true if either operand is a compile-time constant.*/ + is_const = type & KCOV_CMP_CONST; + printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, " + "size: %lu, %s\n", + ip, type, arg1, arg2, size, + is_const ? "const" : "non-const"); + } + if (ioctl(fd, KCOV_DISABLE, 0)) + perror("ioctl"), exit(1); + /* Free resources. */ + if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) + perror("munmap"), exit(1); + if (close(fd)) + perror("close"), exit(1); + return 0; + } + +Note that the kcov modes (coverage collection or comparison operands) are +mutually exclusive. diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt index cd977db7630c..b404d592ce58 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt @@ -7,7 +7,9 @@ Required Properties: - compatible: Should be one of: - "mediatek,mt2701-apmixedsys" + - "mediatek,mt2712-apmixedsys", "syscon" - "mediatek,mt6797-apmixedsys" + - "mediatek,mt7622-apmixedsys" - "mediatek,mt8135-apmixedsys" - "mediatek,mt8173-apmixedsys" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.txt new file mode 100644 index 000000000000..9b8f578d5e19 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.txt @@ -0,0 +1,22 @@ +MediaTek AUDSYS controller +============================ + +The MediaTek AUDSYS controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be one of: + - "mediatek,mt7622-audsys", "syscon" +- #clock-cells: Must be 1 + +The AUDSYS controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +audsys: audsys@11220000 { + compatible = "mediatek,mt7622-audsys", "syscon"; + reg = <0 0x11220000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt index 4137196dd686..4010e37c53a0 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: Should be: - "mediatek,mt2701-bdpsys", "syscon" + - "mediatek,mt2712-bdpsys", "syscon" - #clock-cells: Must be 1 The bdpsys controller uses the common clk binding from diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt index 768f3a5bc055..7aa3fa167668 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: Should be: - "mediatek,mt2701-ethsys", "syscon" + - "mediatek,mt7622-ethsys", "syscon" - #clock-cells: Must be 1 The ethsys controller uses the common clk binding from diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt index beed7b594cea..f5629d64cef2 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt @@ -8,6 +8,7 @@ Required Properties: - compatible: Should be: - "mediatek,mt2701-hifsys", "syscon" + - "mediatek,mt7622-hifsys", "syscon" - #clock-cells: Must be 1 The hifsys controller uses the common clk binding from diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt index 047b11ae5f45..868bd51a98be 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: Should be one of: - "mediatek,mt2701-imgsys", "syscon" + - "mediatek,mt2712-imgsys", "syscon" - "mediatek,mt6797-imgsys", "syscon" - "mediatek,mt8173-imgsys", "syscon" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt index 58d58e2006b8..566f153f9f83 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt @@ -8,7 +8,9 @@ Required Properties: - compatible: Should be one of: - "mediatek,mt2701-infracfg", "syscon" + - "mediatek,mt2712-infracfg", "syscon" - "mediatek,mt6797-infracfg", "syscon" + - "mediatek,mt7622-infracfg", "syscon" - "mediatek,mt8135-infracfg", "syscon" - "mediatek,mt8173-infracfg", "syscon" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,jpgdecsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,jpgdecsys.txt new file mode 100644 index 000000000000..2df799cd06a7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,jpgdecsys.txt @@ -0,0 +1,22 @@ +Mediatek jpgdecsys controller +============================ + +The Mediatek jpgdecsys controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt2712-jpgdecsys", "syscon" +- #clock-cells: Must be 1 + +The jpgdecsys controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +jpgdecsys: syscon@19000000 { + compatible = "mediatek,mt2712-jpgdecsys", "syscon"; + reg = <0 0x19000000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mcucfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mcucfg.txt new file mode 100644 index 000000000000..b8fb03f3613e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mcucfg.txt @@ -0,0 +1,22 @@ +Mediatek mcucfg controller +============================ + +The Mediatek mcucfg controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be one of: + - "mediatek,mt2712-mcucfg", "syscon" +- #clock-cells: Must be 1 + +The mcucfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +mcucfg: syscon@10220000 { + compatible = "mediatek,mt2712-mcucfg", "syscon"; + reg = <0 0x10220000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mfgcfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mfgcfg.txt new file mode 100644 index 000000000000..859e67b416d5 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mfgcfg.txt @@ -0,0 +1,22 @@ +Mediatek mfgcfg controller +============================ + +The Mediatek mfgcfg controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be one of: + - "mediatek,mt2712-mfgcfg", "syscon" +- #clock-cells: Must be 1 + +The mfgcfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +mfgcfg: syscon@13000000 { + compatible = "mediatek,mt2712-mfgcfg", "syscon"; + reg = <0 0x13000000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt index 70529e0b58e9..4eb8bbe15c01 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: Should be one of: - "mediatek,mt2701-mmsys", "syscon" + - "mediatek,mt2712-mmsys", "syscon" - "mediatek,mt6797-mmsys", "syscon" - "mediatek,mt8173-mmsys", "syscon" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pciesys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pciesys.txt new file mode 100644 index 000000000000..d5d5f1227665 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pciesys.txt @@ -0,0 +1,22 @@ +MediaTek PCIESYS controller +============================ + +The MediaTek PCIESYS controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt7622-pciesys", "syscon" +- #clock-cells: Must be 1 + +The PCIESYS controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +pciesys: pciesys@1a100800 { + compatible = "mediatek,mt7622-pciesys", "syscon"; + reg = <0 0x1a100800 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt index e494366782aa..fb58ca8c2770 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt @@ -8,6 +8,8 @@ Required Properties: - compatible: Should be one of: - "mediatek,mt2701-pericfg", "syscon" + - "mediatek,mt2712-pericfg", "syscon" + - "mediatek,mt7622-pericfg", "syscon" - "mediatek,mt8135-pericfg", "syscon" - "mediatek,mt8173-pericfg", "syscon" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,sgmiisys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,sgmiisys.txt new file mode 100644 index 000000000000..d113b8e741f3 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,sgmiisys.txt @@ -0,0 +1,22 @@ +MediaTek SGMIISYS controller +============================ + +The MediaTek SGMIISYS controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt7622-sgmiisys", "syscon" +- #clock-cells: Must be 1 + +The SGMIISYS controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +sgmiisys: sgmiisys@1b128000 { + compatible = "mediatek,mt7622-sgmiisys", "syscon"; + reg = <0 0x1b128000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,ssusbsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ssusbsys.txt new file mode 100644 index 000000000000..00760019da00 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ssusbsys.txt @@ -0,0 +1,22 @@ +MediaTek SSUSBSYS controller +============================ + +The MediaTek SSUSBSYS controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt7622-ssusbsys", "syscon" +- #clock-cells: Must be 1 + +The SSUSBSYS controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +ssusbsys: ssusbsys@1a000000 { + compatible = "mediatek,mt7622-ssusbsys", "syscon"; + reg = <0 0x1a000000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt index ec93ecbb9f3c..24014a7e2332 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt @@ -7,7 +7,9 @@ Required Properties: - compatible: Should be one of: - "mediatek,mt2701-topckgen" + - "mediatek,mt2712-topckgen", "syscon" - "mediatek,mt6797-topckgen" + - "mediatek,mt7622-topckgen" - "mediatek,mt8135-topckgen" - "mediatek,mt8173-topckgen" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt index d150104f928a..ea40d05089f8 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: Should be one of: - "mediatek,mt2701-vdecsys", "syscon" + - "mediatek,mt2712-vdecsys", "syscon" - "mediatek,mt6797-vdecsys", "syscon" - "mediatek,mt8173-vdecsys", "syscon" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vencsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vencsys.txt index 8a93be643647..851545357e94 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vencsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vencsys.txt @@ -6,6 +6,7 @@ The Mediatek vencsys controller provides various clocks to the system. Required Properties: - compatible: Should be one of: + - "mediatek,mt2712-vencsys", "syscon" - "mediatek,mt6797-vencsys", "syscon" - "mediatek,mt8173-vencsys", "syscon" - #clock-cells: Must be 1 diff --git a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt index 2cba012f5af0..6030afb10b5c 100644 --- a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt +++ b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt @@ -33,6 +33,12 @@ Required Properties: - clock-names: Aliases for the above clocks. They should be "pll_ref", "pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively. +Optional Properties: + + - power-domains: a phandle to respective power domain node as described by + generic PM domain bindings (see power/power_domain.txt for more + information). + The following is the list of clocks generated by the controller. Each clock is assigned an identifier and client nodes use this identifier to specify the clock which they consume. Some of the clocks are available only on a particular diff --git a/Documentation/devicetree/bindings/clock/exynos4-clock.txt b/Documentation/devicetree/bindings/clock/exynos4-clock.txt index f5a5b19ed3b2..bc61c952cb0b 100644 --- a/Documentation/devicetree/bindings/clock/exynos4-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos4-clock.txt @@ -41,3 +41,46 @@ Example 2: UART controller node that consumes the clock generated by the clock clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>; clock-names = "uart", "clk_uart_baud0"; }; + +Exynos4412 SoC contains some additional clocks for FIMC-ISP (Camera ISP) +subsystem. Registers for those clocks are located in the ISP power domain. +Because those registers are also located in a different memory region than +the main clock controller, a separate clock controller has to be defined for +handling them. + +Required Properties: + +- compatible: should be "samsung,exynos4412-isp-clock". + +- reg: physical base address of the ISP clock controller and length of memory + mapped region. + +- #clock-cells: should be 1. + +- clocks: list of the clock controller input clock identifiers, + from common clock bindings, should point to CLK_ACLK200 and + CLK_ACLK400_MCUISP clocks from the main clock controller. + +- clock-names: list of the clock controller input clock names, + as described in clock-bindings.txt, should be "aclk200" and + "aclk400_mcuisp". + +- power-domains: a phandle to ISP power domain node as described by + generic PM domain bindings. + +Example 3: The clock controllers bindings for Exynos4412 SoCs. + + clock: clock-controller@10030000 { + compatible = "samsung,exynos4412-clock"; + reg = <0x10030000 0x18000>; + #clock-cells = <1>; + }; + + isp_clock: clock-controller@10048000 { + compatible = "samsung,exynos4412-isp-clock"; + reg = <0x10048000 0x1000>; + #clock-cells = <1>; + power-domains = <&pd_isp>; + clocks = <&clock CLK_ACLK200>, <&clock CLK_ACLK400_MCUISP>; + clock-names = "aclk200", "aclk400_mcuisp"; + }; diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index fe885abc9cb4..c473dd38dd55 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -168,6 +168,11 @@ Required Properties: - aclk_cam1_400 - aclk_cam1_552 +Optional properties: + - power-domains: a phandle to respective power domain node as described by + generic PM domain bindings (see power/power_domain.txt for more + information). + Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. @@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_top CLK_ACLK_G2D_266>, <&cmu_top CLK_ACLK_G2D_400>; + power-domains = <&pd_g2d>; }; cmu_disp: clock-controller@13b90000 { @@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below. <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>, <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>, <&cmu_mif CLK_ACLK_DISP_333>; + power-domains = <&pd_disp>; }; cmu_aud: clock-controller@114c0000 { @@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below. clock-names = "oscclk", "fout_aud_pll"; clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>; + power-domains = <&pd_aud>; }; cmu_bus0: clock-controller@13600000 { @@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below. clock-names = "oscclk", "aclk_g3d_400"; clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>; + power-domains = <&pd_g3d>; }; cmu_gscl: clock-controller@13cf0000 { @@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_top CLK_ACLK_GSCL_111>, <&cmu_top CLK_ACLK_GSCL_333>; + power-domains = <&pd_gscl>; }; cmu_apollo: clock-controller@11900000 { @@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_top CLK_SCLK_JPEG_MSCL>, <&cmu_top CLK_ACLK_MSCL_400>; + power-domains = <&pd_mscl>; }; cmu_mfc: clock-controller@15280000 { @@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below. clock-names = "oscclk", "aclk_mfc_400"; clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>; + power-domains = <&pd_mfc>; }; cmu_hevc: clock-controller@14f80000 { @@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below. clock-names = "oscclk", "aclk_hevc_400"; clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>; + power-domains = <&pd_hevc>; }; cmu_isp: clock-controller@146d0000 { @@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below. clocks = <&xxti>, <&cmu_top CLK_ACLK_ISP_DIS_400>, <&cmu_top CLK_ACLK_ISP_400>; + power-domains = <&pd_isp>; }; cmu_cam0: clock-controller@120d0000 { @@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below. <&cmu_top CLK_ACLK_CAM0_333>, <&cmu_top CLK_ACLK_CAM0_400>, <&cmu_top CLK_ACLK_CAM0_552>; + power-domains = <&pd_cam0>; }; cmu_cam1: clock-controller@145d0000 { @@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below. <&cmu_top CLK_ACLK_CAM1_333>, <&cmu_top CLK_ACLK_CAM1_400>, <&cmu_top CLK_ACLK_CAM1_552>; + power-domains = <&pd_cam1>; }; Example 3: UART controller node that consumes the clock generated by the clock diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index a7235e9e1c97..4491d1c104aa 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -10,12 +10,23 @@ Required properties : - compatible : shall contain only one of the following. The generic compatible "qcom,rpmcc" should be also included. + "qcom,rpmcc-msm8660", "qcom,rpmcc" + "qcom,rpmcc-apq8060", "qcom,rpmcc" "qcom,rpmcc-msm8916", "qcom,rpmcc" "qcom,rpmcc-msm8974", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" + "qcom,rpmcc-msm8996", "qcom,rpmcc" - #clock-cells : shall contain 1 +The clock enumerators are defined in +and come in pairs: FOO_CLK followed by FOO_A_CLK. The latter clock +is an "active" clock, which means that the consumer only care that the +clock is available when the apps CPU subsystem is active, i.e. not +suspended or in deep idle. If it is important that the clock keeps running +during system suspend, you need to specify the non-active clock, the one +not containing *_A_* in the enumerator name. + Example: smd { compatible = "qcom,smd"; diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt index 316e13686568..f1890d0777a6 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt @@ -22,6 +22,7 @@ Required Properties: - "renesas,r8a7794-cpg-mssr" for the r8a7794 SoC (R-Car E2) - "renesas,r8a7795-cpg-mssr" for the r8a7795 SoC (R-Car H3) - "renesas,r8a7796-cpg-mssr" for the r8a7796 SoC (R-Car M3-W) + - "renesas,r8a77970-cpg-mssr" for the r8a77970 SoC (R-Car V3M) - "renesas,r8a77995-cpg-mssr" for the r8a77995 SoC (R-Car D3) - reg: Base address and length of the memory resource used by the CPG/MSSR @@ -31,8 +32,8 @@ Required Properties: clock-names - clock-names: List of external parent clock names. Valid names are: - "extal" (r8a7743, r8a7745, r8a7790, r8a7791, r8a7792, r8a7793, r8a7794, - r8a7795, r8a7796, r8a77995) - - "extalr" (r8a7795, r8a7796) + r8a7795, r8a7796, r8a77970, r8a77995) + - "extalr" (r8a7795, r8a7796, r8a77970) - "usb_extal" (r8a7743, r8a7745, r8a7790, r8a7791, r8a7793, r8a7794) - #clock-cells: Must be 2 diff --git a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt index bb5d942075fb..8ff3e2774ed8 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt @@ -1,6 +1,6 @@ -* Renesas RZ Clock Pulse Generator (CPG) +* Renesas RZ/A1 Clock Pulse Generator (CPG) -The CPG generates core clocks for the RZ SoCs. It includes the PLL, variable +The CPG generates core clocks for the RZ/A1 SoCs. It includes the PLL, variable CPU and GPU clocks, and several fixed ratio dividers. The CPG also provides a Clock Domain for SoC devices, in combination with the CPG Module Stop (MSTP) Clocks. diff --git a/Documentation/devicetree/bindings/display/google,goldfish-fb.txt b/Documentation/devicetree/bindings/display/google,goldfish-fb.txt new file mode 100644 index 000000000000..751fa9f51e5d --- /dev/null +++ b/Documentation/devicetree/bindings/display/google,goldfish-fb.txt @@ -0,0 +1,17 @@ +Android Goldfish framebuffer + +Android Goldfish framebuffer device used by Android emulator. + +Required properties: + +- compatible : should contain "google,goldfish-fb" +- reg : +- interrupts : + +Example: + + display-controller@1f008000 { + compatible = "google,goldfish-fb"; + interrupts = <0x10>; + reg = <0x1f008000 0x100>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index f79854783c2c..5bf77f6dd19d 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -129,7 +129,7 @@ Optional properties: example: -display@di0 { +disp0 { compatible = "fsl,imx-parallel-display"; edid = [edid-data]; interface-pix-fmt = "rgb24"; diff --git a/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt b/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt index f248056da24c..bb2075df9b38 100644 --- a/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt @@ -1,7 +1,9 @@ * Cadence Quad SPI controller Required properties: -- compatible : Should be "cdns,qspi-nor". +- compatible : should be one of the following: + Generic default - "cdns,qspi-nor". + For TI 66AK2G SoC - "ti,k2g-qspi", "cdns,qspi-nor". - reg : Contains two entries, each of which is a tuple consisting of a physical address and length. The first entry is the address and length of the controller register set. The second entry is the @@ -14,6 +16,9 @@ Required properties: Optional properties: - cdns,is-decoded-cs : Flag to indicate whether decoder is used or not. +- cdns,rclk-en : Flag to indicate that QSPI return clock is used to latch + the read data rather than the QSPI clock. Make sure that QSPI return + clock is populated on the board before using this property. Optional subnodes: Subnodes of the Cadence Quad SPI controller are spi slave nodes with additional diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt index 504291d2e5c2..0ee8edb60efc 100644 --- a/Documentation/devicetree/bindings/mtd/denali-nand.txt +++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt @@ -29,7 +29,7 @@ nand: nand@ff900000 { #address-cells = <1>; #size-cells = <1>; compatible = "altr,socfpga-denali-nand"; - reg = <0xff900000 0x100000>, <0xffb80000 0x10000>; + reg = <0xff900000 0x20>, <0xffb80000 0x1000>; reg-names = "nand_data", "denali_reg"; interrupts = <0 144 4>; }; diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt index 4cab5d85cf6f..376fa2f50e6b 100644 --- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt +++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt @@ -14,6 +14,7 @@ Required properties: at25df641 at26df081a en25s64 + mr25h128 mr25h256 mr25h10 mr25h40 diff --git a/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt index 840f9405dcf0..56d3668e2c50 100644 --- a/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt @@ -1,13 +1,16 @@ * Serial NOR flash controller for MTK MT81xx (and similar) Required properties: -- compatible: The possible values are: - "mediatek,mt2701-nor" - "mediatek,mt7623-nor" +- compatible: For mt8173, compatible should be "mediatek,mt8173-nor", + and it's the fallback compatible for other Soc. + For every other SoC, should contain both the SoC-specific compatible + string and "mediatek,mt8173-nor". + The possible values are: + "mediatek,mt2701-nor", "mediatek,mt8173-nor" + "mediatek,mt2712-nor", "mediatek,mt8173-nor" + "mediatek,mt7622-nor", "mediatek,mt8173-nor" + "mediatek,mt7623-nor", "mediatek,mt8173-nor" "mediatek,mt8173-nor" - For mt8173, compatible should be "mediatek,mt8173-nor". - For every other SoC, should contain both the SoC-specific compatible string - and "mediatek,mt8173-nor". - reg: physical base address and length of the controller's register - clocks: the phandle of the clocks needed by the nor controller - clock-names: the names of the clocks diff --git a/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt b/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt index d9b655f11048..d4ee4da58463 100644 --- a/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt +++ b/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt @@ -5,9 +5,13 @@ Required properties: - compatible: Should be set to one of the following: marvell,pxa3xx-nand marvell,armada370-nand + marvell,armada-8k-nand - reg: The register base for the controller - interrupts: The interrupt to map - #address-cells: Set to <1> if the node includes partitions + - marvell,system-controller: Set to retrieve the syscon node that handles + NAND controller related registers (only required + with marvell,armada-8k-nand compatible). Optional properties: diff --git a/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt b/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt index 7e94b802395d..74c118015980 100644 --- a/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt +++ b/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt @@ -9,6 +9,7 @@ Required Properties: - "renesas,pwm-r8a7794": for R-Car E2 - "renesas,pwm-r8a7795": for R-Car H3 - "renesas,pwm-r8a7796": for R-Car M3-W + - "renesas,pwm-r8a77995": for R-Car D3 - reg: base address and length of the registers block for the PWM. - #pwm-cells: should be 2. See pwm.txt in this directory for a description of the cells format. diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 7ff3f7903f26..00d3d58a102f 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -10,6 +10,7 @@ on the Qualcomm Hexagon core. "qcom,q6v5-pil", "qcom,msm8916-mss-pil", "qcom,msm8974-mss-pil" + "qcom,msm8996-mss-pil" - reg: Usage: required diff --git a/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt b/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt index 323cf26374cb..c797bc9d77d2 100644 --- a/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt +++ b/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt @@ -1,20 +1,20 @@ * i.MX25 Real Time Clock controller -This binding supports the following chips: i.MX25, i.MX53 - Required properties: - compatible: should be: "fsl,imx25-rtc" - reg: physical base address of the controller and length of memory mapped region. +- clocks: should contain the phandle for the rtc clock - interrupts: rtc alarm interrupt Optional properties: -- interrupts: dryice security violation interrupt +- interrupts: dryice security violation interrupt (second entry) Example: -rtc@80056000 { - compatible = "fsl,imx53-rtc", "fsl,imx25-rtc"; - reg = <0x80056000 2000>; - interrupts = <29 56>; +rtc@53ffc000 { + compatible = "fsl,imx25-rtc"; + reg = <0x53ffc000 0x4000>; + clocks = <&clks 81>; + interrupts = <25 56>; }; diff --git a/Documentation/devicetree/bindings/rtc/pcf85363.txt b/Documentation/devicetree/bindings/rtc/pcf85363.txt new file mode 100644 index 000000000000..76fdabc59742 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/pcf85363.txt @@ -0,0 +1,17 @@ +NXP PCF85363 Real Time Clock +============================ + +Required properties: +- compatible: Should contain "nxp,pcf85363". +- reg: I2C address for chip. + +Optional properties: +- interrupts: IRQ line for the RTC (not implemented). + +Example: + +pcf85363: pcf85363@51 { + compatible = "nxp,pcf85363"; + reg = <0x51>; +}; + diff --git a/Documentation/devicetree/bindings/rtc/rtc-mt7622.txt b/Documentation/devicetree/bindings/rtc/rtc-mt7622.txt new file mode 100644 index 000000000000..09fe8f51476f --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-mt7622.txt @@ -0,0 +1,21 @@ +Device-Tree bindings for MediaTek SoC based RTC + +Required properties: +- compatible : Should be + "mediatek,mt7622-rtc", "mediatek,soc-rtc" : for MT7622 SoC +- reg : Specifies base physical address and size of the registers; +- interrupts : Should contain the interrupt for RTC alarm; +- clocks : Specifies list of clock specifiers, corresponding to + entries in clock-names property; +- clock-names : Should contain "rtc" entries + +Example: + +rtc: rtc@10212800 { + compatible = "mediatek,mt7622-rtc", + "mediatek,soc-rtc"; + reg = <0 0x10212800 0 0x200>; + interrupts = ; + clocks = <&topckgen CLK_TOP_RTC>; + clock-names = "rtc"; +}; diff --git a/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt new file mode 100644 index 000000000000..7c170da0d4b7 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt @@ -0,0 +1,27 @@ +Spreadtrum SC27xx Real Time Clock + +Required properties: +- compatible: should be "sprd,sc2731-rtc". +- reg: address offset of rtc register. +- interrupt-parent: phandle for the interrupt controller. +- interrupts: rtc alarm interrupt. + +Example: + + sc2731_pmic: pmic@0 { + compatible = "sprd,sc2731"; + reg = <0>; + spi-max-frequency = <26000000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + + rtc@280 { + compatible = "sprd,sc2731-rtc"; + reg = <0x280>; + interrupt-parent = <&sc2731_pmic>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt index b277eca861f7..9663cab52246 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt @@ -39,6 +39,14 @@ of these nodes are defined by the individual bindings for the specific function Definition: a list of channels tied to this function, used for matching the function to a set of virtual channels +- qcom,intents: + Usage: optional + Value type: + Definition: a list of size,amount pairs describing what intents should + be preallocated for this virtual channel. This can be used + to tweak the default intents available for the channel to + meet expectations of the remote. + = EXAMPLE The following example represents the GLINK RPM node on a MSM8996 device, with the function for the "rpm_request" channel defined, which is used for @@ -69,6 +77,8 @@ regualtors and root clocks. compatible = "qcom,rpm-msm8996"; qcom,glink-channels = "rpm_requests"; + qcom,intents = <0x400 5 + 0x800 1>; ... }; }; diff --git a/Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt b/Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt new file mode 100644 index 000000000000..9d43553a8d39 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt @@ -0,0 +1,20 @@ +* Broadcom STB thermal management + +Thermal management core, provided by the AVS TMON hardware block. + +Required properties: +- compatible: must be "brcm,avs-tmon" and/or "brcm,avs-tmon-bcm7445" +- reg: address range for the AVS TMON registers +- interrupts: temperature monitor interrupt, for high/low threshold triggers +- interrupt-names: should be "tmon" +- interrupt-parent: the parent interrupt controller + +Example: + + thermal@f04d1500 { + compatible = "brcm,avs-tmon-bcm7445", "brcm,avs-tmon"; + reg = <0xf04d1500 0x28>; + interrupts = <0x6>; + interrupt-names = "tmon"; + interrupt-parent = <&avs_host_l2_intc>; + }; diff --git a/Documentation/devicetree/bindings/thermal/imx-thermal.txt b/Documentation/devicetree/bindings/thermal/imx-thermal.txt index 3c67bd50aa10..28be51afdb6a 100644 --- a/Documentation/devicetree/bindings/thermal/imx-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/imx-thermal.txt @@ -7,10 +7,17 @@ Required properties: is higher than panic threshold, system will auto reboot by SRC module. - fsl,tempmon : phandle pointer to system controller that contains TEMPMON control registers, e.g. ANATOP on imx6q. +- nvmem-cells: A phandle to the calibration cells provided by ocotp. +- nvmem-cell-names: Should be "calib", "temp_grade". + +Deprecated properties: - fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON calibration data, e.g. OCOTP on imx6q. The details about calibration data can be found in SoC Reference Manual. +Direct access to OCOTP via fsl,tempmon-data is incorrect on some newer chips +because it does not handle OCOTP clock requirements. + Optional properties: - clocks : thermal sensor's clock source. diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt index e3a6234fb1ac..43d744e5305e 100644 --- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt @@ -2,6 +2,7 @@ Required properties: - compatible : should be "rockchip,-tsadc" + "rockchip,rv1108-tsadc": found on RV1108 SoCs "rockchip,rk3228-tsadc": found on RK3228 SoCs "rockchip,rk3288-tsadc": found on RK3288 SoCs "rockchip,rk3328-tsadc": found on RK3328 SoCs diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt index 27dce08edd73..5f3143f97098 100644 --- a/Documentation/devicetree/bindings/trivial-devices.txt +++ b/Documentation/devicetree/bindings/trivial-devices.txt @@ -55,7 +55,6 @@ epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE emmicro,em3027 EM Microelectronic EM3027 Real-time Clock fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer -fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51 fsl,mma7660 MMA7660FC: 3-Axis Orientation/Motion Detection Sensor fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer fsl,mpl3115 MPL3115: Absolute Digital Pressure Sensor @@ -73,7 +72,6 @@ maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs maxim,max6621 PECI-to-I2C translator for PECI-to-SMBus/I2C protocol conversion maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface -mc,rv3029c2 Real Time Clock Module with I2C-Bus mcube,mc3230 mCube 3-axis 8-bit digital accelerometer memsic,mxc6225 MEMSIC 2-axis 8-bit digital accelerometer microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k) @@ -142,6 +140,7 @@ microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k) microchip,tc654 PWM Fan Speed Controller With Fan Fault Detection microchip,tc655 PWM Fan Speed Controller With Fan Fault Detection +microcrystal,rv3029 Real Time Clock Module with I2C-Bus miramems,da226 MiraMEMS DA226 2-axis 14-bit digital accelerometer miramems,da280 MiraMEMS DA280 3-axis 14-bit digital accelerometer miramems,da311 MiraMEMS DA311 3-axis 12-bit digital accelerometer diff --git a/Documentation/devicetree/bindings/usb/usb-device.txt b/Documentation/devicetree/bindings/usb/usb-device.txt index ce02cebac26a..1b27cebb47f4 100644 --- a/Documentation/devicetree/bindings/usb/usb-device.txt +++ b/Documentation/devicetree/bindings/usb/usb-device.txt @@ -4,24 +4,35 @@ Usually, we only use device tree for hard wired USB device. The reference binding doc is from: http://www.devicetree.org/open-firmware/bindings/usb/usb-1_0.ps + Required properties: -- compatible: usbVID,PID. The textual representation of VID, PID shall - be in lower case hexadecimal with leading zeroes suppressed. The - other compatible strings from the above standard binding could also - be used, but a device adhering to this binding may leave out all except - for usbVID,PID. -- reg: the port number which this device is connecting to, the range - is 1-31. +- compatible: "usbVID,PID", where VID is the vendor id and PID the product id. + The textual representation of VID and PID shall be in lower case hexadecimal + with leading zeroes suppressed. The other compatible strings from the above + standard binding could also be used, but a device adhering to this binding + may leave out all except for "usbVID,PID". +- reg: the number of the USB hub port or the USB host-controller port to which + this device is attached. The range is 1-255. + + +Required properties for hub nodes with device nodes: +- #address-cells: shall be 1 +- #size-cells: shall be 0 -Example: -&usb1 { +Required properties for host-controller nodes with device nodes: +- #address-cells: shall be 1 +- #size-cells: shall be 0 + + +Example: +&usb1 { /* host controller */ #address-cells = <1>; #size-cells = <0>; - hub: genesys@1 { + hub@1 { /* hub connected to port 1 */ compatible = "usb5e3,608"; reg = <1>; }; -} +}; diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 69f08c0f23a8..c180045eb43b 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -237,6 +237,7 @@ CLOCK devm_clk_get() devm_clk_put() devm_clk_hw_register() + devm_of_clk_add_hw_provider() DMA dmam_alloc_coherent() diff --git a/Documentation/filesystems/cramfs.txt b/Documentation/filesystems/cramfs.txt index 4006298f6707..8e19a53d648b 100644 --- a/Documentation/filesystems/cramfs.txt +++ b/Documentation/filesystems/cramfs.txt @@ -45,6 +45,48 @@ you can just change the #define in mkcramfs.c, so long as you don't mind the filesystem becoming unreadable to future kernels. +Memory Mapped cramfs image +-------------------------- + +The CRAMFS_MTD Kconfig option adds support for loading data directly from +a physical linear memory range (usually non volatile memory like Flash) +instead of going through the block device layer. This saves some memory +since no intermediate buffering is necessary to hold the data before +decompressing. + +And when data blocks are kept uncompressed and properly aligned, they will +automatically be mapped directly into user space whenever possible providing +eXecute-In-Place (XIP) from ROM of read-only segments. Data segments mapped +read-write (hence they have to be copied to RAM) may still be compressed in +the cramfs image in the same file along with non compressed read-only +segments. Both MMU and no-MMU systems are supported. This is particularly +handy for tiny embedded systems with very tight memory constraints. + +The location of the cramfs image in memory is system dependent. You must +know the proper physical address where the cramfs image is located and +configure an MTD device for it. Also, that MTD device must be supported +by a map driver that implements the "point" method. Examples of such +MTD drivers are cfi_cmdset_0001 (Intel/Sharp CFI flash) or physmap +(Flash device in physical memory map). MTD partitions based on such devices +are fine too. Then that device should be specified with the "mtd:" prefix +as the mount device argument. For example, to mount the MTD device named +"fs_partition" on the /mnt directory: + +$ mount -t cramfs mtd:fs_partition /mnt + +To boot a kernel with this as root filesystem, suffice to specify +something like "root=mtd:fs_partition" on the kernel command line. + + +Tools +----- + +A version of mkcramfs that can take advantage of the latest capabilities +described above can be found here: + +https://github.com/npitre/cramfs-tools + + For /usr/share/magic -------------------- diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 93e0a2404532..17bb4dc28fae 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -501,10 +501,6 @@ in your dentry operations instead. is non-NULL. Note that link body isn't available anymore, so if you need it, store it as cookie. -- -[mandatory] - __fd_install() & fd_install() can now sleep. Callers should not - hold a spinlock or other resources that do not allow a schedule. --- [mandatory] any symlink that might use page_follow_link_light/page_put_link() must have inode_nohighmem(inode) called before anything might start playing with diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index ec571b9bb18a..2a84bb334894 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -181,6 +181,7 @@ read the file /proc/PID/status: VmPTE: 20 kb VmSwap: 0 kB HugetlbPages: 0 kB + CoreDumping: 0 Threads: 1 SigQ: 0/28578 SigPnd: 0000000000000000 @@ -253,6 +254,8 @@ Table 1-2: Contents of the status files (as of 4.8) VmSwap amount of swap used by anonymous private data (shmem swap usage is not included) HugetlbPages size of hugetlb memory portions + CoreDumping process's memory is currently being dumped + (killing the process may lead to a corrupted core) Threads number of threads SigQ number of signals queued/max. number for queue SigPnd bitmap of pending signals for the thread diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 96f8ec7dbe4e..36625aa66c27 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -409,5 +409,15 @@ those drivers as simple as possible, so lots of room for refactoring: Contact: Noralf Trønnes, Daniel Vetter +AMD DC Display Driver +--------------------- + +AMD DC is the display driver for AMD devices starting with Vega. There has been +a bunch of progress cleaning it up but there's still plenty of work to be done. + +See drivers/gpu/drm/amd/display/TODO for tasks. + +Contact: Harry Wentland, Alex Deucher + Outside DRM =========== diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 57af2f7963ee..937e33c46211 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -435,8 +435,7 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: PM status to 'suspended' and update its parent's counter of 'active' children as appropriate (it is only valid to use this function if 'power.runtime_error' is set or 'power.disable_depth' is greater than - zero); it will fail and return an error code if the device has a child - which is active and the 'power.ignore_children' flag is unset + zero) bool pm_runtime_active(struct device *dev); - return true if the device's runtime PM status is 'active' or its diff --git a/Documentation/process/5.Posting.rst b/Documentation/process/5.Posting.rst index 1b7728b19ea7..645fa9c7388a 100644 --- a/Documentation/process/5.Posting.rst +++ b/Documentation/process/5.Posting.rst @@ -213,6 +213,11 @@ The tags in common use are: which can be found in Documentation/process/submitting-patches.rst. Code without a proper signoff cannot be merged into the mainline. + - Co-Developed-by: states that the patch was also created by another developer + along with the original author. This is useful at times when multiple + people work on a single patch. Note, this person also needs to have a + Signed-off-by: line in the patch as well. + - Acked-by: indicates an agreement by another developer (often a maintainer of the relevant code) that the patch is appropriate for inclusion into the kernel. diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index 1266eeae45f6..9ce7256c6edb 100644 --- a/Documentation/security/keys/core.rst +++ b/Documentation/security/keys/core.rst @@ -628,12 +628,12 @@ The keyctl syscall functions are: defined key type will return its data as is. If a key type does not implement this function, error EOPNOTSUPP will result. - As much of the data as can be fitted into the buffer will be copied to - userspace if the buffer pointer is not NULL. - - On a successful return, the function will always return the amount of data - available rather than the amount copied. + If the specified buffer is too small, then the size of the buffer required + will be returned. Note that in this case, the contents of the buffer may + have been overwritten in some undefined way. + Otherwise, on success, the function will return the amount of data copied + into the buffer. * Instantiate a partially constructed key:: diff --git a/Documentation/svga.txt b/Documentation/svga.txt index 119f1515b1ac..b6c2f9acca92 100644 --- a/Documentation/svga.txt +++ b/Documentation/svga.txt @@ -67,8 +67,7 @@ The menu looks like:: tells what video adapter did Linux detect -- it's either a generic adapter name (MDA, CGA, HGC, EGA, VGA, VESA VGA [a VGA with VESA-compliant BIOS]) or a chipset name (e.g., Trident). Direct detection -of chipsets is turned off by default (see CONFIG_VIDEO_SVGA in chapter 4 to see -how to enable it if you really want) as it's inherently unreliable due to +of chipsets is turned off by default as it's inherently unreliable due to absolutely insane PC design. "0 0F00 80x25" means that the first menu item (the menu items are numbered @@ -138,7 +137,7 @@ The ID numbers can be divided to those regions:: 0x0f05 VGA 80x30 (480 scans, 16-point font) 0x0f06 VGA 80x34 (480 scans, 14-point font) 0x0f07 VGA 80x60 (480 scans, 8-point font) - 0x0f08 Graphics hack (see the CONFIG_VIDEO_HACK paragraph below) + 0x0f08 Graphics hack (see the VIDEO_GFX_HACK paragraph below) 0x1000 to 0x7fff - modes specified by resolution. The code has a "0xRRCC" form where RR is a number of rows and CC is a number of columns. @@ -160,58 +159,22 @@ end of the display. Options ~~~~~~~ -Some options can be set in the source text (in arch/i386/boot/video.S). -All of them are simple #define's -- change them to #undef's when you want to -switch them off. Currently supported: - -CONFIG_VIDEO_SVGA - enables autodetection of SVGA cards. This is switched -off by default as it's a bit unreliable due to terribly bad PC design. If you -really want to have the adapter autodetected (maybe in case the ``scan`` feature -doesn't work on your machine), switch this on and don't cry if the results -are not completely sane. In case you really need this feature, please drop me -a mail as I think of removing it some day. - -CONFIG_VIDEO_VESA - enables autodetection of VESA modes. If it doesn't work -on your machine (or displays a "Error: Scanning of VESA modes failed" message), -you can switch it off and report as a bug. - -CONFIG_VIDEO_COMPACT - enables compacting of the video mode list. If there -are more modes with the same screen size, only the first one is kept (see above -for more info on mode ordering). However, in very strange cases it's possible -that the first "version" of the mode doesn't work although some of the others -do -- in this case turn this switch off to see the rest. - -CONFIG_VIDEO_RETAIN - enables retaining of screen contents when switching -video modes. Works only with some boot loaders which leave enough room for the -buffer. (If you have old LILO, you can adjust heap_end_ptr and loadflags -in setup.S, but it's better to upgrade the boot loader...) - -CONFIG_VIDEO_LOCAL - enables inclusion of "local modes" in the list. The -local modes are added automatically to the beginning of the list not depending -on hardware configuration. The local modes are listed in the source text after -the "local_mode_table:" line. The comment before this line describes the format -of the table (which also includes a video card name to be displayed on the -top of the menu). - -CONFIG_VIDEO_400_HACK - force setting of 400 scan lines for standard VGA -modes. This option is intended to be used on certain buggy BIOSes which draw -some useless logo using font download and then fail to reset the correct mode. -Don't use unless needed as it forces resetting the video card. - -CONFIG_VIDEO_GFX_HACK - includes special hack for setting of graphics modes -to be used later by special drivers (e.g., 800x600 on IBM ThinkPad -- see -ftp://ftp.phys.keio.ac.jp/pub/XFree86/800x600/XF86Configs/XF86Config.IBM_TP560). +Build options for arch/x86/boot/* are selected by the kernel kconfig +utility and the kernel .config file. + +VIDEO_GFX_HACK - includes special hack for setting of graphics modes +to be used later by special drivers. Allows to set _any_ BIOS mode including graphic ones and forcing specific text screen resolution instead of peeking it from BIOS variables. Don't use unless you think you know what you're doing. To activate this setup, use -mode number 0x0f08 (see section 3). +mode number 0x0f08 (see the Mode IDs section above). Still doesn't work? ~~~~~~~~~~~~~~~~~~~ When the mode detection doesn't work (e.g., the mode list is incorrect or the machine hangs instead of displaying the menu), try to switch off some of -the configuration options listed in section 4. If it fails, you can still use +the configuration options listed under "Options". If it fails, you can still use your kernel with the video mode set directly via the kernel parameter. In either case, please send me a bug report containing what _exactly_ @@ -228,10 +191,6 @@ contains the most common video BIOS bug called "incorrect vertical display end setting". Adding 0x8000 to the mode ID might fix the problem. Unfortunately, this must be done manually -- no autodetection mechanisms are available. -If you have a VGA card and your display still looks as on EGA, your BIOS -is probably broken and you need to set the CONFIG_VIDEO_400_HACK switch to -force setting of the correct mode. - History ~~~~~~~ diff --git a/Documentation/switchtec.txt b/Documentation/switchtec.txt index a0a9c7b3d4d5..f788264921ff 100644 --- a/Documentation/switchtec.txt +++ b/Documentation/switchtec.txt @@ -78,3 +78,15 @@ The following IOCTLs are also supported by the device: between PCI Function Framework number (used by the event system) and Switchtec Logic Port ID and Partition number (which is more user friendly). + + +Non-Transparent Bridge (NTB) Driver +=================================== + +An NTB driver is provided for the switchtec hardware in switchtec_ntb. +Currently, it only supports switches configured with exactly 2 +partitions. It also requires the following configuration settings: + +* Both partitions must be able to access each other's GAS spaces. + Thus, the bits in the GAS Access Vector under Management Settings + must be set to support this. diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 055c8b3e1018..b920423f88cb 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -818,7 +818,7 @@ tooling to work, you can do: swappiness This control is used to define how aggressive the kernel will swap -memory pages. Higher values will increase agressiveness, lower values +memory pages. Higher values will increase aggressiveness, lower values decrease the amount of swap. A value of 0 instructs the kernel not to initiate swap until the amount of free and file-backed pages is less than the high water mark in a zone. diff --git a/Documentation/translations/ko_KR/memory-barriers.txt b/Documentation/translations/ko_KR/memory-barriers.txt index ec3b46e27b7a..0a0930ab4156 100644 --- a/Documentation/translations/ko_KR/memory-barriers.txt +++ b/Documentation/translations/ko_KR/memory-barriers.txt @@ -82,7 +82,7 @@ Documentation/memory-barriers.txt - SMP 배리어 짝맞추기. - 메모리 배리어 시퀀스의 예. - 읽기 메모리 배리어 vs 로드 예측. - - 이행성 + - Multicopy 원자성. (*) 명시적 커널 배리어. @@ -656,6 +656,11 @@ Documentation/RCU/rcu_dereference.txt 파일을 주의 깊게 읽어 주시기 해줍니다. +데이터 의존성에 의해 제공되는 이 순서규칙은 이를 포함하고 있는 CPU 에 +지역적임을 알아두시기 바랍니다. 더 많은 정보를 위해선 "Multicopy 원자성" +섹션을 참고하세요. + + 데이터 의존성 배리어는 매우 중요한데, 예를 들어 RCU 시스템에서 그렇습니다. include/linux/rcupdate.h 의 rcu_assign_pointer() 와 rcu_dereference() 를 참고하세요. 여기서 데이터 의존성 배리어는 RCU 로 관리되는 포인터의 타겟을 현재 @@ -864,38 +869,10 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레 주어진 if 문의 then 절과 else 절에게만 (그리고 이 두 절 내에서 호출되는 함수들에게까지) 적용되지, 이 if 문을 뒤따르는 코드에는 적용되지 않습니다. -마지막으로, 컨트롤 의존성은 이행성 (transitivity) 을 제공하지 -않습니다-. 이건 -'x' 와 'y' 가 둘 다 0 이라는 초기값을 가졌다는 가정 하의 두개의 예제로 -보이겠습니다: - - CPU 0 CPU 1 - ======================= ======================= - r1 = READ_ONCE(x); r2 = READ_ONCE(y); - if (r1 > 0) if (r2 > 0) - WRITE_ONCE(y, 1); WRITE_ONCE(x, 1); - - assert(!(r1 == 1 && r2 == 1)); - -이 두 CPU 예제에서 assert() 의 조건은 항상 참일 것입니다. 그리고, 만약 컨트롤 -의존성이 이행성을 (실제로는 그러지 않지만) 보장한다면, 다음의 CPU 가 추가되어도 -아래의 assert() 조건은 참이 될것입니다: - CPU 2 - ===================== - WRITE_ONCE(x, 2); +컨트롤 의존성에 의해 제공되는 이 순서규칙은 이를 포함하고 있는 CPU 에 +지역적입니다. 더 많은 정보를 위해선 "Multicopy 원자성" 섹션을 참고하세요. - assert(!(r1 == 2 && r2 == 1 && x == 2)); /* FAILS!!! */ - -하지만 컨트롤 의존성은 이행성을 제공하지 -않기- 때문에, 세개의 CPU 예제가 실행 -완료된 후에 위의 assert() 의 조건은 거짓으로 평가될 수 있습니다. 세개의 CPU -예제가 순서를 지키길 원한다면, CPU 0 와 CPU 1 코드의 로드와 스토어 사이, "if" -문 바로 다음에 smp_mb()를 넣어야 합니다. 더 나아가서, 최초의 두 CPU 예제는 -매우 위험하므로 사용되지 않아야 합니다. - -이 두개의 예제는 다음 논문: -http://www.cl.cam.ac.uk/users/pes20/ppc-supplemental/test6.pdf 와 -이 사이트: https://www.cl.cam.ac.uk/~pes20/ppcmem/index.html 에 나온 LB 와 WWC -리트머스 테스트입니다. 요약하자면: @@ -930,8 +907,8 @@ http://www.cl.cam.ac.uk/users/pes20/ppc-supplemental/test6.pdf 와 (*) 컨트롤 의존성은 보통 다른 타입의 배리어들과 짝을 맞춰 사용됩니다. - (*) 컨트롤 의존성은 이행성을 제공하지 -않습니다-. 이행성이 필요하다면, - smp_mb() 를 사용하세요. + (*) 컨트롤 의존성은 multicopy 원자성을 제공하지 -않습니다-. 모든 CPU 들이 + 특정 스토어를 동시에 보길 원한다면, smp_mb() 를 사용하세요. (*) 컴파일러는 컨트롤 의존성을 이해하고 있지 않습니다. 따라서 컴파일러가 여러분의 코드를 망가뜨리지 않도록 하는건 여러분이 해야 하는 일입니다. @@ -943,13 +920,14 @@ SMP 배리어 짝맞추기 CPU 간 상호작용을 다룰 때에 일부 타입의 메모리 배리어는 항상 짝을 맞춰 사용되어야 합니다. 적절하게 짝을 맞추지 않은 코드는 사실상 에러에 가깝습니다. -범용 배리어들은 범용 배리어끼리도 짝을 맞추지만 이행성이 없는 대부분의 다른 -타입의 배리어들과도 짝을 맞춥니다. ACQUIRE 배리어는 RELEASE 배리어와 짝을 -맞춥니다만, 둘 다 범용 배리어를 포함해 다른 배리어들과도 짝을 맞출 수 있습니다. -쓰기 배리어는 데이터 의존성 배리어나 컨트롤 의존성, ACQUIRE 배리어, RELEASE -배리어, 읽기 배리어, 또는 범용 배리어와 짝을 맞춥니다. 비슷하게 읽기 배리어나 -컨트롤 의존성, 또는 데이터 의존성 배리어는 쓰기 배리어나 ACQUIRE 배리어, -RELEASE 배리어, 또는 범용 배리어와 짝을 맞추는데, 다음과 같습니다: +범용 배리어들은 범용 배리어끼리도 짝을 맞추지만 multicopy 원자성이 없는 +대부분의 다른 타입의 배리어들과도 짝을 맞춥니다. ACQUIRE 배리어는 RELEASE +배리어와 짝을 맞춥니다만, 둘 다 범용 배리어를 포함해 다른 배리어들과도 짝을 +맞출 수 있습니다. 쓰기 배리어는 데이터 의존성 배리어나 컨트롤 의존성, ACQUIRE +배리어, RELEASE 배리어, 읽기 배리어, 또는 범용 배리어와 짝을 맞춥니다. +비슷하게 읽기 배리어나 컨트롤 의존성, 또는 데이터 의존성 배리어는 쓰기 배리어나 +ACQUIRE 배리어, RELEASE 배리어, 또는 범용 배리어와 짝을 맞추는데, 다음과 +같습니다: CPU 1 CPU 2 =============== =============== @@ -975,7 +953,7 @@ RELEASE 배리어, 또는 범용 배리어와 짝을 맞추는데, 다음과 같 =============== =============================== r1 = READ_ONCE(y); <범용 배리어> - WRITE_ONCE(y, 1); if (r2 = READ_ONCE(x)) { + WRITE_ONCE(x, 1); if (r2 = READ_ONCE(x)) { <묵시적 컨트롤 의존성> WRITE_ONCE(y, 1); } @@ -1361,57 +1339,74 @@ A 의 로드 두개가 모두 B 의 로드 뒤에 있지만, 서로 다른 값 : : +-------+ -이행성 ------- +MULTICOPY 원자성 +---------------- -이행성(transitivity)은 실제의 컴퓨터 시스템에서 항상 제공되지는 않는, 순서 -맞추기에 대한 상당히 직관적인 개념입니다. 다음의 예가 이행성을 보여줍니다: +Multicopy 원자성은 실제의 컴퓨터 시스템에서 항상 제공되지는 않는, 순서 맞추기에 +대한 상당히 직관적인 개념으로, 특정 스토어가 모든 CPU 들에게 동시에 보여지게 +됨을, 달리 말하자면 모든 CPU 들이 모든 스토어들이 보여지는 순서를 동의하게 되는 +것입니다. 하지만, 완전한 multicopy 원자성의 사용은 가치있는 하드웨어 +최적화들을 무능하게 만들어버릴 수 있어서, 보다 완화된 형태의 ``다른 multicopy +원자성'' 라는 이름의, 특정 스토어가 모든 -다른- CPU 들에게는 동시에 보여지게 +하는 보장을 대신 제공합니다. 이 문서의 뒷부분들은 이 완화된 형태에 대해 논하게 +됩니다만, 단순히 ``multicopy 원자성'' 이라고 부르겠습니다. + +다음의 예가 multicopy 원자성을 보입니다: CPU 1 CPU 2 CPU 3 ======================= ======================= ======================= { X = 0, Y = 0 } - STORE X=1 LOAD X STORE Y=1 - <범용 배리어> <범용 배리어> - LOAD Y LOAD X - -CPU 2 의 X 로드가 1을 리턴했고 Y 로드가 0을 리턴했다고 해봅시다. 이는 CPU 2 의 -X 로드가 CPU 1 의 X 스토어 뒤에 이루어졌고 CPU 2 의 Y 로드는 CPU 3 의 Y 스토어 -전에 이루어졌음을 의미합니다. 그럼 "CPU 3 의 X 로드는 0을 리턴할 수 있나요?" - -CPU 2 의 X 로드는 CPU 1 의 스토어 후에 이루어졌으니, CPU 3 의 X 로드는 1을 -리턴하는게 자연스럽습니다. 이런 생각이 이행성의 한 예입니다: CPU A 에서 실행된 -로드가 CPU B 에서의 같은 변수에 대한 로드를 뒤따른다면, CPU A 의 로드는 CPU B -의 로드가 내놓은 값과 같거나 ê·¸ 후의 값을 내놓아야 합니다. - -리눅스 커널에서 범용 배리어의 사용은 이행성을 보장합니다. 따라서, 앞의 예에서 -CPU 2 의 X 로드가 1을, Y 로드는 0을 리턴했다면, CPU 3 의 X 로드는 반드시 1을 -리턴합니다. - -하지만, 읽기나 쓰기 배리어에 대해서는 이행성이 보장되지 -않습니다-. 예를 들어, -앞의 예에서 CPU 2 의 범용 배리어가 아래처럼 읽기 배리어로 바뀐 경우를 생각해 -봅시다: + STORE X=1 r1=LOAD X (reads 1) LOAD Y (reads 1) + <범용 배리어> <읽기 배리어> + STORE Y=r1 LOAD X + +CPU 2 의 Y 로의 스토어에 사용되는 X 로드의 결과가 1 이었고 CPU 3 의 Y 로드가 +1을 리턴했다고 해봅시다. 이는 CPU 1 의 X 로의 스토어가 CPU 2 의 X 로부터의 +로드를 앞서고 CPU 2 의 Y 로의 스토어가 CPU 3 의 Y 로부터의 로드를 앞섬을 +의미합니다. 또한, 여기서의 메모리 배리어들은 CPU 2 가 자신의 로드를 자신의 +스토어 전에 수행하고, CPU 3 가 Y 로부터의 로드를 X 로부터의 로드 전에 수행함을 +보장합니다. 그럼 "CPU 3 의 X 로부터의 로드는 0 을 리턴할 수 있을까요?" + +CPU 3 의 X 로드가 CPU 2 의 로드보다 뒤에 이루어졌으므로, CPU 3 의 X 로부터의 +로드는 1 을 리턴한다고 예상하는게 당연합니다. 이런 예상은 multicopy +원자성으로부터 나옵니다: CPU B 에서 수행된 로드가 CPU A 의 같은 변수로부터의 +로드를 뒤따른다면 (그리고 CPU A 가 자신이 읽은 값으로 먼저 해당 변수에 스토어 +하지 않았다면) multicopy 원자성을 제공하는 시스템에서는, CPU B 의 로드가 CPU A +의 로드와 같은 값 또는 ê·¸ 나중 값을 리턴해야만 합니다. 하지만, 리눅스 커널은 +시스템들이 multicopy 원자성을 제공할 것을 요구하지 않습니다. + +앞의 범용 메모리 배리어의 사용은 모든 multicopy 원자성의 부족을 보상해줍니다. +앞의 예에서, CPU 2 의 X 로부터의 로드가 1 을 리턴했고 CPU 3 의 Y 로부터의 +로드가 1 을 리턴했다면, CPU 3 의 X 로부터의 로드는 1을 리턴해야만 합니다. + +하지만, 의존성, 읽기 배리어, 쓰기 배리어는 항상 non-multicopy 원자성을 보상해 +주지는 않습니다. 예를 들어, CPU 2 의 범용 배리어가 앞의 예에서 사라져서 +아래처럼 데이터 의존성만 남게 되었다고 해봅시다: CPU 1 CPU 2 CPU 3 ======================= ======================= ======================= { X = 0, Y = 0 } - STORE X=1 LOAD X STORE Y=1 - <읽기 배리어> <범용 배리어> - LOAD Y LOAD X - -이 코드는 이행성을 갖지 않습니다: 이 예에서는, CPU 2 의 X 로드가 1을 -리턴하고, Y 로드는 0을 리턴하지만 CPU 3 의 X 로드가 0을 리턴하는 것도 완전히 -합법적입니다. - -CPU 2 의 읽기 배리어가 자신의 읽기는 순서를 맞춰줘도, CPU 1 의 스토어와의 -순서를 맞춰준다고는 보장할 수 없다는게 핵심입니다. 따라서, CPU 1 ê³¼ CPU 2 가 -버퍼나 캐시를 공유하는 시스템에서 이 예제 코드가 실행된다면, CPU 2 는 CPU 1 이 -쓴 값에 좀 빨리 접근할 수 있을 것입니다. 따라서 CPU 1 ê³¼ CPU 2 의 접근으로 -조합된 순서를 모든 CPU 가 동의할 수 있도록 하기 위해 범용 배리어가 필요합니다. - -범용 배리어는 "글로벌 이행성"을 제공해서, 모든 CPU 들이 오퍼레이션들의 순서에 -동의하게 할 것입니다. 반면, release-acquire 조합은 "로컬 이행성" 만을 -제공해서, 해당 조합이 사용된 CPU 들만이 해당 액세스들의 조합된 순서에 동의함이 -보장됩니다. 예를 들어, 존경스런 Herman Hollerith 의 C 코드로 ë³´ë©´: + STORE X=1 r1=LOAD X (reads 1) LOAD Y (reads 1) + <데이터 의존성> <읽기 배리어> + STORE Y=r1 LOAD X (reads 0) + +이 변화는 non-multicopy 원자성이 만연하게 합니다: 이 예에서, CPU 2 의 X +로부터의 로드가 1을 리턴하고, CPU 3 의 Y 로부터의 로드가 1 을 리턴하는데, CPU 3 +의 X 로부터의 로드가 0 을 리턴하는게 완전히 합법적입니다. + +핵심은, CPU 2 의 데이터 의존성이 자신의 로드와 스토어를 순서짓지만, CPU 1 의 +스토어에 대한 순서는 보장하지 않는다는 것입니다. 따라서, 이 예제가 CPU 1 ê³¼ +CPU 2 가 스토어 버퍼나 한 수준의 캐시를 공유하는, multicopy 원자성을 제공하지 +않는 시스템에서 수행된다면 CPU 2 는 CPU 1 의 쓰기에 이른 접근을 할 수도 +있습니다. 따라서, 모든 CPU 들이 여러 접근들의 조합된 순서에 대해서 동의하게 +하기 위해서는 범용 배리어가 필요합니다. + +범용 배리어는 non-multicopy 원자성만 보상할 수 있는게 아니라, -모든- CPU 들이 +-모든- 오퍼레이션들의 순서를 동일하게 인식하게 하는 추가적인 순서 보장을 +만들어냅니다. 반대로, release-acquire 짝의 연결은 이런 추가적인 순서는 +제공하지 않는데, 해당 연결에 들어있는 CPU 들만이 메모리 접근의 조합된 순서에 +대해 동의할 것으로 보장됨을 의미합니다. 예를 들어, 존경스런 Herman Hollerith +의 코드를 C 코드로 변환하면: int u, v, x, y, z; @@ -1444,8 +1439,7 @@ CPU 2 의 읽기 배리어가 자신의 읽기는 순서를 맞춰줘도, CPU 1 } cpu0(), cpu1(), 그리고 cpu2() 는 smp_store_release()/smp_load_acquire() 쌍의 -연결을 통한 로컬 이행성에 동참하고 있으므로, 다음과 같은 결과는 나오지 않을 -겁니다: +연결에 참여되어 있으므로, 다음과 같은 결과는 나오지 않을 겁니다: r0 == 1 && r1 == 1 && r2 == 1 @@ -1454,8 +1448,9 @@ cpu0() 의 쓰기를 봐야만 하므로, 다음과 같은 결과도 없을 겁 r1 == 1 && r5 == 0 -하지만, release-acquire 타동성은 동참한 CPU 들에만 적용되므로 cpu3() 에는 -적용되지 않습니다. 따라서, 다음과 같은 결과가 가능합니다: +하지만, release-acquire 에 의해 제공되는 순서는 해당 연결에 동참한 CPU 들에만 +적용되므로 cpu3() 에, 적어도 스토어들 외에는 적용되지 않습니다. 따라서, 다음과 +같은 결과가 가능합니다: r0 == 0 && r1 == 1 && r2 == 1 && r3 == 0 && r4 == 0 @@ -1482,8 +1477,8 @@ u 로의 스토어를 cpu1() 의 v 로부터의 로드 뒤에 일어난 것으 이런 결과는 어떤 것도 재배치 되지 않는, 순차적 일관성을 가진 가상의 시스템에서도 일어날 수 있음을 기억해 두시기 바랍니다. -다시 말하지만, 당신의 코드가 글로벌 이행성을 필요로 한다면, 범용 배리어를 -사용하십시오. +다시 말하지만, 당신의 코드가 모든 오퍼레이션들의 완전한 순서를 필요로 한다면, +범용 배리어를 사용하십시오. ================== @@ -3046,6 +3041,9 @@ AMD64 Architecture Programmer's Manual Volume 2: System Programming Chapter 7.1: Memory-Access Ordering Chapter 7.4: Buffering and Combining Memory Writes +ARM Architecture Reference Manual (ARMv8, for ARMv8-A architecture profile) + Chapter B2: The AArch64 Application Level Memory Model + IA-32 Intel Architecture Software Developer's Manual, Volume 3: System Programming Guide Chapter 7.1: Locked Atomic Operations @@ -3057,6 +3055,8 @@ The SPARC Architecture Manual, Version 9 Appendix D: Formal Specification of the Memory Models Appendix J: Programming with the Memory Models +Storage in the PowerPC (Stone and Fitzgerald) + UltraSPARC Programmer Reference Manual Chapter 5: Memory Accesses and Cacheability Chapter 15: Sparc-V9 Memory Models diff --git a/Documentation/virtual/kvm/devices/arm-vgic-its.txt b/Documentation/virtual/kvm/devices/arm-vgic-its.txt index 8d5830eab26a..4f0c9fc40365 100644 --- a/Documentation/virtual/kvm/devices/arm-vgic-its.txt +++ b/Documentation/virtual/kvm/devices/arm-vgic-its.txt @@ -64,6 +64,8 @@ Groups: -EINVAL: Inconsistent restored data -EFAULT: Invalid guest ram access -EBUSY: One or more VCPUS are running + -EACCES: The virtual ITS is backed by a physical GICv4 ITS, and the + state is not available KVM_DEV_ARM_VGIC_GRP_ITS_REGS Attributes: diff --git a/Kbuild b/Kbuild index af161aa1facd..005304205482 100644 --- a/Kbuild +++ b/Kbuild @@ -18,7 +18,6 @@ targets := kernel/bounds.s # We use internal kbuild rules to avoid the "is up to date" message from make kernel/bounds.s: kernel/bounds.c FORCE - $(Q)mkdir -p $(dir $@) $(call if_changed_dep,cc_s_c) $(obj)/$(bounds-file): kernel/bounds.s FORCE @@ -54,7 +53,6 @@ targets += arch/$(SRCARCH)/kernel/asm-offsets.s # We use internal kbuild rules to avoid the "is up to date" message from make arch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c \ $(obj)/$(timeconst-file) $(obj)/$(bounds-file) FORCE - $(Q)mkdir -p $(dir $@) $(call if_changed_dep,cc_s_c) $(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s FORCE diff --git a/MAINTAINERS b/MAINTAINERS index b5b00ebb04c5..aa71ab52fd76 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -384,6 +384,7 @@ ACPI WMI DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan F: drivers/platform/x86/wmi.c +F: include/uapi/linux/wmi.h AD1889 ALSA SOUND DRIVER M: Thibaut Varene @@ -1589,10 +1590,13 @@ F: drivers/rtc/rtc-armada38x.c ARM/Mediatek RTC DRIVER M: Eddie Huang +M: Sean Wang L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/rtc/rtc-mt7622.txt F: drivers/rtc/rtc-mt6397.c +F: drivers/rtc/rtc-mt7622.c ARM/Mediatek SoC support M: Matthias Brugger @@ -2986,6 +2990,14 @@ S: Maintained F: Documentation/devicetree/bindings/cpufreq/brcm,stb-avs-cpu-freq.txt F: drivers/cpufreq/brcmstb* +BROADCOM STB AVS TMON DRIVER +M: Markus Mayer +M: bcm-kernel-feedback-list@broadcom.com +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt +F: drivers/thermal/broadcom/brcmstb* + BROADCOM STB NAND FLASH DRIVER M: Brian Norris M: Kamal Dasu @@ -3720,8 +3732,8 @@ F: drivers/cpuidle/* F: include/linux/cpuidle.h CRAMFS FILESYSTEM -W: http://sourceforge.net/projects/cramfs/ -S: Orphan / Obsolete +M: Nicolas Pitre +S: Maintained F: Documentation/filesystems/cramfs.txt F: fs/cramfs/ @@ -4022,6 +4034,26 @@ M: "Maciej W. Rozycki" S: Maintained F: drivers/net/fddi/defxx.* +DELL SMBIOS DRIVER +M: Pali Rohár +M: Mario Limonciello +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/dell-smbios.* + +DELL SMBIOS SMM DRIVER +M: Mario Limonciello +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/dell-smbios-smm.c + +DELL SMBIOS WMI DRIVER +M: Mario Limonciello +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/dell-smbios-wmi.c +F: tools/wmi/dell-smbios-example.c + DELL LAPTOP DRIVER M: Matthew Garrett M: Pali Rohár @@ -4051,12 +4083,17 @@ S: Maintained F: Documentation/dcdbas.txt F: drivers/firmware/dcdbas.* -DELL WMI EXTRAS DRIVER +DELL WMI NOTIFICATIONS DRIVER M: Matthew Garrett M: Pali Rohár S: Maintained F: drivers/platform/x86/dell-wmi.c +DELL WMI DESCRIPTOR DRIVER +M: Mario Limonciello +S: Maintained +F: drivers/platform/x86/dell-wmi-descriptor.c + DELTA ST MEDIA DRIVER M: Hugues Fruchet L: linux-media@vger.kernel.org @@ -4208,7 +4245,7 @@ L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-diolan-u2c.c -DIRECT ACCESS (DAX) +FILESYSTEM DIRECT ACCESS (DAX) M: Matthew Wilcox M: Ross Zwisler L: linux-fsdevel@vger.kernel.org @@ -4217,6 +4254,12 @@ F: fs/dax.c F: include/linux/dax.h F: include/trace/events/fs_dax.h +DEVICE DIRECT ACCESS (DAX) +M: Dan Williams +L: linux-nvdimm@lists.01.org +S: Supported +F: drivers/dax/ + DIRECTORY NOTIFICATION (DNOTIFY) M: Jan Kara R: Amir Goldstein @@ -5210,7 +5253,7 @@ F: drivers/video/fbdev/s1d13xxxfb.c F: include/video/s1d13xxxfb.h ERRSEQ ERROR TRACKING INFRASTRUCTURE -M: Jeff Layton +M: Jeff Layton S: Maintained F: lib/errseq.c F: include/linux/errseq.h @@ -5398,7 +5441,7 @@ F: include/scsi/libfcoe.h F: include/uapi/scsi/fc/ FILE LOCKING (flock() and fcntl()/lockf()) -M: Jeff Layton +M: Jeff Layton M: "J. Bruce Fields" L: linux-fsdevel@vger.kernel.org S: Maintained @@ -6841,6 +6884,7 @@ F: drivers/ipack/ INFINIBAND SUBSYSTEM M: Doug Ledford +M: Jason Gunthorpe L: linux-rdma@vger.kernel.org W: http://www.openfabrics.org/ Q: http://patchwork.kernel.org/project/linux-rdma/list/ @@ -7166,6 +7210,11 @@ F: Documentation/wimax/README.i2400m F: drivers/net/wimax/i2400m/ F: include/uapi/linux/wimax/i2400m.h +INTEL WMI THUNDERBOLT FORCE POWER DRIVER +M: Mario Limonciello +S: Maintained +F: drivers/platform/x86/intel-wmi-thunderbolt.c + INTEL(R) TRACE HUB M: Alexander Shishkin S: Supported @@ -7427,7 +7476,7 @@ JFS FILESYSTEM M: Dave Kleikamp L: jfs-discussion@lists.sourceforge.net W: http://jfs.sourceforge.net/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git +T: git git://github.com/kleikamp/linux-shaggy.git S: Maintained F: Documentation/filesystems/jfs.txt F: fs/jfs/ @@ -7543,7 +7592,7 @@ S: Odd Fixes KERNEL NFSD, SUNRPC, AND LOCKD SERVERS M: "J. Bruce Fields" -M: Jeff Layton +M: Jeff Layton L: linux-nfs@vger.kernel.org W: http://nfs.sourceforge.net/ T: git git://linux-nfs.org/~bfields/linux.git @@ -9680,12 +9729,11 @@ S: Supported F: drivers/ntb/hw/idt/ NTB INTEL DRIVER -M: Jon Mason M: Dave Jiang L: linux-ntb@googlegroups.com S: Supported -W: https://github.com/jonmason/ntb/wiki -T: git git://github.com/jonmason/ntb.git +W: https://github.com/davejiang/linux/wiki +T: git https://github.com/davejiang/linux.git F: drivers/ntb/hw/intel/ NTFS FILESYSTEM @@ -10397,6 +10445,8 @@ F: Documentation/switchtec.txt F: Documentation/ABI/testing/sysfs-class-switchtec F: drivers/pci/switch/switchtec* F: include/uapi/linux/switchtec_ioctl.h +F: include/linux/switchtec.h +F: drivers/ntb/hw/mscc/ PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support) M: Thomas Petazzoni @@ -10615,6 +10665,12 @@ S: Maintained F: crypto/pcrypt.c F: include/crypto/pcrypt.h +PEAQ WMI HOTKEYS DRIVER +M: Hans de Goede +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/peaq-wmi.c + PER-CPU MEMORY ALLOCATOR M: Tejun Heo M: Christoph Lameter @@ -11528,6 +11584,7 @@ F: include/linux/rpmsg/ RENESAS CLOCK DRIVERS M: Geert Uytterhoeven L: linux-renesas-soc@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git clk-renesas S: Supported F: drivers/clk/renesas/ diff --git a/Makefile b/Makefile index 763ab35df12a..efb942ad0b55 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,10 @@ NAME = Fearless Coyote # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. +# That's our default target when none is given on the command line +PHONY := _all +_all: + # o Do not use make's built-in rules and variables # (this increases performance and avoids hard-to-debug behaviour); # o Look for make include files relative to root of kernel src @@ -117,10 +121,6 @@ ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O) endif -# That's our default target when none is given on the command line -PHONY := _all -_all: - # Cancel implicit rules on top Makefile $(CURDIR)/Makefile Makefile: ; @@ -187,15 +187,6 @@ ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M) endif -# If building an external module we do not care about the all: rule -# but instead _all depend on modules -PHONY += all -ifeq ($(KBUILD_EXTMOD),) -_all: all -else -_all: modules -endif - ifeq ($(KBUILD_SRC),) # building in the source tree srctree := . @@ -207,6 +198,9 @@ else srctree := $(KBUILD_SRC) endif endif + +export KBUILD_CHECKSRC KBUILD_EXTMOD KBUILD_SRC + objtree := . src := $(srctree) obj := $(objtree) @@ -215,6 +209,74 @@ VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) export srctree objtree VPATH +# To make sure we do not include .config for any of the *config targets +# catch them early, and hand them over to scripts/kconfig/Makefile +# It is allowed to specify more targets when calling make, including +# mixing *config targets and build targets. +# For example 'make oldconfig all'. +# Detect when mixed targets is specified, and make a second invocation +# of make so .config is not included in this case either (for *config). + +version_h := include/generated/uapi/linux/version.h +old_version_h := include/linux/version.h + +no-dot-config-targets := clean mrproper distclean \ + cscope gtags TAGS tags help% %docs check% coccicheck \ + $(version_h) headers_% archheaders archscripts \ + kernelversion %src-pkg + +config-targets := 0 +mixed-targets := 0 +dot-config := 1 + +ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) + ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) + dot-config := 0 + endif +endif + +ifeq ($(KBUILD_EXTMOD),) + ifneq ($(filter config %config,$(MAKECMDGOALS)),) + config-targets := 1 + ifneq ($(words $(MAKECMDGOALS)),1) + mixed-targets := 1 + endif + endif +endif +# install and modules_install need also be processed one by one +ifneq ($(filter install,$(MAKECMDGOALS)),) + ifneq ($(filter modules_install,$(MAKECMDGOALS)),) + mixed-targets := 1 + endif +endif + +ifeq ($(mixed-targets),1) +# =========================================================================== +# We're called with mixed targets (*config and build targets). +# Handle them one by one. + +PHONY += $(MAKECMDGOALS) __build_one_by_one + +$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one + @: + +__build_one_by_one: + $(Q)set -e; \ + for i in $(MAKECMDGOALS); do \ + $(MAKE) -f $(srctree)/Makefile $$i; \ + done + +else + +# We need some generic definitions (do not try to remake the file). +scripts/Kbuild.include: ; +include scripts/Kbuild.include + +# Read KERNELRELEASE from include/config/kernel.release (if it exists) +KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) +KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION) +export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION + # SUBARCH tells the usermode build what the underlying arch is. That is set # first, and if a usermode build is happening, the "ARCH=um" on the command # line overrides the setting of ARCH below. If a native build is happening, @@ -285,9 +347,6 @@ ifeq ($(ARCH),tilegx) SRCARCH := tile endif -# Where to locate arch specific headers -hdr-arch := $(SRCARCH) - KCONFIG_CONFIG ?= .config export KCONFIG_CONFIG @@ -308,45 +367,6 @@ HOSTCXXFLAGS := -O2 $(HOST_LFS_CFLAGS) HOSTLDFLAGS := $(HOST_LFS_LDFLAGS) HOST_LOADLIBES := $(HOST_LFS_LIBS) -ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1) -HOSTCFLAGS += -Wno-unused-value -Wno-unused-parameter \ - -Wno-missing-field-initializers -fno-delete-null-pointer-checks -endif - -# Decide whether to build built-in, modular, or both. -# Normally, just do built-in. - -KBUILD_MODULES := -KBUILD_BUILTIN := 1 - -# If we have only "make modules", don't compile built-in objects. -# When we're building modules with modversions, we need to consider -# the built-in objects during the descend as well, in order to -# make sure the checksums are up to date before we record them. - -ifeq ($(MAKECMDGOALS),modules) - KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) -endif - -# If we have "make modules", compile modules -# in addition to whatever we do anyway. -# Just "make" or "make all" shall build modules as well - -ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) - KBUILD_MODULES := 1 -endif - -ifeq ($(MAKECMDGOALS),) - KBUILD_MODULES := 1 -endif - -export KBUILD_MODULES KBUILD_BUILTIN -export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD - -# We need some generic definitions (do not try to remake the file). -scripts/Kbuild.include: ; -include scripts/Kbuild.include - # Make variables (CC, etc...) AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld @@ -374,14 +394,11 @@ LDFLAGS_MODULE = CFLAGS_KERNEL = AFLAGS_KERNEL = LDFLAGS_vmlinux = -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disable-warning,maybe-uninitialized,) -CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) - # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE := \ - -I$(srctree)/arch/$(hdr-arch)/include/uapi \ - -I$(objtree)/arch/$(hdr-arch)/include/generated/uapi \ + -I$(srctree)/arch/$(SRCARCH)/include/uapi \ + -I$(objtree)/arch/$(SRCARCH)/include/generated/uapi \ -I$(srctree)/include/uapi \ -I$(objtree)/include/generated/uapi \ -include $(srctree)/include/linux/kconfig.h @@ -389,40 +406,33 @@ USERINCLUDE := \ # Use LINUXINCLUDE when you must reference the include/ directory. # Needed to be compatible with the O= option LINUXINCLUDE := \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -I$(objtree)/arch/$(hdr-arch)/include/generated \ + -I$(srctree)/arch/$(SRCARCH)/include \ + -I$(objtree)/arch/$(SRCARCH)/include/generated \ $(if $(KBUILD_SRC), -I$(srctree)/include) \ -I$(objtree)/include \ $(USERINCLUDE) -KBUILD_CPPFLAGS := -D__KERNEL__ - +KBUILD_AFLAGS := -D__ASSEMBLY__ KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -fno-strict-aliasing -fno-common -fshort-wchar \ -Werror-implicit-function-declaration \ -Wno-format-security \ - -std=gnu89 $(call cc-option,-fno-PIE) - - + -std=gnu89 +KBUILD_CPPFLAGS := -D__KERNEL__ KBUILD_AFLAGS_KERNEL := KBUILD_CFLAGS_KERNEL := -KBUILD_AFLAGS := -D__ASSEMBLY__ $(call cc-option,-fno-PIE) KBUILD_AFLAGS_MODULE := -DMODULE KBUILD_CFLAGS_MODULE := -DMODULE KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds +GCC_PLUGINS_CFLAGS := -# Read KERNELRELEASE from include/config/kernel.release (if it exists) -KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) -KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION) - -export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC export CPP AR NM STRIP OBJCOPY OBJDUMP HOSTLDFLAGS HOST_LOADLIBES export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS -export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KCOV CFLAGS_KASAN CFLAGS_UBSAN +export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_KASAN CFLAGS_UBSAN export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL @@ -464,73 +474,6 @@ ifneq ($(KBUILD_SRC),) $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) endif -# Support for using generic headers in asm-generic -PHONY += asm-generic uapi-asm-generic -asm-generic: uapi-asm-generic - $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \ - src=asm obj=arch/$(SRCARCH)/include/generated/asm -uapi-asm-generic: - $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \ - src=uapi/asm obj=arch/$(SRCARCH)/include/generated/uapi/asm - -# To make sure we do not include .config for any of the *config targets -# catch them early, and hand them over to scripts/kconfig/Makefile -# It is allowed to specify more targets when calling make, including -# mixing *config targets and build targets. -# For example 'make oldconfig all'. -# Detect when mixed targets is specified, and make a second invocation -# of make so .config is not included in this case either (for *config). - -version_h := include/generated/uapi/linux/version.h -old_version_h := include/linux/version.h - -no-dot-config-targets := clean mrproper distclean \ - cscope gtags TAGS tags help% %docs check% coccicheck \ - $(version_h) headers_% archheaders archscripts \ - kernelversion %src-pkg - -config-targets := 0 -mixed-targets := 0 -dot-config := 1 - -ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) - ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) - dot-config := 0 - endif -endif - -ifeq ($(KBUILD_EXTMOD),) - ifneq ($(filter config %config,$(MAKECMDGOALS)),) - config-targets := 1 - ifneq ($(words $(MAKECMDGOALS)),1) - mixed-targets := 1 - endif - endif -endif -# install and modules_install need also be processed one by one -ifneq ($(filter install,$(MAKECMDGOALS)),) - ifneq ($(filter modules_install,$(MAKECMDGOALS)),) - mixed-targets := 1 - endif -endif - -ifeq ($(mixed-targets),1) -# =========================================================================== -# We're called with mixed targets (*config and build targets). -# Handle them one by one. - -PHONY += $(MAKECMDGOALS) __build_one_by_one - -$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one - @: - -__build_one_by_one: - $(Q)set -e; \ - for i in $(MAKECMDGOALS); do \ - $(MAKE) -f $(srctree)/Makefile $$i; \ - done - -else ifeq ($(config-targets),1) # =========================================================================== # *config targets only - make sure prerequisites are updated, and descend @@ -553,6 +496,44 @@ else # Build targets only - this includes vmlinux, arch specific targets, clean # targets and others. In general all targets except *config targets. +# If building an external module we do not care about the all: rule +# but instead _all depend on modules +PHONY += all +ifeq ($(KBUILD_EXTMOD),) +_all: all +else +_all: modules +endif + +# Decide whether to build built-in, modular, or both. +# Normally, just do built-in. + +KBUILD_MODULES := +KBUILD_BUILTIN := 1 + +# If we have only "make modules", don't compile built-in objects. +# When we're building modules with modversions, we need to consider +# the built-in objects during the descend as well, in order to +# make sure the checksums are up to date before we record them. + +ifeq ($(MAKECMDGOALS),modules) + KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) +endif + +# If we have "make modules", compile modules +# in addition to whatever we do anyway. +# Just "make" or "make all" shall build modules as well + +ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) + KBUILD_MODULES := 1 +endif + +ifeq ($(MAKECMDGOALS),) + KBUILD_MODULES := 1 +endif + +export KBUILD_MODULES KBUILD_BUILTIN + ifeq ($(KBUILD_EXTMOD),) # Additional helpers built in scripts/ # Carefully list dependencies so we do not try to build scripts twice @@ -623,6 +604,11 @@ endif # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux +KBUILD_CFLAGS += $(call cc-option,-fno-PIE) +KBUILD_AFLAGS += $(call cc-option,-fno-PIE) +CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disable-warning,maybe-uninitialized,) +export CFLAGS_GCOV CFLAGS_KCOV + # The arch Makefile can set ARCH_{CPP,A,C}FLAGS to override the default # values of the respective KBUILD_* variables ARCH_CPPFLAGS := @@ -654,11 +640,12 @@ KBUILD_CFLAGS += $(call cc-ifversion, -lt, 0409, \ KBUILD_CFLAGS += $(call cc-option,--param=allow-store-data-races=0) # check for 'asm goto' -ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC) $(KBUILD_CFLAGS)), y) +ifeq ($(call shell-cached,$(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC) $(KBUILD_CFLAGS)), y) KBUILD_CFLAGS += -DCC_HAVE_ASM_GOTO KBUILD_AFLAGS += -DCC_HAVE_ASM_GOTO endif +include scripts/Makefile.kcov include scripts/Makefile.gcc-plugins ifdef CONFIG_READABLE_ASM @@ -790,7 +777,7 @@ KBUILD_CFLAGS += $(call cc-option,-fdata-sections,) endif # arch Makefile may override CC so keep this after arch Makefile is included -NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) +NOSTDINC_FLAGS += -nostdinc -isystem $(call shell-cached,$(CC) -print-file-name=include) CHECKFLAGS += $(NOSTDINC_FLAGS) # warn about C99 declaration after statement @@ -1072,6 +1059,15 @@ prepare0: archprepare gcc-plugins # All the preparing.. prepare: prepare0 prepare-objtool +# Support for using generic headers in asm-generic +PHONY += asm-generic uapi-asm-generic +asm-generic: uapi-asm-generic + $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \ + src=asm obj=arch/$(SRCARCH)/include/generated/asm +uapi-asm-generic: + $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \ + src=uapi/asm obj=arch/$(SRCARCH)/include/generated/uapi/asm + PHONY += prepare-objtool prepare-objtool: $(objtool_target) @@ -1140,8 +1136,8 @@ headerdep: #Default location for installed headers export INSTALL_HDR_PATH = $(objtree)/usr -# If we do an all arch process set dst to include/arch-$(hdr-arch) -hdr-dst = $(if $(KBUILD_HEADERS), dst=include/arch-$(hdr-arch), dst=include) +# If we do an all arch process set dst to include/arch-$(SRCARCH) +hdr-dst = $(if $(KBUILD_HEADERS), dst=include/arch-$(SRCARCH), dst=include) PHONY += archheaders archheaders: @@ -1159,10 +1155,10 @@ headers_install_all: PHONY += headers_install headers_install: __headers - $(if $(wildcard $(srctree)/arch/$(hdr-arch)/include/uapi/asm/Kbuild),, \ + $(if $(wildcard $(srctree)/arch/$(SRCARCH)/include/uapi/asm/Kbuild),, \ $(error Headers not exportable for the $(SRCARCH) architecture)) $(Q)$(MAKE) $(hdr-inst)=include/uapi dst=include - $(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi $(hdr-dst) + $(Q)$(MAKE) $(hdr-inst)=arch/$(SRCARCH)/include/uapi $(hdr-dst) PHONY += headers_check_all headers_check_all: headers_install_all @@ -1171,7 +1167,7 @@ headers_check_all: headers_install_all PHONY += headers_check headers_check: headers_install $(Q)$(MAKE) $(hdr-inst)=include/uapi dst=include HDRCHECK=1 - $(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi $(hdr-dst) HDRCHECK=1 + $(Q)$(MAKE) $(hdr-inst)=arch/$(SRCARCH)/include/uapi $(hdr-dst) HDRCHECK=1 # --------------------------------------------------------------------------- # Kernel selftest @@ -1284,7 +1280,7 @@ CLEAN_DIRS += $(MODVERDIR) # Directories & files removed with 'make mrproper' MRPROPER_DIRS += include/config usr/include include/generated \ arch/*/include/generated .tmp_objdiff -MRPROPER_FILES += .config .config.old .version .old_version \ +MRPROPER_FILES += .config .config.old .version \ Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \ signing_key.pem signing_key.priv signing_key.x509 \ x509.genkey extra_certificates signing_key.x509.keyid \ @@ -1394,7 +1390,7 @@ help: @echo ' export_report - List the usages of all exported symbols' @echo ' headers_check - Sanity check on exported headers' @echo ' headerdep - Detect inclusion cycles in headers' - @$(MAKE) -f $(srctree)/scripts/Makefile.help checker-help + @echo ' coccicheck - Check with Coccinelle' @echo '' @echo 'Kernel selftest:' @echo ' kselftest - Build and run kernel selftest (run as root)' @@ -1557,6 +1553,7 @@ clean: $(clean-dirs) -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ -o -name '*.symtypes' -o -name 'modules.order' \ -o -name modules.builtin -o -name '.tmp_*.o.*' \ + -o -name .cache.mk \ -o -name '*.c.[012]*.*' \ -o -name '*.ll' \ -o -name '*.gcno' \) -type f -print | xargs rm -f @@ -1703,8 +1700,7 @@ cmd_crmodverdir = $(Q)mkdir -p $(MODVERDIR) \ # read all saved command lines -targets := $(wildcard $(sort $(targets))) -cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) +cmd_files := $(wildcard .*.cmd $(foreach f,$(sort $(targets)),$(dir $(f)).$(notdir $(f)).cmd)) ifneq ($(cmd_files),) $(cmd_files): ; # Do not try to update included dependency files diff --git a/arch/alpha/include/uapi/asm/mman.h b/arch/alpha/include/uapi/asm/mman.h index 6bf730063e3f..2dbdf59258d9 100644 --- a/arch/alpha/include/uapi/asm/mman.h +++ b/arch/alpha/include/uapi/asm/mman.h @@ -12,6 +12,7 @@ #define MAP_SHARED 0x01 /* Share changes */ #define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */ #define MAP_TYPE 0x0f /* Mask for type of mapping (OSF/1 is _wrong_) */ #define MAP_FIXED 0x100 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x10 /* don't use a file */ diff --git a/arch/arm/Makefile b/arch/arm/Makefile index def8824fc71c..80351e505fd5 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -16,11 +16,11 @@ LDFLAGS := LDFLAGS_vmlinux :=-p --no-undefined -X --pic-veneer ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) LDFLAGS_vmlinux += --be8 -LDFLAGS_MODULE += --be8 +KBUILD_LDFLAGS_MODULE += --be8 endif ifeq ($(CONFIG_ARM_MODULE_PLTS),y) -LDFLAGS_MODULE += -T $(srctree)/arch/arm/kernel/module.lds +KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/arm/kernel/module.lds endif GZFLAGS :=-9 @@ -122,7 +122,7 @@ CFLAGS_ISA :=-mthumb $(AFLAGS_AUTOIT) $(AFLAGS_NOWARN) AFLAGS_ISA :=$(CFLAGS_ISA) -Wa$(comma)-mthumb # Work around buggy relocation from gas if requested: ifeq ($(CONFIG_THUMB2_AVOID_R_ARM_THM_JUMP11),y) -CFLAGS_MODULE +=-fno-optimize-sibling-calls +KBUILD_CFLAGS_MODULE +=-fno-optimize-sibling-calls endif else CFLAGS_ISA :=$(call cc-option,-marm,) diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig index f24628db5409..e2bd35b6780c 100644 --- a/arch/arm/kvm/Kconfig +++ b/arch/arm/kvm/Kconfig @@ -4,6 +4,7 @@ # source "virt/kvm/Kconfig" +source "virt/lib/Kconfig" menuconfig VIRTUALIZATION bool "Virtualization" @@ -23,6 +24,8 @@ config KVM select PREEMPT_NOTIFIERS select ANON_INODES select ARM_GIC + select ARM_GIC_V3 + select ARM_GIC_V3_ITS select HAVE_KVM_CPU_RELAX_INTERCEPT select HAVE_KVM_ARCH_TLB_FLUSH_ALL select KVM_MMIO @@ -36,6 +39,8 @@ config KVM select HAVE_KVM_IRQCHIP select HAVE_KVM_IRQ_ROUTING select HAVE_KVM_MSI + select IRQ_BYPASS_MANAGER + select HAVE_KVM_IRQ_BYPASS depends on ARM_VIRT_EXT && ARM_LPAE && ARM_ARCH_TIMER ---help--- Support hosting virtualized guest machines. diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index f550abd64a25..48de846f2246 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile @@ -32,6 +32,7 @@ obj-y += $(KVM)/arm/vgic/vgic-init.o obj-y += $(KVM)/arm/vgic/vgic-irqfd.o obj-y += $(KVM)/arm/vgic/vgic-v2.o obj-y += $(KVM)/arm/vgic/vgic-v3.o +obj-y += $(KVM)/arm/vgic/vgic-v4.o obj-y += $(KVM)/arm/vgic/vgic-mmio.o obj-y += $(KVM)/arm/vgic/vgic-mmio-v2.o obj-y += $(KVM)/arm/vgic/vgic-mmio-v3.o diff --git a/arch/arm/mach-pxa/cm-x255.c b/arch/arm/mach-pxa/cm-x255.c index b592f79a1742..fa8e7dd4d898 100644 --- a/arch/arm/mach-pxa/cm-x255.c +++ b/arch/arm/mach-pxa/cm-x255.c @@ -14,7 +14,7 @@ #include #include #include - +#include #include #include @@ -176,6 +176,17 @@ static inline void cmx255_init_nor(void) {} #endif #if defined(CONFIG_MTD_NAND_GPIO) || defined(CONFIG_MTD_NAND_GPIO_MODULE) + +static struct gpiod_lookup_table cmx255_nand_gpiod_table = { + .dev_id = "gpio-nand", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NAND_CS, "nce", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("gpio-pxa", GPIO_NAND_CLE, "cle", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("gpio-pxa", GPIO_NAND_ALE, "ale", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("gpio-pxa", GPIO_NAND_RB, "rdy", GPIO_ACTIVE_HIGH), + }, +}; + static struct resource cmx255_nand_resource[] = { [0] = { .start = PXA_CS1_PHYS, @@ -198,11 +209,6 @@ static struct mtd_partition cmx255_nand_parts[] = { }; static struct gpio_nand_platdata cmx255_nand_platdata = { - .gpio_nce = GPIO_NAND_CS, - .gpio_cle = GPIO_NAND_CLE, - .gpio_ale = GPIO_NAND_ALE, - .gpio_rdy = GPIO_NAND_RB, - .gpio_nwp = -1, .parts = cmx255_nand_parts, .num_parts = ARRAY_SIZE(cmx255_nand_parts), .chip_delay = 25, @@ -220,6 +226,7 @@ static struct platform_device cmx255_nand = { static void __init cmx255_init_nand(void) { + gpiod_add_lookup_table(&cmx255_nand_gpiod_table); platform_device_register(&cmx255_nand); } #else diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index e39d487bf724..a3c7f271ad4c 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -215,7 +215,6 @@ typedef struct compat_siginfo { } compat_siginfo_t; #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL /* * A pointer passed in from user mode. This should not diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig index 13f81f971390..2257dfcc44cc 100644 --- a/arch/arm64/kvm/Kconfig +++ b/arch/arm64/kvm/Kconfig @@ -4,6 +4,7 @@ # source "virt/kvm/Kconfig" +source "virt/lib/Kconfig" menuconfig VIRTUALIZATION bool "Virtualization" @@ -36,6 +37,8 @@ config KVM select HAVE_KVM_MSI select HAVE_KVM_IRQCHIP select HAVE_KVM_IRQ_ROUTING + select IRQ_BYPASS_MANAGER + select HAVE_KVM_IRQ_BYPASS ---help--- Support hosting virtualized guest machines. We don't support KVM with 16K page tables yet, due to the multiple diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 861acbbac385..87c4f7ae24de 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -27,6 +27,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-init.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-irqfd.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-v2.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-v3.o +kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-v4.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v2.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v3.o diff --git a/arch/c6x/Makefile b/arch/c6x/Makefile index 6b0be670ddfa..6f6096ff05a4 100644 --- a/arch/c6x/Makefile +++ b/arch/c6x/Makefile @@ -12,7 +12,7 @@ cflags-y += -mno-dsbt -msdata=none -D__linux__ cflags-$(CONFIG_C6X_BIG_KERNEL) += -mlong-calls -CFLAGS_MODULE += -mlong-calls -mno-dsbt -msdata=none +KBUILD_CFLAGS_MODULE += -mlong-calls -mno-dsbt -msdata=none CHECKFLAGS += diff --git a/arch/frv/kernel/.gitignore b/arch/frv/kernel/.gitignore new file mode 100644 index 000000000000..c5f676c3c224 --- /dev/null +++ b/arch/frv/kernel/.gitignore @@ -0,0 +1 @@ +vmlinux.lds diff --git a/arch/hexagon/Makefile b/arch/hexagon/Makefile index 48fe08230a80..2efaa18e995a 100644 --- a/arch/hexagon/Makefile +++ b/arch/hexagon/Makefile @@ -12,9 +12,9 @@ KBUILD_CFLAGS += -fno-short-enums # Modules must use either long-calls, or use pic/plt. # Use long-calls for now, it's easier. And faster. -# CFLAGS_MODULE += -fPIC -# LDFLAGS_MODULE += -shared -CFLAGS_MODULE += -mlong-calls +# KBUILD_CFLAGS_MODULE += -fPIC +# KBUILD_LDFLAGS_MODULE += -shared +KBUILD_CFLAGS_MODULE += -mlong-calls cflags-y += $(call cc-option,-mv${CONFIG_HEXAGON_ARCH_VERSION}) aflags-y += $(call cc-option,-mv${CONFIG_HEXAGON_ARCH_VERSION}) diff --git a/arch/hexagon/kernel/ptrace.c b/arch/hexagon/kernel/ptrace.c index ecd75e2e8eb3..fa76493c1745 100644 --- a/arch/hexagon/kernel/ptrace.c +++ b/arch/hexagon/kernel/ptrace.c @@ -18,8 +18,6 @@ * 02110-1301, USA. */ -#include - #include #include #include @@ -180,7 +178,7 @@ static const struct user_regset hexagon_regsets[] = { }; static const struct user_regset_view hexagon_user_view = { - .name = UTS_MACHINE, + .name = "hexagon", .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI, .regsets = hexagon_regsets, diff --git a/arch/ia64/include/asm/topology.h b/arch/ia64/include/asm/topology.h index 3ad8f6988363..82f9bf702804 100644 --- a/arch/ia64/include/asm/topology.h +++ b/arch/ia64/include/asm/topology.h @@ -33,13 +33,6 @@ cpu_all_mask : \ &node_to_cpu_mask[node]) -/* - * Returns the number of the node containing Node 'nid'. - * Not implemented here. Multi-level hierarchies detected with - * the help of node_distance(). - */ -#define parent_node(nid) (nid) - /* * Determines the node for a given pci bus */ diff --git a/arch/ia64/kernel/asm-offsets.c b/arch/ia64/kernel/asm-offsets.c index f7693f49c573..f4db2168d1b8 100644 --- a/arch/ia64/kernel/asm-offsets.c +++ b/arch/ia64/kernel/asm-offsets.c @@ -31,8 +31,8 @@ void foo(void) DEFINE(SIGFRAME_SIZE, sizeof (struct sigframe)); DEFINE(UNW_FRAME_INFO_SIZE, sizeof (struct unw_frame_info)); - BUILD_BUG_ON(sizeof(struct upid) != 32); - DEFINE(IA64_UPID_SHIFT, 5); + BUILD_BUG_ON(sizeof(struct upid) != 16); + DEFINE(IA64_UPID_SHIFT, 4); BLANK(); diff --git a/arch/mips/include/asm/compat-signal.h b/arch/mips/include/asm/compat-signal.h index e87cd243b0f4..c3b7a2550d1d 100644 --- a/arch/mips/include/asm/compat-signal.h +++ b/arch/mips/include/asm/compat-signal.h @@ -14,45 +14,16 @@ static inline int __copy_conv_sigset_to_user(compat_sigset_t __user *d, const sigset_t *s) { - int err; + BUILD_BUG_ON(sizeof(*d) != sizeof(*s)); + BUILD_BUG_ON(_NSIG_WORDS != 2); - BUG_ON(sizeof(*d) != sizeof(*s)); - BUG_ON(_NSIG_WORDS != 2); - - err = __put_user(s->sig[0], &d->sig[0]); - err |= __put_user(s->sig[0] >> 32, &d->sig[1]); - err |= __put_user(s->sig[1], &d->sig[2]); - err |= __put_user(s->sig[1] >> 32, &d->sig[3]); - - return err; + return put_compat_sigset(d, s, sizeof(*d)); } static inline int __copy_conv_sigset_from_user(sigset_t *d, const compat_sigset_t __user *s) { - int err; - union sigset_u { - sigset_t s; - compat_sigset_t c; - } *u = (union sigset_u *) d; - - BUG_ON(sizeof(*d) != sizeof(*s)); - BUG_ON(_NSIG_WORDS != 2); - -#ifdef CONFIG_CPU_BIG_ENDIAN - err = __get_user(u->c.sig[1], &s->sig[0]); - err |= __get_user(u->c.sig[0], &s->sig[1]); - err |= __get_user(u->c.sig[3], &s->sig[2]); - err |= __get_user(u->c.sig[2], &s->sig[3]); -#endif -#ifdef CONFIG_CPU_LITTLE_ENDIAN - err = __get_user(u->c.sig[0], &s->sig[0]); - err |= __get_user(u->c.sig[1], &s->sig[1]); - err |= __get_user(u->c.sig[2], &s->sig[2]); - err |= __get_user(u->c.sig[3], &s->sig[3]); -#endif - - return err; + return get_compat_sigset(d, s); } #endif /* __ASM_COMPAT_SIGNAL_H */ diff --git a/arch/mips/include/asm/compat.h b/arch/mips/include/asm/compat.h index 8e2b5b556488..49691331ada4 100644 --- a/arch/mips/include/asm/compat.h +++ b/arch/mips/include/asm/compat.h @@ -200,7 +200,6 @@ typedef struct compat_siginfo { } compat_siginfo_t; #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL /* * A pointer passed in from user mode. This should not diff --git a/arch/mips/include/uapi/asm/mman.h b/arch/mips/include/uapi/asm/mman.h index 20c3df7a8fdd..606e02ca4b6c 100644 --- a/arch/mips/include/uapi/asm/mman.h +++ b/arch/mips/include/uapi/asm/mman.h @@ -29,6 +29,7 @@ */ #define MAP_SHARED 0x001 /* Share changes */ #define MAP_PRIVATE 0x002 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x003 /* share + validate extension flags */ #define MAP_TYPE 0x00f /* Mask for type of mapping */ #define MAP_FIXED 0x010 /* Interpret addr exactly */ diff --git a/arch/mn10300/mm/fault.c b/arch/mn10300/mm/fault.c index f23781d6bbb3..f0bfa1448744 100644 --- a/arch/mn10300/mm/fault.c +++ b/arch/mn10300/mm/fault.c @@ -60,7 +60,7 @@ void bust_spinlocks(int yes) void do_BUG(const char *file, int line) { bust_spinlocks(1); - printk(KERN_EMERG "------------[ cut here ]------------\n"); + printk(KERN_EMERG CUT_HERE); printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); } diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 1fd3eb5b66c6..9792d8cf4f56 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -32,6 +32,7 @@ config PARISC select GENERIC_PCI_IOMAP select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_SMP_IDLE_THREAD + select GENERIC_CPU_DEVICES select GENERIC_STRNCPY_FROM_USER select SYSCTL_ARCH_UNALIGN_ALLOW select SYSCTL_EXCEPTION_TRACE @@ -60,9 +61,6 @@ config PARISC config CPU_BIG_ENDIAN def_bool y -config CPU_BIG_ENDIAN - def_bool y - config MMU def_bool y @@ -288,6 +286,21 @@ config SMP If you don't know what to do here, say N. +config PARISC_CPU_TOPOLOGY + bool "Support cpu topology definition" + depends on SMP + default y + help + Support PARISC cpu topology definition. + +config SCHED_MC + bool "Multi-core scheduler support" + depends on PARISC_CPU_TOPOLOGY && PA8X00 + help + Multi-core scheduler support improves the CPU scheduler's decision + making when dealing with multi-core CPU chips at a cost of slightly + increased overhead in some places. If unsure say N here. + config IRQSTACKS bool "Use separate kernel stacks when processing interrupts" default y diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile index 01946ebaff72..e2364ff59180 100644 --- a/arch/parisc/Makefile +++ b/arch/parisc/Makefile @@ -22,7 +22,7 @@ KBUILD_IMAGE := vmlinuz KBUILD_DEFCONFIG := default_defconfig NM = sh $(srctree)/arch/parisc/nm -CHECKFLAGS += -D__hppa__=1 +CHECKFLAGS += -D__hppa__=1 -mbig-endian LIBGCC = $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name) export LIBGCC diff --git a/arch/parisc/include/asm/compat.h b/arch/parisc/include/asm/compat.h index 07f48827afda..acf8aa07cbe0 100644 --- a/arch/parisc/include/asm/compat.h +++ b/arch/parisc/include/asm/compat.h @@ -195,7 +195,6 @@ typedef struct compat_siginfo { } compat_siginfo_t; #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL struct compat_ipc64_perm { compat_key_t key; diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h index efee44a5e063..339e83ddb39e 100644 --- a/arch/parisc/include/asm/pdc.h +++ b/arch/parisc/include/asm/pdc.h @@ -18,261 +18,6 @@ extern unsigned long parisc_cell_loc; /* cell location of CPU (PAT) */ #define PDC_TYPE_SYSTEM_MAP 1 /* 32-bit, but supports PDC_SYSTEM_MAP */ #define PDC_TYPE_SNAKE 2 /* Doesn't support SYSTEM_MAP */ -struct pdc_chassis_info { /* for PDC_CHASSIS_INFO */ - unsigned long actcnt; /* actual number of bytes returned */ - unsigned long maxcnt; /* maximum number of bytes that could be returned */ -}; - -struct pdc_coproc_cfg { /* for PDC_COPROC_CFG */ - unsigned long ccr_functional; - unsigned long ccr_present; - unsigned long revision; - unsigned long model; -}; - -struct pdc_model { /* for PDC_MODEL */ - unsigned long hversion; - unsigned long sversion; - unsigned long hw_id; - unsigned long boot_id; - unsigned long sw_id; - unsigned long sw_cap; - unsigned long arch_rev; - unsigned long pot_key; - unsigned long curr_key; -}; - -struct pdc_cache_cf { /* for PDC_CACHE (I/D-caches) */ - unsigned long -#ifdef CONFIG_64BIT - cc_padW:32, -#endif - cc_alias: 4, /* alias boundaries for virtual addresses */ - cc_block: 4, /* to determine most efficient stride */ - cc_line : 3, /* maximum amount written back as a result of store (multiple of 16 bytes) */ - cc_shift: 2, /* how much to shift cc_block left */ - cc_wt : 1, /* 0 = WT-Dcache, 1 = WB-Dcache */ - cc_sh : 2, /* 0 = separate I/D-cache, else shared I/D-cache */ - cc_cst : 3, /* 0 = incoherent D-cache, 1=coherent D-cache */ - cc_pad1 : 10, /* reserved */ - cc_hv : 3; /* hversion dependent */ -}; - -struct pdc_tlb_cf { /* for PDC_CACHE (I/D-TLB's) */ - unsigned long tc_pad0:12, /* reserved */ -#ifdef CONFIG_64BIT - tc_padW:32, -#endif - tc_sh : 2, /* 0 = separate I/D-TLB, else shared I/D-TLB */ - tc_hv : 1, /* HV */ - tc_page : 1, /* 0 = 2K page-size-machine, 1 = 4k page size */ - tc_cst : 3, /* 0 = incoherent operations, else coherent operations */ - tc_aid : 5, /* ITLB: width of access ids of processor (encoded!) */ - tc_sr : 8; /* ITLB: width of space-registers (encoded) */ -}; - -struct pdc_cache_info { /* main-PDC_CACHE-structure (caches & TLB's) */ - /* I-cache */ - unsigned long ic_size; /* size in bytes */ - struct pdc_cache_cf ic_conf; /* configuration */ - unsigned long ic_base; /* base-addr */ - unsigned long ic_stride; - unsigned long ic_count; - unsigned long ic_loop; - /* D-cache */ - unsigned long dc_size; /* size in bytes */ - struct pdc_cache_cf dc_conf; /* configuration */ - unsigned long dc_base; /* base-addr */ - unsigned long dc_stride; - unsigned long dc_count; - unsigned long dc_loop; - /* Instruction-TLB */ - unsigned long it_size; /* number of entries in I-TLB */ - struct pdc_tlb_cf it_conf; /* I-TLB-configuration */ - unsigned long it_sp_base; - unsigned long it_sp_stride; - unsigned long it_sp_count; - unsigned long it_off_base; - unsigned long it_off_stride; - unsigned long it_off_count; - unsigned long it_loop; - /* data-TLB */ - unsigned long dt_size; /* number of entries in D-TLB */ - struct pdc_tlb_cf dt_conf; /* D-TLB-configuration */ - unsigned long dt_sp_base; - unsigned long dt_sp_stride; - unsigned long dt_sp_count; - unsigned long dt_off_base; - unsigned long dt_off_stride; - unsigned long dt_off_count; - unsigned long dt_loop; -}; - -#if 0 -/* If you start using the next struct, you'll have to adjust it to - * work with 64-bit firmware I think -PB - */ -struct pdc_iodc { /* PDC_IODC */ - unsigned char hversion_model; - unsigned char hversion; - unsigned char spa; - unsigned char type; - unsigned int sversion_rev:4; - unsigned int sversion_model:19; - unsigned int sversion_opt:8; - unsigned char rev; - unsigned char dep; - unsigned char features; - unsigned char pad1; - unsigned int checksum:16; - unsigned int length:16; - unsigned int pad[15]; -} __attribute__((aligned(8))) ; -#endif - -#ifndef CONFIG_PA20 -/* no BLTBs in pa2.0 processors */ -struct pdc_btlb_info_range { - __u8 res00; - __u8 num_i; - __u8 num_d; - __u8 num_comb; -}; - -struct pdc_btlb_info { /* PDC_BLOCK_TLB, return of PDC_BTLB_INFO */ - unsigned int min_size; /* minimum size of BTLB in pages */ - unsigned int max_size; /* maximum size of BTLB in pages */ - struct pdc_btlb_info_range fixed_range_info; - struct pdc_btlb_info_range variable_range_info; -}; - -#endif /* !CONFIG_PA20 */ - -struct pdc_mem_retinfo { /* PDC_MEM/PDC_MEM_MEMINFO (return info) */ - unsigned long pdt_size; - unsigned long pdt_entries; - unsigned long pdt_status; - unsigned long first_dbe_loc; - unsigned long good_mem; -}; - -struct pdc_mem_read_pdt { /* PDC_MEM/PDC_MEM_READ_PDT (return info) */ - unsigned long pdt_entries; -}; - -#ifdef CONFIG_64BIT -struct pdc_memory_table_raddr { /* PDC_MEM/PDC_MEM_TABLE (return info) */ - unsigned long entries_returned; - unsigned long entries_total; -}; - -struct pdc_memory_table { /* PDC_MEM/PDC_MEM_TABLE (arguments) */ - unsigned long paddr; - unsigned int pages; - unsigned int reserved; -}; -#endif /* CONFIG_64BIT */ - -struct pdc_system_map_mod_info { /* PDC_SYSTEM_MAP/FIND_MODULE */ - unsigned long mod_addr; - unsigned long mod_pgs; - unsigned long add_addrs; -}; - -struct pdc_system_map_addr_info { /* PDC_SYSTEM_MAP/FIND_ADDRESS */ - unsigned long mod_addr; - unsigned long mod_pgs; -}; - -struct pdc_initiator { /* PDC_INITIATOR */ - int host_id; - int factor; - int width; - int mode; -}; - -struct hardware_path { - char flags; /* see bit definitions below */ - char bc[6]; /* Bus Converter routing info to a specific */ - /* I/O adaptor (< 0 means none, > 63 resvd) */ - char mod; /* fixed field of specified module */ -}; - -/* - * Device path specifications used by PDC. - */ -struct pdc_module_path { - struct hardware_path path; - unsigned int layers[6]; /* device-specific info (ctlr #, unit # ...) */ -}; - -#ifndef CONFIG_PA20 -/* Only used on some pre-PA2.0 boxes */ -struct pdc_memory_map { /* PDC_MEMORY_MAP */ - unsigned long hpa; /* mod's register set address */ - unsigned long more_pgs; /* number of additional I/O pgs */ -}; -#endif - -struct pdc_tod { - unsigned long tod_sec; - unsigned long tod_usec; -}; - -/* architected results from PDC_PIM/transfer hpmc on a PA1.1 machine */ - -struct pdc_hpmc_pim_11 { /* PDC_PIM */ - __u32 gr[32]; - __u32 cr[32]; - __u32 sr[8]; - __u32 iasq_back; - __u32 iaoq_back; - __u32 check_type; - __u32 cpu_state; - __u32 rsvd1; - __u32 cache_check; - __u32 tlb_check; - __u32 bus_check; - __u32 assists_check; - __u32 rsvd2; - __u32 assist_state; - __u32 responder_addr; - __u32 requestor_addr; - __u32 path_info; - __u64 fr[32]; -}; - -/* - * architected results from PDC_PIM/transfer hpmc on a PA2.0 machine - * - * Note that PDC_PIM doesn't care whether or not wide mode was enabled - * so the results are different on PA1.1 vs. PA2.0 when in narrow mode. - * - * Note also that there are unarchitected results available, which - * are hversion dependent. Do a "ser pim 0 hpmc" after rebooting, since - * the firmware is probably the best way of printing hversion dependent - * data. - */ - -struct pdc_hpmc_pim_20 { /* PDC_PIM */ - __u64 gr[32]; - __u64 cr[32]; - __u64 sr[8]; - __u64 iasq_back; - __u64 iaoq_back; - __u32 check_type; - __u32 cpu_state; - __u32 cache_check; - __u32 tlb_check; - __u32 bus_check; - __u32 assists_check; - __u32 assist_state; - __u32 path_info; - __u64 responder_addr; - __u64 requestor_addr; - __u64 fr[32]; -}; - void pdc_console_init(void); /* in pdc_console.c */ void pdc_console_restart(void); diff --git a/arch/parisc/include/asm/topology.h b/arch/parisc/include/asm/topology.h new file mode 100644 index 000000000000..6f0750c74e47 --- /dev/null +++ b/arch/parisc/include/asm/topology.h @@ -0,0 +1,36 @@ +#ifndef _ASM_PARISC_TOPOLOGY_H +#define _ASM_PARISC_TOPOLOGY_H + +#ifdef CONFIG_PARISC_CPU_TOPOLOGY + +#include + +struct cputopo_parisc { + int thread_id; + int core_id; + int socket_id; + cpumask_t thread_sibling; + cpumask_t core_sibling; +}; + +extern struct cputopo_parisc cpu_topology[NR_CPUS]; + +#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id) +#define topology_core_id(cpu) (cpu_topology[cpu].core_id) +#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling) +#define topology_sibling_cpumask(cpu) (&cpu_topology[cpu].thread_sibling) + +void init_cpu_topology(void); +void store_cpu_topology(unsigned int cpuid); +const struct cpumask *cpu_coregroup_mask(int cpu); + +#else + +static inline void init_cpu_topology(void) { } +static inline void store_cpu_topology(unsigned int cpuid) { } + +#endif + +#include + +#endif /* _ASM_ARM_TOPOLOGY_H */ diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h index d1af0d74a188..80510ba44c08 100644 --- a/arch/parisc/include/uapi/asm/mman.h +++ b/arch/parisc/include/uapi/asm/mman.h @@ -12,6 +12,7 @@ #define MAP_SHARED 0x01 /* Share changes */ #define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */ #define MAP_TYPE 0x03 /* Mask for type of mapping */ #define MAP_FIXED 0x04 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x10 /* don't use a file */ diff --git a/arch/parisc/include/uapi/asm/pdc.h b/arch/parisc/include/uapi/asm/pdc.h index 0ad117617f1a..593eeb573138 100644 --- a/arch/parisc/include/uapi/asm/pdc.h +++ b/arch/parisc/include/uapi/asm/pdc.h @@ -16,6 +16,7 @@ #define PDC_ERROR -3 /* Call could not complete without an error */ #define PDC_NE_MOD -5 /* Module not found */ #define PDC_NE_CELL_MOD -7 /* Cell module not found */ +#define PDC_NE_BOOTDEV -9 /* Cannot locate a console device or boot device */ #define PDC_INVALID_ARG -10 /* Called with an invalid argument */ #define PDC_BUS_POW_WARN -12 /* Call could not complete in allowed power budget */ #define PDC_NOT_NARROW -17 /* Narrow mode not supported */ @@ -340,9 +341,6 @@ #if !defined(__ASSEMBLY__) -#include - - /* flags of the device_path */ #define PF_AUTOBOOT 0x80 #define PF_AUTOSEARCH 0x40 @@ -418,9 +416,255 @@ struct zeropage { int pad430[116]; /* [0x600] processor dependent */ - __u32 pad600[1]; - __u32 proc_sti; /* pointer to STI ROM */ - __u32 pad608[126]; + unsigned int pad600[1]; + unsigned int proc_sti; /* pointer to STI ROM */ + unsigned int pad608[126]; +}; + +struct pdc_chassis_info { /* for PDC_CHASSIS_INFO */ + unsigned long actcnt; /* actual number of bytes returned */ + unsigned long maxcnt; /* maximum number of bytes that could be returned */ +}; + +struct pdc_coproc_cfg { /* for PDC_COPROC_CFG */ + unsigned long ccr_functional; + unsigned long ccr_present; + unsigned long revision; + unsigned long model; +}; + +struct pdc_model { /* for PDC_MODEL */ + unsigned long hversion; + unsigned long sversion; + unsigned long hw_id; + unsigned long boot_id; + unsigned long sw_id; + unsigned long sw_cap; + unsigned long arch_rev; + unsigned long pot_key; + unsigned long curr_key; +}; + +struct pdc_cache_cf { /* for PDC_CACHE (I/D-caches) */ + unsigned long +#ifdef __LP64__ + cc_padW:32, +#endif + cc_alias: 4, /* alias boundaries for virtual addresses */ + cc_block: 4, /* to determine most efficient stride */ + cc_line : 3, /* maximum amount written back as a result of store (multiple of 16 bytes) */ + cc_shift: 2, /* how much to shift cc_block left */ + cc_wt : 1, /* 0 = WT-Dcache, 1 = WB-Dcache */ + cc_sh : 2, /* 0 = separate I/D-cache, else shared I/D-cache */ + cc_cst : 3, /* 0 = incoherent D-cache, 1=coherent D-cache */ + cc_pad1 : 10, /* reserved */ + cc_hv : 3; /* hversion dependent */ +}; + +struct pdc_tlb_cf { /* for PDC_CACHE (I/D-TLB's) */ + unsigned long tc_pad0:12, /* reserved */ +#ifdef __LP64__ + tc_padW:32, +#endif + tc_sh : 2, /* 0 = separate I/D-TLB, else shared I/D-TLB */ + tc_hv : 1, /* HV */ + tc_page : 1, /* 0 = 2K page-size-machine, 1 = 4k page size */ + tc_cst : 3, /* 0 = incoherent operations, else coherent operations */ + tc_aid : 5, /* ITLB: width of access ids of processor (encoded!) */ + tc_sr : 8; /* ITLB: width of space-registers (encoded) */ +}; + +struct pdc_cache_info { /* main-PDC_CACHE-structure (caches & TLB's) */ + /* I-cache */ + unsigned long ic_size; /* size in bytes */ + struct pdc_cache_cf ic_conf; /* configuration */ + unsigned long ic_base; /* base-addr */ + unsigned long ic_stride; + unsigned long ic_count; + unsigned long ic_loop; + /* D-cache */ + unsigned long dc_size; /* size in bytes */ + struct pdc_cache_cf dc_conf; /* configuration */ + unsigned long dc_base; /* base-addr */ + unsigned long dc_stride; + unsigned long dc_count; + unsigned long dc_loop; + /* Instruction-TLB */ + unsigned long it_size; /* number of entries in I-TLB */ + struct pdc_tlb_cf it_conf; /* I-TLB-configuration */ + unsigned long it_sp_base; + unsigned long it_sp_stride; + unsigned long it_sp_count; + unsigned long it_off_base; + unsigned long it_off_stride; + unsigned long it_off_count; + unsigned long it_loop; + /* data-TLB */ + unsigned long dt_size; /* number of entries in D-TLB */ + struct pdc_tlb_cf dt_conf; /* D-TLB-configuration */ + unsigned long dt_sp_base; + unsigned long dt_sp_stride; + unsigned long dt_sp_count; + unsigned long dt_off_base; + unsigned long dt_off_stride; + unsigned long dt_off_count; + unsigned long dt_loop; +}; + +/* Might need adjustment to work with 64-bit firmware */ +struct pdc_iodc { /* PDC_IODC */ + unsigned char hversion_model; + unsigned char hversion; + unsigned char spa; + unsigned char type; + unsigned int sversion_rev:4; + unsigned int sversion_model:19; + unsigned int sversion_opt:8; + unsigned char rev; + unsigned char dep; + unsigned char features; + unsigned char pad1; + unsigned int checksum:16; + unsigned int length:16; + unsigned int pad[15]; +} __attribute__((aligned(8))) ; + +/* no BLTBs in pa2.0 processors */ +struct pdc_btlb_info_range { + unsigned char res00; + unsigned char num_i; + unsigned char num_d; + unsigned char num_comb; +}; + +struct pdc_btlb_info { /* PDC_BLOCK_TLB, return of PDC_BTLB_INFO */ + unsigned int min_size; /* minimum size of BTLB in pages */ + unsigned int max_size; /* maximum size of BTLB in pages */ + struct pdc_btlb_info_range fixed_range_info; + struct pdc_btlb_info_range variable_range_info; +}; + +struct pdc_mem_retinfo { /* PDC_MEM/PDC_MEM_MEMINFO (return info) */ + unsigned long pdt_size; + unsigned long pdt_entries; + unsigned long pdt_status; + unsigned long first_dbe_loc; + unsigned long good_mem; +}; + +struct pdc_mem_read_pdt { /* PDC_MEM/PDC_MEM_READ_PDT (return info) */ + unsigned long pdt_entries; +}; + +#ifdef __LP64__ +struct pdc_memory_table_raddr { /* PDC_MEM/PDC_MEM_TABLE (return info) */ + unsigned long entries_returned; + unsigned long entries_total; +}; + +struct pdc_memory_table { /* PDC_MEM/PDC_MEM_TABLE (arguments) */ + unsigned long paddr; + unsigned int pages; + unsigned int reserved; +}; +#endif /* __LP64__ */ + +struct pdc_system_map_mod_info { /* PDC_SYSTEM_MAP/FIND_MODULE */ + unsigned long mod_addr; + unsigned long mod_pgs; + unsigned long add_addrs; +}; + +struct pdc_system_map_addr_info { /* PDC_SYSTEM_MAP/FIND_ADDRESS */ + unsigned long mod_addr; + unsigned long mod_pgs; +}; + +struct pdc_initiator { /* PDC_INITIATOR */ + int host_id; + int factor; + int width; + int mode; +}; + +struct hardware_path { + char flags; /* see bit definitions below */ + char bc[6]; /* Bus Converter routing info to a specific */ + /* I/O adaptor (< 0 means none, > 63 resvd) */ + char mod; /* fixed field of specified module */ +}; + +/* + * Device path specifications used by PDC. + */ +struct pdc_module_path { + struct hardware_path path; + unsigned int layers[6]; /* device-specific info (ctlr #, unit # ...) */ +}; + +/* Only used on some pre-PA2.0 boxes */ +struct pdc_memory_map { /* PDC_MEMORY_MAP */ + unsigned long hpa; /* mod's register set address */ + unsigned long more_pgs; /* number of additional I/O pgs */ +}; + +struct pdc_tod { + unsigned long tod_sec; + unsigned long tod_usec; +}; + +/* architected results from PDC_PIM/transfer hpmc on a PA1.1 machine */ + +struct pdc_hpmc_pim_11 { /* PDC_PIM */ + unsigned int gr[32]; + unsigned int cr[32]; + unsigned int sr[8]; + unsigned int iasq_back; + unsigned int iaoq_back; + unsigned int check_type; + unsigned int cpu_state; + unsigned int rsvd1; + unsigned int cache_check; + unsigned int tlb_check; + unsigned int bus_check; + unsigned int assists_check; + unsigned int rsvd2; + unsigned int assist_state; + unsigned int responder_addr; + unsigned int requestor_addr; + unsigned int path_info; + unsigned long long fr[32]; +}; + +/* + * architected results from PDC_PIM/transfer hpmc on a PA2.0 machine + * + * Note that PDC_PIM doesn't care whether or not wide mode was enabled + * so the results are different on PA1.1 vs. PA2.0 when in narrow mode. + * + * Note also that there are unarchitected results available, which + * are hversion dependent. Do a "ser pim 0 hpmc" after rebooting, since + * the firmware is probably the best way of printing hversion dependent + * data. + */ + +struct pdc_hpmc_pim_20 { /* PDC_PIM */ + unsigned long long gr[32]; + unsigned long long cr[32]; + unsigned long long sr[8]; + unsigned long long iasq_back; + unsigned long long iaoq_back; + unsigned int check_type; + unsigned int cpu_state; + unsigned int cache_check; + unsigned int tlb_check; + unsigned int bus_check; + unsigned int assists_check; + unsigned int assist_state; + unsigned int path_info; + unsigned long long responder_addr; + unsigned long long requestor_addr; + unsigned long long fr[32]; }; #endif /* !defined(__ASSEMBLY__) */ diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile index 649dc3eda448..eafd06ab59ef 100644 --- a/arch/parisc/kernel/Makefile +++ b/arch/parisc/kernel/Makefile @@ -9,8 +9,7 @@ obj-y := cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \ pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \ ptrace.o hardware.o inventory.o drivers.o \ signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \ - process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \ - topology.o + process.o processor.o pdc_cons.o pdc_chassis.o unwind.o ifdef CONFIG_FUNCTION_TRACER # Do not profile debug and lowlevel utilities @@ -30,5 +29,6 @@ obj-$(CONFIG_AUDIT) += audit.o obj64-$(CONFIG_AUDIT) += compat_audit.o # only supported for PCX-W/U in 64-bit mode at the moment obj-$(CONFIG_64BIT) += perf.o perf_asm.o $(obj64-y) +obj-$(CONFIG_PARISC_CPU_TOPOLOGY) += topology.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o diff --git a/arch/parisc/kernel/processor.c b/arch/parisc/kernel/processor.c index e120d63c1b28..45cc65902fce 100644 --- a/arch/parisc/kernel/processor.c +++ b/arch/parisc/kernel/processor.c @@ -184,6 +184,9 @@ static int __init processor_probe(struct parisc_device *dev) p->txn_addr = txn_addr; /* save CPU IRQ address */ p->cpu_num = cpu_info.cpu_num; p->cpu_loc = cpu_info.cpu_loc; + + store_cpu_topology(cpuid); + #ifdef CONFIG_SMP /* ** FIXME: review if any other initialization is clobbered @@ -325,6 +328,8 @@ int __init init_per_cpu(int cpunum) set_firmware_width(); ret = pdc_coproc_cfg(&coproc_cfg); + store_cpu_topology(cpunum); + if(ret >= 0 && coproc_cfg.ccr_functional) { mtctl(coproc_cfg.ccr_functional, 10); /* 10 == Coprocessor Control Reg */ @@ -388,6 +393,14 @@ show_cpuinfo (struct seq_file *m, void *v) boot_cpu_data.cpu_hz / 1000000, boot_cpu_data.cpu_hz % 1000000 ); +#ifdef CONFIG_PARISC_CPU_TOPOLOGY + seq_printf(m, "physical id\t: %d\n", + topology_physical_package_id(cpu)); + seq_printf(m, "siblings\t: %d\n", + cpumask_weight(topology_core_cpumask(cpu))); + seq_printf(m, "core id\t\t: %d\n", topology_core_id(cpu)); +#endif + seq_printf(m, "capabilities\t:"); if (boot_cpu_data.pdc.capabilities & PDC_MODEL_OS32) seq_puts(m, " os32"); diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c index f7d0c3b33d70..0e9675f857a5 100644 --- a/arch/parisc/kernel/setup.c +++ b/arch/parisc/kernel/setup.c @@ -408,6 +408,8 @@ void __init start_parisc(void) cpunum = smp_processor_id(); + init_cpu_topology(); + set_firmware_width_unlocked(); ret = pdc_coproc_cfg_unlocked(&coproc_cfg); diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index f2a4038e275b..342073f44d3f 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -93,7 +93,6 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) unsigned long usp = (regs->gr[30] & ~(0x01UL)); unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE; #ifdef CONFIG_64BIT - compat_sigset_t compat_set; struct compat_rt_sigframe __user * compat_frame; if (is_compat_task()) @@ -114,9 +113,8 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) if (is_compat_task()) { DBG(2,"sys_rt_sigreturn: ELF32 process.\n"); - if (__copy_from_user(&compat_set, &compat_frame->uc.uc_sigmask, sizeof(compat_set))) + if (get_compat_sigset(&set, &compat_frame->uc.uc_sigmask)) goto give_sigsegv; - sigset_32to64(&set,&compat_set); } else #endif { @@ -238,7 +236,6 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs, int err = 0; #ifdef CONFIG_64BIT struct compat_rt_sigframe __user * compat_frame; - compat_sigset_t compat_set; #endif usp = (regs->gr[30] & ~(0x01UL)); @@ -261,8 +258,8 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs, DBG(1,"setup_rt_frame: frame->uc.uc_mcontext = 0x%p\n", &compat_frame->uc.uc_mcontext); err |= setup_sigcontext32(&compat_frame->uc.uc_mcontext, &compat_frame->regs, regs, in_syscall); - sigset_64to32(&compat_set,set); - err |= __copy_to_user(&compat_frame->uc.uc_sigmask, &compat_set, sizeof(compat_set)); + err |= put_compat_sigset(&compat_frame->uc.uc_sigmask, set, + sizeof(compat_sigset_t)); } else #endif { diff --git a/arch/parisc/kernel/signal32.c b/arch/parisc/kernel/signal32.c index 9e0cb6a577d6..41afa9cd1f55 100644 --- a/arch/parisc/kernel/signal32.c +++ b/arch/parisc/kernel/signal32.c @@ -46,19 +46,6 @@ #define DBG(LEVEL, ...) #endif -inline void -sigset_32to64(sigset_t *s64, compat_sigset_t *s32) -{ - s64->sig[0] = s32->sig[0] | ((unsigned long)s32->sig[1] << 32); -} - -inline void -sigset_64to32(compat_sigset_t *s32, sigset_t *s64) -{ - s32->sig[0] = s64->sig[0] & 0xffffffffUL; - s32->sig[1] = (s64->sig[0] >> 32) & 0xffffffffUL; -} - long restore_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __user * rf, struct pt_regs *regs) diff --git a/arch/parisc/kernel/signal32.h b/arch/parisc/kernel/signal32.h index af51d4ccee42..719e7417732c 100644 --- a/arch/parisc/kernel/signal32.h +++ b/arch/parisc/kernel/signal32.h @@ -79,8 +79,6 @@ struct compat_rt_sigframe { #define FUNCTIONCALLFRAME32 48 #define PARISC_RT_SIGFRAME_SIZE32 (((sizeof(struct compat_rt_sigframe) + FUNCTIONCALLFRAME32) + SIGFRAME32) & -SIGFRAME32) -void sigset_32to64(sigset_t *s64, compat_sigset_t *s32); -void sigset_64to32(compat_sigset_t *s32, sigset_t *s64); long restore_sigcontext32(struct compat_sigcontext __user *sc, struct compat_regfile __user *rf, struct pt_regs *regs); diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index 41e60a9c7db2..e775f80ae28c 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -690,15 +690,15 @@ cas_action: /* ELF32 Process entry path */ lws_compare_and_swap_2: #ifdef CONFIG_64BIT - /* Clip the input registers */ + /* Clip the input registers. We don't need to clip %r23 as we + only use it for word operations */ depdi 0, 31, 32, %r26 depdi 0, 31, 32, %r25 depdi 0, 31, 32, %r24 - depdi 0, 31, 32, %r23 #endif /* Check the validity of the size pointer */ - subi,>>= 4, %r23, %r0 + subi,>>= 3, %r23, %r0 b,n lws_exit_nosys /* Jump to the functions which will load the old and new values into diff --git a/arch/parisc/kernel/topology.c b/arch/parisc/kernel/topology.c index f5159381fdd6..0a10e4ddc528 100644 --- a/arch/parisc/kernel/topology.c +++ b/arch/parisc/kernel/topology.c @@ -1,37 +1,142 @@ /* - * arch/parisc/kernel/topology.c - Populate sysfs with topology information + * arch/parisc/kernel/topology.c * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * Copyright (C) 2017 Helge Deller * - * 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, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. + * based on arch/arm/kernel/topology.c * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. */ -#include -#include -#include -#include +#include +#include +#include -static DEFINE_PER_CPU(struct cpu, cpu_devices); +#include -static int __init topology_init(void) + /* + * cpu topology table + */ +struct cputopo_parisc cpu_topology[NR_CPUS] __read_mostly; +EXPORT_SYMBOL_GPL(cpu_topology); + +const struct cpumask *cpu_coregroup_mask(int cpu) { - int num; + return &cpu_topology[cpu].core_sibling; +} + +static void update_siblings_masks(unsigned int cpuid) +{ + struct cputopo_parisc *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; + int cpu; + + /* update core and thread sibling masks */ + for_each_possible_cpu(cpu) { + cpu_topo = &cpu_topology[cpu]; + + if (cpuid_topo->socket_id != cpu_topo->socket_id) + continue; + + cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); + if (cpu != cpuid) + cpumask_set_cpu(cpu, &cpuid_topo->core_sibling); + + if (cpuid_topo->core_id != cpu_topo->core_id) + continue; + + cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling); + if (cpu != cpuid) + cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling); + } + smp_wmb(); +} + +static int dualcores_found __initdata; + +/* + * store_cpu_topology is called at boot when only one cpu is running + * and with the mutex cpu_hotplug.lock locked, when several cpus have booted, + * which prevents simultaneous write access to cpu_topology array + */ +void __init store_cpu_topology(unsigned int cpuid) +{ + struct cputopo_parisc *cpuid_topo = &cpu_topology[cpuid]; + struct cpuinfo_parisc *p; + int max_socket = -1; + unsigned long cpu; + + /* If the cpu topology has been already set, just return */ + if (cpuid_topo->core_id != -1) + return; - for_each_present_cpu(num) { - register_cpu(&per_cpu(cpu_devices, num), num); + /* create cpu topology mapping */ + cpuid_topo->thread_id = -1; + cpuid_topo->core_id = 0; + + p = &per_cpu(cpu_data, cpuid); + for_each_online_cpu(cpu) { + const struct cpuinfo_parisc *cpuinfo = &per_cpu(cpu_data, cpu); + + if (cpu == cpuid) /* ignore current cpu */ + continue; + + if (cpuinfo->cpu_loc == p->cpu_loc) { + cpuid_topo->core_id = cpu_topology[cpu].core_id; + if (p->cpu_loc) { + cpuid_topo->core_id++; + cpuid_topo->socket_id = cpu_topology[cpu].socket_id; + dualcores_found = 1; + continue; + } + } + + if (cpuid_topo->socket_id == -1) + max_socket = max(max_socket, cpu_topology[cpu].socket_id); } - return 0; + + if (cpuid_topo->socket_id == -1) + cpuid_topo->socket_id = max_socket + 1; + + update_siblings_masks(cpuid); + + pr_info("CPU%u: thread %d, cpu %d, socket %d\n", + cpuid, cpu_topology[cpuid].thread_id, + cpu_topology[cpuid].core_id, + cpu_topology[cpuid].socket_id); } -subsys_initcall(topology_init); +static struct sched_domain_topology_level parisc_mc_topology[] = { +#ifdef CONFIG_SCHED_MC + { cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) }, +#endif + + { cpu_cpu_mask, SD_INIT_NAME(DIE) }, + { NULL, }, +}; + +/* + * init_cpu_topology is called at boot when only one cpu is running + * which prevent simultaneous write access to cpu_topology array + */ +void __init init_cpu_topology(void) +{ + unsigned int cpu; + + /* init core mask and capacity */ + for_each_possible_cpu(cpu) { + struct cputopo_parisc *cpu_topo = &(cpu_topology[cpu]); + + cpu_topo->thread_id = -1; + cpu_topo->core_id = -1; + cpu_topo->socket_id = -1; + cpumask_clear(&cpu_topo->core_sibling); + cpumask_clear(&cpu_topo->thread_sibling); + } + smp_wmb(); + + /* Set scheduler topology descriptor */ + if (dualcores_found) + set_sched_topology(parisc_mc_topology); +} diff --git a/arch/powerpc/include/asm/compat.h b/arch/powerpc/include/asm/compat.h index a035b1e5dfa7..8a2aecfe9b02 100644 --- a/arch/powerpc/include/asm/compat.h +++ b/arch/powerpc/include/asm/compat.h @@ -185,7 +185,6 @@ typedef struct compat_siginfo { } compat_siginfo_t; #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL /* * A pointer passed in from user mode. This should not diff --git a/arch/powerpc/include/asm/imc-pmu.h b/arch/powerpc/include/asm/imc-pmu.h index 7f74c282710f..fad0e6ff460f 100644 --- a/arch/powerpc/include/asm/imc-pmu.h +++ b/arch/powerpc/include/asm/imc-pmu.h @@ -20,11 +20,6 @@ #include #include -/* - * For static allocation of some of the structures. - */ -#define IMC_MAX_PMUS 32 - /* * Compatibility macros for IMC devices */ @@ -125,4 +120,5 @@ enum { extern int init_imc_pmu(struct device_node *parent, struct imc_pmu *pmu_ptr, int pmu_id); extern void thread_imc_disable(void); +extern int get_max_nest_dev(void); #endif /* __ASM_POWERPC_IMC_PMU_H */ diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 602e0fde19b4..8bdc2f96c5d6 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -735,8 +735,8 @@ static __init void cpufeatures_cpu_quirks(void) */ if ((version & 0xffffff00) == 0x004e0100) cur_cpu_spec->cpu_features |= CPU_FTR_POWER9_DD1; - else if ((version & 0xffffefff) == 0x004e0200) - cur_cpu_spec->cpu_features &= ~CPU_FTR_POWER9_DD2_1; + else if ((version & 0xffffefff) == 0x004e0201) + cur_cpu_spec->cpu_features |= CPU_FTR_POWER9_DD2_1; } static void __init cpufeatures_setup_finished(void) diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 16d16583cf11..9ffd73296f64 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -94,40 +94,13 @@ */ static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set) { - compat_sigset_t cset; - - switch (_NSIG_WORDS) { - case 4: cset.sig[6] = set->sig[3] & 0xffffffffull; - cset.sig[7] = set->sig[3] >> 32; - case 3: cset.sig[4] = set->sig[2] & 0xffffffffull; - cset.sig[5] = set->sig[2] >> 32; - case 2: cset.sig[2] = set->sig[1] & 0xffffffffull; - cset.sig[3] = set->sig[1] >> 32; - case 1: cset.sig[0] = set->sig[0] & 0xffffffffull; - cset.sig[1] = set->sig[0] >> 32; - } - return copy_to_user(uset, &cset, sizeof(*uset)); + return put_compat_sigset(uset, set, sizeof(*uset)); } static inline int get_sigset_t(sigset_t *set, const compat_sigset_t __user *uset) { - compat_sigset_t s32; - - if (copy_from_user(&s32, uset, sizeof(*uset))) - return -EFAULT; - - /* - * Swap the 2 words of the 64-bit sigset_t (they are stored - * in the "wrong" endian in 32-bit user storage). - */ - switch (_NSIG_WORDS) { - case 4: set->sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); - case 3: set->sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); - case 2: set->sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); - case 1: set->sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); - } - return 0; + return get_compat_sigset(set, uset); } #define to_user_ptr(p) ptr_to_compat(p) diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index c9de03e0c1f1..d469224c4ada 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c @@ -21,6 +21,7 @@ #include #include #include +#include static int __patch_instruction(unsigned int *addr, unsigned int instr) { @@ -146,11 +147,8 @@ int patch_instruction(unsigned int *addr, unsigned int instr) * During early early boot patch_instruction is called * when text_poke_area is not ready, but we still need * to allow patching. We just do the plain old patching - * We use slab_is_available and per cpu read * via this_cpu_read - * of text_poke_area. Per-CPU areas might not be up early - * this can create problems with just using this_cpu_read() */ - if (!slab_is_available() || !this_cpu_read(text_poke_area)) + if (!this_cpu_read(*PTRRELOC(&text_poke_area))) return __patch_instruction(addr, instr); local_irq_save(flags); diff --git a/arch/powerpc/mm/slice.c b/arch/powerpc/mm/slice.c index 564fff06f5c1..23ec2c5e3b78 100644 --- a/arch/powerpc/mm/slice.c +++ b/arch/powerpc/mm/slice.c @@ -122,7 +122,8 @@ static int slice_high_has_vma(struct mm_struct *mm, unsigned long slice) return !slice_area_is_free(mm, start, end - start); } -static void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret) +static void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret, + unsigned long high_limit) { unsigned long i; @@ -133,15 +134,16 @@ static void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret) if (!slice_low_has_vma(mm, i)) ret->low_slices |= 1u << i; - if (mm->context.slb_addr_limit <= SLICE_LOW_TOP) + if (high_limit <= SLICE_LOW_TOP) return; - for (i = 0; i < GET_HIGH_SLICE_INDEX(mm->context.slb_addr_limit); i++) + for (i = 0; i < GET_HIGH_SLICE_INDEX(high_limit); i++) if (!slice_high_has_vma(mm, i)) __set_bit(i, ret->high_slices); } -static void slice_mask_for_size(struct mm_struct *mm, int psize, struct slice_mask *ret) +static void slice_mask_for_size(struct mm_struct *mm, int psize, struct slice_mask *ret, + unsigned long high_limit) { unsigned char *hpsizes; int index, mask_index; @@ -156,8 +158,11 @@ static void slice_mask_for_size(struct mm_struct *mm, int psize, struct slice_ma if (((lpsizes >> (i * 4)) & 0xf) == psize) ret->low_slices |= 1u << i; + if (high_limit <= SLICE_LOW_TOP) + return; + hpsizes = mm->context.high_slices_psize; - for (i = 0; i < GET_HIGH_SLICE_INDEX(mm->context.slb_addr_limit); i++) { + for (i = 0; i < GET_HIGH_SLICE_INDEX(high_limit); i++) { mask_index = i & 0x1; index = i >> 1; if (((hpsizes[index] >> (mask_index * 4)) & 0xf) == psize) @@ -169,6 +174,10 @@ static int slice_check_fit(struct mm_struct *mm, struct slice_mask mask, struct slice_mask available) { DECLARE_BITMAP(result, SLICE_NUM_HIGH); + /* + * Make sure we just do bit compare only to the max + * addr limit and not the full bit map size. + */ unsigned long slice_count = GET_HIGH_SLICE_INDEX(mm->context.slb_addr_limit); bitmap_and(result, mask.high_slices, @@ -472,7 +481,7 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, /* First make up a "good" mask of slices that have the right size * already */ - slice_mask_for_size(mm, psize, &good_mask); + slice_mask_for_size(mm, psize, &good_mask, high_limit); slice_print_mask(" good_mask", good_mask); /* @@ -497,7 +506,7 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, #ifdef CONFIG_PPC_64K_PAGES /* If we support combo pages, we can allow 64k pages in 4k slices */ if (psize == MMU_PAGE_64K) { - slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask); + slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask, high_limit); if (fixed) slice_or_mask(&good_mask, &compat_mask); } @@ -530,11 +539,11 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, return newaddr; } } - - /* We don't fit in the good mask, check what other slices are + /* + * We don't fit in the good mask, check what other slices are * empty and thus can be converted */ - slice_mask_for_free(mm, &potential_mask); + slice_mask_for_free(mm, &potential_mask, high_limit); slice_or_mask(&potential_mask, &good_mask); slice_print_mask(" potential", potential_mask); @@ -744,17 +753,18 @@ int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, { struct slice_mask mask, available; unsigned int psize = mm->context.user_psize; + unsigned long high_limit = mm->context.slb_addr_limit; if (radix_enabled()) return 0; slice_range_to_mask(addr, len, &mask); - slice_mask_for_size(mm, psize, &available); + slice_mask_for_size(mm, psize, &available, high_limit); #ifdef CONFIG_PPC_64K_PAGES /* We need to account for 4k slices too */ if (psize == MMU_PAGE_64K) { struct slice_mask compat_mask; - slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask); + slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask, high_limit); slice_or_mask(&available, &compat_mask); } #endif diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index 36344117c680..0ead3cd73caa 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -26,7 +26,7 @@ */ static DEFINE_MUTEX(nest_init_lock); static DEFINE_PER_CPU(struct imc_pmu_ref *, local_nest_imc_refc); -static struct imc_pmu *per_nest_pmu_arr[IMC_MAX_PMUS]; +static struct imc_pmu **per_nest_pmu_arr; static cpumask_t nest_imc_cpumask; struct imc_pmu_ref *nest_imc_refc; static int nest_pmus; @@ -286,13 +286,14 @@ static struct imc_pmu_ref *get_nest_pmu_ref(int cpu) static void nest_change_cpu_context(int old_cpu, int new_cpu) { struct imc_pmu **pn = per_nest_pmu_arr; - int i; if (old_cpu < 0 || new_cpu < 0) return; - for (i = 0; *pn && i < IMC_MAX_PMUS; i++, pn++) + while (*pn) { perf_pmu_migrate_context(&(*pn)->pmu, old_cpu, new_cpu); + pn++; + } } static int ppc_nest_imc_cpu_offline(unsigned int cpu) @@ -467,7 +468,7 @@ static int nest_imc_event_init(struct perf_event *event) * Nest HW counter memory resides in a per-chip reserve-memory (HOMER). * Get the base memory addresss for this cpu. */ - chip_id = topology_physical_package_id(event->cpu); + chip_id = cpu_to_chip_id(event->cpu); pcni = pmu->mem_info; do { if (pcni->id == chip_id) { @@ -524,19 +525,19 @@ static int nest_imc_event_init(struct perf_event *event) */ static int core_imc_mem_init(int cpu, int size) { - int phys_id, rc = 0, core_id = (cpu / threads_per_core); + int nid, rc = 0, core_id = (cpu / threads_per_core); struct imc_mem_info *mem_info; /* * alloc_pages_node() will allocate memory for core in the * local node only. */ - phys_id = topology_physical_package_id(cpu); + nid = cpu_to_node(cpu); mem_info = &core_imc_pmu->mem_info[core_id]; mem_info->id = core_id; /* We need only vbase for core counters */ - mem_info->vbase = page_address(alloc_pages_node(phys_id, + mem_info->vbase = page_address(alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE | __GFP_NOWARN, get_order(size))); if (!mem_info->vbase) @@ -797,14 +798,14 @@ static int core_imc_event_init(struct perf_event *event) static int thread_imc_mem_alloc(int cpu_id, int size) { u64 ldbar_value, *local_mem = per_cpu(thread_imc_mem, cpu_id); - int phys_id = topology_physical_package_id(cpu_id); + int nid = cpu_to_node(cpu_id); if (!local_mem) { /* * This case could happen only once at start, since we dont * free the memory in cpu offline path. */ - local_mem = page_address(alloc_pages_node(phys_id, + local_mem = page_address(alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE | __GFP_NOWARN, get_order(size))); if (!local_mem) @@ -1194,6 +1195,7 @@ static void imc_common_cpuhp_mem_free(struct imc_pmu *pmu_ptr) kfree(pmu_ptr->attr_groups[IMC_EVENT_ATTR]->attrs); kfree(pmu_ptr->attr_groups[IMC_EVENT_ATTR]); kfree(pmu_ptr); + kfree(per_nest_pmu_arr); return; } @@ -1218,6 +1220,13 @@ static int imc_mem_init(struct imc_pmu *pmu_ptr, struct device_node *parent, return -ENOMEM; /* Needed for hotplug/migration */ + if (!per_nest_pmu_arr) { + per_nest_pmu_arr = kcalloc(get_max_nest_dev() + 1, + sizeof(struct imc_pmu *), + GFP_KERNEL); + if (!per_nest_pmu_arr) + return -ENOMEM; + } per_nest_pmu_arr[pmu_index] = pmu_ptr; break; case IMC_DOMAIN_CORE: diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 1fbb5da17dd2..e47761cdcb98 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -1093,7 +1093,7 @@ static int show_spu_loadavg(struct seq_file *s, void *private) LOAD_INT(c), LOAD_FRAC(c), count_active_contexts(), atomic_read(&nr_spu_contexts), - task_active_pid_ns(current)->last_pid); + idr_get_cursor(&task_active_pid_ns(current)->idr)); return 0; } diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c index 21f6531fae20..465ea105b771 100644 --- a/arch/powerpc/platforms/powernv/opal-imc.c +++ b/arch/powerpc/platforms/powernv/opal-imc.c @@ -153,6 +153,22 @@ static void disable_core_pmu_counters(void) put_online_cpus(); } +int get_max_nest_dev(void) +{ + struct device_node *node; + u32 pmu_units = 0, type; + + for_each_compatible_node(node, NULL, IMC_DTB_UNIT_COMPAT) { + if (of_property_read_u32(node, "type", &type)) + continue; + + if (type == IMC_TYPE_CHIP) + pmu_units++; + } + + return pmu_units; +} + static int opal_imc_counters_probe(struct platform_device *pdev) { struct device_node *imc_dev = pdev->dev.of_node; @@ -191,8 +207,10 @@ static int opal_imc_counters_probe(struct platform_device *pdev) break; } - if (!imc_pmu_create(imc_dev, pmu_count, domain)) - pmu_count++; + if (!imc_pmu_create(imc_dev, pmu_count, domain)) { + if (domain == IMC_DOMAIN_NEST) + pmu_count++; + } } return 0; diff --git a/arch/powerpc/platforms/powernv/vas.c b/arch/powerpc/platforms/powernv/vas.c index c488621dbec3..aebbe95c9230 100644 --- a/arch/powerpc/platforms/powernv/vas.c +++ b/arch/powerpc/platforms/powernv/vas.c @@ -135,6 +135,7 @@ int chip_to_vas_id(int chipid) } return -1; } +EXPORT_SYMBOL(chip_to_vas_id); static int vas_probe(struct platform_device *pdev) { diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 863a62a6de3c..829c67986db7 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -148,6 +148,7 @@ config S390 select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_FUTEX_CMPXCHG if FUTEX + select HAVE_GCC_PLUGINS select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZ4 @@ -158,6 +159,8 @@ config S390 select HAVE_KRETPROBES select HAVE_KVM select HAVE_LIVEPATCH + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP select HAVE_MEMBLOCK_PHYS_MAP @@ -538,22 +541,6 @@ config ARCH_RANDOM If unsure, say Y. -config ALTERNATIVES - def_bool y - prompt "Patch optimized instructions for running CPU type" - help - When enabled the kernel code is compiled with additional - alternative instructions blocks optimized for newer CPU types. - These alternative instructions blocks are patched at kernel boot - time when running CPU supports them. This mechanism is used to - optimize some critical code paths (i.e. spinlocks) for newer CPUs - even if kernel is build to support older machine generations. - - This mechanism could be disabled by appending "noaltinstr" - option to the kernel command line. - - If unsure, say Y. - endmenu menu "Memory setup" diff --git a/arch/s390/configs/default_defconfig b/arch/s390/configs/default_defconfig index 84eccc88c065..5af8458951cf 100644 --- a/arch/s390/configs/default_defconfig +++ b/arch/s390/configs/default_defconfig @@ -629,6 +629,7 @@ CONFIG_STACK_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_FUNCTION_PROFILER=y CONFIG_HIST_TRIGGERS=y +CONFIG_DMA_API_DEBUG=y CONFIG_LKDTM=m CONFIG_TEST_LIST_SORT=y CONFIG_TEST_SORT=y @@ -637,14 +638,12 @@ CONFIG_RBTREE_TEST=y CONFIG_INTERVAL_TREE_TEST=m CONFIG_PERCPU_TEST=m CONFIG_ATOMIC64_SELFTEST=y -CONFIG_DMA_API_DEBUG=y CONFIG_TEST_BPF=m CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_S390_PTDUMP=y CONFIG_ENCRYPTED_KEYS=m CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y -CONFIG_HARDENED_USERCOPY=y CONFIG_FORTIFY_SOURCE=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y @@ -660,13 +659,11 @@ CONFIG_CRYPTO_PCRYPT=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_MCRYPTD=m CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_KEYWRAP=m -CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_VMAC=m CONFIG_CRYPTO_CRC32=m diff --git a/arch/s390/configs/gcov_defconfig b/arch/s390/configs/gcov_defconfig index f7202358e6d7..d52eafe57ae8 100644 --- a/arch/s390/configs/gcov_defconfig +++ b/arch/s390/configs/gcov_defconfig @@ -587,7 +587,6 @@ CONFIG_BIG_KEYS=y CONFIG_ENCRYPTED_KEYS=m CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y -CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=0 @@ -605,13 +604,10 @@ CONFIG_CRYPTO_PCRYPT=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_MCRYPTD=m CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_CCM=m -CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_KEYWRAP=m -CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_VMAC=m CONFIG_CRYPTO_CRC32=m diff --git a/arch/s390/configs/performance_defconfig b/arch/s390/configs/performance_defconfig index 03100fe74ea8..20ed149e1137 100644 --- a/arch/s390/configs/performance_defconfig +++ b/arch/s390/configs/performance_defconfig @@ -585,7 +585,6 @@ CONFIG_BIG_KEYS=y CONFIG_ENCRYPTED_KEYS=m CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y -CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=0 @@ -603,13 +602,10 @@ CONFIG_CRYPTO_PCRYPT=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_MCRYPTD=m CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_CCM=m -CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_KEYWRAP=m -CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_VMAC=m CONFIG_CRYPTO_CRC32=m diff --git a/arch/s390/include/asm/alternative.h b/arch/s390/include/asm/alternative.h index 6c268f6a51d3..a72002056b54 100644 --- a/arch/s390/include/asm/alternative.h +++ b/arch/s390/include/asm/alternative.h @@ -15,14 +15,9 @@ struct alt_instr { u8 replacementlen; /* length of new instruction */ } __packed; -#ifdef CONFIG_ALTERNATIVES -extern void apply_alternative_instructions(void); -extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); -#else -static inline void apply_alternative_instructions(void) {}; -static inline void apply_alternatives(struct alt_instr *start, - struct alt_instr *end) {}; -#endif +void apply_alternative_instructions(void); +void apply_alternatives(struct alt_instr *start, struct alt_instr *end); + /* * |661: |662: |6620 |663: * +-----------+---------------------+ @@ -109,7 +104,6 @@ static inline void apply_alternatives(struct alt_instr *start, b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \ INSTR_LEN_SANITY_CHECK(altinstr_len(num)) -#ifdef CONFIG_ALTERNATIVES /* alternative assembly primitive: */ #define ALTERNATIVE(oldinstr, altinstr, facility) \ ".pushsection .altinstr_replacement, \"ax\"\n" \ @@ -130,14 +124,6 @@ static inline void apply_alternatives(struct alt_instr *start, ALTINSTR_ENTRY(facility1, 1) \ ALTINSTR_ENTRY(facility2, 2) \ ".popsection\n" -#else -/* Alternative instructions are disabled, let's put just oldinstr in */ -#define ALTERNATIVE(oldinstr, altinstr, facility) \ - oldinstr "\n" - -#define ALTERNATIVE_2(oldinstr, altinstr1, facility1, altinstr2, facility2) \ - oldinstr "\n" -#endif /* * Alternative instructions for different CPU types or capabilities. diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h index 1b60eb3676d5..5e6a63641a5f 100644 --- a/arch/s390/include/asm/compat.h +++ b/arch/s390/include/asm/compat.h @@ -263,7 +263,6 @@ typedef struct compat_siginfo { #define si_overrun _sifields._timer._overrun #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL /* * A pointer passed in from user mode. This should not diff --git a/arch/s390/include/asm/cpu_mf.h b/arch/s390/include/asm/cpu_mf.h index 05480e4cc5ca..792cda339af1 100644 --- a/arch/s390/include/asm/cpu_mf.h +++ b/arch/s390/include/asm/cpu_mf.h @@ -144,6 +144,12 @@ struct hws_trailer_entry { unsigned long long progusage2; /* */ } __packed; +/* Load program parameter */ +static inline void lpp(void *pp) +{ + asm volatile(".insn s,0xb2800000,0(%0)\n":: "a" (pp) : "memory"); +} + /* Query counter information */ static inline int qctri(struct cpumf_ctr_info *info) { @@ -167,7 +173,7 @@ static inline int lcctl(u64 ctl) " .insn s,0xb2840000,%1\n" " ipm %0\n" " srl %0,28\n" - : "=d" (cc) : "m" (ctl) : "cc"); + : "=d" (cc) : "Q" (ctl) : "cc"); return cc; } diff --git a/arch/s390/include/asm/futex.h b/arch/s390/include/asm/futex.h index 9b5a3469fed9..5e97a4353147 100644 --- a/arch/s390/include/asm/futex.h +++ b/arch/s390/include/asm/futex.h @@ -26,9 +26,9 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) { int oldval = 0, newval, ret; + mm_segment_t old_fs; - load_kernel_asce(); - + old_fs = enable_sacf_uaccess(); pagefault_disable(); switch (op) { case FUTEX_OP_SET: @@ -55,6 +55,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, ret = -ENOSYS; } pagefault_enable(); + disable_sacf_uaccess(old_fs); if (!ret) *oval = oldval; @@ -65,9 +66,10 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval) { + mm_segment_t old_fs; int ret; - load_kernel_asce(); + old_fs = enable_sacf_uaccess(); asm volatile( " sacf 256\n" "0: cs %1,%4,0(%5)\n" @@ -77,6 +79,7 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, : "=d" (ret), "+d" (oldval), "=m" (*uaddr) : "0" (-EFAULT), "d" (newval), "a" (uaddr), "m" (*uaddr) : "cc", "memory"); + disable_sacf_uaccess(old_fs); *uval = oldval; return ret; } diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index 9eb36a1592c7..ec6592e8ba36 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -115,33 +115,28 @@ struct lowcore { /* Address space pointer. */ __u64 kernel_asce; /* 0x0378 */ __u64 user_asce; /* 0x0380 */ + __u64 vdso_asce; /* 0x0388 */ /* * The lpp and current_pid fields form a * 64-bit value that is set as program * parameter with the LPP instruction. */ - __u32 lpp; /* 0x0388 */ - __u32 current_pid; /* 0x038c */ + __u32 lpp; /* 0x0390 */ + __u32 current_pid; /* 0x0394 */ /* SMP info area */ - __u32 cpu_nr; /* 0x0390 */ - __u32 softirq_pending; /* 0x0394 */ - __u64 percpu_offset; /* 0x0398 */ - __u64 vdso_per_cpu_data; /* 0x03a0 */ - __u64 machine_flags; /* 0x03a8 */ - __u32 preempt_count; /* 0x03b0 */ - __u8 pad_0x03b4[0x03b8-0x03b4]; /* 0x03b4 */ - __u64 gmap; /* 0x03b8 */ - __u32 spinlock_lockval; /* 0x03c0 */ - __u32 spinlock_index; /* 0x03c4 */ - __u32 fpu_flags; /* 0x03c8 */ - __u8 pad_0x03cc[0x0400-0x03cc]; /* 0x03cc */ - - /* Per cpu primary space access list */ - __u32 paste[16]; /* 0x0400 */ - - __u8 pad_0x04c0[0x0e00-0x0440]; /* 0x0440 */ + __u32 cpu_nr; /* 0x0398 */ + __u32 softirq_pending; /* 0x039c */ + __u32 preempt_count; /* 0x03a0 */ + __u32 spinlock_lockval; /* 0x03a4 */ + __u32 spinlock_index; /* 0x03a8 */ + __u32 fpu_flags; /* 0x03ac */ + __u64 percpu_offset; /* 0x03b0 */ + __u64 vdso_per_cpu_data; /* 0x03b8 */ + __u64 machine_flags; /* 0x03c0 */ + __u64 gmap; /* 0x03c8 */ + __u8 pad_0x03d0[0x0e00-0x03d0]; /* 0x03d0 */ /* * 0xe00 contains the address of the IPL Parameter Information @@ -193,14 +188,14 @@ extern struct lowcore *lowcore_ptr[]; static inline void set_prefix(__u32 address) { - asm volatile("spx %0" : : "m" (address) : "memory"); + asm volatile("spx %0" : : "Q" (address) : "memory"); } static inline __u32 store_prefix(void) { __u32 address; - asm volatile("stpx %0" : "=m" (address)); + asm volatile("stpx %0" : "=Q" (address)); return address; } diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h index cf4c1cb17dcd..f4a07f788f78 100644 --- a/arch/s390/include/asm/mmu_context.h +++ b/arch/s390/include/asm/mmu_context.h @@ -73,41 +73,38 @@ static inline int init_new_context(struct task_struct *tsk, static inline void set_user_asce(struct mm_struct *mm) { S390_lowcore.user_asce = mm->context.asce; - if (current->thread.mm_segment.ar4) - __ctl_load(S390_lowcore.user_asce, 7, 7); - set_cpu_flag(CIF_ASCE_PRIMARY); + __ctl_load(S390_lowcore.user_asce, 1, 1); + clear_cpu_flag(CIF_ASCE_PRIMARY); } static inline void clear_user_asce(void) { S390_lowcore.user_asce = S390_lowcore.kernel_asce; - - __ctl_load(S390_lowcore.user_asce, 1, 1); - __ctl_load(S390_lowcore.user_asce, 7, 7); -} - -static inline void load_kernel_asce(void) -{ - unsigned long asce; - - __ctl_store(asce, 1, 1); - if (asce != S390_lowcore.kernel_asce) - __ctl_load(S390_lowcore.kernel_asce, 1, 1); + __ctl_load(S390_lowcore.kernel_asce, 1, 1); set_cpu_flag(CIF_ASCE_PRIMARY); } +mm_segment_t enable_sacf_uaccess(void); +void disable_sacf_uaccess(mm_segment_t old_fs); + static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { int cpu = smp_processor_id(); - S390_lowcore.user_asce = next->context.asce; if (prev == next) return; + S390_lowcore.user_asce = next->context.asce; cpumask_set_cpu(cpu, &next->context.cpu_attach_mask); - /* Clear old ASCE by loading the kernel ASCE. */ - __ctl_load(S390_lowcore.kernel_asce, 1, 1); - __ctl_load(S390_lowcore.kernel_asce, 7, 7); + /* Clear previous user-ASCE from CR1 and CR7 */ + if (!test_cpu_flag(CIF_ASCE_PRIMARY)) { + __ctl_load(S390_lowcore.kernel_asce, 1, 1); + set_cpu_flag(CIF_ASCE_PRIMARY); + } + if (test_cpu_flag(CIF_ASCE_SECONDARY)) { + __ctl_load(S390_lowcore.vdso_asce, 7, 7); + clear_cpu_flag(CIF_ASCE_SECONDARY); + } cpumask_clear_cpu(cpu, &prev->context.cpu_attach_mask); } @@ -117,7 +114,6 @@ static inline void finish_arch_post_lock_switch(void) struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; - load_kernel_asce(); if (mm) { preempt_disable(); while (atomic_read(&mm->context.flush_count)) diff --git a/arch/s390/include/asm/perf_event.h b/arch/s390/include/asm/perf_event.h index 79aa6421fedb..d6c9d1e0dc2d 100644 --- a/arch/s390/include/asm/perf_event.h +++ b/arch/s390/include/asm/perf_event.h @@ -64,27 +64,10 @@ struct perf_sf_sde_regs { #define REG_OVERFLOW 1 #define OVERFLOW_REG(hwc) ((hwc)->extra_reg.config) #define SFB_ALLOC_REG(hwc) ((hwc)->extra_reg.alloc) -#define RAWSAMPLE_REG(hwc) ((hwc)->config) #define TEAR_REG(hwc) ((hwc)->last_tag) #define SAMPL_RATE(hwc) ((hwc)->event_base) #define SAMPL_FLAGS(hwc) ((hwc)->config_base) #define SAMPL_DIAG_MODE(hwc) (SAMPL_FLAGS(hwc) & PERF_CPUM_SF_DIAG_MODE) #define SDB_FULL_BLOCKS(hwc) (SAMPL_FLAGS(hwc) & PERF_CPUM_SF_FULL_BLOCKS) -/* Structure for sampling data entries to be passed as perf raw sample data - * to user space. Note that raw sample data must be aligned and, thus, might - * be padded with zeros. - */ -struct sf_raw_sample { -#define SF_RAW_SAMPLE_BASIC PERF_CPUM_SF_BASIC_MODE -#define SF_RAW_SAMPLE_DIAG PERF_CPUM_SF_DIAG_MODE - u64 format; - u32 size; /* Size of sf_raw_sample */ - u16 bsdes; /* Basic-sampling data entry size */ - u16 dsdes; /* Diagnostic-sampling data entry size */ - struct hws_basic_entry basic; /* Basic-sampling data entry */ - struct hws_diag_entry diag; /* Diagnostic-sampling data entry */ - u8 padding[]; /* Padding to next multiple of 8 */ -} __packed; - #endif /* _ASM_S390_PERF_EVENT_H */ diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index f25bfe888933..bfbfad482289 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -109,9 +109,7 @@ extern void execve_tail(void); #define HAVE_ARCH_PICK_MMAP_LAYOUT -typedef struct { - __u32 ar4; -} mm_segment_t; +typedef unsigned int mm_segment_t; /* * Thread structure @@ -247,7 +245,7 @@ static inline unsigned short stap(void) { unsigned short cpu_address; - asm volatile("stap %0" : "=m" (cpu_address)); + asm volatile("stap %0" : "=Q" (cpu_address)); return cpu_address; } diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 2f84e77f1f1b..a3788dafc0e1 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -13,10 +13,12 @@ #define PIF_SYSCALL 0 /* inside a system call */ #define PIF_PER_TRAP 1 /* deliver sigtrap on return to user */ #define PIF_SYSCALL_RESTART 2 /* restart the current system call */ +#define PIF_GUEST_FAULT 3 /* indicates program check in sie64a */ #define _PIF_SYSCALL _BITUL(PIF_SYSCALL) #define _PIF_PER_TRAP _BITUL(PIF_PER_TRAP) #define _PIF_SYSCALL_RESTART _BITUL(PIF_SYSCALL_RESTART) +#define _PIF_GUEST_FAULT _BITUL(PIF_GUEST_FAULT) #ifndef __ASSEMBLY__ diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 8bc87dcb10eb..2eb0c8a7b664 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -36,7 +36,7 @@ #define MACHINE_FLAG_SCC _BITUL(17) #define LPP_MAGIC _BITUL(31) -#define LPP_PFAULT_PID_MASK _AC(0xffffffff, UL) +#define LPP_PID_MASK _AC(0xffffffff, UL) #ifndef __ASSEMBLY__ diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index cdd0f0d999e2..ad6b91013a05 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -16,7 +16,7 @@ #include #include #include - +#include /* * The fs value determines whether argument validity checking should be @@ -26,27 +26,16 @@ * For historical reasons, these macros are grossly misnamed. */ -#define MAKE_MM_SEG(a) ((mm_segment_t) { (a) }) - - -#define KERNEL_DS MAKE_MM_SEG(0) -#define USER_DS MAKE_MM_SEG(1) +#define KERNEL_DS (0) +#define KERNEL_DS_SACF (1) +#define USER_DS (2) +#define USER_DS_SACF (3) #define get_ds() (KERNEL_DS) #define get_fs() (current->thread.mm_segment) -#define segment_eq(a,b) ((a).ar4 == (b).ar4) +#define segment_eq(a,b) (((a) & 2) == ((b) & 2)) -static inline void set_fs(mm_segment_t fs) -{ - current->thread.mm_segment = fs; - if (uaccess_kernel()) { - set_cpu_flag(CIF_ASCE_SECONDARY); - __ctl_load(S390_lowcore.kernel_asce, 7, 7); - } else { - clear_cpu_flag(CIF_ASCE_SECONDARY); - __ctl_load(S390_lowcore.user_asce, 7, 7); - } -} +void set_fs(mm_segment_t fs); static inline int __range_ok(unsigned long addr, unsigned long size) { @@ -95,7 +84,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n); static inline int __put_user_fn(void *x, void __user *ptr, unsigned long size) { - unsigned long spec = 0x810000UL; + unsigned long spec = 0x010000UL; int rc; switch (size) { @@ -125,7 +114,7 @@ static inline int __put_user_fn(void *x, void __user *ptr, unsigned long size) static inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size) { - unsigned long spec = 0x81UL; + unsigned long spec = 0x01UL; int rc; switch (size) { diff --git a/arch/s390/include/asm/vdso.h b/arch/s390/include/asm/vdso.h index ae6261ef97d5..169d7604eb80 100644 --- a/arch/s390/include/asm/vdso.h +++ b/arch/s390/include/asm/vdso.h @@ -46,6 +46,7 @@ struct vdso_per_cpu_data { }; extern struct vdso_data *vdso_data; +extern struct vdso_data boot_vdso_data; void vdso_alloc_boot_cpu(struct lowcore *lowcore); int vdso_alloc_per_cpu(struct lowcore *lowcore); diff --git a/arch/s390/include/uapi/asm/perf_regs.h b/arch/s390/include/uapi/asm/perf_regs.h new file mode 100644 index 000000000000..7c8564f98205 --- /dev/null +++ b/arch/s390/include/uapi/asm/perf_regs.h @@ -0,0 +1,43 @@ +#ifndef _ASM_S390_PERF_REGS_H +#define _ASM_S390_PERF_REGS_H + +enum perf_event_s390_regs { + PERF_REG_S390_R0, + PERF_REG_S390_R1, + PERF_REG_S390_R2, + PERF_REG_S390_R3, + PERF_REG_S390_R4, + PERF_REG_S390_R5, + PERF_REG_S390_R6, + PERF_REG_S390_R7, + PERF_REG_S390_R8, + PERF_REG_S390_R9, + PERF_REG_S390_R10, + PERF_REG_S390_R11, + PERF_REG_S390_R12, + PERF_REG_S390_R13, + PERF_REG_S390_R14, + PERF_REG_S390_R15, + PERF_REG_S390_FP0, + PERF_REG_S390_FP1, + PERF_REG_S390_FP2, + PERF_REG_S390_FP3, + PERF_REG_S390_FP4, + PERF_REG_S390_FP5, + PERF_REG_S390_FP6, + PERF_REG_S390_FP7, + PERF_REG_S390_FP8, + PERF_REG_S390_FP9, + PERF_REG_S390_FP10, + PERF_REG_S390_FP11, + PERF_REG_S390_FP12, + PERF_REG_S390_FP13, + PERF_REG_S390_FP14, + PERF_REG_S390_FP15, + PERF_REG_S390_MASK, + PERF_REG_S390_PC, + + PERF_REG_S390_MAX +}; + +#endif /* _ASM_S390_PERF_REGS_H */ diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 83bc82001c06..909bce65cb2b 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -59,7 +59,7 @@ obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o -obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o +obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o extra-y += head.o head64.o vmlinux.lds @@ -77,10 +77,9 @@ obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_UPROBES) += uprobes.o -obj-$(CONFIG_ALTERNATIVES) += alternative.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o -obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o +obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o obj-$(CONFIG_TRACEPOINTS) += trace.o diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 33ec80df7ed4..587b195b588d 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -171,6 +171,7 @@ int main(void) OFFSET(__LC_RESTART_DATA, lowcore, restart_data); OFFSET(__LC_RESTART_SOURCE, lowcore, restart_source); OFFSET(__LC_USER_ASCE, lowcore, user_asce); + OFFSET(__LC_VDSO_ASCE, lowcore, vdso_asce); OFFSET(__LC_LPP, lowcore, lpp); OFFSET(__LC_CURRENT_PID, lowcore, current_pid); OFFSET(__LC_PERCPU_OFFSET, lowcore, percpu_offset); @@ -178,7 +179,6 @@ int main(void) OFFSET(__LC_MACHINE_FLAGS, lowcore, machine_flags); OFFSET(__LC_PREEMPT_COUNT, lowcore, preempt_count); OFFSET(__LC_GMAP, lowcore, gmap); - OFFSET(__LC_PASTE, lowcore, paste); /* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */ OFFSET(__LC_DUMP_REIPL, lowcore, ipib); /* hardware defined lowcore locations 0x1000 - 0x18ff */ diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index a4a1208e3df3..ef246940b44c 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -50,19 +50,6 @@ typedef struct struct ucontext32 uc; } rt_sigframe32; -static inline void sigset_to_sigset32(unsigned long *set64, - compat_sigset_word *set32) -{ - set32[0] = (compat_sigset_word) set64[0]; - set32[1] = (compat_sigset_word)(set64[0] >> 32); -} - -static inline void sigset32_to_sigset(compat_sigset_word *set32, - unsigned long *set64) -{ - set64[0] = (unsigned long) set32[0] | ((unsigned long) set32[1] << 32); -} - int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) { int err; @@ -294,12 +281,10 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) { struct pt_regs *regs = task_pt_regs(current); sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; - compat_sigset_t cset; sigset_t set; - if (__copy_from_user(&cset.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) + if (get_compat_sigset(&set, (compat_sigset_t __user *)frame->sc.oldmask)) goto badframe; - sigset32_to_sigset(cset.sig, set.sig); set_current_blocked(&set); save_fpu_regs(); if (restore_sigregs32(regs, &frame->sregs)) @@ -317,12 +302,10 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) { struct pt_regs *regs = task_pt_regs(current); rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; - compat_sigset_t cset; sigset_t set; - if (__copy_from_user(&cset, &frame->uc.uc_sigmask, sizeof(cset))) + if (get_compat_sigset(&set, &frame->uc.uc_sigmask)) goto badframe; - sigset32_to_sigset(cset.sig, set.sig); set_current_blocked(&set); if (compat_restore_altstack(&frame->uc.uc_stack)) goto badframe; @@ -372,7 +355,6 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, { int sig = ksig->sig; sigframe32 __user *frame; - struct sigcontext32 sc; unsigned long restorer; size_t frame_size; @@ -394,9 +376,10 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, return -EFAULT; /* Create struct sigcontext32 on the signal stack */ - sigset_to_sigset32(set->sig, sc.oldmask); - sc.sregs = (__u32)(unsigned long __force) &frame->sregs; - if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) + if (put_compat_sigset((compat_sigset_t __user *)frame->sc.oldmask, + set, sizeof(compat_sigset_t))) + return -EFAULT; + if (__put_user(ptr_to_compat(&frame->sc), &frame->sc.sregs)) return -EFAULT; /* Store registers needed to create the signal frame */ @@ -455,7 +438,6 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - compat_sigset_t cset; rt_sigframe32 __user *frame; unsigned long restorer; size_t frame_size; @@ -502,12 +484,11 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, store_sigregs(); /* Create ucontext on the signal stack. */ - sigset_to_sigset32(set->sig, cset.sig); if (__put_user(uc_flags, &frame->uc.uc_flags) || __put_user(0, &frame->uc.uc_link) || __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || save_sigregs32(regs, &frame->uc.uc_mcontext) || - __copy_to_user(&frame->uc.uc_sigmask, &cset, sizeof(cset)) || + put_compat_sigset(&frame->uc.uc_sigmask, set, sizeof(compat_sigset_t)) || save_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) return -EFAULT; diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index b811d3a8417d..3be829721cf9 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -480,7 +480,7 @@ void show_code(struct pt_regs *regs) { char *mode = user_mode(regs) ? "User" : "Krnl"; unsigned char code[64]; - char buffer[64], *ptr; + char buffer[128], *ptr; mm_segment_t old_fs; unsigned long addr; int start, end, opsize, hops, i; @@ -543,7 +543,7 @@ void show_code(struct pt_regs *regs) start += opsize; pr_cont("%s", buffer); ptr = buffer; - ptr += sprintf(ptr, "\n "); + ptr += sprintf(ptr, "\n\t "); hops++; } pr_cont("\n"); diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index f498d201f98d..a316cd6999ad 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -379,13 +379,21 @@ ENTRY(system_call) jg s390_handle_mcck # TIF bit will be cleared by handler # -# _CIF_ASCE_PRIMARY and/or CIF_ASCE_SECONDARY set, load user space asce +# _CIF_ASCE_PRIMARY and/or _CIF_ASCE_SECONDARY set, load user space asce # .Lsysc_asce: + ni __LC_CPU_FLAGS+7,255-_CIF_ASCE_SECONDARY + lctlg %c7,%c7,__LC_VDSO_ASCE # load secondary asce + TSTMSK __LC_CPU_FLAGS,_CIF_ASCE_PRIMARY + jz .Lsysc_return +#ifndef CONFIG_HAVE_MARCH_Z10_FEATURES + tm __LC_STFLE_FAC_LIST+3,0x10 # has MVCOS ? + jnz .Lsysc_set_fs_fixup ni __LC_CPU_FLAGS+7,255-_CIF_ASCE_PRIMARY lctlg %c1,%c1,__LC_USER_ASCE # load primary asce - TSTMSK __LC_CPU_FLAGS,_CIF_ASCE_SECONDARY - jz .Lsysc_return + j .Lsysc_return +.Lsysc_set_fs_fixup: +#endif larl %r14,.Lsysc_return jg set_fs_fixup @@ -518,6 +526,7 @@ ENTRY(pgm_check_handler) stmg %r8,%r15,__LC_SAVE_AREA_SYNC lg %r10,__LC_LAST_BREAK lg %r12,__LC_CURRENT + lghi %r11,0 larl %r13,cleanup_critical lmg %r8,%r9,__LC_PGM_OLD_PSW tmhh %r8,0x0001 # test problem state bit @@ -532,6 +541,7 @@ ENTRY(pgm_check_handler) ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE lctlg %c1,%c1,__LC_USER_ASCE # load primary asce larl %r9,sie_exit # skip forward to sie_exit + lghi %r11,_PIF_GUEST_FAULT #endif 0: tmhh %r8,0x4000 # PER bit set in old PSW ? jnz 1f # -> enabled, can't be a double fault @@ -549,13 +559,14 @@ ENTRY(pgm_check_handler) jz 3f mvc __THREAD_trap_tdb(256,%r14),0(%r13) 3: stg %r10,__THREAD_last_break(%r14) -4: la %r11,STACK_FRAME_OVERHEAD(%r15) +4: lgr %r13,%r11 + la %r11,STACK_FRAME_OVERHEAD(%r15) stmg %r0,%r7,__PT_R0(%r11) mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC stmg %r8,%r9,__PT_PSW(%r11) mvc __PT_INT_CODE(4,%r11),__LC_PGM_ILC mvc __PT_INT_PARM_LONG(8,%r11),__LC_TRANS_EXC_CODE - xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11) + stg %r13,__PT_FLAGS(%r11) stg %r10,__PT_ARGS(%r11) tm __LC_PGM_ILC+3,0x80 # check for per exception jz 5f @@ -738,10 +749,18 @@ ENTRY(io_int_handler) # _CIF_ASCE_PRIMARY and/or CIF_ASCE_SECONDARY set, load user space asce # .Lio_asce: + ni __LC_CPU_FLAGS+7,255-_CIF_ASCE_SECONDARY + lctlg %c7,%c7,__LC_VDSO_ASCE # load secondary asce + TSTMSK __LC_CPU_FLAGS,_CIF_ASCE_PRIMARY + jz .Lio_return +#ifndef CONFIG_HAVE_MARCH_Z10_FEATURES + tm __LC_STFLE_FAC_LIST+3,0x10 # has MVCOS ? + jnz .Lio_set_fs_fixup ni __LC_CPU_FLAGS+7,255-_CIF_ASCE_PRIMARY lctlg %c1,%c1,__LC_USER_ASCE # load primary asce - TSTMSK __LC_CPU_FLAGS,_CIF_ASCE_SECONDARY - jz .Lio_return + j .Lio_return +.Lio_set_fs_fixup: +#endif larl %r14,.Lio_return jg set_fs_fixup diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 172002da7075..38a973ccf501 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -28,7 +28,7 @@ ENTRY(startup_continue) lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area # move IPL device to lowcore - lghi %r0,__LC_PASTE + larl %r0,boot_vdso_data stg %r0,__LC_VDSO_PER_CPU # # Setup stack diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 6d9f73bb4142..7b87991416fd 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -433,16 +433,13 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *s; char *secstrings; - if (IS_ENABLED(CONFIG_ALTERNATIVES)) { - secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; - for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { - if (!strcmp(".altinstructions", - secstrings + s->sh_name)) { - /* patch .altinstructions */ - void *aseg = (void *)s->sh_addr; + secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { + if (!strcmp(".altinstructions", secstrings + s->sh_name)) { + /* patch .altinstructions */ + void *aseg = (void *)s->sh_addr; - apply_alternatives(aseg, aseg + s->sh_size); - } + apply_alternatives(aseg, aseg + s->sh_size); } } diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 3f3cda41f32a..6ff169253cae 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -191,7 +191,6 @@ static int notrace s390_check_registers(union mci mci, int umode) { union ctlreg2 cr2; int kill_task; - void *fpt_save_area; kill_task = 0; @@ -224,7 +223,6 @@ static int notrace s390_check_registers(union mci mci, int umode) if (!test_cpu_flag(CIF_FPU)) kill_task = 1; } - fpt_save_area = &S390_lowcore.floating_pt_save_area; if (!mci.fc) { /* * Floating point control register can't be restored. diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index bd4bbf61aaf3..227b38bd82c9 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,15 @@ struct sf_buffer { unsigned long *tail; /* last sample-data-block-table */ }; +struct aux_buffer { + struct sf_buffer sfb; + unsigned long head; /* index of SDB of buffer head */ + unsigned long alert_mark; /* index of SDB of alert request position */ + unsigned long empty_mark; /* mark of SDB not marked full */ + unsigned long *sdb_index; /* SDB address for fast lookup */ + unsigned long *sdbt_index; /* SDBT address for fast lookup */ +}; + struct cpu_hw_sf { /* CPU-measurement sampling information block */ struct hws_qsi_info_block qsi; @@ -85,6 +95,7 @@ struct cpu_hw_sf { struct sf_buffer sfb; /* Sampling buffer */ unsigned int flags; /* Status flags */ struct perf_event *event; /* Scheduled perf event */ + struct perf_output_handle handle; /* AUX buffer output handle */ }; static DEFINE_PER_CPU(struct cpu_hw_sf, cpu_hw_sf); @@ -341,22 +352,6 @@ static void sfb_init_allocs(unsigned long num, struct hw_perf_event *hwc) sfb_account_allocs(num, hwc); } -static size_t event_sample_size(struct hw_perf_event *hwc) -{ - struct sf_raw_sample *sfr = (struct sf_raw_sample *) RAWSAMPLE_REG(hwc); - size_t sample_size; - - /* The sample size depends on the sampling function: The basic-sampling - * function must be always enabled, diagnostic-sampling function is - * optional. - */ - sample_size = sfr->bsdes; - if (SAMPL_DIAG_MODE(hwc)) - sample_size += sfr->dsdes; - - return sample_size; -} - static void deallocate_buffers(struct cpu_hw_sf *cpuhw) { if (cpuhw->sfb.sdbt) @@ -366,35 +361,7 @@ static void deallocate_buffers(struct cpu_hw_sf *cpuhw) static int allocate_buffers(struct cpu_hw_sf *cpuhw, struct hw_perf_event *hwc) { unsigned long n_sdb, freq, factor; - size_t sfr_size, sample_size; - struct sf_raw_sample *sfr; - - /* Allocate raw sample buffer - * - * The raw sample buffer is used to temporarily store sampling data - * entries for perf raw sample processing. The buffer size mainly - * depends on the size of diagnostic-sampling data entries which is - * machine-specific. The exact size calculation includes: - * 1. The first 4 bytes of diagnostic-sampling data entries are - * already reflected in the sf_raw_sample structure. Subtract - * these bytes. - * 2. The perf raw sample data must be 8-byte aligned (u64) and - * perf's internal data size must be considered too. So add - * an additional u32 for correct alignment and subtract before - * allocating the buffer. - * 3. Store the raw sample buffer pointer in the perf event - * hardware structure. - */ - sfr_size = ALIGN((sizeof(*sfr) - sizeof(sfr->diag) + cpuhw->qsi.dsdes) + - sizeof(u32), sizeof(u64)); - sfr_size -= sizeof(u32); - sfr = kzalloc(sfr_size, GFP_KERNEL); - if (!sfr) - return -ENOMEM; - sfr->size = sfr_size; - sfr->bsdes = cpuhw->qsi.bsdes; - sfr->dsdes = cpuhw->qsi.dsdes; - RAWSAMPLE_REG(hwc) = (unsigned long) sfr; + size_t sample_size; /* Calculate sampling buffers using 4K pages * @@ -420,7 +387,7 @@ static int allocate_buffers(struct cpu_hw_sf *cpuhw, struct hw_perf_event *hwc) * ensure a minimum of CPUM_SF_MIN_SDBT (one table can manage up * to 511 SDBs). */ - sample_size = event_sample_size(hwc); + sample_size = sizeof(struct hws_basic_entry); freq = sample_rate_to_freq(&cpuhw->qsi, SAMPL_RATE(hwc)); factor = 1; n_sdb = DIV_ROUND_UP(freq, factor * ((PAGE_SIZE-64) / sample_size)); @@ -619,10 +586,6 @@ static int reserve_pmc_hardware(void) static void hw_perf_event_destroy(struct perf_event *event) { - /* Free raw sample buffer */ - if (RAWSAMPLE_REG(&event->hw)) - kfree((void *) RAWSAMPLE_REG(&event->hw)); - /* Release PMC if this is the last perf event */ if (!atomic_add_unless(&num_events, -1, 1)) { mutex_lock(&pmc_reserve_mutex); @@ -642,15 +605,8 @@ static void hw_init_period(struct hw_perf_event *hwc, u64 period) static void hw_reset_registers(struct hw_perf_event *hwc, unsigned long *sdbt_origin) { - struct sf_raw_sample *sfr; - /* (Re)set to first sample-data-block-table */ TEAR_REG(hwc) = (unsigned long) sdbt_origin; - - /* (Re)set raw sampling buffer register */ - sfr = (struct sf_raw_sample *) RAWSAMPLE_REG(hwc); - memset(&sfr->basic, 0, sizeof(sfr->basic)); - memset(&sfr->diag, 0, sfr->dsdes); } static unsigned long hw_limit_rate(const struct hws_qsi_info_block *si, @@ -660,6 +616,67 @@ static unsigned long hw_limit_rate(const struct hws_qsi_info_block *si, si->min_sampl_rate, si->max_sampl_rate); } +static u32 cpumsf_pid_type(struct perf_event *event, + u32 pid, enum pid_type type) +{ + struct task_struct *tsk; + + /* Idle process */ + if (!pid) + goto out; + + tsk = find_task_by_pid_ns(pid, &init_pid_ns); + pid = -1; + if (tsk) { + /* + * Only top level events contain the pid namespace in which + * they are created. + */ + if (event->parent) + event = event->parent; + pid = __task_pid_nr_ns(tsk, type, event->ns); + /* + * See also 1d953111b648 + * "perf/core: Don't report zero PIDs for exiting tasks". + */ + if (!pid && !pid_alive(tsk)) + pid = -1; + } +out: + return pid; +} + +static void cpumsf_output_event_pid(struct perf_event *event, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + u32 pid; + struct perf_event_header header; + struct perf_output_handle handle; + + /* + * Obtain the PID from the basic-sampling data entry and + * correct the data->tid_entry.pid value. + */ + pid = data->tid_entry.pid; + + /* Protect callchain buffers, tasks */ + rcu_read_lock(); + + perf_prepare_sample(&header, data, event, regs); + if (perf_output_begin(&handle, event, header.size)) + goto out; + + /* Update the process ID (see also kernel/events/core.c) */ + data->tid_entry.pid = cpumsf_pid_type(event, pid, __PIDTYPE_TGID); + data->tid_entry.tid = cpumsf_pid_type(event, pid, PIDTYPE_PID); + + perf_output_sample(&handle, &header, data, event); + perf_output_end(&handle); +out: + rcu_read_unlock(); +} + static int __hw_perf_event_init(struct perf_event *event) { struct cpu_hw_sf *cpuhw; @@ -770,6 +787,10 @@ static int __hw_perf_event_init(struct perf_event *event) hwc->extra_reg.reg = REG_OVERFLOW; OVERFLOW_REG(hwc) = 0; + /* Use AUX buffer. No need to allocate it by ourself */ + if (attr->config == PERF_EVENT_CPUM_SF_DIAG) + return 0; + /* Allocate the per-CPU sampling buffer using the CPU information * from the event. If the event is not pinned to a particular * CPU (event->cpu == -1; or cpuhw == NULL), allocate sampling @@ -789,6 +810,14 @@ static int __hw_perf_event_init(struct perf_event *event) break; } } + + /* If PID/TID sampling is active, replace the default overflow + * handler to extract and resolve the PIDs from the basic-sampling + * data entries. + */ + if (event->attr.sample_type & PERF_SAMPLE_TID) + if (is_default_overflow_handler(event)) + event->overflow_handler = cpumsf_output_event_pid; out: return err; } @@ -866,10 +895,15 @@ static void cpumsf_pmu_enable(struct pmu *pmu) */ if (cpuhw->event) { hwc = &cpuhw->event->hw; - /* Account number of overflow-designated buffer extents */ - sfb_account_overflows(cpuhw, hwc); - if (sfb_has_pending_allocs(&cpuhw->sfb, hwc)) - extend_sampling_buffer(&cpuhw->sfb, hwc); + if (!(SAMPL_DIAG_MODE(hwc))) { + /* + * Account number of overflow-designated + * buffer extents + */ + sfb_account_overflows(cpuhw, hwc); + if (sfb_has_pending_allocs(&cpuhw->sfb, hwc)) + extend_sampling_buffer(&cpuhw->sfb, hwc); + } } /* (Re)enable the PMU and sampling facility */ @@ -884,6 +918,9 @@ static void cpumsf_pmu_enable(struct pmu *pmu) return; } + /* Load current program parameter */ + lpp(&S390_lowcore.lpp); + debug_sprintf_event(sfdbg, 6, "pmu_enable: es=%i cs=%i ed=%i cd=%i " "tear=%p dear=%p\n", cpuhw->lsctl.es, cpuhw->lsctl.cs, cpuhw->lsctl.ed, cpuhw->lsctl.cd, @@ -967,22 +1004,16 @@ static int perf_exclude_event(struct perf_event *event, struct pt_regs *regs, * * Return non-zero if an event overflow occurred. */ -static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr) +static int perf_push_sample(struct perf_event *event, + struct hws_basic_entry *basic) { int overflow; struct pt_regs regs; struct perf_sf_sde_regs *sde_regs; struct perf_sample_data data; - struct perf_raw_record raw = { - .frag = { - .size = sfr->size, - .data = sfr, - }, - }; /* Setup perf sample */ perf_sample_data_init(&data, 0, event->hw.last_period); - data.raw = &raw; /* Setup pt_regs to look like an CPU-measurement external interrupt * using the Program Request Alert code. The regs.int_parm_long @@ -994,11 +1025,11 @@ static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr) regs.int_parm = CPU_MF_INT_SF_PRA; sde_regs = (struct perf_sf_sde_regs *) ®s.int_parm_long; - psw_bits(regs.psw).ia = sfr->basic.ia; - psw_bits(regs.psw).dat = sfr->basic.T; - psw_bits(regs.psw).wait = sfr->basic.W; - psw_bits(regs.psw).pstate = sfr->basic.P; - psw_bits(regs.psw).as = sfr->basic.AS; + psw_bits(regs.psw).ia = basic->ia; + psw_bits(regs.psw).dat = basic->T; + psw_bits(regs.psw).wait = basic->W; + psw_bits(regs.psw).pstate = basic->P; + psw_bits(regs.psw).as = basic->AS; /* * Use the hardware provided configuration level to decide if the @@ -1011,7 +1042,7 @@ static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr) * If the value differs from 0xffff (the host value), we assume to * be a KVM guest. */ - switch (sfr->basic.CL) { + switch (basic->CL) { case 1: /* logical partition */ sde_regs->in_guest = 0; break; @@ -1019,11 +1050,17 @@ static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr) sde_regs->in_guest = 1; break; default: /* old machine, use heuristics */ - if (sfr->basic.gpp || sfr->basic.prim_asn != 0xffff) + if (basic->gpp || basic->prim_asn != 0xffff) sde_regs->in_guest = 1; break; } + /* + * Store the PID value from the sample-data-entry to be + * processed and resolved by cpumsf_output_event_pid(). + */ + data.tid_entry.pid = basic->hpp & LPP_PID_MASK; + overflow = 0; if (perf_exclude_event(event, ®s, sde_regs)) goto out; @@ -1041,75 +1078,12 @@ static void perf_event_count_update(struct perf_event *event, u64 count) local64_add(count, &event->count); } -static int sample_format_is_valid(struct hws_combined_entry *sample, - unsigned int flags) -{ - if (likely(flags & PERF_CPUM_SF_BASIC_MODE)) - /* Only basic-sampling data entries with data-entry-format - * version of 0x0001 can be processed. - */ - if (sample->basic.def != 0x0001) - return 0; - if (flags & PERF_CPUM_SF_DIAG_MODE) - /* The data-entry-format number of diagnostic-sampling data - * entries can vary. Because diagnostic data is just passed - * through, do only a sanity check on the DEF. - */ - if (sample->diag.def < 0x8001) - return 0; - return 1; -} - -static int sample_is_consistent(struct hws_combined_entry *sample, - unsigned long flags) -{ - /* This check applies only to basic-sampling data entries of potentially - * combined-sampling data entries. Invalid entries cannot be processed - * by the PMU and, thus, do not deliver an associated - * diagnostic-sampling data entry. - */ - if (unlikely(!(flags & PERF_CPUM_SF_BASIC_MODE))) - return 0; - /* - * Samples are skipped, if they are invalid or for which the - * instruction address is not predictable, i.e., the wait-state bit is - * set. - */ - if (sample->basic.I || sample->basic.W) - return 0; - return 1; -} - -static void reset_sample_slot(struct hws_combined_entry *sample, - unsigned long flags) -{ - if (likely(flags & PERF_CPUM_SF_BASIC_MODE)) - sample->basic.def = 0; - if (flags & PERF_CPUM_SF_DIAG_MODE) - sample->diag.def = 0; -} - -static void sfr_store_sample(struct sf_raw_sample *sfr, - struct hws_combined_entry *sample) -{ - if (likely(sfr->format & PERF_CPUM_SF_BASIC_MODE)) - sfr->basic = sample->basic; - if (sfr->format & PERF_CPUM_SF_DIAG_MODE) - memcpy(&sfr->diag, &sample->diag, sfr->dsdes); -} - -static void debug_sample_entry(struct hws_combined_entry *sample, - struct hws_trailer_entry *te, - unsigned long flags) +static void debug_sample_entry(struct hws_basic_entry *sample, + struct hws_trailer_entry *te) { debug_sprintf_event(sfdbg, 4, "hw_collect_samples: Found unknown " - "sampling data entry: te->f=%i basic.def=%04x (%p)" - " diag.def=%04x (%p)\n", te->f, - sample->basic.def, &sample->basic, - (flags & PERF_CPUM_SF_DIAG_MODE) - ? sample->diag.def : 0xFFFF, - (flags & PERF_CPUM_SF_DIAG_MODE) - ? &sample->diag : NULL); + "sampling data entry: te->f=%i basic.def=%04x (%p)\n", + te->f, sample->def, sample); } /* hw_collect_samples() - Walk through a sample-data-block and collect samples @@ -1135,44 +1109,37 @@ static void debug_sample_entry(struct hws_combined_entry *sample, static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt, unsigned long long *overflow) { - unsigned long flags = SAMPL_FLAGS(&event->hw); - struct hws_combined_entry *sample; struct hws_trailer_entry *te; - struct sf_raw_sample *sfr; - size_t sample_size; - - /* Prepare and initialize raw sample data */ - sfr = (struct sf_raw_sample *) RAWSAMPLE_REG(&event->hw); - sfr->format = flags & PERF_CPUM_SF_MODE_MASK; + struct hws_basic_entry *sample; - sample_size = event_sample_size(&event->hw); te = (struct hws_trailer_entry *) trailer_entry_ptr(*sdbt); - sample = (struct hws_combined_entry *) *sdbt; + sample = (struct hws_basic_entry *) *sdbt; while ((unsigned long *) sample < (unsigned long *) te) { /* Check for an empty sample */ - if (!sample->basic.def) + if (!sample->def) break; /* Update perf event period */ perf_event_count_update(event, SAMPL_RATE(&event->hw)); - /* Check sampling data entry */ - if (sample_format_is_valid(sample, flags)) { + /* Check whether sample is valid */ + if (sample->def == 0x0001) { /* If an event overflow occurred, the PMU is stopped to * throttle event delivery. Remaining sample data is * discarded. */ if (!*overflow) { - if (sample_is_consistent(sample, flags)) { + /* Check whether sample is consistent */ + if (sample->I == 0 && sample->W == 0) { /* Deliver sample data to perf */ - sfr_store_sample(sfr, sample); - *overflow = perf_push_sample(event, sfr); + *overflow = perf_push_sample(event, + sample); } } else /* Count discarded samples */ *overflow += 1; } else { - debug_sample_entry(sample, te, flags); + debug_sample_entry(sample, te); /* Sample slot is not yet written or other record. * * This condition can occur if the buffer was reused @@ -1188,8 +1155,8 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt, } /* Reset sample slot and advance to next sample */ - reset_sample_slot(sample, flags); - sample += sample_size; + sample->def = 0; + sample++; } } @@ -1215,6 +1182,13 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) unsigned long long event_overflow, sampl_overflow, num_sdb, te_flags; int done; + /* + * AUX buffer is used when in diagnostic sampling mode. + * No perf events/samples are created. + */ + if (SAMPL_DIAG_MODE(&event->hw)) + return; + if (flush_all && SDB_FULL_BLOCKS(hwc)) flush_all = 0; @@ -1291,6 +1265,439 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) sampl_overflow, event_overflow); } +#define AUX_SDB_INDEX(aux, i) ((i) % aux->sfb.num_sdb) +#define AUX_SDB_NUM(aux, start, end) (end >= start ? end - start + 1 : 0) +#define AUX_SDB_NUM_ALERT(aux) AUX_SDB_NUM(aux, aux->head, aux->alert_mark) +#define AUX_SDB_NUM_EMPTY(aux) AUX_SDB_NUM(aux, aux->head, aux->empty_mark) + +/* + * Get trailer entry by index of SDB. + */ +static struct hws_trailer_entry *aux_sdb_trailer(struct aux_buffer *aux, + unsigned long index) +{ + unsigned long sdb; + + index = AUX_SDB_INDEX(aux, index); + sdb = aux->sdb_index[index]; + return (struct hws_trailer_entry *)trailer_entry_ptr(sdb); +} + +/* + * Finish sampling on the cpu. Called by cpumsf_pmu_del() with pmu + * disabled. Collect the full SDBs in AUX buffer which have not reached + * the point of alert indicator. And ignore the SDBs which are not + * full. + * + * 1. Scan SDBs to see how much data is there and consume them. + * 2. Remove alert indicator in the buffer. + */ +static void aux_output_end(struct perf_output_handle *handle) +{ + unsigned long i, range_scan, idx; + struct aux_buffer *aux; + struct hws_trailer_entry *te; + + aux = perf_get_aux(handle); + if (!aux) + return; + + range_scan = AUX_SDB_NUM_ALERT(aux); + for (i = 0, idx = aux->head; i < range_scan; i++, idx++) { + te = aux_sdb_trailer(aux, idx); + if (!(te->flags & SDB_TE_BUFFER_FULL_MASK)) + break; + } + /* i is num of SDBs which are full */ + perf_aux_output_end(handle, i << PAGE_SHIFT); + + /* Remove alert indicators in the buffer */ + te = aux_sdb_trailer(aux, aux->alert_mark); + te->flags &= ~SDB_TE_ALERT_REQ_MASK; + + debug_sprintf_event(sfdbg, 6, "aux_output_end: collect %lx SDBs\n", i); +} + +/* + * Start sampling on the CPU. Called by cpumsf_pmu_add() when an event + * is first added to the CPU or rescheduled again to the CPU. It is called + * with pmu disabled. + * + * 1. Reset the trailer of SDBs to get ready for new data. + * 2. Tell the hardware where to put the data by reset the SDBs buffer + * head(tear/dear). + */ +static int aux_output_begin(struct perf_output_handle *handle, + struct aux_buffer *aux, + struct cpu_hw_sf *cpuhw) +{ + unsigned long range; + unsigned long i, range_scan, idx; + unsigned long head, base, offset; + struct hws_trailer_entry *te; + + if (WARN_ON_ONCE(handle->head & ~PAGE_MASK)) + return -EINVAL; + + aux->head = handle->head >> PAGE_SHIFT; + range = (handle->size + 1) >> PAGE_SHIFT; + if (range <= 1) + return -ENOMEM; + + /* + * SDBs between aux->head and aux->empty_mark are already ready + * for new data. range_scan is num of SDBs not within them. + */ + if (range > AUX_SDB_NUM_EMPTY(aux)) { + range_scan = range - AUX_SDB_NUM_EMPTY(aux); + idx = aux->empty_mark + 1; + for (i = 0; i < range_scan; i++, idx++) { + te = aux_sdb_trailer(aux, idx); + te->flags = te->flags & ~SDB_TE_BUFFER_FULL_MASK; + te->flags = te->flags & ~SDB_TE_ALERT_REQ_MASK; + te->overflow = 0; + } + /* Save the position of empty SDBs */ + aux->empty_mark = aux->head + range - 1; + } + + /* Set alert indicator */ + aux->alert_mark = aux->head + range/2 - 1; + te = aux_sdb_trailer(aux, aux->alert_mark); + te->flags = te->flags | SDB_TE_ALERT_REQ_MASK; + + /* Reset hardware buffer head */ + head = AUX_SDB_INDEX(aux, aux->head); + base = aux->sdbt_index[head / CPUM_SF_SDB_PER_TABLE]; + offset = head % CPUM_SF_SDB_PER_TABLE; + cpuhw->lsctl.tear = base + offset * sizeof(unsigned long); + cpuhw->lsctl.dear = aux->sdb_index[head]; + + debug_sprintf_event(sfdbg, 6, "aux_output_begin: " + "head->alert_mark->empty_mark (num_alert, range)" + "[%lx -> %lx -> %lx] (%lx, %lx) " + "tear index %lx, tear %lx dear %lx\n", + aux->head, aux->alert_mark, aux->empty_mark, + AUX_SDB_NUM_ALERT(aux), range, + head / CPUM_SF_SDB_PER_TABLE, + cpuhw->lsctl.tear, + cpuhw->lsctl.dear); + + return 0; +} + +/* + * Set alert indicator on SDB at index @alert_index while sampler is running. + * + * Return true if successfully. + * Return false if full indicator is already set by hardware sampler. + */ +static bool aux_set_alert(struct aux_buffer *aux, unsigned long alert_index, + unsigned long long *overflow) +{ + unsigned long long orig_overflow, orig_flags, new_flags; + struct hws_trailer_entry *te; + + te = aux_sdb_trailer(aux, alert_index); + do { + orig_flags = te->flags; + orig_overflow = te->overflow; + *overflow = orig_overflow; + if (orig_flags & SDB_TE_BUFFER_FULL_MASK) { + /* + * SDB is already set by hardware. + * Abort and try to set somewhere + * behind. + */ + return false; + } + new_flags = orig_flags | SDB_TE_ALERT_REQ_MASK; + } while (!cmpxchg_double(&te->flags, &te->overflow, + orig_flags, orig_overflow, + new_flags, 0ULL)); + return true; +} + +/* + * aux_reset_buffer() - Scan and setup SDBs for new samples + * @aux: The AUX buffer to set + * @range: The range of SDBs to scan started from aux->head + * @overflow: Set to overflow count + * + * Set alert indicator on the SDB at index of aux->alert_mark. If this SDB is + * marked as empty, check if it is already set full by the hardware sampler. + * If yes, that means new data is already there before we can set an alert + * indicator. Caller should try to set alert indicator to some position behind. + * + * Scan the SDBs in AUX buffer from behind aux->empty_mark. They are used + * previously and have already been consumed by user space. Reset these SDBs + * (clear full indicator and alert indicator) for new data. + * If aux->alert_mark fall in this area, just set it. Overflow count is + * recorded while scanning. + * + * SDBs between aux->head and aux->empty_mark are already reset at last time. + * and ready for new samples. So scanning on this area could be skipped. + * + * Return true if alert indicator is set successfully and false if not. + */ +static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range, + unsigned long long *overflow) +{ + unsigned long long orig_overflow, orig_flags, new_flags; + unsigned long i, range_scan, idx; + struct hws_trailer_entry *te; + + if (range <= AUX_SDB_NUM_EMPTY(aux)) + /* + * No need to scan. All SDBs in range are marked as empty. + * Just set alert indicator. Should check race with hardware + * sampler. + */ + return aux_set_alert(aux, aux->alert_mark, overflow); + + if (aux->alert_mark <= aux->empty_mark) + /* + * Set alert indicator on empty SDB. Should check race + * with hardware sampler. + */ + if (!aux_set_alert(aux, aux->alert_mark, overflow)) + return false; + + /* + * Scan the SDBs to clear full and alert indicator used previously. + * Start scanning from one SDB behind empty_mark. If the new alert + * indicator fall into this range, set it. + */ + range_scan = range - AUX_SDB_NUM_EMPTY(aux); + idx = aux->empty_mark + 1; + for (i = 0; i < range_scan; i++, idx++) { + te = aux_sdb_trailer(aux, idx); + do { + orig_flags = te->flags; + orig_overflow = te->overflow; + new_flags = orig_flags & ~SDB_TE_BUFFER_FULL_MASK; + if (idx == aux->alert_mark) + new_flags |= SDB_TE_ALERT_REQ_MASK; + else + new_flags &= ~SDB_TE_ALERT_REQ_MASK; + } while (!cmpxchg_double(&te->flags, &te->overflow, + orig_flags, orig_overflow, + new_flags, 0ULL)); + *overflow += orig_overflow; + } + + /* Update empty_mark to new position */ + aux->empty_mark = aux->head + range - 1; + + return true; +} + +/* + * Measurement alert handler for diagnostic mode sampling. + */ +static void hw_collect_aux(struct cpu_hw_sf *cpuhw) +{ + struct aux_buffer *aux; + int done = 0; + unsigned long range = 0, size; + unsigned long long overflow = 0; + struct perf_output_handle *handle = &cpuhw->handle; + unsigned long num_sdb; + + aux = perf_get_aux(handle); + if (WARN_ON_ONCE(!aux)) + return; + + /* Inform user space new data arrived */ + size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT; + perf_aux_output_end(handle, size); + num_sdb = aux->sfb.num_sdb; + + while (!done) { + /* Get an output handle */ + aux = perf_aux_output_begin(handle, cpuhw->event); + if (handle->size == 0) { + pr_err("The AUX buffer with %lu pages for the " + "diagnostic-sampling mode is full\n", + num_sdb); + debug_sprintf_event(sfdbg, 1, "AUX buffer used up\n"); + break; + } + if (WARN_ON_ONCE(!aux)) + return; + + /* Update head and alert_mark to new position */ + aux->head = handle->head >> PAGE_SHIFT; + range = (handle->size + 1) >> PAGE_SHIFT; + if (range == 1) + aux->alert_mark = aux->head; + else + aux->alert_mark = aux->head + range/2 - 1; + + if (aux_reset_buffer(aux, range, &overflow)) { + if (!overflow) { + done = 1; + break; + } + size = range << PAGE_SHIFT; + perf_aux_output_end(&cpuhw->handle, size); + pr_err("Sample data caused the AUX buffer with %lu " + "pages to overflow\n", num_sdb); + debug_sprintf_event(sfdbg, 1, "head %lx range %lx " + "overflow %llx\n", + aux->head, range, overflow); + } else { + size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT; + perf_aux_output_end(&cpuhw->handle, size); + debug_sprintf_event(sfdbg, 6, "head %lx alert %lx " + "already full, try another\n", + aux->head, aux->alert_mark); + } + } + + if (done) + debug_sprintf_event(sfdbg, 6, "aux_reset_buffer: " + "[%lx -> %lx -> %lx] (%lx, %lx)\n", + aux->head, aux->alert_mark, aux->empty_mark, + AUX_SDB_NUM_ALERT(aux), range); +} + +/* + * Callback when freeing AUX buffers. + */ +static void aux_buffer_free(void *data) +{ + struct aux_buffer *aux = data; + unsigned long i, num_sdbt; + + if (!aux) + return; + + /* Free SDBT. SDB is freed by the caller */ + num_sdbt = aux->sfb.num_sdbt; + for (i = 0; i < num_sdbt; i++) + free_page(aux->sdbt_index[i]); + + kfree(aux->sdbt_index); + kfree(aux->sdb_index); + kfree(aux); + + debug_sprintf_event(sfdbg, 4, "aux_buffer_free: free " + "%lu SDBTs\n", num_sdbt); +} + +/* + * aux_buffer_setup() - Setup AUX buffer for diagnostic mode sampling + * @cpu: On which to allocate, -1 means current + * @pages: Array of pointers to buffer pages passed from perf core + * @nr_pages: Total pages + * @snapshot: Flag for snapshot mode + * + * This is the callback when setup an event using AUX buffer. Perf tool can + * trigger this by an additional mmap() call on the event. Unlike the buffer + * for basic samples, AUX buffer belongs to the event. It is scheduled with + * the task among online cpus when it is a per-thread event. + * + * Return the private AUX buffer structure if success or NULL if fails. + */ +static void *aux_buffer_setup(int cpu, void **pages, int nr_pages, + bool snapshot) +{ + struct sf_buffer *sfb; + struct aux_buffer *aux; + unsigned long *new, *tail; + int i, n_sdbt; + + if (!nr_pages || !pages) + return NULL; + + if (nr_pages > CPUM_SF_MAX_SDB * CPUM_SF_SDB_DIAG_FACTOR) { + pr_err("AUX buffer size (%i pages) is larger than the " + "maximum sampling buffer limit\n", + nr_pages); + return NULL; + } else if (nr_pages < CPUM_SF_MIN_SDB * CPUM_SF_SDB_DIAG_FACTOR) { + pr_err("AUX buffer size (%i pages) is less than the " + "minimum sampling buffer limit\n", + nr_pages); + return NULL; + } + + /* Allocate aux_buffer struct for the event */ + aux = kmalloc(sizeof(struct aux_buffer), GFP_KERNEL); + if (!aux) + goto no_aux; + sfb = &aux->sfb; + + /* Allocate sdbt_index for fast reference */ + n_sdbt = (nr_pages + CPUM_SF_SDB_PER_TABLE - 1) / CPUM_SF_SDB_PER_TABLE; + aux->sdbt_index = kmalloc_array(n_sdbt, sizeof(void *), GFP_KERNEL); + if (!aux->sdbt_index) + goto no_sdbt_index; + + /* Allocate sdb_index for fast reference */ + aux->sdb_index = kmalloc_array(nr_pages, sizeof(void *), GFP_KERNEL); + if (!aux->sdb_index) + goto no_sdb_index; + + /* Allocate the first SDBT */ + sfb->num_sdbt = 0; + sfb->sdbt = (unsigned long *) get_zeroed_page(GFP_KERNEL); + if (!sfb->sdbt) + goto no_sdbt; + aux->sdbt_index[sfb->num_sdbt++] = (unsigned long)sfb->sdbt; + tail = sfb->tail = sfb->sdbt; + + /* + * Link the provided pages of AUX buffer to SDBT. + * Allocate SDBT if needed. + */ + for (i = 0; i < nr_pages; i++, tail++) { + if (require_table_link(tail)) { + new = (unsigned long *) get_zeroed_page(GFP_KERNEL); + if (!new) + goto no_sdbt; + aux->sdbt_index[sfb->num_sdbt++] = (unsigned long)new; + /* Link current page to tail of chain */ + *tail = (unsigned long)(void *) new + 1; + tail = new; + } + /* Tail is the entry in a SDBT */ + *tail = (unsigned long)pages[i]; + aux->sdb_index[i] = (unsigned long)pages[i]; + } + sfb->num_sdb = nr_pages; + + /* Link the last entry in the SDBT to the first SDBT */ + *tail = (unsigned long) sfb->sdbt + 1; + sfb->tail = tail; + + /* + * Initial all SDBs are zeroed. Mark it as empty. + * So there is no need to clear the full indicator + * when this event is first added. + */ + aux->empty_mark = sfb->num_sdb - 1; + + debug_sprintf_event(sfdbg, 4, "aux_buffer_setup: setup %lu SDBTs" + " and %lu SDBs\n", + sfb->num_sdbt, sfb->num_sdb); + + return aux; + +no_sdbt: + /* SDBs (AUX buffer pages) are freed by caller */ + for (i = 0; i < sfb->num_sdbt; i++) + free_page(aux->sdbt_index[i]); + kfree(aux->sdb_index); +no_sdb_index: + kfree(aux->sdbt_index); +no_sdbt_index: + kfree(aux); +no_aux: + return NULL; +} + static void cpumsf_pmu_read(struct perf_event *event) { /* Nothing to do ... updates are interrupt-driven */ @@ -1342,12 +1749,13 @@ static void cpumsf_pmu_stop(struct perf_event *event, int flags) static int cpumsf_pmu_add(struct perf_event *event, int flags) { struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); + struct aux_buffer *aux; int err; if (cpuhw->flags & PMU_F_IN_USE) return -EAGAIN; - if (!cpuhw->sfb.sdbt) + if (!SAMPL_DIAG_MODE(&event->hw) && !cpuhw->sfb.sdbt) return -EINVAL; err = 0; @@ -1362,10 +1770,12 @@ static int cpumsf_pmu_add(struct perf_event *event, int flags) */ cpuhw->lsctl.s = 0; cpuhw->lsctl.h = 1; - cpuhw->lsctl.tear = (unsigned long) cpuhw->sfb.sdbt; - cpuhw->lsctl.dear = *(unsigned long *) cpuhw->sfb.sdbt; cpuhw->lsctl.interval = SAMPL_RATE(&event->hw); - hw_reset_registers(&event->hw, cpuhw->sfb.sdbt); + if (!SAMPL_DIAG_MODE(&event->hw)) { + cpuhw->lsctl.tear = (unsigned long) cpuhw->sfb.sdbt; + cpuhw->lsctl.dear = *(unsigned long *) cpuhw->sfb.sdbt; + hw_reset_registers(&event->hw, cpuhw->sfb.sdbt); + } /* Ensure sampling functions are in the disabled state. If disabled, * switch on sampling enable control. */ @@ -1373,9 +1783,18 @@ static int cpumsf_pmu_add(struct perf_event *event, int flags) err = -EAGAIN; goto out; } - cpuhw->lsctl.es = 1; - if (SAMPL_DIAG_MODE(&event->hw)) + if (SAMPL_DIAG_MODE(&event->hw)) { + aux = perf_aux_output_begin(&cpuhw->handle, event); + if (!aux) { + err = -EINVAL; + goto out; + } + err = aux_output_begin(&cpuhw->handle, aux, cpuhw); + if (err) + goto out; cpuhw->lsctl.ed = 1; + } + cpuhw->lsctl.es = 1; /* Set in_use flag and store event */ cpuhw->event = event; @@ -1401,6 +1820,8 @@ static void cpumsf_pmu_del(struct perf_event *event, int flags) cpuhw->flags &= ~PMU_F_IN_USE; cpuhw->event = NULL; + if (SAMPL_DIAG_MODE(&event->hw)) + aux_output_end(&cpuhw->handle); perf_event_update_userpage(event); perf_pmu_enable(event->pmu); } @@ -1448,6 +1869,9 @@ static struct pmu cpumf_sampling = { .read = cpumsf_pmu_read, .attr_groups = cpumsf_pmu_attr_groups, + + .setup_aux = aux_buffer_setup, + .free_aux = aux_buffer_free, }; static void cpumf_measurement_alert(struct ext_code ext_code, @@ -1471,7 +1895,10 @@ static void cpumf_measurement_alert(struct ext_code ext_code, /* Program alert request */ if (alert & CPU_MF_INT_SF_PRA) { if (cpuhw->flags & PMU_F_IN_USE) - hw_perf_event_update(cpuhw->event, 0); + if (SAMPL_DIAG_MODE(&cpuhw->event->hw)) + hw_collect_aux(cpuhw); + else + hw_perf_event_update(cpuhw->event, 0); else WARN_ON_ONCE(!(cpuhw->flags & PMU_F_IN_USE)); } @@ -1590,6 +2017,9 @@ static int __init init_cpum_sampling_pmu(void) return -ENODEV; } + if (!si.as && !si.ad) + return -ENODEV; + if (si.bsdes != sizeof(struct hws_basic_entry)) { pr_cpumsf_err(RS_INIT_FAILURE_BSDES); return -EINVAL; diff --git a/arch/s390/kernel/perf_regs.c b/arch/s390/kernel/perf_regs.c new file mode 100644 index 000000000000..f8603ebed669 --- /dev/null +++ b/arch/s390/kernel/perf_regs.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + freg_t fp; + + if (WARN_ON_ONCE((u32)idx >= PERF_REG_S390_MAX)) + return 0; + + if (idx >= PERF_REG_S390_R0 && idx <= PERF_REG_S390_R15) + return regs->gprs[idx]; + + if (idx >= PERF_REG_S390_FP0 && idx <= PERF_REG_S390_FP15) { + if (!user_mode(regs)) + return 0; + + idx -= PERF_REG_S390_FP0; + fp = MACHINE_HAS_VX ? *(freg_t *)(current->thread.fpu.vxrs + idx) + : current->thread.fpu.fprs[idx]; + return fp.ui; + } + + if (idx == PERF_REG_S390_MASK) + return regs->psw.mask; + if (idx == PERF_REG_S390_PC) + return regs->psw.addr; + + return regs->gprs[idx]; +} + +#define REG_RESERVED (~((1UL << PERF_REG_S390_MAX) - 1)) + +int perf_reg_validate(u64 mask) +{ + if (!mask || mask & REG_RESERVED) + return -EINVAL; + + return 0; +} + +u64 perf_reg_abi(struct task_struct *task) +{ + if (test_tsk_thread_flag(task, TIF_31BIT)) + return PERF_SAMPLE_REGS_ABI_32; + + return PERF_SAMPLE_REGS_ABI_64; +} + +void perf_get_regs_user(struct perf_regs *regs_user, + struct pt_regs *regs, + struct pt_regs *regs_user_copy) +{ + /* + * Use the regs from the first interruption and let + * perf_sample_regs_intr() handle interrupts (regs == get_irq_regs()). + * + * Also save FPU registers for user-space tasks only. + */ + regs_user->regs = task_pt_regs(current); + if (user_mode(regs_user->regs)) + save_fpu_regs(); + regs_user->abi = perf_reg_abi(current); +} diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index 0520854a4dab..39a218703c50 100644 --- a/arch/s390/kernel/vdso.c +++ b/arch/s390/kernel/vdso.c @@ -158,16 +158,9 @@ int vdso_alloc_per_cpu(struct lowcore *lowcore) { unsigned long segment_table, page_table, page_frame; struct vdso_per_cpu_data *vd; - u32 *psal, *aste; - int i; - - lowcore->vdso_per_cpu_data = __LC_PASTE; - - if (!vdso_enabled) - return 0; segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER); - page_table = get_zeroed_page(GFP_KERNEL | GFP_DMA); + page_table = get_zeroed_page(GFP_KERNEL); page_frame = get_zeroed_page(GFP_KERNEL); if (!segment_table || !page_table || !page_frame) goto out; @@ -179,25 +172,15 @@ int vdso_alloc_per_cpu(struct lowcore *lowcore) vd->cpu_nr = lowcore->cpu_nr; vd->node_id = cpu_to_node(vd->cpu_nr); - /* Set up access register mode page table */ + /* Set up page table for the vdso address space */ memset64((u64 *)segment_table, _SEGMENT_ENTRY_EMPTY, _CRST_ENTRIES); memset64((u64 *)page_table, _PAGE_INVALID, PTRS_PER_PTE); *(unsigned long *) segment_table = _SEGMENT_ENTRY + page_table; *(unsigned long *) page_table = _PAGE_PROTECT + page_frame; - psal = (u32 *) (page_table + 256*sizeof(unsigned long)); - aste = psal + 32; - - for (i = 4; i < 32; i += 4) - psal[i] = 0x80000000; - - lowcore->paste[4] = (u32)(addr_t) psal; - psal[0] = 0x02000000; - psal[2] = (u32)(addr_t) aste; - *(unsigned long *) (aste + 2) = segment_table + + lowcore->vdso_asce = segment_table + _ASCE_TABLE_LENGTH + _ASCE_USER_BITS + _ASCE_TYPE_SEGMENT; - aste[4] = (u32)(addr_t) psal; lowcore->vdso_per_cpu_data = page_frame; return 0; @@ -212,14 +195,8 @@ out: void vdso_free_per_cpu(struct lowcore *lowcore) { unsigned long segment_table, page_table, page_frame; - u32 *psal, *aste; - - if (!vdso_enabled) - return; - psal = (u32 *)(addr_t) lowcore->paste[4]; - aste = (u32 *)(addr_t) psal[2]; - segment_table = *(unsigned long *)(aste + 2) & PAGE_MASK; + segment_table = lowcore->vdso_asce & PAGE_MASK; page_table = *(unsigned long *) segment_table; page_frame = *(unsigned long *) page_table; @@ -228,16 +205,6 @@ void vdso_free_per_cpu(struct lowcore *lowcore) free_pages(segment_table, SEGMENT_ORDER); } -static void vdso_init_cr5(void) -{ - unsigned long cr5; - - if (!vdso_enabled) - return; - cr5 = offsetof(struct lowcore, paste); - __ctl_load(cr5, 5, 5); -} - /* * This is called from binfmt_elf, we create the special vma for the * vDSO and insert it into the mm struct tree @@ -314,8 +281,6 @@ static int __init vdso_init(void) { int i; - if (!vdso_enabled) - return 0; vdso_init_data(vdso_data); #ifdef CONFIG_COMPAT /* Calculate the size of the 32 bit vDSO */ @@ -354,7 +319,6 @@ static int __init vdso_init(void) vdso64_pagelist[vdso64_pages] = NULL; if (vdso_alloc_per_cpu(&S390_lowcore)) BUG(); - vdso_init_cr5(); get_page(virt_to_page(vdso_data)); diff --git a/arch/s390/kernel/vdso32/getcpu.S b/arch/s390/kernel/vdso32/getcpu.S index 6e30769dd017..5477a2c112fb 100644 --- a/arch/s390/kernel/vdso32/getcpu.S +++ b/arch/s390/kernel/vdso32/getcpu.S @@ -15,23 +15,11 @@ .type __kernel_getcpu,@function __kernel_getcpu: .cfi_startproc - ear %r1,%a4 - lhi %r4,1 - sll %r4,24 - sar %a4,%r4 la %r4,0 - epsw %r0,0 - sacf 512 + sacf 256 l %r5,__VDSO_CPU_NR(%r4) l %r4,__VDSO_NODE_ID(%r4) - tml %r0,0x4000 - jo 1f - tml %r0,0x8000 - jno 0f - sacf 256 - j 1f -0: sacf 0 -1: sar %a4,%r1 + sacf 0 ltr %r2,%r2 jz 2f st %r5,0(%r2) diff --git a/arch/s390/kernel/vdso64/clock_gettime.S b/arch/s390/kernel/vdso64/clock_gettime.S index 9c3b12626dba..5d7b56b49458 100644 --- a/arch/s390/kernel/vdso64/clock_gettime.S +++ b/arch/s390/kernel/vdso64/clock_gettime.S @@ -114,23 +114,12 @@ __kernel_clock_gettime: br %r14 /* CPUCLOCK_VIRT for this thread */ -9: icm %r0,15,__VDSO_ECTG_OK(%r5) +9: lghi %r4,0 + icm %r0,15,__VDSO_ECTG_OK(%r5) jz 12f - ear %r2,%a4 - llilh %r4,0x0100 - sar %a4,%r4 - lghi %r4,0 - epsw %r5,0 - sacf 512 /* Magic ectg instruction */ + sacf 256 /* Magic ectg instruction */ .insn ssf,0xc80100000000,__VDSO_ECTG_BASE(4),__VDSO_ECTG_USER(4),4 - tml %r5,0x4000 - jo 11f - tml %r5,0x8000 - jno 10f - sacf 256 - j 11f -10: sacf 0 -11: sar %a4,%r2 + sacf 0 algr %r1,%r0 /* r1 = cputime as TOD value */ mghi %r1,1000 /* convert to nanoseconds */ srlg %r1,%r1,12 /* r1 = cputime in nanosec */ diff --git a/arch/s390/kernel/vdso64/getcpu.S b/arch/s390/kernel/vdso64/getcpu.S index 43983764b959..e9c34364d97b 100644 --- a/arch/s390/kernel/vdso64/getcpu.S +++ b/arch/s390/kernel/vdso64/getcpu.S @@ -15,22 +15,11 @@ .type __kernel_getcpu,@function __kernel_getcpu: .cfi_startproc - ear %r1,%a4 - llilh %r4,0x0100 - sar %a4,%r4 la %r4,0 - epsw %r0,0 - sacf 512 + sacf 256 l %r5,__VDSO_CPU_NR(%r4) l %r4,__VDSO_NODE_ID(%r4) - tml %r0,0x4000 - jo 1f - tml %r0,0x8000 - jno 0f - sacf 256 - j 1f -0: sacf 0 -1: sar %a4,%r1 + sacf 0 ltgr %r2,%r2 jz 2f st %r5,0(%r2) diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 84c0faeaf7ea..30a7c8c29964 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c @@ -78,7 +78,7 @@ static inline int arch_load_niai4(int *lock) ALTERNATIVE("", ".long 0xb2fa0040", 49) /* NIAI 4 */ " l %0,%1\n" : "=d" (owner) : "Q" (*lock) : "memory"); - return owner; + return owner; } static inline int arch_cmpxchg_niai8(int *lock, int old, int new) @@ -226,9 +226,10 @@ static inline void arch_spin_lock_classic(arch_spinlock_t *lp) /* Try to get the lock if it is free. */ if (!owner) { new = (old & _Q_TAIL_MASK) | lockval; - if (arch_cmpxchg_niai8(&lp->lock, old, new)) + if (arch_cmpxchg_niai8(&lp->lock, old, new)) { /* Got the lock */ - return; + return; + } continue; } if (count-- >= 0) diff --git a/arch/s390/lib/uaccess.c b/arch/s390/lib/uaccess.c index 802903c50de1..cae5a1e16cbd 100644 --- a/arch/s390/lib/uaccess.c +++ b/arch/s390/lib/uaccess.c @@ -40,10 +40,67 @@ static inline int copy_with_mvcos(void) } #endif +void set_fs(mm_segment_t fs) +{ + current->thread.mm_segment = fs; + if (fs == USER_DS) { + __ctl_load(S390_lowcore.user_asce, 1, 1); + clear_cpu_flag(CIF_ASCE_PRIMARY); + } else { + __ctl_load(S390_lowcore.kernel_asce, 1, 1); + set_cpu_flag(CIF_ASCE_PRIMARY); + } + if (fs & 1) { + if (fs == USER_DS_SACF) + __ctl_load(S390_lowcore.user_asce, 7, 7); + else + __ctl_load(S390_lowcore.kernel_asce, 7, 7); + set_cpu_flag(CIF_ASCE_SECONDARY); + } +} +EXPORT_SYMBOL(set_fs); + +mm_segment_t enable_sacf_uaccess(void) +{ + mm_segment_t old_fs; + unsigned long asce, cr; + + old_fs = current->thread.mm_segment; + if (old_fs & 1) + return old_fs; + current->thread.mm_segment |= 1; + asce = S390_lowcore.kernel_asce; + if (likely(old_fs == USER_DS)) { + __ctl_store(cr, 1, 1); + if (cr != S390_lowcore.kernel_asce) { + __ctl_load(S390_lowcore.kernel_asce, 1, 1); + set_cpu_flag(CIF_ASCE_PRIMARY); + } + asce = S390_lowcore.user_asce; + } + __ctl_store(cr, 7, 7); + if (cr != asce) { + __ctl_load(asce, 7, 7); + set_cpu_flag(CIF_ASCE_SECONDARY); + } + return old_fs; +} +EXPORT_SYMBOL(enable_sacf_uaccess); + +void disable_sacf_uaccess(mm_segment_t old_fs) +{ + if (old_fs == USER_DS && test_facility(27)) { + __ctl_load(S390_lowcore.user_asce, 1, 1); + clear_cpu_flag(CIF_ASCE_PRIMARY); + } + current->thread.mm_segment = old_fs; +} +EXPORT_SYMBOL(disable_sacf_uaccess); + static inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr, unsigned long size) { - register unsigned long reg0 asm("0") = 0x81UL; + register unsigned long reg0 asm("0") = 0x01UL; unsigned long tmp1, tmp2; tmp1 = -4096UL; @@ -74,8 +131,9 @@ static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr, unsigned long size) { unsigned long tmp1, tmp2; + mm_segment_t old_fs; - load_kernel_asce(); + old_fs = enable_sacf_uaccess(); tmp1 = -256UL; asm volatile( " sacf 0\n" @@ -102,6 +160,7 @@ static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr, EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b) : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2) : : "cc", "memory"); + disable_sacf_uaccess(old_fs); return size; } @@ -116,7 +175,7 @@ EXPORT_SYMBOL(raw_copy_from_user); static inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x, unsigned long size) { - register unsigned long reg0 asm("0") = 0x810000UL; + register unsigned long reg0 asm("0") = 0x010000UL; unsigned long tmp1, tmp2; tmp1 = -4096UL; @@ -147,8 +206,9 @@ static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x, unsigned long size) { unsigned long tmp1, tmp2; + mm_segment_t old_fs; - load_kernel_asce(); + old_fs = enable_sacf_uaccess(); tmp1 = -256UL; asm volatile( " sacf 0\n" @@ -175,6 +235,7 @@ static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x, EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b) : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2) : : "cc", "memory"); + disable_sacf_uaccess(old_fs); return size; } @@ -189,7 +250,7 @@ EXPORT_SYMBOL(raw_copy_to_user); static inline unsigned long copy_in_user_mvcos(void __user *to, const void __user *from, unsigned long size) { - register unsigned long reg0 asm("0") = 0x810081UL; + register unsigned long reg0 asm("0") = 0x010001UL; unsigned long tmp1, tmp2; tmp1 = -4096UL; @@ -212,9 +273,10 @@ static inline unsigned long copy_in_user_mvcos(void __user *to, const void __use static inline unsigned long copy_in_user_mvc(void __user *to, const void __user *from, unsigned long size) { + mm_segment_t old_fs; unsigned long tmp1; - load_kernel_asce(); + old_fs = enable_sacf_uaccess(); asm volatile( " sacf 256\n" " aghi %0,-1\n" @@ -238,6 +300,7 @@ static inline unsigned long copy_in_user_mvc(void __user *to, const void __user EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b) : "+a" (size), "+a" (to), "+a" (from), "=a" (tmp1) : : "cc", "memory"); + disable_sacf_uaccess(old_fs); return size; } @@ -251,7 +314,7 @@ EXPORT_SYMBOL(raw_copy_in_user); static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size) { - register unsigned long reg0 asm("0") = 0x810000UL; + register unsigned long reg0 asm("0") = 0x010000UL; unsigned long tmp1, tmp2; tmp1 = -4096UL; @@ -279,9 +342,10 @@ static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size static inline unsigned long clear_user_xc(void __user *to, unsigned long size) { + mm_segment_t old_fs; unsigned long tmp1, tmp2; - load_kernel_asce(); + old_fs = enable_sacf_uaccess(); asm volatile( " sacf 256\n" " aghi %0,-1\n" @@ -310,6 +374,7 @@ static inline unsigned long clear_user_xc(void __user *to, unsigned long size) EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b) : "+a" (size), "+a" (to), "=a" (tmp1), "=a" (tmp2) : : "cc", "memory"); + disable_sacf_uaccess(old_fs); return size; } @@ -345,10 +410,15 @@ static inline unsigned long strnlen_user_srst(const char __user *src, unsigned long __strnlen_user(const char __user *src, unsigned long size) { + mm_segment_t old_fs; + unsigned long len; + if (unlikely(!size)) return 0; - load_kernel_asce(); - return strnlen_user_srst(src, size); + old_fs = enable_sacf_uaccess(); + len = strnlen_user_srst(src, size); + disable_sacf_uaccess(old_fs); + return len; } EXPORT_SYMBOL(__strnlen_user); diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 242b78c0a9ec..93faeca52284 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -50,6 +50,13 @@ #define VM_FAULT_SIGNAL 0x080000 #define VM_FAULT_PFAULT 0x100000 +enum fault_type { + KERNEL_FAULT, + USER_FAULT, + VDSO_FAULT, + GMAP_FAULT, +}; + static unsigned long store_indication __read_mostly; static int __init fault_init(void) @@ -99,27 +106,34 @@ void bust_spinlocks(int yes) } /* - * Returns the address space associated with the fault. - * Returns 0 for kernel space and 1 for user space. + * Find out which address space caused the exception. + * Access register mode is impossible, ignore space == 3. */ -static inline int user_space_fault(struct pt_regs *regs) +static inline enum fault_type get_fault_type(struct pt_regs *regs) { unsigned long trans_exc_code; - /* - * The lowest two bits of the translation exception - * identification indicate which paging table was used. - */ trans_exc_code = regs->int_parm_long & 3; - if (trans_exc_code == 3) /* home space -> kernel */ - return 0; - if (user_mode(regs)) - return 1; - if (trans_exc_code == 2) /* secondary space -> set_fs */ - return current->thread.mm_segment.ar4; - if (current->flags & PF_VCPU) - return 1; - return 0; + if (likely(trans_exc_code == 0)) { + /* primary space exception */ + if (IS_ENABLED(CONFIG_PGSTE) && + test_pt_regs_flag(regs, PIF_GUEST_FAULT)) + return GMAP_FAULT; + if (current->thread.mm_segment == USER_DS) + return USER_FAULT; + return KERNEL_FAULT; + } + if (trans_exc_code == 2) { + /* secondary space exception */ + if (current->thread.mm_segment & 1) { + if (current->thread.mm_segment == USER_DS_SACF) + return USER_FAULT; + return KERNEL_FAULT; + } + return VDSO_FAULT; + } + /* home space exception -> access via kernel ASCE */ + return KERNEL_FAULT; } static int bad_address(void *p) @@ -204,20 +218,23 @@ static void dump_fault_info(struct pt_regs *regs) break; } pr_cont("mode while using "); - if (!user_space_fault(regs)) { - asce = S390_lowcore.kernel_asce; - pr_cont("kernel "); - } -#ifdef CONFIG_PGSTE - else if ((current->flags & PF_VCPU) && S390_lowcore.gmap) { - struct gmap *gmap = (struct gmap *)S390_lowcore.gmap; - asce = gmap->asce; - pr_cont("gmap "); - } -#endif - else { + switch (get_fault_type(regs)) { + case USER_FAULT: asce = S390_lowcore.user_asce; pr_cont("user "); + break; + case VDSO_FAULT: + asce = S390_lowcore.vdso_asce; + pr_cont("vdso "); + break; + case GMAP_FAULT: + asce = ((struct gmap *) S390_lowcore.gmap)->asce; + pr_cont("gmap "); + break; + case KERNEL_FAULT: + asce = S390_lowcore.kernel_asce; + pr_cont("kernel "); + break; } pr_cont("ASCE.\n"); dump_pagetable(asce, regs->int_parm_long & __FAIL_ADDR_MASK); @@ -273,7 +290,7 @@ static noinline void do_no_context(struct pt_regs *regs) * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ - if (!user_space_fault(regs)) + if (get_fault_type(regs) == KERNEL_FAULT) printk(KERN_ALERT "Unable to handle kernel pointer dereference" " in virtual kernel address space\n"); else @@ -395,12 +412,11 @@ static noinline void do_fault_error(struct pt_regs *regs, int access, int fault) */ static inline int do_exception(struct pt_regs *regs, int access) { -#ifdef CONFIG_PGSTE struct gmap *gmap; -#endif struct task_struct *tsk; struct mm_struct *mm; struct vm_area_struct *vma; + enum fault_type type; unsigned long trans_exc_code; unsigned long address; unsigned int flags; @@ -425,8 +441,19 @@ static inline int do_exception(struct pt_regs *regs, int access) * user context. */ fault = VM_FAULT_BADCONTEXT; - if (unlikely(!user_space_fault(regs) || faulthandler_disabled() || !mm)) + type = get_fault_type(regs); + switch (type) { + case KERNEL_FAULT: + goto out; + case VDSO_FAULT: + fault = VM_FAULT_BADMAP; goto out; + case USER_FAULT: + case GMAP_FAULT: + if (faulthandler_disabled() || !mm) + goto out; + break; + } address = trans_exc_code & __FAIL_ADDR_MASK; perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); @@ -437,10 +464,9 @@ static inline int do_exception(struct pt_regs *regs, int access) flags |= FAULT_FLAG_WRITE; down_read(&mm->mmap_sem); -#ifdef CONFIG_PGSTE - gmap = (current->flags & PF_VCPU) ? - (struct gmap *) S390_lowcore.gmap : NULL; - if (gmap) { + gmap = NULL; + if (IS_ENABLED(CONFIG_PGSTE) && type == GMAP_FAULT) { + gmap = (struct gmap *) S390_lowcore.gmap; current->thread.gmap_addr = address; current->thread.gmap_write_flag = !!(flags & FAULT_FLAG_WRITE); current->thread.gmap_int_code = regs->int_code & 0xffff; @@ -452,7 +478,6 @@ static inline int do_exception(struct pt_regs *regs, int access) if (gmap->pfault_enabled) flags |= FAULT_FLAG_RETRY_NOWAIT; } -#endif retry: fault = VM_FAULT_BADMAP; @@ -507,15 +532,14 @@ retry: regs, address); } if (fault & VM_FAULT_RETRY) { -#ifdef CONFIG_PGSTE - if (gmap && (flags & FAULT_FLAG_RETRY_NOWAIT)) { + if (IS_ENABLED(CONFIG_PGSTE) && gmap && + (flags & FAULT_FLAG_RETRY_NOWAIT)) { /* FAULT_FLAG_RETRY_NOWAIT has been set, * mmap_sem has not been released */ current->thread.gmap_pfault = 1; fault = VM_FAULT_PFAULT; goto out_up; } -#endif /* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk * of starvation. */ flags &= ~(FAULT_FLAG_ALLOW_RETRY | @@ -525,8 +549,7 @@ retry: goto retry; } } -#ifdef CONFIG_PGSTE - if (gmap) { + if (IS_ENABLED(CONFIG_PGSTE) && gmap) { address = __gmap_link(gmap, current->thread.gmap_addr, address); if (address == -EFAULT) { @@ -538,7 +561,6 @@ retry: goto out_up; } } -#endif fault = 0; out_up: up_read(&mm->mmap_sem); @@ -706,7 +728,7 @@ static void pfault_interrupt(struct ext_code ext_code, return; inc_irq_stat(IRQEXT_PFL); /* Get the token (= pid of the affected task). */ - pid = param64 & LPP_PFAULT_PID_MASK; + pid = param64 & LPP_PID_MASK; rcu_read_lock(); tsk = find_task_by_pid_ns(pid, &init_pid_ns); if (tsk) diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c index 2f66290c9b92..b2c140193b0a 100644 --- a/arch/s390/mm/gmap.c +++ b/arch/s390/mm/gmap.c @@ -1187,12 +1187,11 @@ static void gmap_unshadow_pgt(struct gmap *sg, unsigned long raddr) static void __gmap_unshadow_sgt(struct gmap *sg, unsigned long raddr, unsigned long *sgt) { - unsigned long asce, *pgt; + unsigned long *pgt; struct page *page; int i; BUG_ON(!gmap_is_shadow(sg)); - asce = (unsigned long) sgt | _ASCE_TYPE_SEGMENT; for (i = 0; i < _CRST_ENTRIES; i++, raddr += _SEGMENT_SIZE) { if (!(sgt[i] & _SEGMENT_ENTRY_ORIGIN)) continue; @@ -1245,12 +1244,11 @@ static void gmap_unshadow_sgt(struct gmap *sg, unsigned long raddr) static void __gmap_unshadow_r3t(struct gmap *sg, unsigned long raddr, unsigned long *r3t) { - unsigned long asce, *sgt; + unsigned long *sgt; struct page *page; int i; BUG_ON(!gmap_is_shadow(sg)); - asce = (unsigned long) r3t | _ASCE_TYPE_REGION3; for (i = 0; i < _CRST_ENTRIES; i++, raddr += _REGION3_SIZE) { if (!(r3t[i] & _REGION_ENTRY_ORIGIN)) continue; @@ -1303,12 +1301,11 @@ static void gmap_unshadow_r3t(struct gmap *sg, unsigned long raddr) static void __gmap_unshadow_r2t(struct gmap *sg, unsigned long raddr, unsigned long *r2t) { - unsigned long asce, *r3t; + unsigned long *r3t; struct page *page; int i; BUG_ON(!gmap_is_shadow(sg)); - asce = (unsigned long) r2t | _ASCE_TYPE_REGION2; for (i = 0; i < _CRST_ENTRIES; i++, raddr += _REGION2_SIZE) { if (!(r2t[i] & _REGION_ENTRY_ORIGIN)) continue; diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 817c9e16e83e..671535e64aba 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -95,6 +95,7 @@ void __init paging_init(void) } init_mm.context.asce = (__pa(init_mm.pgd) & PAGE_MASK) | asce_bits; S390_lowcore.kernel_asce = init_mm.context.asce; + S390_lowcore.user_asce = S390_lowcore.kernel_asce; crst_table_init((unsigned long *) init_mm.pgd, pgd_type); vmem_map_init(); diff --git a/arch/s390/mm/pgalloc.c b/arch/s390/mm/pgalloc.c index 4ad4c4f77b4d..434a9564917b 100644 --- a/arch/s390/mm/pgalloc.c +++ b/arch/s390/mm/pgalloc.c @@ -71,10 +71,8 @@ static void __crst_table_upgrade(void *arg) { struct mm_struct *mm = arg; - if (current->active_mm == mm) { - clear_user_asce(); + if (current->active_mm == mm) set_user_asce(mm); - } __tlb_flush_local(); } diff --git a/arch/s390/tools/Makefile b/arch/s390/tools/Makefile index 2ebf2872cc16..2e70e25de07a 100644 --- a/arch/s390/tools/Makefile +++ b/arch/s390/tools/Makefile @@ -21,4 +21,4 @@ include/generated/facilities.h: $(obj)/gen_facilities FORCE $(call filechk,facilities.h) include/generated/dis.h: $(obj)/gen_opcode_table FORCE - $(call filechk,dis.h,__FUN) + $(call filechk,dis.h) diff --git a/arch/sh/Makefile b/arch/sh/Makefile index 280bbff12102..65300193b99f 100644 --- a/arch/sh/Makefile +++ b/arch/sh/Makefile @@ -15,6 +15,12 @@ ifneq ($(SUBARCH),$(ARCH)) endif endif +ifeq ($(ARCH),sh) +KBUILD_DEFCONFIG := shx3_defconfig +else +KBUILD_DEFCONFIG := cayman_defconfig +endif + isa-y := any isa-$(CONFIG_SH_DSP) := sh isa-$(CONFIG_CPU_SH2) := sh2 @@ -105,14 +111,12 @@ ifdef CONFIG_SUPERH32 UTS_MACHINE := sh BITS := 32 LDFLAGS_vmlinux += -e _stext -KBUILD_DEFCONFIG := shx3_defconfig else UTS_MACHINE := sh64 BITS := 64 LDFLAGS_vmlinux += --defsym phys_stext=_stext-$(CONFIG_PAGE_OFFSET) \ --defsym phys_stext_shmedia=phys_stext+1 \ -e phys_stext_shmedia -KBUILD_DEFCONFIG := cayman_defconfig endif ifdef CONFIG_CPU_LITTLE_ENDIAN diff --git a/arch/sh/boot/compressed/.gitignore b/arch/sh/boot/compressed/.gitignore index 2374a83d87b2..edff113f1b85 100644 --- a/arch/sh/boot/compressed/.gitignore +++ b/arch/sh/boot/compressed/.gitignore @@ -1 +1,6 @@ +ashiftrt.S +ashldi3.c +ashlsi3.S +ashrsi3.S +lshrsi3.S vmlinux.bin.* diff --git a/arch/sh/boot/compressed/misc.c b/arch/sh/boot/compressed/misc.c index f2d9d3079d4e..627ce8e75e01 100644 --- a/arch/sh/boot/compressed/misc.c +++ b/arch/sh/boot/compressed/misc.c @@ -104,6 +104,18 @@ static void error(char *x) while(1); /* Halt */ } +unsigned long __stack_chk_guard; + +void __stack_chk_guard_setup(void) +{ + __stack_chk_guard = 0x000a0dff; +} + +void __stack_chk_fail(void) +{ + error("stack-protector: Kernel stack is corrupted\n"); +} + #ifdef CONFIG_SUPERH64 #define stackalign 8 #else @@ -118,6 +130,8 @@ void decompress_kernel(void) { unsigned long output_addr; + __stack_chk_guard_setup(); + #ifdef CONFIG_SUPERH64 output_addr = (CONFIG_MEMORY_START + 0x2000); #else diff --git a/arch/sh/include/asm/topology.h b/arch/sh/include/asm/topology.h index 9a32eb4098df..1db470e02456 100644 --- a/arch/sh/include/asm/topology.h +++ b/arch/sh/include/asm/topology.h @@ -5,7 +5,6 @@ #ifdef CONFIG_NUMA #define cpu_to_node(cpu) ((void)(cpu),0) -#define parent_node(node) ((void)(node),0) #define cpumask_of_node(node) ((void)node, cpu_online_mask) diff --git a/arch/sparc/Kbuild b/arch/sparc/Kbuild index 675afa285ddb..b4d0f570cc00 100644 --- a/arch/sparc/Kbuild +++ b/arch/sparc/Kbuild @@ -7,3 +7,4 @@ obj-y += mm/ obj-y += math-emu/ obj-y += net/ obj-y += crypto/ +obj-$(CONFIG_SPARC64) += vdso/ diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 4e83f950713e..6bf594ace663 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -84,6 +84,8 @@ config SPARC64 select HAVE_REGS_AND_STACK_ACCESS_API select ARCH_USE_QUEUED_RWLOCKS select ARCH_USE_QUEUED_SPINLOCKS + select GENERIC_TIME_VSYSCALL + select ARCH_CLOCKSOURCE_DATA config ARCH_DEFCONFIG string @@ -96,9 +98,6 @@ config ARCH_PROC_KCORE_TEXT config CPU_BIG_ENDIAN def_bool y -config CPU_BIG_ENDIAN - def_bool y - config ARCH_ATU bool default y if SPARC64 diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile index dbc448923f48..edac927e4952 100644 --- a/arch/sparc/Makefile +++ b/arch/sparc/Makefile @@ -81,6 +81,10 @@ install: archclean: $(Q)$(MAKE) $(clean)=$(boot) +PHONY += vdso_install +vdso_install: + $(Q)$(MAKE) $(build)=arch/sparc/vdso $@ + # This is the image used for packaging KBUILD_IMAGE := $(boot)/zImage diff --git a/arch/sparc/include/asm/bitops_64.h b/arch/sparc/include/asm/bitops_64.h index a90eea24b286..ca7ea5913494 100644 --- a/arch/sparc/include/asm/bitops_64.h +++ b/arch/sparc/include/asm/bitops_64.h @@ -23,10 +23,11 @@ void set_bit(unsigned long nr, volatile unsigned long *addr); void clear_bit(unsigned long nr, volatile unsigned long *addr); void change_bit(unsigned long nr, volatile unsigned long *addr); +int fls(unsigned int word); +int __fls(unsigned long word); + #include -#include -#include #include #ifdef __KERNEL__ diff --git a/arch/sparc/include/asm/clocksource.h b/arch/sparc/include/asm/clocksource.h new file mode 100644 index 000000000000..d63ef224befe --- /dev/null +++ b/arch/sparc/include/asm/clocksource.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _ASM_SPARC_CLOCKSOURCE_H +#define _ASM_SPARC_CLOCKSOURCE_H + +/* VDSO clocksources */ +#define VCLOCK_NONE 0 /* Nothing userspace can do. */ +#define VCLOCK_TICK 1 /* Use %tick. */ +#define VCLOCK_STICK 2 /* Use %stick. */ + +struct arch_clocksource_data { + int vclock_mode; +}; + +#endif /* _ASM_SPARC_CLOCKSOURCE_H */ diff --git a/arch/sparc/include/asm/cmpxchg_32.h b/arch/sparc/include/asm/cmpxchg_32.h index 3e3823db303e..c73b5a3ab7b9 100644 --- a/arch/sparc/include/asm/cmpxchg_32.h +++ b/arch/sparc/include/asm/cmpxchg_32.h @@ -63,6 +63,9 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size) (unsigned long)_n_, sizeof(*(ptr))); \ }) +u64 __cmpxchg_u64(u64 *ptr, u64 old, u64 new); +#define cmpxchg64(ptr, old, new) __cmpxchg_u64(ptr, old, new) + #include /* diff --git a/arch/sparc/include/asm/compat.h b/arch/sparc/include/asm/compat.h index 977c3f280ba1..fa38c78de0f0 100644 --- a/arch/sparc/include/asm/compat.h +++ b/arch/sparc/include/asm/compat.h @@ -209,7 +209,6 @@ typedef struct compat_siginfo { } compat_siginfo_t; #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL /* * A pointer passed in from user mode. This should not diff --git a/arch/sparc/include/asm/elf_64.h b/arch/sparc/include/asm/elf_64.h index 5894389f5ed5..25340df3570c 100644 --- a/arch/sparc/include/asm/elf_64.h +++ b/arch/sparc/include/asm/elf_64.h @@ -211,4 +211,18 @@ do { if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \ (current->personality & (~PER_MASK))); \ } while (0) +extern unsigned int vdso_enabled; + +#define ARCH_DLINFO \ +do { \ + if (vdso_enabled) \ + NEW_AUX_ENT(AT_SYSINFO_EHDR, \ + (unsigned long)current->mm->context.vdso); \ +} while (0) + +struct linux_binprm; + +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 +extern int arch_setup_additional_pages(struct linux_binprm *bprm, + int uses_interp); #endif /* !(__ASM_SPARC64_ELF_H) */ diff --git a/arch/sparc/include/asm/mmu_64.h b/arch/sparc/include/asm/mmu_64.h index 5fe64a57b4ba..ad4fb93508ba 100644 --- a/arch/sparc/include/asm/mmu_64.h +++ b/arch/sparc/include/asm/mmu_64.h @@ -97,6 +97,7 @@ typedef struct { unsigned long thp_pte_count; struct tsb_config tsb_block[MM_NUM_TSBS]; struct hv_tsb_descr tsb_descr[MM_NUM_TSBS]; + void *vdso; } mm_context_t; #endif /* !__ASSEMBLY__ */ diff --git a/arch/sparc/include/asm/mmu_context_64.h b/arch/sparc/include/asm/mmu_context_64.h index e25d25b0a34b..b361702ef52a 100644 --- a/arch/sparc/include/asm/mmu_context_64.h +++ b/arch/sparc/include/asm/mmu_context_64.h @@ -8,9 +8,11 @@ #include #include +#include #include #include +#include static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) { diff --git a/arch/sparc/include/asm/processor_64.h b/arch/sparc/include/asm/processor_64.h index c7c79fe8d265..aac23d4a4ddd 100644 --- a/arch/sparc/include/asm/processor_64.h +++ b/arch/sparc/include/asm/processor_64.h @@ -200,6 +200,13 @@ unsigned long get_wchan(struct task_struct *task); * To make a long story short, we are trying to yield the current cpu * strand during busy loops. */ +#ifdef BUILD_VDSO +#define cpu_relax() asm volatile("\n99:\n\t" \ + "rd %%ccr, %%g0\n\t" \ + "rd %%ccr, %%g0\n\t" \ + "rd %%ccr, %%g0\n\t" \ + ::: "memory") +#else /* ! BUILD_VDSO */ #define cpu_relax() asm volatile("\n99:\n\t" \ "rd %%ccr, %%g0\n\t" \ "rd %%ccr, %%g0\n\t" \ @@ -211,6 +218,7 @@ unsigned long get_wchan(struct task_struct *task); "nop\n\t" \ ".previous" \ ::: "memory") +#endif /* Prefetch support. This is tuned for UltraSPARC-III and later. * UltraSPARC-I will treat these as nops, and UltraSPARC-II has diff --git a/arch/sparc/include/asm/topology_64.h b/arch/sparc/include/asm/topology_64.h index 3831b1911a19..34c628a22ea5 100644 --- a/arch/sparc/include/asm/topology_64.h +++ b/arch/sparc/include/asm/topology_64.h @@ -11,8 +11,6 @@ static inline int cpu_to_node(int cpu) return numa_cpu_lookup_table[cpu]; } -#define parent_node(node) (node) - #define cpumask_of_node(node) ((node) == -1 ? \ cpu_all_mask : \ &numa_cpumask_lookup_table[node]) diff --git a/arch/sparc/include/asm/tsb.h b/arch/sparc/include/asm/tsb.h index 25b6abdb3908..522a677e050d 100644 --- a/arch/sparc/include/asm/tsb.h +++ b/arch/sparc/include/asm/tsb.h @@ -217,7 +217,7 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end; sllx REG2, 32, REG2; \ andcc REG1, REG2, %g0; \ be,pt %xcc, 700f; \ - sethi %hi(0x1ffc0000), REG2; \ + sethi %hi(0xffe00000), REG2; \ sllx REG2, 1, REG2; \ brgez,pn REG1, FAIL_LABEL; \ andn REG1, REG2, REG1; \ diff --git a/arch/sparc/include/asm/vdso.h b/arch/sparc/include/asm/vdso.h new file mode 100644 index 000000000000..93b628731a5e --- /dev/null +++ b/arch/sparc/include/asm/vdso.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _ASM_SPARC_VDSO_H +#define _ASM_SPARC_VDSO_H + +struct vdso_image { + void *data; + unsigned long size; /* Always a multiple of PAGE_SIZE */ + long sym_vvar_start; /* Negative offset to the vvar area */ + long sym_vread_tick; /* Start of vread_tick section */ + long sym_vread_tick_patch_start; /* Start of tick read */ + long sym_vread_tick_patch_end; /* End of tick read */ +}; + +#ifdef CONFIG_SPARC64 +extern const struct vdso_image vdso_image_64_builtin; +#endif +#ifdef CONFIG_COMPAT +extern const struct vdso_image vdso_image_32_builtin; +#endif + +#endif /* _ASM_SPARC_VDSO_H */ diff --git a/arch/sparc/include/asm/vvar.h b/arch/sparc/include/asm/vvar.h new file mode 100644 index 000000000000..0289503d1cb0 --- /dev/null +++ b/arch/sparc/include/asm/vvar.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _ASM_SPARC_VVAR_DATA_H +#define _ASM_SPARC_VVAR_DATA_H + +#include +#include +#include +#include + +struct vvar_data { + unsigned int seq; + + int vclock_mode; + struct { /* extract of a clocksource struct */ + u64 cycle_last; + u64 mask; + int mult; + int shift; + } clock; + /* open coded 'struct timespec' */ + u64 wall_time_sec; + u64 wall_time_snsec; + u64 monotonic_time_snsec; + u64 monotonic_time_sec; + u64 monotonic_time_coarse_sec; + u64 monotonic_time_coarse_nsec; + u64 wall_time_coarse_sec; + u64 wall_time_coarse_nsec; + + int tz_minuteswest; + int tz_dsttime; +}; + +extern struct vvar_data *vvar_data; +extern int vdso_fix_stick; + +static inline unsigned int vvar_read_begin(const struct vvar_data *s) +{ + unsigned int ret; + +repeat: + ret = READ_ONCE(s->seq); + if (unlikely(ret & 1)) { + cpu_relax(); + goto repeat; + } + smp_rmb(); /* Finish all reads before we return seq */ + return ret; +} + +static inline int vvar_read_retry(const struct vvar_data *s, + unsigned int start) +{ + smp_rmb(); /* Finish all reads before checking the value of seq */ + return unlikely(s->seq != start); +} + +static inline void vvar_write_begin(struct vvar_data *s) +{ + ++s->seq; + smp_wmb(); /* Makes sure that increment of seq is reflected */ +} + +static inline void vvar_write_end(struct vvar_data *s) +{ + smp_wmb(); /* Makes the value of seq current before we increment */ + ++s->seq; +} + + +#endif /* _ASM_SPARC_VVAR_DATA_H */ diff --git a/arch/sparc/include/uapi/asm/auxvec.h b/arch/sparc/include/uapi/asm/auxvec.h index ad6f360261f6..5f80a70cc901 100644 --- a/arch/sparc/include/uapi/asm/auxvec.h +++ b/arch/sparc/include/uapi/asm/auxvec.h @@ -1,4 +1,8 @@ #ifndef __ASMSPARC_AUXVEC_H #define __ASMSPARC_AUXVEC_H +#define AT_SYSINFO_EHDR 33 + +#define AT_VECTOR_SIZE_ARCH 1 + #endif /* !(__ASMSPARC_AUXVEC_H) */ diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 8de9617589a5..cc97545737f0 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SPARC32) += systbls_32.o obj-y += time_$(BITS).o obj-$(CONFIG_SPARC32) += windows.o obj-y += cpu.o +obj-$(CONFIG_SPARC64) += vdso.o obj-$(CONFIG_SPARC32) += devices.o obj-y += ptrace_$(BITS).o obj-y += unaligned_$(BITS).o diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S index 9e293de12052..a41e6e16eb36 100644 --- a/arch/sparc/kernel/head_64.S +++ b/arch/sparc/kernel/head_64.S @@ -641,6 +641,8 @@ niagara4_patch: nop call niagara4_patch_pageops nop + call niagara4_patch_fls + nop ba,a,pt %xcc, 80f nop diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c index 1ef6156b1530..418592a09b41 100644 --- a/arch/sparc/kernel/mdesc.c +++ b/arch/sparc/kernel/mdesc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -71,7 +72,7 @@ struct mdesc_handle { struct list_head list; struct mdesc_mem_ops *mops; void *self_base; - atomic_t refcnt; + refcount_t refcnt; unsigned int handle_size; struct mdesc_hdr mdesc; }; @@ -153,7 +154,7 @@ static void mdesc_handle_init(struct mdesc_handle *hp, memset(hp, 0, handle_size); INIT_LIST_HEAD(&hp->list); hp->self_base = base; - atomic_set(&hp->refcnt, 1); + refcount_set(&hp->refcnt, 1); hp->handle_size = handle_size; } @@ -183,7 +184,7 @@ static void __init mdesc_memblock_free(struct mdesc_handle *hp) unsigned int alloc_size; unsigned long start; - BUG_ON(atomic_read(&hp->refcnt) != 0); + BUG_ON(refcount_read(&hp->refcnt) != 0); BUG_ON(!list_empty(&hp->list)); alloc_size = PAGE_ALIGN(hp->handle_size); @@ -221,7 +222,7 @@ static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) static void mdesc_kfree(struct mdesc_handle *hp) { - BUG_ON(atomic_read(&hp->refcnt) != 0); + BUG_ON(refcount_read(&hp->refcnt) != 0); BUG_ON(!list_empty(&hp->list)); kfree(hp->self_base); @@ -260,7 +261,7 @@ struct mdesc_handle *mdesc_grab(void) spin_lock_irqsave(&mdesc_lock, flags); hp = cur_mdesc; if (hp) - atomic_inc(&hp->refcnt); + refcount_inc(&hp->refcnt); spin_unlock_irqrestore(&mdesc_lock, flags); return hp; @@ -272,7 +273,7 @@ void mdesc_release(struct mdesc_handle *hp) unsigned long flags; spin_lock_irqsave(&mdesc_lock, flags); - if (atomic_dec_and_test(&hp->refcnt)) { + if (refcount_dec_and_test(&hp->refcnt)) { list_del_init(&hp->list); hp->mops->free(hp); } @@ -514,7 +515,7 @@ void mdesc_update(void) if (status != HV_EOK || real_len > len) { printk(KERN_ERR "MD: mdesc reread fails with %lu\n", status); - atomic_dec(&hp->refcnt); + refcount_dec(&hp->refcnt); mdesc_free(hp); goto out; } @@ -527,7 +528,7 @@ void mdesc_update(void) mdesc_notify_clients(orig_hp, hp); spin_lock_irqsave(&mdesc_lock, flags); - if (atomic_dec_and_test(&orig_hp->refcnt)) + if (refcount_dec_and_test(&orig_hp->refcnt)) mdesc_free(orig_hp); else list_add(&orig_hp->list, &mdesc_zombie_list); diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c index 5c572de64c74..54a6159b9cd8 100644 --- a/arch/sparc/kernel/signal32.c +++ b/arch/sparc/kernel/signal32.c @@ -249,7 +249,6 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) compat_uptr_t fpu_save; compat_uptr_t rwin_save; sigset_t set; - compat_sigset_t seta; int err, i; /* Always make any pending restarted system calls return -EINTR */ @@ -312,7 +311,7 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) err |= __get_user(fpu_save, &sf->fpu_save); if (!err && fpu_save) err |= restore_fpu_state(regs, compat_ptr(fpu_save)); - err |= copy_from_user(&seta, &sf->mask, sizeof(compat_sigset_t)); + err |= get_compat_sigset(&set, &sf->mask); err |= compat_restore_altstack(&sf->stack); if (err) goto segv; @@ -323,7 +322,6 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) goto segv; } - set.sig[0] = seta.sig[0] + (((long)seta.sig[1]) << 32); set_current_blocked(&set); return; segv: @@ -555,7 +553,6 @@ static int setup_rt_frame32(struct ksignal *ksig, struct pt_regs *regs, void __user *tail; int sigframe_size; u32 psr; - compat_sigset_t seta; /* 1. Make sure everything is clean */ synchronize_user_stack(); @@ -625,9 +622,7 @@ static int setup_rt_frame32(struct ksignal *ksig, struct pt_regs *regs, /* Setup sigaltstack */ err |= __compat_save_altstack(&sf->stack, regs->u_regs[UREG_FP]); - seta.sig[1] = (oldset->sig[0] >> 32); - seta.sig[0] = oldset->sig[0]; - err |= __copy_to_user(&sf->mask, &seta, sizeof(compat_sigset_t)); + err |= put_compat_sigset(&sf->mask, oldset, sizeof(compat_sigset_t)); if (!wsaved) { err |= copy_in_user((u32 __user *)sf, diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index b4e1478413a1..6d964bdefbaa 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -160,7 +160,6 @@ COMPAT_SYSCALL_DEFINE5(rt_sigaction, int, sig, { struct k_sigaction new_ka, old_ka; int ret; - compat_sigset_t set32; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(compat_sigset_t)) @@ -172,8 +171,7 @@ COMPAT_SYSCALL_DEFINE5(rt_sigaction, int, sig, new_ka.ka_restorer = restorer; ret = get_user(u_handler, &act->sa_handler); new_ka.sa.sa_handler = compat_ptr(u_handler); - ret |= copy_from_user(&set32, &act->sa_mask, sizeof(compat_sigset_t)); - sigset_from_compat(&new_ka.sa.sa_mask, &set32); + ret |= get_compat_sigset(&new_ka.sa.sa_mask, &act->sa_mask); ret |= get_user(new_ka.sa.sa_flags, &act->sa_flags); ret |= get_user(u_restorer, &act->sa_restorer); new_ka.sa.sa_restorer = compat_ptr(u_restorer); @@ -184,9 +182,9 @@ COMPAT_SYSCALL_DEFINE5(rt_sigaction, int, sig, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - sigset_to_compat(&set32, &old_ka.sa.sa_mask); ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler); - ret |= copy_to_user(&oact->sa_mask, &set32, sizeof(compat_sigset_t)); + ret |= put_compat_sigset(&oact->sa_mask, &old_ka.sa.sa_mask, + sizeof(oact->sa_mask)); ret |= put_user(old_ka.sa.sa_flags, &oact->sa_flags); ret |= put_user(ptr_to_compat(old_ka.sa.sa_restorer), &oact->sa_restorer); if (ret) diff --git a/arch/sparc/kernel/time_64.c b/arch/sparc/kernel/time_64.c index 3b397081047a..2ef8cfa9677e 100644 --- a/arch/sparc/kernel/time_64.c +++ b/arch/sparc/kernel/time_64.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -54,6 +53,8 @@ DEFINE_SPINLOCK(rtc_lock); +unsigned int __read_mostly vdso_fix_stick; + #ifdef CONFIG_SMP unsigned long profile_pc(struct pt_regs *regs) { @@ -831,12 +832,17 @@ static void init_tick_ops(struct sparc64_tick_ops *ops) void __init time_init_early(void) { if (tlb_type == spitfire) { - if (is_hummingbird()) + if (is_hummingbird()) { init_tick_ops(&hbtick_operations); - else + clocksource_tick.archdata.vclock_mode = VCLOCK_NONE; + } else { init_tick_ops(&tick_operations); + clocksource_tick.archdata.vclock_mode = VCLOCK_TICK; + vdso_fix_stick = 1; + } } else { init_tick_ops(&stick_operations); + clocksource_tick.archdata.vclock_mode = VCLOCK_STICK; } } diff --git a/arch/sparc/kernel/vdso.c b/arch/sparc/kernel/vdso.c new file mode 100644 index 000000000000..58880662b271 --- /dev/null +++ b/arch/sparc/kernel/vdso.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2001 Andrea Arcangeli SuSE + * Copyright 2003 Andi Kleen, SuSE Labs. + * + * Thanks to hpa@transmeta.com for some useful hint. + * Special thanks to Ingo Molnar for his early experience with + * a different vsyscall implementation for Linux/IA32 and for the name. + */ + +#include +#include +#include + +#include + +void update_vsyscall_tz(void) +{ + if (unlikely(vvar_data == NULL)) + return; + + vvar_data->tz_minuteswest = sys_tz.tz_minuteswest; + vvar_data->tz_dsttime = sys_tz.tz_dsttime; +} + +void update_vsyscall(struct timekeeper *tk) +{ + struct vvar_data *vdata = vvar_data; + + if (unlikely(vdata == NULL)) + return; + + vvar_write_begin(vdata); + vdata->vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode; + vdata->clock.cycle_last = tk->tkr_mono.cycle_last; + vdata->clock.mask = tk->tkr_mono.mask; + vdata->clock.mult = tk->tkr_mono.mult; + vdata->clock.shift = tk->tkr_mono.shift; + + vdata->wall_time_sec = tk->xtime_sec; + vdata->wall_time_snsec = tk->tkr_mono.xtime_nsec; + + vdata->monotonic_time_sec = tk->xtime_sec + + tk->wall_to_monotonic.tv_sec; + vdata->monotonic_time_snsec = tk->tkr_mono.xtime_nsec + + (tk->wall_to_monotonic.tv_nsec << + tk->tkr_mono.shift); + + while (vdata->monotonic_time_snsec >= + (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) { + vdata->monotonic_time_snsec -= + ((u64)NSEC_PER_SEC) << tk->tkr_mono.shift; + vdata->monotonic_time_sec++; + } + + vdata->wall_time_coarse_sec = tk->xtime_sec; + vdata->wall_time_coarse_nsec = + (long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift); + + vdata->monotonic_time_coarse_sec = + vdata->wall_time_coarse_sec + tk->wall_to_monotonic.tv_sec; + vdata->monotonic_time_coarse_nsec = + vdata->wall_time_coarse_nsec + tk->wall_to_monotonic.tv_nsec; + + while (vdata->monotonic_time_coarse_nsec >= NSEC_PER_SEC) { + vdata->monotonic_time_coarse_nsec -= NSEC_PER_SEC; + vdata->monotonic_time_coarse_sec++; + } + + vvar_write_end(vdata); +} diff --git a/arch/sparc/kernel/viohs.c b/arch/sparc/kernel/viohs.c index c858f5f3ce2c..635d67ffc9a3 100644 --- a/arch/sparc/kernel/viohs.c +++ b/arch/sparc/kernel/viohs.c @@ -798,9 +798,9 @@ void vio_port_up(struct vio_driver_state *vio) } EXPORT_SYMBOL(vio_port_up); -static void vio_port_timer(unsigned long _arg) +static void vio_port_timer(struct timer_list *t) { - struct vio_driver_state *vio = (struct vio_driver_state *) _arg; + struct vio_driver_state *vio = from_timer(vio, t, timer); vio_port_up(vio); } @@ -849,7 +849,7 @@ int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev, vio->ops = ops; - setup_timer(&vio->timer, vio_port_timer, (unsigned long) vio); + timer_setup(&vio->timer, vio_port_timer, 0); return 0; } diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index 44829a8dc458..0f0f76b4f6cd 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -17,6 +17,9 @@ lib-$(CONFIG_SPARC64) += atomic_64.o lib-$(CONFIG_SPARC32) += lshrdi3.o ashldi3.o lib-$(CONFIG_SPARC32) += muldi3.o bitext.o cmpdi2.o lib-$(CONFIG_SPARC64) += multi3.o +lib-$(CONFIG_SPARC64) += fls.o +lib-$(CONFIG_SPARC64) += fls64.o +obj-$(CONFIG_SPARC64) += NG4fls.o lib-$(CONFIG_SPARC64) += copy_page.o clear_page.o bzero.o lib-$(CONFIG_SPARC64) += csum_copy.o csum_copy_from_user.o csum_copy_to_user.o diff --git a/arch/sparc/lib/NG4fls.S b/arch/sparc/lib/NG4fls.S new file mode 100644 index 000000000000..2d0991e5b034 --- /dev/null +++ b/arch/sparc/lib/NG4fls.S @@ -0,0 +1,30 @@ +/* NG4fls.S: SPARC optimized fls and __fls for T4 and above. + * + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + */ + +#include + +#define LZCNT_O0_G2 \ + .word 0x85b002e8 + + .text + .register %g2, #scratch + .register %g3, #scratch + +ENTRY(NG4fls) + LZCNT_O0_G2 !lzcnt %o0, %g2 + mov 64, %g3 + retl + sub %g3, %g2, %o0 +ENDPROC(NG4fls) + +ENTRY(__NG4fls) + brz,pn %o0, 1f + LZCNT_O0_G2 !lzcnt %o0, %g2 + mov 63, %g3 + sub %g3, %g2, %o0 +1: + retl + nop +ENDPROC(__NG4fls) diff --git a/arch/sparc/lib/NG4patch.S b/arch/sparc/lib/NG4patch.S index aa58ab39f9a6..37866175c921 100644 --- a/arch/sparc/lib/NG4patch.S +++ b/arch/sparc/lib/NG4patch.S @@ -4,6 +4,8 @@ * Copyright (C) 2012 David S. Miller */ +#include + #define BRANCH_ALWAYS 0x10680000 #define NOP 0x01000000 #define NG_DO_PATCH(OLD, NEW) \ @@ -53,3 +55,10 @@ niagara4_patch_pageops: retl nop .size niagara4_patch_pageops,.-niagara4_patch_pageops + +ENTRY(niagara4_patch_fls) + NG_DO_PATCH(fls, NG4fls) + NG_DO_PATCH(__fls, __NG4fls) + retl + nop +ENDPROC(niagara4_patch_fls) diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c index 5010df497387..465a901a0ada 100644 --- a/arch/sparc/lib/atomic32.c +++ b/arch/sparc/lib/atomic32.c @@ -173,6 +173,20 @@ unsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new) } EXPORT_SYMBOL(__cmpxchg_u32); +u64 __cmpxchg_u64(u64 *ptr, u64 old, u64 new) +{ + unsigned long flags; + u64 prev; + + spin_lock_irqsave(ATOMIC_HASH(ptr), flags); + if ((prev = *ptr) == old) + *ptr = new; + spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags); + + return prev; +} +EXPORT_SYMBOL(__cmpxchg_u64); + unsigned long __xchg_u32(volatile u32 *ptr, u32 new) { unsigned long flags; diff --git a/arch/sparc/lib/fls.S b/arch/sparc/lib/fls.S new file mode 100644 index 000000000000..06b8d300bcae --- /dev/null +++ b/arch/sparc/lib/fls.S @@ -0,0 +1,67 @@ +/* fls.S: SPARC default fls definition. + * + * SPARC default fls definition, which follows the same algorithm as + * in generic fls(). This function will be boot time patched on T4 + * and onward. + */ + +#include +#include + + .text + .register %g2, #scratch + .register %g3, #scratch +ENTRY(fls) + brz,pn %o0, 6f + mov 0, %o1 + sethi %hi(0xffff0000), %g3 + mov %o0, %g2 + andcc %o0, %g3, %g0 + be,pt %icc, 8f + mov 32, %o1 + sethi %hi(0xff000000), %g3 + andcc %g2, %g3, %g0 + bne,pt %icc, 3f + sethi %hi(0xf0000000), %g3 + sll %o0, 8, %o0 +1: + add %o1, -8, %o1 + sra %o0, 0, %o0 + mov %o0, %g2 +2: + sethi %hi(0xf0000000), %g3 +3: + andcc %g2, %g3, %g0 + bne,pt %icc, 4f + sethi %hi(0xc0000000), %g3 + sll %o0, 4, %o0 + add %o1, -4, %o1 + sra %o0, 0, %o0 + mov %o0, %g2 +4: + andcc %g2, %g3, %g0 + be,a,pt %icc, 7f + sll %o0, 2, %o0 +5: + xnor %g0, %o0, %o0 + srl %o0, 31, %o0 + sub %o1, %o0, %o1 +6: + jmp %o7 + 8 + sra %o1, 0, %o0 +7: + add %o1, -2, %o1 + ba,pt %xcc, 5b + sra %o0, 0, %o0 +8: + sll %o0, 16, %o0 + sethi %hi(0xff000000), %g3 + sra %o0, 0, %o0 + mov %o0, %g2 + andcc %g2, %g3, %g0 + bne,pt %icc, 2b + mov 16, %o1 + ba,pt %xcc, 1b + sll %o0, 8, %o0 +ENDPROC(fls) +EXPORT_SYMBOL(fls) diff --git a/arch/sparc/lib/fls64.S b/arch/sparc/lib/fls64.S new file mode 100644 index 000000000000..c83e22ae9586 --- /dev/null +++ b/arch/sparc/lib/fls64.S @@ -0,0 +1,61 @@ +/* fls64.S: SPARC default __fls definition. + * + * SPARC default __fls definition, which follows the same algorithm as + * in generic __fls(). This function will be boot time patched on T4 + * and onward. + */ + +#include +#include + + .text + .register %g2, #scratch + .register %g3, #scratch +ENTRY(__fls) + mov -1, %g2 + sllx %g2, 32, %g2 + and %o0, %g2, %g2 + brnz,pt %g2, 1f + mov 63, %g1 + sllx %o0, 32, %o0 + mov 31, %g1 +1: + mov -1, %g2 + sllx %g2, 48, %g2 + and %o0, %g2, %g2 + brnz,pt %g2, 2f + mov -1, %g2 + sllx %o0, 16, %o0 + add %g1, -16, %g1 +2: + mov -1, %g2 + sllx %g2, 56, %g2 + and %o0, %g2, %g2 + brnz,pt %g2, 3f + mov -1, %g2 + sllx %o0, 8, %o0 + add %g1, -8, %g1 +3: + sllx %g2, 60, %g2 + and %o0, %g2, %g2 + brnz,pt %g2, 4f + mov -1, %g2 + sllx %o0, 4, %o0 + add %g1, -4, %g1 +4: + sllx %g2, 62, %g2 + and %o0, %g2, %g2 + brnz,pt %g2, 5f + mov -1, %g3 + sllx %o0, 2, %o0 + add %g1, -2, %g1 +5: + mov 0, %g2 + sllx %g3, 63, %g3 + and %o0, %g3, %o0 + movre %o0, 1, %g2 + sub %g1, %g2, %g1 + jmp %o7+8 + sra %g1, 0, %o0 +ENDPROC(__fls) +EXPORT_SYMBOL(__fls) diff --git a/arch/sparc/vdso/.gitignore b/arch/sparc/vdso/.gitignore new file mode 100644 index 000000000000..ef925b998222 --- /dev/null +++ b/arch/sparc/vdso/.gitignore @@ -0,0 +1,3 @@ +vdso.lds +vdso-image-*.c +vdso2c diff --git a/arch/sparc/vdso/Makefile b/arch/sparc/vdso/Makefile new file mode 100644 index 000000000000..a6615d8864f7 --- /dev/null +++ b/arch/sparc/vdso/Makefile @@ -0,0 +1,149 @@ +# +# Building vDSO images for sparc. +# + +KBUILD_CFLAGS += $(DISABLE_LTO) + +VDSO64-$(CONFIG_SPARC64) := y +VDSOCOMPAT-$(CONFIG_COMPAT) := y + +# files to link into the vdso +vobjs-y := vdso-note.o vclock_gettime.o + +# files to link into kernel +obj-y += vma.o + +# vDSO images to build +vdso_img-$(VDSO64-y) += 64 +vdso_img-$(VDSOCOMPAT-y) += 32 + +vobjs := $(foreach F,$(vobjs-y),$(obj)/$F) + +$(obj)/vdso.o: $(obj)/vdso.so + +targets += vdso.lds $(vobjs-y) + +# Build the vDSO image C files and link them in. +vdso_img_objs := $(vdso_img-y:%=vdso-image-%.o) +vdso_img_cfiles := $(vdso_img-y:%=vdso-image-%.c) +vdso_img_sodbg := $(vdso_img-y:%=vdso%.so.dbg) +obj-y += $(vdso_img_objs) +targets += $(vdso_img_cfiles) +targets += $(vdso_img_sodbg) +.SECONDARY: $(vdso_img-y:%=$(obj)/vdso-image-%.c) \ + $(vdso_img-y:%=$(obj)/vdso%.so) + +export CPPFLAGS_vdso.lds += -P -C + +VDSO_LDFLAGS_vdso.lds = -m64 -Wl,-soname=linux-vdso.so.1 \ + -Wl,--no-undefined \ + -Wl,-z,max-page-size=8192 -Wl,-z,common-page-size=8192 \ + $(DISABLE_LTO) + +$(obj)/vdso64.so.dbg: $(src)/vdso.lds $(vobjs) FORCE + $(call if_changed,vdso) + +HOST_EXTRACFLAGS += -I$(srctree)/tools/include +hostprogs-y += vdso2c + +quiet_cmd_vdso2c = VDSO2C $@ +define cmd_vdso2c + $(obj)/vdso2c $< $(<:%.dbg=%) $@ +endef + +$(obj)/vdso-image-%.c: $(obj)/vdso%.so.dbg $(obj)/vdso%.so $(obj)/vdso2c FORCE + $(call if_changed,vdso2c) + +# +# Don't omit frame pointers for ease of userspace debugging, but do +# optimize sibling calls. +# +CFL := $(PROFILING) -mcmodel=medlow -fPIC -O2 -fasynchronous-unwind-tables \ + -m64 -ffixed-g2 -ffixed-g3 -fcall-used-g4 -fcall-used-g5 -ffixed-g6 \ + -ffixed-g7 $(filter -g%,$(KBUILD_CFLAGS)) \ + $(call cc-option, -fno-stack-protector) -fno-omit-frame-pointer \ + -foptimize-sibling-calls -DBUILD_VDSO + +$(vobjs): KBUILD_CFLAGS += $(CFL) + +# +# vDSO code runs in userspace and -pg doesn't help with profiling anyway. +# +CFLAGS_REMOVE_vdso-note.o = -pg +CFLAGS_REMOVE_vclock_gettime.o = -pg + +$(obj)/%.so: OBJCOPYFLAGS := -S +$(obj)/%.so: $(obj)/%.so.dbg + $(call if_changed,objcopy) + +CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds) +VDSO_LDFLAGS_vdso32.lds = -m32 -Wl,-m,elf32_sparc,-soname=linux-gate.so.1 + +#This makes sure the $(obj) subdirectory exists even though vdso32/ +#is not a kbuild sub-make subdirectory +override obj-dirs = $(dir $(obj)) $(obj)/vdso32/ + +targets += vdso32/vdso32.lds +targets += vdso32/vdso-note.o +targets += vdso32/vclock_gettime.o + +KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) -DBUILD_VDSO +$(obj)/vdso32.so.dbg: KBUILD_AFLAGS = $(KBUILD_AFLAGS_32) +$(obj)/vdso32.so.dbg: asflags-$(CONFIG_SPARC64) += -m32 + +KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS)) +KBUILD_CFLAGS_32 := $(filter-out -mcmodel=medlow,$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 += -m32 -msoft-float -fpic -mno-app-regs -ffixed-g7 +KBUILD_CFLAGS_32 += $(call cc-option, -fno-stack-protector) +KBUILD_CFLAGS_32 += $(call cc-option, -foptimize-sibling-calls) +KBUILD_CFLAGS_32 += -fno-omit-frame-pointer +KBUILD_CFLAGS_32 += -DDISABLE_BRANCH_PROFILING +KBUILD_CFLAGS_32 += -mv8plus +$(obj)/vdso32.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32) + +$(obj)/vdso32.so.dbg: FORCE \ + $(obj)/vdso32/vdso32.lds \ + $(obj)/vdso32/vclock_gettime.o \ + $(obj)/vdso32/vdso-note.o + $(call if_changed,vdso) + +# +# The DSO images are built using a special linker script. +# +quiet_cmd_vdso = VDSO $@ + cmd_vdso = $(CC) -nostdlib -o $@ \ + $(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \ + -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^) + +VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) \ + $(call cc-ldoption, -Wl$(comma)--build-id) -Wl,-Bsymbolic +GCOV_PROFILE := n + +# +# Install the unstripped copies of vdso*.so. If our toolchain supports +# build-id, install .build-id links as well. +# +quiet_cmd_vdso_install = INSTALL $(@:install_%=%) +define cmd_vdso_install + cp $< "$(MODLIB)/vdso/$(@:install_%=%)"; \ + if readelf -n $< |grep -q 'Build ID'; then \ + buildid=`readelf -n $< |grep 'Build ID' |sed -e 's/^.*Build ID: \(.*\)$$/\1/'`; \ + first=`echo $$buildid | cut -b-2`; \ + last=`echo $$buildid | cut -b3-`; \ + mkdir -p "$(MODLIB)/vdso/.build-id/$$first"; \ + ln -sf "../../$(@:install_%=%)" "$(MODLIB)/vdso/.build-id/$$first/$$last.debug"; \ + fi +endef + +vdso_img_insttargets := $(vdso_img_sodbg:%.dbg=install_%) + +$(MODLIB)/vdso: FORCE + @mkdir -p $(MODLIB)/vdso + +$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso FORCE + $(call cmd,vdso_install) + +PHONY += vdso_install $(vdso_img_insttargets) +vdso_install: $(vdso_img_insttargets) FORCE diff --git a/arch/sparc/vdso/vclock_gettime.c b/arch/sparc/vdso/vclock_gettime.c new file mode 100644 index 000000000000..3feb3d960ca5 --- /dev/null +++ b/arch/sparc/vdso/vclock_gettime.c @@ -0,0 +1,264 @@ +/* + * Copyright 2006 Andi Kleen, SUSE Labs. + * Subject to the GNU Public License, v.2 + * + * Fast user context implementation of clock_gettime, gettimeofday, and time. + * + * The code should have no internal unresolved relocations. + * Check with readelf after changing. + * Also alternative() doesn't work. + */ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +/* Disable profiling for userspace code: */ +#ifndef DISABLE_BRANCH_PROFILING +#define DISABLE_BRANCH_PROFILING +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef TICK_PRIV_BIT +#ifdef CONFIG_SPARC64 +#define TICK_PRIV_BIT (1UL << 63) +#else +#define TICK_PRIV_BIT (1ULL << 63) +#endif + +#define SYSCALL_STRING \ + "ta 0x6d;" \ + "sub %%g0, %%o0, %%o0;" \ + +#define SYSCALL_CLOBBERS \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", \ + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", \ + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", \ + "f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46", \ + "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62", \ + "cc", "memory" + +/* + * Compute the vvar page's address in the process address space, and return it + * as a pointer to the vvar_data. + */ +static notrace noinline struct vvar_data * +get_vvar_data(void) +{ + unsigned long ret; + + /* + * vdso data page is the first vDSO page so grab the return address + * and move up a page to get to the data page. + */ + ret = (unsigned long)__builtin_return_address(0); + ret &= ~(8192 - 1); + ret -= 8192; + + return (struct vvar_data *) ret; +} + +static notrace long +vdso_fallback_gettime(long clock, struct timespec *ts) +{ + register long num __asm__("g1") = __NR_clock_gettime; + register long o0 __asm__("o0") = clock; + register long o1 __asm__("o1") = (long) ts; + + __asm__ __volatile__(SYSCALL_STRING : "=r" (o0) : "r" (num), + "0" (o0), "r" (o1) : SYSCALL_CLOBBERS); + return o0; +} + +static notrace __always_inline long +vdso_fallback_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + register long num __asm__("g1") = __NR_gettimeofday; + register long o0 __asm__("o0") = (long) tv; + register long o1 __asm__("o1") = (long) tz; + + __asm__ __volatile__(SYSCALL_STRING : "=r" (o0) : "r" (num), + "0" (o0), "r" (o1) : SYSCALL_CLOBBERS); + return o0; +} + +#ifdef CONFIG_SPARC64 +static notrace noinline u64 +vread_tick(void) { + u64 ret; + + __asm__ __volatile__("rd %%asr24, %0 \n" + ".section .vread_tick_patch, \"ax\" \n" + "rd %%tick, %0 \n" + ".previous \n" + : "=&r" (ret)); + return ret & ~TICK_PRIV_BIT; +} +#else +static notrace noinline u64 +vread_tick(void) +{ + unsigned int lo, hi; + + __asm__ __volatile__("rd %%asr24, %%g1\n\t" + "srlx %%g1, 32, %1\n\t" + "srl %%g1, 0, %0\n" + ".section .vread_tick_patch, \"ax\" \n" + "rd %%tick, %%g1\n" + ".previous \n" + : "=&r" (lo), "=&r" (hi) + : + : "g1"); + return lo | ((u64)hi << 32); +} +#endif + +static notrace inline u64 +vgetsns(struct vvar_data *vvar) +{ + u64 v; + u64 cycles; + + cycles = vread_tick(); + v = (cycles - vvar->clock.cycle_last) & vvar->clock.mask; + return v * vvar->clock.mult; +} + +static notrace noinline int +do_realtime(struct vvar_data *vvar, struct timespec *ts) +{ + unsigned long seq; + u64 ns; + + ts->tv_nsec = 0; + do { + seq = vvar_read_begin(vvar); + ts->tv_sec = vvar->wall_time_sec; + ns = vvar->wall_time_snsec; + ns += vgetsns(vvar); + ns >>= vvar->clock.shift; + } while (unlikely(vvar_read_retry(vvar, seq))); + + timespec_add_ns(ts, ns); + + return 0; +} + +static notrace noinline int +do_monotonic(struct vvar_data *vvar, struct timespec *ts) +{ + unsigned long seq; + u64 ns; + + ts->tv_nsec = 0; + do { + seq = vvar_read_begin(vvar); + ts->tv_sec = vvar->monotonic_time_sec; + ns = vvar->monotonic_time_snsec; + ns += vgetsns(vvar); + ns >>= vvar->clock.shift; + } while (unlikely(vvar_read_retry(vvar, seq))); + + timespec_add_ns(ts, ns); + + return 0; +} + +static notrace noinline int +do_realtime_coarse(struct vvar_data *vvar, struct timespec *ts) +{ + unsigned long seq; + + do { + seq = vvar_read_begin(vvar); + ts->tv_sec = vvar->wall_time_coarse_sec; + ts->tv_nsec = vvar->wall_time_coarse_nsec; + } while (unlikely(vvar_read_retry(vvar, seq))); + return 0; +} + +static notrace noinline int +do_monotonic_coarse(struct vvar_data *vvar, struct timespec *ts) +{ + unsigned long seq; + + do { + seq = vvar_read_begin(vvar); + ts->tv_sec = vvar->monotonic_time_coarse_sec; + ts->tv_nsec = vvar->monotonic_time_coarse_nsec; + } while (unlikely(vvar_read_retry(vvar, seq))); + + return 0; +} + +notrace int +__vdso_clock_gettime(clockid_t clock, struct timespec *ts) +{ + struct vvar_data *vvd = get_vvar_data(); + + switch (clock) { + case CLOCK_REALTIME: + if (unlikely(vvd->vclock_mode == VCLOCK_NONE)) + break; + return do_realtime(vvd, ts); + case CLOCK_MONOTONIC: + if (unlikely(vvd->vclock_mode == VCLOCK_NONE)) + break; + return do_monotonic(vvd, ts); + case CLOCK_REALTIME_COARSE: + return do_realtime_coarse(vvd, ts); + case CLOCK_MONOTONIC_COARSE: + return do_monotonic_coarse(vvd, ts); + } + /* + * Unknown clock ID ? Fall back to the syscall. + */ + return vdso_fallback_gettime(clock, ts); +} +int +clock_gettime(clockid_t, struct timespec *) + __attribute__((weak, alias("__vdso_clock_gettime"))); + +notrace int +__vdso_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + struct vvar_data *vvd = get_vvar_data(); + + if (likely(vvd->vclock_mode != VCLOCK_NONE)) { + if (likely(tv != NULL)) { + union tstv_t { + struct timespec ts; + struct timeval tv; + } *tstv = (union tstv_t *) tv; + do_realtime(vvd, &tstv->ts); + /* + * Assign before dividing to ensure that the division is + * done in the type of tv_usec, not tv_nsec. + * + * There cannot be > 1 billion usec in a second: + * do_realtime() has already distributed such overflow + * into tv_sec. So we can assign it to an int safely. + */ + tstv->tv.tv_usec = tstv->ts.tv_nsec; + tstv->tv.tv_usec /= 1000; + } + if (unlikely(tz != NULL)) { + /* Avoid memcpy. Some old compilers fail to inline it */ + tz->tz_minuteswest = vvd->tz_minuteswest; + tz->tz_dsttime = vvd->tz_dsttime; + } + return 0; + } + return vdso_fallback_gettimeofday(tv, tz); +} +int +gettimeofday(struct timeval *, struct timezone *) + __attribute__((weak, alias("__vdso_gettimeofday"))); diff --git a/arch/sparc/vdso/vdso-layout.lds.S b/arch/sparc/vdso/vdso-layout.lds.S new file mode 100644 index 000000000000..f2c83abaca12 --- /dev/null +++ b/arch/sparc/vdso/vdso-layout.lds.S @@ -0,0 +1,104 @@ +/* + * Linker script for vDSO. This is an ELF shared object prelinked to + * its virtual address, and with only one read-only segment. + * This script controls its layout. + */ + +#if defined(BUILD_VDSO64) +# define SHDR_SIZE 64 +#elif defined(BUILD_VDSO32) +# define SHDR_SIZE 40 +#else +# error unknown VDSO target +#endif + +#define NUM_FAKE_SHDRS 7 + +SECTIONS +{ + /* + * User/kernel shared data is before the vDSO. This may be a little + * uglier than putting it after the vDSO, but it avoids issues with + * non-allocatable things that dangle past the end of the PT_LOAD + * segment. Page size is 8192 for both 64-bit and 32-bit vdso binaries + */ + + vvar_start = . -8192; + vvar_data = vvar_start; + + . = SIZEOF_HEADERS; + + .hash : { *(.hash) } :text + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + .dynamic : { *(.dynamic) } :text :dynamic + + .rodata : { + *(.rodata*) + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + + /* + * Ideally this would live in a C file: kept in here for + * compatibility with x86-64. + */ + VDSO_FAKE_SECTION_TABLE_START = .; + . = . + NUM_FAKE_SHDRS * SHDR_SIZE; + VDSO_FAKE_SECTION_TABLE_END = .; + } :text + + .fake_shstrtab : { *(.fake_shstrtab) } :text + + + .note : { *(.note.*) } :text :note + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + + + /* + * Text is well-separated from actual data: there's plenty of + * stuff that isn't used at runtime in between. + */ + + .text : { *(.text*) } :text =0x90909090, + + .vread_tick_patch : { + vread_tick_patch_start = .; + *(.vread_tick_patch) + vread_tick_patch_end = .; + } + + /DISCARD/ : { + *(.discard) + *(.discard.*) + *(__bug_table) + } +} + +/* + * Very old versions of ld do not recognize this name token; use the constant. + */ +#define PT_GNU_EH_FRAME 0x6474e550 + +/* + * We must supply the ELF program headers explicitly to get just one + * PT_LOAD segment, and set the flags explicitly to make segments read-only. + */ +PHDRS +{ + text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + note PT_NOTE FLAGS(4); /* PF_R */ + eh_frame_hdr PT_GNU_EH_FRAME; +} diff --git a/arch/sparc/vdso/vdso-note.S b/arch/sparc/vdso/vdso-note.S new file mode 100644 index 000000000000..79a071e4357e --- /dev/null +++ b/arch/sparc/vdso/vdso-note.S @@ -0,0 +1,12 @@ +/* + * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text. + * Here we can supply some information useful to userland. + */ + +#include +#include +#include + +ELFNOTE_START(Linux, 0, "a") + .long LINUX_VERSION_CODE +ELFNOTE_END diff --git a/arch/sparc/vdso/vdso.lds.S b/arch/sparc/vdso/vdso.lds.S new file mode 100644 index 000000000000..f3caa29a331c --- /dev/null +++ b/arch/sparc/vdso/vdso.lds.S @@ -0,0 +1,25 @@ +/* + * Linker script for 64-bit vDSO. + * We #include the file to define the layout details. + * + * This file defines the version script giving the user-exported symbols in + * the DSO. + */ + +#define BUILD_VDSO64 + +#include "vdso-layout.lds.S" + +/* + * This controls what userland symbols we export from the vDSO. + */ +VERSION { + LINUX_2.6 { + global: + clock_gettime; + __vdso_clock_gettime; + gettimeofday; + __vdso_gettimeofday; + local: *; + }; +} diff --git a/arch/sparc/vdso/vdso2c.c b/arch/sparc/vdso/vdso2c.c new file mode 100644 index 000000000000..9f5b1cd6d51d --- /dev/null +++ b/arch/sparc/vdso/vdso2c.c @@ -0,0 +1,234 @@ +/* + * vdso2c - A vdso image preparation tool + * Copyright (c) 2014 Andy Lutomirski and others + * Licensed under the GPL v2 + * + * vdso2c requires stripped and unstripped input. It would be trivial + * to fully strip the input in here, but, for reasons described below, + * we need to write a section table. Doing this is more or less + * equivalent to dropping all non-allocatable sections, but it's + * easier to let objcopy handle that instead of doing it ourselves. + * If we ever need to do something fancier than what objcopy provides, + * it would be straightforward to add here. + * + * We keep a section table for a few reasons: + * + * Binutils has issues debugging the vDSO: it reads the section table to + * find SHT_NOTE; it won't look at PT_NOTE for the in-memory vDSO, which + * would break build-id if we removed the section table. Binutils + * also requires that shstrndx != 0. See: + * https://sourceware.org/bugzilla/show_bug.cgi?id=17064 + * + * elfutils might not look for PT_NOTE if there is a section table at + * all. I don't know whether this matters for any practical purpose. + * + * For simplicity, rather than hacking up a partial section table, we + * just write a mostly complete one. We omit non-dynamic symbols, + * though, since they're rather large. + * + * Once binutils gets fixed, we might be able to drop this for all but + * the 64-bit vdso, since build-id only works in kernel RPMs, and + * systems that update to new enough kernel RPMs will likely update + * binutils in sync. build-id has never worked for home-built kernel + * RPMs without manual symlinking, and I suspect that no one ever does + * that. + */ + +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +const char *outfilename; + +/* Symbols that we need in vdso2c. */ +enum { + sym_vvar_start, + sym_VDSO_FAKE_SECTION_TABLE_START, + sym_VDSO_FAKE_SECTION_TABLE_END, + sym_vread_tick, + sym_vread_tick_patch_start, + sym_vread_tick_patch_end +}; + +struct vdso_sym { + const char *name; + int export; +}; + +struct vdso_sym required_syms[] = { + [sym_vvar_start] = {"vvar_start", 1}, + [sym_VDSO_FAKE_SECTION_TABLE_START] = { + "VDSO_FAKE_SECTION_TABLE_START", 0 + }, + [sym_VDSO_FAKE_SECTION_TABLE_END] = { + "VDSO_FAKE_SECTION_TABLE_END", 0 + }, + [sym_vread_tick] = {"vread_tick", 1}, + [sym_vread_tick_patch_start] = {"vread_tick_patch_start", 1}, + [sym_vread_tick_patch_end] = {"vread_tick_patch_end", 1} +}; + +__attribute__((format(printf, 1, 2))) __attribute__((noreturn)) +static void fail(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "Error: "); + vfprintf(stderr, format, ap); + if (outfilename) + unlink(outfilename); + exit(1); + va_end(ap); +} + +/* + * Evil macros for big-endian reads and writes + */ +#define GBE(x, bits, ifnot) \ + __builtin_choose_expr( \ + (sizeof(*(x)) == bits/8), \ + (__typeof__(*(x)))get_unaligned_be##bits(x), ifnot) + +#define LAST_GBE(x) \ + __builtin_choose_expr(sizeof(*(x)) == 1, *(x), (void)(0)) + +#define GET_BE(x) \ + GBE(x, 64, GBE(x, 32, GBE(x, 16, LAST_GBE(x)))) + +#define PBE(x, val, bits, ifnot) \ + __builtin_choose_expr( \ + (sizeof(*(x)) == bits/8), \ + put_unaligned_be##bits((val), (x)), ifnot) + +#define LAST_PBE(x, val) \ + __builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), (void)(0)) + +#define PUT_BE(x, val) \ + PBE(x, val, 64, PBE(x, val, 32, PBE(x, val, 16, LAST_PBE(x, val)))) + +#define NSYMS ARRAY_SIZE(required_syms) + +#define BITSFUNC3(name, bits, suffix) name##bits##suffix +#define BITSFUNC2(name, bits, suffix) BITSFUNC3(name, bits, suffix) +#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS, ) + +#define INT_BITS BITSFUNC2(int, ELF_BITS, _t) + +#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x +#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) +#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) + +#define ELF_BITS 64 +#include "vdso2c.h" +#undef ELF_BITS + +#define ELF_BITS 32 +#include "vdso2c.h" +#undef ELF_BITS + +static void go(void *raw_addr, size_t raw_len, + void *stripped_addr, size_t stripped_len, + FILE *outfile, const char *name) +{ + Elf64_Ehdr *hdr = (Elf64_Ehdr *)raw_addr; + + if (hdr->e_ident[EI_CLASS] == ELFCLASS64) { + go64(raw_addr, raw_len, stripped_addr, stripped_len, + outfile, name); + } else if (hdr->e_ident[EI_CLASS] == ELFCLASS32) { + go32(raw_addr, raw_len, stripped_addr, stripped_len, + outfile, name); + } else { + fail("unknown ELF class\n"); + } +} + +static void map_input(const char *name, void **addr, size_t *len, int prot) +{ + off_t tmp_len; + + int fd = open(name, O_RDONLY); + + if (fd == -1) + err(1, "%s", name); + + tmp_len = lseek(fd, 0, SEEK_END); + if (tmp_len == (off_t)-1) + err(1, "lseek"); + *len = (size_t)tmp_len; + + *addr = mmap(NULL, tmp_len, prot, MAP_PRIVATE, fd, 0); + if (*addr == MAP_FAILED) + err(1, "mmap"); + + close(fd); +} + +int main(int argc, char **argv) +{ + size_t raw_len, stripped_len; + void *raw_addr, *stripped_addr; + FILE *outfile; + char *name, *tmp; + int namelen; + + if (argc != 4) { + printf("Usage: vdso2c RAW_INPUT STRIPPED_INPUT OUTPUT\n"); + return 1; + } + + /* + * Figure out the struct name. If we're writing to a .so file, + * generate raw output insted. + */ + name = strdup(argv[3]); + namelen = strlen(name); + if (namelen >= 3 && !strcmp(name + namelen - 3, ".so")) { + name = NULL; + } else { + tmp = strrchr(name, '/'); + if (tmp) + name = tmp + 1; + tmp = strchr(name, '.'); + if (tmp) + *tmp = '\0'; + for (tmp = name; *tmp; tmp++) + if (*tmp == '-') + *tmp = '_'; + } + + map_input(argv[1], &raw_addr, &raw_len, PROT_READ); + map_input(argv[2], &stripped_addr, &stripped_len, PROT_READ); + + outfilename = argv[3]; + outfile = fopen(outfilename, "w"); + if (!outfile) + err(1, "%s", argv[2]); + + go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name); + + munmap(raw_addr, raw_len); + munmap(stripped_addr, stripped_len); + fclose(outfile); + + return 0; +} diff --git a/arch/sparc/vdso/vdso2c.h b/arch/sparc/vdso/vdso2c.h new file mode 100644 index 000000000000..808decb0f7be --- /dev/null +++ b/arch/sparc/vdso/vdso2c.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +/* + * This file is included up to twice from vdso2c.c. It generates code for + * 32-bit and 64-bit vDSOs. We will eventually need both for 64-bit builds, + * since 32-bit vDSOs will then be built for 32-bit userspace. + */ + +static void BITSFUNC(go)(void *raw_addr, size_t raw_len, + void *stripped_addr, size_t stripped_len, + FILE *outfile, const char *name) +{ + int found_load = 0; + unsigned long load_size = -1; /* Work around bogus warning */ + unsigned long mapping_size; + int i; + unsigned long j; + + ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr; + ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr; + ELF(Dyn) *dyn = 0, *dyn_end = 0; + INT_BITS syms[NSYMS] = {}; + + ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_BE(&hdr->e_phoff)); + + /* Walk the segment table. */ + for (i = 0; i < GET_BE(&hdr->e_phnum); i++) { + if (GET_BE(&pt[i].p_type) == PT_LOAD) { + if (found_load) + fail("multiple PT_LOAD segs\n"); + + if (GET_BE(&pt[i].p_offset) != 0 || + GET_BE(&pt[i].p_vaddr) != 0) + fail("PT_LOAD in wrong place\n"); + + if (GET_BE(&pt[i].p_memsz) != GET_BE(&pt[i].p_filesz)) + fail("cannot handle memsz != filesz\n"); + + load_size = GET_BE(&pt[i].p_memsz); + found_load = 1; + } else if (GET_BE(&pt[i].p_type) == PT_DYNAMIC) { + dyn = raw_addr + GET_BE(&pt[i].p_offset); + dyn_end = raw_addr + GET_BE(&pt[i].p_offset) + + GET_BE(&pt[i].p_memsz); + } + } + if (!found_load) + fail("no PT_LOAD seg\n"); + + if (stripped_len < load_size) + fail("stripped input is too short\n"); + + /* Walk the dynamic table */ + for (i = 0; dyn + i < dyn_end && + GET_BE(&dyn[i].d_tag) != DT_NULL; i++) { + typeof(dyn[i].d_tag) tag = GET_BE(&dyn[i].d_tag); + typeof(dyn[i].d_un.d_val) val = GET_BE(&dyn[i].d_un.d_val); + + if ((tag == DT_RELSZ || tag == DT_RELASZ) && (val != 0)) + fail("vdso image contains dynamic relocations\n"); + } + + /* Walk the section table */ + for (i = 0; i < GET_BE(&hdr->e_shnum); i++) { + ELF(Shdr) *sh = raw_addr + GET_BE(&hdr->e_shoff) + + GET_BE(&hdr->e_shentsize) * i; + if (GET_BE(&sh->sh_type) == SHT_SYMTAB) + symtab_hdr = sh; + } + + if (!symtab_hdr) + fail("no symbol table\n"); + + strtab_hdr = raw_addr + GET_BE(&hdr->e_shoff) + + GET_BE(&hdr->e_shentsize) * GET_BE(&symtab_hdr->sh_link); + + /* Walk the symbol table */ + for (i = 0; + i < GET_BE(&symtab_hdr->sh_size) / GET_BE(&symtab_hdr->sh_entsize); + i++) { + int k; + + ELF(Sym) *sym = raw_addr + GET_BE(&symtab_hdr->sh_offset) + + GET_BE(&symtab_hdr->sh_entsize) * i; + const char *name = raw_addr + GET_BE(&strtab_hdr->sh_offset) + + GET_BE(&sym->st_name); + + for (k = 0; k < NSYMS; k++) { + if (!strcmp(name, required_syms[k].name)) { + if (syms[k]) { + fail("duplicate symbol %s\n", + required_syms[k].name); + } + + /* + * Careful: we use negative addresses, but + * st_value is unsigned, so we rely + * on syms[k] being a signed type of the + * correct width. + */ + syms[k] = GET_BE(&sym->st_value); + } + } + } + + /* Validate mapping addresses. */ + if (syms[sym_vvar_start] % 8192) + fail("vvar_begin must be a multiple of 8192\n"); + + if (!name) { + fwrite(stripped_addr, stripped_len, 1, outfile); + return; + } + + mapping_size = (stripped_len + 8191) / 8192 * 8192; + + fprintf(outfile, "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n"); + fprintf(outfile, "#include \n"); + fprintf(outfile, "#include \n"); + fprintf(outfile, "\n"); + fprintf(outfile, + "static unsigned char raw_data[%lu] __ro_after_init __aligned(8192)= {", + mapping_size); + for (j = 0; j < stripped_len; j++) { + if (j % 10 == 0) + fprintf(outfile, "\n\t"); + fprintf(outfile, "0x%02X, ", + (int)((unsigned char *)stripped_addr)[j]); + } + fprintf(outfile, "\n};\n\n"); + + fprintf(outfile, "const struct vdso_image %s_builtin = {\n", name); + fprintf(outfile, "\t.data = raw_data,\n"); + fprintf(outfile, "\t.size = %lu,\n", mapping_size); + for (i = 0; i < NSYMS; i++) { + if (required_syms[i].export && syms[i]) + fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n", + required_syms[i].name, (int64_t)syms[i]); + } + fprintf(outfile, "};\n"); +} diff --git a/arch/sparc/vdso/vdso32/.gitignore b/arch/sparc/vdso/vdso32/.gitignore new file mode 100644 index 000000000000..e45fba9d0ced --- /dev/null +++ b/arch/sparc/vdso/vdso32/.gitignore @@ -0,0 +1 @@ +vdso32.lds diff --git a/arch/sparc/vdso/vdso32/vclock_gettime.c b/arch/sparc/vdso/vdso32/vclock_gettime.c new file mode 100644 index 000000000000..026abb3b826c --- /dev/null +++ b/arch/sparc/vdso/vdso32/vclock_gettime.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +#define BUILD_VDSO32 + +#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE +#undef CONFIG_OPTIMIZE_INLINING +#endif + +#ifdef CONFIG_SPARC64 + +/* + * in case of a 32 bit VDSO for a 64 bit kernel fake a 32 bit kernel + * configuration + */ +#undef CONFIG_64BIT +#undef CONFIG_SPARC64 +#define BUILD_VDSO32_64 +#define CONFIG_32BIT +#undef CONFIG_QUEUED_RWLOCKS +#undef CONFIG_QUEUED_SPINLOCKS + +#endif + +#include "../vclock_gettime.c" diff --git a/arch/sparc/vdso/vdso32/vdso-note.S b/arch/sparc/vdso/vdso32/vdso-note.S new file mode 100644 index 000000000000..e234983cf0d8 --- /dev/null +++ b/arch/sparc/vdso/vdso32/vdso-note.S @@ -0,0 +1,12 @@ +/* + * This supplies .note.* sections to go into the PT_NOTE inside the vDSO + * text. Here we can supply some information useful to userland. + */ + +#include +#include +#include + +ELFNOTE_START(Linux, 0, "a") + .long LINUX_VERSION_CODE +ELFNOTE_END diff --git a/arch/sparc/vdso/vdso32/vdso32.lds.S b/arch/sparc/vdso/vdso32/vdso32.lds.S new file mode 100644 index 000000000000..53575ee154c4 --- /dev/null +++ b/arch/sparc/vdso/vdso32/vdso32.lds.S @@ -0,0 +1,24 @@ +/* + * Linker script for sparc32 vDSO + * We #include the file to define the layout details. + * + * This file defines the version script giving the user-exported symbols in + * the DSO. + */ + +#define BUILD_VDSO32 +#include "../vdso-layout.lds.S" + +/* + * This controls what userland symbols we export from the vDSO. + */ +VERSION { + LINUX_2.6 { + global: + clock_gettime; + __vdso_clock_gettime; + gettimeofday; + __vdso_gettimeofday; + local: *; + }; +} diff --git a/arch/sparc/vdso/vma.c b/arch/sparc/vdso/vma.c new file mode 100644 index 000000000000..0a6f50098e23 --- /dev/null +++ b/arch/sparc/vdso/vma.c @@ -0,0 +1,268 @@ +/* + * Set up the VMAs to tell the VM about the vDSO. + * Copyright 2007 Andi Kleen, SUSE Labs. + * Subject to the GPL, v.2 + */ + +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned int __read_mostly vdso_enabled = 1; + +static struct vm_special_mapping vvar_mapping = { + .name = "[vvar]" +}; + +#ifdef CONFIG_SPARC64 +static struct vm_special_mapping vdso_mapping64 = { + .name = "[vdso]" +}; +#endif + +#ifdef CONFIG_COMPAT +static struct vm_special_mapping vdso_mapping32 = { + .name = "[vdso]" +}; +#endif + +struct vvar_data *vvar_data; + +#define SAVE_INSTR_SIZE 4 + +/* + * Allocate pages for the vdso and vvar, and copy in the vdso text from the + * kernel image. + */ +int __init init_vdso_image(const struct vdso_image *image, + struct vm_special_mapping *vdso_mapping) +{ + int i; + struct page *dp, **dpp = NULL; + int dnpages = 0; + struct page *cp, **cpp = NULL; + int cnpages = (image->size) / PAGE_SIZE; + + /* + * First, the vdso text. This is initialied data, an integral number of + * pages long. + */ + if (WARN_ON(image->size % PAGE_SIZE != 0)) + goto oom; + + cpp = kcalloc(cnpages, sizeof(struct page *), GFP_KERNEL); + vdso_mapping->pages = cpp; + + if (!cpp) + goto oom; + + if (vdso_fix_stick) { + /* + * If the system uses %tick instead of %stick, patch the VDSO + * with instruction reading %tick instead of %stick. + */ + unsigned int j, k = SAVE_INSTR_SIZE; + unsigned char *data = image->data; + + for (j = image->sym_vread_tick_patch_start; + j < image->sym_vread_tick_patch_end; j++) { + + data[image->sym_vread_tick + k] = data[j]; + k++; + } + } + + for (i = 0; i < cnpages; i++) { + cp = alloc_page(GFP_KERNEL); + if (!cp) + goto oom; + cpp[i] = cp; + copy_page(page_address(cp), image->data + i * PAGE_SIZE); + } + + /* + * Now the vvar page. This is uninitialized data. + */ + + if (vvar_data == NULL) { + dnpages = (sizeof(struct vvar_data) / PAGE_SIZE) + 1; + if (WARN_ON(dnpages != 1)) + goto oom; + dpp = kcalloc(dnpages, sizeof(struct page *), GFP_KERNEL); + vvar_mapping.pages = dpp; + + if (!dpp) + goto oom; + + dp = alloc_page(GFP_KERNEL); + if (!dp) + goto oom; + + dpp[0] = dp; + vvar_data = page_address(dp); + memset(vvar_data, 0, PAGE_SIZE); + + vvar_data->seq = 0; + } + + return 0; + oom: + if (cpp != NULL) { + for (i = 0; i < cnpages; i++) { + if (cpp[i] != NULL) + __free_page(cpp[i]); + } + kfree(cpp); + vdso_mapping->pages = NULL; + } + + if (dpp != NULL) { + for (i = 0; i < dnpages; i++) { + if (dpp[i] != NULL) + __free_page(dpp[i]); + } + kfree(dpp); + vvar_mapping.pages = NULL; + } + + pr_warn("Cannot allocate vdso\n"); + vdso_enabled = 0; + return -ENOMEM; +} + +static int __init init_vdso(void) +{ + int err = 0; +#ifdef CONFIG_SPARC64 + err = init_vdso_image(&vdso_image_64_builtin, &vdso_mapping64); + if (err) + return err; +#endif + +#ifdef CONFIG_COMPAT + err = init_vdso_image(&vdso_image_32_builtin, &vdso_mapping32); +#endif + return err; + +} +subsys_initcall(init_vdso); + +struct linux_binprm; + +/* Shuffle the vdso up a bit, randomly. */ +static unsigned long vdso_addr(unsigned long start, unsigned int len) +{ + unsigned int offset; + + /* This loses some more bits than a modulo, but is cheaper */ + offset = get_random_int() & (PTRS_PER_PTE - 1); + return start + (offset << PAGE_SHIFT); +} + +static int map_vdso(const struct vdso_image *image, + struct vm_special_mapping *vdso_mapping) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + unsigned long text_start, addr = 0; + int ret = 0; + + down_write(&mm->mmap_sem); + + /* + * First, get an unmapped region: then randomize it, and make sure that + * region is free. + */ + if (current->flags & PF_RANDOMIZE) { + addr = get_unmapped_area(NULL, 0, + image->size - image->sym_vvar_start, + 0, 0); + if (IS_ERR_VALUE(addr)) { + ret = addr; + goto up_fail; + } + addr = vdso_addr(addr, image->size - image->sym_vvar_start); + } + addr = get_unmapped_area(NULL, addr, + image->size - image->sym_vvar_start, 0, 0); + if (IS_ERR_VALUE(addr)) { + ret = addr; + goto up_fail; + } + + text_start = addr - image->sym_vvar_start; + current->mm->context.vdso = (void __user *)text_start; + + /* + * MAYWRITE to allow gdb to COW and set breakpoints + */ + vma = _install_special_mapping(mm, + text_start, + image->size, + VM_READ|VM_EXEC| + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, + vdso_mapping); + + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto up_fail; + } + + vma = _install_special_mapping(mm, + addr, + -image->sym_vvar_start, + VM_READ|VM_MAYREAD, + &vvar_mapping); + + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + do_munmap(mm, text_start, image->size, NULL); + } + +up_fail: + if (ret) + current->mm->context.vdso = NULL; + + up_write(&mm->mmap_sem); + return ret; +} + +int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +{ + + if (!vdso_enabled) + return 0; + +#if defined CONFIG_COMPAT + if (!(is_32bit_task())) + return map_vdso(&vdso_image_64_builtin, &vdso_mapping64); + else + return map_vdso(&vdso_image_32_builtin, &vdso_mapping32); +#else + return map_vdso(&vdso_image_64_builtin, &vdso_mapping64); +#endif + +} + +static __init int vdso_setup(char *s) +{ + int err; + unsigned long val; + + err = kstrtoul(s, 10, &val); + vdso_enabled = val; + return err; +} +__setup("vdso=", vdso_setup); diff --git a/arch/tile/include/asm/compat.h b/arch/tile/include/asm/compat.h index c14e36f008c8..62a7b83025dd 100644 --- a/arch/tile/include/asm/compat.h +++ b/arch/tile/include/asm/compat.h @@ -173,7 +173,6 @@ typedef struct compat_siginfo { } compat_siginfo_t; #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL struct compat_ipc64_perm { compat_key_t key; diff --git a/arch/tile/include/asm/topology.h b/arch/tile/include/asm/topology.h index b11d5fcd2c41..635a0a4596f0 100644 --- a/arch/tile/include/asm/topology.h +++ b/arch/tile/include/asm/topology.h @@ -29,12 +29,6 @@ static inline int cpu_to_node(int cpu) return cpu_2_node[cpu]; } -/* - * Returns the number of the node containing Node 'node'. - * This architecture is flat, so it is a pretty simple function! - */ -#define parent_node(node) (node) - /* Returns a bitmask of CPUs on Node 'node'. */ static inline const struct cpumask *cpumask_of_node(int node) { diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common index d9280482a2f8..c68add8df3ae 100644 --- a/arch/um/Kconfig.common +++ b/arch/um/Kconfig.common @@ -10,7 +10,6 @@ config UML select HAVE_DEBUG_KMEMLEAK select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES - select GENERIC_IO select GENERIC_CLOCKEVENTS select HAVE_GCC_PLUGINS select TTY # Needed for line.c diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index c366c0adeb40..1943aebadede 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -130,10 +130,6 @@ $(obj)/vdsox32.so.dbg: $(src)/vdsox32.lds $(vobjx32s) FORCE CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds) VDSO_LDFLAGS_vdso32.lds = -m32 -Wl,-m,elf_i386 -Wl,-soname=linux-gate.so.1 -# This makes sure the $(obj) subdirectory exists even though vdso32/ -# is not a kbuild sub-make subdirectory. -override obj-dirs = $(dir $(obj)) $(obj)/vdso32/ - targets += vdso32/vdso32.lds targets += vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o targets += vdso32/vclock_gettime.o diff --git a/arch/x86/include/asm/compat.h b/arch/x86/include/asm/compat.h index a600a6cda9ec..2cbd75dd2fd3 100644 --- a/arch/x86/include/asm/compat.h +++ b/arch/x86/include/asm/compat.h @@ -210,7 +210,6 @@ typedef struct compat_siginfo { } compat_siginfo_t; #define COMPAT_OFF_T_MAX 0x7fffffff -#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL struct compat_ipc64_perm { compat_key_t key; diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 90cb82dbba57..570e8bb1f386 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -22,7 +22,7 @@ obj-y += common.o obj-y += rdrand.o obj-y += match.o obj-y += bugs.o -obj-$(CONFIG_CPU_FREQ) += aperfmperf.o +obj-y += aperfmperf.o obj-y += cpuid-deps.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/arch/x86/kernel/cpu/aperfmperf.c b/arch/x86/kernel/cpu/aperfmperf.c index 957813e0180d..7eba34df54c3 100644 --- a/arch/x86/kernel/cpu/aperfmperf.c +++ b/arch/x86/kernel/cpu/aperfmperf.c @@ -14,6 +14,8 @@ #include #include +#include "cpu.h" + struct aperfmperf_sample { unsigned int khz; ktime_t time; @@ -24,7 +26,7 @@ struct aperfmperf_sample { static DEFINE_PER_CPU(struct aperfmperf_sample, samples); #define APERFMPERF_CACHE_THRESHOLD_MS 10 -#define APERFMPERF_REFRESH_DELAY_MS 20 +#define APERFMPERF_REFRESH_DELAY_MS 10 #define APERFMPERF_STALE_THRESHOLD_MS 1000 /* @@ -38,8 +40,6 @@ static void aperfmperf_snapshot_khz(void *dummy) u64 aperf, aperf_delta; u64 mperf, mperf_delta; struct aperfmperf_sample *s = this_cpu_ptr(&samples); - ktime_t now = ktime_get(); - s64 time_delta = ktime_ms_delta(now, s->time); unsigned long flags; local_irq_save(flags); @@ -57,38 +57,68 @@ static void aperfmperf_snapshot_khz(void *dummy) if (mperf_delta == 0) return; - s->time = now; + s->time = ktime_get(); s->aperf = aperf; s->mperf = mperf; - - /* If the previous iteration was too long ago, discard it. */ - if (time_delta > APERFMPERF_STALE_THRESHOLD_MS) - s->khz = 0; - else - s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); + s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); } -unsigned int arch_freq_get_on_cpu(int cpu) +static bool aperfmperf_snapshot_cpu(int cpu, ktime_t now, bool wait) { - s64 time_delta; - unsigned int khz; + s64 time_delta = ktime_ms_delta(now, per_cpu(samples.time, cpu)); + + /* Don't bother re-computing within the cache threshold time. */ + if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS) + return true; + + smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, wait); + + /* Return false if the previous iteration was too long ago. */ + return time_delta <= APERFMPERF_STALE_THRESHOLD_MS; +} +unsigned int aperfmperf_get_khz(int cpu) +{ if (!cpu_khz) return 0; if (!static_cpu_has(X86_FEATURE_APERFMPERF)) return 0; - /* Don't bother re-computing within the cache threshold time. */ - time_delta = ktime_ms_delta(ktime_get(), per_cpu(samples.time, cpu)); - khz = per_cpu(samples.khz, cpu); - if (khz && time_delta < APERFMPERF_CACHE_THRESHOLD_MS) - return khz; + aperfmperf_snapshot_cpu(cpu, ktime_get(), true); + return per_cpu(samples.khz, cpu); +} - smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); - khz = per_cpu(samples.khz, cpu); - if (khz) - return khz; +void arch_freq_prepare_all(void) +{ + ktime_t now = ktime_get(); + bool wait = false; + int cpu; + + if (!cpu_khz) + return; + + if (!static_cpu_has(X86_FEATURE_APERFMPERF)) + return; + + for_each_online_cpu(cpu) + if (!aperfmperf_snapshot_cpu(cpu, now, false)) + wait = true; + + if (wait) + msleep(APERFMPERF_REFRESH_DELAY_MS); +} + +unsigned int arch_freq_get_on_cpu(int cpu) +{ + if (!cpu_khz) + return 0; + + if (!static_cpu_has(X86_FEATURE_APERFMPERF)) + return 0; + + if (aperfmperf_snapshot_cpu(cpu, ktime_get(), true)) + return per_cpu(samples.khz, cpu); msleep(APERFMPERF_REFRESH_DELAY_MS); smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index f52a370b6c00..e806b11a99af 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h @@ -47,4 +47,7 @@ extern const struct cpu_dev *const __x86_cpu_dev_start[], extern void get_cpu_cap(struct cpuinfo_x86 *c); extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c); + +unsigned int aperfmperf_get_khz(int cpu); + #endif /* ARCH_X86_CPU_H */ diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c index 6b7e17bf0b71..e7ecedafa1c8 100644 --- a/arch/x86/kernel/cpu/proc.c +++ b/arch/x86/kernel/cpu/proc.c @@ -5,6 +5,8 @@ #include #include +#include "cpu.h" + /* * Get CPU information for use by the procfs. */ @@ -78,8 +80,10 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, "microcode\t: 0x%x\n", c->microcode); if (cpu_has(c, X86_FEATURE_TSC)) { - unsigned int freq = cpufreq_quick_get(cpu); + unsigned int freq = aperfmperf_get_khz(cpu); + if (!freq) + freq = cpufreq_quick_get(cpu); if (!freq) freq = cpu_khz; seq_printf(m, "cpu MHz\t\t: %u.%03u\n", diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index b71daed3cca2..59e13a79c2e3 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -3671,6 +3671,13 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) u32 ecx = msr->index; u64 data = msr->data; switch (ecx) { + case MSR_IA32_CR_PAT: + if (!kvm_mtrr_valid(vcpu, MSR_IA32_CR_PAT, data)) + return 1; + vcpu->arch.pat = data; + svm->vmcb->save.g_pat = data; + mark_dirty(svm->vmcb, VMCB_NPT); + break; case MSR_IA32_TSC: kvm_write_tsc(vcpu, msr); break; diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 7c3522a989d0..714a0673ec3c 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -70,6 +70,9 @@ MODULE_DEVICE_TABLE(x86cpu, vmx_cpu_id); static bool __read_mostly enable_vpid = 1; module_param_named(vpid, enable_vpid, bool, 0444); +static bool __read_mostly enable_vnmi = 1; +module_param_named(vnmi, enable_vnmi, bool, S_IRUGO); + static bool __read_mostly flexpriority_enabled = 1; module_param_named(flexpriority, flexpriority_enabled, bool, S_IRUGO); @@ -202,6 +205,10 @@ struct loaded_vmcs { bool nmi_known_unmasked; unsigned long vmcs_host_cr3; /* May not match real cr3 */ unsigned long vmcs_host_cr4; /* May not match real cr4 */ + /* Support for vnmi-less CPUs */ + int soft_vnmi_blocked; + ktime_t entry_time; + s64 vnmi_blocked_time; struct list_head loaded_vmcss_on_cpu_link; }; @@ -1291,6 +1298,11 @@ static inline bool cpu_has_vmx_invpcid(void) SECONDARY_EXEC_ENABLE_INVPCID; } +static inline bool cpu_has_virtual_nmis(void) +{ + return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS; +} + static inline bool cpu_has_vmx_wbinvd_exit(void) { return vmcs_config.cpu_based_2nd_exec_ctrl & @@ -1348,11 +1360,6 @@ static inline bool nested_cpu_has2(struct vmcs12 *vmcs12, u32 bit) (vmcs12->secondary_vm_exec_control & bit); } -static inline bool nested_cpu_has_virtual_nmis(struct vmcs12 *vmcs12) -{ - return vmcs12->pin_based_vm_exec_control & PIN_BASED_VIRTUAL_NMIS; -} - static inline bool nested_cpu_has_preemption_timer(struct vmcs12 *vmcs12) { return vmcs12->pin_based_vm_exec_control & @@ -3712,9 +3719,9 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf) &_vmexit_control) < 0) return -EIO; - min = PIN_BASED_EXT_INTR_MASK | PIN_BASED_NMI_EXITING | - PIN_BASED_VIRTUAL_NMIS; - opt = PIN_BASED_POSTED_INTR | PIN_BASED_VMX_PREEMPTION_TIMER; + min = PIN_BASED_EXT_INTR_MASK | PIN_BASED_NMI_EXITING; + opt = PIN_BASED_VIRTUAL_NMIS | PIN_BASED_POSTED_INTR | + PIN_BASED_VMX_PREEMPTION_TIMER; if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PINBASED_CTLS, &_pin_based_exec_control) < 0) return -EIO; @@ -5232,6 +5239,10 @@ static u32 vmx_pin_based_exec_ctrl(struct vcpu_vmx *vmx) if (!kvm_vcpu_apicv_active(&vmx->vcpu)) pin_based_exec_ctrl &= ~PIN_BASED_POSTED_INTR; + + if (!enable_vnmi) + pin_based_exec_ctrl &= ~PIN_BASED_VIRTUAL_NMIS; + /* Enable the preemption timer dynamically */ pin_based_exec_ctrl &= ~PIN_BASED_VMX_PREEMPTION_TIMER; return pin_based_exec_ctrl; @@ -5666,7 +5677,8 @@ static void enable_irq_window(struct kvm_vcpu *vcpu) static void enable_nmi_window(struct kvm_vcpu *vcpu) { - if (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & GUEST_INTR_STATE_STI) { + if (!enable_vnmi || + vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & GUEST_INTR_STATE_STI) { enable_irq_window(vcpu); return; } @@ -5706,6 +5718,19 @@ static void vmx_inject_nmi(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); + if (!enable_vnmi) { + /* + * Tracking the NMI-blocked state in software is built upon + * finding the next open IRQ window. This, in turn, depends on + * well-behaving guests: They have to keep IRQs disabled at + * least as long as the NMI handler runs. Otherwise we may + * cause NMI nesting, maybe breaking the guest. But as this is + * highly unlikely, we can live with the residual risk. + */ + vmx->loaded_vmcs->soft_vnmi_blocked = 1; + vmx->loaded_vmcs->vnmi_blocked_time = 0; + } + ++vcpu->stat.nmi_injections; vmx->loaded_vmcs->nmi_known_unmasked = false; @@ -5724,6 +5749,8 @@ static bool vmx_get_nmi_mask(struct kvm_vcpu *vcpu) struct vcpu_vmx *vmx = to_vmx(vcpu); bool masked; + if (!enable_vnmi) + return vmx->loaded_vmcs->soft_vnmi_blocked; if (vmx->loaded_vmcs->nmi_known_unmasked) return false; masked = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & GUEST_INTR_STATE_NMI; @@ -5735,13 +5762,20 @@ static void vmx_set_nmi_mask(struct kvm_vcpu *vcpu, bool masked) { struct vcpu_vmx *vmx = to_vmx(vcpu); - vmx->loaded_vmcs->nmi_known_unmasked = !masked; - if (masked) - vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, - GUEST_INTR_STATE_NMI); - else - vmcs_clear_bits(GUEST_INTERRUPTIBILITY_INFO, - GUEST_INTR_STATE_NMI); + if (!enable_vnmi) { + if (vmx->loaded_vmcs->soft_vnmi_blocked != masked) { + vmx->loaded_vmcs->soft_vnmi_blocked = masked; + vmx->loaded_vmcs->vnmi_blocked_time = 0; + } + } else { + vmx->loaded_vmcs->nmi_known_unmasked = !masked; + if (masked) + vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, + GUEST_INTR_STATE_NMI); + else + vmcs_clear_bits(GUEST_INTERRUPTIBILITY_INFO, + GUEST_INTR_STATE_NMI); + } } static int vmx_nmi_allowed(struct kvm_vcpu *vcpu) @@ -5749,6 +5783,10 @@ static int vmx_nmi_allowed(struct kvm_vcpu *vcpu) if (to_vmx(vcpu)->nested.nested_run_pending) return 0; + if (!enable_vnmi && + to_vmx(vcpu)->loaded_vmcs->soft_vnmi_blocked) + return 0; + return !(vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & (GUEST_INTR_STATE_MOV_SS | GUEST_INTR_STATE_STI | GUEST_INTR_STATE_NMI)); @@ -6476,6 +6514,7 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu) * AAK134, BY25. */ if (!(to_vmx(vcpu)->idt_vectoring_info & VECTORING_INFO_VALID_MASK) && + enable_vnmi && (exit_qualification & INTR_INFO_UNBLOCK_NMI)) vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, GUEST_INTR_STATE_NMI); @@ -6535,6 +6574,7 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu) static int handle_nmi_window(struct kvm_vcpu *vcpu) { + WARN_ON_ONCE(!enable_vnmi); vmcs_clear_bits(CPU_BASED_VM_EXEC_CONTROL, CPU_BASED_VIRTUAL_NMI_PENDING); ++vcpu->stat.nmi_window_exits; @@ -6758,6 +6798,9 @@ static __init int hardware_setup(void) if (!cpu_has_vmx_flexpriority()) flexpriority_enabled = 0; + if (!cpu_has_virtual_nmis()) + enable_vnmi = 0; + /* * set_apic_access_page_addr() is used to reload apic access * page upon invalidation. No need to do anything if not @@ -6962,7 +7005,7 @@ static struct loaded_vmcs *nested_get_current_vmcs02(struct vcpu_vmx *vmx) } /* Create a new VMCS */ - item = kmalloc(sizeof(struct vmcs02_list), GFP_KERNEL); + item = kzalloc(sizeof(struct vmcs02_list), GFP_KERNEL); if (!item) return NULL; item->vmcs02.vmcs = alloc_vmcs(); @@ -7979,6 +8022,7 @@ static int handle_pml_full(struct kvm_vcpu *vcpu) * "blocked by NMI" bit has to be set before next VM entry. */ if (!(to_vmx(vcpu)->idt_vectoring_info & VECTORING_INFO_VALID_MASK) && + enable_vnmi && (exit_qualification & INTR_INFO_UNBLOCK_NMI)) vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, GUEST_INTR_STATE_NMI); @@ -8823,6 +8867,25 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu) return 0; } + if (unlikely(!enable_vnmi && + vmx->loaded_vmcs->soft_vnmi_blocked)) { + if (vmx_interrupt_allowed(vcpu)) { + vmx->loaded_vmcs->soft_vnmi_blocked = 0; + } else if (vmx->loaded_vmcs->vnmi_blocked_time > 1000000000LL && + vcpu->arch.nmi_pending) { + /* + * This CPU don't support us in finding the end of an + * NMI-blocked window if the guest runs with IRQs + * disabled. So we pull the trigger after 1 s of + * futile waiting, but inform the user about this. + */ + printk(KERN_WARNING "%s: Breaking out of NMI-blocked " + "state on VCPU %d after 1 s timeout\n", + __func__, vcpu->vcpu_id); + vmx->loaded_vmcs->soft_vnmi_blocked = 0; + } + } + if (exit_reason < kvm_vmx_max_exit_handlers && kvm_vmx_exit_handlers[exit_reason]) return kvm_vmx_exit_handlers[exit_reason](vcpu); @@ -9105,33 +9168,38 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx) idtv_info_valid = vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK; - if (vmx->loaded_vmcs->nmi_known_unmasked) - return; - /* - * Can't use vmx->exit_intr_info since we're not sure what - * the exit reason is. - */ - exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO); - unblock_nmi = (exit_intr_info & INTR_INFO_UNBLOCK_NMI) != 0; - vector = exit_intr_info & INTR_INFO_VECTOR_MASK; - /* - * SDM 3: 27.7.1.2 (September 2008) - * Re-set bit "block by NMI" before VM entry if vmexit caused by - * a guest IRET fault. - * SDM 3: 23.2.2 (September 2008) - * Bit 12 is undefined in any of the following cases: - * If the VM exit sets the valid bit in the IDT-vectoring - * information field. - * If the VM exit is due to a double fault. - */ - if ((exit_intr_info & INTR_INFO_VALID_MASK) && unblock_nmi && - vector != DF_VECTOR && !idtv_info_valid) - vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, - GUEST_INTR_STATE_NMI); - else - vmx->loaded_vmcs->nmi_known_unmasked = - !(vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) - & GUEST_INTR_STATE_NMI); + if (enable_vnmi) { + if (vmx->loaded_vmcs->nmi_known_unmasked) + return; + /* + * Can't use vmx->exit_intr_info since we're not sure what + * the exit reason is. + */ + exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO); + unblock_nmi = (exit_intr_info & INTR_INFO_UNBLOCK_NMI) != 0; + vector = exit_intr_info & INTR_INFO_VECTOR_MASK; + /* + * SDM 3: 27.7.1.2 (September 2008) + * Re-set bit "block by NMI" before VM entry if vmexit caused by + * a guest IRET fault. + * SDM 3: 23.2.2 (September 2008) + * Bit 12 is undefined in any of the following cases: + * If the VM exit sets the valid bit in the IDT-vectoring + * information field. + * If the VM exit is due to a double fault. + */ + if ((exit_intr_info & INTR_INFO_VALID_MASK) && unblock_nmi && + vector != DF_VECTOR && !idtv_info_valid) + vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, + GUEST_INTR_STATE_NMI); + else + vmx->loaded_vmcs->nmi_known_unmasked = + !(vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) + & GUEST_INTR_STATE_NMI); + } else if (unlikely(vmx->loaded_vmcs->soft_vnmi_blocked)) + vmx->loaded_vmcs->vnmi_blocked_time += + ktime_to_ns(ktime_sub(ktime_get(), + vmx->loaded_vmcs->entry_time)); } static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, @@ -9248,6 +9316,11 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) struct vcpu_vmx *vmx = to_vmx(vcpu); unsigned long debugctlmsr, cr3, cr4; + /* Record the guest's net vcpu time for enforced NMI injections. */ + if (unlikely(!enable_vnmi && + vmx->loaded_vmcs->soft_vnmi_blocked)) + vmx->loaded_vmcs->entry_time = ktime_get(); + /* Don't enter VMX if guest state is invalid, let the exit handler start emulation until we arrive back to a valid state */ if (vmx->emulation_required) diff --git a/arch/xtensa/include/uapi/asm/mman.h b/arch/xtensa/include/uapi/asm/mman.h index 2bfe590694fc..3e9d01ada81f 100644 --- a/arch/xtensa/include/uapi/asm/mman.h +++ b/arch/xtensa/include/uapi/asm/mman.h @@ -36,6 +36,7 @@ */ #define MAP_SHARED 0x001 /* Share changes */ #define MAP_PRIVATE 0x002 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x003 /* share + validate extension flags */ #define MAP_TYPE 0x00f /* Mask for type of mapping */ #define MAP_FIXED 0x010 /* Interpret addr exactly */ diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c index ceefb9a706d6..da1525ec4c87 100644 --- a/block/bfq-cgroup.c +++ b/block/bfq-cgroup.c @@ -24,7 +24,7 @@ #include "bfq-iosched.h" -#ifdef CONFIG_BFQ_GROUP_IOSCHED +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) /* bfqg stats flags */ enum bfqg_stats_flags { @@ -152,6 +152,57 @@ void bfqg_stats_update_avg_queue_size(struct bfq_group *bfqg) bfqg_stats_update_group_wait_time(stats); } +void bfqg_stats_update_io_add(struct bfq_group *bfqg, struct bfq_queue *bfqq, + unsigned int op) +{ + blkg_rwstat_add(&bfqg->stats.queued, op, 1); + bfqg_stats_end_empty_time(&bfqg->stats); + if (!(bfqq == ((struct bfq_data *)bfqg->bfqd)->in_service_queue)) + bfqg_stats_set_start_group_wait_time(bfqg, bfqq_group(bfqq)); +} + +void bfqg_stats_update_io_remove(struct bfq_group *bfqg, unsigned int op) +{ + blkg_rwstat_add(&bfqg->stats.queued, op, -1); +} + +void bfqg_stats_update_io_merged(struct bfq_group *bfqg, unsigned int op) +{ + blkg_rwstat_add(&bfqg->stats.merged, op, 1); +} + +void bfqg_stats_update_completion(struct bfq_group *bfqg, uint64_t start_time, + uint64_t io_start_time, unsigned int op) +{ + struct bfqg_stats *stats = &bfqg->stats; + unsigned long long now = sched_clock(); + + if (time_after64(now, io_start_time)) + blkg_rwstat_add(&stats->service_time, op, + now - io_start_time); + if (time_after64(io_start_time, start_time)) + blkg_rwstat_add(&stats->wait_time, op, + io_start_time - start_time); +} + +#else /* CONFIG_BFQ_GROUP_IOSCHED && CONFIG_DEBUG_BLK_CGROUP */ + +void bfqg_stats_update_io_add(struct bfq_group *bfqg, struct bfq_queue *bfqq, + unsigned int op) { } +void bfqg_stats_update_io_remove(struct bfq_group *bfqg, unsigned int op) { } +void bfqg_stats_update_io_merged(struct bfq_group *bfqg, unsigned int op) { } +void bfqg_stats_update_completion(struct bfq_group *bfqg, uint64_t start_time, + uint64_t io_start_time, unsigned int op) { } +void bfqg_stats_update_dequeue(struct bfq_group *bfqg) { } +void bfqg_stats_set_start_empty_time(struct bfq_group *bfqg) { } +void bfqg_stats_update_idle_time(struct bfq_group *bfqg) { } +void bfqg_stats_set_start_idle_time(struct bfq_group *bfqg) { } +void bfqg_stats_update_avg_queue_size(struct bfq_group *bfqg) { } + +#endif /* CONFIG_BFQ_GROUP_IOSCHED && CONFIG_DEBUG_BLK_CGROUP */ + +#ifdef CONFIG_BFQ_GROUP_IOSCHED + /* * blk-cgroup policy-related handlers * The following functions help in converting between blk-cgroup @@ -229,42 +280,10 @@ void bfqg_and_blkg_put(struct bfq_group *bfqg) blkg_put(bfqg_to_blkg(bfqg)); } -void bfqg_stats_update_io_add(struct bfq_group *bfqg, struct bfq_queue *bfqq, - unsigned int op) -{ - blkg_rwstat_add(&bfqg->stats.queued, op, 1); - bfqg_stats_end_empty_time(&bfqg->stats); - if (!(bfqq == ((struct bfq_data *)bfqg->bfqd)->in_service_queue)) - bfqg_stats_set_start_group_wait_time(bfqg, bfqq_group(bfqq)); -} - -void bfqg_stats_update_io_remove(struct bfq_group *bfqg, unsigned int op) -{ - blkg_rwstat_add(&bfqg->stats.queued, op, -1); -} - -void bfqg_stats_update_io_merged(struct bfq_group *bfqg, unsigned int op) -{ - blkg_rwstat_add(&bfqg->stats.merged, op, 1); -} - -void bfqg_stats_update_completion(struct bfq_group *bfqg, uint64_t start_time, - uint64_t io_start_time, unsigned int op) -{ - struct bfqg_stats *stats = &bfqg->stats; - unsigned long long now = sched_clock(); - - if (time_after64(now, io_start_time)) - blkg_rwstat_add(&stats->service_time, op, - now - io_start_time); - if (time_after64(io_start_time, start_time)) - blkg_rwstat_add(&stats->wait_time, op, - io_start_time - start_time); -} - /* @stats = 0 */ static void bfqg_stats_reset(struct bfqg_stats *stats) { +#ifdef CONFIG_DEBUG_BLK_CGROUP /* queued stats shouldn't be cleared */ blkg_rwstat_reset(&stats->merged); blkg_rwstat_reset(&stats->service_time); @@ -276,6 +295,7 @@ static void bfqg_stats_reset(struct bfqg_stats *stats) blkg_stat_reset(&stats->group_wait_time); blkg_stat_reset(&stats->idle_time); blkg_stat_reset(&stats->empty_time); +#endif } /* @to += @from */ @@ -284,6 +304,7 @@ static void bfqg_stats_add_aux(struct bfqg_stats *to, struct bfqg_stats *from) if (!to || !from) return; +#ifdef CONFIG_DEBUG_BLK_CGROUP /* queued stats shouldn't be cleared */ blkg_rwstat_add_aux(&to->merged, &from->merged); blkg_rwstat_add_aux(&to->service_time, &from->service_time); @@ -296,6 +317,7 @@ static void bfqg_stats_add_aux(struct bfqg_stats *to, struct bfqg_stats *from) blkg_stat_add_aux(&to->group_wait_time, &from->group_wait_time); blkg_stat_add_aux(&to->idle_time, &from->idle_time); blkg_stat_add_aux(&to->empty_time, &from->empty_time); +#endif } /* @@ -342,6 +364,7 @@ void bfq_init_entity(struct bfq_entity *entity, struct bfq_group *bfqg) static void bfqg_stats_exit(struct bfqg_stats *stats) { +#ifdef CONFIG_DEBUG_BLK_CGROUP blkg_rwstat_exit(&stats->merged); blkg_rwstat_exit(&stats->service_time); blkg_rwstat_exit(&stats->wait_time); @@ -353,10 +376,12 @@ static void bfqg_stats_exit(struct bfqg_stats *stats) blkg_stat_exit(&stats->group_wait_time); blkg_stat_exit(&stats->idle_time); blkg_stat_exit(&stats->empty_time); +#endif } static int bfqg_stats_init(struct bfqg_stats *stats, gfp_t gfp) { +#ifdef CONFIG_DEBUG_BLK_CGROUP if (blkg_rwstat_init(&stats->merged, gfp) || blkg_rwstat_init(&stats->service_time, gfp) || blkg_rwstat_init(&stats->wait_time, gfp) || @@ -371,6 +396,7 @@ static int bfqg_stats_init(struct bfqg_stats *stats, gfp_t gfp) bfqg_stats_exit(stats); return -ENOMEM; } +#endif return 0; } @@ -887,6 +913,7 @@ static ssize_t bfq_io_set_weight(struct kernfs_open_file *of, return bfq_io_set_weight_legacy(of_css(of), NULL, weight); } +#ifdef CONFIG_DEBUG_BLK_CGROUP static int bfqg_print_stat(struct seq_file *sf, void *v) { blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)), blkg_prfill_stat, @@ -991,6 +1018,7 @@ static int bfqg_print_avg_queue_size(struct seq_file *sf, void *v) 0, false); return 0; } +#endif /* CONFIG_DEBUG_BLK_CGROUP */ struct bfq_group *bfq_create_group_hierarchy(struct bfq_data *bfqd, int node) { @@ -1028,15 +1056,6 @@ struct cftype bfq_blkcg_legacy_files[] = { }, /* statistics, covers only the tasks in the bfqg */ - { - .name = "bfq.time", - .private = offsetof(struct bfq_group, stats.time), - .seq_show = bfqg_print_stat, - }, - { - .name = "bfq.sectors", - .seq_show = bfqg_print_stat_sectors, - }, { .name = "bfq.io_service_bytes", .private = (unsigned long)&blkcg_policy_bfq, @@ -1047,6 +1066,16 @@ struct cftype bfq_blkcg_legacy_files[] = { .private = (unsigned long)&blkcg_policy_bfq, .seq_show = blkg_print_stat_ios, }, +#ifdef CONFIG_DEBUG_BLK_CGROUP + { + .name = "bfq.time", + .private = offsetof(struct bfq_group, stats.time), + .seq_show = bfqg_print_stat, + }, + { + .name = "bfq.sectors", + .seq_show = bfqg_print_stat_sectors, + }, { .name = "bfq.io_service_time", .private = offsetof(struct bfq_group, stats.service_time), @@ -1067,17 +1096,9 @@ struct cftype bfq_blkcg_legacy_files[] = { .private = offsetof(struct bfq_group, stats.queued), .seq_show = bfqg_print_rwstat, }, +#endif /* CONFIG_DEBUG_BLK_CGROUP */ /* the same statictics which cover the bfqg and its descendants */ - { - .name = "bfq.time_recursive", - .private = offsetof(struct bfq_group, stats.time), - .seq_show = bfqg_print_stat_recursive, - }, - { - .name = "bfq.sectors_recursive", - .seq_show = bfqg_print_stat_sectors_recursive, - }, { .name = "bfq.io_service_bytes_recursive", .private = (unsigned long)&blkcg_policy_bfq, @@ -1088,6 +1109,16 @@ struct cftype bfq_blkcg_legacy_files[] = { .private = (unsigned long)&blkcg_policy_bfq, .seq_show = blkg_print_stat_ios_recursive, }, +#ifdef CONFIG_DEBUG_BLK_CGROUP + { + .name = "bfq.time_recursive", + .private = offsetof(struct bfq_group, stats.time), + .seq_show = bfqg_print_stat_recursive, + }, + { + .name = "bfq.sectors_recursive", + .seq_show = bfqg_print_stat_sectors_recursive, + }, { .name = "bfq.io_service_time_recursive", .private = offsetof(struct bfq_group, stats.service_time), @@ -1132,6 +1163,7 @@ struct cftype bfq_blkcg_legacy_files[] = { .private = offsetof(struct bfq_group, stats.dequeue), .seq_show = bfqg_print_stat, }, +#endif /* CONFIG_DEBUG_BLK_CGROUP */ { } /* terminate */ }; @@ -1147,18 +1179,6 @@ struct cftype bfq_blkg_files[] = { #else /* CONFIG_BFQ_GROUP_IOSCHED */ -void bfqg_stats_update_io_add(struct bfq_group *bfqg, struct bfq_queue *bfqq, - unsigned int op) { } -void bfqg_stats_update_io_remove(struct bfq_group *bfqg, unsigned int op) { } -void bfqg_stats_update_io_merged(struct bfq_group *bfqg, unsigned int op) { } -void bfqg_stats_update_completion(struct bfq_group *bfqg, uint64_t start_time, - uint64_t io_start_time, unsigned int op) { } -void bfqg_stats_update_dequeue(struct bfq_group *bfqg) { } -void bfqg_stats_set_start_empty_time(struct bfq_group *bfqg) { } -void bfqg_stats_update_idle_time(struct bfq_group *bfqg) { } -void bfqg_stats_set_start_idle_time(struct bfq_group *bfqg) { } -void bfqg_stats_update_avg_queue_size(struct bfq_group *bfqg) { } - void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, struct bfq_group *bfqg) {} diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 889a8549d97f..bcb6d21baf12 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -1359,7 +1359,6 @@ static void bfq_bfqq_handle_idle_busy_switch(struct bfq_data *bfqd, bfqq->ttime.last_end_request + bfqd->bfq_slice_idle * 3; - bfqg_stats_update_io_add(bfqq_group(RQ_BFQQ(rq)), bfqq, rq->cmd_flags); /* * bfqq deserves to be weight-raised if: @@ -1633,7 +1632,6 @@ static void bfq_remove_request(struct request_queue *q, if (rq->cmd_flags & REQ_META) bfqq->meta_pending--; - bfqg_stats_update_io_remove(bfqq_group(bfqq), rq->cmd_flags); } static bool bfq_bio_merge(struct blk_mq_hw_ctx *hctx, struct bio *bio) @@ -1746,6 +1744,7 @@ static void bfq_requests_merged(struct request_queue *q, struct request *rq, bfqq->next_rq = rq; bfq_remove_request(q, next); + bfqg_stats_update_io_remove(bfqq_group(bfqq), next->cmd_flags); spin_unlock_irq(&bfqq->bfqd->lock); end: @@ -2229,7 +2228,6 @@ static void __bfq_set_in_service_queue(struct bfq_data *bfqd, struct bfq_queue *bfqq) { if (bfqq) { - bfqg_stats_update_avg_queue_size(bfqq_group(bfqq)); bfq_clear_bfqq_fifo_expire(bfqq); bfqd->budgets_assigned = (bfqd->budgets_assigned * 7 + 256) / 8; @@ -3470,7 +3468,6 @@ check_queue: */ bfq_clear_bfqq_wait_request(bfqq); hrtimer_try_to_cancel(&bfqd->idle_slice_timer); - bfqg_stats_update_idle_time(bfqq_group(bfqq)); } goto keep_queue; } @@ -3696,12 +3693,67 @@ static struct request *bfq_dispatch_request(struct blk_mq_hw_ctx *hctx) { struct bfq_data *bfqd = hctx->queue->elevator->elevator_data; struct request *rq; +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) + struct bfq_queue *in_serv_queue, *bfqq; + bool waiting_rq, idle_timer_disabled; +#endif spin_lock_irq(&bfqd->lock); +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) + in_serv_queue = bfqd->in_service_queue; + waiting_rq = in_serv_queue && bfq_bfqq_wait_request(in_serv_queue); + + rq = __bfq_dispatch_request(hctx); + + idle_timer_disabled = + waiting_rq && !bfq_bfqq_wait_request(in_serv_queue); + +#else rq = __bfq_dispatch_request(hctx); +#endif spin_unlock_irq(&bfqd->lock); +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) + bfqq = rq ? RQ_BFQQ(rq) : NULL; + if (!idle_timer_disabled && !bfqq) + return rq; + + /* + * rq and bfqq are guaranteed to exist until this function + * ends, for the following reasons. First, rq can be + * dispatched to the device, and then can be completed and + * freed, only after this function ends. Second, rq cannot be + * merged (and thus freed because of a merge) any longer, + * because it has already started. Thus rq cannot be freed + * before this function ends, and, since rq has a reference to + * bfqq, the same guarantee holds for bfqq too. + * + * In addition, the following queue lock guarantees that + * bfqq_group(bfqq) exists as well. + */ + spin_lock_irq(hctx->queue->queue_lock); + if (idle_timer_disabled) + /* + * Since the idle timer has been disabled, + * in_serv_queue contained some request when + * __bfq_dispatch_request was invoked above, which + * implies that rq was picked exactly from + * in_serv_queue. Thus in_serv_queue == bfqq, and is + * therefore guaranteed to exist because of the above + * arguments. + */ + bfqg_stats_update_idle_time(bfqq_group(in_serv_queue)); + if (bfqq) { + struct bfq_group *bfqg = bfqq_group(bfqq); + + bfqg_stats_update_avg_queue_size(bfqg); + bfqg_stats_set_start_empty_time(bfqg); + bfqg_stats_update_io_remove(bfqg, rq->cmd_flags); + } + spin_unlock_irq(hctx->queue->queue_lock); +#endif + return rq; } @@ -4159,7 +4211,6 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, */ bfq_clear_bfqq_wait_request(bfqq); hrtimer_try_to_cancel(&bfqd->idle_slice_timer); - bfqg_stats_update_idle_time(bfqq_group(bfqq)); /* * The queue is not empty, because a new request just @@ -4174,10 +4225,12 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, } } -static void __bfq_insert_request(struct bfq_data *bfqd, struct request *rq) +/* returns true if it causes the idle timer to be disabled */ +static bool __bfq_insert_request(struct bfq_data *bfqd, struct request *rq) { struct bfq_queue *bfqq = RQ_BFQQ(rq), *new_bfqq = bfq_setup_cooperator(bfqd, bfqq, rq, true); + bool waiting, idle_timer_disabled = false; if (new_bfqq) { if (bic_to_bfqq(RQ_BIC(rq), 1) != bfqq) @@ -4211,12 +4264,16 @@ static void __bfq_insert_request(struct bfq_data *bfqd, struct request *rq) bfqq = new_bfqq; } + waiting = bfqq && bfq_bfqq_wait_request(bfqq); bfq_add_request(rq); + idle_timer_disabled = waiting && !bfq_bfqq_wait_request(bfqq); rq->fifo_time = ktime_get_ns() + bfqd->bfq_fifo_expire[rq_is_sync(rq)]; list_add_tail(&rq->queuelist, &bfqq->fifo); bfq_rq_enqueued(bfqd, bfqq, rq); + + return idle_timer_disabled; } static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, @@ -4224,6 +4281,11 @@ static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, { struct request_queue *q = hctx->queue; struct bfq_data *bfqd = q->elevator->elevator_data; +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) + struct bfq_queue *bfqq = RQ_BFQQ(rq); + bool idle_timer_disabled = false; + unsigned int cmd_flags; +#endif spin_lock_irq(&bfqd->lock); if (blk_mq_sched_try_insert_merge(q, rq)) { @@ -4242,7 +4304,17 @@ static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, else list_add_tail(&rq->queuelist, &bfqd->dispatch); } else { +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) + idle_timer_disabled = __bfq_insert_request(bfqd, rq); + /* + * Update bfqq, because, if a queue merge has occurred + * in __bfq_insert_request, then rq has been + * redirected into a new queue. + */ + bfqq = RQ_BFQQ(rq); +#else __bfq_insert_request(bfqd, rq); +#endif if (rq_mergeable(rq)) { elv_rqhash_add(q, rq); @@ -4251,7 +4323,35 @@ static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, } } +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) + /* + * Cache cmd_flags before releasing scheduler lock, because rq + * may disappear afterwards (for example, because of a request + * merge). + */ + cmd_flags = rq->cmd_flags; +#endif spin_unlock_irq(&bfqd->lock); + +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) + if (!bfqq) + return; + /* + * bfqq still exists, because it can disappear only after + * either it is merged with another queue, or the process it + * is associated with exits. But both actions must be taken by + * the same process currently executing this flow of + * instruction. + * + * In addition, the following queue lock guarantees that + * bfqq_group(bfqq) exists as well. + */ + spin_lock_irq(q->queue_lock); + bfqg_stats_update_io_add(bfqq_group(bfqq), bfqq, cmd_flags); + if (idle_timer_disabled) + bfqg_stats_update_idle_time(bfqq_group(bfqq)); + spin_unlock_irq(q->queue_lock); +#endif } static void bfq_insert_requests(struct blk_mq_hw_ctx *hctx, @@ -4428,8 +4528,11 @@ static void bfq_finish_request(struct request *rq) * lock is held. */ - if (!RB_EMPTY_NODE(&rq->rb_node)) + if (!RB_EMPTY_NODE(&rq->rb_node)) { bfq_remove_request(rq->q, rq); + bfqg_stats_update_io_remove(bfqq_group(bfqq), + rq->cmd_flags); + } bfq_put_rq_priv_body(bfqq); } diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h index ac0809c72c98..91c4390903a1 100644 --- a/block/bfq-iosched.h +++ b/block/bfq-iosched.h @@ -689,7 +689,7 @@ enum bfqq_expiration { }; struct bfqg_stats { -#ifdef CONFIG_BFQ_GROUP_IOSCHED +#if defined(CONFIG_BFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP) /* number of ios merged */ struct blkg_rwstat merged; /* total time spent on device in ns, may not be accurate w/ queueing */ @@ -717,7 +717,7 @@ struct bfqg_stats { uint64_t start_idle_time; uint64_t start_empty_time; uint16_t flags; -#endif /* CONFIG_BFQ_GROUP_IOSCHED */ +#endif /* CONFIG_BFQ_GROUP_IOSCHED && CONFIG_DEBUG_BLK_CGROUP */ }; #ifdef CONFIG_BFQ_GROUP_IOSCHED diff --git a/block/bfq-wf2q.c b/block/bfq-wf2q.c index 414ba686a847..e495d3f9b4b0 100644 --- a/block/bfq-wf2q.c +++ b/block/bfq-wf2q.c @@ -843,7 +843,6 @@ void bfq_bfqq_served(struct bfq_queue *bfqq, int served) st->vtime += bfq_delta(served, st->wsum); bfq_forget_idle(st); } - bfqg_stats_set_start_empty_time(bfqq_group(bfqq)); bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %d secs", served); } diff --git a/block/bio.c b/block/bio.c index b94a802f8ba3..228229f3bb76 100644 --- a/block/bio.c +++ b/block/bio.c @@ -597,6 +597,7 @@ void __bio_clone_fast(struct bio *bio, struct bio *bio_src) * so we don't set nor calculate new physical/hw segment counts here */ bio->bi_disk = bio_src->bi_disk; + bio->bi_partno = bio_src->bi_partno; bio_set_flag(bio, BIO_CLONED); bio->bi_opf = bio_src->bi_opf; bio->bi_write_hint = bio_src->bi_write_hint; @@ -1061,14 +1062,21 @@ struct bio_map_data { struct iovec iov[]; }; -static struct bio_map_data *bio_alloc_map_data(unsigned int iov_count, +static struct bio_map_data *bio_alloc_map_data(struct iov_iter *data, gfp_t gfp_mask) { - if (iov_count > UIO_MAXIOV) + struct bio_map_data *bmd; + if (data->nr_segs > UIO_MAXIOV) return NULL; - return kmalloc(sizeof(struct bio_map_data) + - sizeof(struct iovec) * iov_count, gfp_mask); + bmd = kmalloc(sizeof(struct bio_map_data) + + sizeof(struct iovec) * data->nr_segs, gfp_mask); + if (!bmd) + return NULL; + memcpy(bmd->iov, data->iov, sizeof(struct iovec) * data->nr_segs); + bmd->iter = *data; + bmd->iter.iov = bmd->iov; + return bmd; } /** @@ -1079,7 +1087,7 @@ static struct bio_map_data *bio_alloc_map_data(unsigned int iov_count, * Copy all pages from iov_iter to bio. * Returns 0 on success, or error on failure. */ -static int bio_copy_from_iter(struct bio *bio, struct iov_iter iter) +static int bio_copy_from_iter(struct bio *bio, struct iov_iter *iter) { int i; struct bio_vec *bvec; @@ -1090,9 +1098,9 @@ static int bio_copy_from_iter(struct bio *bio, struct iov_iter iter) ret = copy_page_from_iter(bvec->bv_page, bvec->bv_offset, bvec->bv_len, - &iter); + iter); - if (!iov_iter_count(&iter)) + if (!iov_iter_count(iter)) break; if (ret < bvec->bv_len) @@ -1186,40 +1194,18 @@ int bio_uncopy_user(struct bio *bio) */ struct bio *bio_copy_user_iov(struct request_queue *q, struct rq_map_data *map_data, - const struct iov_iter *iter, + struct iov_iter *iter, gfp_t gfp_mask) { struct bio_map_data *bmd; struct page *page; struct bio *bio; - int i, ret; - int nr_pages = 0; + int i = 0, ret; + int nr_pages; unsigned int len = iter->count; unsigned int offset = map_data ? offset_in_page(map_data->offset) : 0; - for (i = 0; i < iter->nr_segs; i++) { - unsigned long uaddr; - unsigned long end; - unsigned long start; - - uaddr = (unsigned long) iter->iov[i].iov_base; - end = (uaddr + iter->iov[i].iov_len + PAGE_SIZE - 1) - >> PAGE_SHIFT; - start = uaddr >> PAGE_SHIFT; - - /* - * Overflow, abort - */ - if (end < start) - return ERR_PTR(-EINVAL); - - nr_pages += end - start; - } - - if (offset) - nr_pages++; - - bmd = bio_alloc_map_data(iter->nr_segs, gfp_mask); + bmd = bio_alloc_map_data(iter, gfp_mask); if (!bmd) return ERR_PTR(-ENOMEM); @@ -1229,9 +1215,10 @@ struct bio *bio_copy_user_iov(struct request_queue *q, * shortlived one. */ bmd->is_our_pages = map_data ? 0 : 1; - memcpy(bmd->iov, iter->iov, sizeof(struct iovec) * iter->nr_segs); - bmd->iter = *iter; - bmd->iter.iov = bmd->iov; + + nr_pages = DIV_ROUND_UP(offset + len, PAGE_SIZE); + if (nr_pages > BIO_MAX_PAGES) + nr_pages = BIO_MAX_PAGES; ret = -ENOMEM; bio = bio_kmalloc(gfp_mask, nr_pages); @@ -1280,17 +1267,24 @@ struct bio *bio_copy_user_iov(struct request_queue *q, if (ret) goto cleanup; + if (map_data) + map_data->offset += bio->bi_iter.bi_size; + /* * success */ if (((iter->type & WRITE) && (!map_data || !map_data->null_mapped)) || (map_data && map_data->from_user)) { - ret = bio_copy_from_iter(bio, *iter); + ret = bio_copy_from_iter(bio, iter); if (ret) goto cleanup; + } else { + iov_iter_advance(iter, bio->bi_iter.bi_size); } bio->bi_private = bmd; + if (map_data && map_data->null_mapped) + bio_set_flag(bio, BIO_NULL_MAPPED); return bio; cleanup: if (!map_data) @@ -1311,111 +1305,74 @@ out_bmd: * device. Returns an error pointer in case of error. */ struct bio *bio_map_user_iov(struct request_queue *q, - const struct iov_iter *iter, + struct iov_iter *iter, gfp_t gfp_mask) { int j; - int nr_pages = 0; - struct page **pages; struct bio *bio; - int cur_page = 0; - int ret, offset; - struct iov_iter i; - struct iovec iov; + int ret; struct bio_vec *bvec; - iov_for_each(iov, i, *iter) { - unsigned long uaddr = (unsigned long) iov.iov_base; - unsigned long len = iov.iov_len; - unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT; - unsigned long start = uaddr >> PAGE_SHIFT; - - /* - * Overflow, abort - */ - if (end < start) - return ERR_PTR(-EINVAL); - - nr_pages += end - start; - /* - * buffer must be aligned to at least logical block size for now - */ - if (uaddr & queue_dma_alignment(q)) - return ERR_PTR(-EINVAL); - } - - if (!nr_pages) + if (!iov_iter_count(iter)) return ERR_PTR(-EINVAL); - bio = bio_kmalloc(gfp_mask, nr_pages); + bio = bio_kmalloc(gfp_mask, iov_iter_npages(iter, BIO_MAX_PAGES)); if (!bio) return ERR_PTR(-ENOMEM); - ret = -ENOMEM; - pages = kcalloc(nr_pages, sizeof(struct page *), gfp_mask); - if (!pages) - goto out; + while (iov_iter_count(iter)) { + struct page **pages; + ssize_t bytes; + size_t offs, added = 0; + int npages; - iov_for_each(iov, i, *iter) { - unsigned long uaddr = (unsigned long) iov.iov_base; - unsigned long len = iov.iov_len; - unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT; - unsigned long start = uaddr >> PAGE_SHIFT; - const int local_nr_pages = end - start; - const int page_limit = cur_page + local_nr_pages; - - ret = get_user_pages_fast(uaddr, local_nr_pages, - (iter->type & WRITE) != WRITE, - &pages[cur_page]); - if (unlikely(ret < local_nr_pages)) { - for (j = cur_page; j < page_limit; j++) { - if (!pages[j]) - break; - put_page(pages[j]); - } - ret = -EFAULT; + bytes = iov_iter_get_pages_alloc(iter, &pages, LONG_MAX, &offs); + if (unlikely(bytes <= 0)) { + ret = bytes ? bytes : -EFAULT; goto out_unmap; } - offset = offset_in_page(uaddr); - for (j = cur_page; j < page_limit; j++) { - unsigned int bytes = PAGE_SIZE - offset; - unsigned short prev_bi_vcnt = bio->bi_vcnt; + npages = DIV_ROUND_UP(offs + bytes, PAGE_SIZE); - if (len <= 0) - break; - - if (bytes > len) - bytes = len; - - /* - * sorry... - */ - if (bio_add_pc_page(q, bio, pages[j], bytes, offset) < - bytes) - break; + if (unlikely(offs & queue_dma_alignment(q))) { + ret = -EINVAL; + j = 0; + } else { + for (j = 0; j < npages; j++) { + struct page *page = pages[j]; + unsigned int n = PAGE_SIZE - offs; + unsigned short prev_bi_vcnt = bio->bi_vcnt; - /* - * check if vector was merged with previous - * drop page reference if needed - */ - if (bio->bi_vcnt == prev_bi_vcnt) - put_page(pages[j]); + if (n > bytes) + n = bytes; - len -= bytes; - offset = 0; - } + if (!bio_add_pc_page(q, bio, page, n, offs)) + break; - cur_page = j; + /* + * check if vector was merged with previous + * drop page reference if needed + */ + if (bio->bi_vcnt == prev_bi_vcnt) + put_page(page); + + added += n; + bytes -= n; + offs = 0; + } + iov_iter_advance(iter, added); + } /* * release the pages we didn't map into the bio, if any */ - while (j < page_limit) + while (j < npages) put_page(pages[j++]); + kvfree(pages); + /* couldn't stuff something into bio? */ + if (bytes) + break; } - kfree(pages); - bio_set_flag(bio, BIO_USER_MAPPED); /* @@ -1431,8 +1388,6 @@ struct bio *bio_map_user_iov(struct request_queue *q, bio_for_each_segment_all(bvec, bio, j) { put_page(bvec->bv_page); } - out: - kfree(pages); bio_put(bio); return ERR_PTR(ret); } diff --git a/block/blk-core.c b/block/blk-core.c index 7c54c195e79e..1038706edd87 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -637,8 +637,8 @@ void blk_set_queue_dying(struct request_queue *q) spin_lock_irq(q->queue_lock); blk_queue_for_each_rl(rl, q) { if (rl->rq_pool) { - wake_up(&rl->wait[BLK_RW_SYNC]); - wake_up(&rl->wait[BLK_RW_ASYNC]); + wake_up_all(&rl->wait[BLK_RW_SYNC]); + wake_up_all(&rl->wait[BLK_RW_ASYNC]); } } spin_unlock_irq(q->queue_lock); diff --git a/block/blk-map.c b/block/blk-map.c index d5251edcc0dd..b21f8e86f120 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -67,13 +67,6 @@ static int __blk_rq_map_user_iov(struct request *rq, bio->bi_opf &= ~REQ_OP_MASK; bio->bi_opf |= req_op(rq); - if (map_data && map_data->null_mapped) - bio_set_flag(bio, BIO_NULL_MAPPED); - - iov_iter_advance(iter, bio->bi_iter.bi_size); - if (map_data) - map_data->offset += bio->bi_iter.bi_size; - orig_bio = bio; /* diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index 1063b644efcd..e284d9cb9237 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -19,6 +19,7 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PKCS#7 testing key type"); +MODULE_AUTHOR("Red Hat, Inc."); static unsigned pkcs7_usage; module_param_named(usage, pkcs7_usage, uint, S_IWUSR | S_IRUGO); diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index d140d8bb2c96..c1ca1e86f5c4 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) "PKCS7: "fmt #include +#include #include #include #include @@ -19,6 +20,10 @@ #include "pkcs7_parser.h" #include "pkcs7-asn1.h" +MODULE_DESCRIPTION("PKCS#7 parser"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + struct pkcs7_parse_context { struct pkcs7_message *msg; /* Message being constructed */ struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */ diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index d916235d6cf5..bc3035ef27a2 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -22,6 +22,8 @@ #include #include +MODULE_DESCRIPTION("In-software asymmetric public-key subtype"); +MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); /* diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index eea71dc9686c..c9013582c026 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -275,4 +275,5 @@ module_init(x509_key_init); module_exit(x509_key_exit); MODULE_DESCRIPTION("X.509 certificate parser"); +MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 9c2c49b6a240..ff2580e7611d 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -183,13 +183,33 @@ static int xlat_bus_status(void *buf, unsigned int cmd, u32 status) return 0; } -static int xlat_nvdimm_status(void *buf, unsigned int cmd, u32 status) +#define ACPI_LABELS_LOCKED 3 + +static int xlat_nvdimm_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd, + u32 status) { + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + switch (cmd) { case ND_CMD_GET_CONFIG_SIZE: + /* + * In the _LSI, _LSR, _LSW case the locked status is + * communicated via the read/write commands + */ + if (nfit_mem->has_lsi) + break; + if (status >> 16 & ND_CONFIG_LOCKED) return -EACCES; break; + case ND_CMD_GET_CONFIG_DATA: + if (nfit_mem->has_lsr && status == ACPI_LABELS_LOCKED) + return -EACCES; + break; + case ND_CMD_SET_CONFIG_DATA: + if (nfit_mem->has_lsw && status == ACPI_LABELS_LOCKED) + return -EACCES; + break; default: break; } @@ -205,13 +225,182 @@ static int xlat_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd, { if (!nvdimm) return xlat_bus_status(buf, cmd, status); - return xlat_nvdimm_status(buf, cmd, status); + return xlat_nvdimm_status(nvdimm, buf, cmd, status); +} + +/* convert _LS{I,R} packages to the buffer object acpi_nfit_ctl expects */ +static union acpi_object *pkg_to_buf(union acpi_object *pkg) +{ + int i; + void *dst; + size_t size = 0; + union acpi_object *buf = NULL; + + if (pkg->type != ACPI_TYPE_PACKAGE) { + WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n", + pkg->type); + goto err; + } + + for (i = 0; i < pkg->package.count; i++) { + union acpi_object *obj = &pkg->package.elements[i]; + + if (obj->type == ACPI_TYPE_INTEGER) + size += 4; + else if (obj->type == ACPI_TYPE_BUFFER) + size += obj->buffer.length; + else { + WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n", + obj->type); + goto err; + } + } + + buf = ACPI_ALLOCATE(sizeof(*buf) + size); + if (!buf) + goto err; + + dst = buf + 1; + buf->type = ACPI_TYPE_BUFFER; + buf->buffer.length = size; + buf->buffer.pointer = dst; + for (i = 0; i < pkg->package.count; i++) { + union acpi_object *obj = &pkg->package.elements[i]; + + if (obj->type == ACPI_TYPE_INTEGER) { + memcpy(dst, &obj->integer.value, 4); + dst += 4; + } else if (obj->type == ACPI_TYPE_BUFFER) { + memcpy(dst, obj->buffer.pointer, obj->buffer.length); + dst += obj->buffer.length; + } + } +err: + ACPI_FREE(pkg); + return buf; +} + +static union acpi_object *int_to_buf(union acpi_object *integer) +{ + union acpi_object *buf = ACPI_ALLOCATE(sizeof(*buf) + 4); + void *dst = NULL; + + if (!buf) + goto err; + + if (integer->type != ACPI_TYPE_INTEGER) { + WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n", + integer->type); + goto err; + } + + dst = buf + 1; + buf->type = ACPI_TYPE_BUFFER; + buf->buffer.length = 4; + buf->buffer.pointer = dst; + memcpy(dst, &integer->integer.value, 4); +err: + ACPI_FREE(integer); + return buf; +} + +static union acpi_object *acpi_label_write(acpi_handle handle, u32 offset, + u32 len, void *data) +{ + acpi_status rc; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_object_list input = { + .count = 3, + .pointer = (union acpi_object []) { + [0] = { + .integer.type = ACPI_TYPE_INTEGER, + .integer.value = offset, + }, + [1] = { + .integer.type = ACPI_TYPE_INTEGER, + .integer.value = len, + }, + [2] = { + .buffer.type = ACPI_TYPE_BUFFER, + .buffer.pointer = data, + .buffer.length = len, + }, + }, + }; + + rc = acpi_evaluate_object(handle, "_LSW", &input, &buf); + if (ACPI_FAILURE(rc)) + return NULL; + return int_to_buf(buf.pointer); +} + +static union acpi_object *acpi_label_read(acpi_handle handle, u32 offset, + u32 len) +{ + acpi_status rc; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_object_list input = { + .count = 2, + .pointer = (union acpi_object []) { + [0] = { + .integer.type = ACPI_TYPE_INTEGER, + .integer.value = offset, + }, + [1] = { + .integer.type = ACPI_TYPE_INTEGER, + .integer.value = len, + }, + }, + }; + + rc = acpi_evaluate_object(handle, "_LSR", &input, &buf); + if (ACPI_FAILURE(rc)) + return NULL; + return pkg_to_buf(buf.pointer); +} + +static union acpi_object *acpi_label_info(acpi_handle handle) +{ + acpi_status rc; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + + rc = acpi_evaluate_object(handle, "_LSI", NULL, &buf); + if (ACPI_FAILURE(rc)) + return NULL; + return pkg_to_buf(buf.pointer); +} + +static u8 nfit_dsm_revid(unsigned family, unsigned func) +{ + static const u8 revid_table[NVDIMM_FAMILY_MAX+1][32] = { + [NVDIMM_FAMILY_INTEL] = { + [NVDIMM_INTEL_GET_MODES] = 2, + [NVDIMM_INTEL_GET_FWINFO] = 2, + [NVDIMM_INTEL_START_FWUPDATE] = 2, + [NVDIMM_INTEL_SEND_FWUPDATE] = 2, + [NVDIMM_INTEL_FINISH_FWUPDATE] = 2, + [NVDIMM_INTEL_QUERY_FWUPDATE] = 2, + [NVDIMM_INTEL_SET_THRESHOLD] = 2, + [NVDIMM_INTEL_INJECT_ERROR] = 2, + }, + }; + u8 id; + + if (family > NVDIMM_FAMILY_MAX) + return 0; + if (func > 31) + return 0; + id = revid_table[family][func]; + if (id == 0) + return 1; /* default */ + return id; } int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) { struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); union acpi_object in_obj, in_buf, *out_obj; const struct nd_cmd_desc *desc = NULL; struct device *dev = acpi_desc->dev; @@ -235,7 +424,6 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, } if (nvdimm) { - struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct acpi_device *adev = nfit_mem->adev; if (!adev) @@ -294,7 +482,29 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, in_buf.buffer.pointer, min_t(u32, 256, in_buf.buffer.length), true); - out_obj = acpi_evaluate_dsm(handle, guid, 1, func, &in_obj); + /* call the BIOS, prefer the named methods over _DSM if available */ + if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE && nfit_mem->has_lsi) + out_obj = acpi_label_info(handle); + else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && nfit_mem->has_lsr) { + struct nd_cmd_get_config_data_hdr *p = buf; + + out_obj = acpi_label_read(handle, p->in_offset, p->in_length); + } else if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA + && nfit_mem->has_lsw) { + struct nd_cmd_set_config_hdr *p = buf; + + out_obj = acpi_label_write(handle, p->in_offset, p->in_length, + p->in_buf); + } else { + u8 revid; + + if (nvdimm) + revid = nfit_dsm_revid(nfit_mem->family, func); + else + revid = 1; + out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj); + } + if (!out_obj) { dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name, cmd_name); @@ -356,8 +566,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, * Set fw_status for all the commands with a known format to be * later interpreted by xlat_status(). */ - if (i >= 1 && ((cmd >= ND_CMD_ARS_CAP && cmd <= ND_CMD_CLEAR_ERROR) - || (cmd >= ND_CMD_SMART && cmd <= ND_CMD_VENDOR))) + if (i >= 1 && ((!nvdimm && cmd >= ND_CMD_ARS_CAP + && cmd <= ND_CMD_CLEAR_ERROR) + || (nvdimm && cmd >= ND_CMD_SMART + && cmd <= ND_CMD_VENDOR))) fw_status = *(u32 *) out_obj->buffer.pointer; if (offset + in_buf.buffer.length < buf_len) { @@ -1431,6 +1643,7 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, { struct acpi_device *adev, *adev_dimm; struct device *dev = acpi_desc->dev; + union acpi_object *obj; unsigned long dsm_mask; const guid_t *guid; int i; @@ -1463,7 +1676,7 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, * different command sets. Note, that checking for function0 (bit0) * tells us if any commands are reachable through this GUID. */ - for (i = NVDIMM_FAMILY_INTEL; i <= NVDIMM_FAMILY_MSFT; i++) + for (i = 0; i <= NVDIMM_FAMILY_MAX; i++) if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) if (family < 0 || i == default_dsm_family) family = i; @@ -1473,7 +1686,7 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, if (override_dsm_mask && !disable_vendor_specific) dsm_mask = override_dsm_mask; else if (nfit_mem->family == NVDIMM_FAMILY_INTEL) { - dsm_mask = 0x3fe; + dsm_mask = NVDIMM_INTEL_CMDMASK; if (disable_vendor_specific) dsm_mask &= ~(1 << ND_CMD_VENDOR); } else if (nfit_mem->family == NVDIMM_FAMILY_HPE1) { @@ -1493,9 +1706,32 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, guid = to_nfit_uuid(nfit_mem->family); for_each_set_bit(i, &dsm_mask, BITS_PER_LONG) - if (acpi_check_dsm(adev_dimm->handle, guid, 1, 1ULL << i)) + if (acpi_check_dsm(adev_dimm->handle, guid, + nfit_dsm_revid(nfit_mem->family, i), + 1ULL << i)) set_bit(i, &nfit_mem->dsm_mask); + obj = acpi_label_info(adev_dimm->handle); + if (obj) { + ACPI_FREE(obj); + nfit_mem->has_lsi = 1; + dev_dbg(dev, "%s: has _LSI\n", dev_name(&adev_dimm->dev)); + } + + obj = acpi_label_read(adev_dimm->handle, 0, 0); + if (obj) { + ACPI_FREE(obj); + nfit_mem->has_lsr = 1; + dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); + } + + obj = acpi_label_write(adev_dimm->handle, 0, 0, NULL); + if (obj) { + ACPI_FREE(obj); + nfit_mem->has_lsw = 1; + dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); + } + return 0; } @@ -1571,8 +1807,21 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) * userspace interface. */ cmd_mask = 1UL << ND_CMD_CALL; - if (nfit_mem->family == NVDIMM_FAMILY_INTEL) - cmd_mask |= nfit_mem->dsm_mask; + if (nfit_mem->family == NVDIMM_FAMILY_INTEL) { + /* + * These commands have a 1:1 correspondence + * between DSM payload and libnvdimm ioctl + * payload format. + */ + cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK; + } + + if (nfit_mem->has_lsi) + set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask); + if (nfit_mem->has_lsr) + set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask); + if (nfit_mem->has_lsw) + set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask); flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush : NULL; @@ -1645,6 +1894,7 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc) int i; nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en; + nd_desc->bus_dsm_mask = acpi_desc->bus_nfit_cmd_force_en; adev = to_acpi_dev(acpi_desc); if (!adev) return; @@ -2239,7 +2489,7 @@ static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc, if (ars_status->out_length < 44 + sizeof(struct nd_ars_record) * (i + 1)) break; - rc = nvdimm_bus_add_poison(nvdimm_bus, + rc = nvdimm_bus_add_badrange(nvdimm_bus, ars_status->records[i].err_address, ars_status->records[i].length); if (rc) diff --git a/drivers/acpi/nfit/mce.c b/drivers/acpi/nfit/mce.c index feeb95d574fa..b92921439657 100644 --- a/drivers/acpi/nfit/mce.c +++ b/drivers/acpi/nfit/mce.c @@ -67,7 +67,7 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val, continue; /* If this fails due to an -ENOMEM, there is little we can do */ - nvdimm_bus_add_poison(acpi_desc->nvdimm_bus, + nvdimm_bus_add_badrange(acpi_desc->nvdimm_bus, ALIGN(mce->addr, L1_CACHE_BYTES), L1_CACHE_BYTES); nvdimm_region_notify(nfit_spa->nd_region, diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index 54292db61262..f0cf18b2da8b 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -24,7 +24,7 @@ /* ACPI 6.1 */ #define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba" -/* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf */ +/* http://pmem.io/documents/NVDIMM_DSM_Interface-V1.6.pdf */ #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" /* https://github.com/HewlettPackard/hpe-nvm/blob/master/Documentation/ */ @@ -38,6 +38,37 @@ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ | ACPI_NFIT_MEM_NOT_ARMED | ACPI_NFIT_MEM_MAP_FAILED) +#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_MSFT + +#define NVDIMM_STANDARD_CMDMASK \ +(1 << ND_CMD_SMART | 1 << ND_CMD_SMART_THRESHOLD | 1 << ND_CMD_DIMM_FLAGS \ + | 1 << ND_CMD_GET_CONFIG_SIZE | 1 << ND_CMD_GET_CONFIG_DATA \ + | 1 << ND_CMD_SET_CONFIG_DATA | 1 << ND_CMD_VENDOR_EFFECT_LOG_SIZE \ + | 1 << ND_CMD_VENDOR_EFFECT_LOG | 1 << ND_CMD_VENDOR) + +/* + * Command numbers that the kernel needs to know about to handle + * non-default DSM revision ids + */ +enum nvdimm_family_cmds { + NVDIMM_INTEL_LATCH_SHUTDOWN = 10, + NVDIMM_INTEL_GET_MODES = 11, + NVDIMM_INTEL_GET_FWINFO = 12, + NVDIMM_INTEL_START_FWUPDATE = 13, + NVDIMM_INTEL_SEND_FWUPDATE = 14, + NVDIMM_INTEL_FINISH_FWUPDATE = 15, + NVDIMM_INTEL_QUERY_FWUPDATE = 16, + NVDIMM_INTEL_SET_THRESHOLD = 17, + NVDIMM_INTEL_INJECT_ERROR = 18, +}; + +#define NVDIMM_INTEL_CMDMASK \ +(NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \ + | 1 << NVDIMM_INTEL_GET_FWINFO | 1 << NVDIMM_INTEL_START_FWUPDATE \ + | 1 << NVDIMM_INTEL_SEND_FWUPDATE | 1 << NVDIMM_INTEL_FINISH_FWUPDATE \ + | 1 << NVDIMM_INTEL_QUERY_FWUPDATE | 1 << NVDIMM_INTEL_SET_THRESHOLD \ + | 1 << NVDIMM_INTEL_INJECT_ERROR | 1 << NVDIMM_INTEL_LATCH_SHUTDOWN) + enum nfit_uuids { /* for simplicity alias the uuid index with the family id */ NFIT_DEV_DIMM = NVDIMM_FAMILY_INTEL, @@ -140,6 +171,9 @@ struct nfit_mem { struct resource *flush_wpq; unsigned long dsm_mask; int family; + u32 has_lsi:1; + u32 has_lsr:1; + u32 has_lsw:1; }; struct acpi_nfit_desc { @@ -167,6 +201,7 @@ struct acpi_nfit_desc { unsigned int init_complete:1; unsigned long dimm_cmd_force_en; unsigned long bus_cmd_force_en; + unsigned long bus_nfit_cmd_force_en; int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa, void *iobuf, u64 len, int rw); }; diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 0a9e5979aaa9..9d49a1acebe3 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -355,6 +355,7 @@ acpi_evaluate_reference(acpi_handle handle, } if (package->package.count > ACPI_MAX_HANDLES) { + kfree(package); return AE_NO_MEMORY; } list->count = package->package.count; diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 2362b9e9701e..027d159ac381 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1101,29 +1101,13 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) goto out; } - if (dev->power.runtime_status == status) + if (dev->power.runtime_status == status || !parent) goto out_set; if (status == RPM_SUSPENDED) { - /* - * It is invalid to suspend a device with an active child, - * unless it has been set to ignore its children. - */ - if (!dev->power.ignore_children && - atomic_read(&dev->power.child_count)) { - dev_err(dev, "runtime PM trying to suspend device but active child\n"); - error = -EBUSY; - goto out; - } - - if (parent) { - atomic_add_unless(&parent->power.child_count, -1, 0); - notify_parent = !parent->power.ignore_children; - } - goto out_set; - } - - if (parent) { + atomic_add_unless(&parent->power.child_count, -1, 0); + notify_parent = !parent->power.ignore_children; + } else { spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING); /* @@ -1307,6 +1291,13 @@ void pm_runtime_enable(struct device *dev) else dev_warn(dev, "Unbalanced %s!\n", __func__); + WARN(!dev->power.disable_depth && + dev->power.runtime_status == RPM_SUSPENDED && + !dev->power.ignore_children && + atomic_read(&dev->power.child_count) > 0, + "Enabling runtime PM for inactive device (%s) with active children\n", + dev_name(dev)); + spin_unlock_irqrestore(&dev->power.lock, flags); } EXPORT_SYMBOL_GPL(pm_runtime_enable); diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 923b417eaf4c..40579d0cb3d1 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -302,7 +302,6 @@ config BLK_DEV_SX8 config BLK_DEV_RAM tristate "RAM block device support" - select DAX if BLK_DEV_RAM_DAX ---help--- Saying Y here will allow you to use a portion of your RAM memory as a block device, so that you can make file systems on it, read and @@ -338,17 +337,6 @@ config BLK_DEV_RAM_SIZE The default value is 4096 kilobytes. Only change this if you know what you are doing. -config BLK_DEV_RAM_DAX - bool "Support Direct Access (DAX) to RAM block devices" - depends on BLK_DEV_RAM && FS_DAX - default n - help - Support filesystems using DAX to access RAM block devices. This - avoids double-buffering data in the page cache before copying it - to the block device. Answering Y will slightly enlarge the kernel, - and will prevent RAM block device backing store memory from being - allocated from highmem (only a problem for highmem systems). - config CDROM_PKTCDVD tristate "Packet writing on CD/DVD media (DEPRECATED)" depends on !UML diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 4e3fb9f104af..e5aa62fcf5a8 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -146,6 +146,7 @@ static struct amiga_floppy_struct unit[FD_MAX_UNITS]; static struct timer_list flush_track_timer[FD_MAX_UNITS]; static struct timer_list post_write_timer; +static unsigned long post_write_timer_drive; static struct timer_list motor_on_timer; static struct timer_list motor_off_timer[FD_MAX_UNITS]; static int on_attempts; @@ -323,7 +324,7 @@ static void fd_deselect (int drive) } -static void motor_on_callback(unsigned long ignored) +static void motor_on_callback(struct timer_list *unused) { if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) { complete_all(&motor_on_completion); @@ -355,7 +356,7 @@ static int fd_motor_on(int nr) on_attempts = -1; #if 0 printk (KERN_ERR "motor_on failed, turning motor off\n"); - fd_motor_off (nr); + fd_motor_off (motor_off_timer + nr); return 0; #else printk (KERN_WARNING "DSKRDY not set after 1.5 seconds - assuming drive is spinning notwithstanding\n"); @@ -365,20 +366,17 @@ static int fd_motor_on(int nr) return 1; } -static void fd_motor_off(unsigned long drive) +static void fd_motor_off(struct timer_list *timer) { - long calledfromint; -#ifdef MODULE - long decusecount; + unsigned long drive = ((unsigned long)timer - + (unsigned long)&motor_off_timer[0]) / + sizeof(motor_off_timer[0]); - decusecount = drive & 0x40000000; -#endif - calledfromint = drive & 0x80000000; drive&=3; - if (calledfromint && !try_fdc(drive)) { + if (!try_fdc(drive)) { /* We would be blocked in an interrupt, so try again later */ - motor_off_timer[drive].expires = jiffies + 1; - add_timer(motor_off_timer + drive); + timer->expires = jiffies + 1; + add_timer(timer); return; } unit[drive].motor = 0; @@ -392,8 +390,6 @@ static void floppy_off (unsigned int nr) int drive; drive = nr & 3; - /* called this way it is always from interrupt */ - motor_off_timer[drive].data = nr | 0x80000000; mod_timer(motor_off_timer + drive, jiffies + 3*HZ); } @@ -435,7 +431,7 @@ static int fd_calibrate(int drive) break; if (--n == 0) { printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive); - fd_motor_off (drive); + fd_motor_off (motor_off_timer + drive); unit[drive].track = -1; rel_fdc(); return 0; @@ -564,7 +560,7 @@ static irqreturn_t fd_block_done(int irq, void *dummy) if (block_flag == 2) { /* writing */ writepending = 2; post_write_timer.expires = jiffies + 1; /* at least 2 ms */ - post_write_timer.data = selected; + post_write_timer_drive = selected; add_timer(&post_write_timer); } else { /* reading */ @@ -651,6 +647,10 @@ static void post_write (unsigned long drive) rel_fdc(); /* corresponds to get_fdc() in raw_write */ } +static void post_write_callback(struct timer_list *timer) +{ + post_write(post_write_timer_drive); +} /* * The following functions are to convert the block contents into raw data @@ -1244,8 +1244,12 @@ static void dos_write(int disk) /* FIXME: this assumes the drive is still spinning - * which is only true if we complete writing a track within three seconds */ -static void flush_track_callback(unsigned long nr) +static void flush_track_callback(struct timer_list *timer) { + unsigned long nr = ((unsigned long)timer - + (unsigned long)&flush_track_timer[0]) / + sizeof(flush_track_timer[0]); + nr&=3; writefromint = 1; if (!try_fdc(nr)) { @@ -1649,8 +1653,7 @@ static void floppy_release(struct gendisk *disk, fmode_t mode) fd_ref[drive] = 0; } #ifdef MODULE -/* the mod_use counter is handled this way */ - floppy_off (drive | 0x40000000); + floppy_off (drive); #endif mutex_unlock(&amiflop_mutex); } @@ -1791,27 +1794,19 @@ static int __init amiga_floppy_probe(struct platform_device *pdev) floppy_find, NULL, NULL); /* initialize variables */ - init_timer(&motor_on_timer); + timer_setup(&motor_on_timer, motor_on_callback, 0); motor_on_timer.expires = 0; - motor_on_timer.data = 0; - motor_on_timer.function = motor_on_callback; for (i = 0; i < FD_MAX_UNITS; i++) { - init_timer(&motor_off_timer[i]); + timer_setup(&motor_off_timer[i], fd_motor_off, 0); motor_off_timer[i].expires = 0; - motor_off_timer[i].data = i|0x80000000; - motor_off_timer[i].function = fd_motor_off; - init_timer(&flush_track_timer[i]); + timer_setup(&flush_track_timer[i], flush_track_callback, 0); flush_track_timer[i].expires = 0; - flush_track_timer[i].data = i; - flush_track_timer[i].function = flush_track_callback; unit[i].track = -1; } - init_timer(&post_write_timer); + timer_setup(&post_write_timer, post_write_callback, 0); post_write_timer.expires = 0; - post_write_timer.data = 0; - post_write_timer.function = post_write; for (i = 0; i < 128; i++) mfmdecode[i]=255; diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index dc43254e05a4..55ab25f79a08 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -744,7 +744,7 @@ count_targets(struct aoedev *d, int *untainted) } static void -rexmit_timer(ulong vp) +rexmit_timer(struct timer_list *timer) { struct aoedev *d; struct aoetgt *t; @@ -758,7 +758,7 @@ rexmit_timer(ulong vp) int utgts; /* number of aoetgt descriptors (not slots) */ int since; - d = (struct aoedev *) vp; + d = from_timer(d, timer, timer); spin_lock_irqsave(&d->lock, flags); @@ -1429,7 +1429,7 @@ aoecmd_ata_id(struct aoedev *d) d->rttavg = RTTAVG_INIT; d->rttdev = RTTDEV_INIT; - d->timer.function = rexmit_timer; + d->timer.function = (TIMER_FUNC_TYPE)rexmit_timer; skb = skb_clone(skb, GFP_ATOMIC); if (skb) { diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index b28fefb90391..697f735b07a4 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -15,7 +15,6 @@ #include #include "aoe.h" -static void dummy_timer(ulong); static void freetgt(struct aoedev *d, struct aoetgt *t); static void skbpoolfree(struct aoedev *d); @@ -146,11 +145,11 @@ aoedev_put(struct aoedev *d) } static void -dummy_timer(ulong vp) +dummy_timer(struct timer_list *t) { struct aoedev *d; - d = (struct aoedev *)vp; + d = from_timer(d, t, timer); if (d->flags & DEVFL_TKILL) return; d->timer.expires = jiffies + HZ; @@ -466,9 +465,7 @@ aoedev_by_aoeaddr(ulong maj, int min, int do_alloc) INIT_WORK(&d->work, aoecmd_sleepwork); spin_lock_init(&d->lock); skb_queue_head_init(&d->skbpool); - init_timer(&d->timer); - d->timer.data = (ulong) d; - d->timer.function = dummy_timer; + timer_setup(&d->timer, dummy_timer, 0); d->timer.expires = jiffies + HZ; add_timer(&d->timer); d->bufpool = NULL; /* defer to aoeblk_gdalloc */ diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 588360d79fca..8028a3a7e7fd 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -21,11 +21,6 @@ #include #include #include -#ifdef CONFIG_BLK_DEV_RAM_DAX -#include -#include -#include -#endif #include @@ -45,9 +40,6 @@ struct brd_device { struct request_queue *brd_queue; struct gendisk *brd_disk; -#ifdef CONFIG_BLK_DEV_RAM_DAX - struct dax_device *dax_dev; -#endif struct list_head brd_list; /* @@ -112,9 +104,6 @@ static struct page *brd_insert_page(struct brd_device *brd, sector_t sector) * restriction might be able to be lifted. */ gfp_flags = GFP_NOIO | __GFP_ZERO; -#ifndef CONFIG_BLK_DEV_RAM_DAX - gfp_flags |= __GFP_HIGHMEM; -#endif page = alloc_page(gfp_flags); if (!page) return NULL; @@ -334,43 +323,6 @@ static int brd_rw_page(struct block_device *bdev, sector_t sector, return err; } -#ifdef CONFIG_BLK_DEV_RAM_DAX -static long __brd_direct_access(struct brd_device *brd, pgoff_t pgoff, - long nr_pages, void **kaddr, pfn_t *pfn) -{ - struct page *page; - - if (!brd) - return -ENODEV; - page = brd_insert_page(brd, (sector_t)pgoff << PAGE_SECTORS_SHIFT); - if (!page) - return -ENOSPC; - *kaddr = page_address(page); - *pfn = page_to_pfn_t(page); - - return 1; -} - -static long brd_dax_direct_access(struct dax_device *dax_dev, - pgoff_t pgoff, long nr_pages, void **kaddr, pfn_t *pfn) -{ - struct brd_device *brd = dax_get_private(dax_dev); - - return __brd_direct_access(brd, pgoff, nr_pages, kaddr, pfn); -} - -static size_t brd_dax_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff, - void *addr, size_t bytes, struct iov_iter *i) -{ - return copy_from_iter(addr, bytes, i); -} - -static const struct dax_operations brd_dax_ops = { - .direct_access = brd_dax_direct_access, - .copy_from_iter = brd_dax_copy_from_iter, -}; -#endif - static const struct block_device_operations brd_fops = { .owner = THIS_MODULE, .rw_page = brd_rw_page, @@ -451,21 +403,8 @@ static struct brd_device *brd_alloc(int i) set_capacity(disk, rd_size * 2); disk->queue->backing_dev_info->capabilities |= BDI_CAP_SYNCHRONOUS_IO; -#ifdef CONFIG_BLK_DEV_RAM_DAX - queue_flag_set_unlocked(QUEUE_FLAG_DAX, brd->brd_queue); - brd->dax_dev = alloc_dax(brd, disk->disk_name, &brd_dax_ops); - if (!brd->dax_dev) - goto out_free_inode; -#endif - - return brd; -#ifdef CONFIG_BLK_DEV_RAM_DAX -out_free_inode: - kill_dax(brd->dax_dev); - put_dax(brd->dax_dev); -#endif out_free_queue: blk_cleanup_queue(brd->brd_queue); out_free_dev: @@ -505,10 +444,6 @@ out: static void brd_del_one(struct brd_device *brd) { list_del(&brd->brd_list); -#ifdef CONFIG_BLK_DEV_RAM_DAX - kill_dax(brd->dax_dev); - put_dax(brd->dax_dev); -#endif del_gendisk(brd->brd_disk); brd_free(brd); } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index a54183935aa1..eae484acfbbc 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -903,10 +903,14 @@ static void unlock_fdc(void) } /* switches the motor off after a given timeout */ -static void motor_off_callback(unsigned long nr) +static void motor_off_callback(struct timer_list *t) { + unsigned long nr = t - motor_off_timer; unsigned char mask = ~(0x10 << UNIT(nr)); + if (WARN_ON_ONCE(nr >= N_DRIVE)) + return; + set_dor(FDC(nr), mask, 0); } @@ -3047,7 +3051,7 @@ static void raw_cmd_done(int flag) else raw_cmd->flags &= ~FD_RAW_DISK_CHANGE; if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER) - motor_off_callback(current_drive); + motor_off_callback(&motor_off_timer[current_drive]); if (raw_cmd->next && (!(raw_cmd->flags & FD_RAW_FAILURE) || @@ -4542,7 +4546,7 @@ static int __init do_floppy_init(void) disks[drive]->fops = &floppy_fops; sprintf(disks[drive]->disk_name, "fd%d", drive); - setup_timer(&motor_off_timer[drive], motor_off_callback, drive); + timer_setup(&motor_off_timer[drive], motor_off_callback, 0); } err = register_blkdev(FLOPPY_MAJOR, "fd"); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index adc877dfef5c..38fc5f397fde 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -348,7 +348,6 @@ struct rbd_client_id { struct rbd_mapping { u64 size; u64 features; - bool read_only; }; /* @@ -450,12 +449,11 @@ static DEFINE_IDA(rbd_dev_id_ida); static struct workqueue_struct *rbd_wq; /* - * Default to false for now, as single-major requires >= 0.75 version of - * userspace rbd utility. + * single-major requires >= 0.75 version of userspace rbd utility. */ -static bool single_major = false; +static bool single_major = true; module_param(single_major, bool, S_IRUGO); -MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: false)"); +MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: true)"); static int rbd_img_request_submit(struct rbd_img_request *img_request); @@ -608,9 +606,6 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) struct rbd_device *rbd_dev = bdev->bd_disk->private_data; bool removing = false; - if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) - return -EROFS; - spin_lock_irq(&rbd_dev->lock); if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) removing = true; @@ -640,46 +635,24 @@ static void rbd_release(struct gendisk *disk, fmode_t mode) static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg) { - int ret = 0; - int val; - bool ro; - bool ro_changed = false; + int ro; - /* get_user() may sleep, so call it before taking rbd_dev->lock */ - if (get_user(val, (int __user *)(arg))) + if (get_user(ro, (int __user *)arg)) return -EFAULT; - ro = val ? true : false; - /* Snapshot doesn't allow to write*/ + /* Snapshots can't be marked read-write */ if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro) return -EROFS; - spin_lock_irq(&rbd_dev->lock); - /* prevent others open this device */ - if (rbd_dev->open_count > 1) { - ret = -EBUSY; - goto out; - } - - if (rbd_dev->mapping.read_only != ro) { - rbd_dev->mapping.read_only = ro; - ro_changed = true; - } - -out: - spin_unlock_irq(&rbd_dev->lock); - /* set_disk_ro() may sleep, so call it after releasing rbd_dev->lock */ - if (ret == 0 && ro_changed) - set_disk_ro(rbd_dev->disk, ro ? 1 : 0); - - return ret; + /* Let blkdev_roset() handle it */ + return -ENOTTY; } static int rbd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct rbd_device *rbd_dev = bdev->bd_disk->private_data; - int ret = 0; + int ret; switch (cmd) { case BLKROSET: @@ -4050,15 +4023,8 @@ static void rbd_queue_workfn(struct work_struct *work) goto err_rq; } - /* Only reads are allowed to a read-only device */ - - if (op_type != OBJ_OP_READ) { - if (rbd_dev->mapping.read_only) { - result = -EROFS; - goto err_rq; - } - rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP); - } + rbd_assert(op_type == OBJ_OP_READ || + rbd_dev->spec->snap_id == CEPH_NOSNAP); /* * Quit early if the mapped snapshot no longer exists. It's @@ -4423,7 +4389,6 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) /* enable the discard support */ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); q->limits.discard_granularity = segment_size; - q->limits.discard_alignment = segment_size; blk_queue_max_discard_sectors(q, segment_size / SECTOR_SIZE); blk_queue_max_write_zeroes_sectors(q, segment_size / SECTOR_SIZE); @@ -5994,7 +5959,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev) goto err_out_disk; set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE); - set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only); + set_disk_ro(rbd_dev->disk, rbd_dev->opts->read_only); ret = dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id); if (ret) @@ -6145,7 +6110,6 @@ static ssize_t do_rbd_add(struct bus_type *bus, struct rbd_options *rbd_opts = NULL; struct rbd_spec *spec = NULL; struct rbd_client *rbdc; - bool read_only; int rc; if (!try_module_get(THIS_MODULE)) @@ -6194,11 +6158,8 @@ static ssize_t do_rbd_add(struct bus_type *bus, } /* If we are mapping a snapshot it must be marked read-only */ - - read_only = rbd_dev->opts->read_only; if (rbd_dev->spec->snap_id != CEPH_NOSNAP) - read_only = true; - rbd_dev->mapping.read_only = read_only; + rbd_dev->opts->read_only = true; rc = rbd_dev_device_setup(rbd_dev); if (rc) diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 9f931f8f6b4c..e620e423102b 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -239,10 +239,10 @@ static unsigned short write_postamble[] = { static void seek_track(struct floppy_state *fs, int n); static void init_dma(struct dbdma_cmd *cp, int cmd, void *buf, int count); static void act(struct floppy_state *fs); -static void scan_timeout(unsigned long data); -static void seek_timeout(unsigned long data); -static void settle_timeout(unsigned long data); -static void xfer_timeout(unsigned long data); +static void scan_timeout(struct timer_list *t); +static void seek_timeout(struct timer_list *t); +static void settle_timeout(struct timer_list *t); +static void xfer_timeout(struct timer_list *t); static irqreturn_t swim3_interrupt(int irq, void *dev_id); /*static void fd_dma_interrupt(int irq, void *dev_id);*/ static int grab_drive(struct floppy_state *fs, enum swim_state state, @@ -392,13 +392,12 @@ static void do_fd_request(struct request_queue * q) } static void set_timeout(struct floppy_state *fs, int nticks, - void (*proc)(unsigned long)) + void (*proc)(struct timer_list *t)) { if (fs->timeout_pending) del_timer(&fs->timeout); fs->timeout.expires = jiffies + nticks; - fs->timeout.function = proc; - fs->timeout.data = (unsigned long) fs; + fs->timeout.function = (TIMER_FUNC_TYPE)proc; add_timer(&fs->timeout); fs->timeout_pending = 1; } @@ -569,9 +568,9 @@ static void act(struct floppy_state *fs) } } -static void scan_timeout(unsigned long data) +static void scan_timeout(struct timer_list *t) { - struct floppy_state *fs = (struct floppy_state *) data; + struct floppy_state *fs = from_timer(fs, t, timeout); struct swim3 __iomem *sw = fs->swim3; unsigned long flags; @@ -594,9 +593,9 @@ static void scan_timeout(unsigned long data) spin_unlock_irqrestore(&swim3_lock, flags); } -static void seek_timeout(unsigned long data) +static void seek_timeout(struct timer_list *t) { - struct floppy_state *fs = (struct floppy_state *) data; + struct floppy_state *fs = from_timer(fs, t, timeout); struct swim3 __iomem *sw = fs->swim3; unsigned long flags; @@ -614,9 +613,9 @@ static void seek_timeout(unsigned long data) spin_unlock_irqrestore(&swim3_lock, flags); } -static void settle_timeout(unsigned long data) +static void settle_timeout(struct timer_list *t) { - struct floppy_state *fs = (struct floppy_state *) data; + struct floppy_state *fs = from_timer(fs, t, timeout); struct swim3 __iomem *sw = fs->swim3; unsigned long flags; @@ -644,9 +643,9 @@ static void settle_timeout(unsigned long data) spin_unlock_irqrestore(&swim3_lock, flags); } -static void xfer_timeout(unsigned long data) +static void xfer_timeout(struct timer_list *t) { - struct floppy_state *fs = (struct floppy_state *) data; + struct floppy_state *fs = from_timer(fs, t, timeout); struct swim3 __iomem *sw = fs->swim3; struct dbdma_regs __iomem *dr = fs->dma; unsigned long flags; @@ -1182,7 +1181,7 @@ static int swim3_add_device(struct macio_dev *mdev, int index) return -EBUSY; } - init_timer(&fs->timeout); + timer_setup(&fs->timeout, NULL, 0); swim3_info("SWIM3 floppy controller %s\n", mdev->media_bay ? "in media bay" : ""); diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index 70d434bc1cbf..c4ef73c6f455 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -204,9 +204,6 @@ static ssize_t bt_bmc_read(struct file *file, char __user *buf, ssize_t ret = 0; ssize_t nread; - if (!access_ok(VERIFY_WRITE, buf, count)) - return -EFAULT; - WARN_ON(*ppos); if (wait_event_interruptible(bt_bmc->queue, @@ -277,9 +274,6 @@ static ssize_t bt_bmc_write(struct file *file, const char __user *buf, if (count < 5) return -EINVAL; - if (!access_ok(VERIFY_READ, buf, count)) - return -EFAULT; - WARN_ON(*ppos); /* diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index aadabd9d1e2b..cd8d689138ff 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -14,14 +14,20 @@ #include #include #include +#include #include "pmc.h" -#define UTMI_FIXED_MUL 40 +/* + * The purpose of this clock is to generate a 480 MHz signal. A different + * rate can't be configured. + */ +#define UTMI_RATE 480000000 struct clk_utmi { struct clk_hw hw; - struct regmap *regmap; + struct regmap *regmap_pmc; + struct regmap *regmap_sfr; }; #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) @@ -37,13 +43,54 @@ static inline bool clk_utmi_ready(struct regmap *regmap) static int clk_utmi_prepare(struct clk_hw *hw) { + struct clk_hw *hw_parent; struct clk_utmi *utmi = to_clk_utmi(hw); unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN; + unsigned int utmi_ref_clk_freq; + unsigned long parent_rate; + + /* + * If mainck rate is different from 12 MHz, we have to configure the + * FREQ field of the SFR_UTMICKTRIM register to generate properly + * the utmi clock. + */ + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); + + switch (parent_rate) { + case 12000000: + utmi_ref_clk_freq = 0; + break; + case 16000000: + utmi_ref_clk_freq = 1; + break; + case 24000000: + utmi_ref_clk_freq = 2; + break; + /* + * Not supported on SAMA5D2 but it's not an issue since MAINCK + * maximum value is 24 MHz. + */ + case 48000000: + utmi_ref_clk_freq = 3; + break; + default: + pr_err("UTMICK: unsupported mainck rate\n"); + return -EINVAL; + } - regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr); + if (utmi->regmap_sfr) { + regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, + AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); + } else if (utmi_ref_clk_freq) { + pr_err("UTMICK: sfr node required\n"); + return -EINVAL; + } - while (!clk_utmi_ready(utmi->regmap)) + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); + + while (!clk_utmi_ready(utmi->regmap_pmc)) cpu_relax(); return 0; @@ -53,21 +100,22 @@ static int clk_utmi_is_prepared(struct clk_hw *hw) { struct clk_utmi *utmi = to_clk_utmi(hw); - return clk_utmi_ready(utmi->regmap); + return clk_utmi_ready(utmi->regmap_pmc); } static void clk_utmi_unprepare(struct clk_hw *hw) { struct clk_utmi *utmi = to_clk_utmi(hw); - regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0); + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, + AT91_PMC_UPLLEN, 0); } static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - /* UTMI clk is a fixed clk multiplier */ - return parent_rate * UTMI_FIXED_MUL; + /* UTMI clk rate is fixed. */ + return UTMI_RATE; } static const struct clk_ops utmi_ops = { @@ -78,7 +126,7 @@ static const struct clk_ops utmi_ops = { }; static struct clk_hw * __init -at91_clk_register_utmi(struct regmap *regmap, +at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, const char *name, const char *parent_name) { struct clk_utmi *utmi; @@ -97,7 +145,8 @@ at91_clk_register_utmi(struct regmap *regmap, init.flags = CLK_SET_RATE_GATE; utmi->hw.init = &init; - utmi->regmap = regmap; + utmi->regmap_pmc = regmap_pmc; + utmi->regmap_sfr = regmap_sfr; hw = &utmi->hw; ret = clk_hw_register(NULL, &utmi->hw); @@ -114,17 +163,35 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np) struct clk_hw *hw; const char *parent_name; const char *name = np->name; - struct regmap *regmap; + struct regmap *regmap_pmc, *regmap_sfr; parent_name = of_clk_get_parent_name(np, 0); of_property_read_string(np, "clock-output-names", &name); - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) + regmap_pmc = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap_pmc)) return; - hw = at91_clk_register_utmi(regmap, name, parent_name); + /* + * If the device supports different mainck rates, this value has to be + * set in the UTMI Clock Trimming register. + * - 9x5: mainck supports several rates but it is indicated that a + * 12 MHz is needed in case of USB. + * - sama5d3 and sama5d2: mainck supports several rates. Configuring + * the FREQ field of the UTMI Clock Trimming register is mandatory. + * - sama5d4: mainck is at 12 MHz. + * + * We only need to retrieve sama5d3 or sama5d2 sfr regmap. + */ + regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d3-sfr"); + if (IS_ERR(regmap_sfr)) { + regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); + if (IS_ERR(regmap_sfr)) + regmap_sfr = NULL; + } + + hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name); if (IS_ERR(hw)) return; diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index c37a7f0e83aa..281f4322355c 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -579,18 +579,13 @@ static u32 *parent_process(const char *clocks[], */ parent_names = kmalloc_array(parent_count, sizeof(*parent_names), GFP_KERNEL); - if (!parent_names) { - pr_err("%s: error allocating %u parent names\n", __func__, - parent_count); + if (!parent_names) return ERR_PTR(-ENOMEM); - } /* There is at least one parent, so allocate a selector array */ parent_sel = kmalloc_array(parent_count, sizeof(*parent_sel), GFP_KERNEL); if (!parent_sel) { - pr_err("%s: error allocating %u parent selectors\n", __func__, - parent_count); kfree(parent_names); return ERR_PTR(-ENOMEM); diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index c933be01c7db..0a7e7d5a7506 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -665,7 +665,7 @@ static int cdce925_probe(struct i2c_client *client, init.ops = &cdce925_pll_ops; init.flags = 0; init.parent_names = &parent_name; - init.num_parents = parent_name ? 1 : 0; + init.num_parents = 1; /* Register PLL clocks */ for (i = 0; i < data->chip_info->num_plls; ++i) { diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c index 86b245746a6b..151513c655c3 100644 --- a/drivers/clk/clk-gpio.c +++ b/drivers/clk/clk-gpio.c @@ -15,9 +15,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -95,14 +93,12 @@ const struct clk_ops clk_gpio_mux_ops = { EXPORT_SYMBOL_GPL(clk_gpio_mux_ops); static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags, - const struct clk_ops *clk_gpio_ops) + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags, const struct clk_ops *clk_gpio_ops) { struct clk_gpio *clk_gpio; struct clk_hw *hw; struct clk_init_data init = {}; - unsigned long gpio_flags; int err; if (dev) @@ -113,32 +109,13 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, if (!clk_gpio) return ERR_PTR(-ENOMEM); - if (active_low) - gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_HIGH; - else - gpio_flags = GPIOF_OUT_INIT_LOW; - - if (dev) - err = devm_gpio_request_one(dev, gpio, gpio_flags, name); - else - err = gpio_request_one(gpio, gpio_flags, name); - if (err) { - if (err != -EPROBE_DEFER) - pr_err("%s: %s: Error requesting clock control gpio %u\n", - __func__, name, gpio); - if (!dev) - kfree(clk_gpio); - - return ERR_PTR(err); - } - init.name = name; init.ops = clk_gpio_ops; init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; - clk_gpio->gpiod = gpio_to_desc(gpio); + clk_gpio->gpiod = gpiod; clk_gpio->hw.init = &init; hw = &clk_gpio->hw; @@ -151,7 +128,6 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, return hw; if (!dev) { - gpiod_put(clk_gpio->gpiod); kfree(clk_gpio); } @@ -164,29 +140,27 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of this clock's parent - * @gpio: gpio number to gate this clock - * @active_low: true if gpio should be set to 0 to enable clock + * @gpiod: gpio descriptor to gate this clock * @flags: clock flags */ struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name, - const char *parent_name, unsigned gpio, bool active_low, + const char *parent_name, struct gpio_desc *gpiod, unsigned long flags) { return clk_register_gpio(dev, name, (parent_name ? &parent_name : NULL), - (parent_name ? 1 : 0), gpio, active_low, flags, + (parent_name ? 1 : 0), gpiod, flags, &clk_gpio_gate_ops); } EXPORT_SYMBOL_GPL(clk_hw_register_gpio_gate); struct clk *clk_register_gpio_gate(struct device *dev, const char *name, - const char *parent_name, unsigned gpio, bool active_low, + const char *parent_name, struct gpio_desc *gpiod, unsigned long flags) { struct clk_hw *hw; - hw = clk_hw_register_gpio_gate(dev, name, parent_name, gpio, active_low, - flags); + hw = clk_hw_register_gpio_gate(dev, name, parent_name, gpiod, flags); if (IS_ERR(hw)) return ERR_CAST(hw); return hw->clk; @@ -199,13 +173,12 @@ EXPORT_SYMBOL_GPL(clk_register_gpio_gate); * @name: name of this clock * @parent_names: names of this clock's parents * @num_parents: number of parents listed in @parent_names - * @gpio: gpio number to gate this clock - * @active_low: true if gpio should be set to 0 to enable clock + * @gpiod: gpio descriptor to gate this clock * @flags: clock flags */ struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags) + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags) { if (num_parents != 2) { pr_err("mux-clock %s must have 2 parents\n", name); @@ -213,18 +186,18 @@ struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, } return clk_register_gpio(dev, name, parent_names, num_parents, - gpio, active_low, flags, &clk_gpio_mux_ops); + gpiod, flags, &clk_gpio_mux_ops); } EXPORT_SYMBOL_GPL(clk_hw_register_gpio_mux); struct clk *clk_register_gpio_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags) + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags) { struct clk_hw *hw; hw = clk_hw_register_gpio_mux(dev, name, parent_names, num_parents, - gpio, active_low, flags); + gpiod, flags); if (IS_ERR(hw)) return ERR_CAST(hw); return hw->clk; @@ -236,10 +209,10 @@ static int gpio_clk_driver_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; const char **parent_names, *gpio_name; unsigned int num_parents; - int gpio; - enum of_gpio_flags of_flags; + struct gpio_desc *gpiod; struct clk *clk; - bool active_low, is_mux; + bool is_mux; + int ret; num_parents = of_clk_get_parent_count(node); if (num_parents) { @@ -255,28 +228,27 @@ static int gpio_clk_driver_probe(struct platform_device *pdev) is_mux = of_device_is_compatible(node, "gpio-mux-clock"); - gpio_name = is_mux ? "select-gpios" : "enable-gpios"; - gpio = of_get_named_gpio_flags(node, gpio_name, 0, &of_flags); - if (gpio < 0) { - if (gpio == -EPROBE_DEFER) + gpio_name = is_mux ? "select" : "enable"; + gpiod = devm_gpiod_get(&pdev->dev, gpio_name, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + ret = PTR_ERR(gpiod); + if (ret == -EPROBE_DEFER) pr_debug("%s: %s: GPIOs not yet available, retry later\n", node->name, __func__); else - pr_err("%s: %s: Can't get '%s' DT property\n", + pr_err("%s: %s: Can't get '%s' named GPIO property\n", node->name, __func__, gpio_name); - return gpio; + return ret; } - active_low = of_flags & OF_GPIO_ACTIVE_LOW; - if (is_mux) clk = clk_register_gpio_mux(&pdev->dev, node->name, - parent_names, num_parents, gpio, active_low, 0); + parent_names, num_parents, gpiod, 0); else clk = clk_register_gpio_gate(&pdev->dev, node->name, - parent_names ? parent_names[0] : NULL, gpio, - active_low, 0); + parent_names ? parent_names[0] : NULL, gpiod, + 0); if (IS_ERR(clk)) return PTR_ERR(clk); diff --git a/drivers/clk/clk-hsdk-pll.c b/drivers/clk/clk-hsdk-pll.c index bbf237173b37..c4ee280f454d 100644 --- a/drivers/clk/clk-hsdk-pll.c +++ b/drivers/clk/clk-hsdk-pll.c @@ -139,7 +139,7 @@ static inline void hsdk_pll_set_cfg(struct hsdk_pll_clk *clk, val |= cfg->odiv << CGU_PLL_CTRL_ODIV_SHIFT; val |= cfg->band << CGU_PLL_CTRL_BAND_SHIFT; - dev_dbg(clk->dev, "write configurarion: %#x\n", val); + dev_dbg(clk->dev, "write configuration: %#x\n", val); hsdk_pll_write(clk, CGU_PLL_CTRL, val); } @@ -169,7 +169,7 @@ static unsigned long hsdk_pll_recalc_rate(struct clk_hw *hw, val = hsdk_pll_read(clk, CGU_PLL_CTRL); - dev_dbg(clk->dev, "current configurarion: %#x\n", val); + dev_dbg(clk->dev, "current configuration: %#x\n", val); /* Check if PLL is disabled */ if (val & CGU_PLL_CTRL_PD) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 16a3d5717f4e..39cabe157163 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -134,11 +134,9 @@ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name, } /* allocate the mux */ - mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); - if (!mux) { - pr_err("%s: could not allocate mux clk\n", __func__); + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) return ERR_PTR(-ENOMEM); - } init.name = name; if (clk_mux_flags & CLK_MUX_READ_ONLY) diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c index a94c3f56c590..61c3e40507d3 100644 --- a/drivers/clk/clk-stm32h7.c +++ b/drivers/clk/clk-stm32h7.c @@ -384,7 +384,7 @@ static void get_cfg_composite_div(const struct composite_clk_gcfg *gcfg, mux_ops = div_ops = gate_ops = NULL; mux_hw = div_hw = gate_hw = NULL; - if (gcfg->mux && gcfg->mux) { + if (gcfg->mux && cfg->mux) { mux = _get_cmux(base + cfg->mux->offset, cfg->mux->shift, cfg->mux->width, @@ -410,7 +410,7 @@ static void get_cfg_composite_div(const struct composite_clk_gcfg *gcfg, } } - if (gcfg->gate && gcfg->gate) { + if (gcfg->gate && cfg->gate) { gate = _get_cgate(base + cfg->gate->offset, cfg->gate->bit_idx, gcfg->gate->flags, lock); diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 7b222a5db931..25dfe050ae9f 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -82,7 +82,7 @@ static const struct clk_ops twl6040_pdmclk_ops = { .recalc_rate = twl6040_pdmclk_recalc_rate, }; -static struct clk_init_data twl6040_pdmclk_init = { +static const struct clk_init_data twl6040_pdmclk_init = { .name = "pdmclk", .ops = &twl6040_pdmclk_ops, .flags = CLK_GET_RATE_NOCACHE, diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c index ec8aafda6e24..7b3e1921771f 100644 --- a/drivers/clk/clk-u300.c +++ b/drivers/clk/clk-u300.c @@ -229,15 +229,15 @@ #define U300_SYSCON_S0CCR_CLOCK_FREQ_MASK (0x01E0) #define U300_SYSCON_S0CCR_CLOCK_SELECT_MASK (0x001E) #define U300_SYSCON_S0CCR_CLOCK_ENABLE (0x0001) -#define U300_SYSCON_S0CCR_SEL_MCLK (0x8<<1) -#define U300_SYSCON_S0CCR_SEL_ACC_FSM_CLK (0xA<<1) -#define U300_SYSCON_S0CCR_SEL_PLL60_48_CLK (0xC<<1) -#define U300_SYSCON_S0CCR_SEL_PLL60_60_CLK (0xD<<1) -#define U300_SYSCON_S0CCR_SEL_ACC_PLL208_CLK (0xE<<1) -#define U300_SYSCON_S0CCR_SEL_APP_PLL13_CLK (0x0<<1) -#define U300_SYSCON_S0CCR_SEL_APP_FSM_CLK (0x2<<1) -#define U300_SYSCON_S0CCR_SEL_RTC_CLK (0x4<<1) -#define U300_SYSCON_S0CCR_SEL_APP_PLL208_CLK (0x6<<1) +#define U300_SYSCON_S0CCR_SEL_MCLK (0x8 << 1) +#define U300_SYSCON_S0CCR_SEL_ACC_FSM_CLK (0xA << 1) +#define U300_SYSCON_S0CCR_SEL_PLL60_48_CLK (0xC << 1) +#define U300_SYSCON_S0CCR_SEL_PLL60_60_CLK (0xD << 1) +#define U300_SYSCON_S0CCR_SEL_ACC_PLL208_CLK (0xE << 1) +#define U300_SYSCON_S0CCR_SEL_APP_PLL13_CLK (0x0 << 1) +#define U300_SYSCON_S0CCR_SEL_APP_FSM_CLK (0x2 << 1) +#define U300_SYSCON_S0CCR_SEL_RTC_CLK (0x4 << 1) +#define U300_SYSCON_S0CCR_SEL_APP_PLL208_CLK (0x6 << 1) /* SYS_1_CLK_CONTROL second clock control 16 bit (R/W) */ #define U300_SYSCON_S1CCR (0x124) #define U300_SYSCON_S1CCR_FIELD_MASK (0x43FF) @@ -247,16 +247,16 @@ #define U300_SYSCON_S1CCR_CLOCK_FREQ_MASK (0x01E0) #define U300_SYSCON_S1CCR_CLOCK_SELECT_MASK (0x001E) #define U300_SYSCON_S1CCR_CLOCK_ENABLE (0x0001) -#define U300_SYSCON_S1CCR_SEL_MCLK (0x8<<1) -#define U300_SYSCON_S1CCR_SEL_ACC_FSM_CLK (0xA<<1) -#define U300_SYSCON_S1CCR_SEL_PLL60_48_CLK (0xC<<1) -#define U300_SYSCON_S1CCR_SEL_PLL60_60_CLK (0xD<<1) -#define U300_SYSCON_S1CCR_SEL_ACC_PLL208_CLK (0xE<<1) -#define U300_SYSCON_S1CCR_SEL_ACC_PLL13_CLK (0x0<<1) -#define U300_SYSCON_S1CCR_SEL_APP_FSM_CLK (0x2<<1) -#define U300_SYSCON_S1CCR_SEL_RTC_CLK (0x4<<1) -#define U300_SYSCON_S1CCR_SEL_APP_PLL208_CLK (0x6<<1) -/* SYS_2_CLK_CONTROL third clock contol 16 bit (R/W) */ +#define U300_SYSCON_S1CCR_SEL_MCLK (0x8 << 1) +#define U300_SYSCON_S1CCR_SEL_ACC_FSM_CLK (0xA << 1) +#define U300_SYSCON_S1CCR_SEL_PLL60_48_CLK (0xC << 1) +#define U300_SYSCON_S1CCR_SEL_PLL60_60_CLK (0xD << 1) +#define U300_SYSCON_S1CCR_SEL_ACC_PLL208_CLK (0xE << 1) +#define U300_SYSCON_S1CCR_SEL_ACC_PLL13_CLK (0x0 << 1) +#define U300_SYSCON_S1CCR_SEL_APP_FSM_CLK (0x2 << 1) +#define U300_SYSCON_S1CCR_SEL_RTC_CLK (0x4 << 1) +#define U300_SYSCON_S1CCR_SEL_APP_PLL208_CLK (0x6 << 1) +/* SYS_2_CLK_CONTROL third clock control 16 bit (R/W) */ #define U300_SYSCON_S2CCR (0x128) #define U300_SYSCON_S2CCR_FIELD_MASK (0xC3FF) #define U300_SYSCON_S2CCR_CLK_STEAL (0x8000) @@ -266,15 +266,15 @@ #define U300_SYSCON_S2CCR_CLOCK_FREQ_MASK (0x01E0) #define U300_SYSCON_S2CCR_CLOCK_SELECT_MASK (0x001E) #define U300_SYSCON_S2CCR_CLOCK_ENABLE (0x0001) -#define U300_SYSCON_S2CCR_SEL_MCLK (0x8<<1) -#define U300_SYSCON_S2CCR_SEL_ACC_FSM_CLK (0xA<<1) -#define U300_SYSCON_S2CCR_SEL_PLL60_48_CLK (0xC<<1) -#define U300_SYSCON_S2CCR_SEL_PLL60_60_CLK (0xD<<1) -#define U300_SYSCON_S2CCR_SEL_ACC_PLL208_CLK (0xE<<1) -#define U300_SYSCON_S2CCR_SEL_ACC_PLL13_CLK (0x0<<1) -#define U300_SYSCON_S2CCR_SEL_APP_FSM_CLK (0x2<<1) -#define U300_SYSCON_S2CCR_SEL_RTC_CLK (0x4<<1) -#define U300_SYSCON_S2CCR_SEL_APP_PLL208_CLK (0x6<<1) +#define U300_SYSCON_S2CCR_SEL_MCLK (0x8 << 1) +#define U300_SYSCON_S2CCR_SEL_ACC_FSM_CLK (0xA << 1) +#define U300_SYSCON_S2CCR_SEL_PLL60_48_CLK (0xC << 1) +#define U300_SYSCON_S2CCR_SEL_PLL60_60_CLK (0xD << 1) +#define U300_SYSCON_S2CCR_SEL_ACC_PLL208_CLK (0xE << 1) +#define U300_SYSCON_S2CCR_SEL_ACC_PLL13_CLK (0x0 << 1) +#define U300_SYSCON_S2CCR_SEL_APP_FSM_CLK (0x2 << 1) +#define U300_SYSCON_S2CCR_SEL_RTC_CLK (0x4 << 1) +#define U300_SYSCON_S2CCR_SEL_APP_PLL208_CLK (0x6 << 1) /* SC_PLL_IRQ_CONTROL 16bit (R/W) */ #define U300_SYSCON_PICR (0x0130) #define U300_SYSCON_PICR_MASK (0x00FF) @@ -378,7 +378,7 @@ * +- ISP Image Signal Processor (U335 only) * +- CDS (U335 only) * +- DMA Direct Memory Access Controller - * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) + * +- AAIF APP/ACC Interface (Mobile Scalable Link, MSL) * +- APEX * +- VIDEO_ENC AVE2/3 Video Encoder * +- XGAM Graphics Accelerator Controller @@ -568,14 +568,14 @@ syscon_clk_recalc_rate(struct clk_hw *hw, struct clk_syscon *sclk = to_syscon(hw); u16 perf = syscon_get_perf(); - switch(sclk->clk_val) { + switch (sclk->clk_val) { case U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN: case U300_SYSCON_SBCER_I2C0_CLK_EN: case U300_SYSCON_SBCER_I2C1_CLK_EN: case U300_SYSCON_SBCER_MMC_CLK_EN: case U300_SYSCON_SBCER_SPI_CLK_EN: /* The FAST clocks have one progression */ - switch(perf) { + switch (perf) { case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: return 13000000; @@ -586,7 +586,7 @@ syscon_clk_recalc_rate(struct clk_hw *hw, case U300_SYSCON_SBCER_NANDIF_CLK_EN: case U300_SYSCON_SBCER_XGAM_CLK_EN: /* AMBA interconnect peripherals */ - switch(perf) { + switch (perf) { case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: return 6500000; @@ -598,7 +598,7 @@ syscon_clk_recalc_rate(struct clk_hw *hw, case U300_SYSCON_SBCER_SEMI_CLK_EN: case U300_SYSCON_SBCER_EMIF_CLK_EN: /* EMIF speeds */ - switch(perf) { + switch (perf) { case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: return 13000000; @@ -609,7 +609,7 @@ syscon_clk_recalc_rate(struct clk_hw *hw, } case U300_SYSCON_SBCER_CPU_CLK_EN: /* And the fast CPU clock */ - switch(perf) { + switch (perf) { case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: return 13000000; @@ -702,12 +702,10 @@ syscon_clk_register(struct device *dev, const char *name, struct clk_init_data init; int ret; - sclk = kzalloc(sizeof(struct clk_syscon), GFP_KERNEL); - if (!sclk) { - pr_err("could not allocate syscon clock %s\n", - name); + sclk = kzalloc(sizeof(*sclk), GFP_KERNEL); + if (!sclk) return ERR_PTR(-ENOMEM); - } + init.name = name; init.ops = &syscon_clk_ops; init.flags = flags; @@ -1123,12 +1121,10 @@ mclk_clk_register(struct device *dev, const char *name, struct clk_init_data init; int ret; - mclk = kzalloc(sizeof(struct clk_mclk), GFP_KERNEL); - if (!mclk) { - pr_err("could not allocate MMC/SD clock %s\n", - name); + mclk = kzalloc(sizeof(*mclk), GFP_KERNEL); + if (!mclk) return ERR_PTR(-ENOMEM); - } + init.name = "mclk"; init.ops = &mclk_ops; init.flags = 0; diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index a47960aacfa5..146769532325 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -52,7 +52,7 @@ static const struct clk_ops wm831x_xtal_ops = { .recalc_rate = wm831x_xtal_recalc_rate, }; -static struct clk_init_data wm831x_xtal_init = { +static const struct clk_init_data wm831x_xtal_init = { .name = "xtal", .ops = &wm831x_xtal_ops, }; @@ -225,7 +225,7 @@ static const struct clk_ops wm831x_fll_ops = { .get_parent = wm831x_fll_get_parent, }; -static struct clk_init_data wm831x_fll_init = { +static const struct clk_init_data wm831x_fll_init = { .name = "fll", .ops = &wm831x_fll_ops, .parent_names = wm831x_fll_parents, @@ -338,7 +338,7 @@ static const struct clk_ops wm831x_clkout_ops = { .set_parent = wm831x_clkout_set_parent, }; -static struct clk_init_data wm831x_clkout_init = { +static const struct clk_init_data wm831x_clkout_init = { .name = "clkout", .ops = &wm831x_clkout_ops, .parent_names = wm831x_clkout_parents, diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 4c75821a3933..531b030d4d4e 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -146,10 +146,8 @@ static struct clk *xgene_register_clk_pll(struct device *dev, /* allocate the APM clock structure */ apmclk = kzalloc(sizeof(*apmclk), GFP_KERNEL); - if (!apmclk) { - pr_err("%s: could not allocate APM clk\n", __func__); + if (!apmclk) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &xgene_clk_pll_ops; @@ -191,7 +189,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty int version = xgene_pllclk_version(np); reg = of_iomap(np, 0); - if (reg == NULL) { + if (!reg) { pr_err("Unable to map CSR register for %pOF\n", np); return; } @@ -467,7 +465,7 @@ static int xgene_clk_enable(struct clk_hw *hw) if (pclk->lock) spin_lock_irqsave(pclk->lock, flags); - if (pclk->param.csr_reg != NULL) { + if (pclk->param.csr_reg) { pr_debug("%s clock enabled\n", clk_hw_get_name(hw)); /* First enable the clock */ data = xgene_clk_read(pclk->param.csr_reg + @@ -507,7 +505,7 @@ static void xgene_clk_disable(struct clk_hw *hw) if (pclk->lock) spin_lock_irqsave(pclk->lock, flags); - if (pclk->param.csr_reg != NULL) { + if (pclk->param.csr_reg) { pr_debug("%s clock disabled\n", clk_hw_get_name(hw)); /* First put the CSR in reset */ data = xgene_clk_read(pclk->param.csr_reg + @@ -533,7 +531,7 @@ static int xgene_clk_is_enabled(struct clk_hw *hw) struct xgene_clk *pclk = to_xgene_clk(hw); u32 data = 0; - if (pclk->param.csr_reg != NULL) { + if (pclk->param.csr_reg) { pr_debug("%s clock checking\n", clk_hw_get_name(hw)); data = xgene_clk_read(pclk->param.csr_reg + pclk->param.reg_clk_offset); @@ -542,7 +540,7 @@ static int xgene_clk_is_enabled(struct clk_hw *hw) "disabled"); } - if (pclk->param.csr_reg == NULL) + if (!pclk->param.csr_reg) return 1; return data & pclk->param.reg_clk_mask ? 1 : 0; } @@ -650,10 +648,8 @@ static struct clk *xgene_register_clk(struct device *dev, /* allocate the APM clock structure */ apmclk = kzalloc(sizeof(*apmclk), GFP_KERNEL); - if (!apmclk) { - pr_err("%s: could not allocate APM clk\n", __func__); + if (!apmclk) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &xgene_clk_ops; @@ -709,7 +705,7 @@ static void __init xgene_devclk_init(struct device_node *np) break; } map_res = of_iomap(np, i); - if (map_res == NULL) { + if (!map_res) { pr_err("Unable to map resource %d for %pOF\n", i, np); goto err; } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c8d83acda006..647d056df88c 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,7 @@ struct clk_core { const struct clk_ops *ops; struct clk_hw *hw; struct module *owner; + struct device *dev; struct clk_core *parent; const char **parent_names; struct clk_core **parents; @@ -87,6 +89,26 @@ struct clk { struct hlist_node clks_node; }; +/*** runtime pm ***/ +static int clk_pm_runtime_get(struct clk_core *core) +{ + int ret = 0; + + if (!core->dev) + return 0; + + ret = pm_runtime_get_sync(core->dev); + return ret < 0 ? ret : 0; +} + +static void clk_pm_runtime_put(struct clk_core *core) +{ + if (!core->dev) + return; + + pm_runtime_put_sync(core->dev); +} + /*** locking ***/ static void clk_prepare_lock(void) { @@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags) static bool clk_core_is_prepared(struct clk_core *core) { + bool ret = false; + /* * .is_prepared is optional for clocks that can prepare * fall back to software usage counter if it is missing @@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core) if (!core->ops->is_prepared) return core->prepare_count; - return core->ops->is_prepared(core->hw); + if (!clk_pm_runtime_get(core)) { + ret = core->ops->is_prepared(core->hw); + clk_pm_runtime_put(core); + } + + return ret; } static bool clk_core_is_enabled(struct clk_core *core) { + bool ret = false; + /* * .is_enabled is only mandatory for clocks that gate * fall back to software usage counter if .is_enabled is missing @@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core) if (!core->ops->is_enabled) return core->enable_count; - return core->ops->is_enabled(core->hw); + /* + * Check if clock controller's device is runtime active before + * calling .is_enabled callback. If not, assume that clock is + * disabled, because we might be called from atomic context, from + * which pm_runtime_get() is not allowed. + * This function is called mainly from clk_disable_unused_subtree, + * which ensures proper runtime pm activation of controller before + * taking enable spinlock, but the below check is needed if one tries + * to call it from other places. + */ + if (core->dev) { + pm_runtime_get_noresume(core->dev); + if (!pm_runtime_active(core->dev)) { + ret = false; + goto done; + } + } + + ret = core->ops->is_enabled(core->hw); +done: + clk_pm_runtime_put(core); + + return ret; } /*** helper functions ***/ @@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core) if (core->ops->unprepare) core->ops->unprepare(core->hw); + clk_pm_runtime_put(core); + trace_clk_unprepare_complete(core); clk_core_unprepare(core->parent); } @@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core) return 0; if (core->prepare_count == 0) { - ret = clk_core_prepare(core->parent); + ret = clk_pm_runtime_get(core); if (ret) return ret; + ret = clk_core_prepare(core->parent); + if (ret) + goto runtime_put; + trace_clk_prepare(core); if (core->ops->prepare) @@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core) trace_clk_prepare_complete(core); - if (ret) { - clk_core_unprepare(core->parent); - return ret; - } + if (ret) + goto unprepare; } core->prepare_count++; return 0; +unprepare: + clk_core_unprepare(core->parent); +runtime_put: + clk_pm_runtime_put(core); + return ret; } static int clk_core_prepare_lock(struct clk_core *core) @@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) if (core->flags & CLK_IGNORE_UNUSED) return; + if (clk_pm_runtime_get(core)) + return; + if (clk_core_is_prepared(core)) { trace_clk_unprepare(core); if (core->ops->unprepare_unused) @@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) core->ops->unprepare(core->hw); trace_clk_unprepare_complete(core); } + + clk_pm_runtime_put(core); } static void clk_disable_unused_subtree(struct clk_core *core) @@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core) if (core->flags & CLK_OPS_PARENT_ENABLE) clk_core_prepare_enable(core->parent); + if (clk_pm_runtime_get(core)) + goto unprepare_out; + flags = clk_enable_lock(); if (core->enable_count) @@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core) unlock_out: clk_enable_unlock(flags); + clk_pm_runtime_put(core); +unprepare_out: if (core->flags & CLK_OPS_PARENT_ENABLE) clk_core_disable_unprepare(core->parent); } @@ -1038,9 +1110,13 @@ EXPORT_SYMBOL_GPL(clk_get_accuracy); static unsigned long clk_recalc(struct clk_core *core, unsigned long parent_rate) { - if (core->ops->recalc_rate) - return core->ops->recalc_rate(core->hw, parent_rate); - return parent_rate; + unsigned long rate = parent_rate; + + if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) { + rate = core->ops->recalc_rate(core->hw, parent_rate); + clk_pm_runtime_put(core); + } + return rate; } /** @@ -1565,6 +1641,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core, { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; + int ret = 0; if (!core) return 0; @@ -1581,21 +1658,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core, if (!top) return -EINVAL; + ret = clk_pm_runtime_get(core); + if (ret) + return ret; + /* notify that we are about to change rates */ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); if (fail_clk) { pr_debug("%s: failed to set %s rate\n", __func__, fail_clk->name); clk_propagate_rate_change(top, ABORT_RATE_CHANGE); - return -EBUSY; + ret = -EBUSY; + goto err; } /* change the rates */ clk_change_rate(top); core->req_rate = req_rate; +err: + clk_pm_runtime_put(core); - return 0; + return ret; } /** @@ -1826,12 +1910,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) p_rate = parent->rate; } + ret = clk_pm_runtime_get(core); + if (ret) + goto out; + /* propagate PRE_RATE_CHANGE notifications */ ret = __clk_speculate_rates(core, p_rate); /* abort if a driver objects */ if (ret & NOTIFY_STOP_MASK) - goto out; + goto runtime_put; /* do the re-parent */ ret = __clk_set_parent(core, parent, p_index); @@ -1844,6 +1932,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) __clk_recalc_accuracies(core); } +runtime_put: + clk_pm_runtime_put(core); out: clk_prepare_unlock(); @@ -2350,7 +2440,7 @@ static inline void clk_debug_unregister(struct clk_core *core) */ static int __clk_core_init(struct clk_core *core) { - int i, ret = 0; + int i, ret; struct clk_core *orphan; struct hlist_node *tmp2; unsigned long rate; @@ -2360,6 +2450,10 @@ static int __clk_core_init(struct clk_core *core) clk_prepare_lock(); + ret = clk_pm_runtime_get(core); + if (ret) + goto unlock; + /* check to see if a clock with this name is already registered */ if (clk_core_lookup(core->name)) { pr_debug("%s: clk %s already initialized\n", @@ -2512,6 +2606,8 @@ static int __clk_core_init(struct clk_core *core) kref_init(&core->ref); out: + clk_pm_runtime_put(core); +unlock: clk_prepare_unlock(); if (!ret) @@ -2583,6 +2679,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) goto fail_name; } core->ops = hw->init->ops; + if (dev && pm_runtime_enabled(dev)) + core->dev = dev; if (dev && dev->driver) core->owner = dev->driver->owner; core->hw = hw; @@ -3177,6 +3275,37 @@ int of_clk_add_hw_provider(struct device_node *np, } EXPORT_SYMBOL_GPL(of_clk_add_hw_provider); +static void devm_of_clk_release_provider(struct device *dev, void *res) +{ + of_clk_del_provider(*(struct device_node **)res); +} + +int devm_of_clk_add_hw_provider(struct device *dev, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + struct device_node **ptr, *np; + int ret; + + ptr = devres_alloc(devm_of_clk_release_provider, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + np = dev->of_node; + ret = of_clk_add_hw_provider(np, get, data); + if (!ret) { + *ptr = np; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider); + /** * of_clk_del_provider() - Remove a previously registered clock provider * @np: Device node pointer associated with clock provider @@ -3198,6 +3327,27 @@ void of_clk_del_provider(struct device_node *np) } EXPORT_SYMBOL_GPL(of_clk_del_provider); +static int devm_clk_provider_match(struct device *dev, void *res, void *data) +{ + struct device_node **np = res; + + if (WARN_ON(!np || !*np)) + return 0; + + return *np == data; +} + +void devm_of_clk_del_provider(struct device *dev) +{ + int ret; + + ret = devres_release(dev, devm_of_clk_release_provider, + devm_clk_provider_match, dev->of_node); + + WARN_ON(ret); +} +EXPORT_SYMBOL(devm_of_clk_del_provider); + static struct clk_hw * __of_clk_get_hw_from_provider(struct of_clk_provider *provider, struct of_phandle_args *clkspec) diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index fa0fba653898..77072c7778b9 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -415,7 +415,7 @@ static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate, return mmc_clk_set_timing(hw, rate); } -static struct clk_ops clk_mmc_ops = { +static const struct clk_ops clk_mmc_ops = { .prepare = mmc_clk_prepare, .determine_rate = mmc_clk_determine_rate, .set_rate = mmc_clk_set_rate, diff --git a/drivers/clk/hisilicon/clk-hi3660.c b/drivers/clk/hisilicon/clk-hi3660.c index a18258eb89cb..f40419959656 100644 --- a/drivers/clk/hisilicon/clk-hi3660.c +++ b/drivers/clk/hisilicon/clk-hi3660.c @@ -34,7 +34,7 @@ static const struct hisi_fixed_rate_clock hi3660_fixed_rate_clks[] = { /* crgctrl */ static const struct hisi_fixed_factor_clock hi3660_crg_fixed_factor_clks[] = { - { HI3660_FACTOR_UART3, "clk_factor_uart3", "iomcu_peri0", 1, 8, 0, }, + { HI3660_FACTOR_UART3, "clk_factor_uart3", "iomcu_peri0", 1, 16, 0, }, { HI3660_CLK_FACTOR_MMC, "clk_factor_mmc", "clkin_sys", 1, 6, 0, }, { HI3660_CLK_GATE_I2C0, "clk_gate_i2c0", "clk_i2c0_iomcu", 1, 4, 0, }, { HI3660_CLK_GATE_I2C1, "clk_gate_i2c1", "clk_i2c1_iomcu", 1, 4, 0, }, diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index e786d717f75d..a87809d4bd52 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -145,7 +145,7 @@ static struct hisi_gate_clock hi6220_separated_gate_clks_sys[] __initdata = { { HI6220_BBPPLL_SEL, "bbppll_sel", "pll0_bbp_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 9, 0, }, { HI6220_MEDIA_PLL_SRC, "media_pll_src", "pll_media_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 10, 0, }, { HI6220_MMC2_SEL, "mmc2_sel", "mmc2_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 11, 0, }, - { HI6220_CS_ATB_SYSPLL, "cs_atb_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 12, 0, }, + { HI6220_CS_ATB_SYSPLL, "cs_atb_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IS_CRITICAL, 0x270, 12, 0, }, }; static struct hisi_mux_clock hi6220_mux_clks_sys[] __initdata = { diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c index 14b05efa3c2a..9584f0c32dda 100644 --- a/drivers/clk/hisilicon/clk-hix5hd2.c +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -208,7 +208,7 @@ static void clk_ether_unprepare(struct clk_hw *hw) writel_relaxed(val, clk->ctrl_reg); } -static struct clk_ops clk_ether_ops = { +static const struct clk_ops clk_ether_ops = { .prepare = clk_ether_prepare, .unprepare = clk_ether_unprepare, }; @@ -247,7 +247,7 @@ static void clk_complex_disable(struct clk_hw *hw) writel_relaxed(val, clk->phy_reg); } -static struct clk_ops clk_complex_ops = { +static const struct clk_ops clk_complex_ops = { .enable = clk_complex_enable, .disable = clk_complex_disable, }; diff --git a/drivers/clk/hisilicon/clkgate-separated.c b/drivers/clk/hisilicon/clkgate-separated.c index 7908bc3c9ec7..f36bdef91831 100644 --- a/drivers/clk/hisilicon/clkgate-separated.c +++ b/drivers/clk/hisilicon/clkgate-separated.c @@ -88,7 +88,7 @@ static int clkgate_separated_is_enabled(struct clk_hw *hw) return reg ? 1 : 0; } -static struct clk_ops clkgate_separated_ops = { +static const struct clk_ops clkgate_separated_ops = { .enable = clkgate_separated_enable, .disable = clkgate_separated_disable, .is_enabled = clkgate_separated_is_enabled, @@ -105,10 +105,8 @@ struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name, struct clk_init_data init; sclk = kzalloc(sizeof(*sclk), GFP_KERNEL); - if (!sclk) { - pr_err("%s: fail to allocate separated gated clk\n", __func__); + if (!sclk) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clkgate_separated_ops; diff --git a/drivers/clk/hisilicon/crg-hi3798cv200.c b/drivers/clk/hisilicon/crg-hi3798cv200.c index ed8bb5f7507f..8478948e858e 100644 --- a/drivers/clk/hisilicon/crg-hi3798cv200.c +++ b/drivers/clk/hisilicon/crg-hi3798cv200.c @@ -47,6 +47,8 @@ #define HI3798CV200_FIXED_12M 81 #define HI3798CV200_FIXED_48M 82 #define HI3798CV200_FIXED_60M 83 +#define HI3798CV200_FIXED_166P5M 84 +#define HI3798CV200_SDIO0_MUX 85 #define HI3798CV200_CRG_NR_CLKS 128 @@ -63,6 +65,7 @@ static const struct hisi_fixed_rate_clock hi3798cv200_fixed_rate_clks[] = { { HI3798CV200_FIXED_75M, "75m", NULL, 0, 75000000, }, { HI3798CV200_FIXED_100M, "100m", NULL, 0, 100000000, }, { HI3798CV200_FIXED_150M, "150m", NULL, 0, 150000000, }, + { HI3798CV200_FIXED_166P5M, "166p5m", NULL, 0, 165000000, }, { HI3798CV200_FIXED_200M, "200m", NULL, 0, 200000000, }, { HI3798CV200_FIXED_250M, "250m", NULL, 0, 250000000, }, }; @@ -75,12 +78,19 @@ static const char *const comphy1_mux_p[] = { "100m", "25m"}; static u32 comphy1_mux_table[] = {2, 3}; +static const char *const sdio_mux_p[] = { + "100m", "50m", "150m", "166p5m" }; +static u32 sdio_mux_table[] = {0, 1, 2, 3}; + static struct hisi_mux_clock hi3798cv200_mux_clks[] = { { HI3798CV200_MMC_MUX, "mmc_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), CLK_SET_RATE_PARENT, 0xa0, 8, 3, 0, mmc_mux_table, }, { HI3798CV200_COMBPHY1_MUX, "combphy1_mux", comphy1_mux_p, ARRAY_SIZE(comphy1_mux_p), CLK_SET_RATE_PARENT, 0x188, 10, 2, 0, comphy1_mux_table, }, + { HI3798CV200_SDIO0_MUX, "sdio0_mux", sdio_mux_p, + ARRAY_SIZE(sdio_mux_p), CLK_SET_RATE_PARENT, + 0x9c, 8, 2, 0, sdio_mux_table, }, }; static const struct hisi_gate_clock hi3798cv200_gate_clks[] = { @@ -104,7 +114,7 @@ static const struct hisi_gate_clock hi3798cv200_gate_clks[] = { /* SDIO */ { HISTB_SDIO0_BIU_CLK, "clk_sdio0_biu", "200m", CLK_SET_RATE_PARENT, 0x9c, 0, 0, }, - { HISTB_SDIO0_CIU_CLK, "clk_sdio0_ciu", "mmc_mux", + { HISTB_SDIO0_CIU_CLK, "clk_sdio0_ciu", "sdio0_mux", CLK_SET_RATE_PARENT, 0x9c, 1, 0, }, /* EMMC */ { HISTB_MMC_BIU_CLK, "clk_mmc_biu", "200m", diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c index 5cc99590f9a3..6df3389687bc 100644 --- a/drivers/clk/imx/clk-busy.c +++ b/drivers/clk/imx/clk-busy.c @@ -72,7 +72,7 @@ static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate, return ret; } -static struct clk_ops clk_busy_divider_ops = { +static const struct clk_ops clk_busy_divider_ops = { .recalc_rate = clk_busy_divider_recalc_rate, .round_rate = clk_busy_divider_round_rate, .set_rate = clk_busy_divider_set_rate, @@ -147,7 +147,7 @@ static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index) return ret; } -static struct clk_ops clk_busy_mux_ops = { +static const struct clk_ops clk_busy_mux_ops = { .get_parent = clk_busy_mux_get_parent, .set_parent = clk_busy_mux_set_parent, }; diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index db44a198a0d9..60fc9d7a9723 100644 --- a/drivers/clk/imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -118,7 +118,7 @@ static void clk_gate2_disable_unused(struct clk_hw *hw) spin_unlock_irqrestore(gate->lock, flags); } -static struct clk_ops clk_gate2_ops = { +static const struct clk_ops clk_gate2_ops = { .enable = clk_gate2_enable, .disable = clk_gate2_disable, .disable_unused = clk_gate2_disable_unused, diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index c07df719b8a3..8d518ad5dc13 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -761,7 +761,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); clk[IMX6QDL_CLK_GPU3D_CORE] = imx_clk_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26); clk[IMX6QDL_CLK_HDMI_IAHB] = imx_clk_gate2("hdmi_iahb", "ahb", base + 0x70, 0); - clk[IMX6QDL_CLK_HDMI_ISFR] = imx_clk_gate2("hdmi_isfr", "video_27m", base + 0x70, 4); + clk[IMX6QDL_CLK_HDMI_ISFR] = imx_clk_gate2("hdmi_isfr", "mipi_core_cfg", base + 0x70, 4); clk[IMX6QDL_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6); clk[IMX6QDL_CLK_I2C2] = imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8); clk[IMX6QDL_CLK_I2C3] = imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10); diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index 5e8c18afce9a..85c118164469 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -267,7 +267,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6ULL_CLK_EPDC_SEL] = imx_clk_mux("epdc_sel", base + 0x34, 9, 3, epdc_sels, ARRAY_SIZE(epdc_sels)); } clks[IMX6UL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); - clks[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_mux("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels)); + clks[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_mux_flags("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels), CLK_SET_RATE_PARENT); clks[IMX6UL_CLK_LCDIF_SEL] = imx_clk_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels)); clks[IMX6UL_CLK_LDB_DI0_DIV_SEL] = imx_clk_mux("ldb_di0", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels)); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 2305699db467..80dc211eb74b 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -54,11 +54,6 @@ static const char *arm_m4_sel[] = { "osc", "pll_sys_main_240m_clk", "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", }; -static const char *arm_m0_sel[] = { "osc", "pll_sys_main_120m_clk", - "pll_enet_125m_clk", "pll_sys_pfd2_135m_clk", - "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk", - "pll_usb_main_clk", }; - static const char *axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk", "pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd5_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_sys_pfd7_clk", }; @@ -510,7 +505,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_SRC] = imx_clk_mux2("arm_a7_src", base + 0x8000, 24, 3, arm_a7_sel, ARRAY_SIZE(arm_a7_sel)); clks[IMX7D_ARM_M4_ROOT_SRC] = imx_clk_mux2("arm_m4_src", base + 0x8080, 24, 3, arm_m4_sel, ARRAY_SIZE(arm_m4_sel)); - clks[IMX7D_ARM_M0_ROOT_SRC] = imx_clk_mux2("arm_m0_src", base + 0x8100, 24, 3, arm_m0_sel, ARRAY_SIZE(arm_m0_sel)); clks[IMX7D_MAIN_AXI_ROOT_SRC] = imx_clk_mux2("axi_src", base + 0x8800, 24, 3, axi_sel, ARRAY_SIZE(axi_sel)); clks[IMX7D_DISP_AXI_ROOT_SRC] = imx_clk_mux2("disp_axi_src", base + 0x8880, 24, 3, disp_axi_sel, ARRAY_SIZE(disp_axi_sel)); clks[IMX7D_ENET_AXI_ROOT_SRC] = imx_clk_mux2("enet_axi_src", base + 0x8900, 24, 3, enet_axi_sel, ARRAY_SIZE(enet_axi_sel)); @@ -582,7 +576,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_CG] = imx_clk_gate3("arm_a7_cg", "arm_a7_src", base + 0x8000, 28); clks[IMX7D_ARM_M4_ROOT_CG] = imx_clk_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28); - clks[IMX7D_ARM_M0_ROOT_CG] = imx_clk_gate3("arm_m0_cg", "arm_m0_src", base + 0x8100, 28); clks[IMX7D_MAIN_AXI_ROOT_CG] = imx_clk_gate3("axi_cg", "axi_src", base + 0x8800, 28); clks[IMX7D_DISP_AXI_ROOT_CG] = imx_clk_gate3("disp_axi_cg", "disp_axi_src", base + 0x8880, 28); clks[IMX7D_ENET_AXI_ROOT_CG] = imx_clk_gate3("enet_axi_cg", "enet_axi_src", base + 0x8900, 28); @@ -721,7 +714,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_DIV] = imx_clk_divider2("arm_a7_div", "arm_a7_cg", base + 0x8000, 0, 3); clks[IMX7D_ARM_M4_ROOT_DIV] = imx_clk_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3); - clks[IMX7D_ARM_M0_ROOT_DIV] = imx_clk_divider2("arm_m0_div", "arm_m0_cg", base + 0x8100, 0, 3); clks[IMX7D_MAIN_AXI_ROOT_DIV] = imx_clk_divider2("axi_post_div", "axi_pre_div", base + 0x8800, 0, 6); clks[IMX7D_DISP_AXI_ROOT_DIV] = imx_clk_divider2("disp_axi_post_div", "disp_axi_pre_div", base + 0x8880, 0, 6); clks[IMX7D_ENET_AXI_ROOT_DIV] = imx_clk_divider2("enet_axi_post_div", "enet_axi_pre_div", base + 0x8900, 0, 6); @@ -793,11 +785,10 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_CLK] = imx_clk_gate4("arm_a7_root_clk", "arm_a7_div", base + 0x4000, 0); clks[IMX7D_ARM_M4_ROOT_CLK] = imx_clk_gate4("arm_m4_root_clk", "arm_m4_div", base + 0x4010, 0); - clks[IMX7D_ARM_M0_ROOT_CLK] = imx_clk_gate4("arm_m0_root_clk", "arm_m0_div", base + 0x4020, 0); clks[IMX7D_MAIN_AXI_ROOT_CLK] = imx_clk_gate4("main_axi_root_clk", "axi_post_div", base + 0x4040, 0); clks[IMX7D_DISP_AXI_ROOT_CLK] = imx_clk_gate4("disp_axi_root_clk", "disp_axi_post_div", base + 0x4050, 0); clks[IMX7D_ENET_AXI_ROOT_CLK] = imx_clk_gate4("enet_axi_root_clk", "enet_axi_post_div", base + 0x4060, 0); - clks[IMX7D_OCRAM_CLK] = imx_clk_gate4("ocram_clk", "axi_post_div", base + 0x4110, 0); + clks[IMX7D_OCRAM_CLK] = imx_clk_gate4("ocram_clk", "main_axi_root_clk", base + 0x4110, 0); clks[IMX7D_OCRAM_S_CLK] = imx_clk_gate4("ocram_s_clk", "ahb_root_clk", base + 0x4120, 0); clks[IMX7D_DRAM_ROOT_CLK] = imx_clk_gate4("dram_root_clk", "dram_post_div", base + 0x4130, 0); clks[IMX7D_DRAM_PHYM_ROOT_CLK] = imx_clk_gate4("dram_phym_root_clk", "dram_phym_cg", base + 0x4130, 0); diff --git a/drivers/clk/imx/clk-pllv1.c b/drivers/clk/imx/clk-pllv1.c index e47a1c2fe8bd..4ba9973d4c18 100644 --- a/drivers/clk/imx/clk-pllv1.c +++ b/drivers/clk/imx/clk-pllv1.c @@ -107,7 +107,7 @@ static unsigned long clk_pllv1_recalc_rate(struct clk_hw *hw, return ull; } -static struct clk_ops clk_pllv1_ops = { +static const struct clk_ops clk_pllv1_ops = { .recalc_rate = clk_pllv1_recalc_rate, }; diff --git a/drivers/clk/imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c index 9842d657e974..85b5cbe9744c 100644 --- a/drivers/clk/imx/clk-pllv2.c +++ b/drivers/clk/imx/clk-pllv2.c @@ -227,7 +227,7 @@ static void clk_pllv2_unprepare(struct clk_hw *hw) __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); } -static struct clk_ops clk_pllv2_ops = { +static const struct clk_ops clk_pllv2_ops = { .prepare = clk_pllv2_prepare, .unprepare = clk_pllv2_unprepare, .recalc_rate = clk_pllv2_recalc_rate, diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index 28739a9a6e37..59dc0aad553c 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -50,6 +50,56 @@ config COMMON_CLK_MT2701_BDPSYS ---help--- This driver supports Mediatek MT2701 bdpsys clocks. +config COMMON_CLK_MT2712 + bool "Clock driver for Mediatek MT2712" + depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST + select COMMON_CLK_MEDIATEK + default ARCH_MEDIATEK && ARM64 + ---help--- + This driver supports Mediatek MT2712 basic clocks. + +config COMMON_CLK_MT2712_BDPSYS + bool "Clock driver for Mediatek MT2712 bdpsys" + depends on COMMON_CLK_MT2712 + ---help--- + This driver supports Mediatek MT2712 bdpsys clocks. + +config COMMON_CLK_MT2712_IMGSYS + bool "Clock driver for Mediatek MT2712 imgsys" + depends on COMMON_CLK_MT2712 + ---help--- + This driver supports Mediatek MT2712 imgsys clocks. + +config COMMON_CLK_MT2712_JPGDECSYS + bool "Clock driver for Mediatek MT2712 jpgdecsys" + depends on COMMON_CLK_MT2712 + ---help--- + This driver supports Mediatek MT2712 jpgdecsys clocks. + +config COMMON_CLK_MT2712_MFGCFG + bool "Clock driver for Mediatek MT2712 mfgcfg" + depends on COMMON_CLK_MT2712 + ---help--- + This driver supports Mediatek MT2712 mfgcfg clocks. + +config COMMON_CLK_MT2712_MMSYS + bool "Clock driver for Mediatek MT2712 mmsys" + depends on COMMON_CLK_MT2712 + ---help--- + This driver supports Mediatek MT2712 mmsys clocks. + +config COMMON_CLK_MT2712_VDECSYS + bool "Clock driver for Mediatek MT2712 vdecsys" + depends on COMMON_CLK_MT2712 + ---help--- + This driver supports Mediatek MT2712 vdecsys clocks. + +config COMMON_CLK_MT2712_VENCSYS + bool "Clock driver for Mediatek MT2712 vencsys" + depends on COMMON_CLK_MT2712 + ---help--- + This driver supports Mediatek MT2712 vencsys clocks. + config COMMON_CLK_MT6797 bool "Clock driver for Mediatek MT6797" depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST @@ -82,6 +132,36 @@ config COMMON_CLK_MT6797_VENCSYS ---help--- This driver supports Mediatek MT6797 vencsys clocks. +config COMMON_CLK_MT7622 + bool "Clock driver for MediaTek MT7622" + depends on ARCH_MEDIATEK || COMPILE_TEST + select COMMON_CLK_MEDIATEK + default ARCH_MEDIATEK + ---help--- + This driver supports MediaTek MT7622 basic clocks and clocks + required for various periperals found on MediaTek. + +config COMMON_CLK_MT7622_ETHSYS + bool "Clock driver for MediaTek MT7622 ETHSYS" + depends on COMMON_CLK_MT7622 + ---help--- + This driver add support for clocks for Ethernet and SGMII + required on MediaTek MT7622 SoC. + +config COMMON_CLK_MT7622_HIFSYS + bool "Clock driver for MediaTek MT7622 HIFSYS" + depends on COMMON_CLK_MT7622 + ---help--- + This driver supports MediaTek MT7622 HIFSYS clocks providing + to PCI-E and USB. + +config COMMON_CLK_MT7622_AUDSYS + bool "Clock driver for MediaTek MT7622 AUDSYS" + depends on COMMON_CLK_MT7622 + ---help--- + This driver supports MediaTek MT7622 AUDSYS clocks providing + to audio consumers such as I2S and TDM. + config COMMON_CLK_MT8135 bool "Clock driver for Mediatek MT8135" depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index ba2a070765f0..c421ffcd49ff 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -13,5 +13,17 @@ obj-$(CONFIG_COMMON_CLK_MT2701_HIFSYS) += clk-mt2701-hif.o obj-$(CONFIG_COMMON_CLK_MT2701_IMGSYS) += clk-mt2701-img.o obj-$(CONFIG_COMMON_CLK_MT2701_MMSYS) += clk-mt2701-mm.o obj-$(CONFIG_COMMON_CLK_MT2701_VDECSYS) += clk-mt2701-vdec.o +obj-$(CONFIG_COMMON_CLK_MT2712) += clk-mt2712.o +obj-$(CONFIG_COMMON_CLK_MT2712_BDPSYS) += clk-mt2712-bdp.o +obj-$(CONFIG_COMMON_CLK_MT2712_IMGSYS) += clk-mt2712-img.o +obj-$(CONFIG_COMMON_CLK_MT2712_JPGDECSYS) += clk-mt2712-jpgdec.o +obj-$(CONFIG_COMMON_CLK_MT2712_MFGCFG) += clk-mt2712-mfg.o +obj-$(CONFIG_COMMON_CLK_MT2712_MMSYS) += clk-mt2712-mm.o +obj-$(CONFIG_COMMON_CLK_MT2712_VDECSYS) += clk-mt2712-vdec.o +obj-$(CONFIG_COMMON_CLK_MT2712_VENCSYS) += clk-mt2712-venc.o +obj-$(CONFIG_COMMON_CLK_MT7622) += clk-mt7622.o +obj-$(CONFIG_COMMON_CLK_MT7622_ETHSYS) += clk-mt7622-eth.o +obj-$(CONFIG_COMMON_CLK_MT7622_HIFSYS) += clk-mt7622-hif.o +obj-$(CONFIG_COMMON_CLK_MT7622_AUDSYS) += clk-mt7622-aud.o obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c index 9598889f972b..8e7f16fd87c9 100644 --- a/drivers/clk/mediatek/clk-mt2701.c +++ b/drivers/clk/mediatek/clk-mt2701.c @@ -750,7 +750,7 @@ static const struct mtk_fixed_factor infra_fixed_divs[] = { static struct clk_onecell_data *infra_clk_data; -static void mtk_infrasys_init_early(struct device_node *node) +static void __init mtk_infrasys_init_early(struct device_node *node) { int r, i; diff --git a/drivers/clk/mediatek/clk-mt2712-bdp.c b/drivers/clk/mediatek/clk-mt2712-bdp.c new file mode 100644 index 000000000000..5fe4728c076e --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712-bdp.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs bdp_cg_regs = { + .set_ofs = 0x100, + .clr_ofs = 0x100, + .sta_ofs = 0x100, +}; + +#define GATE_BDP(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &bdp_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +static const struct mtk_gate bdp_clks[] = { + GATE_BDP(CLK_BDP_BRIDGE_B, "bdp_bridge_b", "mm_sel", 0), + GATE_BDP(CLK_BDP_BRIDGE_DRAM, "bdp_bridge_d", "mm_sel", 1), + GATE_BDP(CLK_BDP_LARB_DRAM, "bdp_larb_d", "mm_sel", 2), + GATE_BDP(CLK_BDP_WR_CHANNEL_VDI_PXL, "bdp_vdi_pxl", "tvd_sel", 3), + GATE_BDP(CLK_BDP_WR_CHANNEL_VDI_DRAM, "bdp_vdi_d", "mm_sel", 4), + GATE_BDP(CLK_BDP_WR_CHANNEL_VDI_B, "bdp_vdi_b", "mm_sel", 5), + GATE_BDP(CLK_BDP_MT_B, "bdp_fmt_b", "mm_sel", 9), + GATE_BDP(CLK_BDP_DISPFMT_27M, "bdp_27m", "di_sel", 10), + GATE_BDP(CLK_BDP_DISPFMT_27M_VDOUT, "bdp_27m_vdout", "di_sel", 11), + GATE_BDP(CLK_BDP_DISPFMT_27_74_74, "bdp_27_74_74", "di_sel", 12), + GATE_BDP(CLK_BDP_DISPFMT_2FS, "bdp_2fs", "di_sel", 13), + GATE_BDP(CLK_BDP_DISPFMT_2FS_2FS74_148, "bdp_2fs74_148", "di_sel", 14), + GATE_BDP(CLK_BDP_DISPFMT_B, "bdp_b", "mm_sel", 15), + GATE_BDP(CLK_BDP_VDO_DRAM, "bdp_vdo_d", "mm_sel", 16), + GATE_BDP(CLK_BDP_VDO_2FS, "bdp_vdo_2fs", "di_sel", 17), + GATE_BDP(CLK_BDP_VDO_B, "bdp_vdo_b", "mm_sel", 18), + GATE_BDP(CLK_BDP_WR_CHANNEL_DI_PXL, "bdp_di_pxl", "di_sel", 19), + GATE_BDP(CLK_BDP_WR_CHANNEL_DI_DRAM, "bdp_di_d", "mm_sel", 20), + GATE_BDP(CLK_BDP_WR_CHANNEL_DI_B, "bdp_di_b", "mm_sel", 21), + GATE_BDP(CLK_BDP_NR_AGENT, "bdp_nr_agent", "nr_sel", 22), + GATE_BDP(CLK_BDP_NR_DRAM, "bdp_nr_d", "mm_sel", 23), + GATE_BDP(CLK_BDP_NR_B, "bdp_nr_b", "mm_sel", 24), + GATE_BDP(CLK_BDP_BRIDGE_RT_B, "bdp_bridge_rt_b", "mm_sel", 25), + GATE_BDP(CLK_BDP_BRIDGE_RT_DRAM, "bdp_bridge_rt_d", "mm_sel", 26), + GATE_BDP(CLK_BDP_LARB_RT_DRAM, "bdp_larb_rt_d", "mm_sel", 27), + GATE_BDP(CLK_BDP_TVD_TDC, "bdp_tvd_tdc", "mm_sel", 28), + GATE_BDP(CLK_BDP_TVD_54, "bdp_tvd_clk_54", "tvd_sel", 29), + GATE_BDP(CLK_BDP_TVD_CBUS, "bdp_tvd_cbus", "mm_sel", 30), +}; + +static int clk_mt2712_bdp_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_BDP_NR_CLK); + + mtk_clk_register_gates(node, bdp_clks, ARRAY_SIZE(bdp_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712_bdp[] = { + { .compatible = "mediatek,mt2712-bdpsys", }, + {} +}; + +static struct platform_driver clk_mt2712_bdp_drv = { + .probe = clk_mt2712_bdp_probe, + .driver = { + .name = "clk-mt2712-bdp", + .of_match_table = of_match_clk_mt2712_bdp, + }, +}; + +builtin_platform_driver(clk_mt2712_bdp_drv); diff --git a/drivers/clk/mediatek/clk-mt2712-img.c b/drivers/clk/mediatek/clk-mt2712-img.c new file mode 100644 index 000000000000..139ff55d495e --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712-img.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs img_cg_regs = { + .set_ofs = 0x0, + .clr_ofs = 0x0, + .sta_ofs = 0x0, +}; + +#define GATE_IMG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &img_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +static const struct mtk_gate img_clks[] = { + GATE_IMG(CLK_IMG_SMI_LARB2, "img_smi_larb2", "mm_sel", 0), + GATE_IMG(CLK_IMG_SENINF_SCAM_EN, "img_scam_en", "csi0", 3), + GATE_IMG(CLK_IMG_SENINF_CAM_EN, "img_cam_en", "mm_sel", 8), + GATE_IMG(CLK_IMG_CAM_SV_EN, "img_cam_sv_en", "mm_sel", 9), + GATE_IMG(CLK_IMG_CAM_SV1_EN, "img_cam_sv1_en", "mm_sel", 10), + GATE_IMG(CLK_IMG_CAM_SV2_EN, "img_cam_sv2_en", "mm_sel", 11), +}; + +static int clk_mt2712_img_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK); + + mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712_img[] = { + { .compatible = "mediatek,mt2712-imgsys", }, + {} +}; + +static struct platform_driver clk_mt2712_img_drv = { + .probe = clk_mt2712_img_probe, + .driver = { + .name = "clk-mt2712-img", + .of_match_table = of_match_clk_mt2712_img, + }, +}; + +builtin_platform_driver(clk_mt2712_img_drv); diff --git a/drivers/clk/mediatek/clk-mt2712-jpgdec.c b/drivers/clk/mediatek/clk-mt2712-jpgdec.c new file mode 100644 index 000000000000..c7d4aada4892 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712-jpgdec.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs jpgdec_cg_regs = { + .set_ofs = 0x4, + .clr_ofs = 0x8, + .sta_ofs = 0x0, +}; + +#define GATE_JPGDEC(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &jpgdec_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate jpgdec_clks[] = { + GATE_JPGDEC(CLK_JPGDEC_JPGDEC1, "jpgdec_jpgdec1", "jpgdec_sel", 0), + GATE_JPGDEC(CLK_JPGDEC_JPGDEC, "jpgdec_jpgdec", "jpgdec_sel", 4), +}; + +static int clk_mt2712_jpgdec_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_JPGDEC_NR_CLK); + + mtk_clk_register_gates(node, jpgdec_clks, ARRAY_SIZE(jpgdec_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712_jpgdec[] = { + { .compatible = "mediatek,mt2712-jpgdecsys", }, + {} +}; + +static struct platform_driver clk_mt2712_jpgdec_drv = { + .probe = clk_mt2712_jpgdec_probe, + .driver = { + .name = "clk-mt2712-jpgdec", + .of_match_table = of_match_clk_mt2712_jpgdec, + }, +}; + +builtin_platform_driver(clk_mt2712_jpgdec_drv); diff --git a/drivers/clk/mediatek/clk-mt2712-mfg.c b/drivers/clk/mediatek/clk-mt2712-mfg.c new file mode 100644 index 000000000000..570f72d48d4d --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712-mfg.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs mfg_cg_regs = { + .set_ofs = 0x4, + .clr_ofs = 0x8, + .sta_ofs = 0x0, +}; + +#define GATE_MFG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &mfg_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate mfg_clks[] = { + GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_sel", 0), +}; + +static int clk_mt2712_mfg_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_MFG_NR_CLK); + + mtk_clk_register_gates(node, mfg_clks, ARRAY_SIZE(mfg_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712_mfg[] = { + { .compatible = "mediatek,mt2712-mfgcfg", }, + {} +}; + +static struct platform_driver clk_mt2712_mfg_drv = { + .probe = clk_mt2712_mfg_probe, + .driver = { + .name = "clk-mt2712-mfg", + .of_match_table = of_match_clk_mt2712_mfg, + }, +}; + +builtin_platform_driver(clk_mt2712_mfg_drv); diff --git a/drivers/clk/mediatek/clk-mt2712-mm.c b/drivers/clk/mediatek/clk-mt2712-mm.c new file mode 100644 index 000000000000..a8b4b6d42488 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712-mm.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs mm0_cg_regs = { + .set_ofs = 0x104, + .clr_ofs = 0x108, + .sta_ofs = 0x100, +}; + +static const struct mtk_gate_regs mm1_cg_regs = { + .set_ofs = 0x114, + .clr_ofs = 0x118, + .sta_ofs = 0x110, +}; + +static const struct mtk_gate_regs mm2_cg_regs = { + .set_ofs = 0x224, + .clr_ofs = 0x228, + .sta_ofs = 0x220, +}; + +#define GATE_MM0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &mm0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_MM1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &mm1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_MM2(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &mm2_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate mm_clks[] = { + /* MM0 */ + GATE_MM0(CLK_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 0), + GATE_MM0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1), + GATE_MM0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 2), + GATE_MM0(CLK_MM_MDP_RDMA0, "mm_mdp_rdma0", "mm_sel", 3), + GATE_MM0(CLK_MM_MDP_RDMA1, "mm_mdp_rdma1", "mm_sel", 4), + GATE_MM0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 5), + GATE_MM0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 6), + GATE_MM0(CLK_MM_MDP_RSZ2, "mm_mdp_rsz2", "mm_sel", 7), + GATE_MM0(CLK_MM_MDP_TDSHP0, "mm_mdp_tdshp0", "mm_sel", 8), + GATE_MM0(CLK_MM_MDP_TDSHP1, "mm_mdp_tdshp1", "mm_sel", 9), + GATE_MM0(CLK_MM_MDP_CROP, "mm_mdp_crop", "mm_sel", 10), + GATE_MM0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11), + GATE_MM0(CLK_MM_MDP_WROT0, "mm_mdp_wrot0", "mm_sel", 12), + GATE_MM0(CLK_MM_MDP_WROT1, "mm_mdp_wrot1", "mm_sel", 13), + GATE_MM0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 14), + GATE_MM0(CLK_MM_MUTEX_32K, "mm_mutex_32k", "clk32k", 15), + GATE_MM0(CLK_MM_DISP_OVL0, "mm_disp_ovl0", "mm_sel", 16), + GATE_MM0(CLK_MM_DISP_OVL1, "mm_disp_ovl1", "mm_sel", 17), + GATE_MM0(CLK_MM_DISP_RDMA0, "mm_disp_rdma0", "mm_sel", 18), + GATE_MM0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 19), + GATE_MM0(CLK_MM_DISP_RDMA2, "mm_disp_rdma2", "mm_sel", 20), + GATE_MM0(CLK_MM_DISP_WDMA0, "mm_disp_wdma0", "mm_sel", 21), + GATE_MM0(CLK_MM_DISP_WDMA1, "mm_disp_wdma1", "mm_sel", 22), + GATE_MM0(CLK_MM_DISP_COLOR0, "mm_disp_color0", "mm_sel", 23), + GATE_MM0(CLK_MM_DISP_COLOR1, "mm_disp_color1", "mm_sel", 24), + GATE_MM0(CLK_MM_DISP_AAL, "mm_disp_aal", "mm_sel", 25), + GATE_MM0(CLK_MM_DISP_GAMMA, "mm_disp_gamma", "mm_sel", 26), + GATE_MM0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 27), + GATE_MM0(CLK_MM_DISP_SPLIT0, "mm_disp_split0", "mm_sel", 28), + GATE_MM0(CLK_MM_DISP_OD, "mm_disp_od", "mm_sel", 31), + /* MM1 */ + GATE_MM1(CLK_MM_DISP_PWM0_MM, "mm_pwm0_mm", "mm_sel", 0), + GATE_MM1(CLK_MM_DISP_PWM0_26M, "mm_pwm0_26m", "pwm_sel", 1), + GATE_MM1(CLK_MM_DISP_PWM1_MM, "mm_pwm1_mm", "mm_sel", 2), + GATE_MM1(CLK_MM_DISP_PWM1_26M, "mm_pwm1_26m", "pwm_sel", 3), + GATE_MM1(CLK_MM_DSI0_ENGINE, "mm_dsi0_engine", "mm_sel", 4), + GATE_MM1(CLK_MM_DSI0_DIGITAL, "mm_dsi0_digital", "dsi0_lntc", 5), + GATE_MM1(CLK_MM_DSI1_ENGINE, "mm_dsi1_engine", "mm_sel", 6), + GATE_MM1(CLK_MM_DSI1_DIGITAL, "mm_dsi1_digital", "dsi1_lntc", 7), + GATE_MM1(CLK_MM_DPI_PIXEL, "mm_dpi_pixel", "vpll_dpix", 8), + GATE_MM1(CLK_MM_DPI_ENGINE, "mm_dpi_engine", "mm_sel", 9), + GATE_MM1(CLK_MM_DPI1_PIXEL, "mm_dpi1_pixel", "vpll3_dpix", 10), + GATE_MM1(CLK_MM_DPI1_ENGINE, "mm_dpi1_engine", "mm_sel", 11), + GATE_MM1(CLK_MM_LVDS_PIXEL, "mm_lvds_pixel", "vpll_dpix", 16), + GATE_MM1(CLK_MM_LVDS_CTS, "mm_lvds_cts", "lvdstx", 17), + GATE_MM1(CLK_MM_SMI_LARB4, "mm_smi_larb4", "mm_sel", 18), + GATE_MM1(CLK_MM_SMI_COMMON1, "mm_smi_common1", "mm_sel", 21), + GATE_MM1(CLK_MM_SMI_LARB5, "mm_smi_larb5", "mm_sel", 22), + GATE_MM1(CLK_MM_MDP_RDMA2, "mm_mdp_rdma2", "mm_sel", 23), + GATE_MM1(CLK_MM_MDP_TDSHP2, "mm_mdp_tdshp2", "mm_sel", 24), + GATE_MM1(CLK_MM_DISP_OVL2, "mm_disp_ovl2", "mm_sel", 25), + GATE_MM1(CLK_MM_DISP_WDMA2, "mm_disp_wdma2", "mm_sel", 26), + GATE_MM1(CLK_MM_DISP_COLOR2, "mm_disp_color2", "mm_sel", 27), + GATE_MM1(CLK_MM_DISP_AAL1, "mm_disp_aal1", "mm_sel", 28), + GATE_MM1(CLK_MM_DISP_OD1, "mm_disp_od1", "mm_sel", 29), + GATE_MM1(CLK_MM_LVDS1_PIXEL, "mm_lvds1_pixel", "vpll3_dpix", 30), + GATE_MM1(CLK_MM_LVDS1_CTS, "mm_lvds1_cts", "lvdstx3", 31), + /* MM2 */ + GATE_MM2(CLK_MM_SMI_LARB7, "mm_smi_larb7", "mm_sel", 0), + GATE_MM2(CLK_MM_MDP_RDMA3, "mm_mdp_rdma3", "mm_sel", 1), + GATE_MM2(CLK_MM_MDP_WROT2, "mm_mdp_wrot2", "mm_sel", 2), + GATE_MM2(CLK_MM_DSI2, "mm_dsi2", "mm_sel", 3), + GATE_MM2(CLK_MM_DSI2_DIGITAL, "mm_dsi2_digital", "dsi0_lntc", 4), + GATE_MM2(CLK_MM_DSI3, "mm_dsi3", "mm_sel", 5), + GATE_MM2(CLK_MM_DSI3_DIGITAL, "mm_dsi3_digital", "dsi1_lntc", 6), +}; + +static int clk_mt2712_mm_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK); + + mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712_mm[] = { + { .compatible = "mediatek,mt2712-mmsys", }, + {} +}; + +static struct platform_driver clk_mt2712_mm_drv = { + .probe = clk_mt2712_mm_probe, + .driver = { + .name = "clk-mt2712-mm", + .of_match_table = of_match_clk_mt2712_mm, + }, +}; + +builtin_platform_driver(clk_mt2712_mm_drv); diff --git a/drivers/clk/mediatek/clk-mt2712-vdec.c b/drivers/clk/mediatek/clk-mt2712-vdec.c new file mode 100644 index 000000000000..55c64ee8cc91 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712-vdec.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs vdec0_cg_regs = { + .set_ofs = 0x0, + .clr_ofs = 0x4, + .sta_ofs = 0x0, +}; + +static const struct mtk_gate_regs vdec1_cg_regs = { + .set_ofs = 0x8, + .clr_ofs = 0xc, + .sta_ofs = 0x8, +}; + +#define GATE_VDEC0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &vdec0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +#define GATE_VDEC1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &vdec1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate vdec_clks[] = { + /* VDEC0 */ + GATE_VDEC0(CLK_VDEC_CKEN, "vdec_cken", "vdec_sel", 0), + /* VDEC1 */ + GATE_VDEC1(CLK_VDEC_LARB1_CKEN, "vdec_larb1_cken", "vdec_sel", 0), + GATE_VDEC1(CLK_VDEC_IMGRZ_CKEN, "vdec_imgrz_cken", "vdec_sel", 1), +}; + +static int clk_mt2712_vdec_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_VDEC_NR_CLK); + + mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712_vdec[] = { + { .compatible = "mediatek,mt2712-vdecsys", }, + {} +}; + +static struct platform_driver clk_mt2712_vdec_drv = { + .probe = clk_mt2712_vdec_probe, + .driver = { + .name = "clk-mt2712-vdec", + .of_match_table = of_match_clk_mt2712_vdec, + }, +}; + +builtin_platform_driver(clk_mt2712_vdec_drv); diff --git a/drivers/clk/mediatek/clk-mt2712-venc.c b/drivers/clk/mediatek/clk-mt2712-venc.c new file mode 100644 index 000000000000..ccbfe98777c8 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712-venc.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs venc_cg_regs = { + .set_ofs = 0x4, + .clr_ofs = 0x8, + .sta_ofs = 0x0, +}; + +#define GATE_VENC(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &venc_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate venc_clks[] = { + GATE_VENC(CLK_VENC_SMI_COMMON_CON, "venc_smi", "mm_sel", 0), + GATE_VENC(CLK_VENC_VENC, "venc_venc", "venc_sel", 4), + GATE_VENC(CLK_VENC_SMI_LARB6, "venc_smi_larb6", "jpgdec_sel", 12), +}; + +static int clk_mt2712_venc_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_VENC_NR_CLK); + + mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712_venc[] = { + { .compatible = "mediatek,mt2712-vencsys", }, + {} +}; + +static struct platform_driver clk_mt2712_venc_drv = { + .probe = clk_mt2712_venc_probe, + .driver = { + .name = "clk-mt2712-venc", + .of_match_table = of_match_clk_mt2712_venc, + }, +}; + +builtin_platform_driver(clk_mt2712_venc_drv); diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c new file mode 100644 index 000000000000..498d13799388 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2712.c @@ -0,0 +1,1435 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static DEFINE_SPINLOCK(mt2712_clk_lock); + +static const struct mtk_fixed_clk top_fixed_clks[] = { + FIXED_CLK(CLK_TOP_VPLL3_DPIX, "vpll3_dpix", NULL, 200000000), + FIXED_CLK(CLK_TOP_VPLL_DPIX, "vpll_dpix", NULL, 200000000), + FIXED_CLK(CLK_TOP_LTEPLL_FS26M, "ltepll_fs26m", NULL, 26000000), + FIXED_CLK(CLK_TOP_DMPLL, "dmpll_ck", NULL, 350000000), + FIXED_CLK(CLK_TOP_DSI0_LNTC, "dsi0_lntc", NULL, 143000000), + FIXED_CLK(CLK_TOP_DSI1_LNTC, "dsi1_lntc", NULL, 143000000), + FIXED_CLK(CLK_TOP_LVDSTX3_CLKDIG_CTS, "lvdstx3", NULL, 140000000), + FIXED_CLK(CLK_TOP_LVDSTX_CLKDIG_CTS, "lvdstx", NULL, 140000000), + FIXED_CLK(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", NULL, 32768), + FIXED_CLK(CLK_TOP_CLKRTC_INT, "clkrtc_int", NULL, 32747), + FIXED_CLK(CLK_TOP_CSI0, "csi0", NULL, 26000000), + FIXED_CLK(CLK_TOP_CVBSPLL, "cvbspll", NULL, 108000000), +}; + +static const struct mtk_fixed_factor top_early_divs[] = { + FACTOR(CLK_TOP_SYS_26M, "sys_26m", "clk26m", 1, + 1), + FACTOR(CLK_TOP_CLK26M_D2, "clk26m_d2", "sys_26m", 1, + 2), +}; + +static const struct mtk_fixed_factor top_divs[] = { + FACTOR(CLK_TOP_ARMCA35PLL, "armca35pll_ck", "armca35pll", 1, + 1), + FACTOR(CLK_TOP_ARMCA35PLL_600M, "armca35pll_600m", "armca35pll_ck", 1, + 2), + FACTOR(CLK_TOP_ARMCA35PLL_400M, "armca35pll_400m", "armca35pll_ck", 1, + 3), + FACTOR(CLK_TOP_ARMCA72PLL, "armca72pll_ck", "armca72pll", 1, + 1), + FACTOR(CLK_TOP_SYSPLL, "syspll_ck", "mainpll", 1, + 1), + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "syspll_ck", 1, + 2), + FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1, + 2), + FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1, + 4), + FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1, + 8), + FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1, + 16), + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "syspll_ck", 1, + 3), + FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1, + 2), + FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1, + 4), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "syspll_ck", 1, + 5), + FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1, + 2), + FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1, + 4), + FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "syspll_ck", 1, + 7), + FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1, + 2), + FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1, + 4), + FACTOR(CLK_TOP_UNIVPLL, "univpll_ck", "univpll", 1, + 1), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll_ck", 1, + 7), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll_ck", 1, + 26), + FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univpll_ck", 1, + 52), + FACTOR(CLK_TOP_UNIVPLL_D104, "univpll_d104", "univpll_ck", 1, + 104), + FACTOR(CLK_TOP_UNIVPLL_D208, "univpll_d208", "univpll_ck", 1, + 208), + FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll_ck", 1, + 2), + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1, + 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1, + 4), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_d2", 1, + 8), + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll_ck", 1, + 3), + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_d3", 1, + 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_d3", 1, + 4), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_d3", 1, + 8), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll_ck", 1, + 5), + FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1, + 2), + FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1, + 4), + FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univpll_d5", 1, + 8), + FACTOR(CLK_TOP_F_MP0_PLL1, "f_mp0_pll1_ck", "univpll_d2", 1, + 1), + FACTOR(CLK_TOP_F_MP0_PLL2, "f_mp0_pll2_ck", "univpll1_d2", 1, + 1), + FACTOR(CLK_TOP_F_BIG_PLL1, "f_big_pll1_ck", "univpll_d2", 1, + 1), + FACTOR(CLK_TOP_F_BIG_PLL2, "f_big_pll2_ck", "univpll1_d2", 1, + 1), + FACTOR(CLK_TOP_F_BUS_PLL1, "f_bus_pll1_ck", "univpll_d2", 1, + 1), + FACTOR(CLK_TOP_F_BUS_PLL2, "f_bus_pll2_ck", "univpll1_d2", 1, + 1), + FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, + 1), + FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1_ck", 1, + 2), + FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1_ck", 1, + 4), + FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1_ck", 1, + 8), + FACTOR(CLK_TOP_APLL1_D16, "apll1_d16", "apll1_ck", 1, + 16), + FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, + 1), + FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2_ck", 1, + 2), + FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2_ck", 1, + 4), + FACTOR(CLK_TOP_APLL2_D8, "apll2_d8", "apll2_ck", 1, + 8), + FACTOR(CLK_TOP_APLL2_D16, "apll2_d16", "apll2_ck", 1, + 16), + FACTOR(CLK_TOP_LVDSPLL, "lvdspll_ck", "lvdspll", 1, + 1), + FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll_ck", 1, + 2), + FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll_ck", 1, + 4), + FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll_ck", 1, + 8), + FACTOR(CLK_TOP_LVDSPLL2, "lvdspll2_ck", "lvdspll2", 1, + 1), + FACTOR(CLK_TOP_LVDSPLL2_D2, "lvdspll2_d2", "lvdspll2_ck", 1, + 2), + FACTOR(CLK_TOP_LVDSPLL2_D4, "lvdspll2_d4", "lvdspll2_ck", 1, + 4), + FACTOR(CLK_TOP_LVDSPLL2_D8, "lvdspll2_d8", "lvdspll2_ck", 1, + 8), + FACTOR(CLK_TOP_ETHERPLL_125M, "etherpll_125m", "etherpll", 1, + 1), + FACTOR(CLK_TOP_ETHERPLL_50M, "etherpll_50m", "etherpll", 1, + 1), + FACTOR(CLK_TOP_CVBS, "cvbs", "cvbspll", 1, + 1), + FACTOR(CLK_TOP_CVBS_D2, "cvbs_d2", "cvbs", 1, + 2), + FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, + 1), + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll_ck", 1, + 2), + FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, + 1), + FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll_ck", 1, + 2), + FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, + 1), + FACTOR(CLK_TOP_VCODECPLL_D2, "vcodecpll_d2", "vcodecpll_ck", 1, + 2), + FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll", 1, + 1), + FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_ck", 1, + 2), + FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_ck", 1, + 4), + FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_ck", 1, + 8), + FACTOR(CLK_TOP_TVDPLL_429M, "tvdpll_429m", "tvdpll", 1, + 1), + FACTOR(CLK_TOP_TVDPLL_429M_D2, "tvdpll_429m_d2", "tvdpll_429m", 1, + 2), + FACTOR(CLK_TOP_TVDPLL_429M_D4, "tvdpll_429m_d4", "tvdpll_429m", 1, + 4), + FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, + 1), + FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll_ck", 1, + 2), + FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll_ck", 1, + 4), + FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, + 1), + FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2_ck", 1, + 2), + FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2_ck", 1, + 4), + FACTOR(CLK_TOP_D2A_ULCLK_6P5M, "d2a_ulclk_6p5m", "clk26m", 1, + 4), +}; + +static const char * const axi_parents[] = { + "clk26m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "msdcpll2_ck" +}; + +static const char * const mem_parents[] = { + "clk26m", + "dmpll_ck" +}; + +static const char * const mm_parents[] = { + "clk26m", + "vencpll_ck", + "syspll_d3", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll1_d2", + "univpll2_d2" +}; + +static const char * const pwm_parents[] = { + "clk26m", + "univpll2_d4", + "univpll3_d2", + "univpll1_d4" +}; + +static const char * const vdec_parents[] = { + "clk26m", + "vcodecpll_ck", + "tvdpll_429m", + "univpll_d3", + "vencpll_ck", + "syspll_d3", + "univpll1_d2", + "mmpll_d2", + "syspll3_d2", + "tvdpll_ck" +}; + +static const char * const venc_parents[] = { + "clk26m", + "univpll1_d2", + "mmpll_d2", + "tvdpll_d2", + "syspll1_d2", + "univpll_d5", + "vcodecpll_d2", + "univpll2_d2", + "syspll3_d2" +}; + +static const char * const mfg_parents[] = { + "clk26m", + "mmpll_ck", + "univpll_d3", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "syspll_d3", + "syspll1_d2", + "syspll_d5", + "univpll_d3", + "univpll1_d2", + "univpll_d5", + "univpll2_d2" +}; + +static const char * const camtg_parents[] = { + "clk26m", + "univpll_d52", + "univpll_d208", + "univpll_d104", + "clk26m_d2", + "univpll_d26", + "univpll2_d8", + "syspll3_d4", + "syspll3_d2", + "univpll1_d4", + "univpll2_d2" +}; + +static const char * const uart_parents[] = { + "clk26m", + "univpll2_d8" +}; + +static const char * const spi_parents[] = { + "clk26m", + "univpll2_d4", + "univpll1_d4", + "univpll2_d2", + "univpll3_d2", + "univpll1_d8" +}; + +static const char * const usb20_parents[] = { + "clk26m", + "univpll1_d8", + "univpll3_d4" +}; + +static const char * const usb30_parents[] = { + "clk26m", + "univpll3_d2", + "univpll3_d4", + "univpll2_d4" +}; + +static const char * const msdc50_0_h_parents[] = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const msdc50_0_parents[] = { + "clk26m", + "msdcpll_ck", + "msdcpll_d2", + "univpll1_d4", + "syspll2_d2", + "msdcpll_d4", + "vencpll_d2", + "univpll1_d2", + "msdcpll2_ck", + "msdcpll2_d2", + "msdcpll2_d4" +}; + +static const char * const msdc30_1_parents[] = { + "clk26m", + "univpll2_d2", + "msdcpll_d2", + "univpll1_d4", + "syspll2_d2", + "univpll_d7", + "vencpll_d2" +}; + +static const char * const msdc30_3_parents[] = { + "clk26m", + "msdcpll2_ck", + "msdcpll2_d2", + "univpll2_d2", + "msdcpll2_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d2", + "msdcpll_ck", + "msdcpll_d2", + "msdcpll_d4" +}; + +static const char * const audio_parents[] = { + "clk26m", + "syspll3_d4", + "syspll4_d4", + "syspll1_d16" +}; + +static const char * const aud_intbus_parents[] = { + "clk26m", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d8", + "syspll3_d2", + "syspll3_d4" +}; + +static const char * const pmicspi_parents[] = { + "clk26m", + "syspll1_d8", + "syspll3_d4", + "syspll1_d16", + "univpll3_d4", + "univpll_d26", + "syspll3_d4" +}; + +static const char * const dpilvds1_parents[] = { + "clk26m", + "lvdspll2_ck", + "lvdspll2_d2", + "lvdspll2_d4", + "lvdspll2_d8", + "clkfpc" +}; + +static const char * const atb_parents[] = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "syspll_d5" +}; + +static const char * const nr_parents[] = { + "clk26m", + "univpll1_d4", + "syspll2_d2", + "syspll1_d4", + "univpll1_d8", + "univpll3_d2", + "univpll2_d2", + "syspll_d5" +}; + +static const char * const nfi2x_parents[] = { + "clk26m", + "syspll4_d4", + "univpll3_d4", + "univpll1_d8", + "syspll2_d4", + "univpll3_d2", + "syspll_d7", + "syspll2_d2", + "univpll2_d2", + "syspll_d5", + "syspll1_d2" +}; + +static const char * const irda_parents[] = { + "clk26m", + "univpll2_d4", + "syspll2_d4", + "univpll2_d8" +}; + +static const char * const cci400_parents[] = { + "clk26m", + "vencpll_ck", + "armca35pll_600m", + "armca35pll_400m", + "univpll_d2", + "syspll_d2", + "msdcpll_ck", + "univpll_d3" +}; + +static const char * const aud_1_parents[] = { + "clk26m", + "apll1_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const aud_2_parents[] = { + "clk26m", + "apll2_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const mem_mfg_parents[] = { + "clk26m", + "mmpll_ck", + "univpll_d3" +}; + +static const char * const axi_mfg_parents[] = { + "clk26m", + "axi_sel", + "univpll_d5" +}; + +static const char * const scam_parents[] = { + "clk26m", + "syspll3_d2", + "univpll2_d4", + "syspll2_d4" +}; + +static const char * const nfiecc_parents[] = { + "clk26m", + "nfi2x_sel", + "syspll_d7", + "syspll2_d2", + "univpll2_d2", + "univpll_d5", + "syspll1_d2" +}; + +static const char * const pe2_mac_p0_parents[] = { + "clk26m", + "syspll1_d8", + "syspll4_d2", + "syspll2_d4", + "univpll2_d4", + "syspll3_d2" +}; + +static const char * const dpilvds_parents[] = { + "clk26m", + "lvdspll_ck", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8", + "clkfpc" +}; + +static const char * const hdcp_parents[] = { + "clk26m", + "syspll4_d2", + "syspll3_d4", + "univpll2_d4" +}; + +static const char * const hdcp_24m_parents[] = { + "clk26m", + "univpll_d26", + "univpll_d52", + "univpll2_d8" +}; + +static const char * const rtc_parents[] = { + "clkrtc_int", + "clkrtc_ext", + "clk26m", + "univpll3_d8" +}; + +static const char * const spinor_parents[] = { + "clk26m", + "clk26m_d2", + "syspll4_d4", + "univpll2_d8", + "univpll3_d4", + "syspll4_d2", + "syspll2_d4", + "univpll2_d4", + "etherpll_125m", + "syspll1_d4" +}; + +static const char * const apll_parents[] = { + "clk26m", + "apll1_ck", + "apll1_d2", + "apll1_d4", + "apll1_d8", + "apll1_d16", + "apll2_ck", + "apll2_d2", + "apll2_d4", + "apll2_d8", + "apll2_d16", + "clk26m", + "clk26m" +}; + +static const char * const a1sys_hp_parents[] = { + "clk26m", + "apll1_ck", + "apll1_d2", + "apll1_d4", + "apll1_d8" +}; + +static const char * const a2sys_hp_parents[] = { + "clk26m", + "apll2_ck", + "apll2_d2", + "apll2_d4", + "apll2_d8" +}; + +static const char * const asm_l_parents[] = { + "clk26m", + "univpll2_d4", + "univpll2_d2", + "syspll_d5" +}; + +static const char * const i2so1_parents[] = { + "clk26m", + "apll1_ck", + "apll2_ck" +}; + +static const char * const ether_125m_parents[] = { + "clk26m", + "etherpll_125m", + "univpll3_d2" +}; + +static const char * const ether_50m_parents[] = { + "clk26m", + "etherpll_50m", + "univpll_d26", + "univpll3_d4" +}; + +static const char * const jpgdec_parents[] = { + "clk26m", + "univpll_d3", + "tvdpll_429m", + "vencpll_ck", + "syspll_d3", + "vcodecpll_ck", + "univpll1_d2", + "armca35pll_400m", + "tvdpll_429m_d2", + "tvdpll_429m_d4" +}; + +static const char * const spislv_parents[] = { + "clk26m", + "univpll2_d4", + "univpll1_d4", + "univpll2_d2", + "univpll3_d2", + "univpll1_d8", + "univpll1_d2", + "univpll_d5" +}; + +static const char * const ether_parents[] = { + "clk26m", + "etherpll_50m", + "univpll_d26" +}; + +static const char * const di_parents[] = { + "clk26m", + "tvdpll_d2", + "tvdpll_d4", + "tvdpll_d8", + "vencpll_ck", + "vencpll_d2", + "cvbs", + "cvbs_d2" +}; + +static const char * const tvd_parents[] = { + "clk26m", + "cvbs_d2", + "univpll2_d8" +}; + +static const char * const i2c_parents[] = { + "clk26m", + "univpll_d26", + "univpll2_d4", + "univpll3_d2", + "univpll1_d4" +}; + +static const char * const msdc0p_aes_parents[] = { + "clk26m", + "msdcpll_ck", + "univpll_d3", + "vcodecpll_ck" +}; + +static const char * const cmsys_parents[] = { + "clk26m", + "univpll_d3", + "syspll_d3", + "syspll1_d2", + "syspll2_d2" +}; + +static const char * const gcpu_parents[] = { + "clk26m", + "syspll_d3", + "syspll1_d2", + "univpll1_d2", + "univpll_d5", + "univpll3_d2", + "univpll_d3" +}; + +static const char * const aud_apll1_parents[] = { + "apll1", + "clkaud_ext_i_1" +}; + +static const char * const aud_apll2_parents[] = { + "apll2", + "clkaud_ext_i_2" +}; + +static const char * const audull_vtx_parents[] = { + "d2a_ulclk_6p5m", + "clkaud_ext_i_0" +}; + +static struct mtk_composite top_muxes[] = { + /* CLK_CFG_0 */ + MUX_GATE_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x040, 0, 3, + 7, CLK_IS_CRITICAL), + MUX_GATE_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x040, 8, 1, + 15, CLK_IS_CRITICAL), + MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", + mm_parents, 0x040, 24, 3, 31), + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", + pwm_parents, 0x050, 0, 2, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", + vdec_parents, 0x050, 8, 4, 15), + MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", + venc_parents, 0x050, 16, 4, 23), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", + mfg_parents, 0x050, 24, 4, 31), + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", + camtg_parents, 0x060, 0, 4, 7), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", + uart_parents, 0x060, 8, 1, 15), + MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", + spi_parents, 0x060, 16, 3, 23), + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", + usb20_parents, 0x060, 24, 2, 31), + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", + usb30_parents, 0x070, 0, 2, 7), + MUX_GATE(CLK_TOP_MSDC50_0_HCLK_SEL, "msdc50_0_h_sel", + msdc50_0_h_parents, 0x070, 8, 3, 15), + MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", + msdc50_0_parents, 0x070, 16, 4, 23), + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", + msdc30_1_parents, 0x070, 24, 3, 31), + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", + msdc30_1_parents, 0x080, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", + msdc30_3_parents, 0x080, 8, 4, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", + audio_parents, 0x080, 16, 2, 23), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", + aud_intbus_parents, 0x080, 24, 3, 31), + /* CLK_CFG_5 */ + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", + pmicspi_parents, 0x090, 0, 3, 7), + MUX_GATE(CLK_TOP_DPILVDS1_SEL, "dpilvds1_sel", + dpilvds1_parents, 0x090, 8, 3, 15), + MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", + atb_parents, 0x090, 16, 2, 23), + MUX_GATE(CLK_TOP_NR_SEL, "nr_sel", + nr_parents, 0x090, 24, 3, 31), + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_NFI2X_SEL, "nfi2x_sel", + nfi2x_parents, 0x0a0, 0, 4, 7), + MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", + irda_parents, 0x0a0, 8, 2, 15), + MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", + cci400_parents, 0x0a0, 16, 3, 23), + MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", + aud_1_parents, 0x0a0, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", + aud_2_parents, 0x0b0, 0, 2, 7), + MUX_GATE(CLK_TOP_MEM_MFG_IN_AS_SEL, "mem_mfg_sel", + mem_mfg_parents, 0x0b0, 8, 2, 15), + MUX_GATE(CLK_TOP_AXI_MFG_IN_AS_SEL, "axi_mfg_sel", + axi_mfg_parents, 0x0b0, 16, 2, 23), + MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", + scam_parents, 0x0b0, 24, 2, 31), + /* CLK_CFG_8 */ + MUX_GATE(CLK_TOP_NFIECC_SEL, "nfiecc_sel", + nfiecc_parents, 0x0c0, 0, 3, 7), + MUX_GATE(CLK_TOP_PE2_MAC_P0_SEL, "pe2_mac_p0_sel", + pe2_mac_p0_parents, 0x0c0, 8, 3, 15), + MUX_GATE(CLK_TOP_PE2_MAC_P1_SEL, "pe2_mac_p1_sel", + pe2_mac_p0_parents, 0x0c0, 16, 3, 23), + MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", + dpilvds_parents, 0x0c0, 24, 3, 31), + /* CLK_CFG_9 */ + MUX_GATE(CLK_TOP_MSDC50_3_HCLK_SEL, "msdc50_3_h_sel", + msdc50_0_h_parents, 0x0d0, 0, 3, 7), + MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", + hdcp_parents, 0x0d0, 8, 2, 15), + MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", + hdcp_24m_parents, 0x0d0, 16, 2, 23), + MUX_GATE_FLAGS(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x0d0, 24, 2, + 31, CLK_IS_CRITICAL), + /* CLK_CFG_10 */ + MUX_GATE(CLK_TOP_SPINOR_SEL, "spinor_sel", + spinor_parents, 0x500, 0, 4, 7), + MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", + apll_parents, 0x500, 8, 4, 15), + MUX_GATE(CLK_TOP_APLL2_SEL, "apll2_sel", + apll_parents, 0x500, 16, 4, 23), + MUX_GATE(CLK_TOP_A1SYS_HP_SEL, "a1sys_hp_sel", + a1sys_hp_parents, 0x500, 24, 3, 31), + /* CLK_CFG_11 */ + MUX_GATE(CLK_TOP_A2SYS_HP_SEL, "a2sys_hp_sel", + a2sys_hp_parents, 0x510, 0, 3, 7), + MUX_GATE(CLK_TOP_ASM_L_SEL, "asm_l_sel", + asm_l_parents, 0x510, 8, 2, 15), + MUX_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel", + asm_l_parents, 0x510, 16, 2, 23), + MUX_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel", + asm_l_parents, 0x510, 24, 2, 31), + /* CLK_CFG_12 */ + MUX_GATE(CLK_TOP_I2SO1_SEL, "i2so1_sel", + i2so1_parents, 0x520, 0, 2, 7), + MUX_GATE(CLK_TOP_I2SO2_SEL, "i2so2_sel", + i2so1_parents, 0x520, 8, 2, 15), + MUX_GATE(CLK_TOP_I2SO3_SEL, "i2so3_sel", + i2so1_parents, 0x520, 16, 2, 23), + MUX_GATE(CLK_TOP_TDMO0_SEL, "tdmo0_sel", + i2so1_parents, 0x520, 24, 2, 31), + /* CLK_CFG_13 */ + MUX_GATE(CLK_TOP_TDMO1_SEL, "tdmo1_sel", + i2so1_parents, 0x530, 0, 2, 7), + MUX_GATE(CLK_TOP_I2SI1_SEL, "i2si1_sel", + i2so1_parents, 0x530, 8, 2, 15), + MUX_GATE(CLK_TOP_I2SI2_SEL, "i2si2_sel", + i2so1_parents, 0x530, 16, 2, 23), + MUX_GATE(CLK_TOP_I2SI3_SEL, "i2si3_sel", + i2so1_parents, 0x530, 24, 2, 31), + /* CLK_CFG_14 */ + MUX_GATE(CLK_TOP_ETHER_125M_SEL, "ether_125m_sel", + ether_125m_parents, 0x540, 0, 2, 7), + MUX_GATE(CLK_TOP_ETHER_50M_SEL, "ether_50m_sel", + ether_50m_parents, 0x540, 8, 2, 15), + MUX_GATE(CLK_TOP_JPGDEC_SEL, "jpgdec_sel", + jpgdec_parents, 0x540, 16, 4, 23), + MUX_GATE(CLK_TOP_SPISLV_SEL, "spislv_sel", + spislv_parents, 0x540, 24, 3, 31), + /* CLK_CFG_15 */ + MUX_GATE(CLK_TOP_ETHER_50M_RMII_SEL, "ether_sel", + ether_parents, 0x550, 0, 2, 7), + MUX_GATE(CLK_TOP_CAM2TG_SEL, "cam2tg_sel", + camtg_parents, 0x550, 8, 4, 15), + MUX_GATE(CLK_TOP_DI_SEL, "di_sel", + di_parents, 0x550, 16, 3, 23), + MUX_GATE(CLK_TOP_TVD_SEL, "tvd_sel", + tvd_parents, 0x550, 24, 2, 31), + /* CLK_CFG_16 */ + MUX_GATE(CLK_TOP_I2C_SEL, "i2c_sel", + i2c_parents, 0x560, 0, 3, 7), + MUX_GATE(CLK_TOP_PWM_INFRA_SEL, "pwm_infra_sel", + pwm_parents, 0x560, 8, 2, 15), + MUX_GATE(CLK_TOP_MSDC0P_AES_SEL, "msdc0p_aes_sel", + msdc0p_aes_parents, 0x560, 16, 2, 23), + MUX_GATE(CLK_TOP_CMSYS_SEL, "cmsys_sel", + cmsys_parents, 0x560, 24, 3, 31), + /* CLK_CFG_17 */ + MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel", + gcpu_parents, 0x570, 0, 3, 7), + /* CLK_AUDDIV_4 */ + MUX(CLK_TOP_AUD_APLL1_SEL, "aud_apll1_sel", + aud_apll1_parents, 0x134, 0, 1), + MUX(CLK_TOP_AUD_APLL2_SEL, "aud_apll2_sel", + aud_apll2_parents, 0x134, 1, 1), + MUX(CLK_TOP_DA_AUDULL_VTX_6P5M_SEL, "audull_vtx_sel", + audull_vtx_parents, 0x134, 31, 1), +}; + +static const char * const mcu_mp0_parents[] = { + "clk26m", + "armca35pll_ck", + "f_mp0_pll1_ck", + "f_mp0_pll2_ck" +}; + +static const char * const mcu_mp2_parents[] = { + "clk26m", + "armca72pll_ck", + "f_big_pll1_ck", + "f_big_pll2_ck" +}; + +static const char * const mcu_bus_parents[] = { + "clk26m", + "cci400_sel", + "f_bus_pll1_ck", + "f_bus_pll2_ck" +}; + +static struct mtk_composite mcu_muxes[] = { + /* mp0_pll_divider_cfg */ + MUX_GATE_FLAGS(CLK_MCU_MP0_SEL, "mcu_mp0_sel", mcu_mp0_parents, 0x7A0, + 9, 2, -1, CLK_IS_CRITICAL), + /* mp2_pll_divider_cfg */ + MUX_GATE_FLAGS(CLK_MCU_MP2_SEL, "mcu_mp2_sel", mcu_mp2_parents, 0x7A8, + 9, 2, -1, CLK_IS_CRITICAL), + /* bus_pll_divider_cfg */ + MUX_GATE_FLAGS(CLK_MCU_BUS_SEL, "mcu_bus_sel", mcu_bus_parents, 0x7C0, + 9, 2, -1, CLK_IS_CRITICAL), +}; + +static const struct mtk_clk_divider top_adj_divs[] = { + DIV_ADJ(CLK_TOP_APLL_DIV0, "apll_div0", "i2so1_sel", 0x124, 0, 8), + DIV_ADJ(CLK_TOP_APLL_DIV1, "apll_div1", "i2so2_sel", 0x124, 8, 8), + DIV_ADJ(CLK_TOP_APLL_DIV2, "apll_div2", "i2so3_sel", 0x124, 16, 8), + DIV_ADJ(CLK_TOP_APLL_DIV3, "apll_div3", "tdmo0_sel", 0x124, 24, 8), + DIV_ADJ(CLK_TOP_APLL_DIV4, "apll_div4", "tdmo1_sel", 0x128, 0, 8), + DIV_ADJ(CLK_TOP_APLL_DIV5, "apll_div5", "i2si1_sel", 0x128, 8, 8), + DIV_ADJ(CLK_TOP_APLL_DIV6, "apll_div6", "i2si2_sel", 0x128, 16, 8), + DIV_ADJ(CLK_TOP_APLL_DIV7, "apll_div7", "i2si3_sel", 0x128, 24, 8), +}; + +static const struct mtk_gate_regs top_cg_regs = { + .set_ofs = 0x120, + .clr_ofs = 0x120, + .sta_ofs = 0x120, +}; + +#define GATE_TOP(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &top_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +static const struct mtk_gate top_clks[] = { + GATE_TOP(CLK_TOP_APLL_DIV_PDN0, "apll_div_pdn0", "i2so1_sel", 0), + GATE_TOP(CLK_TOP_APLL_DIV_PDN1, "apll_div_pdn1", "i2so2_sel", 1), + GATE_TOP(CLK_TOP_APLL_DIV_PDN2, "apll_div_pdn2", "i2so3_sel", 2), + GATE_TOP(CLK_TOP_APLL_DIV_PDN3, "apll_div_pdn3", "tdmo0_sel", 3), + GATE_TOP(CLK_TOP_APLL_DIV_PDN4, "apll_div_pdn4", "tdmo1_sel", 4), + GATE_TOP(CLK_TOP_APLL_DIV_PDN5, "apll_div_pdn5", "i2si1_sel", 5), + GATE_TOP(CLK_TOP_APLL_DIV_PDN6, "apll_div_pdn6", "i2si2_sel", 6), + GATE_TOP(CLK_TOP_APLL_DIV_PDN7, "apll_div_pdn7", "i2si3_sel", 7), +}; + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x40, + .clr_ofs = 0x44, + .sta_ofs = 0x40, +}; + +#define GATE_INFRA(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] = { + GATE_INFRA(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0), + GATE_INFRA(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6), + GATE_INFRA(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8), + GATE_INFRA(CLK_INFRA_KP, "infra_kp", "axi_sel", 16), + GATE_INFRA(CLK_INFRA_AO_SPI0, "infra_ao_spi0", "spi_sel", 24), + GATE_INFRA(CLK_INFRA_AO_SPI1, "infra_ao_spi1", "spislv_sel", 25), + GATE_INFRA(CLK_INFRA_AO_UART5, "infra_ao_uart5", "axi_sel", 26), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x8, + .clr_ofs = 0x10, + .sta_ofs = 0x18, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0xc, + .clr_ofs = 0x14, + .sta_ofs = 0x1c, +}; + +static const struct mtk_gate_regs peri2_cg_regs = { + .set_ofs = 0x42c, + .clr_ofs = 0x42c, + .sta_ofs = 0x42c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI2(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri2_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate peri_clks[] = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_NFI, "per_nfi", + "axi_sel", 0), + GATE_PERI0(CLK_PERI_THERM, "per_therm", + "axi_sel", 1), + GATE_PERI0(CLK_PERI_PWM0, "per_pwm0", + "pwm_sel", 2), + GATE_PERI0(CLK_PERI_PWM1, "per_pwm1", + "pwm_sel", 3), + GATE_PERI0(CLK_PERI_PWM2, "per_pwm2", + "pwm_sel", 4), + GATE_PERI0(CLK_PERI_PWM3, "per_pwm3", + "pwm_sel", 5), + GATE_PERI0(CLK_PERI_PWM4, "per_pwm4", + "pwm_sel", 6), + GATE_PERI0(CLK_PERI_PWM5, "per_pwm5", + "pwm_sel", 7), + GATE_PERI0(CLK_PERI_PWM6, "per_pwm6", + "pwm_sel", 8), + GATE_PERI0(CLK_PERI_PWM7, "per_pwm7", + "pwm_sel", 9), + GATE_PERI0(CLK_PERI_PWM, "per_pwm", + "pwm_sel", 10), + GATE_PERI0(CLK_PERI_AP_DMA, "per_ap_dma", + "axi_sel", 13), + GATE_PERI0(CLK_PERI_MSDC30_0, "per_msdc30_0", + "msdc50_0_sel", 14), + GATE_PERI0(CLK_PERI_MSDC30_1, "per_msdc30_1", + "msdc30_1_sel", 15), + GATE_PERI0(CLK_PERI_MSDC30_2, "per_msdc30_2", + "msdc30_2_sel", 16), + GATE_PERI0(CLK_PERI_MSDC30_3, "per_msdc30_3", + "msdc30_3_sel", 17), + GATE_PERI0(CLK_PERI_UART0, "per_uart0", + "uart_sel", 20), + GATE_PERI0(CLK_PERI_UART1, "per_uart1", + "uart_sel", 21), + GATE_PERI0(CLK_PERI_UART2, "per_uart2", + "uart_sel", 22), + GATE_PERI0(CLK_PERI_UART3, "per_uart3", + "uart_sel", 23), + GATE_PERI0(CLK_PERI_I2C0, "per_i2c0", + "axi_sel", 24), + GATE_PERI0(CLK_PERI_I2C1, "per_i2c1", + "axi_sel", 25), + GATE_PERI0(CLK_PERI_I2C2, "per_i2c2", + "axi_sel", 26), + GATE_PERI0(CLK_PERI_I2C3, "per_i2c3", + "axi_sel", 27), + GATE_PERI0(CLK_PERI_I2C4, "per_i2c4", + "axi_sel", 28), + GATE_PERI0(CLK_PERI_AUXADC, "per_auxadc", + "ltepll_fs26m", 29), + GATE_PERI0(CLK_PERI_SPI0, "per_spi0", + "spi_sel", 30), + /* PERI1 */ + GATE_PERI1(CLK_PERI_SPI, "per_spi", + "spinor_sel", 1), + GATE_PERI1(CLK_PERI_I2C5, "per_i2c5", + "axi_sel", 3), + GATE_PERI1(CLK_PERI_SPI2, "per_spi2", + "spi_sel", 5), + GATE_PERI1(CLK_PERI_SPI3, "per_spi3", + "spi_sel", 6), + GATE_PERI1(CLK_PERI_SPI5, "per_spi5", + "spi_sel", 8), + GATE_PERI1(CLK_PERI_UART4, "per_uart4", + "uart_sel", 9), + GATE_PERI1(CLK_PERI_SFLASH, "per_sflash", + "uart_sel", 11), + GATE_PERI1(CLK_PERI_GMAC, "per_gmac", + "uart_sel", 12), + GATE_PERI1(CLK_PERI_PCIE0, "per_pcie0", + "uart_sel", 14), + GATE_PERI1(CLK_PERI_PCIE1, "per_pcie1", + "uart_sel", 15), + GATE_PERI1(CLK_PERI_GMAC_PCLK, "per_gmac_pclk", + "uart_sel", 16), + /* PERI2 */ + GATE_PERI2(CLK_PERI_MSDC50_0_EN, "per_msdc50_0_en", + "msdc50_0_sel", 0), + GATE_PERI2(CLK_PERI_MSDC30_1_EN, "per_msdc30_1_en", + "msdc30_1_sel", 1), + GATE_PERI2(CLK_PERI_MSDC30_2_EN, "per_msdc30_2_en", + "msdc30_2_sel", 2), + GATE_PERI2(CLK_PERI_MSDC30_3_EN, "per_msdc30_3_en", + "msdc30_3_sel", 3), + GATE_PERI2(CLK_PERI_MSDC50_0_HCLK_EN, "per_msdc50_0_h", + "msdc50_0_h_sel", 4), + GATE_PERI2(CLK_PERI_MSDC50_3_HCLK_EN, "per_msdc50_3_h", + "msdc50_3_h_sel", 5), +}; + +#define MT2712_PLL_FMAX (3000UL * MHZ) + +#define CON0_MT2712_RST_BAR BIT(24) + +#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \ + _tuner_en_bit, _pcw_reg, _pcw_shift, \ + _div_table) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT2712_RST_BAR, \ + .fmax = MT2712_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .tuner_en_reg = _tuner_en_reg, \ + .tuner_en_bit = _tuner_en_bit, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + .div_table = _div_table, \ + } + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \ + _tuner_en_bit, _pcw_reg, _pcw_shift) \ + PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \ + _pcwbits, _pd_reg, _pd_shift, _tuner_reg, \ + _tuner_en_reg, _tuner_en_bit, _pcw_reg, \ + _pcw_shift, NULL) + +static const struct mtk_pll_div_table armca35pll_div_table[] = { + { .div = 0, .freq = MT2712_PLL_FMAX }, + { .div = 1, .freq = 1202500000 }, + { .div = 2, .freq = 500500000 }, + { .div = 3, .freq = 315250000 }, + { .div = 4, .freq = 157625000 }, + { } /* sentinel */ +}; + +static const struct mtk_pll_div_table armca72pll_div_table[] = { + { .div = 0, .freq = MT2712_PLL_FMAX }, + { .div = 1, .freq = 994500000 }, + { .div = 2, .freq = 520000000 }, + { .div = 3, .freq = 315250000 }, + { .div = 4, .freq = 157625000 }, + { } /* sentinel */ +}; + +static const struct mtk_pll_div_table mmpll_div_table[] = { + { .div = 0, .freq = MT2712_PLL_FMAX }, + { .div = 1, .freq = 1001000000 }, + { .div = 2, .freq = 601250000 }, + { .div = 3, .freq = 250250000 }, + { .div = 4, .freq = 125125000 }, + { } /* sentinel */ +}; + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0230, 0x023C, 0xf0000101, + HAVE_RST_BAR, 31, 0x0230, 4, 0, 0, 0, 0x0234, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0240, 0x024C, 0xfe000101, + HAVE_RST_BAR, 31, 0x0240, 4, 0, 0, 0, 0x0244, 0), + PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x0320, 0x032C, 0xc0000101, + 0, 31, 0x0320, 4, 0, 0, 0, 0x0324, 0), + PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x0280, 0x028C, 0x00000101, + 0, 31, 0x0280, 4, 0, 0, 0, 0x0284, 0), + PLL(CLK_APMIXED_APLL1, "apll1", 0x0330, 0x0340, 0x00000101, + 0, 31, 0x0330, 4, 0x0338, 0x0014, 0, 0x0334, 0), + PLL(CLK_APMIXED_APLL2, "apll2", 0x0350, 0x0360, 0x00000101, + 0, 31, 0x0350, 4, 0x0358, 0x0014, 1, 0x0354, 0), + PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x0370, 0x037c, 0x00000101, + 0, 31, 0x0370, 4, 0, 0, 0, 0x0374, 0), + PLL(CLK_APMIXED_LVDSPLL2, "lvdspll2", 0x0390, 0x039C, 0x00000101, + 0, 31, 0x0390, 4, 0, 0, 0, 0x0394, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0270, 0x027C, 0x00000101, + 0, 31, 0x0270, 4, 0, 0, 0, 0x0274, 0), + PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x0410, 0x041C, 0x00000101, + 0, 31, 0x0410, 4, 0, 0, 0, 0x0414, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x0290, 0x029C, 0xc0000101, + 0, 31, 0x0290, 4, 0, 0, 0, 0x0294, 0), + PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0250, 0x0260, 0x00000101, + 0, 31, 0x0250, 4, 0, 0, 0, 0x0254, 0, + mmpll_div_table), + PLL_B(CLK_APMIXED_ARMCA35PLL, "armca35pll", 0x0100, 0x0110, 0xf0000101, + HAVE_RST_BAR, 31, 0x0100, 4, 0, 0, 0, 0x0104, 0, + armca35pll_div_table), + PLL_B(CLK_APMIXED_ARMCA72PLL, "armca72pll", 0x0210, 0x0220, 0x00000101, + 0, 31, 0x0210, 4, 0, 0, 0, 0x0214, 0, + armca72pll_div_table), + PLL(CLK_APMIXED_ETHERPLL, "etherpll", 0x0300, 0x030C, 0xc0000101, + 0, 31, 0x0300, 4, 0, 0, 0, 0x0304, 0), +}; + +static int clk_mt2712_apmixed_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static struct clk_onecell_data *top_clk_data; + +static void clk_mt2712_top_init_early(struct device_node *node) +{ + int r, i; + + if (!top_clk_data) { + top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + for (i = 0; i < CLK_TOP_NR_CLK; i++) + top_clk_data->clks[i] = ERR_PTR(-EPROBE_DEFER); + } + + mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs), + top_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, top_clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} + +CLK_OF_DECLARE_DRIVER(mt2712_topckgen, "mediatek,mt2712-topckgen", + clk_mt2712_top_init_early); + +static int clk_mt2712_top_probe(struct platform_device *pdev) +{ + int r, i; + struct device_node *node = pdev->dev.of_node; + void __iomem *base; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + pr_err("%s(): ioremap failed\n", __func__); + return PTR_ERR(base); + } + + if (!top_clk_data) { + top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + } else { + for (i = 0; i < CLK_TOP_NR_CLK; i++) { + if (top_clk_data->clks[i] == ERR_PTR(-EPROBE_DEFER)) + top_clk_data->clks[i] = ERR_PTR(-ENOENT); + } + } + + mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), + top_clk_data); + mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs), + top_clk_data); + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data); + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, + &mt2712_clk_lock, top_clk_data); + mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs), base, + &mt2712_clk_lock, top_clk_data); + mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks), + top_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, top_clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static int clk_mt2712_infra_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0x30); + + return r; +} + +static int clk_mt2712_peri_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0); + + return r; +} + +static int clk_mt2712_mcu_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + void __iomem *base; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + pr_err("%s(): ioremap failed\n", __func__); + return PTR_ERR(base); + } + + clk_data = mtk_alloc_clk_data(CLK_MCU_NR_CLK); + + mtk_clk_register_composites(mcu_muxes, ARRAY_SIZE(mcu_muxes), base, + &mt2712_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r != 0) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt2712[] = { + { + .compatible = "mediatek,mt2712-apmixedsys", + .data = clk_mt2712_apmixed_probe, + }, { + .compatible = "mediatek,mt2712-topckgen", + .data = clk_mt2712_top_probe, + }, { + .compatible = "mediatek,mt2712-infracfg", + .data = clk_mt2712_infra_probe, + }, { + .compatible = "mediatek,mt2712-pericfg", + .data = clk_mt2712_peri_probe, + }, { + .compatible = "mediatek,mt2712-mcucfg", + .data = clk_mt2712_mcu_probe, + }, { + /* sentinel */ + } +}; + +static int clk_mt2712_probe(struct platform_device *pdev) +{ + int (*clk_probe)(struct platform_device *); + int r; + + clk_probe = of_device_get_match_data(&pdev->dev); + if (!clk_probe) + return -EINVAL; + + r = clk_probe(pdev); + if (r != 0) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt2712_drv = { + .probe = clk_mt2712_probe, + .driver = { + .name = "clk-mt2712", + .owner = THIS_MODULE, + .of_match_table = of_match_clk_mt2712, + }, +}; + +static int __init clk_mt2712_init(void) +{ + return platform_driver_register(&clk_mt2712_drv); +} + +arch_initcall(clk_mt2712_init); diff --git a/drivers/clk/mediatek/clk-mt7622-aud.c b/drivers/clk/mediatek/clk-mt7622-aud.c new file mode 100644 index 000000000000..fad7d9fc53ba --- /dev/null +++ b/drivers/clk/mediatek/clk-mt7622-aud.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Chen Zhong + * Sean Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +#define GATE_AUDIO0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &audio0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +#define GATE_AUDIO1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &audio1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +#define GATE_AUDIO2(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &audio2_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +#define GATE_AUDIO3(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &audio3_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +static const struct mtk_gate_regs audio0_cg_regs = { + .set_ofs = 0x0, + .clr_ofs = 0x0, + .sta_ofs = 0x0, +}; + +static const struct mtk_gate_regs audio1_cg_regs = { + .set_ofs = 0x10, + .clr_ofs = 0x10, + .sta_ofs = 0x10, +}; + +static const struct mtk_gate_regs audio2_cg_regs = { + .set_ofs = 0x14, + .clr_ofs = 0x14, + .sta_ofs = 0x14, +}; + +static const struct mtk_gate_regs audio3_cg_regs = { + .set_ofs = 0x634, + .clr_ofs = 0x634, + .sta_ofs = 0x634, +}; + +static const struct mtk_gate audio_clks[] = { + /* AUDIO0 */ + GATE_AUDIO0(CLK_AUDIO_AFE, "audio_afe", "rtc", 2), + GATE_AUDIO0(CLK_AUDIO_HDMI, "audio_hdmi", "apll1_ck_sel", 20), + GATE_AUDIO0(CLK_AUDIO_SPDF, "audio_spdf", "apll1_ck_sel", 21), + GATE_AUDIO0(CLK_AUDIO_APLL, "audio_apll", "apll1_ck_sel", 23), + /* AUDIO1 */ + GATE_AUDIO1(CLK_AUDIO_I2SIN1, "audio_i2sin1", "a1sys_hp_sel", 0), + GATE_AUDIO1(CLK_AUDIO_I2SIN2, "audio_i2sin2", "a1sys_hp_sel", 1), + GATE_AUDIO1(CLK_AUDIO_I2SIN3, "audio_i2sin3", "a1sys_hp_sel", 2), + GATE_AUDIO1(CLK_AUDIO_I2SIN4, "audio_i2sin4", "a1sys_hp_sel", 3), + GATE_AUDIO1(CLK_AUDIO_I2SO1, "audio_i2so1", "a1sys_hp_sel", 6), + GATE_AUDIO1(CLK_AUDIO_I2SO2, "audio_i2so2", "a1sys_hp_sel", 7), + GATE_AUDIO1(CLK_AUDIO_I2SO3, "audio_i2so3", "a1sys_hp_sel", 8), + GATE_AUDIO1(CLK_AUDIO_I2SO4, "audio_i2so4", "a1sys_hp_sel", 9), + GATE_AUDIO1(CLK_AUDIO_ASRCI1, "audio_asrci1", "asm_h_sel", 12), + GATE_AUDIO1(CLK_AUDIO_ASRCI2, "audio_asrci2", "asm_h_sel", 13), + GATE_AUDIO1(CLK_AUDIO_ASRCO1, "audio_asrco1", "asm_h_sel", 14), + GATE_AUDIO1(CLK_AUDIO_ASRCO2, "audio_asrco2", "asm_h_sel", 15), + GATE_AUDIO1(CLK_AUDIO_INTDIR, "audio_intdir", "intdir_sel", 20), + GATE_AUDIO1(CLK_AUDIO_A1SYS, "audio_a1sys", "a1sys_hp_sel", 21), + GATE_AUDIO1(CLK_AUDIO_A2SYS, "audio_a2sys", "a2sys_hp_sel", 22), + /* AUDIO2 */ + GATE_AUDIO2(CLK_AUDIO_UL1, "audio_ul1", "a1sys_hp_sel", 0), + GATE_AUDIO2(CLK_AUDIO_UL2, "audio_ul2", "a1sys_hp_sel", 1), + GATE_AUDIO2(CLK_AUDIO_UL3, "audio_ul3", "a1sys_hp_sel", 2), + GATE_AUDIO2(CLK_AUDIO_UL4, "audio_ul4", "a1sys_hp_sel", 3), + GATE_AUDIO2(CLK_AUDIO_UL5, "audio_ul5", "a1sys_hp_sel", 4), + GATE_AUDIO2(CLK_AUDIO_UL6, "audio_ul6", "a1sys_hp_sel", 5), + GATE_AUDIO2(CLK_AUDIO_DL1, "audio_dl1", "a1sys_hp_sel", 6), + GATE_AUDIO2(CLK_AUDIO_DL2, "audio_dl2", "a1sys_hp_sel", 7), + GATE_AUDIO2(CLK_AUDIO_DL3, "audio_dl3", "a1sys_hp_sel", 8), + GATE_AUDIO2(CLK_AUDIO_DL4, "audio_dl4", "a1sys_hp_sel", 9), + GATE_AUDIO2(CLK_AUDIO_DL5, "audio_dl5", "a1sys_hp_sel", 10), + GATE_AUDIO2(CLK_AUDIO_DL6, "audio_dl6", "a1sys_hp_sel", 11), + GATE_AUDIO2(CLK_AUDIO_DLMCH, "audio_dlmch", "a1sys_hp_sel", 12), + GATE_AUDIO2(CLK_AUDIO_ARB1, "audio_arb1", "a1sys_hp_sel", 13), + GATE_AUDIO2(CLK_AUDIO_AWB, "audio_awb", "a1sys_hp_sel", 14), + GATE_AUDIO2(CLK_AUDIO_AWB2, "audio_awb2", "a1sys_hp_sel", 15), + GATE_AUDIO2(CLK_AUDIO_DAI, "audio_dai", "a1sys_hp_sel", 16), + GATE_AUDIO2(CLK_AUDIO_MOD, "audio_mod", "a1sys_hp_sel", 17), + /* AUDIO3 */ + GATE_AUDIO3(CLK_AUDIO_ASRCI3, "audio_asrci3", "asm_h_sel", 2), + GATE_AUDIO3(CLK_AUDIO_ASRCI4, "audio_asrci4", "asm_h_sel", 3), + GATE_AUDIO3(CLK_AUDIO_ASRCO3, "audio_asrco3", "asm_h_sel", 6), + GATE_AUDIO3(CLK_AUDIO_ASRCO4, "audio_asrco4", "asm_h_sel", 7), + GATE_AUDIO3(CLK_AUDIO_MEM_ASRC1, "audio_mem_asrc1", "asm_h_sel", 10), + GATE_AUDIO3(CLK_AUDIO_MEM_ASRC2, "audio_mem_asrc2", "asm_h_sel", 11), + GATE_AUDIO3(CLK_AUDIO_MEM_ASRC3, "audio_mem_asrc3", "asm_h_sel", 12), + GATE_AUDIO3(CLK_AUDIO_MEM_ASRC4, "audio_mem_asrc4", "asm_h_sel", 13), + GATE_AUDIO3(CLK_AUDIO_MEM_ASRC5, "audio_mem_asrc5", "asm_h_sel", 14), +}; + +static int clk_mt7622_audiosys_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + + clk_data = mtk_alloc_clk_data(CLK_AUDIO_NR_CLK); + + mtk_clk_register_gates(node, audio_clks, ARRAY_SIZE(audio_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt7622_aud[] = { + { + .compatible = "mediatek,mt7622-audsys", + .data = clk_mt7622_audiosys_init, + }, { + /* sentinel */ + } +}; + +static int clk_mt7622_aud_probe(struct platform_device *pdev) +{ + int (*clk_init)(struct platform_device *); + int r; + + clk_init = of_device_get_match_data(&pdev->dev); + if (!clk_init) + return -EINVAL; + + r = clk_init(pdev); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt7622_aud_drv = { + .probe = clk_mt7622_aud_probe, + .driver = { + .name = "clk-mt7622-aud", + .of_match_table = of_match_clk_mt7622_aud, + }, +}; + +builtin_platform_driver(clk_mt7622_aud_drv); diff --git a/drivers/clk/mediatek/clk-mt7622-eth.c b/drivers/clk/mediatek/clk-mt7622-eth.c new file mode 100644 index 000000000000..6328127bbb3c --- /dev/null +++ b/drivers/clk/mediatek/clk-mt7622-eth.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Chen Zhong + * Sean Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +#define GATE_ETH(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = ð_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate_regs eth_cg_regs = { + .set_ofs = 0x30, + .clr_ofs = 0x30, + .sta_ofs = 0x30, +}; + +static const struct mtk_gate eth_clks[] = { + GATE_ETH(CLK_ETH_HSDMA_EN, "eth_hsdma_en", "eth_sel", 5), + GATE_ETH(CLK_ETH_ESW_EN, "eth_esw_en", "eth_500m", 6), + GATE_ETH(CLK_ETH_GP2_EN, "eth_gp2_en", "txclk_src_pre", 7), + GATE_ETH(CLK_ETH_GP1_EN, "eth_gp1_en", "txclk_src_pre", 8), + GATE_ETH(CLK_ETH_GP0_EN, "eth_gp0_en", "txclk_src_pre", 9), +}; + +static const struct mtk_gate_regs sgmii_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &sgmii_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii_clks[] = { + GATE_SGMII(CLK_SGMII_TX250M_EN, "sgmii_tx250m_en", + "ssusb_tx250m", 2), + GATE_SGMII(CLK_SGMII_RX250M_EN, "sgmii_rx250m_en", + "ssusb_eq_rx250m", 3), + GATE_SGMII(CLK_SGMII_CDR_REF, "sgmii_cdr_ref", + "ssusb_cdr_ref", 4), + GATE_SGMII(CLK_SGMII_CDR_FB, "sgmii_cdr_fb", + "ssusb_cdr_fb", 5), +}; + +static int clk_mt7622_ethsys_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + + clk_data = mtk_alloc_clk_data(CLK_ETH_NR_CLK); + + mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + mtk_register_reset_controller(node, 1, 0x34); + + return r; +} + +static int clk_mt7622_sgmiisys_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SGMII_NR_CLK); + + mtk_clk_register_gates(node, sgmii_clks, ARRAY_SIZE(sgmii_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static const struct of_device_id of_match_clk_mt7622_eth[] = { + { + .compatible = "mediatek,mt7622-ethsys", + .data = clk_mt7622_ethsys_init, + }, { + .compatible = "mediatek,mt7622-sgmiisys", + .data = clk_mt7622_sgmiisys_init, + }, { + /* sentinel */ + } +}; + +static int clk_mt7622_eth_probe(struct platform_device *pdev) +{ + int (*clk_init)(struct platform_device *); + int r; + + clk_init = of_device_get_match_data(&pdev->dev); + if (!clk_init) + return -EINVAL; + + r = clk_init(pdev); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt7622_eth_drv = { + .probe = clk_mt7622_eth_probe, + .driver = { + .name = "clk-mt7622-eth", + .of_match_table = of_match_clk_mt7622_eth, + }, +}; + +builtin_platform_driver(clk_mt7622_eth_drv); diff --git a/drivers/clk/mediatek/clk-mt7622-hif.c b/drivers/clk/mediatek/clk-mt7622-hif.c new file mode 100644 index 000000000000..a6e8534276c6 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt7622-hif.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Chen Zhong + * Sean Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +#define GATE_PCIE(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &pcie_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +#define GATE_SSUSB(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &ssusb_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate_regs pcie_cg_regs = { + .set_ofs = 0x30, + .clr_ofs = 0x30, + .sta_ofs = 0x30, +}; + +static const struct mtk_gate_regs ssusb_cg_regs = { + .set_ofs = 0x30, + .clr_ofs = 0x30, + .sta_ofs = 0x30, +}; + +static const struct mtk_gate ssusb_clks[] = { + GATE_SSUSB(CLK_SSUSB_U2_PHY_1P_EN, "ssusb_u2_phy_1p", + "to_u2_phy_1p", 0), + GATE_SSUSB(CLK_SSUSB_U2_PHY_EN, "ssusb_u2_phy_en", "to_u2_phy", 1), + GATE_SSUSB(CLK_SSUSB_REF_EN, "ssusb_ref_en", "to_usb3_ref", 5), + GATE_SSUSB(CLK_SSUSB_SYS_EN, "ssusb_sys_en", "to_usb3_sys", 6), + GATE_SSUSB(CLK_SSUSB_MCU_EN, "ssusb_mcu_en", "axi_sel", 7), + GATE_SSUSB(CLK_SSUSB_DMA_EN, "ssusb_dma_en", "hif_sel", 8), +}; + +static const struct mtk_gate pcie_clks[] = { + GATE_PCIE(CLK_PCIE_P1_AUX_EN, "pcie_p1_aux_en", "p1_1mhz", 12), + GATE_PCIE(CLK_PCIE_P1_OBFF_EN, "pcie_p1_obff_en", "free_run_4mhz", 13), + GATE_PCIE(CLK_PCIE_P1_AHB_EN, "pcie_p1_ahb_en", "axi_sel", 14), + GATE_PCIE(CLK_PCIE_P1_AXI_EN, "pcie_p1_axi_en", "hif_sel", 15), + GATE_PCIE(CLK_PCIE_P1_MAC_EN, "pcie_p1_mac_en", "pcie1_mac_en", 16), + GATE_PCIE(CLK_PCIE_P1_PIPE_EN, "pcie_p1_pipe_en", "pcie1_pipe_en", 17), + GATE_PCIE(CLK_PCIE_P0_AUX_EN, "pcie_p0_aux_en", "p0_1mhz", 18), + GATE_PCIE(CLK_PCIE_P0_OBFF_EN, "pcie_p0_obff_en", "free_run_4mhz", 19), + GATE_PCIE(CLK_PCIE_P0_AHB_EN, "pcie_p0_ahb_en", "axi_sel", 20), + GATE_PCIE(CLK_PCIE_P0_AXI_EN, "pcie_p0_axi_en", "hif_sel", 21), + GATE_PCIE(CLK_PCIE_P0_MAC_EN, "pcie_p0_mac_en", "pcie0_mac_en", 22), + GATE_PCIE(CLK_PCIE_P0_PIPE_EN, "pcie_p0_pipe_en", "pcie0_pipe_en", 23), + GATE_PCIE(CLK_SATA_AHB_EN, "sata_ahb_en", "axi_sel", 26), + GATE_PCIE(CLK_SATA_AXI_EN, "sata_axi_en", "hif_sel", 27), + GATE_PCIE(CLK_SATA_ASIC_EN, "sata_asic_en", "sata_asic", 28), + GATE_PCIE(CLK_SATA_RBC_EN, "sata_rbc_en", "sata_rbc", 29), + GATE_PCIE(CLK_SATA_PM_EN, "sata_pm_en", "univpll2_d4", 30), +}; + +static int clk_mt7622_ssusbsys_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SSUSB_NR_CLK); + + mtk_clk_register_gates(node, ssusb_clks, ARRAY_SIZE(ssusb_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + mtk_register_reset_controller(node, 1, 0x34); + + return r; +} + +static int clk_mt7622_pciesys_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + + clk_data = mtk_alloc_clk_data(CLK_PCIE_NR_CLK); + + mtk_clk_register_gates(node, pcie_clks, ARRAY_SIZE(pcie_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + mtk_register_reset_controller(node, 1, 0x34); + + return r; +} + +static const struct of_device_id of_match_clk_mt7622_hif[] = { + { + .compatible = "mediatek,mt7622-pciesys", + .data = clk_mt7622_pciesys_init, + }, { + .compatible = "mediatek,mt7622-ssusbsys", + .data = clk_mt7622_ssusbsys_init, + }, { + /* sentinel */ + } +}; + +static int clk_mt7622_hif_probe(struct platform_device *pdev) +{ + int (*clk_init)(struct platform_device *); + int r; + + clk_init = of_device_get_match_data(&pdev->dev); + if (!clk_init) + return -EINVAL; + + r = clk_init(pdev); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt7622_hif_drv = { + .probe = clk_mt7622_hif_probe, + .driver = { + .name = "clk-mt7622-hif", + .of_match_table = of_match_clk_mt7622_hif, + }, +}; + +builtin_platform_driver(clk_mt7622_hif_drv); diff --git a/drivers/clk/mediatek/clk-mt7622.c b/drivers/clk/mediatek/clk-mt7622.c new file mode 100644 index 000000000000..92f7e32770c6 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt7622.c @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Chen Zhong + * Sean Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" +#include "clk-cpumux.h" + +#include +#include /* for consumer */ + +#define MT7622_PLL_FMAX (2500UL * MHZ) +#define CON0_MT7622_RST_BAR BIT(27) + +#define PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,\ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ + _pcw_shift, _div_table, _parent_name) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT7622_RST_BAR, \ + .fmax = MT7622_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + .div_table = _div_table, \ + .parent_name = _parent_name, \ + } + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ + _pcw_shift) \ + PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,\ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \ + NULL, "clkxtal") + +#define GATE_APMIXED(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &apmixed_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +#define GATE_INFRA(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_TOP0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &top0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +#define GATE_TOP1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &top1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static DEFINE_SPINLOCK(mt7622_clk_lock); + +static const char * const infra_mux1_parents[] = { + "clkxtal", + "armpll", + "main_core_en", + "armpll" +}; + +static const char * const axi_parents[] = { + "clkxtal", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "univpll_d7" +}; + +static const char * const mem_parents[] = { + "clkxtal", + "dmpll_ck" +}; + +static const char * const ddrphycfg_parents[] = { + "clkxtal", + "syspll1_d8" +}; + +static const char * const eth_parents[] = { + "clkxtal", + "syspll1_d2", + "univpll1_d2", + "syspll1_d4", + "univpll_d5", + "clk_null", + "univpll_d7" +}; + +static const char * const pwm_parents[] = { + "clkxtal", + "univpll2_d4" +}; + +static const char * const f10m_ref_parents[] = { + "clkxtal", + "syspll4_d16" +}; + +static const char * const nfi_infra_parents[] = { + "clkxtal", + "clkxtal", + "clkxtal", + "clkxtal", + "clkxtal", + "clkxtal", + "clkxtal", + "clkxtal", + "univpll2_d8", + "syspll1_d8", + "univpll1_d8", + "syspll4_d2", + "univpll2_d4", + "univpll3_d2", + "syspll1_d4" +}; + +static const char * const flash_parents[] = { + "clkxtal", + "univpll_d80_d4", + "syspll2_d8", + "syspll3_d4", + "univpll3_d4", + "univpll1_d8", + "syspll2_d4", + "univpll2_d4" +}; + +static const char * const uart_parents[] = { + "clkxtal", + "univpll2_d8" +}; + +static const char * const spi0_parents[] = { + "clkxtal", + "syspll3_d2", + "clkxtal", + "syspll2_d4", + "syspll4_d2", + "univpll2_d4", + "univpll1_d8", + "clkxtal" +}; + +static const char * const spi1_parents[] = { + "clkxtal", + "syspll3_d2", + "clkxtal", + "syspll4_d4", + "syspll4_d2", + "univpll2_d4", + "univpll1_d8", + "clkxtal" +}; + +static const char * const msdc30_0_parents[] = { + "clkxtal", + "univpll2_d16", + "univ48m" +}; + +static const char * const a1sys_hp_parents[] = { + "clkxtal", + "aud1pll_ck", + "aud2pll_ck", + "clkxtal" +}; + +static const char * const intdir_parents[] = { + "clkxtal", + "syspll_d2", + "univpll_d2", + "sgmiipll_ck" +}; + +static const char * const aud_intbus_parents[] = { + "clkxtal", + "syspll1_d4", + "syspll4_d2", + "syspll3_d2" +}; + +static const char * const pmicspi_parents[] = { + "clkxtal", + "clk_null", + "clk_null", + "clk_null", + "clk_null", + "univpll2_d16" +}; + +static const char * const atb_parents[] = { + "clkxtal", + "syspll1_d2", + "syspll_d5" +}; + +static const char * const audio_parents[] = { + "clkxtal", + "syspll3_d4", + "syspll4_d4", + "univpll1_d16" +}; + +static const char * const usb20_parents[] = { + "clkxtal", + "univpll3_d4", + "syspll1_d8", + "clkxtal" +}; + +static const char * const aud1_parents[] = { + "clkxtal", + "aud1pll_ck" +}; + +static const char * const aud2_parents[] = { + "clkxtal", + "aud2pll_ck" +}; + +static const char * const asm_l_parents[] = { + "clkxtal", + "syspll_d5", + "univpll2_d2", + "univpll2_d4" +}; + +static const char * const apll1_ck_parents[] = { + "aud1_sel", + "aud2_sel" +}; + +static const char * const peribus_ck_parents[] = { + "syspll1_d8", + "syspll1_d4" +}; + +static const struct mtk_gate_regs apmixed_cg_regs = { + .set_ofs = 0x8, + .clr_ofs = 0x8, + .sta_ofs = 0x8, +}; + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x40, + .clr_ofs = 0x44, + .sta_ofs = 0x48, +}; + +static const struct mtk_gate_regs top0_cg_regs = { + .set_ofs = 0x120, + .clr_ofs = 0x120, + .sta_ofs = 0x120, +}; + +static const struct mtk_gate_regs top1_cg_regs = { + .set_ofs = 0x128, + .clr_ofs = 0x128, + .sta_ofs = 0x128, +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x8, + .clr_ofs = 0x10, + .sta_ofs = 0x18, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0xC, + .clr_ofs = 0x14, + .sta_ofs = 0x1C, +}; + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0x00000001, + PLL_AO, 21, 0x0204, 24, 0, 0x0204, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0210, 0x021C, 0x00000001, + HAVE_RST_BAR, 21, 0x0214, 24, 0, 0x0214, 0), + PLL(CLK_APMIXED_UNIV2PLL, "univ2pll", 0x0220, 0x022C, 0x00000001, + HAVE_RST_BAR, 7, 0x0224, 24, 0, 0x0224, 14), + PLL(CLK_APMIXED_ETH1PLL, "eth1pll", 0x0300, 0x0310, 0x00000001, + 0, 21, 0x0300, 1, 0, 0x0304, 0), + PLL(CLK_APMIXED_ETH2PLL, "eth2pll", 0x0314, 0x0320, 0x00000001, + 0, 21, 0x0314, 1, 0, 0x0318, 0), + PLL(CLK_APMIXED_AUD1PLL, "aud1pll", 0x0324, 0x0330, 0x00000001, + 0, 31, 0x0324, 1, 0, 0x0328, 0), + PLL(CLK_APMIXED_AUD2PLL, "aud2pll", 0x0334, 0x0340, 0x00000001, + 0, 31, 0x0334, 1, 0, 0x0338, 0), + PLL(CLK_APMIXED_TRGPLL, "trgpll", 0x0344, 0x0354, 0x00000001, + 0, 21, 0x0344, 1, 0, 0x0348, 0), + PLL(CLK_APMIXED_SGMIPLL, "sgmipll", 0x0358, 0x0368, 0x00000001, + 0, 21, 0x0358, 1, 0, 0x035C, 0), +}; + +static const struct mtk_gate apmixed_clks[] = { + GATE_APMIXED(CLK_APMIXED_MAIN_CORE_EN, "main_core_en", "mainpll", 5), +}; + +static const struct mtk_gate infra_clks[] = { + GATE_INFRA(CLK_INFRA_DBGCLK_PD, "infra_dbgclk_pd", "axi_sel", 0), + GATE_INFRA(CLK_INFRA_TRNG, "trng_ck", "axi_sel", 2), + GATE_INFRA(CLK_INFRA_AUDIO_PD, "infra_audio_pd", "aud_intbus_sel", 5), + GATE_INFRA(CLK_INFRA_IRRX_PD, "infra_irrx_pd", "irrx_sel", 16), + GATE_INFRA(CLK_INFRA_APXGPT_PD, "infra_apxgpt_pd", "f10m_ref_sel", 18), + GATE_INFRA(CLK_INFRA_PMIC_PD, "infra_pmic_pd", "pmicspi_sel", 22), +}; + +static const struct mtk_fixed_clk top_fixed_clks[] = { + FIXED_CLK(CLK_TOP_TO_U2_PHY, "to_u2_phy", "clkxtal", + 31250000), + FIXED_CLK(CLK_TOP_TO_U2_PHY_1P, "to_u2_phy_1p", "clkxtal", + 31250000), + FIXED_CLK(CLK_TOP_PCIE0_PIPE_EN, "pcie0_pipe_en", "clkxtal", + 125000000), + FIXED_CLK(CLK_TOP_PCIE1_PIPE_EN, "pcie1_pipe_en", "clkxtal", + 125000000), + FIXED_CLK(CLK_TOP_SSUSB_TX250M, "ssusb_tx250m", "clkxtal", + 250000000), + FIXED_CLK(CLK_TOP_SSUSB_EQ_RX250M, "ssusb_eq_rx250m", "clkxtal", + 250000000), + FIXED_CLK(CLK_TOP_SSUSB_CDR_REF, "ssusb_cdr_ref", "clkxtal", + 33333333), + FIXED_CLK(CLK_TOP_SSUSB_CDR_FB, "ssusb_cdr_fb", "clkxtal", + 50000000), + FIXED_CLK(CLK_TOP_SATA_ASIC, "sata_asic", "clkxtal", + 50000000), + FIXED_CLK(CLK_TOP_SATA_RBC, "sata_rbc", "clkxtal", + 50000000), +}; + +static const struct mtk_fixed_factor top_divs[] = { + FACTOR(CLK_TOP_TO_USB3_SYS, "to_usb3_sys", "eth1pll", 1, 4), + FACTOR(CLK_TOP_P1_1MHZ, "p1_1mhz", "eth1pll", 1, 500), + FACTOR(CLK_TOP_4MHZ, "free_run_4mhz", "eth1pll", 1, 125), + FACTOR(CLK_TOP_P0_1MHZ, "p0_1mhz", "eth1pll", 1, 500), + FACTOR(CLK_TOP_TXCLK_SRC_PRE, "txclk_src_pre", "sgmiipll_d2", 1, 1), + FACTOR(CLK_TOP_RTC, "rtc", "clkxtal", 1, 1024), + FACTOR(CLK_TOP_MEMPLL, "mempll", "clkxtal", 32, 1), + FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "mempll", 1, 1), + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2), + FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "mainpll", 1, 4), + FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "mainpll", 1, 8), + FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "mainpll", 1, 16), + FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "mainpll", 1, 12), + FACTOR(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "mainpll", 1, 24), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5), + FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "mainpll", 1, 10), + FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "mainpll", 1, 20), + FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "mainpll", 1, 14), + FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "mainpll", 1, 28), + FACTOR(CLK_TOP_SYSPLL4_D16, "syspll4_d16", "mainpll", 1, 112), + FACTOR(CLK_TOP_UNIVPLL, "univpll", "univ2pll", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll", 1, 8), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll", 1, 16), + FACTOR(CLK_TOP_UNIVPLL1_D16, "univpll1_d16", "univpll", 1, 32), + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll", 1, 6), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll", 1, 12), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll", 1, 24), + FACTOR(CLK_TOP_UNIVPLL2_D16, "univpll2_d16", "univpll", 1, 48), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll", 1, 10), + FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll", 1, 20), + FACTOR(CLK_TOP_UNIVPLL3_D16, "univpll3_d16", "univpll", 1, 80), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIVPLL_D80_D4, "univpll_d80_d4", "univpll", 1, 320), + FACTOR(CLK_TOP_UNIV48M, "univ48m", "univpll", 1, 25), + FACTOR(CLK_TOP_SGMIIPLL, "sgmiipll_ck", "sgmipll", 1, 1), + FACTOR(CLK_TOP_SGMIIPLL_D2, "sgmiipll_d2", "sgmipll", 1, 2), + FACTOR(CLK_TOP_AUD1PLL, "aud1pll_ck", "aud1pll", 1, 1), + FACTOR(CLK_TOP_AUD2PLL, "aud2pll_ck", "aud2pll", 1, 1), + FACTOR(CLK_TOP_AUD_I2S2_MCK, "aud_i2s2_mck", "i2s2_mck_sel", 1, 2), + FACTOR(CLK_TOP_TO_USB3_REF, "to_usb3_ref", "univpll2_d4", 1, 4), + FACTOR(CLK_TOP_PCIE1_MAC_EN, "pcie1_mac_en", "univpll1_d4", 1, 1), + FACTOR(CLK_TOP_PCIE0_MAC_EN, "pcie0_mac_en", "univpll1_d4", 1, 1), + FACTOR(CLK_TOP_ETH_500M, "eth_500m", "eth1pll", 1, 1), +}; + +static const struct mtk_gate top_clks[] = { + /* TOP0 */ + GATE_TOP0(CLK_TOP_APLL1_DIV_PD, "apll1_ck_div_pd", "apll1_ck_div", 0), + GATE_TOP0(CLK_TOP_APLL2_DIV_PD, "apll2_ck_div_pd", "apll2_ck_div", 1), + GATE_TOP0(CLK_TOP_I2S0_MCK_DIV_PD, "i2s0_mck_div_pd", "i2s0_mck_div", + 2), + GATE_TOP0(CLK_TOP_I2S1_MCK_DIV_PD, "i2s1_mck_div_pd", "i2s1_mck_div", + 3), + GATE_TOP0(CLK_TOP_I2S2_MCK_DIV_PD, "i2s2_mck_div_pd", "i2s2_mck_div", + 4), + GATE_TOP0(CLK_TOP_I2S3_MCK_DIV_PD, "i2s3_mck_div_pd", "i2s3_mck_div", + 5), + + /* TOP1 */ + GATE_TOP1(CLK_TOP_A1SYS_HP_DIV_PD, "a1sys_div_pd", "a1sys_div", 0), + GATE_TOP1(CLK_TOP_A2SYS_HP_DIV_PD, "a2sys_div_pd", "a2sys_div", 16), +}; + +static const struct mtk_clk_divider top_adj_divs[] = { + DIV_ADJ(CLK_TOP_APLL1_DIV, "apll1_ck_div", "apll1_ck_sel", + 0x120, 24, 3), + DIV_ADJ(CLK_TOP_APLL2_DIV, "apll2_ck_div", "apll2_ck_sel", + 0x120, 28, 3), + DIV_ADJ(CLK_TOP_I2S0_MCK_DIV, "i2s0_mck_div", "i2s0_mck_sel", + 0x124, 0, 7), + DIV_ADJ(CLK_TOP_I2S1_MCK_DIV, "i2s1_mck_div", "i2s1_mck_sel", + 0x124, 8, 7), + DIV_ADJ(CLK_TOP_I2S2_MCK_DIV, "i2s2_mck_div", "aud_i2s2_mck", + 0x124, 16, 7), + DIV_ADJ(CLK_TOP_I2S3_MCK_DIV, "i2s3_mck_div", "i2s3_mck_sel", + 0x124, 24, 7), + DIV_ADJ(CLK_TOP_A1SYS_HP_DIV, "a1sys_div", "a1sys_hp_sel", + 0x128, 8, 7), + DIV_ADJ(CLK_TOP_A2SYS_HP_DIV, "a2sys_div", "a2sys_hp_sel", + 0x128, 24, 7), +}; + +static const struct mtk_gate peri_clks[] = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_THERM_PD, "peri_therm_pd", "axi_sel", 1), + GATE_PERI0(CLK_PERI_PWM1_PD, "peri_pwm1_pd", "clkxtal", 2), + GATE_PERI0(CLK_PERI_PWM2_PD, "peri_pwm2_pd", "clkxtal", 3), + GATE_PERI0(CLK_PERI_PWM3_PD, "peri_pwm3_pd", "clkxtal", 4), + GATE_PERI0(CLK_PERI_PWM4_PD, "peri_pwm4_pd", "clkxtal", 5), + GATE_PERI0(CLK_PERI_PWM5_PD, "peri_pwm5_pd", "clkxtal", 6), + GATE_PERI0(CLK_PERI_PWM6_PD, "peri_pwm6_pd", "clkxtal", 7), + GATE_PERI0(CLK_PERI_PWM7_PD, "peri_pwm7_pd", "clkxtal", 8), + GATE_PERI0(CLK_PERI_PWM_PD, "peri_pwm_pd", "clkxtal", 9), + GATE_PERI0(CLK_PERI_AP_DMA_PD, "peri_ap_dma_pd", "axi_sel", 12), + GATE_PERI0(CLK_PERI_MSDC30_0_PD, "peri_msdc30_0", "msdc30_0_sel", 13), + GATE_PERI0(CLK_PERI_MSDC30_1_PD, "peri_msdc30_1", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_UART0_PD, "peri_uart0_pd", "axi_sel", 17), + GATE_PERI0(CLK_PERI_UART1_PD, "peri_uart1_pd", "axi_sel", 18), + GATE_PERI0(CLK_PERI_UART2_PD, "peri_uart2_pd", "axi_sel", 19), + GATE_PERI0(CLK_PERI_UART3_PD, "peri_uart3_pd", "axi_sel", 20), + GATE_PERI0(CLK_PERI_UART4_PD, "peri_uart4_pd", "axi_sel", 21), + GATE_PERI0(CLK_PERI_BTIF_PD, "peri_btif_pd", "axi_sel", 22), + GATE_PERI0(CLK_PERI_I2C0_PD, "peri_i2c0_pd", "axi_sel", 23), + GATE_PERI0(CLK_PERI_I2C1_PD, "peri_i2c1_pd", "axi_sel", 24), + GATE_PERI0(CLK_PERI_I2C2_PD, "peri_i2c2_pd", "axi_sel", 25), + GATE_PERI0(CLK_PERI_SPI1_PD, "peri_spi1_pd", "spi1_sel", 26), + GATE_PERI0(CLK_PERI_AUXADC_PD, "peri_auxadc_pd", "clkxtal", 27), + GATE_PERI0(CLK_PERI_SPI0_PD, "peri_spi0_pd", "spi0_sel", 28), + GATE_PERI0(CLK_PERI_SNFI_PD, "peri_snfi_pd", "nfi_infra_sel", 29), + GATE_PERI0(CLK_PERI_NFI_PD, "peri_nfi_pd", "axi_sel", 30), + GATE_PERI0(CLK_PERI_NFIECC_PD, "peri_nfiecc_pd", "axi_sel", 31), + + /* PERI1 */ + GATE_PERI1(CLK_PERI_FLASH_PD, "peri_flash_pd", "flash_sel", 1), + GATE_PERI1(CLK_PERI_IRTX_PD, "peri_irtx_pd", "irtx_sel", 2), +}; + +static struct mtk_composite infra_muxes[] __initdata = { + MUX(CLK_INFRA_MUX1_SEL, "infra_mux1_sel", infra_mux1_parents, + 0x000, 2, 2), +}; + +static struct mtk_composite top_muxes[] = { + /* CLK_CFG_0 */ + MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, + 0x040, 0, 3, 7), + MUX_GATE(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, + 0x040, 8, 1, 15), + MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, + 0x040, 16, 1, 23), + MUX_GATE(CLK_TOP_ETH_SEL, "eth_sel", eth_parents, + 0x040, 24, 3, 31), + + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, + 0x050, 0, 2, 7), + MUX_GATE(CLK_TOP_F10M_REF_SEL, "f10m_ref_sel", f10m_ref_parents, + 0x050, 8, 1, 15), + MUX_GATE(CLK_TOP_NFI_INFRA_SEL, "nfi_infra_sel", nfi_infra_parents, + 0x050, 16, 4, 23), + MUX_GATE(CLK_TOP_FLASH_SEL, "flash_sel", flash_parents, + 0x050, 24, 3, 31), + + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, + 0x060, 0, 1, 7), + MUX_GATE(CLK_TOP_SPI0_SEL, "spi0_sel", spi0_parents, + 0x060, 8, 3, 15), + MUX_GATE(CLK_TOP_SPI1_SEL, "spi1_sel", spi1_parents, + 0x060, 16, 3, 23), + MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", uart_parents, + 0x060, 24, 3, 31), + + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_0_parents, + 0x070, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_0_parents, + 0x070, 8, 3, 15), + MUX_GATE(CLK_TOP_A1SYS_HP_SEL, "a1sys_hp_sel", a1sys_hp_parents, + 0x070, 16, 2, 23), + MUX_GATE(CLK_TOP_A2SYS_HP_SEL, "a2sys_hp_sel", a1sys_hp_parents, + 0x070, 24, 2, 31), + + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_INTDIR_SEL, "intdir_sel", intdir_parents, + 0x080, 0, 2, 7), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, + 0x080, 8, 2, 15), + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, + 0x080, 16, 3, 23), + MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", ddrphycfg_parents, + 0x080, 24, 2, 31), + + /* CLK_CFG_5 */ + MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, + 0x090, 0, 2, 7), + MUX_GATE(CLK_TOP_HIF_SEL, "hif_sel", eth_parents, + 0x090, 8, 3, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, + 0x090, 16, 2, 23), + MUX_GATE(CLK_TOP_U2_SEL, "usb20_sel", usb20_parents, + 0x090, 24, 2, 31), + + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_AUD1_SEL, "aud1_sel", aud1_parents, + 0x0A0, 0, 1, 7), + MUX_GATE(CLK_TOP_AUD2_SEL, "aud2_sel", aud2_parents, + 0x0A0, 8, 1, 15), + MUX_GATE(CLK_TOP_IRRX_SEL, "irrx_sel", f10m_ref_parents, + 0x0A0, 16, 1, 23), + MUX_GATE(CLK_TOP_IRTX_SEL, "irtx_sel", f10m_ref_parents, + 0x0A0, 24, 1, 31), + + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_ASM_L_SEL, "asm_l_sel", asm_l_parents, + 0x0B0, 0, 2, 7), + MUX_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel", asm_l_parents, + 0x0B0, 8, 2, 15), + MUX_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel", asm_l_parents, + 0x0B0, 16, 2, 23), + + /* CLK_AUDDIV_0 */ + MUX(CLK_TOP_APLL1_SEL, "apll1_ck_sel", apll1_ck_parents, + 0x120, 6, 1), + MUX(CLK_TOP_APLL2_SEL, "apll2_ck_sel", apll1_ck_parents, + 0x120, 7, 1), + MUX(CLK_TOP_I2S0_MCK_SEL, "i2s0_mck_sel", apll1_ck_parents, + 0x120, 8, 1), + MUX(CLK_TOP_I2S1_MCK_SEL, "i2s1_mck_sel", apll1_ck_parents, + 0x120, 9, 1), + MUX(CLK_TOP_I2S2_MCK_SEL, "i2s2_mck_sel", apll1_ck_parents, + 0x120, 10, 1), + MUX(CLK_TOP_I2S3_MCK_SEL, "i2s3_mck_sel", apll1_ck_parents, + 0x120, 11, 1), +}; + +static struct mtk_composite peri_muxes[] = { + /* PERI_GLOBALCON_CKSEL */ + MUX(CLK_PERIBUS_SEL, "peribus_ck_sel", peribus_ck_parents, 0x05C, 0, 1), +}; + +static int mtk_topckgen_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + struct device_node *node = pdev->dev.of_node; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), + clk_data); + + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), + clk_data); + + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), + base, &mt7622_clk_lock, clk_data); + + mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs), + base, &mt7622_clk_lock, clk_data); + + mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks), + clk_data); + + clk_prepare_enable(clk_data->clks[CLK_TOP_AXI_SEL]); + clk_prepare_enable(clk_data->clks[CLK_TOP_MEM_SEL]); + clk_prepare_enable(clk_data->clks[CLK_TOP_DDRPHYCFG_SEL]); + + return of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +static int __init mtk_infrasys_init(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + mtk_clk_register_cpumuxes(node, infra_muxes, ARRAY_SIZE(infra_muxes), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, + clk_data); + if (r) + return r; + + mtk_register_reset_controller(node, 1, 0x30); + + return 0; +} + +static int mtk_apmixedsys_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + if (!clk_data) + return -ENOMEM; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), + clk_data); + + mtk_clk_register_gates(node, apmixed_clks, + ARRAY_SIZE(apmixed_clks), clk_data); + + clk_prepare_enable(clk_data->clks[CLK_APMIXED_ARMPLL]); + clk_prepare_enable(clk_data->clks[CLK_APMIXED_MAIN_CORE_EN]); + + return of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +static int mtk_pericfg_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + struct device_node *node = pdev->dev.of_node; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), + clk_data); + + mtk_clk_register_composites(peri_muxes, ARRAY_SIZE(peri_muxes), base, + &mt7622_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + return r; + + clk_prepare_enable(clk_data->clks[CLK_PERI_UART0_PD]); + + mtk_register_reset_controller(node, 2, 0x0); + + return 0; +} + +static const struct of_device_id of_match_clk_mt7622[] = { + { + .compatible = "mediatek,mt7622-apmixedsys", + .data = mtk_apmixedsys_init, + }, { + .compatible = "mediatek,mt7622-infracfg", + .data = mtk_infrasys_init, + }, { + .compatible = "mediatek,mt7622-topckgen", + .data = mtk_topckgen_init, + }, { + .compatible = "mediatek,mt7622-pericfg", + .data = mtk_pericfg_init, + }, { + /* sentinel */ + } +}; + +static int clk_mt7622_probe(struct platform_device *pdev) +{ + int (*clk_init)(struct platform_device *); + int r; + + clk_init = of_device_get_match_data(&pdev->dev); + if (!clk_init) + return -EINVAL; + + r = clk_init(pdev); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt7622_drv = { + .probe = clk_mt7622_probe, + .driver = { + .name = "clk-mt7622", + .of_match_table = of_match_clk_mt7622, + }, +}; + +static int clk_mt7622_init(void) +{ + return platform_driver_register(&clk_mt7622_drv); +} + +arch_initcall(clk_mt7622_init); diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index f5d6b70ce189..f10250dcece4 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -207,6 +207,8 @@ struct mtk_pll_data { uint32_t en_mask; uint32_t pd_reg; uint32_t tuner_reg; + uint32_t tuner_en_reg; + uint8_t tuner_en_bit; int pd_shift; unsigned int flags; const struct clk_ops *ops; @@ -216,6 +218,7 @@ struct mtk_pll_data { uint32_t pcw_reg; int pcw_shift; const struct mtk_pll_div_table *div_table; + const char *parent_name; }; void mtk_clk_register_plls(struct device_node *node, diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c index a409142e9346..f54e4015b0b1 100644 --- a/drivers/clk/mediatek/clk-pll.c +++ b/drivers/clk/mediatek/clk-pll.c @@ -47,6 +47,7 @@ struct mtk_clk_pll { void __iomem *pd_addr; void __iomem *pwr_addr; void __iomem *tuner_addr; + void __iomem *tuner_en_addr; void __iomem *pcw_addr; const struct mtk_pll_data *data; }; @@ -227,7 +228,10 @@ static int mtk_pll_prepare(struct clk_hw *hw) r |= pll->data->en_mask; writel(r, pll->base_addr + REG_CON0); - if (pll->tuner_addr) { + if (pll->tuner_en_addr) { + r = readl(pll->tuner_en_addr) | BIT(pll->data->tuner_en_bit); + writel(r, pll->tuner_en_addr); + } else if (pll->tuner_addr) { r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; writel(r, pll->tuner_addr); } @@ -254,7 +258,10 @@ static void mtk_pll_unprepare(struct clk_hw *hw) writel(r, pll->base_addr + REG_CON0); } - if (pll->tuner_addr) { + if (pll->tuner_en_addr) { + r = readl(pll->tuner_en_addr) & ~BIT(pll->data->tuner_en_bit); + writel(r, pll->tuner_en_addr); + } else if (pll->tuner_addr) { r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; writel(r, pll->tuner_addr); } @@ -297,13 +304,18 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, pll->pcw_addr = base + data->pcw_reg; if (data->tuner_reg) pll->tuner_addr = base + data->tuner_reg; + if (data->tuner_en_reg) + pll->tuner_en_addr = base + data->tuner_en_reg; pll->hw.init = &init; pll->data = data; init.name = data->name; init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0; init.ops = &mtk_pll_ops; - init.parent_names = &parent_name; + if (data->parent_name) + init.parent_names = &data->parent_name; + else + init.parent_names = &parent_name; init.num_parents = 1; clk = clk_register(NULL, &pll->hw); diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index b2d1e8ed7152..ae385310e980 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -1131,6 +1131,253 @@ static struct clk_gate gxbb_sd_emmc_c_clk0 = { }, }; +/* VPU Clock */ + +static u32 mux_table_vpu[] = {0, 1, 2, 3}; +static const char * const gxbb_vpu_parent_names[] = { + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7" +}; + +static struct clk_mux gxbb_vpu_0_sel = { + .reg = (void *)HHI_VPU_CLK_CNTL, + .mask = 0x3, + .shift = 9, + .lock = &clk_lock, + .table = mux_table_vpu, + .hw.init = &(struct clk_init_data){ + .name = "vpu_0_sel", + .ops = &clk_mux_ops, + /* + * bits 9:10 selects from 4 possible parents: + * fclk_div4, fclk_div3, fclk_div5, fclk_div7, + */ + .parent_names = gxbb_vpu_parent_names, + .num_parents = ARRAY_SIZE(gxbb_vpu_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_divider gxbb_vpu_0_div = { + .reg = (void *)HHI_VPU_CLK_CNTL, + .shift = 0, + .width = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "vpu_0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "vpu_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_vpu_0 = { + .reg = (void *)HHI_VPU_CLK_CNTL, + .bit_idx = 8, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "vpu_0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "vpu_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_mux gxbb_vpu_1_sel = { + .reg = (void *)HHI_VPU_CLK_CNTL, + .mask = 0x3, + .shift = 25, + .lock = &clk_lock, + .table = mux_table_vpu, + .hw.init = &(struct clk_init_data){ + .name = "vpu_1_sel", + .ops = &clk_mux_ops, + /* + * bits 25:26 selects from 4 possible parents: + * fclk_div4, fclk_div3, fclk_div5, fclk_div7, + */ + .parent_names = gxbb_vpu_parent_names, + .num_parents = ARRAY_SIZE(gxbb_vpu_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_divider gxbb_vpu_1_div = { + .reg = (void *)HHI_VPU_CLK_CNTL, + .shift = 16, + .width = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "vpu_1_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "vpu_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_vpu_1 = { + .reg = (void *)HHI_VPU_CLK_CNTL, + .bit_idx = 24, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "vpu_1", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "vpu_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_mux gxbb_vpu = { + .reg = (void *)HHI_VPU_CLK_CNTL, + .mask = 1, + .shift = 31, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "vpu", + .ops = &clk_mux_ops, + /* + * bit 31 selects from 2 possible parents: + * vpu_0 or vpu_1 + */ + .parent_names = (const char *[]){ "vpu_0", "vpu_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +/* VAPB Clock */ + +static u32 mux_table_vapb[] = {0, 1, 2, 3}; +static const char * const gxbb_vapb_parent_names[] = { + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7" +}; + +static struct clk_mux gxbb_vapb_0_sel = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .mask = 0x3, + .shift = 9, + .lock = &clk_lock, + .table = mux_table_vapb, + .hw.init = &(struct clk_init_data){ + .name = "vapb_0_sel", + .ops = &clk_mux_ops, + /* + * bits 9:10 selects from 4 possible parents: + * fclk_div4, fclk_div3, fclk_div5, fclk_div7, + */ + .parent_names = gxbb_vapb_parent_names, + .num_parents = ARRAY_SIZE(gxbb_vapb_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_divider gxbb_vapb_0_div = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .shift = 0, + .width = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "vapb_0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "vapb_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_vapb_0 = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .bit_idx = 8, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "vapb_0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "vapb_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_mux gxbb_vapb_1_sel = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .mask = 0x3, + .shift = 25, + .lock = &clk_lock, + .table = mux_table_vapb, + .hw.init = &(struct clk_init_data){ + .name = "vapb_1_sel", + .ops = &clk_mux_ops, + /* + * bits 25:26 selects from 4 possible parents: + * fclk_div4, fclk_div3, fclk_div5, fclk_div7, + */ + .parent_names = gxbb_vapb_parent_names, + .num_parents = ARRAY_SIZE(gxbb_vapb_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_divider gxbb_vapb_1_div = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .shift = 16, + .width = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "vapb_1_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "vapb_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_vapb_1 = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .bit_idx = 24, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "vapb_1", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "vapb_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_mux gxbb_vapb_sel = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .mask = 1, + .shift = 31, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "vapb_sel", + .ops = &clk_mux_ops, + /* + * bit 31 selects from 2 possible parents: + * vapb_0 or vapb_1 + */ + .parent_names = (const char *[]){ "vapb_0", "vapb_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_gate gxbb_vapb = { + .reg = (void *)HHI_VAPBCLK_CNTL, + .bit_idx = 30, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "vapb", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "vapb_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0); static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1); @@ -1349,6 +1596,21 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw, [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw, [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw, + [CLKID_VPU_0_SEL] = &gxbb_vpu_0_sel.hw, + [CLKID_VPU_0_DIV] = &gxbb_vpu_0_div.hw, + [CLKID_VPU_0] = &gxbb_vpu_0.hw, + [CLKID_VPU_1_SEL] = &gxbb_vpu_1_sel.hw, + [CLKID_VPU_1_DIV] = &gxbb_vpu_1_div.hw, + [CLKID_VPU_1] = &gxbb_vpu_1.hw, + [CLKID_VPU] = &gxbb_vpu.hw, + [CLKID_VAPB_0_SEL] = &gxbb_vapb_0_sel.hw, + [CLKID_VAPB_0_DIV] = &gxbb_vapb_0_div.hw, + [CLKID_VAPB_0] = &gxbb_vapb_0.hw, + [CLKID_VAPB_1_SEL] = &gxbb_vapb_1_sel.hw, + [CLKID_VAPB_1_DIV] = &gxbb_vapb_1_div.hw, + [CLKID_VAPB_1] = &gxbb_vapb_1.hw, + [CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw, + [CLKID_VAPB] = &gxbb_vapb.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -1481,6 +1743,21 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = { [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw, [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw, [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw, + [CLKID_VPU_0_SEL] = &gxbb_vpu_0_sel.hw, + [CLKID_VPU_0_DIV] = &gxbb_vpu_0_div.hw, + [CLKID_VPU_0] = &gxbb_vpu_0.hw, + [CLKID_VPU_1_SEL] = &gxbb_vpu_1_sel.hw, + [CLKID_VPU_1_DIV] = &gxbb_vpu_1_div.hw, + [CLKID_VPU_1] = &gxbb_vpu_1.hw, + [CLKID_VPU] = &gxbb_vpu.hw, + [CLKID_VAPB_0_SEL] = &gxbb_vapb_0_sel.hw, + [CLKID_VAPB_0_DIV] = &gxbb_vapb_0_div.hw, + [CLKID_VAPB_0] = &gxbb_vapb_0.hw, + [CLKID_VAPB_1_SEL] = &gxbb_vapb_1_sel.hw, + [CLKID_VAPB_1_DIV] = &gxbb_vapb_1_div.hw, + [CLKID_VAPB_1] = &gxbb_vapb_1.hw, + [CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw, + [CLKID_VAPB] = &gxbb_vapb.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -1600,6 +1877,11 @@ static struct clk_gate *const gxbb_clk_gates[] = { &gxbb_sd_emmc_a_clk0, &gxbb_sd_emmc_b_clk0, &gxbb_sd_emmc_c_clk0, + &gxbb_vpu_0, + &gxbb_vpu_1, + &gxbb_vapb_0, + &gxbb_vapb_1, + &gxbb_vapb, }; static struct clk_mux *const gxbb_clk_muxes[] = { @@ -1615,6 +1897,12 @@ static struct clk_mux *const gxbb_clk_muxes[] = { &gxbb_sd_emmc_a_clk0_sel, &gxbb_sd_emmc_b_clk0_sel, &gxbb_sd_emmc_c_clk0_sel, + &gxbb_vpu_0_sel, + &gxbb_vpu_1_sel, + &gxbb_vpu, + &gxbb_vapb_0_sel, + &gxbb_vapb_1_sel, + &gxbb_vapb_sel, }; static struct clk_divider *const gxbb_clk_dividers[] = { @@ -1627,6 +1915,10 @@ static struct clk_divider *const gxbb_clk_dividers[] = { &gxbb_sd_emmc_a_clk0_div, &gxbb_sd_emmc_b_clk0_div, &gxbb_sd_emmc_c_clk0_div, + &gxbb_vpu_0_div, + &gxbb_vpu_1_div, + &gxbb_vapb_0_div, + &gxbb_vapb_1_div, }; static struct meson_clk_audio_divider *const gxbb_audio_dividers[] = { diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index 5b1d4b374d1c..aee6fbba2004 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -190,8 +190,12 @@ #define CLKID_SD_EMMC_B_CLK0_DIV 121 #define CLKID_SD_EMMC_C_CLK0_SEL 123 #define CLKID_SD_EMMC_C_CLK0_DIV 124 +#define CLKID_VPU_0_DIV 127 +#define CLKID_VPU_1_DIV 130 +#define CLKID_VAPB_0_DIV 134 +#define CLKID_VAPB_1_DIV 137 -#define NR_CLKS 126 +#define NR_CLKS 141 /* include the CLKIDs that have been made part of the DT binding */ #include diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c index 4c717db05f2d..fb294ada0b03 100644 --- a/drivers/clk/mmp/clk-apbc.c +++ b/drivers/clk/mmp/clk-apbc.c @@ -114,7 +114,7 @@ static void clk_apbc_unprepare(struct clk_hw *hw) spin_unlock_irqrestore(apbc->lock, flags); } -static struct clk_ops clk_apbc_ops = { +static const struct clk_ops clk_apbc_ops = { .prepare = clk_apbc_prepare, .unprepare = clk_apbc_unprepare, }; diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c index 47b5542ce50f..b7ce8f52026e 100644 --- a/drivers/clk/mmp/clk-apmu.c +++ b/drivers/clk/mmp/clk-apmu.c @@ -60,7 +60,7 @@ static void clk_apmu_disable(struct clk_hw *hw) spin_unlock_irqrestore(apmu->lock, flags); } -static struct clk_ops clk_apmu_ops = { +static const struct clk_ops clk_apmu_ops = { .enable = clk_apmu_enable, .disable = clk_apmu_disable, }; diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c index 584a9927993b..cb43d54735b0 100644 --- a/drivers/clk/mmp/clk-frac.c +++ b/drivers/clk/mmp/clk-frac.c @@ -149,7 +149,7 @@ static void clk_factor_init(struct clk_hw *hw) spin_unlock_irqrestore(factor->lock, flags); } -static struct clk_ops clk_factor_ops = { +static const struct clk_ops clk_factor_ops = { .recalc_rate = clk_factor_recalc_rate, .round_rate = clk_factor_round_rate, .set_rate = clk_factor_set_rate, @@ -172,10 +172,8 @@ struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, } factor = kzalloc(sizeof(*factor), GFP_KERNEL); - if (!factor) { - pr_err("%s: could not allocate factor clk\n", __func__); + if (!factor) return ERR_PTR(-ENOMEM); - } /* struct clk_aux assignments */ factor->base = base; diff --git a/drivers/clk/mmp/clk-gate.c b/drivers/clk/mmp/clk-gate.c index d20cd3431ac2..7355595c42e2 100644 --- a/drivers/clk/mmp/clk-gate.c +++ b/drivers/clk/mmp/clk-gate.c @@ -103,10 +103,8 @@ struct clk *mmp_clk_register_gate(struct device *dev, const char *name, /* allocate the gate */ gate = kzalloc(sizeof(*gate), GFP_KERNEL); - if (!gate) { - pr_err("%s:%s could not allocate gate clk\n", __func__, name); + if (!gate) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &mmp_clk_gate_ops; diff --git a/drivers/clk/mmp/clk-mix.c b/drivers/clk/mmp/clk-mix.c index c554833cffc5..90814b2613c0 100644 --- a/drivers/clk/mmp/clk-mix.c +++ b/drivers/clk/mmp/clk-mix.c @@ -229,7 +229,7 @@ static int mmp_clk_mix_determine_rate(struct clk_hw *hw, parent_rate = clk_hw_get_rate(parent); mix_rate = parent_rate / item->divisor; gap = abs(mix_rate - req->rate); - if (parent_best == NULL || gap < gap_best) { + if (!parent_best || gap < gap_best) { parent_best = parent; parent_rate_best = parent_rate; mix_rate_best = mix_rate; @@ -247,7 +247,7 @@ static int mmp_clk_mix_determine_rate(struct clk_hw *hw, div = _get_div(mix, j); mix_rate = parent_rate / div; gap = abs(mix_rate - req->rate); - if (parent_best == NULL || gap < gap_best) { + if (!parent_best || gap < gap_best) { parent_best = parent; parent_rate_best = parent_rate; mix_rate_best = mix_rate; @@ -451,11 +451,8 @@ struct clk *mmp_clk_register_mix(struct device *dev, size_t table_bytes; mix = kzalloc(sizeof(*mix), GFP_KERNEL); - if (!mix) { - pr_err("%s:%s: could not allocate mmp mix clk\n", - __func__, name); + if (!mix) return ERR_PTR(-ENOMEM); - } init.name = name; init.flags = flags | CLK_GET_RATE_NOCACHE; @@ -467,12 +464,9 @@ struct clk *mmp_clk_register_mix(struct device *dev, if (config->table) { table_bytes = sizeof(*config->table) * config->table_size; mix->table = kmemdup(config->table, table_bytes, GFP_KERNEL); - if (!mix->table) { - pr_err("%s:%s: could not allocate mmp mix table\n", - __func__, name); - kfree(mix); - return ERR_PTR(-ENOMEM); - } + if (!mix->table) + goto free_mix; + mix->table_size = config->table_size; } @@ -481,11 +475,8 @@ struct clk *mmp_clk_register_mix(struct device *dev, mix->mux_table = kmemdup(config->mux_table, table_bytes, GFP_KERNEL); if (!mix->mux_table) { - pr_err("%s:%s: could not allocate mmp mix mux-table\n", - __func__, name); kfree(mix->table); - kfree(mix); - return ERR_PTR(-ENOMEM); + goto free_mix; } } @@ -509,4 +500,8 @@ struct clk *mmp_clk_register_mix(struct device *dev, } return clk; + +free_mix: + kfree(mix); + return ERR_PTR(-ENOMEM); } diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index 038023483b98..7460031714da 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -83,19 +83,19 @@ void __init mmp2_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys, void __iomem *apbc_base; mpmu_base = ioremap(mpmu_phys, SZ_4K); - if (mpmu_base == NULL) { + if (!mpmu_base) { pr_err("error to ioremap MPMU base\n"); return; } apmu_base = ioremap(apmu_phys, SZ_4K); - if (apmu_base == NULL) { + if (!apmu_base) { pr_err("error to ioremap APMU base\n"); return; } apbc_base = ioremap(apbc_phys, SZ_4K); - if (apbc_base == NULL) { + if (!apbc_base) { pr_err("error to ioremap APBC base\n"); return; } diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index a9ef9209532a..8e2551ab8462 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -75,19 +75,19 @@ void __init pxa168_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys, void __iomem *apbc_base; mpmu_base = ioremap(mpmu_phys, SZ_4K); - if (mpmu_base == NULL) { + if (!mpmu_base) { pr_err("error to ioremap MPMU base\n"); return; } apmu_base = ioremap(apmu_phys, SZ_4K); - if (apmu_base == NULL) { + if (!apmu_base) { pr_err("error to ioremap APMU base\n"); return; } apbc_base = ioremap(apbc_phys, SZ_4K); - if (apbc_base == NULL) { + if (!apbc_base) { pr_err("error to ioremap APBC base\n"); return; } diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index a520cf7702a1..7a7965141918 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -74,25 +74,25 @@ void __init pxa910_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys, void __iomem *apbc_base; mpmu_base = ioremap(mpmu_phys, SZ_4K); - if (mpmu_base == NULL) { + if (!mpmu_base) { pr_err("error to ioremap MPMU base\n"); return; } apmu_base = ioremap(apmu_phys, SZ_4K); - if (apmu_base == NULL) { + if (!apmu_base) { pr_err("error to ioremap APMU base\n"); return; } apbcp_base = ioremap(apbcp_phys, SZ_4K); - if (apbcp_base == NULL) { + if (!apbcp_base) { pr_err("error to ioremap APBC extension base\n"); return; } apbc_base = ioremap(apbc_phys, SZ_4K); - if (apbc_base == NULL) { + if (!apbc_base) { pr_err("error to ioremap APBC base\n"); return; } diff --git a/drivers/clk/mxs/clk-div.c b/drivers/clk/mxs/clk-div.c index f75e989c578f..ccebd014fc1e 100644 --- a/drivers/clk/mxs/clk-div.c +++ b/drivers/clk/mxs/clk-div.c @@ -67,7 +67,7 @@ static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate, return ret; } -static struct clk_ops clk_div_ops = { +static const struct clk_ops clk_div_ops = { .recalc_rate = clk_div_recalc_rate, .round_rate = clk_div_round_rate, .set_rate = clk_div_set_rate, diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c index f8dd10f6df3d..27b3372adc37 100644 --- a/drivers/clk/mxs/clk-frac.c +++ b/drivers/clk/mxs/clk-frac.c @@ -107,7 +107,7 @@ static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate, return mxs_clk_wait(frac->reg, frac->busy); } -static struct clk_ops clk_frac_ops = { +static const struct clk_ops clk_frac_ops = { .recalc_rate = clk_frac_recalc_rate, .round_rate = clk_frac_round_rate, .set_rate = clk_frac_set_rate, diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c index 74f64c3c4290..b80dc9d5855c 100644 --- a/drivers/clk/pxa/clk-pxa.c +++ b/drivers/clk/pxa/clk-pxa.c @@ -147,9 +147,7 @@ void pxa2xx_core_turbo_switch(bool on) " b 3f\n" "2: b 1b\n" "3: nop\n" - : "=&r" (unused) - : "r" (clkcfg) - : ); + : "=&r" (unused) : "r" (clkcfg)); local_irq_restore(flags); } diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index 1b3e8d265bdb..a2495457e564 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -156,7 +156,6 @@ extern const struct clk_ops clk_dyn_rcg_ops; * @hid_width: number of bits in half integer divider * @parent_map: map from software's parent index to hardware's src_sel field * @freq_tbl: frequency table - * @current_freq: last cached frequency when using branches with shared RCGs * @clkr: regmap clock handle * */ @@ -166,7 +165,6 @@ struct clk_rcg2 { u8 hid_width; const struct parent_map *parent_map; const struct freq_tbl *freq_tbl; - unsigned long current_freq; struct clk_regmap clkr; }; @@ -174,7 +172,6 @@ struct clk_rcg2 { extern const struct clk_ops clk_rcg2_ops; extern const struct clk_ops clk_rcg2_floor_ops; -extern const struct clk_ops clk_rcg2_shared_ops; extern const struct clk_ops clk_edp_pixel_ops; extern const struct clk_ops clk_byte_ops; extern const struct clk_ops clk_byte2_ops; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 1a0985ae20d2..bbeaf9c09dbb 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -358,85 +358,6 @@ const struct clk_ops clk_rcg2_floor_ops = { }; EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops); -static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate) -{ - struct clk_rcg2 *rcg = to_clk_rcg2(hw); - const char *name = clk_hw_get_name(hw); - int ret, count; - - /* force enable RCG */ - ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, - CMD_ROOT_EN, CMD_ROOT_EN); - if (ret) - return ret; - - /* wait for RCG to turn ON */ - for (count = 500; count > 0; count--) { - ret = clk_rcg2_is_enabled(hw); - if (ret) - break; - udelay(1); - } - if (!count) - pr_err("%s: RCG did not turn on\n", name); - - /* set clock rate */ - ret = __clk_rcg2_set_rate(hw, rate, CEIL); - if (ret) - return ret; - - /* clear force enable RCG */ - return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, - CMD_ROOT_EN, 0); -} - -static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct clk_rcg2 *rcg = to_clk_rcg2(hw); - - /* cache the rate */ - rcg->current_freq = rate; - - if (!__clk_is_enabled(hw->clk)) - return 0; - - return clk_rcg2_shared_force_enable(hw, rcg->current_freq); -} - -static unsigned long -clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - struct clk_rcg2 *rcg = to_clk_rcg2(hw); - - return rcg->current_freq = clk_rcg2_recalc_rate(hw, parent_rate); -} - -static int clk_rcg2_shared_enable(struct clk_hw *hw) -{ - struct clk_rcg2 *rcg = to_clk_rcg2(hw); - - return clk_rcg2_shared_force_enable(hw, rcg->current_freq); -} - -static void clk_rcg2_shared_disable(struct clk_hw *hw) -{ - struct clk_rcg2 *rcg = to_clk_rcg2(hw); - - /* switch to XO, which is the lowest entry in the freq table */ - clk_rcg2_shared_set_rate(hw, rcg->freq_tbl[0].freq, 0); -} - -const struct clk_ops clk_rcg2_shared_ops = { - .enable = clk_rcg2_shared_enable, - .disable = clk_rcg2_shared_disable, - .get_parent = clk_rcg2_get_parent, - .recalc_rate = clk_rcg2_shared_recalc_rate, - .determine_rate = clk_rcg2_determine_rate, - .set_rate = clk_rcg2_shared_set_rate, -}; -EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); - struct frac_entry { int num; int den; diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c index df3e5fe8442a..c60f61b10c7f 100644 --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -56,6 +56,18 @@ }, \ } +#define DEFINE_CLK_RPM_FIXED(_platform, _name, _active, r_id, r) \ + static struct clk_rpm _platform##_##_name = { \ + .rpm_clk_id = (r_id), \ + .rate = (r), \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_fixed_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "pxo" }, \ + .num_parents = 1, \ + }, \ + } + #define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ static struct clk_rpm _platform##_##_active; \ static struct clk_rpm _platform##_##_name = { \ @@ -143,6 +155,13 @@ static int clk_rpm_handoff(struct clk_rpm *r) int ret; u32 value = INT_MAX; + /* + * The vendor tree simply reads the status for this + * RPM clock. + */ + if (r->rpm_clk_id == QCOM_RPM_PLL_4) + return 0; + ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, r->rpm_clk_id, &value, 1); if (ret) @@ -269,6 +288,32 @@ out: mutex_unlock(&rpm_clk_lock); } +static int clk_rpm_fixed_prepare(struct clk_hw *hw) +{ + struct clk_rpm *r = to_clk_rpm(hw); + u32 value = 1; + int ret; + + ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, + r->rpm_clk_id, &value, 1); + if (!ret) + r->enabled = true; + + return ret; +} + +static void clk_rpm_fixed_unprepare(struct clk_hw *hw) +{ + struct clk_rpm *r = to_clk_rpm(hw); + u32 value = 0; + int ret; + + ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, + r->rpm_clk_id, &value, 1); + if (!ret) + r->enabled = false; +} + static int clk_rpm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -333,6 +378,13 @@ static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, return r->rate; } +static const struct clk_ops clk_rpm_fixed_ops = { + .prepare = clk_rpm_fixed_prepare, + .unprepare = clk_rpm_fixed_unprepare, + .round_rate = clk_rpm_round_rate, + .recalc_rate = clk_rpm_recalc_rate, +}; + static const struct clk_ops clk_rpm_ops = { .prepare = clk_rpm_prepare, .unprepare = clk_rpm_unprepare, @@ -348,6 +400,45 @@ static const struct clk_ops clk_rpm_branch_ops = { .recalc_rate = clk_rpm_recalc_rate, }; +/* MSM8660/APQ8060 */ +DEFINE_CLK_RPM(msm8660, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); +DEFINE_CLK_RPM(msm8660, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); +DEFINE_CLK_RPM(msm8660, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); +DEFINE_CLK_RPM(msm8660, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); +DEFINE_CLK_RPM(msm8660, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); +DEFINE_CLK_RPM(msm8660, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); +DEFINE_CLK_RPM(msm8660, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); +DEFINE_CLK_RPM(msm8660, smi_clk, smi_a_clk, QCOM_RPM_SMI_CLK); +DEFINE_CLK_RPM(msm8660, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); +DEFINE_CLK_RPM_FIXED(msm8660, pll4_clk, pll4_a_clk, QCOM_RPM_PLL_4, 540672000); + +static struct clk_rpm *msm8660_clks[] = { + [RPM_APPS_FABRIC_CLK] = &msm8660_afab_clk, + [RPM_APPS_FABRIC_A_CLK] = &msm8660_afab_a_clk, + [RPM_SYS_FABRIC_CLK] = &msm8660_sfab_clk, + [RPM_SYS_FABRIC_A_CLK] = &msm8660_sfab_a_clk, + [RPM_MM_FABRIC_CLK] = &msm8660_mmfab_clk, + [RPM_MM_FABRIC_A_CLK] = &msm8660_mmfab_a_clk, + [RPM_DAYTONA_FABRIC_CLK] = &msm8660_daytona_clk, + [RPM_DAYTONA_FABRIC_A_CLK] = &msm8660_daytona_a_clk, + [RPM_SFPB_CLK] = &msm8660_sfpb_clk, + [RPM_SFPB_A_CLK] = &msm8660_sfpb_a_clk, + [RPM_CFPB_CLK] = &msm8660_cfpb_clk, + [RPM_CFPB_A_CLK] = &msm8660_cfpb_a_clk, + [RPM_MMFPB_CLK] = &msm8660_mmfpb_clk, + [RPM_MMFPB_A_CLK] = &msm8660_mmfpb_a_clk, + [RPM_SMI_CLK] = &msm8660_smi_clk, + [RPM_SMI_A_CLK] = &msm8660_smi_a_clk, + [RPM_EBI1_CLK] = &msm8660_ebi1_clk, + [RPM_EBI1_A_CLK] = &msm8660_ebi1_a_clk, + [RPM_PLL4_CLK] = &msm8660_pll4_clk, +}; + +static const struct rpm_clk_desc rpm_clk_msm8660 = { + .clks = msm8660_clks, + .num_clks = ARRAY_SIZE(msm8660_clks), +}; + /* apq8064 */ DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); @@ -386,6 +477,8 @@ static const struct rpm_clk_desc rpm_clk_apq8064 = { }; static const struct of_device_id rpm_clk_match_table[] = { + { .compatible = "qcom,rpmcc-msm8660", .data = &rpm_clk_msm8660 }, + { .compatible = "qcom,rpmcc-apq8060", .data = &rpm_clk_msm8660 }, { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, { } }; diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index cc03d5508627..c26d9007bfc4 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -530,9 +530,91 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8974 = { .clks = msm8974_clks, .num_clks = ARRAY_SIZE(msm8974_clks), }; + +/* msm8996 */ +DEFINE_CLK_SMD_RPM(msm8996, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8996, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8996, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk, + QCOM_SMD_RPM_MMAXI_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM_BRANCH(msm8996, aggre1_noc_clk, aggre1_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 1, 1000); +DEFINE_CLK_SMD_RPM_BRANCH(msm8996, aggre2_noc_clk, aggre2_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 2, 1000); +DEFINE_CLK_SMD_RPM_QDSS(msm8996, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, ln_bb_clk, ln_bb_a_clk, 8); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, div_clk1, div_clk1_a, 0xb); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, div_clk2, div_clk2_a, 0xc); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, div_clk3, div_clk3_a, 0xd); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, bb_clk2_pin, bb_clk2_a_pin, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, rf_clk2_pin, rf_clk2_a_pin, 5); + +static struct clk_smd_rpm *msm8996_clks[] = { + [RPM_SMD_PCNOC_CLK] = &msm8996_pcnoc_clk, + [RPM_SMD_PCNOC_A_CLK] = &msm8996_pcnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8996_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8996_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8996_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8996_cnoc_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8996_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8996_bimc_a_clk, + [RPM_SMD_MMAXI_CLK] = &msm8996_mmssnoc_axi_rpm_clk, + [RPM_SMD_MMAXI_A_CLK] = &msm8996_mmssnoc_axi_rpm_a_clk, + [RPM_SMD_IPA_CLK] = &msm8996_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8996_ipa_a_clk, + [RPM_SMD_CE1_CLK] = &msm8996_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8996_ce1_a_clk, + [RPM_SMD_AGGR1_NOC_CLK] = &msm8996_aggre1_noc_clk, + [RPM_SMD_AGGR1_NOC_A_CLK] = &msm8996_aggre1_noc_a_clk, + [RPM_SMD_AGGR2_NOC_CLK] = &msm8996_aggre2_noc_clk, + [RPM_SMD_AGGR2_NOC_A_CLK] = &msm8996_aggre2_noc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8996_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8996_qdss_a_clk, + [RPM_SMD_BB_CLK1] = &msm8996_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8996_bb_clk1_a, + [RPM_SMD_BB_CLK2] = &msm8996_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8996_bb_clk2_a, + [RPM_SMD_RF_CLK1] = &msm8996_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8996_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8996_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8996_rf_clk2_a, + [RPM_SMD_LN_BB_CLK] = &msm8996_ln_bb_clk, + [RPM_SMD_LN_BB_A_CLK] = &msm8996_ln_bb_a_clk, + [RPM_SMD_DIV_CLK1] = &msm8996_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8996_div_clk1_a, + [RPM_SMD_DIV_CLK2] = &msm8996_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8996_div_clk2_a, + [RPM_SMD_DIV_CLK3] = &msm8996_div_clk3, + [RPM_SMD_DIV_A_CLK3] = &msm8996_div_clk3_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8996_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8996_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2_PIN] = &msm8996_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8996_bb_clk2_a_pin, + [RPM_SMD_RF_CLK1_PIN] = &msm8996_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8996_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8996_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8996_rf_clk2_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8996 = { + .clks = msm8996_clks, + .num_clks = ARRAY_SIZE(msm8996_clks), +}; + static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, + { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index d523991c945f..b8064a336d46 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -111,16 +111,6 @@ qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count) } EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode); -static void qcom_cc_del_clk_provider(void *data) -{ - of_clk_del_provider(data); -} - -static void qcom_cc_reset_unregister(void *data) -{ - reset_controller_unregister(data); -} - static void qcom_cc_gdsc_unregister(void *data) { gdsc_unregister(data); @@ -143,8 +133,10 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path, int ret; clocks_node = of_find_node_by_path("/clocks"); - if (clocks_node) - node = of_find_node_by_name(clocks_node, path); + if (clocks_node) { + node = of_get_child_by_name(clocks_node, path); + of_node_put(clocks_node); + } if (!node) { fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL); @@ -248,13 +240,7 @@ int qcom_cc_really_probe(struct platform_device *pdev, return ret; } - ret = of_clk_add_hw_provider(dev->of_node, qcom_cc_clk_hw_get, cc); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, qcom_cc_del_clk_provider, - pdev->dev.of_node); - + ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc); if (ret) return ret; @@ -266,13 +252,7 @@ int qcom_cc_really_probe(struct platform_device *pdev, reset->regmap = regmap; reset->reset_map = desc->resets; - ret = reset_controller_register(&reset->rcdev); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, qcom_cc_reset_unregister, - &reset->rcdev); - + ret = devm_reset_controller_register(dev, &reset->rcdev); if (ret) return ret; diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index acbb38151ba1..43b5a89c4b28 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -15,6 +15,7 @@ config CLK_RENESAS select CLK_R8A7794 if ARCH_R8A7794 select CLK_R8A7795 if ARCH_R8A7795 select CLK_R8A7796 if ARCH_R8A7796 + select CLK_R8A77970 if ARCH_R8A77970 select CLK_R8A77995 if ARCH_R8A77995 select CLK_SH73A0 if ARCH_SH73A0 @@ -95,6 +96,10 @@ config CLK_R8A7796 bool "R-Car M3-W clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG +config CLK_R8A77970 + bool "R-Car V3M clock support" if COMPILE_TEST + select CLK_RCAR_GEN3_CPG + config CLK_R8A77995 bool "R-Car D3 clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index cbbb081e2145..34c4e0b37afa 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CLK_R8A7792) += r8a7792-cpg-mssr.o obj-$(CONFIG_CLK_R8A7794) += r8a7794-cpg-mssr.o obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o +obj-$(CONFIG_CLK_R8A77970) += r8a77970-cpg-mssr.o obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o diff --git a/drivers/clk/renesas/clk-div6.c b/drivers/clk/renesas/clk-div6.c index 3e0040c0ac87..151336d2ba59 100644 --- a/drivers/clk/renesas/clk-div6.c +++ b/drivers/clk/renesas/clk-div6.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include #include "clk-div6.h" @@ -32,6 +34,7 @@ * @src_shift: Shift to access the register bits to select the parent clock * @src_width: Number of register bits to select the parent clock (may be 0) * @parents: Array to map from valid parent clocks indices to hardware indices + * @nb: Notifier block to save/restore clock state for system resume */ struct div6_clock { struct clk_hw hw; @@ -40,6 +43,7 @@ struct div6_clock { u32 src_shift; u32 src_width; u8 *parents; + struct notifier_block nb; }; #define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw) @@ -176,6 +180,29 @@ static const struct clk_ops cpg_div6_clock_ops = { .set_rate = cpg_div6_clock_set_rate, }; +static int cpg_div6_clock_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct div6_clock *clock = container_of(nb, struct div6_clock, nb); + + switch (action) { + case PM_EVENT_RESUME: + /* + * TODO: This does not yet support DIV6 clocks with multiple + * parents, as the parent selection bits are not restored. + * Fortunately so far such DIV6 clocks are found only on + * R/SH-Mobile SoCs, while the resume functionality is only + * needed on R-Car Gen3. + */ + if (__clk_get_enable_count(clock->hw.clk)) + cpg_div6_clock_enable(&clock->hw); + else + cpg_div6_clock_disable(&clock->hw); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} /** * cpg_div6_register - Register a DIV6 clock @@ -183,11 +210,13 @@ static const struct clk_ops cpg_div6_clock_ops = { * @num_parents: Number of parent clocks of the DIV6 clock (1, 4, or 8) * @parent_names: Array containing the names of the parent clocks * @reg: Mapped register used to control the DIV6 clock + * @notifiers: Optional notifier chain to save/restore state for system resume */ struct clk * __init cpg_div6_register(const char *name, unsigned int num_parents, const char **parent_names, - void __iomem *reg) + void __iomem *reg, + struct raw_notifier_head *notifiers) { unsigned int valid_parents; struct clk_init_data init; @@ -258,6 +287,11 @@ struct clk * __init cpg_div6_register(const char *name, if (IS_ERR(clk)) goto free_parents; + if (notifiers) { + clock->nb.notifier_call = cpg_div6_clock_notifier_call; + raw_notifier_chain_register(notifiers, &clock->nb); + } + return clk; free_parents: @@ -301,7 +335,7 @@ static void __init cpg_div6_clock_init(struct device_node *np) for (i = 0; i < num_parents; i++) parent_names[i] = of_clk_get_parent_name(np, i); - clk = cpg_div6_register(clk_name, num_parents, parent_names, reg); + clk = cpg_div6_register(clk_name, num_parents, parent_names, reg, NULL); if (IS_ERR(clk)) { pr_err("%s: failed to register %s DIV6 clock (%ld)\n", __func__, np->name, PTR_ERR(clk)); diff --git a/drivers/clk/renesas/clk-div6.h b/drivers/clk/renesas/clk-div6.h index 065dfb49adf6..3af640a0b08d 100644 --- a/drivers/clk/renesas/clk-div6.h +++ b/drivers/clk/renesas/clk-div6.h @@ -3,6 +3,7 @@ #define __RENESAS_CLK_DIV6_H__ struct clk *cpg_div6_register(const char *name, unsigned int num_parents, - const char **parent_names, void __iomem *reg); + const char **parent_names, void __iomem *reg, + struct raw_notifier_head *notifiers); #endif diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index 500a9e4e03c4..c944cc421e30 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -156,10 +156,8 @@ static struct clk * __init cpg_mstp_clock_register(const char *name, struct clk *clk; clock = kzalloc(sizeof(*clock), GFP_KERNEL); - if (!clock) { - pr_err("%s: failed to allocate MSTP clock.\n", __func__); + if (!clock) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &cpg_mstp_clock_ops; @@ -196,7 +194,6 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) if (group == NULL || clks == NULL) { kfree(group); kfree(clks); - pr_err("%s: failed to allocate group\n", __func__); return; } diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c index 0b2e56d0d94b..d14cbe1ca29a 100644 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -423,7 +423,6 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) /* We're leaking memory on purpose, there's no point in cleaning * up as the system won't boot anyway. */ - pr_err("%s: failed to allocate cpg\n", __func__); return; } diff --git a/drivers/clk/renesas/clk-rz.c b/drivers/clk/renesas/clk-rz.c index 5adb934326d1..127c58135c8f 100644 --- a/drivers/clk/renesas/clk-rz.c +++ b/drivers/clk/renesas/clk-rz.c @@ -1,5 +1,5 @@ /* - * rz Core CPG Clocks + * RZ/A1 Core CPG Clocks * * Copyright (C) 2013 Ideas On Board SPRL * Copyright (C) 2014 Wolfram Sang, Sang Engineering diff --git a/drivers/clk/renesas/r8a7745-cpg-mssr.c b/drivers/clk/renesas/r8a7745-cpg-mssr.c index 9e2360a8e14b..2859504cc866 100644 --- a/drivers/clk/renesas/r8a7745-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7745-cpg-mssr.c @@ -129,6 +129,7 @@ static const struct mssr_mod_clk r8a7745_mod_clks[] __initconst = { DEF_MOD("scif2", 719, R8A7745_CLK_P), DEF_MOD("scif1", 720, R8A7745_CLK_P), DEF_MOD("scif0", 721, R8A7745_CLK_P), + DEF_MOD("du1", 723, R8A7745_CLK_ZX), DEF_MOD("du0", 724, R8A7745_CLK_ZX), DEF_MOD("ipmmu-sgx", 800, R8A7745_CLK_ZX), DEF_MOD("vin1", 810, R8A7745_CLK_ZG), diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index 762b2f8824f1..b1d9f48eae9e 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -149,7 +149,7 @@ static struct mssr_mod_clk r8a7795_mod_clks[] __initdata = { DEF_MOD("usb-dmac1", 331, R8A7795_CLK_S3D1), DEF_MOD("rwdt", 402, R8A7795_CLK_R), DEF_MOD("intc-ex", 407, R8A7795_CLK_CP), - DEF_MOD("intc-ap", 408, R8A7795_CLK_S3D1), + DEF_MOD("intc-ap", 408, R8A7795_CLK_S0D3), DEF_MOD("audmac1", 501, R8A7795_CLK_S0D3), DEF_MOD("audmac0", 502, R8A7795_CLK_S0D3), DEF_MOD("drif7", 508, R8A7795_CLK_S3D2), @@ -348,6 +348,7 @@ static const struct mssr_mod_reparent r8a7795es1_mod_reparent[] __initconst = { { MOD_CLK_ID(217), R8A7795_CLK_S3D1 }, /* SYS-DMAC2 */ { MOD_CLK_ID(218), R8A7795_CLK_S3D1 }, /* SYS-DMAC1 */ { MOD_CLK_ID(219), R8A7795_CLK_S3D1 }, /* SYS-DMAC0 */ + { MOD_CLK_ID(408), R8A7795_CLK_S3D1 }, /* INTC-AP */ { MOD_CLK_ID(501), R8A7795_CLK_S3D1 }, /* AUDMAC1 */ { MOD_CLK_ID(502), R8A7795_CLK_S3D1 }, /* AUDMAC0 */ { MOD_CLK_ID(523), R8A7795_CLK_S3D4 }, /* PWM */ diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index e5e7fb212288..b3767472088a 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -143,7 +143,7 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { DEF_MOD("usb-dmac1", 331, R8A7796_CLK_S3D1), DEF_MOD("rwdt", 402, R8A7796_CLK_R), DEF_MOD("intc-ex", 407, R8A7796_CLK_CP), - DEF_MOD("intc-ap", 408, R8A7796_CLK_S3D1), + DEF_MOD("intc-ap", 408, R8A7796_CLK_S0D3), DEF_MOD("audmac1", 501, R8A7796_CLK_S0D3), DEF_MOD("audmac0", 502, R8A7796_CLK_S0D3), DEF_MOD("drif7", 508, R8A7796_CLK_S3D2), diff --git a/drivers/clk/renesas/r8a77970-cpg-mssr.c b/drivers/clk/renesas/r8a77970-cpg-mssr.c new file mode 100644 index 000000000000..72f98527473a --- /dev/null +++ b/drivers/clk/renesas/r8a77970-cpg-mssr.c @@ -0,0 +1,199 @@ +/* + * r8a77970 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2017 Cogent Embedded Inc. + * + * Based on r8a7795-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include + +#include + +#include "renesas-cpg-mssr.h" +#include "rcar-gen3-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A77970_CLK_OSC, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_EXTALR, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL3, + CLK_PLL1_DIV2, + CLK_PLL1_DIV4, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +static const struct cpg_core_clk r8a77970_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("extalr", CLK_EXTALR), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL), + DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN3_PLL0, CLK_MAIN), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN), + + DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1), + DEF_FIXED(".pll1_div4", CLK_PLL1_DIV4, CLK_PLL1_DIV2, 2, 1), + + /* Core Clock Outputs */ + DEF_FIXED("ztr", R8A77970_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED("ztrd2", R8A77970_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), + DEF_FIXED("zt", R8A77970_CLK_ZT, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED("zx", R8A77970_CLK_ZX, CLK_PLL1_DIV2, 3, 1), + DEF_FIXED("s1d1", R8A77970_CLK_S1D1, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED("s1d2", R8A77970_CLK_S1D2, CLK_PLL1_DIV2, 8, 1), + DEF_FIXED("s1d4", R8A77970_CLK_S1D4, CLK_PLL1_DIV2, 16, 1), + DEF_FIXED("s2d1", R8A77970_CLK_S2D1, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED("s2d2", R8A77970_CLK_S2D2, CLK_PLL1_DIV2, 12, 1), + DEF_FIXED("s2d4", R8A77970_CLK_S2D4, CLK_PLL1_DIV2, 24, 1), + + DEF_FIXED("cl", R8A77970_CLK_CL, CLK_PLL1_DIV2, 48, 1), + DEF_FIXED("cp", R8A77970_CLK_CP, CLK_EXTAL, 2, 1), + + DEF_DIV6P1("canfd", R8A77970_CLK_CANFD, CLK_PLL1_DIV4, 0x244), + DEF_DIV6P1("mso", R8A77970_CLK_MSO, CLK_PLL1_DIV4, 0x014), + DEF_DIV6P1("csi0", R8A77970_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), + + DEF_FIXED("osc", R8A77970_CLK_OSC, CLK_PLL1_DIV2, 12*1024, 1), + DEF_FIXED("r", R8A77970_CLK_R, CLK_EXTALR, 1, 1), +}; + +static const struct mssr_mod_clk r8a77970_mod_clks[] __initconst = { + DEF_MOD("ivcp1e", 127, R8A77970_CLK_S2D1), + DEF_MOD("scif4", 203, R8A77970_CLK_S2D4), + DEF_MOD("scif3", 204, R8A77970_CLK_S2D4), + DEF_MOD("scif1", 206, R8A77970_CLK_S2D4), + DEF_MOD("scif0", 207, R8A77970_CLK_S2D4), + DEF_MOD("msiof3", 208, R8A77970_CLK_MSO), + DEF_MOD("msiof2", 209, R8A77970_CLK_MSO), + DEF_MOD("msiof1", 210, R8A77970_CLK_MSO), + DEF_MOD("msiof0", 211, R8A77970_CLK_MSO), + DEF_MOD("mfis", 213, R8A77970_CLK_S2D2), + DEF_MOD("sys-dmac2", 217, R8A77970_CLK_S2D1), + DEF_MOD("sys-dmac1", 218, R8A77970_CLK_S2D1), + DEF_MOD("rwdt", 402, R8A77970_CLK_R), + DEF_MOD("intc-ex", 407, R8A77970_CLK_CP), + DEF_MOD("intc-ap", 408, R8A77970_CLK_S2D1), + DEF_MOD("hscif3", 517, R8A77970_CLK_S2D1), + DEF_MOD("hscif2", 518, R8A77970_CLK_S2D1), + DEF_MOD("hscif1", 519, R8A77970_CLK_S2D1), + DEF_MOD("hscif0", 520, R8A77970_CLK_S2D1), + DEF_MOD("thermal", 522, R8A77970_CLK_CP), + DEF_MOD("pwm", 523, R8A77970_CLK_S2D4), + DEF_MOD("fcpvd0", 603, R8A77970_CLK_S2D1), + DEF_MOD("vspd0", 623, R8A77970_CLK_S2D1), + DEF_MOD("csi40", 716, R8A77970_CLK_CSI0), + DEF_MOD("du0", 724, R8A77970_CLK_S2D1), + DEF_MOD("vin3", 808, R8A77970_CLK_S2D1), + DEF_MOD("vin2", 809, R8A77970_CLK_S2D1), + DEF_MOD("vin1", 810, R8A77970_CLK_S2D1), + DEF_MOD("vin0", 811, R8A77970_CLK_S2D1), + DEF_MOD("etheravb", 812, R8A77970_CLK_S2D2), + DEF_MOD("gpio5", 907, R8A77970_CLK_CP), + DEF_MOD("gpio4", 908, R8A77970_CLK_CP), + DEF_MOD("gpio3", 909, R8A77970_CLK_CP), + DEF_MOD("gpio2", 910, R8A77970_CLK_CP), + DEF_MOD("gpio1", 911, R8A77970_CLK_CP), + DEF_MOD("gpio0", 912, R8A77970_CLK_CP), + DEF_MOD("can-fd", 914, R8A77970_CLK_S2D2), + DEF_MOD("i2c4", 927, R8A77970_CLK_S2D2), + DEF_MOD("i2c3", 928, R8A77970_CLK_S2D2), + DEF_MOD("i2c2", 929, R8A77970_CLK_S2D2), + DEF_MOD("i2c1", 930, R8A77970_CLK_S2D2), + DEF_MOD("i2c0", 931, R8A77970_CLK_S2D2), +}; + +static const unsigned int r8a77970_crit_mod_clks[] __initconst = { + MOD_CLK_ID(408), /* INTC-AP (GIC) */ +}; + + +/* + * CPG Clock Data + */ + +/* + * MD EXTAL PLL0 PLL1 PLL3 + * 14 13 19 (MHz) + *------------------------------------------------- + * 0 0 0 16.66 x 1 x192 x192 x96 + * 0 0 1 16.66 x 1 x192 x192 x80 + * 0 1 0 20 x 1 x160 x160 x80 + * 0 1 1 20 x 1 x160 x160 x66 + * 1 0 0 27 / 2 x236 x236 x118 + * 1 0 1 27 / 2 x236 x236 x98 + * 1 1 0 33.33 / 2 x192 x192 x96 + * 1 1 1 33.33 / 2 x192 x192 x80 + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \ + (((md) & BIT(13)) >> 12) | \ + (((md) & BIT(19)) >> 19)) + +static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[8] __initconst = { + /* EXTAL div PLL1 mult/div PLL3 mult/div */ + { 1, 192, 1, 96, 1, }, + { 1, 192, 1, 80, 1, }, + { 1, 160, 1, 80, 1, }, + { 1, 160, 1, 66, 1, }, + { 2, 236, 1, 118, 1, }, + { 2, 236, 1, 98, 1, }, + { 2, 192, 1, 96, 1, }, + { 2, 192, 1, 80, 1, }, +}; + +static int __init r8a77970_cpg_mssr_init(struct device *dev) +{ + const struct rcar_gen3_cpg_pll_config *cpg_pll_config; + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + + return rcar_gen3_cpg_init(cpg_pll_config, CLK_EXTALR, cpg_mode); +} + +const struct cpg_mssr_info r8a77970_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a77970_core_clks, + .num_core_clks = ARRAY_SIZE(r8a77970_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a77970_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a77970_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a77970_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a77970_crit_mod_clks), + + /* Callbacks */ + .init = r8a77970_cpg_mssr_init, + .cpg_clk_register = rcar_gen3_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/r8a77995-cpg-mssr.c b/drivers/clk/renesas/r8a77995-cpg-mssr.c index e594cf8ee63b..ea4cafbe6e85 100644 --- a/drivers/clk/renesas/r8a77995-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77995-cpg-mssr.c @@ -127,7 +127,7 @@ static const struct mssr_mod_clk r8a77995_mod_clks[] __initconst = { DEF_MOD("usb-dmac1", 331, R8A77995_CLK_S3D1), DEF_MOD("rwdt", 402, R8A77995_CLK_R), DEF_MOD("intc-ex", 407, R8A77995_CLK_CP), - DEF_MOD("intc-ap", 408, R8A77995_CLK_S3D1), + DEF_MOD("intc-ap", 408, R8A77995_CLK_S1D2), DEF_MOD("audmac0", 502, R8A77995_CLK_S3D1), DEF_MOD("hscif3", 517, R8A77995_CLK_S3D1C), DEF_MOD("hscif0", 520, R8A77995_CLK_S3D1C), diff --git a/drivers/clk/renesas/rcar-gen2-cpg.c b/drivers/clk/renesas/rcar-gen2-cpg.c index 123b1e622179..feb14579a71b 100644 --- a/drivers/clk/renesas/rcar-gen2-cpg.c +++ b/drivers/clk/renesas/rcar-gen2-cpg.c @@ -262,10 +262,9 @@ static unsigned int cpg_pll0_div __initdata; static u32 cpg_mode __initdata; struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev, - const struct cpg_core_clk *core, - const struct cpg_mssr_info *info, - struct clk **clks, - void __iomem *base) + const struct cpg_core_clk *core, const struct cpg_mssr_info *info, + struct clk **clks, void __iomem *base, + struct raw_notifier_head *notifiers) { const struct clk_div_table *table = NULL; const struct clk *parent; diff --git a/drivers/clk/renesas/rcar-gen2-cpg.h b/drivers/clk/renesas/rcar-gen2-cpg.h index 9eba07ff8b11..020a3baad015 100644 --- a/drivers/clk/renesas/rcar-gen2-cpg.h +++ b/drivers/clk/renesas/rcar-gen2-cpg.h @@ -34,9 +34,9 @@ struct rcar_gen2_cpg_pll_config { }; struct clk *rcar_gen2_cpg_clk_register(struct device *dev, - const struct cpg_core_clk *core, - const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base); + const struct cpg_core_clk *core, const struct cpg_mssr_info *info, + struct clk **clks, void __iomem *base, + struct raw_notifier_head *notifiers); int rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config, unsigned int pll0_div, u32 mode); diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 951105816547..0904886f5501 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,36 @@ #define CPG_PLL2CR 0x002c #define CPG_PLL4CR 0x01f4 +struct cpg_simple_notifier { + struct notifier_block nb; + void __iomem *reg; + u32 saved; +}; + +static int cpg_simple_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct cpg_simple_notifier *csn = + container_of(nb, struct cpg_simple_notifier, nb); + + switch (action) { + case PM_EVENT_SUSPEND: + csn->saved = readl(csn->reg); + return NOTIFY_OK; + + case PM_EVENT_RESUME: + writel(csn->saved, csn->reg); + return NOTIFY_OK; + } + return NOTIFY_DONE; +} + +static void cpg_simple_notifier_register(struct raw_notifier_head *notifiers, + struct cpg_simple_notifier *csn) +{ + csn->nb.notifier_call = cpg_simple_notifier_call; + raw_notifier_chain_register(notifiers, &csn->nb); +} /* * SDn Clock @@ -55,8 +86,8 @@ struct sd_div_table { struct sd_clock { struct clk_hw hw; - void __iomem *reg; const struct sd_div_table *div_table; + struct cpg_simple_notifier csn; unsigned int div_num; unsigned int div_min; unsigned int div_max; @@ -97,12 +128,12 @@ static const struct sd_div_table cpg_sd_div_table[] = { static int cpg_sd_clock_enable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - u32 val = readl(clock->reg); + u32 val = readl(clock->csn.reg); val &= ~(CPG_SD_STP_MASK); val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK; - writel(val, clock->reg); + writel(val, clock->csn.reg); return 0; } @@ -111,14 +142,14 @@ static void cpg_sd_clock_disable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - writel(readl(clock->reg) | CPG_SD_STP_MASK, clock->reg); + writel(readl(clock->csn.reg) | CPG_SD_STP_MASK, clock->csn.reg); } static int cpg_sd_clock_is_enabled(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - return !(readl(clock->reg) & CPG_SD_STP_MASK); + return !(readl(clock->csn.reg) & CPG_SD_STP_MASK); } static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, @@ -170,10 +201,10 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, clock->cur_div_idx = i; - val = readl(clock->reg); + val = readl(clock->csn.reg); val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); - writel(val, clock->reg); + writel(val, clock->csn.reg); return 0; } @@ -188,8 +219,8 @@ static const struct clk_ops cpg_sd_clock_ops = { }; static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, - void __iomem *base, - const char *parent_name) + void __iomem *base, const char *parent_name, + struct raw_notifier_head *notifiers) { struct clk_init_data init; struct sd_clock *clock; @@ -207,12 +238,12 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, init.parent_names = &parent_name; init.num_parents = 1; - clock->reg = base + core->offset; + clock->csn.reg = base + core->offset; clock->hw.init = &init; clock->div_table = cpg_sd_div_table; clock->div_num = ARRAY_SIZE(cpg_sd_div_table); - sd_fc = readl(clock->reg) & CPG_SD_FC_MASK; + sd_fc = readl(clock->csn.reg) & CPG_SD_FC_MASK; for (i = 0; i < clock->div_num; i++) if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) break; @@ -233,8 +264,13 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, clk = clk_register(NULL, &clock->hw); if (IS_ERR(clk)) - kfree(clock); + goto free_clock; + cpg_simple_notifier_register(notifiers, &clock->csn); + return clk; + +free_clock: + kfree(clock); return clk; } @@ -265,7 +301,8 @@ static const struct soc_device_attribute cpg_quirks_match[] __initconst = { struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base) + struct clk **clks, void __iomem *base, + struct raw_notifier_head *notifiers) { const struct clk *parent; unsigned int mult = 1; @@ -331,22 +368,32 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, break; case CLK_TYPE_GEN3_SD: - return cpg_sd_clk_register(core, base, __clk_get_name(parent)); + return cpg_sd_clk_register(core, base, __clk_get_name(parent), + notifiers); case CLK_TYPE_GEN3_R: if (cpg_quirks & RCKCR_CKSEL) { + struct cpg_simple_notifier *csn; + + csn = kzalloc(sizeof(*csn), GFP_KERNEL); + if (!csn) + return ERR_PTR(-ENOMEM); + + csn->reg = base + CPG_RCKCR; + /* * RINT is default. * Only if EXTALR is populated, we switch to it. */ - value = readl(base + CPG_RCKCR) & 0x3f; + value = readl(csn->reg) & 0x3f; if (clk_get_rate(clks[cpg_clk_extalr])) { parent = clks[cpg_clk_extalr]; value |= BIT(15); } - writel(value, base + CPG_RCKCR); + writel(value, csn->reg); + cpg_simple_notifier_register(notifiers, csn); break; } diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index d756ef8b78eb..2e4284399f53 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -44,7 +44,8 @@ struct rcar_gen3_cpg_pll_config { struct clk *rcar_gen3_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base); + struct clk **clks, void __iomem *base, + struct raw_notifier_head *notifiers); int rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config, unsigned int clk_extalr, u32 mode); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index e580a5e6346c..e3d03ffea4bc 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,9 @@ static const u16 srcr[] = { * @num_core_clks: Number of Core Clocks in clks[] * @num_mod_clks: Number of Module Clocks in clks[] * @last_dt_core_clk: ID of the last Core Clock exported to DT + * @notifiers: Notifier chain to save/restore clock state for system resume + * @smstpcr_saved[].mask: Mask of SMSTPCR[] bits under our control + * @smstpcr_saved[].val: Saved values of SMSTPCR[] */ struct cpg_mssr_priv { #ifdef CONFIG_RESET_CONTROLLER @@ -119,6 +123,12 @@ struct cpg_mssr_priv { unsigned int num_core_clks; unsigned int num_mod_clks; unsigned int last_dt_core_clk; + + struct raw_notifier_head notifiers; + struct { + u32 mask; + u32 val; + } smstpcr_saved[ARRAY_SIZE(smstpcr)]; }; @@ -293,7 +303,8 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, if (core->type == CLK_TYPE_DIV6P1) { clk = cpg_div6_register(core->name, 1, &parent_name, - priv->base + core->offset); + priv->base + core->offset, + &priv->notifiers); } else { clk = clk_register_fixed_factor(NULL, core->name, parent_name, 0, @@ -304,7 +315,8 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, default: if (info->cpg_clk_register) clk = info->cpg_clk_register(dev, core, info, - priv->clks, priv->base); + priv->clks, priv->base, + &priv->notifiers); else dev_err(dev, "%s has unsupported core clock type %u\n", core->name, core->type); @@ -382,6 +394,7 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod, dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk); priv->clks[id] = clk; + priv->smstpcr_saved[clock->index / 32].mask |= BIT(clock->index % 32); return; fail: @@ -680,6 +693,12 @@ static const struct of_device_id cpg_mssr_match[] = { .data = &r8a7796_cpg_mssr_info, }, #endif +#ifdef CONFIG_CLK_R8A77970 + { + .compatible = "renesas,r8a77970-cpg-mssr", + .data = &r8a77970_cpg_mssr_info, + }, +#endif #ifdef CONFIG_CLK_R8A77995 { .compatible = "renesas,r8a77995-cpg-mssr", @@ -694,6 +713,85 @@ static void cpg_mssr_del_clk_provider(void *data) of_clk_del_provider(data); } +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW) +static int cpg_mssr_suspend_noirq(struct device *dev) +{ + struct cpg_mssr_priv *priv = dev_get_drvdata(dev); + unsigned int reg; + + /* This is the best we can do to check for the presence of PSCI */ + if (!psci_ops.cpu_suspend) + return 0; + + /* Save module registers with bits under our control */ + for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) { + if (priv->smstpcr_saved[reg].mask) + priv->smstpcr_saved[reg].val = + readl(priv->base + SMSTPCR(reg)); + } + + /* Save core clocks */ + raw_notifier_call_chain(&priv->notifiers, PM_EVENT_SUSPEND, NULL); + + return 0; +} + +static int cpg_mssr_resume_noirq(struct device *dev) +{ + struct cpg_mssr_priv *priv = dev_get_drvdata(dev); + unsigned int reg, i; + u32 mask, oldval, newval; + + /* This is the best we can do to check for the presence of PSCI */ + if (!psci_ops.cpu_suspend) + return 0; + + /* Restore core clocks */ + raw_notifier_call_chain(&priv->notifiers, PM_EVENT_RESUME, NULL); + + /* Restore module clocks */ + for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) { + mask = priv->smstpcr_saved[reg].mask; + if (!mask) + continue; + + oldval = readl(priv->base + SMSTPCR(reg)); + newval = oldval & ~mask; + newval |= priv->smstpcr_saved[reg].val & mask; + if (newval == oldval) + continue; + + writel(newval, priv->base + SMSTPCR(reg)); + + /* Wait until enabled clocks are really enabled */ + mask &= ~priv->smstpcr_saved[reg].val; + if (!mask) + continue; + + for (i = 1000; i > 0; --i) { + oldval = readl(priv->base + MSTPSR(reg)); + if (!(oldval & mask)) + break; + cpu_relax(); + } + + if (!i) + dev_warn(dev, "Failed to enable SMSTP %p[0x%x]\n", + priv->base + SMSTPCR(reg), oldval & mask); + } + + return 0; +} + +static const struct dev_pm_ops cpg_mssr_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cpg_mssr_suspend_noirq, + cpg_mssr_resume_noirq) +}; +#define DEV_PM_OPS &cpg_mssr_pm +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */ + static int __init cpg_mssr_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -729,10 +827,12 @@ static int __init cpg_mssr_probe(struct platform_device *pdev) if (!clks) return -ENOMEM; + dev_set_drvdata(dev, priv); priv->clks = clks; priv->num_core_clks = info->num_total_core_clks; priv->num_mod_clks = info->num_hw_mod_clks; priv->last_dt_core_clk = info->last_dt_core_clk; + RAW_INIT_NOTIFIER_HEAD(&priv->notifiers); for (i = 0; i < nclks; i++) clks[i] = ERR_PTR(-ENOENT); @@ -769,6 +869,7 @@ static struct platform_driver cpg_mssr_driver = { .driver = { .name = "renesas-cpg-mssr", .of_match_table = cpg_mssr_match, + .pm = DEV_PM_OPS, }, }; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 94b9071d1061..0745b0930308 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -127,7 +127,8 @@ struct cpg_mssr_info { struct clk *(*cpg_clk_register)(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base); + struct clk **clks, void __iomem *base, + struct raw_notifier_head *notifiers); }; extern const struct cpg_mssr_info r8a7743_cpg_mssr_info; @@ -138,6 +139,7 @@ extern const struct cpg_mssr_info r8a7792_cpg_mssr_info; extern const struct cpg_mssr_info r8a7794_cpg_mssr_info; extern const struct cpg_mssr_info r8a7795_cpg_mssr_info; extern const struct cpg_mssr_info r8a7796_cpg_mssr_info; +extern const struct cpg_mssr_info r8a77970_cpg_mssr_info; extern const struct cpg_mssr_info r8a77995_cpg_mssr_info; diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 0e09684d43a5..32c19c0f1e14 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -322,8 +322,6 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, sizeof(*rates) * nrates, GFP_KERNEL); if (!cpuclk->rate_table) { - pr_err("%s: could not allocate memory for cpuclk rates\n", - __func__); ret = -ENOMEM; goto unregister_notifier; } diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 00ad0e5f8d66..67e73fd71f09 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -290,15 +290,15 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { RK2928_CLKSEL_CON(0), 6, 2, DFLAGS | CLK_DIVIDER_READ_ONLY, div_core_peri_t, RK2928_CLKGATE_CON(0), 0, GFLAGS), - COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_p, 0, + COMPOSITE(ACLK_VEPU, "aclk_vepu", mux_pll_src_cpll_gpll_p, 0, RK2928_CLKSEL_CON(32), 7, 1, MFLAGS, 0, 5, DFLAGS, RK2928_CLKGATE_CON(3), 9, GFLAGS), - GATE(0, "hclk_vepu", "aclk_vepu", 0, + GATE(HCLK_VEPU, "hclk_vepu", "aclk_vepu", 0, RK2928_CLKGATE_CON(3), 10, GFLAGS), - COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_p, 0, + COMPOSITE(ACLK_VDPU, "aclk_vdpu", mux_pll_src_cpll_gpll_p, 0, RK2928_CLKSEL_CON(32), 15, 1, MFLAGS, 8, 5, DFLAGS, RK2928_CLKGATE_CON(3), 11, GFLAGS), - GATE(0, "hclk_vdpu", "aclk_vdpu", 0, + GATE(HCLK_VDPU, "hclk_vdpu", "aclk_vdpu", 0, RK2928_CLKGATE_CON(3), 12, GFLAGS), GATE(0, "gpll_ddr", "gpll", CLK_IGNORE_UNUSED, @@ -644,13 +644,13 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { GATE(HCLK_I2S1, "hclk_i2s1", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS), GATE(HCLK_I2S2, "hclk_i2s2", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS), - GATE(0, "hclk_cif1", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 6, GFLAGS), + GATE(HCLK_CIF1, "hclk_cif1", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 6, GFLAGS), GATE(0, "hclk_hdmi", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS), GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 14, GFLAGS), - GATE(0, "aclk_cif1", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 7, GFLAGS), + GATE(ACLK_CIF1, "aclk_cif1", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 7, GFLAGS), GATE(PCLK_TIMER1, "pclk_timer1", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 8, GFLAGS), GATE(PCLK_TIMER2, "pclk_timer2", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 9, GFLAGS), diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c index fc56565379dd..7c4d242f19c1 100644 --- a/drivers/clk/rockchip/clk-rk3368.c +++ b/drivers/clk/rockchip/clk-rk3368.c @@ -711,7 +711,7 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = { GATE(PCLK_SIM, "pclk_sim", "pclk_bus", 0, RK3368_CLKGATE_CON(13), 8, GFLAGS), GATE(PCLK_PWM1, "pclk_pwm1", "pclk_bus", 0, RK3368_CLKGATE_CON(13), 6, GFLAGS), GATE(PCLK_UART2, "pclk_uart2", "pclk_bus", 0, RK3368_CLKGATE_CON(13), 5, GFLAGS), - GATE(0, "pclk_efuse_256", "pclk_bus", 0, RK3368_CLKGATE_CON(13), 1, GFLAGS), + GATE(PCLK_EFUSE256, "pclk_efuse_256", "pclk_bus", 0, RK3368_CLKGATE_CON(13), 1, GFLAGS), GATE(0, "pclk_efuse_1024", "pclk_bus", 0, RK3368_CLKGATE_CON(13), 0, GFLAGS), /* diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 23835001e8bd..ef8900bc077f 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o +obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4412-isp.o obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c index 6686e8ba61f9..d2c99d8916b8 100644 --- a/drivers/clk/samsung/clk-cpu.c +++ b/drivers/clk/samsung/clk-cpu.c @@ -457,8 +457,6 @@ int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL); if (!cpuclk->cfg) { - pr_err("%s: could not allocate memory for cpuclk data\n", - __func__); ret = -ENOMEM; goto unregister_clk_nb; } diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index b117783ed404..5bfc92ee3129 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -36,14 +37,13 @@ static struct clk *epll; #define ASS_CLK_DIV 0x4 #define ASS_CLK_GATE 0x8 -#ifdef CONFIG_PM_SLEEP static unsigned long reg_save[][2] = { { ASS_CLK_SRC, 0 }, { ASS_CLK_DIV, 0 }, { ASS_CLK_GATE, 0 }, }; -static int exynos_audss_clk_suspend(struct device *dev) +static int __maybe_unused exynos_audss_clk_suspend(struct device *dev) { int i; @@ -53,7 +53,7 @@ static int exynos_audss_clk_suspend(struct device *dev) return 0; } -static int exynos_audss_clk_resume(struct device *dev) +static int __maybe_unused exynos_audss_clk_resume(struct device *dev) { int i; @@ -62,7 +62,6 @@ static int exynos_audss_clk_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ struct exynos_audss_clk_drvdata { unsigned int has_adma_clk:1; @@ -135,6 +134,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) const struct exynos_audss_clk_drvdata *variant; struct clk_hw **clk_table; struct resource *res; + struct device *dev = &pdev->dev; int i, ret = 0; variant = of_device_get_match_data(&pdev->dev); @@ -142,15 +142,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) return -EINVAL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg_base = devm_ioremap_resource(&pdev->dev, res); + reg_base = devm_ioremap_resource(dev, res); if (IS_ERR(reg_base)) { - dev_err(&pdev->dev, "failed to map audss registers\n"); + dev_err(dev, "failed to map audss registers\n"); return PTR_ERR(reg_base); } epll = ERR_PTR(-ENODEV); - clk_data = devm_kzalloc(&pdev->dev, + clk_data = devm_kzalloc(dev, sizeof(*clk_data) + sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS, GFP_KERNEL); @@ -160,8 +160,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) clk_data->num = variant->num_clks; clk_table = clk_data->hws; - pll_ref = devm_clk_get(&pdev->dev, "pll_ref"); - pll_in = devm_clk_get(&pdev->dev, "pll_in"); + pll_ref = devm_clk_get(dev, "pll_ref"); + pll_in = devm_clk_get(dev, "pll_in"); if (!IS_ERR(pll_ref)) mout_audss_p[0] = __clk_get_name(pll_ref); if (!IS_ERR(pll_in)) { @@ -172,88 +172,103 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) ret = clk_prepare_enable(epll); if (ret) { - dev_err(&pdev->dev, + dev_err(dev, "failed to prepare the epll clock\n"); return ret; } } } - clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss", + + /* + * Enable runtime PM here to allow the clock core using runtime PM + * for the registered clocks. Additionally, we increase the runtime + * PM usage count before registering the clocks, to prevent the + * clock core from runtime suspending the device. + */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(dev, "mout_audss", mout_audss_p, ARRAY_SIZE(mout_audss_p), CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); - cdclk = devm_clk_get(&pdev->dev, "cdclk"); - sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio"); + cdclk = devm_clk_get(dev, "cdclk"); + sclk_audio = devm_clk_get(dev, "sclk_audio"); if (!IS_ERR(cdclk)) mout_i2s_p[1] = __clk_get_name(cdclk); if (!IS_ERR(sclk_audio)) mout_i2s_p[2] = __clk_get_name(sclk_audio); - clk_table[EXYNOS_MOUT_I2S] = clk_hw_register_mux(NULL, "mout_i2s", + clk_table[EXYNOS_MOUT_I2S] = clk_hw_register_mux(dev, "mout_i2s", mout_i2s_p, ARRAY_SIZE(mout_i2s_p), CLK_SET_RATE_NO_REPARENT, reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); - clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(NULL, "dout_srp", + clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp", "mout_audss", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_DIV, 0, 4, 0, &lock); - clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL, + clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev, "dout_aud_bus", "dout_srp", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_DIV, 4, 4, 0, &lock); - clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s", + clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s", "mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0, &lock); - clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(NULL, "srp_clk", + clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(dev, "srp_clk", "dout_srp", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 0, 0, &lock); - clk_table[EXYNOS_I2S_BUS] = clk_hw_register_gate(NULL, "i2s_bus", + clk_table[EXYNOS_I2S_BUS] = clk_hw_register_gate(dev, "i2s_bus", "dout_aud_bus", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 2, 0, &lock); - clk_table[EXYNOS_SCLK_I2S] = clk_hw_register_gate(NULL, "sclk_i2s", + clk_table[EXYNOS_SCLK_I2S] = clk_hw_register_gate(dev, "sclk_i2s", "dout_i2s", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 3, 0, &lock); - clk_table[EXYNOS_PCM_BUS] = clk_hw_register_gate(NULL, "pcm_bus", + clk_table[EXYNOS_PCM_BUS] = clk_hw_register_gate(dev, "pcm_bus", "sclk_pcm", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 4, 0, &lock); - sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in"); + sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in"); if (!IS_ERR(sclk_pcm_in)) sclk_pcm_p = __clk_get_name(sclk_pcm_in); - clk_table[EXYNOS_SCLK_PCM] = clk_hw_register_gate(NULL, "sclk_pcm", + clk_table[EXYNOS_SCLK_PCM] = clk_hw_register_gate(dev, "sclk_pcm", sclk_pcm_p, CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 5, 0, &lock); if (variant->has_adma_clk) { - clk_table[EXYNOS_ADMA] = clk_hw_register_gate(NULL, "adma", + clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma", "dout_srp", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_GATE, 9, 0, &lock); } for (i = 0; i < clk_data->num; i++) { if (IS_ERR(clk_table[i])) { - dev_err(&pdev->dev, "failed to register clock %d\n", i); + dev_err(dev, "failed to register clock %d\n", i); ret = PTR_ERR(clk_table[i]); goto unregister; } } - ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, + ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_data); if (ret) { - dev_err(&pdev->dev, "failed to add clock provider\n"); + dev_err(dev, "failed to add clock provider\n"); goto unregister; } + pm_runtime_put_sync(dev); + return 0; unregister: exynos_audss_clk_teardown(); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); if (!IS_ERR(epll)) clk_disable_unprepare(epll); @@ -266,6 +281,7 @@ static int exynos_audss_clk_remove(struct platform_device *pdev) of_clk_del_provider(pdev->dev.of_node); exynos_audss_clk_teardown(); + pm_runtime_disable(&pdev->dev); if (!IS_ERR(epll)) clk_disable_unprepare(epll); @@ -274,8 +290,10 @@ static int exynos_audss_clk_remove(struct platform_device *pdev) } static const struct dev_pm_ops exynos_audss_clk_pm_ops = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend, - exynos_audss_clk_resume) + SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume, + NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static struct platform_driver exynos_audss_clk_driver = { diff --git a/drivers/clk/samsung/clk-exynos-clkout.c b/drivers/clk/samsung/clk-exynos-clkout.c index a21aea062bae..f29fb5824005 100644 --- a/drivers/clk/samsung/clk-exynos-clkout.c +++ b/drivers/clk/samsung/clk-exynos-clkout.c @@ -144,8 +144,6 @@ static void __init exynos4_clkout_init(struct device_node *node) } CLK_OF_DECLARE_DRIVER(exynos4210_clkout, "samsung,exynos4210-pmu", exynos4_clkout_init); -CLK_OF_DECLARE_DRIVER(exynos4212_clkout, "samsung,exynos4212-pmu", - exynos4_clkout_init); CLK_OF_DECLARE_DRIVER(exynos4412_clkout, "samsung,exynos4412-pmu", exynos4_clkout_init); CLK_OF_DECLARE_DRIVER(exynos3250_clkout, "samsung,exynos3250-pmu", diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index d8d3cb67b402..134f25f2a913 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -550,9 +550,8 @@ static const struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __ /* list of mux clocks supported in all exynos4 soc's */ static const struct samsung_mux_clock exynos4_mux_clks[] __initconst = { - MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, - CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0, - "mout_apll"), + MUX_F(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, + CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0), MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), @@ -737,7 +736,7 @@ static const struct samsung_div_clock exynos4_div_clks[] __initconst = { DIV(0, "div_periph", "div_core2", DIV_CPU0, 12, 3), DIV(0, "div_atb", "mout_core", DIV_CPU0, 16, 3), DIV(0, "div_pclk_dbg", "div_atb", DIV_CPU0, 20, 3), - DIV(CLK_ARM_CLK, "div_core2", "div_core", DIV_CPU0, 28, 3), + DIV(0, "div_core2", "div_core", DIV_CPU0, 28, 3), DIV(0, "div_copy", "mout_hpm", DIV_CPU1, 0, 3), DIV(0, "div_hpm", "div_copy", DIV_CPU1, 4, 3), DIV(0, "div_clkout_cpu", "mout_clkout_cpu", CLKOUT_CMU_CPU, 8, 6), @@ -837,6 +836,12 @@ static const struct samsung_div_clock exynos4x12_div_clks[] __initconst = { DIV(0, "div_spi1_isp", "mout_spi1_isp", E4X12_DIV_ISP, 16, 4), DIV(0, "div_spi1_isp_pre", "div_spi1_isp", E4X12_DIV_ISP, 20, 8), DIV(0, "div_uart_isp", "mout_uart_isp", E4X12_DIV_ISP, 28, 4), + DIV(CLK_SCLK_FIMG2D, "sclk_fimg2d", "mout_g2d", DIV_DMC1, 0, 4), + DIV(CLK_DIV_C2C, "div_c2c", "mout_c2c", DIV_DMC1, 4, 3), + DIV(0, "div_c2c_aclk", "div_c2c", DIV_DMC1, 12, 3), +}; + +static struct samsung_div_clock exynos4x12_isp_div_clks[] = { DIV_F(CLK_DIV_ISP0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3, CLK_GET_RATE_NOCACHE, 0), DIV_F(CLK_DIV_ISP1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3, @@ -846,18 +851,10 @@ static const struct samsung_div_clock exynos4x12_div_clks[] __initconst = { 4, 3, CLK_GET_RATE_NOCACHE, 0), DIV_F(CLK_DIV_MCUISP1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3, CLK_GET_RATE_NOCACHE, 0), - DIV(CLK_SCLK_FIMG2D, "sclk_fimg2d", "mout_g2d", DIV_DMC1, 0, 4), - DIV(CLK_DIV_C2C, "div_c2c", "mout_c2c", DIV_DMC1, 4, 3), - DIV(0, "div_c2c_aclk", "div_c2c", DIV_DMC1, 12, 3), }; /* list of gate clocks supported in all exynos4 soc's */ static const struct samsung_gate_clock exynos4_gate_clks[] __initconst = { - /* - * After all Exynos4 based platforms are migrated to use device tree, - * the device name and clock alias names specified below for some - * of the clocks can be removed. - */ GATE(CLK_PPMULEFT, "ppmuleft", "aclk200", GATE_IP_LEFTBUS, 1, 0, 0), GATE(CLK_PPMURIGHT, "ppmuright", "aclk200", GATE_IP_RIGHTBUS, 1, 0, 0), GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi", SRC_MASK_TV, 0, 0, 0), @@ -1147,6 +1144,13 @@ static const struct samsung_gate_clock exynos4x12_gate_clks[] __initconst = { 0, 0), GATE(CLK_I2S0, "i2s0", "aclk100", E4X12_GATE_IP_MAUDIO, 3, 0, 0), + GATE(CLK_G2D, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0), + GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk200", GATE_IP_DMC, 24, 0, 0), + GATE(CLK_TMU_APBIF, "tmu_apbif", "aclk100", E4X12_GATE_IP_PERIR, 17, 0, + 0), +}; + +static struct samsung_gate_clock exynos4x12_isp_gate_clks[] = { GATE(CLK_FIMC_ISP, "isp", "aclk200", E4X12_GATE_ISP0, 0, CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(CLK_FIMC_DRC, "drc", "aclk200", E4X12_GATE_ISP0, 1, @@ -1199,24 +1203,6 @@ static const struct samsung_gate_clock exynos4x12_gate_clks[] __initconst = { CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(CLK_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), - GATE(CLK_G2D, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0), - GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk200", GATE_IP_DMC, 24, 0, 0), - GATE(CLK_TMU_APBIF, "tmu_apbif", "aclk100", E4X12_GATE_IP_PERIR, 17, 0, - 0), -}; - -static const struct samsung_clock_alias exynos4_aliases[] __initconst = { - ALIAS(CLK_MOUT_CORE, NULL, "moutcore"), - ALIAS(CLK_ARM_CLK, NULL, "armclk"), - ALIAS(CLK_SCLK_APLL, NULL, "mout_apll"), -}; - -static const struct samsung_clock_alias exynos4210_aliases[] __initconst = { - ALIAS(CLK_SCLK_MPLL, NULL, "mout_mpll"), -}; - -static const struct samsung_clock_alias exynos4x12_aliases[] __initconst = { - ALIAS(CLK_MOUT_MPLL_USER_C, NULL, "mout_mpll"), }; /* @@ -1355,14 +1341,14 @@ static const struct samsung_pll_rate_table exynos4x12_vpll_rates[] __initconst = }; static struct samsung_pll_clock exynos4210_plls[nr_plls] __initdata = { - [apll] = PLL_A(pll_4508, CLK_FOUT_APLL, "fout_apll", "fin_pll", - APLL_LOCK, APLL_CON0, "fout_apll", NULL), - [mpll] = PLL_A(pll_4508, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", - E4210_MPLL_LOCK, E4210_MPLL_CON0, "fout_mpll", NULL), - [epll] = PLL_A(pll_4600, CLK_FOUT_EPLL, "fout_epll", "fin_pll", - EPLL_LOCK, EPLL_CON0, "fout_epll", NULL), - [vpll] = PLL_A(pll_4650c, CLK_FOUT_VPLL, "fout_vpll", "mout_vpllsrc", - VPLL_LOCK, VPLL_CON0, "fout_vpll", NULL), + [apll] = PLL(pll_4508, CLK_FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON0, NULL), + [mpll] = PLL(pll_4508, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", + E4210_MPLL_LOCK, E4210_MPLL_CON0, NULL), + [epll] = PLL(pll_4600, CLK_FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), + [vpll] = PLL(pll_4650c, CLK_FOUT_VPLL, "fout_vpll", "mout_vpllsrc", + VPLL_LOCK, VPLL_CON0, NULL), }; static struct samsung_pll_clock exynos4x12_plls[nr_plls] __initdata = { @@ -1416,24 +1402,6 @@ static const struct exynos_cpuclk_cfg_data e4210_armclk_d[] __initconst = { { 0 }, }; -static const struct exynos_cpuclk_cfg_data e4212_armclk_d[] __initconst = { - { 1500000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4210_CPU_DIV1(2, 6), }, - { 1400000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4210_CPU_DIV1(2, 6), }, - { 1300000, E4210_CPU_DIV0(2, 1, 5, 0, 7, 3), E4210_CPU_DIV1(2, 5), }, - { 1200000, E4210_CPU_DIV0(2, 1, 5, 0, 7, 3), E4210_CPU_DIV1(2, 5), }, - { 1100000, E4210_CPU_DIV0(2, 1, 4, 0, 6, 3), E4210_CPU_DIV1(2, 4), }, - { 1000000, E4210_CPU_DIV0(1, 1, 4, 0, 5, 2), E4210_CPU_DIV1(2, 4), }, - { 900000, E4210_CPU_DIV0(1, 1, 3, 0, 5, 2), E4210_CPU_DIV1(2, 3), }, - { 800000, E4210_CPU_DIV0(1, 1, 3, 0, 5, 2), E4210_CPU_DIV1(2, 3), }, - { 700000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), }, - { 600000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), }, - { 500000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), }, - { 400000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), }, - { 300000, E4210_CPU_DIV0(1, 1, 2, 0, 4, 2), E4210_CPU_DIV1(2, 3), }, - { 200000, E4210_CPU_DIV0(1, 1, 1, 0, 3, 1), E4210_CPU_DIV1(2, 3), }, - { 0 }, -}; - #define E4412_CPU_DIV1(cores, hpm, copy) \ (((cores) << 8) | ((hpm) << 4) | ((copy) << 0)) @@ -1527,8 +1495,6 @@ static void __init exynos4_clk_init(struct device_node *np, ARRAY_SIZE(exynos4210_div_clks)); samsung_clk_register_gate(ctx, exynos4210_gate_clks, ARRAY_SIZE(exynos4210_gate_clks)); - samsung_clk_register_alias(ctx, exynos4210_aliases, - ARRAY_SIZE(exynos4210_aliases)); samsung_clk_register_fixed_factor(ctx, exynos4210_fixed_factor_clks, ARRAY_SIZE(exynos4210_fixed_factor_clks)); @@ -1537,32 +1503,31 @@ static void __init exynos4_clk_init(struct device_node *np, e4210_armclk_d, ARRAY_SIZE(e4210_armclk_d), CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); } else { + struct resource res; + samsung_clk_register_mux(ctx, exynos4x12_mux_clks, ARRAY_SIZE(exynos4x12_mux_clks)); samsung_clk_register_div(ctx, exynos4x12_div_clks, ARRAY_SIZE(exynos4x12_div_clks)); samsung_clk_register_gate(ctx, exynos4x12_gate_clks, ARRAY_SIZE(exynos4x12_gate_clks)); - samsung_clk_register_alias(ctx, exynos4x12_aliases, - ARRAY_SIZE(exynos4x12_aliases)); samsung_clk_register_fixed_factor(ctx, exynos4x12_fixed_factor_clks, ARRAY_SIZE(exynos4x12_fixed_factor_clks)); - if (of_machine_is_compatible("samsung,exynos4412")) { - exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk", - mout_core_p4x12[0], mout_core_p4x12[1], 0x14200, - e4412_armclk_d, ARRAY_SIZE(e4412_armclk_d), - CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); - } else { - exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk", - mout_core_p4x12[0], mout_core_p4x12[1], 0x14200, - e4212_armclk_d, ARRAY_SIZE(e4212_armclk_d), - CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); + + of_address_to_resource(np, 0, &res); + if (resource_size(&res) > 0x18000) { + samsung_clk_register_div(ctx, exynos4x12_isp_div_clks, + ARRAY_SIZE(exynos4x12_isp_div_clks)); + samsung_clk_register_gate(ctx, exynos4x12_isp_gate_clks, + ARRAY_SIZE(exynos4x12_isp_gate_clks)); } - } - samsung_clk_register_alias(ctx, exynos4_aliases, - ARRAY_SIZE(exynos4_aliases)); + exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk", + mout_core_p4x12[0], mout_core_p4x12[1], 0x14200, + e4412_armclk_d, ARRAY_SIZE(e4412_armclk_d), + CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); + } if (soc == EXYNOS4X12) exynos4x12_core_down_clock(); diff --git a/drivers/clk/samsung/clk-exynos4412-isp.c b/drivers/clk/samsung/clk-exynos4412-isp.c new file mode 100644 index 000000000000..d5f1ccb36300 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos4412-isp.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Author: Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for Exynos4412 ISP module. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +/* Exynos4x12 specific registers, which belong to ISP power domain */ +#define E4X12_DIV_ISP0 0x0300 +#define E4X12_DIV_ISP1 0x0304 +#define E4X12_GATE_ISP0 0x0800 +#define E4X12_GATE_ISP1 0x0804 + +/* + * Support for CMU save/restore across system suspends + */ +static struct samsung_clk_reg_dump *exynos4x12_save_isp; + +static const unsigned long exynos4x12_clk_isp_save[] __initconst = { + E4X12_DIV_ISP0, + E4X12_DIV_ISP1, + E4X12_GATE_ISP0, + E4X12_GATE_ISP1, +}; + +PNAME(mout_user_aclk400_mcuisp_p4x12) = { "fin_pll", "div_aclk400_mcuisp", }; + +static struct samsung_div_clock exynos4x12_isp_div_clks[] = { + DIV(CLK_ISP_DIV_ISP0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3), + DIV(CLK_ISP_DIV_ISP1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3), + DIV(CLK_ISP_DIV_MCUISP0, "div_mcuisp0", "aclk400_mcuisp", + E4X12_DIV_ISP1, 4, 3), + DIV(CLK_ISP_DIV_MCUISP1, "div_mcuisp1", "div_mcuisp0", + E4X12_DIV_ISP1, 8, 3), + DIV(0, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3), +}; + +static struct samsung_gate_clock exynos4x12_isp_gate_clks[] = { + GATE(CLK_ISP_FIMC_ISP, "isp", "aclk200", E4X12_GATE_ISP0, 0, 0, 0), + GATE(CLK_ISP_FIMC_DRC, "drc", "aclk200", E4X12_GATE_ISP0, 1, 0, 0), + GATE(CLK_ISP_FIMC_FD, "fd", "aclk200", E4X12_GATE_ISP0, 2, 0, 0), + GATE(CLK_ISP_FIMC_LITE0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, 0, 0), + GATE(CLK_ISP_FIMC_LITE1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, 0, 0), + GATE(CLK_ISP_MCUISP, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5, 0, 0), + GATE(CLK_ISP_GICISP, "gicisp", "aclk200", E4X12_GATE_ISP0, 7, 0, 0), + GATE(CLK_ISP_SMMU_ISP, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8, 0, 0), + GATE(CLK_ISP_SMMU_DRC, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9, 0, 0), + GATE(CLK_ISP_SMMU_FD, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10, 0, 0), + GATE(CLK_ISP_SMMU_LITE0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11, + 0, 0), + GATE(CLK_ISP_SMMU_LITE1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12, + 0, 0), + GATE(CLK_ISP_PPMUISPMX, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20, + 0, 0), + GATE(CLK_ISP_PPMUISPX, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21, + 0, 0), + GATE(CLK_ISP_MCUCTL_ISP, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23, + 0, 0), + GATE(CLK_ISP_MPWM_ISP, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24, + 0, 0), + GATE(CLK_ISP_I2C0_ISP, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25, + 0, 0), + GATE(CLK_ISP_I2C1_ISP, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26, + 0, 0), + GATE(CLK_ISP_MTCADC_ISP, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27, + 0, 0), + GATE(CLK_ISP_PWM_ISP, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28, 0, 0), + GATE(CLK_ISP_WDT_ISP, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30, 0, 0), + GATE(CLK_ISP_UART_ISP, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31, + 0, 0), + GATE(CLK_ISP_ASYNCAXIM, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0, + 0, 0), + GATE(CLK_ISP_SMMU_ISPCX, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4, + 0, 0), + GATE(CLK_ISP_SPI0_ISP, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12, + 0, 0), + GATE(CLK_ISP_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, + 0, 0), +}; + +static int __maybe_unused exynos4x12_isp_clk_suspend(struct device *dev) +{ + struct samsung_clk_provider *ctx = dev_get_drvdata(dev); + + samsung_clk_save(ctx->reg_base, exynos4x12_save_isp, + ARRAY_SIZE(exynos4x12_clk_isp_save)); + return 0; +} + +static int __maybe_unused exynos4x12_isp_clk_resume(struct device *dev) +{ + struct samsung_clk_provider *ctx = dev_get_drvdata(dev); + + samsung_clk_restore(ctx->reg_base, exynos4x12_save_isp, + ARRAY_SIZE(exynos4x12_clk_isp_save)); + return 0; +} + +static int __init exynos4x12_isp_clk_probe(struct platform_device *pdev) +{ + struct samsung_clk_provider *ctx; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct resource *res; + void __iomem *reg_base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(reg_base)) { + dev_err(dev, "failed to map registers\n"); + return PTR_ERR(reg_base); + } + + exynos4x12_save_isp = samsung_clk_alloc_reg_dump(exynos4x12_clk_isp_save, + ARRAY_SIZE(exynos4x12_clk_isp_save)); + if (!exynos4x12_save_isp) + return -ENOMEM; + + ctx = samsung_clk_init(np, reg_base, CLK_NR_ISP_CLKS); + ctx->dev = dev; + + platform_set_drvdata(pdev, ctx); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + samsung_clk_register_div(ctx, exynos4x12_isp_div_clks, + ARRAY_SIZE(exynos4x12_isp_div_clks)); + samsung_clk_register_gate(ctx, exynos4x12_isp_gate_clks, + ARRAY_SIZE(exynos4x12_isp_gate_clks)); + + samsung_clk_of_add_provider(np, ctx); + pm_runtime_put(dev); + + return 0; +} + +static const struct of_device_id exynos4x12_isp_clk_of_match[] = { + { .compatible = "samsung,exynos4412-isp-clock", }, + { }, +}; + +static const struct dev_pm_ops exynos4x12_isp_pm_ops = { + SET_RUNTIME_PM_OPS(exynos4x12_isp_clk_suspend, + exynos4x12_isp_clk_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver exynos4x12_isp_clk_driver __refdata = { + .driver = { + .name = "exynos4x12-isp-clk", + .of_match_table = exynos4x12_isp_clk_of_match, + .suppress_bind_attrs = true, + .pm = &exynos4x12_isp_pm_ops, + }, + .probe = exynos4x12_isp_clk_probe, +}; + +static int __init exynos4x12_isp_clk_init(void) +{ + return platform_driver_register(&exynos4x12_isp_clk_driver); +} +core_initcall(exynos4x12_isp_clk_init); diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 27a227d6620c..9b073c98a891 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -293,14 +293,14 @@ static const struct samsung_mux_clock exynos5250_mux_clks[] __initconst = { /* * CMU_CPU */ - MUX_FA(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, - CLK_SET_RATE_PARENT, 0, "mout_apll"), - MUX_A(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"), + MUX_F(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, + CLK_SET_RATE_PARENT, 0), + MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1), /* * CMU_CORE */ - MUX_A(0, "mout_mpll", mout_mpll_p, SRC_CORE1, 8, 1, "mout_mpll"), + MUX(0, "mout_mpll", mout_mpll_p, SRC_CORE1, 8, 1), /* * CMU_TOP @@ -391,7 +391,7 @@ static const struct samsung_div_clock exynos5250_div_clks[] __initconst = { */ DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3), DIV(0, "div_apll", "mout_apll", DIV_CPU0, 24, 3), - DIV_A(0, "div_arm2", "div_arm", DIV_CPU0, 28, 3, "armclk"), + DIV(0, "div_arm2", "div_arm", DIV_CPU0, 28, 3), /* * CMU_TOP @@ -743,10 +743,10 @@ static const struct samsung_pll_rate_table apll_24mhz_tbl[] __initconst = { }; static struct samsung_pll_clock exynos5250_plls[nr_plls] __initdata = { - [apll] = PLL_A(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", - APLL_LOCK, APLL_CON0, "fout_apll", NULL), - [mpll] = PLL_A(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", - MPLL_LOCK, MPLL_CON0, "fout_mpll", NULL), + [apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, + APLL_CON0, NULL), + [mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", MPLL_LOCK, + MPLL_CON0, NULL), [bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", BPLL_LOCK, BPLL_CON0, NULL), [gpll] = PLL(pll_35xx, CLK_FOUT_GPLL, "fout_gpll", "fin_pll", GPLL_LOCK, diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 25601967d1cd..45d34f601e9e 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -600,8 +600,7 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = { TOP_SPARE2, 4, 1), MUX(0, "mout_aclk400_isp", mout_group1_p, SRC_TOP0, 0, 2), - MUX_A(0, "mout_aclk400_mscl", mout_group1_p, - SRC_TOP0, 4, 2, "aclk400_mscl"), + MUX(0, "mout_aclk400_mscl", mout_group1_p, SRC_TOP0, 4, 2), MUX(0, "mout_aclk400_wcore", mout_group1_p, SRC_TOP0, 16, 2), MUX(0, "mout_aclk100_noc", mout_group1_p, SRC_TOP0, 20, 2), @@ -998,7 +997,7 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = { GATE(0, "aclk400_isp", "mout_user_aclk400_isp", GATE_BUS_TOP, 16, 0, 0), GATE(0, "aclk400_mscl", "mout_user_aclk400_mscl", - GATE_BUS_TOP, 17, 0, 0), + GATE_BUS_TOP, 17, CLK_IS_CRITICAL, 0), GATE(0, "aclk200_disp1", "mout_user_aclk200_disp1", GATE_BUS_TOP, 18, CLK_IS_CRITICAL, 0), GATE(CLK_SCLK_MPHY_IXTAL24, "sclk_mphy_ixtal24", "mphy_refclk_ixtal24", diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 11343a597093..db270908037a 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -9,9 +9,13 @@ * Common Clock Framework support for Exynos5433 SoC. */ +#include #include #include #include +#include +#include +#include #include @@ -1991,6 +1995,14 @@ static const unsigned long fsys_clk_regs[] __initconst = { ENABLE_IP_FSYS1, }; +static const struct samsung_clk_reg_dump fsys_suspend_regs[] = { + { MUX_SEL_FSYS0, 0 }, + { MUX_SEL_FSYS1, 0 }, + { MUX_SEL_FSYS2, 0 }, + { MUX_SEL_FSYS3, 0 }, + { MUX_SEL_FSYS4, 0 }, +}; + static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = { /* PHY clocks from USBDRD30_PHY */ FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY, @@ -2296,16 +2308,11 @@ static const struct samsung_cmu_info fsys_cmu_info __initconst = { .nr_clk_ids = FSYS_NR_CLK, .clk_regs = fsys_clk_regs, .nr_clk_regs = ARRAY_SIZE(fsys_clk_regs), + .suspend_regs = fsys_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(fsys_suspend_regs), + .clk_name = "aclk_fsys_200", }; -static void __init exynos5433_cmu_fsys_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &fsys_cmu_info); -} - -CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys", - exynos5433_cmu_fsys_init); - /* * Register offset definitions for CMU_G2D */ @@ -2335,6 +2342,10 @@ static const unsigned long g2d_clk_regs[] __initconst = { DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D, }; +static const struct samsung_clk_reg_dump g2d_suspend_regs[] = { + { MUX_SEL_G2D0, 0 }, +}; + /* list of all parent clock list */ PNAME(mout_aclk_g2d_266_user_p) = { "oscclk", "aclk_g2d_266", }; PNAME(mout_aclk_g2d_400_user_p) = { "oscclk", "aclk_g2d_400", }; @@ -2420,16 +2431,11 @@ static const struct samsung_cmu_info g2d_cmu_info __initconst = { .nr_clk_ids = G2D_NR_CLK, .clk_regs = g2d_clk_regs, .nr_clk_regs = ARRAY_SIZE(g2d_clk_regs), + .suspend_regs = g2d_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(g2d_suspend_regs), + .clk_name = "aclk_g2d_400", }; -static void __init exynos5433_cmu_g2d_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &g2d_cmu_info); -} - -CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d", - exynos5433_cmu_g2d_init); - /* * Register offset definitions for CMU_DISP */ @@ -2494,6 +2500,18 @@ static const unsigned long disp_clk_regs[] __initconst = { CLKOUT_CMU_DISP_DIV_STAT, }; +static const struct samsung_clk_reg_dump disp_suspend_regs[] = { + /* PLL has to be enabled for suspend */ + { DISP_PLL_CON0, 0x85f40502 }, + /* ignore status of external PHY muxes during suspend to avoid hangs */ + { MUX_IGNORE_DISP2, 0x00111111 }, + { MUX_SEL_DISP0, 0 }, + { MUX_SEL_DISP1, 0 }, + { MUX_SEL_DISP2, 0 }, + { MUX_SEL_DISP3, 0 }, + { MUX_SEL_DISP4, 0 }, +}; + /* list of all parent clock list */ PNAME(mout_disp_pll_p) = { "oscclk", "fout_disp_pll", }; PNAME(mout_sclk_dsim1_user_p) = { "oscclk", "sclk_dsim1_disp", }; @@ -2841,16 +2859,11 @@ static const struct samsung_cmu_info disp_cmu_info __initconst = { .nr_clk_ids = DISP_NR_CLK, .clk_regs = disp_clk_regs, .nr_clk_regs = ARRAY_SIZE(disp_clk_regs), + .suspend_regs = disp_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(disp_suspend_regs), + .clk_name = "aclk_disp_333", }; -static void __init exynos5433_cmu_disp_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &disp_cmu_info); -} - -CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp", - exynos5433_cmu_disp_init); - /* * Register offset definitions for CMU_AUD */ @@ -2885,6 +2898,11 @@ static const unsigned long aud_clk_regs[] __initconst = { ENABLE_IP_AUD1, }; +static const struct samsung_clk_reg_dump aud_suspend_regs[] = { + { MUX_SEL_AUD0, 0 }, + { MUX_SEL_AUD1, 0 }, +}; + /* list of all parent clock list */ PNAME(mout_aud_pll_user_aud_p) = { "oscclk", "fout_aud_pll", }; PNAME(mout_sclk_aud_pcm_p) = { "mout_aud_pll_user", "ioclk_audiocdclk0",}; @@ -3011,16 +3029,11 @@ static const struct samsung_cmu_info aud_cmu_info __initconst = { .nr_clk_ids = AUD_NR_CLK, .clk_regs = aud_clk_regs, .nr_clk_regs = ARRAY_SIZE(aud_clk_regs), + .suspend_regs = aud_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(aud_suspend_regs), + .clk_name = "fout_aud_pll", }; -static void __init exynos5433_cmu_aud_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &aud_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud", - exynos5433_cmu_aud_init); - - /* * Register offset definitions for CMU_BUS{0|1|2} */ @@ -3222,6 +3235,10 @@ static const unsigned long g3d_clk_regs[] __initconst = { CLK_STOPCTRL, }; +static const struct samsung_clk_reg_dump g3d_suspend_regs[] = { + { MUX_SEL_G3D, 0 }, +}; + /* list of all parent clock list */ PNAME(mout_aclk_g3d_400_p) = { "mout_g3d_pll", "aclk_g3d_400", }; PNAME(mout_g3d_pll_p) = { "oscclk", "fout_g3d_pll", }; @@ -3295,15 +3312,11 @@ static const struct samsung_cmu_info g3d_cmu_info __initconst = { .nr_clk_ids = G3D_NR_CLK, .clk_regs = g3d_clk_regs, .nr_clk_regs = ARRAY_SIZE(g3d_clk_regs), + .suspend_regs = g3d_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(g3d_suspend_regs), + .clk_name = "aclk_g3d_400", }; -static void __init exynos5433_cmu_g3d_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &g3d_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d", - exynos5433_cmu_g3d_init); - /* * Register offset definitions for CMU_GSCL */ @@ -3342,6 +3355,12 @@ static const unsigned long gscl_clk_regs[] __initconst = { ENABLE_IP_GSCL_SECURE_SMMU_GSCL2, }; +static const struct samsung_clk_reg_dump gscl_suspend_regs[] = { + { MUX_SEL_GSCL, 0 }, + { ENABLE_ACLK_GSCL, 0xfff }, + { ENABLE_PCLK_GSCL, 0xff }, +}; + /* list of all parent clock list */ PNAME(aclk_gscl_111_user_p) = { "oscclk", "aclk_gscl_111", }; PNAME(aclk_gscl_333_user_p) = { "oscclk", "aclk_gscl_333", }; @@ -3436,15 +3455,11 @@ static const struct samsung_cmu_info gscl_cmu_info __initconst = { .nr_clk_ids = GSCL_NR_CLK, .clk_regs = gscl_clk_regs, .nr_clk_regs = ARRAY_SIZE(gscl_clk_regs), + .suspend_regs = gscl_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(gscl_suspend_regs), + .clk_name = "aclk_gscl_111", }; -static void __init exynos5433_cmu_gscl_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &gscl_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl", - exynos5433_cmu_gscl_init); - /* * Register offset definitions for CMU_APOLLO */ @@ -3970,6 +3985,11 @@ static const unsigned long mscl_clk_regs[] __initconst = { ENABLE_IP_MSCL_SECURE_SMMU_JPEG, }; +static const struct samsung_clk_reg_dump mscl_suspend_regs[] = { + { MUX_SEL_MSCL0, 0 }, + { MUX_SEL_MSCL1, 0 }, +}; + /* list of all parent clock list */ PNAME(mout_sclk_jpeg_user_p) = { "oscclk", "sclk_jpeg_mscl", }; PNAME(mout_aclk_mscl_400_user_p) = { "oscclk", "aclk_mscl_400", }; @@ -4082,15 +4102,11 @@ static const struct samsung_cmu_info mscl_cmu_info __initconst = { .nr_clk_ids = MSCL_NR_CLK, .clk_regs = mscl_clk_regs, .nr_clk_regs = ARRAY_SIZE(mscl_clk_regs), + .suspend_regs = mscl_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(mscl_suspend_regs), + .clk_name = "aclk_mscl_400", }; -static void __init exynos5433_cmu_mscl_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &mscl_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl", - exynos5433_cmu_mscl_init); - /* * Register offset definitions for CMU_MFC */ @@ -4120,6 +4136,10 @@ static const unsigned long mfc_clk_regs[] __initconst = { ENABLE_IP_MFC_SECURE_SMMU_MFC, }; +static const struct samsung_clk_reg_dump mfc_suspend_regs[] = { + { MUX_SEL_MFC, 0 }, +}; + PNAME(mout_aclk_mfc_400_user_p) = { "oscclk", "aclk_mfc_400", }; static const struct samsung_mux_clock mfc_mux_clks[] __initconst = { @@ -4190,15 +4210,11 @@ static const struct samsung_cmu_info mfc_cmu_info __initconst = { .nr_clk_ids = MFC_NR_CLK, .clk_regs = mfc_clk_regs, .nr_clk_regs = ARRAY_SIZE(mfc_clk_regs), + .suspend_regs = mfc_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(mfc_suspend_regs), + .clk_name = "aclk_mfc_400", }; -static void __init exynos5433_cmu_mfc_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &mfc_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc", - exynos5433_cmu_mfc_init); - /* * Register offset definitions for CMU_HEVC */ @@ -4228,6 +4244,10 @@ static const unsigned long hevc_clk_regs[] __initconst = { ENABLE_IP_HEVC_SECURE_SMMU_HEVC, }; +static const struct samsung_clk_reg_dump hevc_suspend_regs[] = { + { MUX_SEL_HEVC, 0 }, +}; + PNAME(mout_aclk_hevc_400_user_p) = { "oscclk", "aclk_hevc_400", }; static const struct samsung_mux_clock hevc_mux_clks[] __initconst = { @@ -4300,15 +4320,11 @@ static const struct samsung_cmu_info hevc_cmu_info __initconst = { .nr_clk_ids = HEVC_NR_CLK, .clk_regs = hevc_clk_regs, .nr_clk_regs = ARRAY_SIZE(hevc_clk_regs), + .suspend_regs = hevc_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(hevc_suspend_regs), + .clk_name = "aclk_hevc_400", }; -static void __init exynos5433_cmu_hevc_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &hevc_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc", - exynos5433_cmu_hevc_init); - /* * Register offset definitions for CMU_ISP */ @@ -4342,6 +4358,10 @@ static const unsigned long isp_clk_regs[] __initconst = { ENABLE_IP_ISP3, }; +static const struct samsung_clk_reg_dump isp_suspend_regs[] = { + { MUX_SEL_ISP, 0 }, +}; + PNAME(mout_aclk_isp_dis_400_user_p) = { "oscclk", "aclk_isp_dis_400", }; PNAME(mout_aclk_isp_400_user_p) = { "oscclk", "aclk_isp_400", }; @@ -4553,15 +4573,11 @@ static const struct samsung_cmu_info isp_cmu_info __initconst = { .nr_clk_ids = ISP_NR_CLK, .clk_regs = isp_clk_regs, .nr_clk_regs = ARRAY_SIZE(isp_clk_regs), + .suspend_regs = isp_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(isp_suspend_regs), + .clk_name = "aclk_isp_400", }; -static void __init exynos5433_cmu_isp_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &isp_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp", - exynos5433_cmu_isp_init); - /* * Register offset definitions for CMU_CAM0 */ @@ -4625,6 +4641,15 @@ static const unsigned long cam0_clk_regs[] __initconst = { ENABLE_IP_CAM02, ENABLE_IP_CAM03, }; + +static const struct samsung_clk_reg_dump cam0_suspend_regs[] = { + { MUX_SEL_CAM00, 0 }, + { MUX_SEL_CAM01, 0 }, + { MUX_SEL_CAM02, 0 }, + { MUX_SEL_CAM03, 0 }, + { MUX_SEL_CAM04, 0 }, +}; + PNAME(mout_aclk_cam0_333_user_p) = { "oscclk", "aclk_cam0_333", }; PNAME(mout_aclk_cam0_400_user_p) = { "oscclk", "aclk_cam0_400", }; PNAME(mout_aclk_cam0_552_user_p) = { "oscclk", "aclk_cam0_552", }; @@ -5030,15 +5055,11 @@ static const struct samsung_cmu_info cam0_cmu_info __initconst = { .nr_clk_ids = CAM0_NR_CLK, .clk_regs = cam0_clk_regs, .nr_clk_regs = ARRAY_SIZE(cam0_clk_regs), + .suspend_regs = cam0_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(cam0_suspend_regs), + .clk_name = "aclk_cam0_400", }; -static void __init exynos5433_cmu_cam0_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &cam0_cmu_info); -} -CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0", - exynos5433_cmu_cam0_init); - /* * Register offset definitions for CMU_CAM1 */ @@ -5085,6 +5106,12 @@ static const unsigned long cam1_clk_regs[] __initconst = { ENABLE_IP_CAM12, }; +static const struct samsung_clk_reg_dump cam1_suspend_regs[] = { + { MUX_SEL_CAM10, 0 }, + { MUX_SEL_CAM11, 0 }, + { MUX_SEL_CAM12, 0 }, +}; + PNAME(mout_sclk_isp_uart_user_p) = { "oscclk", "sclk_isp_uart_cam1", }; PNAME(mout_sclk_isp_spi1_user_p) = { "oscclk", "sclk_isp_spi1_cam1", }; PNAME(mout_sclk_isp_spi0_user_p) = { "oscclk", "sclk_isp_spi0_cam1", }; @@ -5403,11 +5430,223 @@ static const struct samsung_cmu_info cam1_cmu_info __initconst = { .nr_clk_ids = CAM1_NR_CLK, .clk_regs = cam1_clk_regs, .nr_clk_regs = ARRAY_SIZE(cam1_clk_regs), + .suspend_regs = cam1_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(cam1_suspend_regs), + .clk_name = "aclk_cam1_400", +}; + + +struct exynos5433_cmu_data { + struct samsung_clk_reg_dump *clk_save; + unsigned int nr_clk_save; + const struct samsung_clk_reg_dump *clk_suspend; + unsigned int nr_clk_suspend; + + struct clk *clk; + struct clk **pclks; + int nr_pclks; + + /* must be the last entry */ + struct samsung_clk_provider ctx; +}; + +static int __maybe_unused exynos5433_cmu_suspend(struct device *dev) +{ + struct exynos5433_cmu_data *data = dev_get_drvdata(dev); + int i; + + samsung_clk_save(data->ctx.reg_base, data->clk_save, + data->nr_clk_save); + + for (i = 0; i < data->nr_pclks; i++) + clk_prepare_enable(data->pclks[i]); + + /* for suspend some registers have to be set to certain values */ + samsung_clk_restore(data->ctx.reg_base, data->clk_suspend, + data->nr_clk_suspend); + + for (i = 0; i < data->nr_pclks; i++) + clk_disable_unprepare(data->pclks[i]); + + clk_disable_unprepare(data->clk); + + return 0; +} + +static int __maybe_unused exynos5433_cmu_resume(struct device *dev) +{ + struct exynos5433_cmu_data *data = dev_get_drvdata(dev); + int i; + + clk_prepare_enable(data->clk); + + for (i = 0; i < data->nr_pclks; i++) + clk_prepare_enable(data->pclks[i]); + + samsung_clk_restore(data->ctx.reg_base, data->clk_save, + data->nr_clk_save); + + for (i = 0; i < data->nr_pclks; i++) + clk_disable_unprepare(data->pclks[i]); + + return 0; +} + +static int __init exynos5433_cmu_probe(struct platform_device *pdev) +{ + const struct samsung_cmu_info *info; + struct exynos5433_cmu_data *data; + struct samsung_clk_provider *ctx; + struct device *dev = &pdev->dev; + struct resource *res; + void __iomem *reg_base; + int i; + + info = of_device_get_match_data(dev); + + data = devm_kzalloc(dev, sizeof(*data) + + sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids, + GFP_KERNEL); + if (!data) + return -ENOMEM; + ctx = &data->ctx; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(reg_base)) { + dev_err(dev, "failed to map registers\n"); + return PTR_ERR(reg_base); + } + + for (i = 0; i < info->nr_clk_ids; ++i) + ctx->clk_data.hws[i] = ERR_PTR(-ENOENT); + + ctx->clk_data.num = info->nr_clk_ids; + ctx->reg_base = reg_base; + ctx->dev = dev; + spin_lock_init(&ctx->lock); + + data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs, + info->nr_clk_regs); + data->nr_clk_save = info->nr_clk_regs; + data->clk_suspend = info->suspend_regs; + data->nr_clk_suspend = info->nr_suspend_regs; + data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks", + "#clock-cells"); + if (data->nr_pclks > 0) { + data->pclks = devm_kcalloc(dev, sizeof(struct clk *), + data->nr_pclks, GFP_KERNEL); + + for (i = 0; i < data->nr_pclks; i++) { + struct clk *clk = of_clk_get(dev->of_node, i); + + if (IS_ERR(clk)) + return PTR_ERR(clk); + data->pclks[i] = clk; + } + } + + if (info->clk_name) + data->clk = clk_get(dev, info->clk_name); + clk_prepare_enable(data->clk); + + platform_set_drvdata(pdev, data); + + /* + * Enable runtime PM here to allow the clock core using runtime PM + * for the registered clocks. Additionally, we increase the runtime + * PM usage count before registering the clocks, to prevent the + * clock core from runtime suspending the device. + */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + if (info->pll_clks) + samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks, + reg_base); + if (info->mux_clks) + samsung_clk_register_mux(ctx, info->mux_clks, + info->nr_mux_clks); + if (info->div_clks) + samsung_clk_register_div(ctx, info->div_clks, + info->nr_div_clks); + if (info->gate_clks) + samsung_clk_register_gate(ctx, info->gate_clks, + info->nr_gate_clks); + if (info->fixed_clks) + samsung_clk_register_fixed_rate(ctx, info->fixed_clks, + info->nr_fixed_clks); + if (info->fixed_factor_clks) + samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks, + info->nr_fixed_factor_clks); + + samsung_clk_of_add_provider(dev->of_node, ctx); + pm_runtime_put_sync(dev); + + return 0; +} + +static const struct of_device_id exynos5433_cmu_of_match[] = { + { + .compatible = "samsung,exynos5433-cmu-aud", + .data = &aud_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-cam0", + .data = &cam0_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-cam1", + .data = &cam1_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-disp", + .data = &disp_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-g2d", + .data = &g2d_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-g3d", + .data = &g3d_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-fsys", + .data = &fsys_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-gscl", + .data = &gscl_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-mfc", + .data = &mfc_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-hevc", + .data = &hevc_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-isp", + .data = &isp_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-mscl", + .data = &mscl_cmu_info, + }, { + }, +}; + +static const struct dev_pm_ops exynos5433_cmu_pm_ops = { + SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume, + NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver exynos5433_cmu_driver __refdata = { + .driver = { + .name = "exynos5433-cmu", + .of_match_table = exynos5433_cmu_of_match, + .suppress_bind_attrs = true, + .pm = &exynos5433_cmu_pm_ops, + }, + .probe = exynos5433_cmu_probe, }; -static void __init exynos5433_cmu_cam1_init(struct device_node *np) +static int __init exynos5433_cmu_init(void) { - samsung_cmu_register_one(np, &cam1_cmu_info); + return platform_driver_register(&exynos5433_cmu_driver); } -CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1", - exynos5433_cmu_cam1_init); +core_initcall(exynos5433_cmu_init); diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c index a80f3ef20801..b08bd54c5e76 100644 --- a/drivers/clk/samsung/clk-exynos5440.c +++ b/drivers/clk/samsung/clk-exynos5440.c @@ -53,8 +53,7 @@ static const struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __ /* mux clocks */ static const struct samsung_mux_clock exynos5440_mux_clks[] __initconst = { MUX(0, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1), - MUX_A(CLK_ARM_CLK, "arm_clk", mout_armclk_p, - CPU_CLK_STATUS, 0, 1, "armclk"), + MUX(CLK_ARM_CLK, "arm_clk", mout_armclk_p, CPU_CLK_STATUS, 0, 1), }; /* divider clocks */ @@ -117,6 +116,13 @@ static const struct samsung_pll_clock exynos5440_plls[] __initconst = { PLL(pll_2550x, CLK_CPLLB, "cpllb", "xtal", 0, 0x50, NULL), }; +/* + * Clock aliases for legacy clkdev look-up. + */ +static const struct samsung_clock_alias exynos5440_aliases[] __initconst = { + ALIAS(CLK_ARM_CLK, NULL, "armclk"), +}; + /* register exynos5440 clocks */ static void __init exynos5440_clk_init(struct device_node *np) { @@ -147,6 +153,8 @@ static void __init exynos5440_clk_init(struct device_node *np) ARRAY_SIZE(exynos5440_div_clks)); samsung_clk_register_gate(ctx, exynos5440_gate_clks, ARRAY_SIZE(exynos5440_gate_clks)); + samsung_clk_register_alias(ctx, exynos5440_aliases, + ARRAY_SIZE(exynos5440_aliases)); samsung_clk_of_add_provider(np, ctx); diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 037c61484098..1c4c7a3039f1 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -1388,7 +1388,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, pll->lock_reg = base + pll_clk->lock_offset; pll->con_reg = base + pll_clk->con_offset; - ret = clk_hw_register(NULL, &pll->hw); + ret = clk_hw_register(ctx->dev, &pll->hw); if (ret) { pr_err("%s: failed to register pll clock %s : %d\n", __func__, pll_clk->name, ret); @@ -1397,15 +1397,6 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, } samsung_clk_add_lookup(ctx, &pll->hw, pll_clk->id); - - if (!pll_clk->alias) - return; - - ret = clk_hw_register_clkdev(&pll->hw, pll_clk->alias, - pll_clk->dev_name); - if (ret) - pr_err("%s: failed to register lookup for %s : %d", - __func__, pll_clk->name, ret); } void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index abb935c42916..d94b85a42356 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -117,8 +117,8 @@ struct samsung_mux_clock s3c2443_common_muxes[] __initdata = { MUX(0, "epllref", epllref_p, CLKSRC, 7, 2), MUX(ESYSCLK, "esysclk", esysclk_p, CLKSRC, 6, 1), MUX(0, "mpllref", mpllref_p, CLKSRC, 3, 1), - MUX_A(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1, "msysclk"), - MUX_A(ARMCLK, "armclk", armclk_p, CLKDIV0, 13, 1, "armclk"), + MUX(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1), + MUX(ARMCLK, "armclk", armclk_p, CLKDIV0, 13, 1), MUX(0, "mux_i2s0", i2s0_p, CLKSRC, 14, 2), }; @@ -189,6 +189,10 @@ struct samsung_gate_clock s3c2443_common_gates[] __initdata = { }; struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { + ALIAS(MSYSCLK, NULL, "msysclk"), + ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(MPLL, NULL, "mpll"), + ALIAS(EPLL, NULL, "epll"), ALIAS(HCLK, NULL, "hclk"), ALIAS(HCLK_SSMC, NULL, "nand"), ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), @@ -221,9 +225,9 @@ struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { /* S3C2416 specific clocks */ static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = { - [mpll] = PLL(pll_6552_s3c2416, 0, "mpll", "mpllref", + [mpll] = PLL(pll_6552_s3c2416, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL), - [epll] = PLL(pll_6553, 0, "epll", "epllref", + [epll] = PLL(pll_6553, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL), }; @@ -275,9 +279,9 @@ struct samsung_clock_alias s3c2416_aliases[] __initdata = { /* S3C2443 specific clocks */ static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = { - [mpll] = PLL(pll_3000, 0, "mpll", "mpllref", + [mpll] = PLL(pll_3000, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL), - [epll] = PLL(pll_2126, 0, "epll", "epllref", + [epll] = PLL(pll_2126, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL), }; diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 7ce0fa86c5ff..8634884aa11c 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -134,7 +134,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, unsigned int idx, ret; for (idx = 0; idx < nr_clk; idx++, list++) { - clk_hw = clk_hw_register_fixed_rate(NULL, list->name, + clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name, list->parent_name, list->flags, list->fixed_rate); if (IS_ERR(clk_hw)) { pr_err("%s: failed to register clock %s\n", __func__, @@ -163,7 +163,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, unsigned int idx; for (idx = 0; idx < nr_clk; idx++, list++) { - clk_hw = clk_hw_register_fixed_factor(NULL, list->name, + clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name, list->parent_name, list->flags, list->mult, list->div); if (IS_ERR(clk_hw)) { pr_err("%s: failed to register clock %s\n", __func__, @@ -181,10 +181,10 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, unsigned int nr_clk) { struct clk_hw *clk_hw; - unsigned int idx, ret; + unsigned int idx; for (idx = 0; idx < nr_clk; idx++, list++) { - clk_hw = clk_hw_register_mux(NULL, list->name, + clk_hw = clk_hw_register_mux(ctx->dev, list->name, list->parent_names, list->num_parents, list->flags, ctx->reg_base + list->offset, list->shift, list->width, list->mux_flags, &ctx->lock); @@ -195,15 +195,6 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, } samsung_clk_add_lookup(ctx, clk_hw, list->id); - - /* register a clock lookup only if a clock alias is specified */ - if (list->alias) { - ret = clk_hw_register_clkdev(clk_hw, list->alias, - list->dev_name); - if (ret) - pr_err("%s: failed to register lookup %s\n", - __func__, list->alias); - } } } @@ -213,17 +204,17 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, unsigned int nr_clk) { struct clk_hw *clk_hw; - unsigned int idx, ret; + unsigned int idx; for (idx = 0; idx < nr_clk; idx++, list++) { if (list->table) - clk_hw = clk_hw_register_divider_table(NULL, + clk_hw = clk_hw_register_divider_table(ctx->dev, list->name, list->parent_name, list->flags, ctx->reg_base + list->offset, list->shift, list->width, list->div_flags, list->table, &ctx->lock); else - clk_hw = clk_hw_register_divider(NULL, list->name, + clk_hw = clk_hw_register_divider(ctx->dev, list->name, list->parent_name, list->flags, ctx->reg_base + list->offset, list->shift, list->width, list->div_flags, &ctx->lock); @@ -234,15 +225,6 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, } samsung_clk_add_lookup(ctx, clk_hw, list->id); - - /* register a clock lookup only if a clock alias is specified */ - if (list->alias) { - ret = clk_hw_register_clkdev(clk_hw, list->alias, - list->dev_name); - if (ret) - pr_err("%s: failed to register lookup %s\n", - __func__, list->alias); - } } } @@ -252,10 +234,10 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, unsigned int nr_clk) { struct clk_hw *clk_hw; - unsigned int idx, ret; + unsigned int idx; for (idx = 0; idx < nr_clk; idx++, list++) { - clk_hw = clk_hw_register_gate(NULL, list->name, list->parent_name, + clk_hw = clk_hw_register_gate(ctx->dev, list->name, list->parent_name, list->flags, ctx->reg_base + list->offset, list->bit_idx, list->gate_flags, &ctx->lock); if (IS_ERR(clk_hw)) { @@ -264,15 +246,6 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, continue; } - /* register a clock lookup only if a clock alias is specified */ - if (list->alias) { - ret = clk_hw_register_clkdev(clk_hw, list->alias, - list->dev_name); - if (ret) - pr_err("%s: failed to register lookup %s\n", - __func__, list->alias); - } - samsung_clk_add_lookup(ctx, clk_hw, list->id); } } diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index b8ca0dd3a38b..3880d2f9d582 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -24,6 +24,7 @@ */ struct samsung_clk_provider { void __iomem *reg_base; + struct device *dev; spinlock_t lock; /* clk_data must be the last entry due to variable lenght 'hws' array */ struct clk_hw_onecell_data clk_data; @@ -106,7 +107,6 @@ struct samsung_fixed_factor_clock { /** * struct samsung_mux_clock: information about mux clock * @id: platform specific id of the clock. - * @dev_name: name of the device to which this clock belongs. * @name: name of this mux clock. * @parent_names: array of pointer to parent clock names. * @num_parents: number of parents listed in @parent_names. @@ -115,11 +115,9 @@ struct samsung_fixed_factor_clock { * @shift: starting bit location of the mux control bit-field in @reg. * @width: width of the mux control bit-field in @reg. * @mux_flags: flags for mux-type clock. - * @alias: optional clock alias name to be assigned to this clock. */ struct samsung_mux_clock { unsigned int id; - const char *dev_name; const char *name; const char *const *parent_names; u8 num_parents; @@ -128,13 +126,11 @@ struct samsung_mux_clock { u8 shift; u8 width; u8 mux_flags; - const char *alias; }; -#define __MUX(_id, dname, cname, pnames, o, s, w, f, mf, a) \ +#define __MUX(_id, cname, pnames, o, s, w, f, mf) \ { \ .id = _id, \ - .dev_name = dname, \ .name = cname, \ .parent_names = pnames, \ .num_parents = ARRAY_SIZE(pnames), \ @@ -143,36 +139,26 @@ struct samsung_mux_clock { .shift = s, \ .width = w, \ .mux_flags = mf, \ - .alias = a, \ } #define MUX(_id, cname, pnames, o, s, w) \ - __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, NULL) - -#define MUX_A(_id, cname, pnames, o, s, w, a) \ - __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, a) + __MUX(_id, cname, pnames, o, s, w, 0, 0) #define MUX_F(_id, cname, pnames, o, s, w, f, mf) \ - __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL) - -#define MUX_FA(_id, cname, pnames, o, s, w, f, mf, a) \ - __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, a) + __MUX(_id, cname, pnames, o, s, w, f, mf) /** * @id: platform specific id of the clock. * struct samsung_div_clock: information about div clock - * @dev_name: name of the device to which this clock belongs. * @name: name of this div clock. * @parent_name: name of the parent clock. * @flags: optional flags for basic clock. * @offset: offset of the register for configuring the div. * @shift: starting bit location of the div control bit-field in @reg. * @div_flags: flags for div-type clock. - * @alias: optional clock alias name to be assigned to this clock. */ struct samsung_div_clock { unsigned int id; - const char *dev_name; const char *name; const char *parent_name; unsigned long flags; @@ -180,14 +166,12 @@ struct samsung_div_clock { u8 shift; u8 width; u8 div_flags; - const char *alias; struct clk_div_table *table; }; -#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a, t) \ +#define __DIV(_id, cname, pname, o, s, w, f, df, t) \ { \ .id = _id, \ - .dev_name = dname, \ .name = cname, \ .parent_name = pname, \ .flags = f, \ @@ -195,70 +179,51 @@ struct samsung_div_clock { .shift = s, \ .width = w, \ .div_flags = df, \ - .alias = a, \ .table = t, \ } #define DIV(_id, cname, pname, o, s, w) \ - __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, NULL) - -#define DIV_A(_id, cname, pname, o, s, w, a) \ - __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a, NULL) + __DIV(_id, cname, pname, o, s, w, 0, 0, NULL) #define DIV_F(_id, cname, pname, o, s, w, f, df) \ - __DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL, NULL) + __DIV(_id, cname, pname, o, s, w, f, df, NULL) #define DIV_T(_id, cname, pname, o, s, w, t) \ - __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, t) + __DIV(_id, cname, pname, o, s, w, 0, 0, t) /** * struct samsung_gate_clock: information about gate clock * @id: platform specific id of the clock. - * @dev_name: name of the device to which this clock belongs. * @name: name of this gate clock. * @parent_name: name of the parent clock. * @flags: optional flags for basic clock. * @offset: offset of the register for configuring the gate. * @bit_idx: bit index of the gate control bit-field in @reg. * @gate_flags: flags for gate-type clock. - * @alias: optional clock alias name to be assigned to this clock. */ struct samsung_gate_clock { unsigned int id; - const char *dev_name; const char *name; const char *parent_name; unsigned long flags; unsigned long offset; u8 bit_idx; u8 gate_flags; - const char *alias; }; -#define __GATE(_id, dname, cname, pname, o, b, f, gf, a) \ +#define __GATE(_id, cname, pname, o, b, f, gf) \ { \ .id = _id, \ - .dev_name = dname, \ .name = cname, \ .parent_name = pname, \ .flags = f, \ .offset = o, \ .bit_idx = b, \ .gate_flags = gf, \ - .alias = a, \ } #define GATE(_id, cname, pname, o, b, f, gf) \ - __GATE(_id, NULL, cname, pname, o, b, f, gf, NULL) - -#define GATE_A(_id, cname, pname, o, b, f, gf, a) \ - __GATE(_id, NULL, cname, pname, o, b, f, gf, a) - -#define GATE_D(_id, dname, cname, pname, o, b, f, gf) \ - __GATE(_id, dname, cname, pname, o, b, f, gf, NULL) - -#define GATE_DA(_id, dname, cname, pname, o, b, f, gf, a) \ - __GATE(_id, dname, cname, pname, o, b, f, gf, a) + __GATE(_id, cname, pname, o, b, f, gf) #define PNAME(x) static const char * const x[] __initconst @@ -275,18 +240,15 @@ struct samsung_clk_reg_dump { /** * struct samsung_pll_clock: information about pll clock * @id: platform specific id of the clock. - * @dev_name: name of the device to which this clock belongs. * @name: name of this pll clock. * @parent_name: name of the parent clock. * @flags: optional flags for basic clock. * @con_offset: offset of the register for configuring the PLL. * @lock_offset: offset of the register for locking the PLL. * @type: Type of PLL to be registered. - * @alias: optional clock alias name to be assigned to this clock. */ struct samsung_pll_clock { unsigned int id; - const char *dev_name; const char *name; const char *parent_name; unsigned long flags; @@ -294,31 +256,23 @@ struct samsung_pll_clock { int lock_offset; enum samsung_pll_type type; const struct samsung_pll_rate_table *rate_table; - const char *alias; }; -#define __PLL(_typ, _id, _dname, _name, _pname, _flags, _lock, _con, \ - _rtable, _alias) \ +#define __PLL(_typ, _id, _name, _pname, _flags, _lock, _con, _rtable) \ { \ .id = _id, \ .type = _typ, \ - .dev_name = _dname, \ .name = _name, \ .parent_name = _pname, \ - .flags = CLK_GET_RATE_NOCACHE, \ + .flags = _flags, \ .con_offset = _con, \ .lock_offset = _lock, \ .rate_table = _rtable, \ - .alias = _alias, \ } #define PLL(_typ, _id, _name, _pname, _lock, _con, _rtable) \ - __PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE, \ - _lock, _con, _rtable, _name) - -#define PLL_A(_typ, _id, _name, _pname, _lock, _con, _alias, _rtable) \ - __PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE, \ - _lock, _con, _rtable, _alias) + __PLL(_typ, _id, _name, _pname, CLK_GET_RATE_NOCACHE, _lock, \ + _con, _rtable) struct samsung_clock_reg_cache { struct list_head node; @@ -352,6 +306,12 @@ struct samsung_cmu_info { /* list and number of clocks registers */ const unsigned long *clk_regs; unsigned int nr_clk_regs; + + /* list and number of clocks registers to set before suspend */ + const struct samsung_clk_reg_dump *suspend_regs; + unsigned int nr_suspend_regs; + /* name of the parent clock needed for CMU register access */ + const char *clk_name; }; extern struct samsung_clk_provider *__init samsung_clk_init( diff --git a/drivers/clk/sirf/clk-atlas6.c b/drivers/clk/sirf/clk-atlas6.c index 665fa681b2e1..0cd11e6893af 100644 --- a/drivers/clk/sirf/clk-atlas6.c +++ b/drivers/clk/sirf/clk-atlas6.c @@ -42,7 +42,7 @@ static struct clk_dmn clk_mmc45 = { }, }; -static struct clk_init_data clk_nand_init = { +static const struct clk_init_data clk_nand_init = { .name = "nand", .ops = &dmn_ops, .parent_names = dmn_clk_parents, diff --git a/drivers/clk/sirf/clk-atlas7.c b/drivers/clk/sirf/clk-atlas7.c index d0c6c9a2d06a..be012b4bab46 100644 --- a/drivers/clk/sirf/clk-atlas7.c +++ b/drivers/clk/sirf/clk-atlas7.c @@ -392,7 +392,7 @@ static const char * const pll_clk_parents[] = { "xin", }; -static struct clk_init_data clk_cpupll_init = { +static const struct clk_init_data clk_cpupll_init = { .name = "cpupll_vco", .ops = &ab_pll_ops, .parent_names = pll_clk_parents, @@ -406,7 +406,7 @@ static struct clk_pll clk_cpupll = { }, }; -static struct clk_init_data clk_mempll_init = { +static const struct clk_init_data clk_mempll_init = { .name = "mempll_vco", .ops = &ab_pll_ops, .parent_names = pll_clk_parents, @@ -420,7 +420,7 @@ static struct clk_pll clk_mempll = { }, }; -static struct clk_init_data clk_sys0pll_init = { +static const struct clk_init_data clk_sys0pll_init = { .name = "sys0pll_vco", .ops = &ab_pll_ops, .parent_names = pll_clk_parents, @@ -434,7 +434,7 @@ static struct clk_pll clk_sys0pll = { }, }; -static struct clk_init_data clk_sys1pll_init = { +static const struct clk_init_data clk_sys1pll_init = { .name = "sys1pll_vco", .ops = &ab_pll_ops, .parent_names = pll_clk_parents, @@ -448,7 +448,7 @@ static struct clk_pll clk_sys1pll = { }, }; -static struct clk_init_data clk_sys2pll_init = { +static const struct clk_init_data clk_sys2pll_init = { .name = "sys2pll_vco", .ops = &ab_pll_ops, .parent_names = pll_clk_parents, @@ -462,7 +462,7 @@ static struct clk_pll clk_sys2pll = { }, }; -static struct clk_init_data clk_sys3pll_init = { +static const struct clk_init_data clk_sys3pll_init = { .name = "sys3pll_vco", .ops = &ab_pll_ops, .parent_names = pll_clk_parents, @@ -596,7 +596,7 @@ static const char * const audiodto_clk_parents[] = { "sys3pll_clk1", }; -static struct clk_init_data clk_audiodto_init = { +static const struct clk_init_data clk_audiodto_init = { .name = "audio_dto", .ops = &dto_ops, .parent_names = audiodto_clk_parents, @@ -617,7 +617,7 @@ static const char * const disp0dto_clk_parents[] = { "sys3pll_clk1", }; -static struct clk_init_data clk_disp0dto_init = { +static const struct clk_init_data clk_disp0dto_init = { .name = "disp0_dto", .ops = &dto_ops, .parent_names = disp0dto_clk_parents, @@ -638,7 +638,7 @@ static const char * const disp1dto_clk_parents[] = { "sys3pll_clk1", }; -static struct clk_init_data clk_disp1dto_init = { +static const struct clk_init_data clk_disp1dto_init = { .name = "disp1_dto", .ops = &dto_ops, .parent_names = disp1dto_clk_parents, diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c index 77e1e2491689..d8f9efa5129a 100644 --- a/drivers/clk/sirf/clk-common.c +++ b/drivers/clk/sirf/clk-common.c @@ -184,7 +184,7 @@ static unsigned long cpu_clk_recalc_rate(struct clk_hw *hw, return clk_hw_get_rate(parent_clk); } -static struct clk_ops std_pll_ops = { +static const struct clk_ops std_pll_ops = { .recalc_rate = pll_clk_recalc_rate, .round_rate = pll_clk_round_rate, .set_rate = pll_clk_set_rate, @@ -194,21 +194,21 @@ static const char * const pll_clk_parents[] = { "osc", }; -static struct clk_init_data clk_pll1_init = { +static const struct clk_init_data clk_pll1_init = { .name = "pll1", .ops = &std_pll_ops, .parent_names = pll_clk_parents, .num_parents = ARRAY_SIZE(pll_clk_parents), }; -static struct clk_init_data clk_pll2_init = { +static const struct clk_init_data clk_pll2_init = { .name = "pll2", .ops = &std_pll_ops, .parent_names = pll_clk_parents, .num_parents = ARRAY_SIZE(pll_clk_parents), }; -static struct clk_init_data clk_pll3_init = { +static const struct clk_init_data clk_pll3_init = { .name = "pll3", .ops = &std_pll_ops, .parent_names = pll_clk_parents, @@ -265,13 +265,13 @@ static unsigned long usb_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long pa return (reg & SIRFSOC_USBPHY_PLL_BYPASS) ? parent_rate : 48*MHZ; } -static struct clk_ops usb_pll_ops = { +static const struct clk_ops usb_pll_ops = { .enable = usb_pll_clk_enable, .disable = usb_pll_clk_disable, .recalc_rate = usb_pll_clk_recalc_rate, }; -static struct clk_init_data clk_usb_pll_init = { +static const struct clk_init_data clk_usb_pll_init = { .name = "usb_pll", .ops = &usb_pll_ops, .parent_names = pll_clk_parents, @@ -437,7 +437,7 @@ static int cpu_clk_set_rate(struct clk_hw *hw, unsigned long rate, return ret2 ? ret2 : ret1; } -static struct clk_ops msi_ops = { +static const struct clk_ops msi_ops = { .set_rate = dmn_clk_set_rate, .round_rate = dmn_clk_round_rate, .recalc_rate = dmn_clk_recalc_rate, @@ -445,7 +445,7 @@ static struct clk_ops msi_ops = { .get_parent = dmn_clk_get_parent, }; -static struct clk_init_data clk_mem_init = { +static const struct clk_init_data clk_mem_init = { .name = "mem", .ops = &msi_ops, .parent_names = dmn_clk_parents, @@ -459,7 +459,7 @@ static struct clk_dmn clk_mem = { }, }; -static struct clk_init_data clk_sys_init = { +static const struct clk_init_data clk_sys_init = { .name = "sys", .ops = &msi_ops, .parent_names = dmn_clk_parents, @@ -474,7 +474,7 @@ static struct clk_dmn clk_sys = { }, }; -static struct clk_init_data clk_io_init = { +static const struct clk_init_data clk_io_init = { .name = "io", .ops = &msi_ops, .parent_names = dmn_clk_parents, @@ -488,7 +488,7 @@ static struct clk_dmn clk_io = { }, }; -static struct clk_ops cpu_ops = { +static const struct clk_ops cpu_ops = { .set_parent = dmn_clk_set_parent, .get_parent = dmn_clk_get_parent, .set_rate = cpu_clk_set_rate, @@ -496,7 +496,7 @@ static struct clk_ops cpu_ops = { .recalc_rate = cpu_clk_recalc_rate, }; -static struct clk_init_data clk_cpu_init = { +static const struct clk_init_data clk_cpu_init = { .name = "cpu", .ops = &cpu_ops, .parent_names = dmn_clk_parents, @@ -511,7 +511,7 @@ static struct clk_dmn clk_cpu = { }, }; -static struct clk_ops dmn_ops = { +static const struct clk_ops dmn_ops = { .is_enabled = std_clk_is_enabled, .enable = std_clk_enable, .disable = std_clk_disable, @@ -524,7 +524,7 @@ static struct clk_ops dmn_ops = { /* dsp, gfx, mm, lcd and vpp domain */ -static struct clk_init_data clk_dsp_init = { +static const struct clk_init_data clk_dsp_init = { .name = "dsp", .ops = &dmn_ops, .parent_names = dmn_clk_parents, @@ -539,7 +539,7 @@ static struct clk_dmn clk_dsp = { }, }; -static struct clk_init_data clk_gfx_init = { +static const struct clk_init_data clk_gfx_init = { .name = "gfx", .ops = &dmn_ops, .parent_names = dmn_clk_parents, @@ -554,7 +554,7 @@ static struct clk_dmn clk_gfx = { }, }; -static struct clk_init_data clk_mm_init = { +static const struct clk_init_data clk_mm_init = { .name = "mm", .ops = &dmn_ops, .parent_names = dmn_clk_parents, @@ -574,7 +574,7 @@ static struct clk_dmn clk_mm = { */ #define clk_gfx2d clk_mm -static struct clk_init_data clk_lcd_init = { +static const struct clk_init_data clk_lcd_init = { .name = "lcd", .ops = &dmn_ops, .parent_names = dmn_clk_parents, @@ -589,7 +589,7 @@ static struct clk_dmn clk_lcd = { }, }; -static struct clk_init_data clk_vpp_init = { +static const struct clk_init_data clk_vpp_init = { .name = "vpp", .ops = &dmn_ops, .parent_names = dmn_clk_parents, @@ -604,21 +604,21 @@ static struct clk_dmn clk_vpp = { }, }; -static struct clk_init_data clk_mmc01_init = { +static const struct clk_init_data clk_mmc01_init = { .name = "mmc01", .ops = &dmn_ops, .parent_names = dmn_clk_parents, .num_parents = ARRAY_SIZE(dmn_clk_parents), }; -static struct clk_init_data clk_mmc23_init = { +static const struct clk_init_data clk_mmc23_init = { .name = "mmc23", .ops = &dmn_ops, .parent_names = dmn_clk_parents, .num_parents = ARRAY_SIZE(dmn_clk_parents), }; -static struct clk_init_data clk_mmc45_init = { +static const struct clk_init_data clk_mmc45_init = { .name = "mmc45", .ops = &dmn_ops, .parent_names = dmn_clk_parents, @@ -679,13 +679,13 @@ static const char * const std_clk_io_parents[] = { "io", }; -static struct clk_ops ios_ops = { +static const struct clk_ops ios_ops = { .is_enabled = std_clk_is_enabled, .enable = std_clk_enable, .disable = std_clk_disable, }; -static struct clk_init_data clk_cphif_init = { +static const struct clk_init_data clk_cphif_init = { .name = "cphif", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -699,7 +699,7 @@ static struct clk_std clk_cphif = { }, }; -static struct clk_init_data clk_dmac0_init = { +static const struct clk_init_data clk_dmac0_init = { .name = "dmac0", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -713,7 +713,7 @@ static struct clk_std clk_dmac0 = { }, }; -static struct clk_init_data clk_dmac1_init = { +static const struct clk_init_data clk_dmac1_init = { .name = "dmac1", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -727,7 +727,7 @@ static struct clk_std clk_dmac1 = { }, }; -static struct clk_init_data clk_audio_init = { +static const struct clk_init_data clk_audio_init = { .name = "audio", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -741,7 +741,7 @@ static struct clk_std clk_audio = { }, }; -static struct clk_init_data clk_uart0_init = { +static const struct clk_init_data clk_uart0_init = { .name = "uart0", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -755,7 +755,7 @@ static struct clk_std clk_uart0 = { }, }; -static struct clk_init_data clk_uart1_init = { +static const struct clk_init_data clk_uart1_init = { .name = "uart1", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -769,7 +769,7 @@ static struct clk_std clk_uart1 = { }, }; -static struct clk_init_data clk_uart2_init = { +static const struct clk_init_data clk_uart2_init = { .name = "uart2", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -783,7 +783,7 @@ static struct clk_std clk_uart2 = { }, }; -static struct clk_init_data clk_usp0_init = { +static const struct clk_init_data clk_usp0_init = { .name = "usp0", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -797,7 +797,7 @@ static struct clk_std clk_usp0 = { }, }; -static struct clk_init_data clk_usp1_init = { +static const struct clk_init_data clk_usp1_init = { .name = "usp1", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -811,7 +811,7 @@ static struct clk_std clk_usp1 = { }, }; -static struct clk_init_data clk_usp2_init = { +static const struct clk_init_data clk_usp2_init = { .name = "usp2", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -825,7 +825,7 @@ static struct clk_std clk_usp2 = { }, }; -static struct clk_init_data clk_vip_init = { +static const struct clk_init_data clk_vip_init = { .name = "vip", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -839,7 +839,7 @@ static struct clk_std clk_vip = { }, }; -static struct clk_init_data clk_spi0_init = { +static const struct clk_init_data clk_spi0_init = { .name = "spi0", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -853,7 +853,7 @@ static struct clk_std clk_spi0 = { }, }; -static struct clk_init_data clk_spi1_init = { +static const struct clk_init_data clk_spi1_init = { .name = "spi1", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -867,7 +867,7 @@ static struct clk_std clk_spi1 = { }, }; -static struct clk_init_data clk_tsc_init = { +static const struct clk_init_data clk_tsc_init = { .name = "tsc", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -881,7 +881,7 @@ static struct clk_std clk_tsc = { }, }; -static struct clk_init_data clk_i2c0_init = { +static const struct clk_init_data clk_i2c0_init = { .name = "i2c0", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -895,7 +895,7 @@ static struct clk_std clk_i2c0 = { }, }; -static struct clk_init_data clk_i2c1_init = { +static const struct clk_init_data clk_i2c1_init = { .name = "i2c1", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -909,7 +909,7 @@ static struct clk_std clk_i2c1 = { }, }; -static struct clk_init_data clk_pwmc_init = { +static const struct clk_init_data clk_pwmc_init = { .name = "pwmc", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -923,7 +923,7 @@ static struct clk_std clk_pwmc = { }, }; -static struct clk_init_data clk_efuse_init = { +static const struct clk_init_data clk_efuse_init = { .name = "efuse", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -937,7 +937,7 @@ static struct clk_std clk_efuse = { }, }; -static struct clk_init_data clk_pulse_init = { +static const struct clk_init_data clk_pulse_init = { .name = "pulse", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -955,7 +955,7 @@ static const char * const std_clk_dsp_parents[] = { "dsp", }; -static struct clk_init_data clk_gps_init = { +static const struct clk_init_data clk_gps_init = { .name = "gps", .ops = &ios_ops, .parent_names = std_clk_dsp_parents, @@ -969,7 +969,7 @@ static struct clk_std clk_gps = { }, }; -static struct clk_init_data clk_mf_init = { +static const struct clk_init_data clk_mf_init = { .name = "mf", .ops = &ios_ops, .parent_names = std_clk_io_parents, @@ -987,7 +987,7 @@ static const char * const std_clk_sys_parents[] = { "sys", }; -static struct clk_init_data clk_security_init = { +static const struct clk_init_data clk_security_init = { .name = "security", .ops = &ios_ops, .parent_names = std_clk_sys_parents, @@ -1005,7 +1005,7 @@ static const char * const std_clk_usb_parents[] = { "usb_pll", }; -static struct clk_init_data clk_usb0_init = { +static const struct clk_init_data clk_usb0_init = { .name = "usb0", .ops = &ios_ops, .parent_names = std_clk_usb_parents, @@ -1019,7 +1019,7 @@ static struct clk_std clk_usb0 = { }, }; -static struct clk_init_data clk_usb1_init = { +static const struct clk_init_data clk_usb1_init = { .name = "usb1", .ops = &ios_ops, .parent_names = std_clk_usb_parents, diff --git a/drivers/clk/sirf/clk-prima2.c b/drivers/clk/sirf/clk-prima2.c index aac1c8ec151a..2f824320c318 100644 --- a/drivers/clk/sirf/clk-prima2.c +++ b/drivers/clk/sirf/clk-prima2.c @@ -42,7 +42,7 @@ static struct clk_dmn clk_mmc45 = { }, }; -static struct clk_init_data clk_nand_init = { +static const struct clk_init_data clk_nand_init = { .name = "nand", .ops = &ios_ops, .parent_names = std_clk_io_parents, diff --git a/drivers/clk/spear/clk-aux-synth.c b/drivers/clk/spear/clk-aux-synth.c index f271c350ef94..906410413bc1 100644 --- a/drivers/clk/spear/clk-aux-synth.c +++ b/drivers/clk/spear/clk-aux-synth.c @@ -29,7 +29,7 @@ #define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw) -static struct aux_clk_masks default_aux_masks = { +static const struct aux_clk_masks default_aux_masks = { .eq_sel_mask = AUX_EQ_SEL_MASK, .eq_sel_shift = AUX_EQ_SEL_SHIFT, .eq1_mask = AUX_EQ1_SEL, @@ -128,7 +128,7 @@ static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate, return 0; } -static struct clk_ops clk_aux_ops = { +static const struct clk_ops clk_aux_ops = { .recalc_rate = clk_aux_recalc_rate, .round_rate = clk_aux_round_rate, .set_rate = clk_aux_set_rate, @@ -136,7 +136,7 @@ static struct clk_ops clk_aux_ops = { struct clk *clk_register_aux(const char *aux_name, const char *gate_name, const char *parent_name, unsigned long flags, void __iomem *reg, - struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + const struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk) { struct clk_aux *aux; @@ -149,10 +149,8 @@ struct clk *clk_register_aux(const char *aux_name, const char *gate_name, } aux = kzalloc(sizeof(*aux), GFP_KERNEL); - if (!aux) { - pr_err("could not allocate aux clk\n"); + if (!aux) return ERR_PTR(-ENOMEM); - } /* struct clk_aux assignments */ if (!masks) diff --git a/drivers/clk/spear/clk-frac-synth.c b/drivers/clk/spear/clk-frac-synth.c index 58d678b5b40a..229c96daece6 100644 --- a/drivers/clk/spear/clk-frac-synth.c +++ b/drivers/clk/spear/clk-frac-synth.c @@ -116,7 +116,7 @@ static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate, return 0; } -static struct clk_ops clk_frac_ops = { +static const struct clk_ops clk_frac_ops = { .recalc_rate = clk_frac_recalc_rate, .round_rate = clk_frac_round_rate, .set_rate = clk_frac_set_rate, @@ -136,10 +136,8 @@ struct clk *clk_register_frac(const char *name, const char *parent_name, } frac = kzalloc(sizeof(*frac), GFP_KERNEL); - if (!frac) { - pr_err("could not allocate frac clk\n"); + if (!frac) return ERR_PTR(-ENOMEM); - } /* struct clk_frac assignments */ frac->reg = reg; diff --git a/drivers/clk/spear/clk-gpt-synth.c b/drivers/clk/spear/clk-gpt-synth.c index 1a722e99e76e..28262f422562 100644 --- a/drivers/clk/spear/clk-gpt-synth.c +++ b/drivers/clk/spear/clk-gpt-synth.c @@ -105,7 +105,7 @@ static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, return 0; } -static struct clk_ops clk_gpt_ops = { +static const struct clk_ops clk_gpt_ops = { .recalc_rate = clk_gpt_recalc_rate, .round_rate = clk_gpt_round_rate, .set_rate = clk_gpt_set_rate, @@ -125,10 +125,8 @@ struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned } gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); - if (!gpt) { - pr_err("could not allocate gpt clk\n"); + if (!gpt) return ERR_PTR(-ENOMEM); - } /* struct clk_gpt assignments */ gpt->reg = reg; diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c index dc21ca4601aa..c08dec30bfa6 100644 --- a/drivers/clk/spear/clk-vco-pll.c +++ b/drivers/clk/spear/clk-vco-pll.c @@ -165,7 +165,7 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate, return 0; } -static struct clk_ops clk_pll_ops = { +static const struct clk_ops clk_pll_ops = { .recalc_rate = clk_pll_recalc_rate, .round_rate = clk_pll_round_rate, .set_rate = clk_pll_set_rate, @@ -266,7 +266,7 @@ static int clk_vco_set_rate(struct clk_hw *hw, unsigned long drate, return 0; } -static struct clk_ops clk_vco_ops = { +static const struct clk_ops clk_vco_ops = { .recalc_rate = clk_vco_recalc_rate, .round_rate = clk_vco_round_rate, .set_rate = clk_vco_set_rate, @@ -292,16 +292,12 @@ struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, } vco = kzalloc(sizeof(*vco), GFP_KERNEL); - if (!vco) { - pr_err("could not allocate vco clk\n"); + if (!vco) return ERR_PTR(-ENOMEM); - } pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("could not allocate pll clk\n"); + if (!pll) goto free_vco; - } /* struct clk_vco assignments */ vco->mode_reg = mode_reg; diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h index 9834944f08b1..af0e25f496c1 100644 --- a/drivers/clk/spear/clk.h +++ b/drivers/clk/spear/clk.h @@ -49,7 +49,7 @@ struct aux_rate_tbl { struct clk_aux { struct clk_hw hw; void __iomem *reg; - struct aux_clk_masks *masks; + const struct aux_clk_masks *masks; struct aux_rate_tbl *rtbl; u8 rtbl_cnt; spinlock_t *lock; @@ -112,7 +112,7 @@ typedef unsigned long (*clk_calc_rate)(struct clk_hw *hw, unsigned long prate, /* clk register routines */ struct clk *clk_register_aux(const char *aux_name, const char *gate_name, const char *parent_name, unsigned long flags, void __iomem *reg, - struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + const struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk); struct clk *clk_register_frac(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c index 2f86e3f94efa..591248c9a88e 100644 --- a/drivers/clk/spear/spear1310_clock.c +++ b/drivers/clk/spear/spear1310_clock.c @@ -284,7 +284,7 @@ static struct frac_rate_tbl clcd_rtbl[] = { }; /* i2s prescaler1 masks */ -static struct aux_clk_masks i2s_prs1_masks = { +static const struct aux_clk_masks i2s_prs1_masks = { .eq_sel_mask = AUX_EQ_SEL_MASK, .eq_sel_shift = SPEAR1310_I2S_PRS1_EQ_SEL_SHIFT, .eq1_mask = AUX_EQ1_SEL, diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index cbb19a90f2d6..e5bc8c828cf0 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -323,7 +323,7 @@ static struct frac_rate_tbl clcd_rtbl[] = { }; /* i2s prescaler1 masks */ -static struct aux_clk_masks i2s_prs1_masks = { +static const struct aux_clk_masks i2s_prs1_masks = { .eq_sel_mask = AUX_EQ_SEL_MASK, .eq_sel_shift = SPEAR1340_I2S_PRS1_EQ_SEL_SHIFT, .eq1_mask = AUX_EQ1_SEL, diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index d1c2fa93ddd9..4141c3fe08ae 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -11,6 +11,7 @@ lib-$(CONFIG_SUNXI_CCU) += ccu_gate.o lib-$(CONFIG_SUNXI_CCU) += ccu_mux.o lib-$(CONFIG_SUNXI_CCU) += ccu_mult.o lib-$(CONFIG_SUNXI_CCU) += ccu_phase.o +lib-$(CONFIG_SUNXI_CCU) += ccu_sdm.o # Multi-factor clocks lib-$(CONFIG_SUNXI_CCU) += ccu_nk.o diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c index 286b0049b7b6..ffa5dac221e4 100644 --- a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c +++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c @@ -28,6 +28,7 @@ #include "ccu_nkmp.h" #include "ccu_nm.h" #include "ccu_phase.h" +#include "ccu_sdm.h" #include "ccu-sun4i-a10.h" @@ -51,16 +52,29 @@ static struct ccu_nkmp pll_core_clk = { * the base (2x, 4x and 8x), and one variable divider (the one true * pll audio). * - * We don't have any need for the variable divider for now, so we just - * hardcode it to match with the clock names. + * With sigma-delta modulation for fractional-N on the audio PLL, + * we have to use specific dividers. This means the variable divider + * can no longer be used, as the audio codec requests the exact clock + * rates we support through this mechanism. So we now hard code the + * variable divider to 1. This means the clock rates will no longer + * match the clock names. */ #define SUN4I_PLL_AUDIO_REG 0x008 + +static struct ccu_sdm_setting pll_audio_sdm_table[] = { + { .rate = 22579200, .pattern = 0xc0010d84, .m = 8, .n = 7 }, + { .rate = 24576000, .pattern = 0xc000ac02, .m = 14, .n = 14 }, +}; + static struct ccu_nm pll_audio_base_clk = { .enable = BIT(31), .n = _SUNXI_CCU_MULT_OFFSET(8, 7, 0), .m = _SUNXI_CCU_DIV_OFFSET(0, 5, 0), + .sdm = _SUNXI_CCU_SDM(pll_audio_sdm_table, 0, + 0x00c, BIT(31)), .common = { .reg = 0x008, + .features = CCU_FEATURE_SIGMA_DELTA_MOD, .hw.init = CLK_HW_INIT("pll-audio-base", "hosc", &ccu_nm_ops, @@ -223,7 +237,7 @@ static struct ccu_mux cpu_clk = { .hw.init = CLK_HW_INIT_PARENTS("cpu", cpu_parents, &ccu_mux_ops, - CLK_IS_CRITICAL), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL), } }; @@ -1021,9 +1035,9 @@ static struct ccu_common *sun4i_sun7i_ccu_clks[] = { &out_b_clk.common }; -/* Post-divider for pll-audio is hardcoded to 4 */ +/* Post-divider for pll-audio is hardcoded to 1 */ static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", - "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", @@ -1420,10 +1434,10 @@ static void __init sun4i_ccu_init(struct device_node *node, return; } - /* Force the PLL-Audio-1x divider to 4 */ + /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN4I_PLL_AUDIO_REG); val &= ~GENMASK(29, 26); - writel(val | (4 << 26), reg + SUN4I_PLL_AUDIO_REG); + writel(val | (1 << 26), reg + SUN4I_PLL_AUDIO_REG); /* * Use the peripheral PLL6 as the AHB parent, instead of CPU / diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.h b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h index c5947c7c050e..23c908ad509f 100644 --- a/drivers/clk/sunxi-ng/ccu-sun4i-a10.h +++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h @@ -29,7 +29,7 @@ #define CLK_PLL_AUDIO_4X 6 #define CLK_PLL_AUDIO_8X 7 #define CLK_PLL_VIDEO0 8 -#define CLK_PLL_VIDEO0_2X 9 +/* The PLL_VIDEO0_2X clock is exported */ #define CLK_PLL_VE 10 #define CLK_PLL_DDR_BASE 11 #define CLK_PLL_DDR 12 @@ -38,7 +38,7 @@ #define CLK_PLL_PERIPH 15 #define CLK_PLL_PERIPH_SATA 16 #define CLK_PLL_VIDEO1 17 -#define CLK_PLL_VIDEO1_2X 18 +/* The PLL_VIDEO1_2X clock is exported */ #define CLK_PLL_GPU 19 /* The CPU clock is exported */ diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.c b/drivers/clk/sunxi-ng/ccu-sun5i.c index ab9e850b3707..fa2c2dd77102 100644 --- a/drivers/clk/sunxi-ng/ccu-sun5i.c +++ b/drivers/clk/sunxi-ng/ccu-sun5i.c @@ -26,6 +26,7 @@ #include "ccu_nkmp.h" #include "ccu_nm.h" #include "ccu_phase.h" +#include "ccu_sdm.h" #include "ccu-sun5i.h" @@ -49,11 +50,20 @@ static struct ccu_nkmp pll_core_clk = { * the base (2x, 4x and 8x), and one variable divider (the one true * pll audio). * - * We don't have any need for the variable divider for now, so we just - * hardcode it to match with the clock names + * With sigma-delta modulation for fractional-N on the audio PLL, + * we have to use specific dividers. This means the variable divider + * can no longer be used, as the audio codec requests the exact clock + * rates we support through this mechanism. So we now hard code the + * variable divider to 1. This means the clock rates will no longer + * match the clock names. */ #define SUN5I_PLL_AUDIO_REG 0x008 +static struct ccu_sdm_setting pll_audio_sdm_table[] = { + { .rate = 22579200, .pattern = 0xc0010d84, .m = 8, .n = 7 }, + { .rate = 24576000, .pattern = 0xc000ac02, .m = 14, .n = 14 }, +}; + static struct ccu_nm pll_audio_base_clk = { .enable = BIT(31), .n = _SUNXI_CCU_MULT_OFFSET(8, 7, 0), @@ -63,8 +73,11 @@ static struct ccu_nm pll_audio_base_clk = { * offset */ .m = _SUNXI_CCU_DIV_OFFSET(0, 5, 0), + .sdm = _SUNXI_CCU_SDM(pll_audio_sdm_table, 0, + 0x00c, BIT(31)), .common = { .reg = 0x008, + .features = CCU_FEATURE_SIGMA_DELTA_MOD, .hw.init = CLK_HW_INIT("pll-audio-base", "hosc", &ccu_nm_ops, @@ -597,9 +610,9 @@ static struct ccu_common *sun5i_a10s_ccu_clks[] = { &iep_clk.common, }; -/* We hardcode the divider to 4 for now */ +/* We hardcode the divider to 1 for now */ static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", - "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", @@ -980,10 +993,10 @@ static void __init sun5i_ccu_init(struct device_node *node, return; } - /* Force the PLL-Audio-1x divider to 4 */ + /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN5I_PLL_AUDIO_REG); - val &= ~GENMASK(19, 16); - writel(val | (3 << 16), reg + SUN5I_PLL_AUDIO_REG); + val &= ~GENMASK(29, 26); + writel(val | (0 << 26), reg + SUN5I_PLL_AUDIO_REG); /* * Use the peripheral PLL as the AHB parent, instead of CPU / diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c index 8af434815fba..72b16ed1012b 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c @@ -31,6 +31,7 @@ #include "ccu_nkmp.h" #include "ccu_nm.h" #include "ccu_phase.h" +#include "ccu_sdm.h" #include "ccu-sun6i-a31.h" @@ -48,18 +49,29 @@ static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_cpu_clk, "pll-cpu", * the base (2x, 4x and 8x), and one variable divider (the one true * pll audio). * - * We don't have any need for the variable divider for now, so we just - * hardcode it to match with the clock names + * With sigma-delta modulation for fractional-N on the audio PLL, + * we have to use specific dividers. This means the variable divider + * can no longer be used, as the audio codec requests the exact clock + * rates we support through this mechanism. So we now hard code the + * variable divider to 1. This means the clock rates will no longer + * match the clock names. */ #define SUN6I_A31_PLL_AUDIO_REG 0x008 -static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", - "osc24M", 0x008, - 8, 7, /* N */ - 0, 5, /* M */ - BIT(31), /* gate */ - BIT(28), /* lock */ - CLK_SET_RATE_UNGATE); +static struct ccu_sdm_setting pll_audio_sdm_table[] = { + { .rate = 22579200, .pattern = 0xc0010d84, .m = 8, .n = 7 }, + { .rate = 24576000, .pattern = 0xc000ac02, .m = 14, .n = 14 }, +}; + +static SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + pll_audio_sdm_table, BIT(24), + 0x284, BIT(31), + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0", "osc24M", 0x010, @@ -608,7 +620,7 @@ static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", lcd_ch1_parents, 0x150, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT); -static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M", 0x150, BIT(30), 0); +static SUNXI_CCU_GATE(hdmi_ddc_clk, "ddc", "osc24M", 0x150, BIT(30), 0); static SUNXI_CCU_GATE(ps_clk, "ps", "lcd1-ch1", 0x140, BIT(31), 0); @@ -950,9 +962,9 @@ static struct ccu_common *sun6i_a31_ccu_clks[] = { &out_c_clk.common, }; -/* We hardcode the divider to 4 for now */ +/* We hardcode the divider to 1 for now */ static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", - "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", @@ -1221,10 +1233,10 @@ static void __init sun6i_a31_ccu_setup(struct device_node *node) return; } - /* Force the PLL-Audio-1x divider to 4 */ + /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN6I_A31_PLL_AUDIO_REG); val &= ~GENMASK(19, 16); - writel(val | (3 << 16), reg + SUN6I_A31_PLL_AUDIO_REG); + writel(val | (0 << 16), reg + SUN6I_A31_PLL_AUDIO_REG); /* Force PLL-MIPI to MIPI mode */ val = readl(reg + SUN6I_A31_PLL_MIPI_REG); diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.h b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h index 4e434011e9e7..27e6ad4133ab 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.h +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h @@ -27,7 +27,9 @@ #define CLK_PLL_AUDIO_4X 4 #define CLK_PLL_AUDIO_8X 5 #define CLK_PLL_VIDEO0 6 -#define CLK_PLL_VIDEO0_2X 7 + +/* The PLL_VIDEO0_2X clock is exported */ + #define CLK_PLL_VE 8 #define CLK_PLL_DDR 9 @@ -35,7 +37,9 @@ #define CLK_PLL_PERIPH_2X 11 #define CLK_PLL_VIDEO1 12 -#define CLK_PLL_VIDEO1_2X 13 + +/* The PLL_VIDEO1_2X clock is exported */ + #define CLK_PLL_GPU 14 #define CLK_PLL_MIPI 15 #define CLK_PLL9 16 diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index d93b452f0df9..a4fa2945f230 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -26,6 +26,7 @@ #include "ccu_nkmp.h" #include "ccu_nm.h" #include "ccu_phase.h" +#include "ccu_sdm.h" #include "ccu-sun8i-a23-a33.h" @@ -52,18 +53,29 @@ static struct ccu_nkmp pll_cpux_clk = { * the base (2x, 4x and 8x), and one variable divider (the one true * pll audio). * - * We don't have any need for the variable divider for now, so we just - * hardcode it to match with the clock names + * With sigma-delta modulation for fractional-N on the audio PLL, + * we have to use specific dividers. This means the variable divider + * can no longer be used, as the audio codec requests the exact clock + * rates we support through this mechanism. So we now hard code the + * variable divider to 1. This means the clock rates will no longer + * match the clock names. */ #define SUN8I_A23_PLL_AUDIO_REG 0x008 -static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", - "osc24M", 0x008, - 8, 7, /* N */ - 0, 5, /* M */ - BIT(31), /* gate */ - BIT(28), /* lock */ - CLK_SET_RATE_UNGATE); +static struct ccu_sdm_setting pll_audio_sdm_table[] = { + { .rate = 22579200, .pattern = 0xc0010d84, .m = 8, .n = 7 }, + { .rate = 24576000, .pattern = 0xc000ac02, .m = 14, .n = 14 }, +}; + +static SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + pll_audio_sdm_table, BIT(24), + 0x284, BIT(31), + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video", "osc24M", 0x010, @@ -538,9 +550,9 @@ static struct ccu_common *sun8i_a23_ccu_clks[] = { &ats_clk.common, }; -/* We hardcode the divider to 4 for now */ +/* We hardcode the divider to 1 for now */ static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", - "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", @@ -720,10 +732,10 @@ static void __init sun8i_a23_ccu_setup(struct device_node *node) return; } - /* Force the PLL-Audio-1x divider to 4 */ + /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN8I_A23_PLL_AUDIO_REG); val &= ~GENMASK(19, 16); - writel(val | (3 << 16), reg + SUN8I_A23_PLL_AUDIO_REG); + writel(val | (0 << 16), reg + SUN8I_A23_PLL_AUDIO_REG); /* Force PLL-MIPI to MIPI mode */ val = readl(reg + SUN8I_A23_PLL_MIPI_REG); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c index e43acebdfbcd..5cedcd0d8be8 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c @@ -354,9 +354,9 @@ static SUNXI_CCU_GATE(bus_tdm_clk, "bus-tdm", "apb1", static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", 0x06c, BIT(0), 0); static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", - 0x06c, BIT(0), 0); + 0x06c, BIT(1), 0); static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", - 0x06c, BIT(0), 0); + 0x06c, BIT(2), 0); static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", 0x06c, BIT(16), 0); static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", @@ -506,7 +506,7 @@ static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents, csi_mclk_table, 0x134, 0, 5, /* M */ - 10, 3, /* mux */ + 8, 3, /* mux */ BIT(15), /* gate */ 0); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c index 5cdaf52669e4..5cc9d9952121 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c @@ -41,11 +41,16 @@ static SUNXI_CCU_GATE(wb_clk, "wb", "wb-div", static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4, CLK_SET_RATE_PARENT); -static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4, - CLK_SET_RATE_PARENT); static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4, CLK_SET_RATE_PARENT); +static SUNXI_CCU_M(mixer0_div_a83_clk, "mixer0-div", "pll-de", 0x0c, 0, 4, + CLK_SET_RATE_PARENT); +static SUNXI_CCU_M(mixer1_div_a83_clk, "mixer1-div", "pll-de", 0x0c, 4, 4, + CLK_SET_RATE_PARENT); +static SUNXI_CCU_M(wb_div_a83_clk, "wb-div", "pll-de", 0x0c, 8, 4, + CLK_SET_RATE_PARENT); + static struct ccu_common *sun8i_a83t_de2_clks[] = { &mixer0_clk.common, &mixer1_clk.common, @@ -55,9 +60,9 @@ static struct ccu_common *sun8i_a83t_de2_clks[] = { &bus_mixer1_clk.common, &bus_wb_clk.common, - &mixer0_div_clk.common, - &mixer1_div_clk.common, - &wb_div_clk.common, + &mixer0_div_a83_clk.common, + &mixer1_div_a83_clk.common, + &wb_div_a83_clk.common, }; static struct ccu_common *sun8i_v3s_de2_clks[] = { @@ -81,9 +86,9 @@ static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, [CLK_BUS_WB] = &bus_wb_clk.common.hw, - [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, - [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw, - [CLK_WB_DIV] = &wb_div_clk.common.hw, + [CLK_MIXER0_DIV] = &mixer0_div_a83_clk.common.hw, + [CLK_MIXER1_DIV] = &mixer1_div_a83_clk.common.hw, + [CLK_WB_DIV] = &wb_div_a83_clk.common.hw, }, .num = CLK_NUMBER, }; diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index 1729ff6a5aae..29bc0566b776 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -26,6 +26,7 @@ #include "ccu_nkmp.h" #include "ccu_nm.h" #include "ccu_phase.h" +#include "ccu_sdm.h" #include "ccu-sun8i-h3.h" @@ -37,25 +38,36 @@ static SUNXI_CCU_NKMP_WITH_GATE_LOCK(pll_cpux_clk, "pll-cpux", 16, 2, /* P */ BIT(31), /* gate */ BIT(28), /* lock */ - 0); + CLK_SET_RATE_UNGATE); /* * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from * the base (2x, 4x and 8x), and one variable divider (the one true * pll audio). * - * We don't have any need for the variable divider for now, so we just - * hardcode it to match with the clock names + * With sigma-delta modulation for fractional-N on the audio PLL, + * we have to use specific dividers. This means the variable divider + * can no longer be used, as the audio codec requests the exact clock + * rates we support through this mechanism. So we now hard code the + * variable divider to 1. This means the clock rates will no longer + * match the clock names. */ #define SUN8I_H3_PLL_AUDIO_REG 0x008 -static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", - "osc24M", 0x008, - 8, 7, /* N */ - 0, 5, /* M */ - BIT(31), /* gate */ - BIT(28), /* lock */ - 0); +static struct ccu_sdm_setting pll_audio_sdm_table[] = { + { .rate = 22579200, .pattern = 0xc0010d84, .m = 8, .n = 7 }, + { .rate = 24576000, .pattern = 0xc000ac02, .m = 14, .n = 14 }, +}; + +static SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + pll_audio_sdm_table, BIT(24), + 0x284, BIT(31), + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video", "osc24M", 0x0010, @@ -67,7 +79,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video", 297000000, /* frac rate 1 */ BIT(31), /* gate */ BIT(28), /* lock */ - 0); + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", "osc24M", 0x0018, @@ -79,7 +91,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", 297000000, /* frac rate 1 */ BIT(31), /* gate */ BIT(28), /* lock */ - 0); + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr", "osc24M", 0x020, @@ -88,7 +100,7 @@ static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr", 0, 2, /* M */ BIT(31), /* gate */ BIT(28), /* lock */ - 0); + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph0_clk, "pll-periph0", "osc24M", 0x028, @@ -97,7 +109,7 @@ static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph0_clk, "pll-periph0", BIT(31), /* gate */ BIT(28), /* lock */ 2, /* post-div */ - 0); + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", "osc24M", 0x0038, @@ -109,7 +121,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", 297000000, /* frac rate 1 */ BIT(31), /* gate */ BIT(28), /* lock */ - 0); + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph1_clk, "pll-periph1", "osc24M", 0x044, @@ -118,7 +130,7 @@ static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph1_clk, "pll-periph1", BIT(31), /* gate */ BIT(28), /* lock */ 2, /* post-div */ - 0); + CLK_SET_RATE_UNGATE); static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", "osc24M", 0x0048, @@ -130,7 +142,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", 297000000, /* frac rate 1 */ BIT(31), /* gate */ BIT(28), /* lock */ - 0); + CLK_SET_RATE_UNGATE); static const char * const cpux_parents[] = { "osc32k", "osc24M", "pll-cpux" , "pll-cpux" }; @@ -484,7 +496,7 @@ static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL); static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", - 0x1a0, 0, 3, BIT(31), 0); + 0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT); static struct ccu_common *sun8i_h3_ccu_clks[] = { &pll_cpux_clk.common, @@ -707,9 +719,9 @@ static struct ccu_common *sun50i_h5_ccu_clks[] = { &gpu_clk.common, }; -/* We hardcode the divider to 4 for now */ +/* We hardcode the divider to 1 for now */ static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", - "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", @@ -1129,10 +1141,10 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node, return; } - /* Force the PLL-Audio-1x divider to 4 */ + /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN8I_H3_PLL_AUDIO_REG); val &= ~GENMASK(19, 16); - writel(val | (3 << 16), reg + SUN8I_H3_PLL_AUDIO_REG); + writel(val | (0 << 16), reg + SUN8I_H3_PLL_AUDIO_REG); sunxi_ccu_probe(node, reg, desc); diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h index cadd1a9f93b6..5d684ce77c54 100644 --- a/drivers/clk/sunxi-ng/ccu_common.h +++ b/drivers/clk/sunxi-ng/ccu_common.h @@ -24,6 +24,7 @@ #define CCU_FEATURE_ALL_PREDIV BIT(4) #define CCU_FEATURE_LOCK_REG BIT(5) #define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6) +#define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7) /* MMC timing mode switch bit */ #define CCU_MMC_NEW_TIMING_MODE BIT(30) diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index a32158e8f2e3..7620aa973a6e 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -90,6 +90,14 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw, if (!m) m++; + if (ccu_sdm_helper_is_enabled(&nm->common, &nm->sdm)) { + unsigned long rate = + ccu_sdm_helper_read_rate(&nm->common, &nm->sdm, + m, n); + if (rate) + return rate; + } + return parent_rate * n / m; } @@ -99,6 +107,12 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, struct ccu_nm *nm = hw_to_ccu_nm(hw); struct _ccu_nm _nm; + if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) + return rate; + + if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) + return rate; + _nm.min_n = nm->n.min ?: 1; _nm.max_n = nm->n.max ?: 1 << nm->n.width; _nm.min_m = 1; @@ -140,7 +154,16 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, _nm.min_m = 1; _nm.max_m = nm->m.max ?: 1 << nm->m.width; - ccu_nm_find_best(parent_rate, rate, &_nm); + if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) { + ccu_sdm_helper_enable(&nm->common, &nm->sdm, rate); + + /* Sigma delta modulation requires specific N and M factors */ + ccu_sdm_helper_get_factors(&nm->common, &nm->sdm, rate, + &_nm.m, &_nm.n); + } else { + ccu_sdm_helper_disable(&nm->common, &nm->sdm); + ccu_nm_find_best(parent_rate, rate, &_nm); + } spin_lock_irqsave(nm->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_nm.h b/drivers/clk/sunxi-ng/ccu_nm.h index e87fd186da78..c623b0c7a23c 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.h +++ b/drivers/clk/sunxi-ng/ccu_nm.h @@ -20,6 +20,7 @@ #include "ccu_div.h" #include "ccu_frac.h" #include "ccu_mult.h" +#include "ccu_sdm.h" /* * struct ccu_nm - Definition of an N-M clock @@ -33,10 +34,34 @@ struct ccu_nm { struct ccu_mult_internal n; struct ccu_div_internal m; struct ccu_frac_internal frac; + struct ccu_sdm_internal sdm; struct ccu_common common; }; +#define SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(_struct, _name, _parent, _reg, \ + _nshift, _nwidth, \ + _mshift, _mwidth, \ + _sdm_table, _sdm_en, \ + _sdm_reg, _sdm_reg_en, \ + _gate, _lock, _flags) \ + struct ccu_nm _struct = { \ + .enable = _gate, \ + .lock = _lock, \ + .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \ + .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .sdm = _SUNXI_CCU_SDM(_sdm_table, _sdm_en, \ + _sdm_reg, _sdm_reg_en),\ + .common = { \ + .reg = _reg, \ + .features = CCU_FEATURE_SIGMA_DELTA_MOD, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &ccu_nm_ops, \ + _flags), \ + }, \ + } + #define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(_struct, _name, _parent, _reg, \ _nshift, _nwidth, \ _mshift, _mwidth, \ diff --git a/drivers/clk/sunxi-ng/ccu_reset.c b/drivers/clk/sunxi-ng/ccu_reset.c index 1dc4e98ea802..b67149143554 100644 --- a/drivers/clk/sunxi-ng/ccu_reset.c +++ b/drivers/clk/sunxi-ng/ccu_reset.c @@ -60,8 +60,22 @@ static int ccu_reset_reset(struct reset_controller_dev *rcdev, return 0; } +static int ccu_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev); + const struct ccu_reset_map *map = &ccu->reset_map[id]; + + /* + * The reset control API expects 0 if reset is not asserted, + * which is the opposite of what our hardware uses. + */ + return !(map->bit & readl(ccu->base + map->reg)); +} + const struct reset_control_ops ccu_reset_ops = { .assert = ccu_reset_assert, .deassert = ccu_reset_deassert, .reset = ccu_reset_reset, + .status = ccu_reset_status, }; diff --git a/drivers/clk/sunxi-ng/ccu_sdm.c b/drivers/clk/sunxi-ng/ccu_sdm.c new file mode 100644 index 000000000000..3b3dc9bdf2b0 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_sdm.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2017 Chen-Yu Tsai + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include +#include + +#include "ccu_sdm.h" + +bool ccu_sdm_helper_is_enabled(struct ccu_common *common, + struct ccu_sdm_internal *sdm) +{ + if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) + return false; + + if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable)) + return false; + + return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable); +} + +void ccu_sdm_helper_enable(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + unsigned long rate) +{ + unsigned long flags; + unsigned int i; + u32 reg; + + if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) + return; + + /* Set the pattern */ + for (i = 0; i < sdm->table_size; i++) + if (sdm->table[i].rate == rate) + writel(sdm->table[i].pattern, + common->base + sdm->tuning_reg); + + /* Make sure SDM is enabled */ + spin_lock_irqsave(common->lock, flags); + reg = readl(common->base + sdm->tuning_reg); + writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg); + spin_unlock_irqrestore(common->lock, flags); + + spin_lock_irqsave(common->lock, flags); + reg = readl(common->base + common->reg); + writel(reg | sdm->enable, common->base + common->reg); + spin_unlock_irqrestore(common->lock, flags); +} + +void ccu_sdm_helper_disable(struct ccu_common *common, + struct ccu_sdm_internal *sdm) +{ + unsigned long flags; + u32 reg; + + if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) + return; + + spin_lock_irqsave(common->lock, flags); + reg = readl(common->base + common->reg); + writel(reg & ~sdm->enable, common->base + common->reg); + spin_unlock_irqrestore(common->lock, flags); + + spin_lock_irqsave(common->lock, flags); + reg = readl(common->base + sdm->tuning_reg); + writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg); + spin_unlock_irqrestore(common->lock, flags); +} + +/* + * Sigma delta modulation provides a way to do fractional-N frequency + * synthesis, in essence allowing the PLL to output any frequency + * within its operational range. On earlier SoCs such as the A10/A20, + * some PLLs support this. On later SoCs, all PLLs support this. + * + * The datasheets do not explain what the "wave top" and "wave bottom" + * parameters mean or do, nor how to calculate the effective output + * frequency. The only examples (and real world usage) are for the audio + * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio + * peripherals. The author lacks the underlying domain knowledge to + * pursue this. + * + * The goal and function of the following code is to support the two + * clock rates used by the audio subsystem, allowing for proper audio + * playback and capture without any pitch or speed changes. + */ +bool ccu_sdm_helper_has_rate(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + unsigned long rate) +{ + unsigned int i; + + if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) + return false; + + for (i = 0; i < sdm->table_size; i++) + if (sdm->table[i].rate == rate) + return true; + + return false; +} + +unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + u32 m, u32 n) +{ + unsigned int i; + u32 reg; + + pr_debug("%s: Read sigma-delta modulation setting\n", + clk_hw_get_name(&common->hw)); + + if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) + return 0; + + pr_debug("%s: clock is sigma-delta modulated\n", + clk_hw_get_name(&common->hw)); + + reg = readl(common->base + sdm->tuning_reg); + + pr_debug("%s: pattern reg is 0x%x", + clk_hw_get_name(&common->hw), reg); + + for (i = 0; i < sdm->table_size; i++) + if (sdm->table[i].pattern == reg && + sdm->table[i].m == m && sdm->table[i].n == n) + return sdm->table[i].rate; + + /* We can't calculate the effective clock rate, so just fail. */ + return 0; +} + +int ccu_sdm_helper_get_factors(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + unsigned long rate, + unsigned long *m, unsigned long *n) +{ + unsigned int i; + + if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) + return -EINVAL; + + for (i = 0; i < sdm->table_size; i++) + if (sdm->table[i].rate == rate) { + *m = sdm->table[i].m; + *n = sdm->table[i].n; + return 0; + } + + /* nothing found */ + return -EINVAL; +} diff --git a/drivers/clk/sunxi-ng/ccu_sdm.h b/drivers/clk/sunxi-ng/ccu_sdm.h new file mode 100644 index 000000000000..2a9b4a2584d6 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_sdm.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017 Chen-Yu Tsai. All rights reserved. + * + * 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. + */ + +#ifndef _CCU_SDM_H +#define _CCU_SDM_H + +#include + +#include "ccu_common.h" + +struct ccu_sdm_setting { + unsigned long rate; + + /* + * XXX We don't know what the step and bottom register fields + * mean. Just copy the whole register value from the vendor + * kernel for now. + */ + u32 pattern; + + /* + * M and N factors here should be the values used in + * calculation, not the raw values written to registers + */ + u32 m; + u32 n; +}; + +struct ccu_sdm_internal { + struct ccu_sdm_setting *table; + u32 table_size; + /* early SoCs don't have the SDM enable bit in the PLL register */ + u32 enable; + /* second enable bit in tuning register */ + u32 tuning_enable; + u16 tuning_reg; +}; + +#define _SUNXI_CCU_SDM(_table, _enable, \ + _reg, _reg_enable) \ + { \ + .table = _table, \ + .table_size = ARRAY_SIZE(_table), \ + .enable = _enable, \ + .tuning_enable = _reg_enable, \ + .tuning_reg = _reg, \ + } + +bool ccu_sdm_helper_is_enabled(struct ccu_common *common, + struct ccu_sdm_internal *sdm); +void ccu_sdm_helper_enable(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + unsigned long rate); +void ccu_sdm_helper_disable(struct ccu_common *common, + struct ccu_sdm_internal *sdm); + +bool ccu_sdm_helper_has_rate(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + unsigned long rate); + +unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + u32 m, u32 n); + +int ccu_sdm_helper_get_factors(struct ccu_common *common, + struct ccu_sdm_internal *sdm, + unsigned long rate, + unsigned long *m, unsigned long *n); + +#endif diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index dfe5e3e32d28..856fef65433b 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -276,13 +276,11 @@ void sunxi_factors_unregister(struct device_node *node, struct clk *clk) { struct clk_hw *hw = __clk_get_hw(clk); struct clk_factors *factors; - const char *name; if (!hw) return; factors = to_clk_factors(hw); - name = clk_hw_get_name(hw); of_clk_del_provider(node); /* TODO: The composite clock stuff will leak a bit here. */ diff --git a/drivers/clk/sunxi/clk-sun9i-mmc.c b/drivers/clk/sunxi/clk-sun9i-mmc.c index 6041bdba2e97..a1a634253d6f 100644 --- a/drivers/clk/sunxi/clk-sun9i-mmc.c +++ b/drivers/clk/sunxi/clk-sun9i-mmc.c @@ -124,7 +124,7 @@ static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev) return PTR_ERR(data->clk); } - data->reset = devm_reset_control_get(&pdev->dev, NULL); + data->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(data->reset)) { dev_err(&pdev->dev, "Could not get reset control\n"); return PTR_ERR(data->reset); diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c index 638ace64033b..a896692b74ec 100644 --- a/drivers/clk/tegra/clk-bpmp.c +++ b/drivers/clk/tegra/clk-bpmp.c @@ -55,6 +55,7 @@ struct tegra_bpmp_clk_message { struct { void *data; size_t size; + int ret; } rx; }; @@ -64,6 +65,7 @@ static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp, struct mrq_clk_request request; struct tegra_bpmp_message msg; void *req = &request; + int err; memset(&request, 0, sizeof(request)); request.cmd_and_id = (clk->cmd << 24) | clk->id; @@ -84,7 +86,13 @@ static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp, msg.rx.data = clk->rx.data; msg.rx.size = clk->rx.size; - return tegra_bpmp_transfer(bpmp, &msg); + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + else if (msg.rx.ret < 0) + return -EINVAL; + + return 0; } static int tegra_bpmp_clk_prepare(struct clk_hw *hw) @@ -414,11 +422,8 @@ static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp, struct tegra_bpmp_clk_info *info = &clocks[count]; err = tegra_bpmp_clk_get_info(bpmp, id, info); - if (err < 0) { - dev_err(bpmp->dev, "failed to query clock %u: %d\n", - id, err); + if (err < 0) continue; - } if (info->num_parents >= U8_MAX) { dev_err(bpmp->dev, diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index 2c44aeb0b97c..0a7deee74eea 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c @@ -1728,10 +1728,10 @@ EXPORT_SYMBOL(tegra_dfll_register); * @pdev: DFLL platform_device * * * Unbind this driver from the DFLL hardware device represented by - * @pdev. The DFLL must be disabled for this to succeed. Returns 0 - * upon success or -EBUSY if the DFLL is still active. + * @pdev. The DFLL must be disabled for this to succeed. Returns a + * soc pointer upon success or -EBUSY if the DFLL is still active. */ -int tegra_dfll_unregister(struct platform_device *pdev) +struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev) { struct tegra_dfll *td = platform_get_drvdata(pdev); @@ -1739,7 +1739,7 @@ int tegra_dfll_unregister(struct platform_device *pdev) if (td->mode != DFLL_DISABLED) { dev_err(&pdev->dev, "must disable DFLL before removing driver\n"); - return -EBUSY; + return ERR_PTR(-EBUSY); } debugfs_remove_recursive(td->debugfs_dir); @@ -1753,6 +1753,6 @@ int tegra_dfll_unregister(struct platform_device *pdev) reset_control_assert(td->dvco_rst); - return 0; + return td->soc; } EXPORT_SYMBOL(tegra_dfll_unregister); diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h index ed2ad888268f..83352c8078f2 100644 --- a/drivers/clk/tegra/clk-dfll.h +++ b/drivers/clk/tegra/clk-dfll.h @@ -43,7 +43,7 @@ struct tegra_dfll_soc_data { int tegra_dfll_register(struct platform_device *pdev, struct tegra_dfll_soc_data *soc); -int tegra_dfll_unregister(struct platform_device *pdev); +struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev); int tegra_dfll_runtime_suspend(struct device *dev); int tegra_dfll_runtime_resume(struct device *dev); diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index 11ee5f9ce99e..b616e33c5255 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -13,6 +13,7 @@ enum clk_id { tegra_clk_amx, tegra_clk_amx1, tegra_clk_apb2ape, + tegra_clk_ahbdma, tegra_clk_apbdma, tegra_clk_apbif, tegra_clk_ape, diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index cf80831de79d..9475c00b7cf9 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -203,3 +203,11 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name, return _tegra_clk_register_periph(name, parent_names, num_parents, periph, clk_base, offset, CLK_SET_RATE_PARENT); } + +struct clk *tegra_clk_register_periph_data(void __iomem *clk_base, + struct tegra_periph_init_data *init) +{ + return _tegra_clk_register_periph(init->name, init->p.parent_names, + init->num_parents, &init->periph, + clk_base, init->offset, init->flags); +} diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 848255cc0209..c02711927d79 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -129,7 +129,6 @@ #define CLK_SOURCE_NVDEC 0x698 #define CLK_SOURCE_NVJPG 0x69c #define CLK_SOURCE_APE 0x6c0 -#define CLK_SOURCE_SOR1 0x410 #define CLK_SOURCE_SDMMC_LEGACY 0x694 #define CLK_SOURCE_QSPI 0x6c4 #define CLK_SOURCE_VI_I2C 0x6c8 @@ -278,7 +277,6 @@ static DEFINE_SPINLOCK(PLLP_OUTA_lock); static DEFINE_SPINLOCK(PLLP_OUTB_lock); static DEFINE_SPINLOCK(PLLP_OUTC_lock); static DEFINE_SPINLOCK(sor0_lock); -static DEFINE_SPINLOCK(sor1_lock); #define MUX_I2S_SPDIF(_id) \ static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \ @@ -604,18 +602,6 @@ static u32 mux_pllp_plld_plld2_clkm_idx[] = { [0] = 0, [1] = 2, [2] = 5, [3] = 6 }; -static const char *mux_sor_safe_sor1_brick_sor1_src[] = { - /* - * Bit 0 of the mux selects sor1_brick, irrespective of bit 1, so the - * sor1_brick parent appears twice in the list below. This is merely - * to support clk_get_parent() if firmware happened to set these bits - * to 0b11. While not an invalid setting, code should always set the - * bits to 0b01 to select sor1_brick. - */ - "sor_safe", "sor1_brick", "sor1_src", "sor1_brick" -}; -#define mux_sor_safe_sor1_brick_sor1_src_idx NULL - static const char *mux_pllp_pllre_clkm[] = { "pll_p", "pll_re_out1", "clk_m" }; @@ -804,8 +790,6 @@ static struct tegra_periph_init_data periph_clks[] = { MUX8("nvdec", mux_pllc2_c_c3_pllp_plla1_clkm, CLK_SOURCE_NVDEC, 194, 0, tegra_clk_nvdec), MUX8("nvjpg", mux_pllc2_c_c3_pllp_plla1_clkm, CLK_SOURCE_NVJPG, 195, 0, tegra_clk_nvjpg), MUX8("ape", mux_plla_pllc4_out0_pllc_pllc4_out1_pllp_pllc4_out2_clkm, CLK_SOURCE_APE, 198, TEGRA_PERIPH_ON_APB, tegra_clk_ape), - MUX8_NOGATE_LOCK("sor1_src", mux_pllp_plld_plld2_clkm, CLK_SOURCE_SOR1, tegra_clk_sor1_src, &sor1_lock), - NODIV("sor1", mux_sor_safe_sor1_brick_sor1_src, CLK_SOURCE_SOR1, 14, MASK(2), 183, 0, tegra_clk_sor1, &sor1_lock), MUX8("sdmmc_legacy", mux_pllp_out3_clkm_pllp_pllc4, CLK_SOURCE_SDMMC_LEGACY, 193, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_sdmmc_legacy), MUX8("qspi", mux_pllp_pllc_pllc_out1_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_QSPI, 211, TEGRA_PERIPH_ON_APB, tegra_clk_qspi), I2C("vii2c", mux_pllp_pllc_clkm, CLK_SOURCE_VI_I2C, 208, tegra_clk_vi_i2c), @@ -823,7 +807,8 @@ static struct tegra_periph_init_data gate_clks[] = { GATE("timer", "clk_m", 5, 0, tegra_clk_timer, CLK_IS_CRITICAL), GATE("isp", "clk_m", 23, 0, tegra_clk_isp, 0), GATE("vcp", "clk_m", 29, 0, tegra_clk_vcp, 0), - GATE("apbdma", "clk_m", 34, 0, tegra_clk_apbdma, 0), + GATE("ahbdma", "hclk", 33, 0, tegra_clk_ahbdma, 0), + GATE("apbdma", "pclk", 34, 0, tegra_clk_apbdma, 0), GATE("kbc", "clk_32k", 36, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_kbc, 0), GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0), GATE("fuse_burn", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse_burn, 0), @@ -927,10 +912,7 @@ static void __init periph_clk_init(void __iomem *clk_base, continue; data->periph.gate.regs = bank; - clk = tegra_clk_register_periph(data->name, - data->p.parent_names, data->num_parents, - &data->periph, clk_base, data->offset, - data->flags); + clk = tegra_clk_register_periph_data(clk_base, data); *dt_clk = clk; } } diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c index 4f6fd307cb70..10047107c1dc 100644 --- a/drivers/clk/tegra/clk-tegra-super-gen4.c +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c @@ -166,7 +166,7 @@ static void __init tegra_sclk_init(void __iomem *clk_base, clk_base + SYSTEM_CLK_RATE, 0, 2, 0, &sysrate_lock); clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | - CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, + CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE, 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); *dt_clk = clk; } diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index fd1a99c05c2d..63087d17c3e2 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -1092,9 +1092,7 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { data = &tegra_periph_clk_list[i]; - clk = tegra_clk_register_periph(data->name, - data->p.parent_names, data->num_parents, - &data->periph, clk_base, data->offset, data->flags); + clk = tegra_clk_register_periph_data(clk_base, data); clks[data->clk_id] = clk; } diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c index ad1c1cc829cb..269d3595758b 100644 --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c @@ -125,19 +125,17 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) return err; } - platform_set_drvdata(pdev, soc); - return 0; } static int tegra124_dfll_fcpu_remove(struct platform_device *pdev) { - struct tegra_dfll_soc_data *soc = platform_get_drvdata(pdev); - int err; + struct tegra_dfll_soc_data *soc; - err = tegra_dfll_unregister(pdev); - if (err < 0) - dev_err(&pdev->dev, "failed to unregister DFLL: %d\n", err); + soc = tegra_dfll_unregister(pdev); + if (IS_ERR(soc)) + dev_err(&pdev->dev, "failed to unregister DFLL: %ld\n", + PTR_ERR(soc)); tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq); diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 837e5cbd60e9..cbd5a2e5c569 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -522,6 +522,8 @@ static struct tegra_devclk devclks[] __initdata = { }; static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = { + [tegra_clk_ahbdma] = { .dt_id = TEGRA20_CLK_AHBDMA, .present = true }, + [tegra_clk_apbdma] = { .dt_id = TEGRA20_CLK_APBDMA, .present = true }, [tegra_clk_spdif_out] = { .dt_id = TEGRA20_CLK_SPDIF_OUT, .present = true }, [tegra_clk_spdif_in] = { .dt_id = TEGRA20_CLK_SPDIF_IN, .present = true }, [tegra_clk_sdmmc1] = { .dt_id = TEGRA20_CLK_SDMMC1, .present = true }, @@ -806,11 +808,6 @@ static void __init tegra20_periph_clk_init(void) clk_base, 0, 3, periph_clk_enb_refcnt); clks[TEGRA20_CLK_AC97] = clk; - /* apbdma */ - clk = tegra_clk_register_periph_gate("apbdma", "pclk", 0, clk_base, - 0, 34, periph_clk_enb_refcnt); - clks[TEGRA20_CLK_APBDMA] = clk; - /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, ARRAY_SIZE(mux_pllmcp_clkm), @@ -850,9 +847,7 @@ static void __init tegra20_periph_clk_init(void) for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { data = &tegra_periph_clk_list[i]; - clk = tegra_clk_register_periph(data->name, data->p.parent_names, - data->num_parents, &data->periph, - clk_base, data->offset, data->flags); + clk = tegra_clk_register_periph_data(clk_base, data); clks[data->clk_id] = clk; } @@ -1025,7 +1020,7 @@ static struct tegra_clk_init_table init_table[] __initdata = { { TEGRA20_CLK_PLL_P_OUT3, TEGRA20_CLK_CLK_MAX, 72000000, 1 }, { TEGRA20_CLK_PLL_P_OUT4, TEGRA20_CLK_CLK_MAX, 24000000, 1 }, { TEGRA20_CLK_PLL_C, TEGRA20_CLK_CLK_MAX, 600000000, 1 }, - { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 120000000, 1 }, + { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 216000000, 1 }, { TEGRA20_CLK_SCLK, TEGRA20_CLK_PLL_C_OUT1, 0, 1 }, { TEGRA20_CLK_HCLK, TEGRA20_CLK_CLK_MAX, 0, 1 }, { TEGRA20_CLK_PCLK, TEGRA20_CLK_CLK_MAX, 60000000, 1 }, diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 6d7a613f2656..9e6260869eb9 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -40,6 +40,7 @@ #define CLK_SOURCE_CSITE 0x1d4 #define CLK_SOURCE_EMC 0x19c +#define CLK_SOURCE_SOR1 0x410 #define PLLC_BASE 0x80 #define PLLC_OUT 0x84 @@ -264,6 +265,7 @@ static DEFINE_SPINLOCK(pll_d_lock); static DEFINE_SPINLOCK(pll_e_lock); static DEFINE_SPINLOCK(pll_re_lock); static DEFINE_SPINLOCK(pll_u_lock); +static DEFINE_SPINLOCK(sor1_lock); static DEFINE_SPINLOCK(emc_lock); /* possible OSC frequencies in Hz */ @@ -2566,8 +2568,8 @@ static int tegra210_enable_pllu(void) reg |= PLL_ENABLE; writel(reg, clk_base + PLLU_BASE); - readl_relaxed_poll_timeout(clk_base + PLLU_BASE, reg, - reg & PLL_BASE_LOCK, 2, 1000); + readl_relaxed_poll_timeout_atomic(clk_base + PLLU_BASE, reg, + reg & PLL_BASE_LOCK, 2, 1000); if (!(reg & PLL_BASE_LOCK)) { pr_err("Timed out waiting for PLL_U to lock\n"); return -ETIMEDOUT; @@ -2628,10 +2630,35 @@ static int tegra210_init_pllu(void) return 0; } +static const char * const sor1_out_parents[] = { + /* + * Bit 0 of the mux selects sor1_pad_clkout, irrespective of bit 1, so + * the sor1_pad_clkout parent appears twice in the list below. This is + * merely to support clk_get_parent() if firmware happened to set + * these bits to 0b11. While not an invalid setting, code should + * always set the bits to 0b01 to select sor1_pad_clkout. + */ + "sor_safe", "sor1_pad_clkout", "sor1", "sor1_pad_clkout", +}; + +static const char * const sor1_parents[] = { + "pll_p", "pll_d_out0", "pll_d2_out0", "clk_m", +}; + +static u32 sor1_parents_idx[] = { 0, 2, 5, 6 }; + +static struct tegra_periph_init_data tegra210_periph[] = { + TEGRA_INIT_DATA_TABLE("sor1", NULL, NULL, sor1_parents, + CLK_SOURCE_SOR1, 29, 0x7, 0, 0, 8, 1, + TEGRA_DIVIDER_ROUND_UP, 183, 0, tegra_clk_sor1, + sor1_parents_idx, 0, &sor1_lock), +}; + static __init void tegra210_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base) { struct clk *clk; + unsigned int i; /* xusb_ss_div2 */ clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0, @@ -2650,6 +2677,12 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, 1, 17, 207); clks[TEGRA210_CLK_DPAUX1] = clk; + clk = clk_register_mux_table(NULL, "sor1_out", sor1_out_parents, + ARRAY_SIZE(sor1_out_parents), 0, + clk_base + CLK_SOURCE_SOR1, 14, 0x3, + 0, NULL, &sor1_lock); + clks[TEGRA210_CLK_SOR1_OUT] = clk; + /* pll_d_dsi_out */ clk = clk_register_gate(NULL, "pll_d_dsi_out", "pll_d_out0", 0, clk_base + PLLD_MISC0, 21, 0, &pll_d_lock); @@ -2694,6 +2727,20 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, 0, NULL); clks[TEGRA210_CLK_ACLK] = clk; + for (i = 0; i < ARRAY_SIZE(tegra210_periph); i++) { + struct tegra_periph_init_data *init = &tegra210_periph[i]; + struct clk **clkp; + + clkp = tegra_lookup_dt_id(init->clk_id, tegra210_clks); + if (!clkp) { + pr_warn("clock %u not found\n", init->clk_id); + continue; + } + + clk = tegra_clk_register_periph_data(clk_base, init); + *clkp = clk; + } + tegra_periph_clk_init(clk_base, pmc_base, tegra210_clks, &pll_p_params); } diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index a2d163f759b4..bee84c554932 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -359,7 +359,7 @@ static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { }; /* PLL parameters */ -static struct tegra_clk_pll_params pll_c_params = { +static struct tegra_clk_pll_params pll_c_params __ro_after_init = { .input_min = 2000000, .input_max = 31000000, .cf_min = 1000000, @@ -388,7 +388,7 @@ static struct div_nmp pllm_nmp = { .override_divp_shift = 15, }; -static struct tegra_clk_pll_params pll_m_params = { +static struct tegra_clk_pll_params pll_m_params __ro_after_init = { .input_min = 2000000, .input_max = 31000000, .cf_min = 1000000, @@ -409,7 +409,7 @@ static struct tegra_clk_pll_params pll_m_params = { TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_FIXED, }; -static struct tegra_clk_pll_params pll_p_params = { +static struct tegra_clk_pll_params pll_p_params __ro_after_init = { .input_min = 2000000, .input_max = 31000000, .cf_min = 1000000, @@ -444,7 +444,7 @@ static struct tegra_clk_pll_params pll_a_params = { TEGRA_PLL_HAS_LOCK_ENABLE, }; -static struct tegra_clk_pll_params pll_d_params = { +static struct tegra_clk_pll_params pll_d_params __ro_after_init = { .input_min = 2000000, .input_max = 40000000, .cf_min = 1000000, @@ -461,7 +461,7 @@ static struct tegra_clk_pll_params pll_d_params = { TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE, }; -static struct tegra_clk_pll_params pll_d2_params = { +static struct tegra_clk_pll_params pll_d2_params __ro_after_init = { .input_min = 2000000, .input_max = 40000000, .cf_min = 1000000, @@ -478,7 +478,7 @@ static struct tegra_clk_pll_params pll_d2_params = { TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE, }; -static struct tegra_clk_pll_params pll_u_params = { +static struct tegra_clk_pll_params pll_u_params __ro_after_init = { .input_min = 2000000, .input_max = 40000000, .cf_min = 1000000, @@ -496,7 +496,7 @@ static struct tegra_clk_pll_params pll_u_params = { TEGRA_PLL_HAS_LOCK_ENABLE, }; -static struct tegra_clk_pll_params pll_x_params = { +static struct tegra_clk_pll_params pll_x_params __ro_after_init = { .input_min = 2000000, .input_max = 31000000, .cf_min = 1000000, @@ -513,7 +513,7 @@ static struct tegra_clk_pll_params pll_x_params = { TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE, }; -static struct tegra_clk_pll_params pll_e_params = { +static struct tegra_clk_pll_params pll_e_params __ro_after_init = { .input_min = 12000000, .input_max = 216000000, .cf_min = 12000000, @@ -788,6 +788,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = { [tegra_clk_extern3] = { .dt_id = TEGRA30_CLK_EXTERN3, .present = true }, [tegra_clk_disp1] = { .dt_id = TEGRA30_CLK_DISP1, .present = true }, [tegra_clk_disp2] = { .dt_id = TEGRA30_CLK_DISP2, .present = true }, + [tegra_clk_ahbdma] = { .dt_id = TEGRA30_CLK_AHBDMA, .present = true }, [tegra_clk_apbdma] = { .dt_id = TEGRA30_CLK_APBDMA, .present = true }, [tegra_clk_rtc] = { .dt_id = TEGRA30_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA30_CLK_TIMER, .present = true }, @@ -964,7 +965,7 @@ static void __init tegra30_super_clk_init(void) * U71 divider of cclk_lp. */ clk = tegra_clk_register_divider("pll_p_out3_cclklp", "pll_p_out3", - clk_base + SUPER_CCLKG_DIVIDER, 0, + clk_base + SUPER_CCLKLP_DIVIDER, 0, TEGRA_DIVIDER_INT, 16, 8, 1, NULL); clk_register_clkdev(clk, "pll_p_out3_cclklp", NULL); @@ -1079,9 +1080,7 @@ static void __init tegra30_periph_clk_init(void) for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { data = &tegra_periph_clk_list[i]; - clk = tegra_clk_register_periph(data->name, data->p.parent_names, - data->num_parents, &data->periph, - clk_base, data->offset, data->flags); + clk = tegra_clk_register_periph_data(clk_base, data); clks[data->clk_id] = clk; } diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 872f1189ad7f..3b2763df51c2 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -662,6 +662,9 @@ struct tegra_periph_init_data { _clk_num, _gate_flags, _clk_id,\ NULL, 0, NULL) +struct clk *tegra_clk_register_periph_data(void __iomem *clk_base, + struct tegra_periph_init_data *init); + /** * struct clk_super_mux - super clock * diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index 13eb04f72389..148815470431 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -274,8 +274,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) /* Get configuration for the ATL instances */ snprintf(prop, sizeof(prop), "atl%u", i); - of_node_get(node); - cfg_node = of_find_node_by_name(node, prop); + cfg_node = of_get_child_by_name(node, prop); if (cfg_node) { ret = of_property_read_u32(cfg_node, "bws", &cdesc->bws); diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index 88f04a4cb890..77f93f6d2806 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -292,10 +292,8 @@ static struct clk *_register_divider(struct device *dev, const char *name, /* allocate the divider */ div = kzalloc(sizeof(*div), GFP_KERNEL); - if (!div) { - pr_err("%s: could not allocate divider clk\n", __func__); + if (!div) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &ti_clk_divider_ops; diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index 18c267b38461..d4705803f3d3 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -108,10 +108,8 @@ static struct clk *_register_mux(struct device *dev, const char *name, /* allocate the mux */ mux = kzalloc(sizeof(*mux), GFP_KERNEL); - if (!mux) { - pr_err("%s: could not allocate mux clk\n", __func__); + if (!mux) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &ti_clk_mux_ops; diff --git a/drivers/clk/uniphier/clk-uniphier-mio.c b/drivers/clk/uniphier/clk-uniphier-mio.c index 16e4d303f535..badc478a86c6 100644 --- a/drivers/clk/uniphier/clk-uniphier-mio.c +++ b/drivers/clk/uniphier/clk-uniphier-mio.c @@ -13,6 +13,8 @@ * GNU General Public License for more details. */ +#include + #include "clk-uniphier.h" #define UNIPHIER_MIO_CLK_SD_FIXED \ @@ -73,15 +75,12 @@ #define UNIPHIER_MIO_CLK_USB2_PHY(idx, ch) \ UNIPHIER_CLK_GATE("usb2" #ch "-phy", (idx), "usb2", 0x20 + 0x200 * (ch), 29) -#define UNIPHIER_MIO_CLK_DMAC(idx) \ - UNIPHIER_CLK_GATE("miodmac", (idx), "stdmac", 0x20, 25) - const struct uniphier_clk_data uniphier_ld4_mio_clk_data[] = { UNIPHIER_MIO_CLK_SD_FIXED, UNIPHIER_MIO_CLK_SD(0, 0), UNIPHIER_MIO_CLK_SD(1, 1), UNIPHIER_MIO_CLK_SD(2, 2), - UNIPHIER_MIO_CLK_DMAC(7), + UNIPHIER_CLK_GATE("miodmac", 7, NULL, 0x20, 25), UNIPHIER_MIO_CLK_USB2(8, 0), UNIPHIER_MIO_CLK_USB2(9, 1), UNIPHIER_MIO_CLK_USB2(10, 2), diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index 07f3b91a7daf..d244e724e198 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -123,7 +123,7 @@ const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = { const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("spll", -1, "ref", 120, 1), /* 2400 MHz */ UNIPHIER_CLK_FACTOR("dapll1", -1, "ref", 128, 1), /* 2560 MHz */ - UNIPHIER_CLK_FACTOR("dapll2", -1, "ref", 144, 125), /* 2949.12 MHz */ + UNIPHIER_CLK_FACTOR("dapll2", -1, "dapll1", 144, 125), /* 2949.12 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "dapll2", 1, 40), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), UNIPHIER_PRO5_SYS_CLK_NAND(2), diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c index f50592775c9d..7cfb59c9136d 100644 --- a/drivers/clk/ux500/clk-prcc.c +++ b/drivers/clk/ux500/clk-prcc.c @@ -107,11 +107,9 @@ static struct clk *clk_reg_prcc(const char *name, return ERR_PTR(-EINVAL); } - clk = kzalloc(sizeof(struct clk_prcc), GFP_KERNEL); - if (!clk) { - pr_err("clk_prcc: %s could not allocate clk\n", __func__); + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) return ERR_PTR(-ENOMEM); - } clk->base = ioremap(phy_base, SZ_4K); if (!clk->base) diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 6e3e16b2e5ca..9d1f2d4550ad 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -258,11 +258,9 @@ static struct clk *clk_reg_prcmu(const char *name, return ERR_PTR(-EINVAL); } - clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL); - if (!clk) { - pr_err("clk_prcmu: %s could not allocate clk\n", __func__); + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) return ERR_PTR(-ENOMEM); - } clk->cg_sel = cg_sel; clk->is_prepared = 1; diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c index 8a4e93ce1e42..7c0403b733ae 100644 --- a/drivers/clk/ux500/clk-sysctrl.c +++ b/drivers/clk/ux500/clk-sysctrl.c @@ -139,11 +139,9 @@ static struct clk *clk_reg_sysctrl(struct device *dev, return ERR_PTR(-EINVAL); } - clk = devm_kzalloc(dev, sizeof(struct clk_sysctrl), GFP_KERNEL); - if (!clk) { - dev_err(dev, "clk_sysctrl: could not allocate clk\n"); + clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); + if (!clk) return ERR_PTR(-ENOMEM); - } /* set main clock registers */ clk->reg_sel[0] = reg_sel[0]; diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c index 09fbe66f1f11..dafe7a45875d 100644 --- a/drivers/clk/versatile/clk-icst.c +++ b/drivers/clk/versatile/clk-icst.c @@ -359,16 +359,13 @@ static struct clk *icst_clk_setup(struct device *dev, struct clk_init_data init; struct icst_params *pclone; - icst = kzalloc(sizeof(struct clk_icst), GFP_KERNEL); - if (!icst) { - pr_err("could not allocate ICST clock!\n"); + icst = kzalloc(sizeof(*icst), GFP_KERNEL); + if (!icst) return ERR_PTR(-ENOMEM); - } pclone = kmemdup(desc->params, sizeof(*pclone), GFP_KERNEL); if (!pclone) { kfree(icst); - pr_err("could not clone ICST params\n"); return ERR_PTR(-ENOMEM); } diff --git a/drivers/dax/device.c b/drivers/dax/device.c index e9f3b3e4bbf4..6833ada237ab 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -222,7 +222,8 @@ __weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, unsigned long size) { struct resource *res; - phys_addr_t phys; + /* gcc-4.6.3-nolibc for i386 complains that this is uninitialized */ + phys_addr_t uninitialized_var(phys); int i; for (i = 0; i < dev_dax->num_resources; i++) { diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 557b93703532..3ec804672601 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -92,21 +92,21 @@ int __bdev_dax_supported(struct super_block *sb, int blocksize) long len; if (blocksize != PAGE_SIZE) { - pr_err("VFS (%s): error: unsupported blocksize for dax\n", + pr_debug("VFS (%s): error: unsupported blocksize for dax\n", sb->s_id); return -EINVAL; } err = bdev_dax_pgoff(bdev, 0, PAGE_SIZE, &pgoff); if (err) { - pr_err("VFS (%s): error: unaligned partition for dax\n", + pr_debug("VFS (%s): error: unaligned partition for dax\n", sb->s_id); return err; } dax_dev = dax_get_by_host(bdev->bd_disk->disk_name); if (!dax_dev) { - pr_err("VFS (%s): error: device does not support dax\n", + pr_debug("VFS (%s): error: device does not support dax\n", sb->s_id); return -EOPNOTSUPP; } @@ -118,7 +118,7 @@ int __bdev_dax_supported(struct super_block *sb, int blocksize) put_dax(dax_dev); if (len < 1) { - pr_err("VFS (%s): error: dax access failed (%ld)", + pr_debug("VFS (%s): error: dax access failed (%ld)\n", sb->s_id, len); return len < 0 ? len : -EIO; } @@ -273,9 +273,6 @@ EXPORT_SYMBOL_GPL(dax_copy_from_iter); void arch_wb_cache_pmem(void *addr, size_t size); void dax_flush(struct dax_device *dax_dev, void *addr, size_t size) { - if (unlikely(!dax_alive(dax_dev))) - return; - if (unlikely(!test_bit(DAXDEV_WRITE_CACHE, &dax_dev->flags))) return; @@ -344,6 +341,9 @@ static struct inode *dax_alloc_inode(struct super_block *sb) struct inode *inode; dax_dev = kmem_cache_alloc(dax_cache, GFP_KERNEL); + if (!dax_dev) + return NULL; + inode = &dax_dev->inode; inode->i_rdev = 0; return inode; diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 9a302799040e..5d101c4053e0 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -27,7 +27,6 @@ #define CREATE_TRACE_POINTS #include -EXPORT_TRACEPOINT_SYMBOL(dma_fence_annotate_wait_on); EXPORT_TRACEPOINT_SYMBOL(dma_fence_emit); EXPORT_TRACEPOINT_SYMBOL(dma_fence_enable_signal); diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 68b2033bc30e..dfbd894d5bb7 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -585,6 +585,13 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) return ret ? : le32_to_cpu(scm_ret); } +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + return -ENODEV; +} + int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) { diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 3fea6f563ca9..688525dd4aee 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -382,6 +382,33 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) return ret ? : res.a1; } +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = mem_region; + desc.args[1] = mem_sz; + desc.args[2] = src; + desc.args[3] = src_sz; + desc.args[4] = dest; + desc.args[5] = dest_sz; + desc.args[6] = 0; + + desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, + QCOM_SCM_VAL, QCOM_SCM_VAL); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, + QCOM_MEM_PROT_ASSIGN_ID, + &desc, &res); + + return ret ? : res.a1; +} + int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) { struct qcom_scm_desc desc = {0}; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 9064e559a01f..af4c75217ea6 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -47,6 +47,19 @@ struct qcom_scm { u64 dload_mode_addr; }; +struct qcom_scm_current_perm_info { + __le32 vmid; + __le32 perm; + __le64 ctx; + __le32 ctx_size; + __le32 unused; +}; + +struct qcom_scm_mem_map_info { + __le64 mem_addr; + __le64 mem_size; +}; + static struct qcom_scm *__scm; static int qcom_scm_clk_enable(void) @@ -415,6 +428,88 @@ int qcom_scm_set_remote_state(u32 state, u32 id) } EXPORT_SYMBOL(qcom_scm_set_remote_state); +/** + * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership + * @mem_addr: mem region whose ownership need to be reassigned + * @mem_sz: size of the region. + * @srcvm: vmid for current set of owners, each set bit in + * flag indicate a unique owner + * @newvm: array having new owners and corrsponding permission + * flags + * @dest_cnt: number of owners in next set. + * + * Return negative errno on failure, 0 on success, with @srcvm updated. + */ +int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, + unsigned int *srcvm, + struct qcom_scm_vmperm *newvm, int dest_cnt) +{ + struct qcom_scm_current_perm_info *destvm; + struct qcom_scm_mem_map_info *mem_to_map; + phys_addr_t mem_to_map_phys; + phys_addr_t dest_phys; + phys_addr_t ptr_phys; + size_t mem_to_map_sz; + size_t dest_sz; + size_t src_sz; + size_t ptr_sz; + int next_vm; + __le32 *src; + void *ptr; + int ret; + int len; + int i; + + src_sz = hweight_long(*srcvm) * sizeof(*src); + mem_to_map_sz = sizeof(*mem_to_map); + dest_sz = dest_cnt * sizeof(*destvm); + ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + + ALIGN(dest_sz, SZ_64); + + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + /* Fill source vmid detail */ + src = ptr; + len = hweight_long(*srcvm); + for (i = 0; i < len; i++) { + src[i] = cpu_to_le32(ffs(*srcvm) - 1); + *srcvm ^= 1 << (ffs(*srcvm) - 1); + } + + /* Fill details of mem buff to map */ + mem_to_map = ptr + ALIGN(src_sz, SZ_64); + mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); + mem_to_map[0].mem_addr = cpu_to_le64(mem_addr); + mem_to_map[0].mem_size = cpu_to_le64(mem_sz); + + next_vm = 0; + /* Fill details of next vmid detail */ + destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + for (i = 0; i < dest_cnt; i++) { + destvm[i].vmid = cpu_to_le32(newvm[i].vmid); + destvm[i].perm = cpu_to_le32(newvm[i].perm); + destvm[i].ctx = 0; + destvm[i].ctx_size = 0; + next_vm |= BIT(newvm[i].vmid); + } + + ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, + ptr_phys, src_sz, dest_phys, dest_sz); + dma_free_coherent(__scm->dev, ALIGN(ptr_sz, SZ_64), ptr, ptr_phys); + if (ret) { + dev_err(__scm->dev, + "Assign memory protection call failed %d.\n", ret); + return -EINVAL; + } + + *srcvm = next_vm; + return 0; +} +EXPORT_SYMBOL(qcom_scm_assign_mem); + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 83f171c23943..dcd7f7917fc7 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -103,5 +103,10 @@ extern int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, size_t *size); extern int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, u32 spare); +#define QCOM_MEM_PROT_ASSIGN_ID 0x16 +extern int __qcom_scm_assign_mem(struct device *dev, + phys_addr_t mem_region, size_t mem_sz, + phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig index 26682454a446..e8af1f5e8a79 100644 --- a/drivers/gpu/drm/amd/amdgpu/Kconfig +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig @@ -41,3 +41,4 @@ config DRM_AMDGPU_GART_DEBUGFS pages. Uses more memory for housekeeping, enable only for debugging. source "drivers/gpu/drm/amd/acp/Kconfig" +source "drivers/gpu/drm/amd/display/Kconfig" diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index 7fc42e077770..78d609123420 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -4,13 +4,19 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. FULL_AMD_PATH=$(src)/.. +DISPLAY_FOLDER_NAME=display +FULL_AMD_DISPLAY_PATH = $(FULL_AMD_PATH)/$(DISPLAY_FOLDER_NAME) ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \ -I$(FULL_AMD_PATH)/include \ -I$(FULL_AMD_PATH)/amdgpu \ -I$(FULL_AMD_PATH)/scheduler \ -I$(FULL_AMD_PATH)/powerplay/inc \ - -I$(FULL_AMD_PATH)/acp/include + -I$(FULL_AMD_PATH)/acp/include \ + -I$(FULL_AMD_DISPLAY_PATH) \ + -I$(FULL_AMD_DISPLAY_PATH)/include \ + -I$(FULL_AMD_DISPLAY_PATH)/dc \ + -I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm amdgpu-y := amdgpu_drv.o @@ -133,4 +139,13 @@ include $(FULL_AMD_PATH)/powerplay/Makefile amdgpu-y += $(AMD_POWERPLAY_FILES) +ifneq ($(CONFIG_DRM_AMD_DC),) + +RELATIVE_AMD_DISPLAY_PATH = ../$(DISPLAY_FOLDER_NAME) +include $(FULL_AMD_DISPLAY_PATH)/Makefile + +amdgpu-y += $(AMD_DISPLAY_FILES) + +endif + obj-$(CONFIG_DRM_AMDGPU)+= amdgpu.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index cbcb6a153aba..5afaf6016b4a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -66,6 +66,7 @@ #include "amdgpu_vce.h" #include "amdgpu_vcn.h" #include "amdgpu_mn.h" +#include "amdgpu_dm.h" #include "gpu_scheduler.h" #include "amdgpu_virt.h" @@ -101,6 +102,8 @@ extern int amdgpu_vm_fragment_size; extern int amdgpu_vm_fault_stop; extern int amdgpu_vm_debug; extern int amdgpu_vm_update_mode; +extern int amdgpu_dc; +extern int amdgpu_dc_log; extern int amdgpu_sched_jobs; extern int amdgpu_sched_hw_submission; extern int amdgpu_no_evict; @@ -1535,6 +1538,7 @@ struct amdgpu_device { /* display */ bool enable_virtual_display; struct amdgpu_mode_info mode_info; + /* For pre-DCE11. DCE11 and later are in "struct amdgpu_device->dm" */ struct work_struct hotplug_work; struct amdgpu_irq_src crtc_irq; struct amdgpu_irq_src pageflip_irq; @@ -1590,6 +1594,9 @@ struct amdgpu_device { /* GDS */ struct amdgpu_gds gds; + /* display related functionality */ + struct amdgpu_display_manager dm; + struct amdgpu_ip_block ip_blocks[AMDGPU_MAX_IP_NUM]; int num_ip_blocks; struct mutex mn_lock; @@ -1653,6 +1660,9 @@ void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v); u64 amdgpu_mm_rdoorbell64(struct amdgpu_device *adev, u32 index); void amdgpu_mm_wdoorbell64(struct amdgpu_device *adev, u32 index, u64 v); +bool amdgpu_device_asic_has_dc_support(enum amd_asic_type asic_type); +bool amdgpu_device_has_dc_support(struct amdgpu_device *adev); + /* * Registers read & write functions. */ @@ -1911,5 +1921,11 @@ int amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser, uint64_t addr, struct amdgpu_bo **bo, struct amdgpu_bo_va_mapping **mapping); +#if defined(CONFIG_DRM_AMD_DC) +int amdgpu_dm_display_resume(struct amdgpu_device *adev ); +#else +static inline int amdgpu_dm_display_resume(struct amdgpu_device *adev) { return 0; } +#endif + #include "amdgpu_object.h" #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index c21adf60a7f2..057e1ecd83ce 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -59,12 +59,6 @@ static bool check_atom_bios(uint8_t *bios, size_t size) return false; } - tmp = bios[0x18] | (bios[0x19] << 8); - if (bios[tmp + 0x14] != 0x0) { - DRM_INFO("Not an x86 BIOS ROM\n"); - return false; - } - bios_header_start = bios[0x48] | (bios[0x49] << 8); if (!bios_header_start) { DRM_INFO("Can't locate bios header\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index a7afe553e0a1..f2b72c7c6857 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -911,10 +911,6 @@ static int amdgpu_cgs_get_active_displays_info(struct cgs_device *cgs_device, struct cgs_display_info *info) { CGS_FUNC_ADEV; - struct amdgpu_crtc *amdgpu_crtc; - struct drm_device *ddev = adev->ddev; - struct drm_crtc *crtc; - uint32_t line_time_us, vblank_lines; struct cgs_mode_info *mode_info; if (info == NULL) @@ -928,30 +924,43 @@ static int amdgpu_cgs_get_active_displays_info(struct cgs_device *cgs_device, mode_info->ref_clock = adev->clock.spll.reference_freq; } - if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { - list_for_each_entry(crtc, - &ddev->mode_config.crtc_list, head) { - amdgpu_crtc = to_amdgpu_crtc(crtc); - if (crtc->enabled) { - info->active_display_mask |= (1 << amdgpu_crtc->crtc_id); - info->display_count++; - } - if (mode_info != NULL && - crtc->enabled && amdgpu_crtc->enabled && - amdgpu_crtc->hw_mode.clock) { - line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) / - amdgpu_crtc->hw_mode.clock; - vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end - - amdgpu_crtc->hw_mode.crtc_vdisplay + - (amdgpu_crtc->v_border * 2); - mode_info->vblank_time_us = vblank_lines * line_time_us; - mode_info->refresh_rate = drm_mode_vrefresh(&amdgpu_crtc->hw_mode); - mode_info->ref_clock = adev->clock.spll.reference_freq; - mode_info = NULL; + if (!amdgpu_device_has_dc_support(adev)) { + struct amdgpu_crtc *amdgpu_crtc; + struct drm_device *ddev = adev->ddev; + struct drm_crtc *crtc; + uint32_t line_time_us, vblank_lines; + + if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { + list_for_each_entry(crtc, + &ddev->mode_config.crtc_list, head) { + amdgpu_crtc = to_amdgpu_crtc(crtc); + if (crtc->enabled) { + info->active_display_mask |= (1 << amdgpu_crtc->crtc_id); + info->display_count++; + } + if (mode_info != NULL && + crtc->enabled && amdgpu_crtc->enabled && + amdgpu_crtc->hw_mode.clock) { + line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) / + amdgpu_crtc->hw_mode.clock; + vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end - + amdgpu_crtc->hw_mode.crtc_vdisplay + + (amdgpu_crtc->v_border * 2); + mode_info->vblank_time_us = vblank_lines * line_time_us; + mode_info->refresh_rate = drm_mode_vrefresh(&amdgpu_crtc->hw_mode); + mode_info->ref_clock = adev->clock.spll.reference_freq; + mode_info = NULL; + } } } + } else { + info->display_count = adev->pm.pm_display_cfg.num_display; + if (mode_info != NULL) { + mode_info->vblank_time_us = adev->pm.pm_display_cfg.min_vblank_time; + mode_info->refresh_rate = adev->pm.pm_display_cfg.vrefresh; + mode_info->ref_clock = adev->clock.spll.reference_freq; + } } - return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 6c78623e1386..a57cec737c18 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1495,8 +1495,11 @@ out: memset(wait, 0, sizeof(*wait)); wait->out.status = (r > 0); wait->out.first_signaled = first; - /* set return value 0 to indicate success */ - r = array[first]->error; + + if (first < fence_count && array[first]) + r = array[first]->error; + else + r = 0; err_free_fence_array: for (i = 0; i < fence_count; i++) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index efcacb827de7..2c574374d9b6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1836,6 +1837,9 @@ static int amdgpu_fini(struct amdgpu_device *adev) adev->ip_blocks[i].status.hw = false; } + if (adev->firmware.load_type == AMDGPU_FW_LOAD_SMU) + amdgpu_ucode_fini_bo(adev); + for (i = adev->num_ip_blocks - 1; i >= 0; i--) { if (!adev->ip_blocks[i].status.sw) continue; @@ -2046,6 +2050,52 @@ static void amdgpu_device_detect_sriov_bios(struct amdgpu_device *adev) } } +bool amdgpu_device_asic_has_dc_support(enum amd_asic_type asic_type) +{ + switch (asic_type) { +#if defined(CONFIG_DRM_AMD_DC) + case CHIP_BONAIRE: + case CHIP_HAWAII: + case CHIP_KAVERI: + case CHIP_CARRIZO: + case CHIP_STONEY: + case CHIP_POLARIS11: + case CHIP_POLARIS10: + case CHIP_POLARIS12: + case CHIP_TONGA: + case CHIP_FIJI: +#if defined(CONFIG_DRM_AMD_DC_PRE_VEGA) + return amdgpu_dc != 0; +#endif + case CHIP_KABINI: + case CHIP_MULLINS: + return amdgpu_dc > 0; + case CHIP_VEGA10: +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case CHIP_RAVEN: +#endif + return amdgpu_dc != 0; +#endif + default: + return false; + } +} + +/** + * amdgpu_device_has_dc_support - check if dc is supported + * + * @adev: amdgpu_device_pointer + * + * Returns true for supported, false for not supported + */ +bool amdgpu_device_has_dc_support(struct amdgpu_device *adev) +{ + if (amdgpu_sriov_vf(adev)) + return false; + + return amdgpu_device_asic_has_dc_support(adev->asic_type); +} + /** * amdgpu_device_init - initialize the driver * @@ -2100,7 +2150,6 @@ int amdgpu_device_init(struct amdgpu_device *adev, adev->audio_endpt_rreg = &amdgpu_block_invalid_rreg; adev->audio_endpt_wreg = &amdgpu_block_invalid_wreg; - DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X 0x%02X).\n", amdgpu_asic_name[adev->asic_type], pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device, pdev->revision); @@ -2242,7 +2291,8 @@ int amdgpu_device_init(struct amdgpu_device *adev, goto failed; } /* init i2c buses */ - amdgpu_atombios_i2c_init(adev); + if (!amdgpu_device_has_dc_support(adev)) + amdgpu_atombios_i2c_init(adev); } /* Fence driver */ @@ -2378,7 +2428,8 @@ void amdgpu_device_fini(struct amdgpu_device *adev) adev->accel_working = false; cancel_delayed_work_sync(&adev->late_init_work); /* free i2c buses */ - amdgpu_i2c_fini(adev); + if (!amdgpu_device_has_dc_support(adev)) + amdgpu_i2c_fini(adev); amdgpu_atombios_fini(adev); kfree(adev->bios); adev->bios = NULL; @@ -2429,12 +2480,14 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon) drm_kms_helper_poll_disable(dev); - /* turn off display hw */ - drm_modeset_lock_all(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + if (!amdgpu_device_has_dc_support(adev)) { + /* turn off display hw */ + drm_modeset_lock_all(dev); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + } + drm_modeset_unlock_all(dev); } - drm_modeset_unlock_all(dev); amdgpu_amdkfd_suspend(adev); @@ -2577,13 +2630,25 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon) /* blat the mode back in */ if (fbcon) { - drm_helper_resume_force_mode(dev); - /* turn on display hw */ - drm_modeset_lock_all(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + if (!amdgpu_device_has_dc_support(adev)) { + /* pre DCE11 */ + drm_helper_resume_force_mode(dev); + + /* turn on display hw */ + drm_modeset_lock_all(dev); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + } + drm_modeset_unlock_all(dev); + } else { + /* + * There is no equivalent atomic helper to turn on + * display, so we defined our own function for this, + * once suspend resume is supported by the atomic + * framework this will be reworked + */ + amdgpu_dm_display_resume(adev); } - drm_modeset_unlock_all(dev); } drm_kms_helper_poll_enable(dev); @@ -2600,7 +2665,10 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon) #ifdef CONFIG_PM dev->dev->power.disable_depth++; #endif - drm_helper_hpd_irq_event(dev); + if (!amdgpu_device_has_dc_support(adev)) + drm_helper_hpd_irq_event(dev); + else + drm_kms_helper_hotplug_event(dev); #ifdef CONFIG_PM dev->dev->power.disable_depth--; #endif @@ -2900,6 +2968,7 @@ give_up_reset: */ int amdgpu_gpu_reset(struct amdgpu_device *adev) { + struct drm_atomic_state *state = NULL; int i, r; int resched; bool need_full_reset, vram_lost = false; @@ -2913,6 +2982,9 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev) /* block TTM */ resched = ttm_bo_lock_delayed_workqueue(&adev->mman.bdev); + /* store modesetting */ + if (amdgpu_device_has_dc_support(adev)) + state = drm_atomic_helper_suspend(adev->ddev); /* block scheduler */ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { @@ -3029,7 +3101,11 @@ out: } } - drm_helper_resume_force_mode(adev->ddev); + if (amdgpu_device_has_dc_support(adev)) { + r = drm_atomic_helper_resume(adev->ddev, state); + amdgpu_dm_display_resume(adev); + } else + drm_helper_resume_force_mode(adev->ddev); ttm_bo_unlock_delayed_workqueue(&adev->mman.bdev, resched); if (r) { @@ -3188,9 +3264,9 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf, pm_pg_lock = (*pos >> 23) & 1; if (*pos & (1ULL << 62)) { - se_bank = (*pos >> 24) & 0x3FF; - sh_bank = (*pos >> 34) & 0x3FF; - instance_bank = (*pos >> 44) & 0x3FF; + se_bank = (*pos & GENMASK_ULL(33, 24)) >> 24; + sh_bank = (*pos & GENMASK_ULL(43, 34)) >> 34; + instance_bank = (*pos & GENMASK_ULL(53, 44)) >> 44; if (se_bank == 0x3FF) se_bank = 0xFFFFFFFF; @@ -3264,9 +3340,9 @@ static ssize_t amdgpu_debugfs_regs_write(struct file *f, const char __user *buf, pm_pg_lock = (*pos >> 23) & 1; if (*pos & (1ULL << 62)) { - se_bank = (*pos >> 24) & 0x3FF; - sh_bank = (*pos >> 34) & 0x3FF; - instance_bank = (*pos >> 44) & 0x3FF; + se_bank = (*pos & GENMASK_ULL(33, 24)) >> 24; + sh_bank = (*pos & GENMASK_ULL(43, 34)) >> 34; + instance_bank = (*pos & GENMASK_ULL(53, 44)) >> 44; if (se_bank == 0x3FF) se_bank = 0xFFFFFFFF; @@ -3614,12 +3690,12 @@ static ssize_t amdgpu_debugfs_wave_read(struct file *f, char __user *buf, return -EINVAL; /* decode offset */ - offset = (*pos & 0x7F); - se = ((*pos >> 7) & 0xFF); - sh = ((*pos >> 15) & 0xFF); - cu = ((*pos >> 23) & 0xFF); - wave = ((*pos >> 31) & 0xFF); - simd = ((*pos >> 37) & 0xFF); + offset = (*pos & GENMASK_ULL(6, 0)); + se = (*pos & GENMASK_ULL(14, 7)) >> 7; + sh = (*pos & GENMASK_ULL(22, 15)) >> 15; + cu = (*pos & GENMASK_ULL(30, 23)) >> 23; + wave = (*pos & GENMASK_ULL(36, 31)) >> 31; + simd = (*pos & GENMASK_ULL(44, 37)) >> 37; /* switch to the specific se/sh/cu */ mutex_lock(&adev->grbm_idx_mutex); @@ -3664,14 +3740,14 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, return -EINVAL; /* decode offset */ - offset = (*pos & 0xFFF); /* in dwords */ - se = ((*pos >> 12) & 0xFF); - sh = ((*pos >> 20) & 0xFF); - cu = ((*pos >> 28) & 0xFF); - wave = ((*pos >> 36) & 0xFF); - simd = ((*pos >> 44) & 0xFF); - thread = ((*pos >> 52) & 0xFF); - bank = ((*pos >> 60) & 1); + offset = *pos & GENMASK_ULL(11, 0); + se = (*pos & GENMASK_ULL(19, 12)) >> 12; + sh = (*pos & GENMASK_ULL(27, 20)) >> 20; + cu = (*pos & GENMASK_ULL(35, 28)) >> 28; + wave = (*pos & GENMASK_ULL(43, 36)) >> 36; + simd = (*pos & GENMASK_ULL(51, 44)) >> 44; + thread = (*pos & GENMASK_ULL(59, 52)) >> 52; + bank = (*pos & GENMASK_ULL(61, 60)) >> 60; data = kmalloc_array(1024, sizeof(*data), GFP_KERNEL); if (!data) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index 6ad243293a78..138beb550a58 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -518,7 +518,7 @@ amdgpu_framebuffer_init(struct drm_device *dev, return 0; } -static struct drm_framebuffer * +struct drm_framebuffer * amdgpu_user_framebuffer_create(struct drm_device *dev, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) @@ -556,7 +556,7 @@ amdgpu_user_framebuffer_create(struct drm_device *dev, return &amdgpu_fb->base; } -static void amdgpu_output_poll_changed(struct drm_device *dev) +void amdgpu_output_poll_changed(struct drm_device *dev) { struct amdgpu_device *adev = dev->dev_private; amdgpu_fb_output_poll_changed(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h new file mode 100644 index 000000000000..3cc0ef0c055e --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h @@ -0,0 +1,33 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __AMDGPU_DISPLAY_H__ +#define __AMDGPU_DISPLAY_H__ + +struct drm_framebuffer * +amdgpu_user_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd); + +void amdgpu_output_poll_changed(struct drm_device *dev); + +#endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h index 7279fb5c3abc..56caaeee6fea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h @@ -433,7 +433,7 @@ struct amdgpu_pm { uint32_t fw_version; uint32_t pcie_gen_mask; uint32_t pcie_mlw_mask; - struct amd_pp_display_configuration pm_display_cfg;/* set by DAL */ + struct amd_pp_display_configuration pm_display_cfg;/* set by dc */ }; #define R600_SSTU_DFLT 0 diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index dd2f060d62a8..ec96bb1f9eaf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -106,6 +106,8 @@ int amdgpu_vm_debug = 0; int amdgpu_vram_page_split = 512; int amdgpu_vm_update_mode = -1; int amdgpu_exp_hw_support = 0; +int amdgpu_dc = -1; +int amdgpu_dc_log = 0; int amdgpu_sched_jobs = 32; int amdgpu_sched_hw_submission = 2; int amdgpu_no_evict = 0; @@ -211,6 +213,12 @@ module_param_named(vram_page_split, amdgpu_vram_page_split, int, 0444); MODULE_PARM_DESC(exp_hw_support, "experimental hw support (1 = enable, 0 = disable (default))"); module_param_named(exp_hw_support, amdgpu_exp_hw_support, int, 0444); +MODULE_PARM_DESC(dc, "Display Core driver (1 = enable, 0 = disable, -1 = auto (default))"); +module_param_named(dc, amdgpu_dc, int, 0444); + +MODULE_PARM_DESC(dc, "Display Core Log Level (0 = minimal (default), 1 = chatty"); +module_param_named(dc_log, amdgpu_dc_log, int, 0444); + MODULE_PARM_DESC(sched_jobs, "the max number of jobs supported in the sw queue (default 32)"); module_param_named(sched_jobs, amdgpu_sched_jobs, int, 0444); @@ -518,15 +526,15 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x6997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, {0x1002, 0x699F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, /* Vega 10 */ - {0x1002, 0x6860, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x6861, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x6862, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x6863, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x6864, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x6867, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x6868, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x686c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, - {0x1002, 0x687f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10|AMD_EXP_HW_SUPPORT}, + {0x1002, 0x6860, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x6861, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x6862, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x6863, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x6864, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x6867, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x6868, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x686c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + {0x1002, 0x687f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, /* Raven */ {0x1002, 0x15dd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RAVEN|AMD_IS_APU|AMD_EXP_HW_SUPPORT}, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index 562930b17a6d..90fa8e8bc6fb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -42,11 +42,6 @@ this contains a helper + a amdgpu fb the helper contains a pointer to amdgpu framebuffer baseclass. */ -struct amdgpu_fbdev { - struct drm_fb_helper helper; - struct amdgpu_framebuffer rfb; - struct amdgpu_device *adev; -}; static int amdgpufb_open(struct fb_info *info, int user) @@ -353,7 +348,8 @@ int amdgpu_fbdev_init(struct amdgpu_device *adev) drm_fb_helper_single_add_all_connectors(&rfbdev->helper); /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(adev->ddev); + if (!amdgpu_device_has_dc_support(adev)) + drm_helper_disable_unused_functions(adev->ddev); drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index a418df1b9422..e87eedcc0da9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -63,6 +63,11 @@ retry: flags, NULL, resv, 0, &bo); if (r) { if (r != -ERESTARTSYS) { + if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) { + flags &= ~AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; + goto retry; + } + if (initial_domain == AMDGPU_GEM_DOMAIN_VRAM) { initial_domain |= AMDGPU_GEM_DOMAIN_GTT; goto retry; @@ -323,7 +328,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, r = amdgpu_ttm_tt_get_user_pages(bo->tbo.ttm, bo->tbo.ttm->pages); if (r) - goto unlock_mmap_sem; + goto release_object; r = amdgpu_bo_reserve(bo, true); if (r) @@ -348,9 +353,6 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, free_pages: release_pages(bo->tbo.ttm->pages, bo->tbo.ttm->num_pages); -unlock_mmap_sem: - up_read(¤t->mm->mmap_sem); - release_object: drm_gem_object_put_unlocked(gobj); @@ -556,9 +558,8 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, if (args->va_address < AMDGPU_VA_RESERVED_SIZE) { dev_err(&dev->pdev->dev, - "va_address 0x%lX is in reserved area 0x%X\n", - (unsigned long)args->va_address, - AMDGPU_VA_RESERVED_SIZE); + "va_address 0x%LX is in reserved area 0x%LX\n", + args->va_address, AMDGPU_VA_RESERVED_SIZE); return -EINVAL; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index 33535d347734..00e0ce10862f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -71,12 +71,6 @@ static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man) { struct amdgpu_gtt_mgr *mgr = man->priv; - spin_lock(&mgr->lock); - if (!drm_mm_clean(&mgr->mm)) { - spin_unlock(&mgr->lock); - return -EBUSY; - } - drm_mm_takedown(&mgr->mm); spin_unlock(&mgr->lock); kfree(mgr); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 538e5f27d120..47c5ce9807db 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -37,6 +37,10 @@ #include +#ifdef CONFIG_DRM_AMD_DC +#include "amdgpu_dm_irq.h" +#endif + #define AMDGPU_WAIT_IDLE_TIMEOUT 200 /* @@ -221,15 +225,6 @@ int amdgpu_irq_init(struct amdgpu_device *adev) spin_lock_init(&adev->irq.lock); - if (!adev->enable_virtual_display) - /* Disable vblank irqs aggressively for power-saving */ - adev->ddev->vblank_disable_immediate = true; - - r = drm_vblank_init(adev->ddev, adev->mode_info.num_crtc); - if (r) { - return r; - } - /* enable msi */ adev->irq.msi_enabled = false; @@ -241,7 +236,21 @@ int amdgpu_irq_init(struct amdgpu_device *adev) } } - INIT_WORK(&adev->hotplug_work, amdgpu_hotplug_work_func); + if (!amdgpu_device_has_dc_support(adev)) { + if (!adev->enable_virtual_display) + /* Disable vblank irqs aggressively for power-saving */ + /* XXX: can this be enabled for DC? */ + adev->ddev->vblank_disable_immediate = true; + + r = drm_vblank_init(adev->ddev, adev->mode_info.num_crtc); + if (r) + return r; + + /* pre DCE11 */ + INIT_WORK(&adev->hotplug_work, + amdgpu_hotplug_work_func); + } + INIT_WORK(&adev->reset_work, amdgpu_irq_reset_work_func); adev->irq.installed = true; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 6f0b26dae3b0..720139e182a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1030,7 +1030,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_GEM_METADATA, amdgpu_gem_metadata_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_GEM_VA, amdgpu_gem_va_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_GEM_OP, amdgpu_gem_op_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF_DRV(AMDGPU_GEM_USERPTR, amdgpu_gem_userptr_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_GEM_USERPTR, amdgpu_gem_userptr_ioctl, DRM_AUTH|DRM_RENDER_ALLOW) }; const int amdgpu_max_kms_ioctl = ARRAY_SIZE(amdgpu_ioctls_kms); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 2af2678ddaf6..ffde1e9666e8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -38,11 +38,15 @@ #include #include #include +#include #include #include #include #include "amdgpu_irq.h" +#include +#include "modules/inc/mod_freesync.h" + struct amdgpu_bo; struct amdgpu_device; struct amdgpu_encoder; @@ -53,9 +57,13 @@ struct amdgpu_hpd; #define to_amdgpu_connector(x) container_of(x, struct amdgpu_connector, base) #define to_amdgpu_encoder(x) container_of(x, struct amdgpu_encoder, base) #define to_amdgpu_framebuffer(x) container_of(x, struct amdgpu_framebuffer, base) +#define to_amdgpu_plane(x) container_of(x, struct amdgpu_plane, base) + +#define to_dm_plane_state(x) container_of(x, struct dm_plane_state, base); #define AMDGPU_MAX_HPD_PINS 6 #define AMDGPU_MAX_CRTCS 6 +#define AMDGPU_MAX_PLANES 6 #define AMDGPU_MAX_AFMT_BLOCKS 9 enum amdgpu_rmx_type { @@ -292,6 +300,30 @@ struct amdgpu_display_funcs { uint16_t connector_object_id, struct amdgpu_hpd *hpd, struct amdgpu_router *router); + /* it is used to enter or exit into free sync mode */ + int (*notify_freesync)(struct drm_device *dev, void *data, + struct drm_file *filp); + /* it is used to allow enablement of freesync mode */ + int (*set_freesync_property)(struct drm_connector *connector, + struct drm_property *property, + uint64_t val); + + +}; + +struct amdgpu_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; + + /* caching for later use */ + uint64_t address; +}; + +struct amdgpu_fbdev { + struct drm_fb_helper helper; + struct amdgpu_framebuffer rfb; + struct list_head fbdev_list; + struct amdgpu_device *adev; }; struct amdgpu_mode_info { @@ -299,6 +331,7 @@ struct amdgpu_mode_info { struct card_info *atom_card_info; bool mode_config_initialized; struct amdgpu_crtc *crtcs[AMDGPU_MAX_CRTCS]; + struct amdgpu_plane *planes[AMDGPU_MAX_PLANES]; struct amdgpu_afmt *afmt[AMDGPU_MAX_AFMT_BLOCKS]; /* DVI-I properties */ struct drm_property *coherent_mode_property; @@ -328,6 +361,7 @@ struct amdgpu_mode_info { int num_dig; /* number of dig blocks */ int disp_priority; const struct amdgpu_display_funcs *funcs; + const enum drm_plane_type *plane_type; }; #define AMDGPU_MAX_BL_LEVEL 0xFF @@ -400,6 +434,14 @@ struct amdgpu_crtc { /* for virtual dce */ struct hrtimer vblank_timer; enum amdgpu_interrupt_state vsync_timer_enabled; + + int otg_inst; + struct drm_pending_vblank_event *event; +}; + +struct amdgpu_plane { + struct drm_plane base; + enum drm_plane_type plane_type; }; struct amdgpu_encoder_atom_dig { @@ -489,6 +531,19 @@ enum amdgpu_connector_dither { AMDGPU_FMT_DITHER_ENABLE = 1, }; +struct amdgpu_dm_dp_aux { + struct drm_dp_aux aux; + struct ddc_service *ddc_service; +}; + +struct amdgpu_i2c_adapter { + struct i2c_adapter base; + + struct ddc_service *ddc_service; +}; + +#define TO_DM_AUX(x) container_of((x), struct amdgpu_dm_dp_aux, aux) + struct amdgpu_connector { struct drm_connector base; uint32_t connector_id; @@ -500,6 +555,14 @@ struct amdgpu_connector { /* we need to mind the EDID between detect and get modes due to analog/digital/tvencoder */ struct edid *edid; + /* number of modes generated from EDID at 'dc_sink' */ + int num_modes; + /* The 'old' sink - before an HPD. + * The 'current' sink is in dc_link->sink. */ + struct dc_sink *dc_sink; + struct dc_link *dc_link; + struct dc_sink *dc_em_sink; + const struct dc_stream *stream; void *con_priv; bool dac_load_detect; bool detected_by_load; /* if the connection status was determined by load */ @@ -510,11 +573,39 @@ struct amdgpu_connector { enum amdgpu_connector_audio audio; enum amdgpu_connector_dither dither; unsigned pixelclock_for_modeset; + + struct drm_dp_mst_topology_mgr mst_mgr; + struct amdgpu_dm_dp_aux dm_dp_aux; + struct drm_dp_mst_port *port; + struct amdgpu_connector *mst_port; + struct amdgpu_encoder *mst_encoder; + struct semaphore mst_sem; + + /* TODO see if we can merge with ddc_bus or make a dm_connector */ + struct amdgpu_i2c_adapter *i2c; + + /* Monitor range limits */ + int min_vfreq ; + int max_vfreq ; + int pixel_clock_mhz; + + /*freesync caps*/ + struct mod_freesync_caps caps; + + struct mutex hpd_lock; + }; -struct amdgpu_framebuffer { - struct drm_framebuffer base; - struct drm_gem_object *obj; +/* TODO: start to use this struct and remove same field from base one */ +struct amdgpu_mst_connector { + struct amdgpu_connector base; + + struct drm_dp_mst_topology_mgr mst_mgr; + struct amdgpu_dm_dp_aux dm_dp_aux; + struct drm_dp_mst_port *port; + struct amdgpu_connector *mst_port; + bool is_mst_connector; + struct amdgpu_encoder *mst_encoder; }; #define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c index a59e04f3eeba..6c570d4e4516 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c @@ -946,6 +946,10 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, struct amdgpu_device *adev = dev_get_drvdata(dev); umode_t effective_mode = attr->mode; + /* no skipping for powerplay */ + if (adev->powerplay.cgs_device) + return effective_mode; + /* Skip limit attributes if DPM is not enabled */ if (!adev->pm.dpm_enabled && (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || @@ -1466,7 +1470,7 @@ void amdgpu_pm_compute_clocks(struct amdgpu_device *adev) list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) { amdgpu_crtc = to_amdgpu_crtc(crtc); - if (crtc->enabled) { + if (amdgpu_crtc->enabled) { adev->pm.dpm.new_active_crtcs |= (1 << amdgpu_crtc->crtc_id); adev->pm.dpm.new_active_crtc_count++; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c index 5f5aa5fddc16..033fba2def6f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c @@ -164,9 +164,6 @@ static int amdgpu_pp_hw_fini(void *handle) ret = adev->powerplay.ip_funcs->hw_fini( adev->powerplay.pp_handle); - if (adev->firmware.load_type == AMDGPU_FW_LOAD_SMU) - amdgpu_ucode_fini_bo(adev); - return ret; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c index 90af8e82b16a..ae9c106979d7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c @@ -169,10 +169,14 @@ struct dma_buf *amdgpu_gem_prime_export(struct drm_device *dev, int flags) { struct amdgpu_bo *bo = gem_to_amdgpu_bo(gobj); + struct dma_buf *buf; if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) || bo->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) return ERR_PTR(-EPERM); - return drm_gem_prime_export(dev, gobj, flags); + buf = drm_gem_prime_export(dev, gobj, flags); + if (!IS_ERR(buf)) + buf->file->f_mapping = dev->anon_inode->i_mapping; + return buf; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 447d446b5015..7714f4a6c8b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -442,8 +442,6 @@ static int psp_hw_fini(void *handle) if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) return 0; - amdgpu_ucode_fini_bo(adev); - psp_ring_destroy(psp, PSP_RING_TYPE__KM); amdgpu_bo_free_kernel(&psp->tmr_bo, &psp->tmr_mc_addr, &psp->tmr_buf); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index aa914256b4bc..bae77353447b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -94,7 +94,8 @@ struct amdgpu_bo_list_entry; #define AMDGPU_MMHUB 1 /* hardcode that limit for now */ -#define AMDGPU_VA_RESERVED_SIZE (8 << 20) +#define AMDGPU_VA_RESERVED_SIZE (8ULL << 20) + /* max vmids dedicated for process */ #define AMDGPU_VM_MAX_RESERVED_VMID 1 diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 26e900627971..4acca92f6a52 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -68,11 +68,6 @@ static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man) struct amdgpu_vram_mgr *mgr = man->priv; spin_lock(&mgr->lock); - if (!drm_mm_clean(&mgr->mm)) { - spin_unlock(&mgr->lock); - return -EBUSY; - } - drm_mm_takedown(&mgr->mm); spin_unlock(&mgr->lock); kfree(mgr); diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c index 567c4a5cf90c..793b1470284d 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik.c +++ b/drivers/gpu/drm/amd/amdgpu/cik.c @@ -65,6 +65,7 @@ #include "oss/oss_2_0_d.h" #include "oss/oss_2_0_sh_mask.h" +#include "amdgpu_dm.h" #include "amdgpu_amdkfd.h" #include "amdgpu_powerplay.h" #include "dce_virtual.h" @@ -1900,6 +1901,10 @@ int cik_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v8_2_ip_block); amdgpu_ip_block_add(adev, &gfx_v7_2_ip_block); @@ -1914,6 +1919,10 @@ int cik_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v8_5_ip_block); amdgpu_ip_block_add(adev, &gfx_v7_3_ip_block); @@ -1928,6 +1937,10 @@ int cik_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v8_1_ip_block); amdgpu_ip_block_add(adev, &gfx_v7_1_ip_block); @@ -1943,6 +1956,10 @@ int cik_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v8_3_ip_block); amdgpu_ip_block_add(adev, &gfx_v7_2_ip_block); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 00868764a0dd..5c8a7a48a4ad 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -4670,6 +4670,14 @@ static int gfx_v7_0_sw_fini(void *handle) gfx_v7_0_cp_compute_fini(adev); gfx_v7_0_rlc_fini(adev); gfx_v7_0_mec_fini(adev); + amdgpu_bo_free_kernel(&adev->gfx.rlc.clear_state_obj, + &adev->gfx.rlc.clear_state_gpu_addr, + (void **)&adev->gfx.rlc.cs_ptr); + if (adev->gfx.rlc.cp_table_size) { + amdgpu_bo_free_kernel(&adev->gfx.rlc.cp_table_obj, + &adev->gfx.rlc.cp_table_gpu_addr, + (void **)&adev->gfx.rlc.cp_table_ptr); + } gfx_v7_0_free_microcode(adev); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index b8002ac3e536..9ecdf621a74a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -2118,6 +2118,15 @@ static int gfx_v8_0_sw_fini(void *handle) gfx_v8_0_mec_fini(adev); gfx_v8_0_rlc_fini(adev); + amdgpu_bo_free_kernel(&adev->gfx.rlc.clear_state_obj, + &adev->gfx.rlc.clear_state_gpu_addr, + (void **)&adev->gfx.rlc.cs_ptr); + if ((adev->asic_type == CHIP_CARRIZO) || + (adev->asic_type == CHIP_STONEY)) { + amdgpu_bo_free_kernel(&adev->gfx.rlc.cp_table_obj, + &adev->gfx.rlc.cp_table_gpu_addr, + (void **)&adev->gfx.rlc.cp_table_ptr); + } gfx_v8_0_free_microcode(adev); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 7f15bb2c5233..da43813d67a4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -207,6 +207,12 @@ static const u32 golden_settings_gc_9_1_rv1[] = SOC15_REG_OFFSET(GC, 0, mmTD_CNTL), 0x01bd9f33, 0x00000800 }; +static const u32 golden_settings_gc_9_x_common[] = +{ + SOC15_REG_OFFSET(GC, 0, mmGRBM_CAM_INDEX), 0xffffffff, 0x00000000, + SOC15_REG_OFFSET(GC, 0, mmGRBM_CAM_DATA), 0xffffffff, 0x2544c382 +}; + #define VEGA10_GB_ADDR_CONFIG_GOLDEN 0x2a114042 #define RAVEN_GB_ADDR_CONFIG_GOLDEN 0x24000042 @@ -242,6 +248,9 @@ static void gfx_v9_0_init_golden_registers(struct amdgpu_device *adev) default: break; } + + amdgpu_program_register_sequence(adev, golden_settings_gc_9_x_common, + (const u32)ARRAY_SIZE(golden_settings_gc_9_x_common)); } static void gfx_v9_0_scratch_init(struct amdgpu_device *adev) @@ -988,12 +997,22 @@ static void gfx_v9_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd, start + SQIND_WAVE_SGPRS_OFFSET, size, dst); } +static void gfx_v9_0_read_wave_vgprs(struct amdgpu_device *adev, uint32_t simd, + uint32_t wave, uint32_t thread, + uint32_t start, uint32_t size, + uint32_t *dst) +{ + wave_read_regs( + adev, simd, wave, thread, + start + SQIND_WAVE_VGPRS_OFFSET, size, dst); +} static const struct amdgpu_gfx_funcs gfx_v9_0_gfx_funcs = { .get_gpu_clock_counter = &gfx_v9_0_get_gpu_clock_counter, .select_se_sh = &gfx_v9_0_select_se_sh, .read_wave_data = &gfx_v9_0_read_wave_data, .read_wave_sgprs = &gfx_v9_0_read_wave_sgprs, + .read_wave_vgprs = &gfx_v9_0_read_wave_vgprs, }; static void gfx_v9_0_gpu_early_init(struct amdgpu_device *adev) @@ -1449,6 +1468,14 @@ static int gfx_v9_0_sw_fini(void *handle) gfx_v9_0_mec_fini(adev); gfx_v9_0_ngg_fini(adev); + amdgpu_bo_free_kernel(&adev->gfx.rlc.clear_state_obj, + &adev->gfx.rlc.clear_state_gpu_addr, + (void **)&adev->gfx.rlc.cs_ptr); + if (adev->asic_type == CHIP_RAVEN) { + amdgpu_bo_free_kernel(&adev->gfx.rlc.cp_table_obj, + &adev->gfx.rlc.cp_table_gpu_addr, + (void **)&adev->gfx.rlc.cp_table_ptr); + } gfx_v9_0_free_microcode(adev); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 621699331e09..c8f1aebeac7a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -392,7 +392,16 @@ static int gmc_v9_0_early_init(void *handle) static int gmc_v9_0_late_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - unsigned vm_inv_eng[AMDGPU_MAX_VMHUBS] = { 3, 3 }; + /* + * The latest engine allocation on gfx9 is: + * Engine 0, 1: idle + * Engine 2, 3: firmware + * Engine 4~13: amdgpu ring, subject to change when ring number changes + * Engine 14~15: idle + * Engine 16: kfd tlb invalidation + * Engine 17: Gart flushes + */ + unsigned vm_inv_eng[AMDGPU_MAX_VMHUBS] = { 4, 4 }; unsigned i; for(i = 0; i < adev->num_rings; ++i) { @@ -405,9 +414,9 @@ static int gmc_v9_0_late_init(void *handle) ring->funcs->vmhub); } - /* Engine 17 is used for GART flushes */ + /* Engine 16 is used for KFD and 17 for GART flushes */ for(i = 0; i < AMDGPU_MAX_VMHUBS; ++i) - BUG_ON(vm_inv_eng[i] > 17); + BUG_ON(vm_inv_eng[i] > 16); return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0); } diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index 3ca9d114f630..4e67fe1e7955 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -532,6 +532,12 @@ int soc15_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#else +# warning "Enable CONFIG_DRM_AMD_DC for display support on SOC15." +#endif amdgpu_ip_block_add(adev, &gfx_v9_0_ip_block); amdgpu_ip_block_add(adev, &sdma_v4_0_ip_block); amdgpu_ip_block_add(adev, &uvd_v7_0_ip_block); @@ -545,6 +551,12 @@ int soc15_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#else +# warning "Enable CONFIG_DRM_AMD_DC for display support on SOC15." +#endif amdgpu_ip_block_add(adev, &gfx_v9_0_ip_block); amdgpu_ip_block_add(adev, &sdma_v4_0_ip_block); amdgpu_ip_block_add(adev, &vcn_v1_0_ip_block); diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index f3cfef48aa99..3a4c2fa7e36d 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -77,6 +77,7 @@ #endif #include "dce_virtual.h" #include "mxgpu_vi.h" +#include "amdgpu_dm.h" /* * Indirect registers accessor @@ -1502,6 +1503,10 @@ int vi_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v10_1_ip_block); amdgpu_ip_block_add(adev, &gfx_v8_0_ip_block); @@ -1518,6 +1523,10 @@ int vi_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v10_0_ip_block); amdgpu_ip_block_add(adev, &gfx_v8_0_ip_block); @@ -1536,6 +1545,10 @@ int vi_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v11_2_ip_block); amdgpu_ip_block_add(adev, &gfx_v8_0_ip_block); @@ -1550,6 +1563,10 @@ int vi_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v11_0_ip_block); amdgpu_ip_block_add(adev, &gfx_v8_0_ip_block); @@ -1567,6 +1584,10 @@ int vi_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); if (adev->enable_virtual_display) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); +#if defined(CONFIG_DRM_AMD_DC) + else if (amdgpu_device_has_dc_support(adev)) + amdgpu_ip_block_add(adev, &dm_ip_block); +#endif else amdgpu_ip_block_add(adev, &dce_v11_0_ip_block); amdgpu_ip_block_add(adev, &gfx_v8_1_ip_block); diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig new file mode 100644 index 000000000000..ec3285f65517 --- /dev/null +++ b/drivers/gpu/drm/amd/display/Kconfig @@ -0,0 +1,45 @@ +menu "Display Engine Configuration" + depends on DRM && DRM_AMDGPU + +config DRM_AMD_DC + bool "AMD DC - Enable new display engine" + default y + help + Choose this option if you want to use the new display engine + support for AMDGPU. This adds required support for Vega and + Raven ASICs. + +config DRM_AMD_DC_PRE_VEGA + bool "DC support for Polaris and older ASICs" + default n + help + Choose this option to enable the new DC support for older asics + by default. This includes Polaris, Carrizo, Tonga, Bonaire, + and Hawaii. + +config DRM_AMD_DC_FBC + bool "AMD FBC - Enable Frame Buffer Compression" + depends on DRM_AMD_DC + help + Choose this option if you want to use frame buffer compression + support. + This is a power optimisation feature, check its availability + on your hardware before enabling this option. + + +config DRM_AMD_DC_DCN1_0 + bool "DCN 1.0 Raven family" + depends on DRM_AMD_DC && X86 + help + Choose this option if you want to have + RV family for display engine + +config DEBUG_KERNEL_DC + bool "Enable kgdb break in DC" + depends on DRM_AMD_DC + help + Choose this option + if you want to hit + kdgb_break in assert. + +endmenu diff --git a/drivers/gpu/drm/amd/display/Makefile b/drivers/gpu/drm/amd/display/Makefile new file mode 100644 index 000000000000..8ba37dd9cf7f --- /dev/null +++ b/drivers/gpu/drm/amd/display/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the DAL (Display Abstract Layer), which is a sub-component +# of the AMDGPU drm driver. +# It provides the HW control for display related functionalities. + +AMDDALPATH = $(RELATIVE_AMD_DISPLAY_PATH) + +subdir-ccflags-y += -I$(AMDDALPATH)/ -I$(AMDDALPATH)/include + +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/inc/ +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/inc/hw +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/inc +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/freesync + +#TODO: remove when Timing Sync feature is complete +subdir-ccflags-y += -DBUILD_FEATURE_TIMING_SYNC=0 + +DAL_LIBS = amdgpu_dm dc modules/freesync + +AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/,$(DAL_LIBS))) + +include $(AMD_DAL) diff --git a/drivers/gpu/drm/amd/display/TODO b/drivers/gpu/drm/amd/display/TODO new file mode 100644 index 000000000000..46464678f2b3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/TODO @@ -0,0 +1,107 @@ +=============================================================================== +TODOs +=============================================================================== + +1. Base this on drm-next - WIP + + +2. Cleanup commit history + + +3. WIP - Drop page flip helper and use DRM's version + + +4. DONE - Flatten all DC objects + * dc_stream/core_stream/stream should just be dc_stream + * Same for other DC objects + + "Is there any major reason to keep all those abstractions? + + Could you collapse everything into struct dc_stream? + + I haven't looked recently but I didn't get the impression there was a + lot of design around what was public/protected, more whatever needed + to be used by someone else was in public." + ~ Dave Airlie + + +5. DONE - Rename DC objects to align more with DRM + * dc_surface -> dc_plane_state + * dc_stream -> dc_stream_state + + +6. DONE - Per-plane and per-stream validation + + +7. WIP - Per-plane and per-stream commit + + +8. WIP - Split pipe_ctx into plane and stream resource structs + + +9. Attach plane and stream reources to state object instead of validate_context + + +10. Remove dc_edid_caps and drm_helpers_parse_edid_caps + * Use drm_display_info instead + * Remove DC's edid quirks and rely on DRM's quirks (add quirks if needed) + + "Making sure you use the sink-specific helper libraries and kernel + subsystems, since there's really no good reason to have 2nd + implementation of those in the kernel. Looks likes that's done for mst + and edid parsing. There's still a bit a midlayer feeling to the edid + parsing side (e.g. dc_edid_caps and dm_helpers_parse_edid_caps, I + think it'd be much better if you convert that over to reading stuff + from drm_display_info and if needed, push stuff into the core). Also, + I can't come up with a good reason why DC needs all this (except to + reimplement half of our edid quirk table, which really isn't a good + idea). Might be good if you put this onto the list of things to fix + long-term, but imo not a blocker. Definitely make sure new stuff + doesn't slip in (i.e. if you start adding edid quirks to DC instead of + the drm core, refactoring to use the core edid stuff was pointless)." + ~ Daniel Vetter + + +11. Remove dc/i2caux. This folder can be somewhat misleading. It's basically an +overy complicated HW programming function for sendind and receiving i2c/aux +commands. We can greatly simplify that and move it into dc/dceXYZ like other +HW blocks. + +12. drm_modeset_lock in MST should no longer be needed in recent kernels + * Adopt appropriate locking scheme + +13. get_modes and best_encoder callbacks look a bit funny. Can probably rip out +a few indirections, and consider removing entirely and using the +drm_atomic_helper_best_encoder default behaviour. + +14. core/dc_debug.c, consider switching to the atomic state debug helpers and +moving all your driver state printing into the various atomic_print_state +callbacks. There's also plans to expose this stuff in a standard way across all +drivers, to make debugging userspace compositors easier across different hw. + +15. Move DP/HDMI dual mode adaptors to drm_dp_dual_mode_helper.c. See +dal_ddc_service_i2c_query_dp_dual_mode_adaptor. + +16. Move to core SCDC helpers (I think those are new since initial DC review). + +17. There's still a pretty massive layer cake around dp aux and DPCD handling, +with like 3 levels of abstraction and using your own structures instead of the +stuff in drm_dp_helper.h. drm_dp_helper.h isn't really great and already has 2 +incompatible styles, just means more reasons not to add a third (or well third +one gets to do the cleanup refactor). + +18. There's a pile of sink handling code, both for DP and HDMI where I didn't +immediately recognize the standard. I think long term it'd be best for the drm +subsystem if we try to move as much of that into helpers/core as possible, and +share it with drivers. But that's a very long term goal, and by far not just an +issue with DC - other drivers, especially around DP sink handling, are equally +guilty. + +19. The DC logger is still a rather sore thing, but I know that the DRM_DEBUG +stuff just isn't up to the challenges either. We need to figure out something +that integrates better with DRM and linux debug printing, while not being +useless with filtering output. dynamic debug printing might be an option. + +20. Use kernel i2c device to program HDMI retimer. Some boards have an HDMI +retimer that we need to program to pass PHY compliance. Currently that's +bypassing the i2c device and goes directly to HW. This should be changed. diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile new file mode 100644 index 000000000000..4699e47aa76b --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for the 'dm' sub-component of DAL. +# It provides the control and status of dm blocks. + + + +AMDGPUDM = amdgpu_dm.o amdgpu_dm_irq.o amdgpu_dm_mst_types.o + +ifneq ($(CONFIG_DRM_AMD_DC),) +AMDGPUDM += amdgpu_dm_services.o amdgpu_dm_helpers.o +endif + +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc + +AMDGPU_DM = $(addprefix $(AMDDALPATH)/amdgpu_dm/,$(AMDGPUDM)) + +AMD_DISPLAY_FILES += $(AMDGPU_DM) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c new file mode 100644 index 000000000000..889ed24084e8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -0,0 +1,4925 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services_types.h" +#include "dc.h" +#include "dc/inc/core_types.h" + +#include "vid.h" +#include "amdgpu.h" +#include "amdgpu_display.h" +#include "atom.h" +#include "amdgpu_dm.h" +#include "amdgpu_pm.h" + +#include "amd_shared.h" +#include "amdgpu_dm_irq.h" +#include "dm_helpers.h" +#include "dm_services_types.h" +#include "amdgpu_dm_mst_types.h" + +#include "ivsrcid/ivsrcid_vislands30.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "modules/inc/mod_freesync.h" + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "ivsrcid/irqsrcs_dcn_1_0.h" + +#include "raven1/DCN/dcn_1_0_offset.h" +#include "raven1/DCN/dcn_1_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "soc15_common.h" +#endif + +#include "modules/inc/mod_freesync.h" + +#include "i2caux_interface.h" + +/* basic init/fini API */ +static int amdgpu_dm_init(struct amdgpu_device *adev); +static void amdgpu_dm_fini(struct amdgpu_device *adev); + +/* initializes drm_device display related structures, based on the information + * provided by DAL. The drm strcutures are: drm_crtc, drm_connector, + * drm_encoder, drm_mode_config + * + * Returns 0 on success + */ +static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev); +/* removes and deallocates the drm structures, created by the above function */ +static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm); + +static void +amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector); + +static int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, + struct amdgpu_plane *aplane, + unsigned long possible_crtcs); +static int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm, + struct drm_plane *plane, + uint32_t link_index); +static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *amdgpu_dm_connector, + uint32_t link_index, + struct amdgpu_encoder *amdgpu_encoder); +static int amdgpu_dm_encoder_init(struct drm_device *dev, + struct amdgpu_encoder *aencoder, + uint32_t link_index); + +static int amdgpu_dm_connector_get_modes(struct drm_connector *connector); + +static int amdgpu_dm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool nonblock); + +static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state); + +static int amdgpu_dm_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state); + + + + +static const enum drm_plane_type dm_plane_type_default[AMDGPU_MAX_PLANES] = { + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, +}; + +static const enum drm_plane_type dm_plane_type_carizzo[AMDGPU_MAX_PLANES] = { + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_OVERLAY,/* YUV Capable Underlay */ +}; + +static const enum drm_plane_type dm_plane_type_stoney[AMDGPU_MAX_PLANES] = { + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_OVERLAY, /* YUV Capable Underlay */ +}; + +/* + * dm_vblank_get_counter + * + * @brief + * Get counter for number of vertical blanks + * + * @param + * struct amdgpu_device *adev - [in] desired amdgpu device + * int disp_idx - [in] which CRTC to get the counter from + * + * @return + * Counter for vertical blanks + */ +static u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc) +{ + if (crtc >= adev->mode_info.num_crtc) + return 0; + else { + struct amdgpu_crtc *acrtc = adev->mode_info.crtcs[crtc]; + struct dm_crtc_state *acrtc_state = to_dm_crtc_state( + acrtc->base.state); + + + if (acrtc_state->stream == NULL) { + DRM_ERROR("dc_stream_state is NULL for crtc '%d'!\n", + crtc); + return 0; + } + + return dc_stream_get_vblank_counter(acrtc_state->stream); + } +} + +static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc, + u32 *vbl, u32 *position) +{ + uint32_t v_blank_start, v_blank_end, h_position, v_position; + + if ((crtc < 0) || (crtc >= adev->mode_info.num_crtc)) + return -EINVAL; + else { + struct amdgpu_crtc *acrtc = adev->mode_info.crtcs[crtc]; + struct dm_crtc_state *acrtc_state = to_dm_crtc_state( + acrtc->base.state); + + if (acrtc_state->stream == NULL) { + DRM_ERROR("dc_stream_state is NULL for crtc '%d'!\n", + crtc); + return 0; + } + + /* + * TODO rework base driver to use values directly. + * for now parse it back into reg-format + */ + dc_stream_get_scanoutpos(acrtc_state->stream, + &v_blank_start, + &v_blank_end, + &h_position, + &v_position); + + *position = v_position | (h_position << 16); + *vbl = v_blank_start | (v_blank_end << 16); + } + + return 0; +} + +static bool dm_is_idle(void *handle) +{ + /* XXX todo */ + return true; +} + +static int dm_wait_for_idle(void *handle) +{ + /* XXX todo */ + return 0; +} + +static bool dm_check_soft_reset(void *handle) +{ + return false; +} + +static int dm_soft_reset(void *handle) +{ + /* XXX todo */ + return 0; +} + +static struct amdgpu_crtc * +get_crtc_by_otg_inst(struct amdgpu_device *adev, + int otg_inst) +{ + struct drm_device *dev = adev->ddev; + struct drm_crtc *crtc; + struct amdgpu_crtc *amdgpu_crtc; + + /* + * following if is check inherited from both functions where this one is + * used now. Need to be checked why it could happen. + */ + if (otg_inst == -1) { + WARN_ON(1); + return adev->mode_info.crtcs[0]; + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + amdgpu_crtc = to_amdgpu_crtc(crtc); + + if (amdgpu_crtc->otg_inst == otg_inst) + return amdgpu_crtc; + } + + return NULL; +} + +static void dm_pflip_high_irq(void *interrupt_params) +{ + struct amdgpu_crtc *amdgpu_crtc; + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + unsigned long flags; + + amdgpu_crtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP); + + /* IRQ could occur when in initial stage */ + /*TODO work and BO cleanup */ + if (amdgpu_crtc == NULL) { + DRM_DEBUG_DRIVER("CRTC is null, returning.\n"); + return; + } + + spin_lock_irqsave(&adev->ddev->event_lock, flags); + + if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED){ + DRM_DEBUG_DRIVER("amdgpu_crtc->pflip_status = %d !=AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p] \n", + amdgpu_crtc->pflip_status, + AMDGPU_FLIP_SUBMITTED, + amdgpu_crtc->crtc_id, + amdgpu_crtc); + spin_unlock_irqrestore(&adev->ddev->event_lock, flags); + return; + } + + + /* wakeup usersapce */ + if (amdgpu_crtc->event) { + /* Update to correct count/ts if racing with vblank irq */ + drm_crtc_accurate_vblank_count(&amdgpu_crtc->base); + + drm_crtc_send_vblank_event(&amdgpu_crtc->base, amdgpu_crtc->event); + + /* page flip completed. clean up */ + amdgpu_crtc->event = NULL; + + } else + WARN_ON(1); + + amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE; + spin_unlock_irqrestore(&adev->ddev->event_lock, flags); + + DRM_DEBUG_DRIVER("%s - crtc :%d[%p], pflip_stat:AMDGPU_FLIP_NONE\n", + __func__, amdgpu_crtc->crtc_id, amdgpu_crtc); + + drm_crtc_vblank_put(&amdgpu_crtc->base); +} + +static void dm_crtc_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + uint8_t crtc_index = 0; + struct amdgpu_crtc *acrtc; + + acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); + + if (acrtc) + crtc_index = acrtc->crtc_id; + + drm_handle_vblank(adev->ddev, crtc_index); +} + +static int dm_set_clockgating_state(void *handle, + enum amd_clockgating_state state) +{ + return 0; +} + +static int dm_set_powergating_state(void *handle, + enum amd_powergating_state state) +{ + return 0; +} + +/* Prototypes of private functions */ +static int dm_early_init(void* handle); + +static void hotplug_notify_work_func(struct work_struct *work) +{ + struct amdgpu_display_manager *dm = container_of(work, struct amdgpu_display_manager, mst_hotplug_work); + struct drm_device *dev = dm->ddev; + + drm_kms_helper_hotplug_event(dev); +} + +#if defined(CONFIG_DRM_AMD_DC_FBC) +#include "dal_asic_id.h" +/* Allocate memory for FBC compressed data */ +/* TODO: Dynamic allocation */ +#define AMDGPU_FBC_SIZE (3840 * 2160 * 4) + +static void amdgpu_dm_initialize_fbc(struct amdgpu_device *adev) +{ + int r; + struct dm_comressor_info *compressor = &adev->dm.compressor; + + if (!compressor->bo_ptr) { + r = amdgpu_bo_create_kernel(adev, AMDGPU_FBC_SIZE, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_VRAM, &compressor->bo_ptr, + &compressor->gpu_addr, &compressor->cpu_addr); + + if (r) + DRM_ERROR("DM: Failed to initialize fbc\n"); + } + +} +#endif + + +/* Init display KMS + * + * Returns 0 on success + */ +static int amdgpu_dm_init(struct amdgpu_device *adev) +{ + struct dc_init_data init_data; + adev->dm.ddev = adev->ddev; + adev->dm.adev = adev; + + /* Zero all the fields */ + memset(&init_data, 0, sizeof(init_data)); + + /* initialize DAL's lock (for SYNC context use) */ + spin_lock_init(&adev->dm.dal_lock); + + /* initialize DAL's mutex */ + mutex_init(&adev->dm.dal_mutex); + + if(amdgpu_dm_irq_init(adev)) { + DRM_ERROR("amdgpu: failed to initialize DM IRQ support.\n"); + goto error; + } + + init_data.asic_id.chip_family = adev->family; + + init_data.asic_id.pci_revision_id = adev->rev_id; + init_data.asic_id.hw_internal_rev = adev->external_rev_id; + + init_data.asic_id.vram_width = adev->mc.vram_width; + /* TODO: initialize init_data.asic_id.vram_type here!!!! */ + init_data.asic_id.atombios_base_address = + adev->mode_info.atom_context->bios; + + init_data.driver = adev; + + adev->dm.cgs_device = amdgpu_cgs_create_device(adev); + + if (!adev->dm.cgs_device) { + DRM_ERROR("amdgpu: failed to create cgs device.\n"); + goto error; + } + + init_data.cgs_device = adev->dm.cgs_device; + + adev->dm.dal = NULL; + + init_data.dce_environment = DCE_ENV_PRODUCTION_DRV; + + if (amdgpu_dc_log) + init_data.log_mask = DC_DEFAULT_LOG_MASK; + else + init_data.log_mask = DC_MIN_LOG_MASK; + +#if defined(CONFIG_DRM_AMD_DC_FBC) + if (adev->family == FAMILY_CZ) + amdgpu_dm_initialize_fbc(adev); + init_data.fbc_gpu_addr = adev->dm.compressor.gpu_addr; +#endif + /* Display Core create. */ + adev->dm.dc = dc_create(&init_data); + + if (adev->dm.dc) { + DRM_INFO("Display Core initialized!\n"); + } else { + DRM_INFO("Display Core failed to initialize!\n"); + goto error; + } + + INIT_WORK(&adev->dm.mst_hotplug_work, hotplug_notify_work_func); + + adev->dm.freesync_module = mod_freesync_create(adev->dm.dc); + if (!adev->dm.freesync_module) { + DRM_ERROR( + "amdgpu: failed to initialize freesync_module.\n"); + } else + DRM_DEBUG_DRIVER("amdgpu: freesync_module init done %p.\n", + adev->dm.freesync_module); + + if (amdgpu_dm_initialize_drm_device(adev)) { + DRM_ERROR( + "amdgpu: failed to initialize sw for display support.\n"); + goto error; + } + + /* Update the actual used number of crtc */ + adev->mode_info.num_crtc = adev->dm.display_indexes_num; + + /* TODO: Add_display_info? */ + + /* TODO use dynamic cursor width */ + adev->ddev->mode_config.cursor_width = adev->dm.dc->caps.max_cursor_size; + adev->ddev->mode_config.cursor_height = adev->dm.dc->caps.max_cursor_size; + + if (drm_vblank_init(adev->ddev, adev->dm.display_indexes_num)) { + DRM_ERROR( + "amdgpu: failed to initialize sw for display support.\n"); + goto error; + } + + DRM_DEBUG_DRIVER("KMS initialized.\n"); + + return 0; +error: + amdgpu_dm_fini(adev); + + return -1; +} + +static void amdgpu_dm_fini(struct amdgpu_device *adev) +{ + amdgpu_dm_destroy_drm_device(&adev->dm); + /* + * TODO: pageflip, vlank interrupt + * + * amdgpu_dm_irq_fini(adev); + */ + + if (adev->dm.cgs_device) { + amdgpu_cgs_destroy_device(adev->dm.cgs_device); + adev->dm.cgs_device = NULL; + } + if (adev->dm.freesync_module) { + mod_freesync_destroy(adev->dm.freesync_module); + adev->dm.freesync_module = NULL; + } + /* DC Destroy TODO: Replace destroy DAL */ + if (adev->dm.dc) + dc_destroy(&adev->dm.dc); + return; +} + +static int dm_sw_init(void *handle) +{ + return 0; +} + +static int dm_sw_fini(void *handle) +{ + return 0; +} + +static int detect_mst_link_for_all_connectors(struct drm_device *dev) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + int ret = 0; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->dc_link->type == dc_connection_mst_branch) { + DRM_DEBUG_DRIVER("DM_MST: starting TM on aconnector: %p [id: %d]\n", + aconnector, aconnector->base.base.id); + + ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true); + if (ret < 0) { + DRM_ERROR("DM_MST: Failed to start MST\n"); + ((struct dc_link *)aconnector->dc_link)->type = dc_connection_single; + return ret; + } + } + } + + drm_modeset_unlock(&dev->mode_config.connection_mutex); + return ret; +} + +static int dm_late_init(void *handle) +{ + struct drm_device *dev = ((struct amdgpu_device *)handle)->ddev; + + return detect_mst_link_for_all_connectors(dev); +} + +static void s3_handle_mst(struct drm_device *dev, bool suspend) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->dc_link->type == dc_connection_mst_branch && + !aconnector->mst_port) { + + if (suspend) + drm_dp_mst_topology_mgr_suspend(&aconnector->mst_mgr); + else + drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr); + } + } + + drm_modeset_unlock(&dev->mode_config.connection_mutex); +} + +static int dm_hw_init(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + /* Create DAL display manager */ + amdgpu_dm_init(adev); + amdgpu_dm_hpd_init(adev); + + return 0; +} + +static int dm_hw_fini(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + amdgpu_dm_hpd_fini(adev); + + amdgpu_dm_irq_fini(adev); + amdgpu_dm_fini(adev); + return 0; +} + +static int dm_suspend(void *handle) +{ + struct amdgpu_device *adev = handle; + struct amdgpu_display_manager *dm = &adev->dm; + int ret = 0; + + s3_handle_mst(adev->ddev, true); + + amdgpu_dm_irq_suspend(adev); + + WARN_ON(adev->dm.cached_state); + adev->dm.cached_state = drm_atomic_helper_suspend(adev->ddev); + + dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3); + + return ret; +} + +static struct amdgpu_dm_connector * +amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + uint32_t i; + struct drm_connector_state *new_con_state; + struct drm_connector *connector; + struct drm_crtc *crtc_from_state; + + for_each_new_connector_in_state(state, connector, new_con_state, i) { + crtc_from_state = new_con_state->crtc; + + if (crtc_from_state == crtc) + return to_amdgpu_dm_connector(connector); + } + + return NULL; +} + +static int dm_resume(void *handle) +{ + struct amdgpu_device *adev = handle; + struct amdgpu_display_manager *dm = &adev->dm; + + /* power on hardware */ + dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D0); + + return 0; +} + +int amdgpu_dm_display_resume(struct amdgpu_device *adev) +{ + struct drm_device *ddev = adev->ddev; + struct amdgpu_display_manager *dm = &adev->dm; + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + struct dm_crtc_state *dm_new_crtc_state; + struct drm_plane *plane; + struct drm_plane_state *new_plane_state; + struct dm_plane_state *dm_new_plane_state; + + int ret = 0; + int i; + + /* program HPD filter */ + dc_resume(dm->dc); + + /* On resume we need to rewrite the MSTM control bits to enamble MST*/ + s3_handle_mst(ddev, false); + + /* + * early enable HPD Rx IRQ, should be done before set mode as short + * pulse interrupts are used for MST + */ + amdgpu_dm_irq_resume_early(adev); + + /* Do detection*/ + list_for_each_entry(connector, + &ddev->mode_config.connector_list, head) { + aconnector = to_amdgpu_dm_connector(connector); + + /* + * this is the case when traversing through already created + * MST connectors, should be skipped + */ + if (aconnector->mst_port) + continue; + + mutex_lock(&aconnector->hpd_lock); + dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD); + aconnector->dc_sink = NULL; + amdgpu_dm_update_connector_after_detect(aconnector); + mutex_unlock(&aconnector->hpd_lock); + } + + /* Force mode set in atomic comit */ + for_each_new_crtc_in_state(adev->dm.cached_state, crtc, new_crtc_state, i) + new_crtc_state->active_changed = true; + + /* + * atomic_check is expected to create the dc states. We need to release + * them here, since they were duplicated as part of the suspend + * procedure. + */ + for_each_new_crtc_in_state(adev->dm.cached_state, crtc, new_crtc_state, i) { + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + if (dm_new_crtc_state->stream) { + WARN_ON(kref_read(&dm_new_crtc_state->stream->refcount) > 1); + dc_stream_release(dm_new_crtc_state->stream); + dm_new_crtc_state->stream = NULL; + } + } + + for_each_new_plane_in_state(adev->dm.cached_state, plane, new_plane_state, i) { + dm_new_plane_state = to_dm_plane_state(new_plane_state); + if (dm_new_plane_state->dc_state) { + WARN_ON(kref_read(&dm_new_plane_state->dc_state->refcount) > 1); + dc_plane_state_release(dm_new_plane_state->dc_state); + dm_new_plane_state->dc_state = NULL; + } + } + + ret = drm_atomic_helper_resume(ddev, adev->dm.cached_state); + + drm_atomic_state_put(adev->dm.cached_state); + adev->dm.cached_state = NULL; + + amdgpu_dm_irq_resume_late(adev); + + return ret; +} + +static const struct amd_ip_funcs amdgpu_dm_funcs = { + .name = "dm", + .early_init = dm_early_init, + .late_init = dm_late_init, + .sw_init = dm_sw_init, + .sw_fini = dm_sw_fini, + .hw_init = dm_hw_init, + .hw_fini = dm_hw_fini, + .suspend = dm_suspend, + .resume = dm_resume, + .is_idle = dm_is_idle, + .wait_for_idle = dm_wait_for_idle, + .check_soft_reset = dm_check_soft_reset, + .soft_reset = dm_soft_reset, + .set_clockgating_state = dm_set_clockgating_state, + .set_powergating_state = dm_set_powergating_state, +}; + +const struct amdgpu_ip_block_version dm_ip_block = +{ + .type = AMD_IP_BLOCK_TYPE_DCE, + .major = 1, + .minor = 0, + .rev = 0, + .funcs = &amdgpu_dm_funcs, +}; + + +static struct drm_atomic_state * +dm_atomic_state_alloc(struct drm_device *dev) +{ + struct dm_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL); + + if (!state) + return NULL; + + if (drm_atomic_state_init(dev, &state->base) < 0) + goto fail; + + return &state->base; + +fail: + kfree(state); + return NULL; +} + +static void +dm_atomic_state_clear(struct drm_atomic_state *state) +{ + struct dm_atomic_state *dm_state = to_dm_atomic_state(state); + + if (dm_state->context) { + dc_release_state(dm_state->context); + dm_state->context = NULL; + } + + drm_atomic_state_default_clear(state); +} + +static void +dm_atomic_state_alloc_free(struct drm_atomic_state *state) +{ + struct dm_atomic_state *dm_state = to_dm_atomic_state(state); + drm_atomic_state_default_release(state); + kfree(dm_state); +} + +static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = { + .fb_create = amdgpu_user_framebuffer_create, + .output_poll_changed = amdgpu_output_poll_changed, + .atomic_check = amdgpu_dm_atomic_check, + .atomic_commit = amdgpu_dm_atomic_commit, + .atomic_state_alloc = dm_atomic_state_alloc, + .atomic_state_clear = dm_atomic_state_clear, + .atomic_state_free = dm_atomic_state_alloc_free +}; + +static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = { + .atomic_commit_tail = amdgpu_dm_atomic_commit_tail +}; + +static void +amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) +{ + struct drm_connector *connector = &aconnector->base; + struct drm_device *dev = connector->dev; + struct dc_sink *sink; + + /* MST handled by drm_mst framework */ + if (aconnector->mst_mgr.mst_state == true) + return; + + + sink = aconnector->dc_link->local_sink; + + /* Edid mgmt connector gets first update only in mode_valid hook and then + * the connector sink is set to either fake or physical sink depends on link status. + * don't do it here if u are during boot + */ + if (aconnector->base.force != DRM_FORCE_UNSPECIFIED + && aconnector->dc_em_sink) { + + /* For S3 resume with headless use eml_sink to fake stream + * because on resume connecotr->sink is set ti NULL + */ + mutex_lock(&dev->mode_config.mutex); + + if (sink) { + if (aconnector->dc_sink) { + amdgpu_dm_remove_sink_from_freesync_module( + connector); + /* retain and release bellow are used for + * bump up refcount for sink because the link don't point + * to it anymore after disconnect so on next crtc to connector + * reshuffle by UMD we will get into unwanted dc_sink release + */ + if (aconnector->dc_sink != aconnector->dc_em_sink) + dc_sink_release(aconnector->dc_sink); + } + aconnector->dc_sink = sink; + amdgpu_dm_add_sink_to_freesync_module( + connector, aconnector->edid); + } else { + amdgpu_dm_remove_sink_from_freesync_module(connector); + if (!aconnector->dc_sink) + aconnector->dc_sink = aconnector->dc_em_sink; + else if (aconnector->dc_sink != aconnector->dc_em_sink) + dc_sink_retain(aconnector->dc_sink); + } + + mutex_unlock(&dev->mode_config.mutex); + return; + } + + /* + * TODO: temporary guard to look for proper fix + * if this sink is MST sink, we should not do anything + */ + if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + return; + + if (aconnector->dc_sink == sink) { + /* We got a DP short pulse (Link Loss, DP CTS, etc...). + * Do nothing!! */ + DRM_DEBUG_DRIVER("DCHPD: connector_id=%d: dc_sink didn't change.\n", + aconnector->connector_id); + return; + } + + DRM_DEBUG_DRIVER("DCHPD: connector_id=%d: Old sink=%p New sink=%p\n", + aconnector->connector_id, aconnector->dc_sink, sink); + + mutex_lock(&dev->mode_config.mutex); + + /* 1. Update status of the drm connector + * 2. Send an event and let userspace tell us what to do */ + if (sink) { + /* TODO: check if we still need the S3 mode update workaround. + * If yes, put it here. */ + if (aconnector->dc_sink) + amdgpu_dm_remove_sink_from_freesync_module( + connector); + + aconnector->dc_sink = sink; + if (sink->dc_edid.length == 0) { + aconnector->edid = NULL; + } else { + aconnector->edid = + (struct edid *) sink->dc_edid.raw_edid; + + + drm_mode_connector_update_edid_property(connector, + aconnector->edid); + } + amdgpu_dm_add_sink_to_freesync_module(connector, aconnector->edid); + + } else { + amdgpu_dm_remove_sink_from_freesync_module(connector); + drm_mode_connector_update_edid_property(connector, NULL); + aconnector->num_modes = 0; + aconnector->dc_sink = NULL; + } + + mutex_unlock(&dev->mode_config.mutex); +} + +static void handle_hpd_irq(void *param) +{ + struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param; + struct drm_connector *connector = &aconnector->base; + struct drm_device *dev = connector->dev; + + /* In case of failure or MST no need to update connector status or notify the OS + * since (for MST case) MST does this in it's own context. + */ + mutex_lock(&aconnector->hpd_lock); + + if (aconnector->fake_enable) + aconnector->fake_enable = false; + + if (dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD)) { + amdgpu_dm_update_connector_after_detect(aconnector); + + + drm_modeset_lock_all(dev); + dm_restore_drm_connector_state(dev, connector); + drm_modeset_unlock_all(dev); + + if (aconnector->base.force == DRM_FORCE_UNSPECIFIED) + drm_kms_helper_hotplug_event(dev); + } + mutex_unlock(&aconnector->hpd_lock); + +} + +static void dm_handle_hpd_rx_irq(struct amdgpu_dm_connector *aconnector) +{ + uint8_t esi[DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI] = { 0 }; + uint8_t dret; + bool new_irq_handled = false; + int dpcd_addr; + int dpcd_bytes_to_read; + + const int max_process_count = 30; + int process_count = 0; + + const struct dc_link_status *link_status = dc_link_get_status(aconnector->dc_link); + + if (link_status->dpcd_caps->dpcd_rev.raw < 0x12) { + dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT; + /* DPCD 0x200 - 0x201 for downstream IRQ */ + dpcd_addr = DP_SINK_COUNT; + } else { + dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI; + /* DPCD 0x2002 - 0x2005 for downstream IRQ */ + dpcd_addr = DP_SINK_COUNT_ESI; + } + + dret = drm_dp_dpcd_read( + &aconnector->dm_dp_aux.aux, + dpcd_addr, + esi, + dpcd_bytes_to_read); + + while (dret == dpcd_bytes_to_read && + process_count < max_process_count) { + uint8_t retry; + dret = 0; + + process_count++; + + DRM_DEBUG_DRIVER("ESI %02x %02x %02x\n", esi[0], esi[1], esi[2]); + /* handle HPD short pulse irq */ + if (aconnector->mst_mgr.mst_state) + drm_dp_mst_hpd_irq( + &aconnector->mst_mgr, + esi, + &new_irq_handled); + + if (new_irq_handled) { + /* ACK at DPCD to notify down stream */ + const int ack_dpcd_bytes_to_write = + dpcd_bytes_to_read - 1; + + for (retry = 0; retry < 3; retry++) { + uint8_t wret; + + wret = drm_dp_dpcd_write( + &aconnector->dm_dp_aux.aux, + dpcd_addr + 1, + &esi[1], + ack_dpcd_bytes_to_write); + if (wret == ack_dpcd_bytes_to_write) + break; + } + + /* check if there is new irq to be handle */ + dret = drm_dp_dpcd_read( + &aconnector->dm_dp_aux.aux, + dpcd_addr, + esi, + dpcd_bytes_to_read); + + new_irq_handled = false; + } else { + break; + } + } + + if (process_count == max_process_count) + DRM_DEBUG_DRIVER("Loop exceeded max iterations\n"); +} + +static void handle_hpd_rx_irq(void *param) +{ + struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param; + struct drm_connector *connector = &aconnector->base; + struct drm_device *dev = connector->dev; + struct dc_link *dc_link = aconnector->dc_link; + bool is_mst_root_connector = aconnector->mst_mgr.mst_state; + + /* TODO:Temporary add mutex to protect hpd interrupt not have a gpio + * conflict, after implement i2c helper, this mutex should be + * retired. + */ + if (dc_link->type != dc_connection_mst_branch) + mutex_lock(&aconnector->hpd_lock); + + if (dc_link_handle_hpd_rx_irq(dc_link, NULL) && + !is_mst_root_connector) { + /* Downstream Port status changed. */ + if (dc_link_detect(dc_link, DETECT_REASON_HPDRX)) { + amdgpu_dm_update_connector_after_detect(aconnector); + + + drm_modeset_lock_all(dev); + dm_restore_drm_connector_state(dev, connector); + drm_modeset_unlock_all(dev); + + drm_kms_helper_hotplug_event(dev); + } + } + if ((dc_link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) || + (dc_link->type == dc_connection_mst_branch)) + dm_handle_hpd_rx_irq(aconnector); + + if (dc_link->type != dc_connection_mst_branch) + mutex_unlock(&aconnector->hpd_lock); +} + +static void register_hpd_handlers(struct amdgpu_device *adev) +{ + struct drm_device *dev = adev->ddev; + struct drm_connector *connector; + struct amdgpu_dm_connector *aconnector; + const struct dc_link *dc_link; + struct dc_interrupt_params int_params = {0}; + + int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; + int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; + + list_for_each_entry(connector, + &dev->mode_config.connector_list, head) { + + aconnector = to_amdgpu_dm_connector(connector); + dc_link = aconnector->dc_link; + + if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd) { + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = dc_link->irq_source_hpd; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + handle_hpd_irq, + (void *) aconnector); + } + + if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd_rx) { + + /* Also register for DP short pulse (hpd_rx). */ + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = dc_link->irq_source_hpd_rx; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + handle_hpd_rx_irq, + (void *) aconnector); + } + } +} + +/* Register IRQ sources and initialize IRQ callbacks */ +static int dce110_register_irq_handlers(struct amdgpu_device *adev) +{ + struct dc *dc = adev->dm.dc; + struct common_irq_params *c_irq_params; + struct dc_interrupt_params int_params = {0}; + int r; + int i; + unsigned client_id = AMDGPU_IH_CLIENTID_LEGACY; + + if (adev->asic_type == CHIP_VEGA10 || + adev->asic_type == CHIP_RAVEN) + client_id = AMDGPU_IH_CLIENTID_DCE; + + int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; + int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; + + /* Actions of amdgpu_irq_add_id(): + * 1. Register a set() function with base driver. + * Base driver will call set() function to enable/disable an + * interrupt in DC hardware. + * 2. Register amdgpu_dm_irq_handler(). + * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts + * coming from DC hardware. + * amdgpu_dm_irq_handler() will re-direct the interrupt to DC + * for acknowledging and handling. */ + + /* Use VBLANK interrupt */ + for (i = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0; i <= VISLANDS30_IV_SRCID_D6_VERTICAL_INTERRUPT0; i++) { + r = amdgpu_irq_add_id(adev, client_id, i, &adev->crtc_irq); + if (r) { + DRM_ERROR("Failed to add crtc irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_crtc_high_irq, c_irq_params); + } + + /* Use GRPH_PFLIP interrupt */ + for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP; + i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) { + r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq); + if (r) { + DRM_ERROR("Failed to add page flip irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_pflip_high_irq, c_irq_params); + + } + + /* HPD */ + r = amdgpu_irq_add_id(adev, client_id, + VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq); + if (r) { + DRM_ERROR("Failed to add hpd irq id!\n"); + return r; + } + + register_hpd_handlers(adev); + + return 0; +} + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +/* Register IRQ sources and initialize IRQ callbacks */ +static int dcn10_register_irq_handlers(struct amdgpu_device *adev) +{ + struct dc *dc = adev->dm.dc; + struct common_irq_params *c_irq_params; + struct dc_interrupt_params int_params = {0}; + int r; + int i; + + int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; + int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; + + /* Actions of amdgpu_irq_add_id(): + * 1. Register a set() function with base driver. + * Base driver will call set() function to enable/disable an + * interrupt in DC hardware. + * 2. Register amdgpu_dm_irq_handler(). + * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts + * coming from DC hardware. + * amdgpu_dm_irq_handler() will re-direct the interrupt to DC + * for acknowledging and handling. + * */ + + /* Use VSTARTUP interrupt */ + for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP; + i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1; + i++) { + r = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_DCE, i, &adev->crtc_irq); + + if (r) { + DRM_ERROR("Failed to add crtc irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_crtc_high_irq, c_irq_params); + } + + /* Use GRPH_PFLIP interrupt */ + for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT; + i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + adev->mode_info.num_crtc - 1; + i++) { + r = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_DCE, i, &adev->pageflip_irq); + if (r) { + DRM_ERROR("Failed to add page flip irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_pflip_high_irq, c_irq_params); + + } + + /* HPD */ + r = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT, + &adev->hpd_irq); + if (r) { + DRM_ERROR("Failed to add hpd irq id!\n"); + return r; + } + + register_hpd_handlers(adev); + + return 0; +} +#endif + +static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev) +{ + int r; + + adev->mode_info.mode_config_initialized = true; + + adev->ddev->mode_config.funcs = (void *)&amdgpu_dm_mode_funcs; + adev->ddev->mode_config.helper_private = &amdgpu_dm_mode_config_helperfuncs; + + adev->ddev->mode_config.max_width = 16384; + adev->ddev->mode_config.max_height = 16384; + + adev->ddev->mode_config.preferred_depth = 24; + adev->ddev->mode_config.prefer_shadow = 1; + /* indicate support of immediate flip */ + adev->ddev->mode_config.async_page_flip = true; + + adev->ddev->mode_config.fb_base = adev->mc.aper_base; + + r = amdgpu_modeset_create_props(adev); + if (r) + return r; + + return 0; +} + +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\ + defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) + +static int amdgpu_dm_backlight_update_status(struct backlight_device *bd) +{ + struct amdgpu_display_manager *dm = bl_get_data(bd); + + if (dc_link_set_backlight_level(dm->backlight_link, + bd->props.brightness, 0, 0)) + return 0; + else + return 1; +} + +static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static const struct backlight_ops amdgpu_dm_backlight_ops = { + .get_brightness = amdgpu_dm_backlight_get_brightness, + .update_status = amdgpu_dm_backlight_update_status, +}; + +static void +amdgpu_dm_register_backlight_device(struct amdgpu_display_manager *dm) +{ + char bl_name[16]; + struct backlight_properties props = { 0 }; + + props.max_brightness = AMDGPU_MAX_BL_LEVEL; + props.type = BACKLIGHT_RAW; + + snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d", + dm->adev->ddev->primary->index); + + dm->backlight_dev = backlight_device_register(bl_name, + dm->adev->ddev->dev, + dm, + &amdgpu_dm_backlight_ops, + &props); + + if (IS_ERR(dm->backlight_dev)) + DRM_ERROR("DM: Backlight registration failed!\n"); + else + DRM_DEBUG_DRIVER("DM: Registered Backlight device: %s\n", bl_name); +} + +#endif + +/* In this architecture, the association + * connector -> encoder -> crtc + * id not really requried. The crtc and connector will hold the + * display_index as an abstraction to use with DAL component + * + * Returns 0 on success + */ +static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) +{ + struct amdgpu_display_manager *dm = &adev->dm; + uint32_t i; + struct amdgpu_dm_connector *aconnector = NULL; + struct amdgpu_encoder *aencoder = NULL; + struct amdgpu_mode_info *mode_info = &adev->mode_info; + uint32_t link_cnt; + unsigned long possible_crtcs; + + link_cnt = dm->dc->caps.max_links; + if (amdgpu_dm_mode_config_init(dm->adev)) { + DRM_ERROR("DM: Failed to initialize mode config\n"); + return -1; + } + + for (i = 0; i < dm->dc->caps.max_planes; i++) { + struct amdgpu_plane *plane; + + plane = kzalloc(sizeof(struct amdgpu_plane), GFP_KERNEL); + mode_info->planes[i] = plane; + + if (!plane) { + DRM_ERROR("KMS: Failed to allocate plane\n"); + goto fail; + } + plane->base.type = mode_info->plane_type[i]; + + /* + * HACK: IGT tests expect that each plane can only have one + * one possible CRTC. For now, set one CRTC for each + * plane that is not an underlay, but still allow multiple + * CRTCs for underlay planes. + */ + possible_crtcs = 1 << i; + if (i >= dm->dc->caps.max_streams) + possible_crtcs = 0xff; + + if (amdgpu_dm_plane_init(dm, mode_info->planes[i], possible_crtcs)) { + DRM_ERROR("KMS: Failed to initialize plane\n"); + goto fail; + } + } + + for (i = 0; i < dm->dc->caps.max_streams; i++) + if (amdgpu_dm_crtc_init(dm, &mode_info->planes[i]->base, i)) { + DRM_ERROR("KMS: Failed to initialize crtc\n"); + goto fail; + } + + dm->display_indexes_num = dm->dc->caps.max_streams; + + /* loops over all connectors on the board */ + for (i = 0; i < link_cnt; i++) { + + if (i > AMDGPU_DM_MAX_DISPLAY_INDEX) { + DRM_ERROR( + "KMS: Cannot support more than %d display indexes\n", + AMDGPU_DM_MAX_DISPLAY_INDEX); + continue; + } + + aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL); + if (!aconnector) + goto fail; + + aencoder = kzalloc(sizeof(*aencoder), GFP_KERNEL); + if (!aencoder) + goto fail; + + if (amdgpu_dm_encoder_init(dm->ddev, aencoder, i)) { + DRM_ERROR("KMS: Failed to initialize encoder\n"); + goto fail; + } + + if (amdgpu_dm_connector_init(dm, aconnector, i, aencoder)) { + DRM_ERROR("KMS: Failed to initialize connector\n"); + goto fail; + } + + if (dc_link_detect(dc_get_link_at_index(dm->dc, i), + DETECT_REASON_BOOT)) + amdgpu_dm_update_connector_after_detect(aconnector); + } + + /* Software is initialized. Now we can register interrupt handlers. */ + switch (adev->asic_type) { + case CHIP_BONAIRE: + case CHIP_HAWAII: + case CHIP_KAVERI: + case CHIP_KABINI: + case CHIP_MULLINS: + case CHIP_TONGA: + case CHIP_FIJI: + case CHIP_CARRIZO: + case CHIP_STONEY: + case CHIP_POLARIS11: + case CHIP_POLARIS10: + case CHIP_POLARIS12: + case CHIP_VEGA10: + if (dce110_register_irq_handlers(dm->adev)) { + DRM_ERROR("DM: Failed to initialize IRQ\n"); + goto fail; + } + break; +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case CHIP_RAVEN: + if (dcn10_register_irq_handlers(dm->adev)) { + DRM_ERROR("DM: Failed to initialize IRQ\n"); + goto fail; + } + /* + * Temporary disable until pplib/smu interaction is implemented + */ + dm->dc->debug.disable_stutter = true; + break; +#endif + default: + DRM_ERROR("Usupported ASIC type: 0x%X\n", adev->asic_type); + goto fail; + } + + return 0; +fail: + kfree(aencoder); + kfree(aconnector); + for (i = 0; i < dm->dc->caps.max_planes; i++) + kfree(mode_info->planes[i]); + return -1; +} + +static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm) +{ + drm_mode_config_cleanup(dm->ddev); + return; +} + +/****************************************************************************** + * amdgpu_display_funcs functions + *****************************************************************************/ + +/** + * dm_bandwidth_update - program display watermarks + * + * @adev: amdgpu_device pointer + * + * Calculate and program the display watermarks and line buffer allocation. + */ +static void dm_bandwidth_update(struct amdgpu_device *adev) +{ + /* TODO: implement later */ +} + +static void dm_set_backlight_level(struct amdgpu_encoder *amdgpu_encoder, + u8 level) +{ + /* TODO: translate amdgpu_encoder to display_index and call DAL */ +} + +static u8 dm_get_backlight_level(struct amdgpu_encoder *amdgpu_encoder) +{ + /* TODO: translate amdgpu_encoder to display_index and call DAL */ + return 0; +} + +static int amdgpu_notify_freesync(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct mod_freesync_params freesync_params; + uint8_t num_streams; + uint8_t i; + + struct amdgpu_device *adev = dev->dev_private; + int r = 0; + + /* Get freesync enable flag from DRM */ + + num_streams = dc_get_current_stream_count(adev->dm.dc); + + for (i = 0; i < num_streams; i++) { + struct dc_stream_state *stream; + stream = dc_get_stream_at_index(adev->dm.dc, i); + + mod_freesync_update_state(adev->dm.freesync_module, + &stream, 1, &freesync_params); + } + + return r; +} + +static const struct amdgpu_display_funcs dm_display_funcs = { + .bandwidth_update = dm_bandwidth_update, /* called unconditionally */ + .vblank_get_counter = dm_vblank_get_counter,/* called unconditionally */ + .vblank_wait = NULL, + .backlight_set_level = + dm_set_backlight_level,/* called unconditionally */ + .backlight_get_level = + dm_get_backlight_level,/* called unconditionally */ + .hpd_sense = NULL,/* called unconditionally */ + .hpd_set_polarity = NULL, /* called unconditionally */ + .hpd_get_gpio_reg = NULL, /* VBIOS parsing. DAL does it. */ + .page_flip_get_scanoutpos = + dm_crtc_get_scanoutpos,/* called unconditionally */ + .add_encoder = NULL, /* VBIOS parsing. DAL does it. */ + .add_connector = NULL, /* VBIOS parsing. DAL does it. */ + .notify_freesync = amdgpu_notify_freesync, + +}; + +#if defined(CONFIG_DEBUG_KERNEL_DC) + +static ssize_t s3_debug_store(struct device *device, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + int s3_state; + struct pci_dev *pdev = to_pci_dev(device); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct amdgpu_device *adev = drm_dev->dev_private; + + ret = kstrtoint(buf, 0, &s3_state); + + if (ret == 0) { + if (s3_state) { + dm_resume(adev); + amdgpu_dm_display_resume(adev); + drm_kms_helper_hotplug_event(adev->ddev); + } else + dm_suspend(adev); + } + + return ret == 0 ? count : 0; +} + +DEVICE_ATTR_WO(s3_debug); + +#endif + +static int dm_early_init(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + adev->ddev->driver->driver_features |= DRIVER_ATOMIC; + amdgpu_dm_set_irq_funcs(adev); + + switch (adev->asic_type) { + case CHIP_BONAIRE: + case CHIP_HAWAII: + adev->mode_info.num_crtc = 6; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 6; + adev->mode_info.plane_type = dm_plane_type_default; + break; + case CHIP_KAVERI: + adev->mode_info.num_crtc = 4; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 7; + adev->mode_info.plane_type = dm_plane_type_default; + break; + case CHIP_KABINI: + case CHIP_MULLINS: + adev->mode_info.num_crtc = 2; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 6; + adev->mode_info.plane_type = dm_plane_type_default; + break; + case CHIP_FIJI: + case CHIP_TONGA: + adev->mode_info.num_crtc = 6; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 7; + adev->mode_info.plane_type = dm_plane_type_default; + break; + case CHIP_CARRIZO: + adev->mode_info.num_crtc = 3; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 9; + adev->mode_info.plane_type = dm_plane_type_carizzo; + break; + case CHIP_STONEY: + adev->mode_info.num_crtc = 2; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 9; + adev->mode_info.plane_type = dm_plane_type_stoney; + break; + case CHIP_POLARIS11: + case CHIP_POLARIS12: + adev->mode_info.num_crtc = 5; + adev->mode_info.num_hpd = 5; + adev->mode_info.num_dig = 5; + adev->mode_info.plane_type = dm_plane_type_default; + break; + case CHIP_POLARIS10: + adev->mode_info.num_crtc = 6; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 6; + adev->mode_info.plane_type = dm_plane_type_default; + break; + case CHIP_VEGA10: + adev->mode_info.num_crtc = 6; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 6; + adev->mode_info.plane_type = dm_plane_type_default; + break; +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case CHIP_RAVEN: + adev->mode_info.num_crtc = 4; + adev->mode_info.num_hpd = 4; + adev->mode_info.num_dig = 4; + adev->mode_info.plane_type = dm_plane_type_default; + break; +#endif + default: + DRM_ERROR("Usupported ASIC type: 0x%X\n", adev->asic_type); + return -EINVAL; + } + + if (adev->mode_info.funcs == NULL) + adev->mode_info.funcs = &dm_display_funcs; + + /* Note: Do NOT change adev->audio_endpt_rreg and + * adev->audio_endpt_wreg because they are initialised in + * amdgpu_device_init() */ +#if defined(CONFIG_DEBUG_KERNEL_DC) + device_create_file( + adev->ddev->dev, + &dev_attr_s3_debug); +#endif + + return 0; +} + +struct dm_connector_state { + struct drm_connector_state base; + + enum amdgpu_rmx_type scaling; + uint8_t underscan_vborder; + uint8_t underscan_hborder; + bool underscan_enable; +}; + +#define to_dm_connector_state(x)\ + container_of((x), struct dm_connector_state, base) + +static bool modeset_required(struct drm_crtc_state *crtc_state, + struct dc_stream_state *new_stream, + struct dc_stream_state *old_stream) +{ + if (!drm_atomic_crtc_needs_modeset(crtc_state)) + return false; + + if (!crtc_state->enable) + return false; + + return crtc_state->active; +} + +static bool modereset_required(struct drm_crtc_state *crtc_state) +{ + if (!drm_atomic_crtc_needs_modeset(crtc_state)) + return false; + + return !crtc_state->enable || !crtc_state->active; +} + +static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = { + .destroy = amdgpu_dm_encoder_destroy, +}; + +static bool fill_rects_from_plane_state(const struct drm_plane_state *state, + struct dc_plane_state *plane_state) +{ + plane_state->src_rect.x = state->src_x >> 16; + plane_state->src_rect.y = state->src_y >> 16; + /*we ignore for now mantissa and do not to deal with floating pixels :(*/ + plane_state->src_rect.width = state->src_w >> 16; + + if (plane_state->src_rect.width == 0) + return false; + + plane_state->src_rect.height = state->src_h >> 16; + if (plane_state->src_rect.height == 0) + return false; + + plane_state->dst_rect.x = state->crtc_x; + plane_state->dst_rect.y = state->crtc_y; + + if (state->crtc_w == 0) + return false; + + plane_state->dst_rect.width = state->crtc_w; + + if (state->crtc_h == 0) + return false; + + plane_state->dst_rect.height = state->crtc_h; + + plane_state->clip_rect = plane_state->dst_rect; + + switch (state->rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: + plane_state->rotation = ROTATION_ANGLE_0; + break; + case DRM_MODE_ROTATE_90: + plane_state->rotation = ROTATION_ANGLE_90; + break; + case DRM_MODE_ROTATE_180: + plane_state->rotation = ROTATION_ANGLE_180; + break; + case DRM_MODE_ROTATE_270: + plane_state->rotation = ROTATION_ANGLE_270; + break; + default: + plane_state->rotation = ROTATION_ANGLE_0; + break; + } + + return true; +} +static int get_fb_info(const struct amdgpu_framebuffer *amdgpu_fb, + uint64_t *tiling_flags, + uint64_t *fb_location) +{ + struct amdgpu_bo *rbo = gem_to_amdgpu_bo(amdgpu_fb->obj); + int r = amdgpu_bo_reserve(rbo, false); + + if (unlikely(r)) { + // Don't show error msg. when return -ERESTARTSYS + if (r != -ERESTARTSYS) + DRM_ERROR("Unable to reserve buffer: %d\n", r); + return r; + } + + if (fb_location) + *fb_location = amdgpu_bo_gpu_offset(rbo); + + if (tiling_flags) + amdgpu_bo_get_tiling_flags(rbo, tiling_flags); + + amdgpu_bo_unreserve(rbo); + + return r; +} + +static int fill_plane_attributes_from_fb(struct amdgpu_device *adev, + struct dc_plane_state *plane_state, + const struct amdgpu_framebuffer *amdgpu_fb, + bool addReq) +{ + uint64_t tiling_flags; + uint64_t fb_location = 0; + uint64_t chroma_addr = 0; + unsigned int awidth; + const struct drm_framebuffer *fb = &amdgpu_fb->base; + int ret = 0; + struct drm_format_name_buf format_name; + + ret = get_fb_info( + amdgpu_fb, + &tiling_flags, + addReq == true ? &fb_location:NULL); + + if (ret) + return ret; + + switch (fb->format->format) { + case DRM_FORMAT_C8: + plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS; + break; + case DRM_FORMAT_RGB565: + plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_RGB565; + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888; + break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010; + break; + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ABGR2101010: + plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010; + break; + case DRM_FORMAT_NV21: + plane_state->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr; + break; + case DRM_FORMAT_NV12: + plane_state->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb; + break; + default: + DRM_ERROR("Unsupported screen format %s\n", + drm_get_format_name(fb->format->format, &format_name)); + return -EINVAL; + } + + if (plane_state->format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + plane_state->address.type = PLN_ADDR_TYPE_GRAPHICS; + plane_state->address.grph.addr.low_part = lower_32_bits(fb_location); + plane_state->address.grph.addr.high_part = upper_32_bits(fb_location); + plane_state->plane_size.grph.surface_size.x = 0; + plane_state->plane_size.grph.surface_size.y = 0; + plane_state->plane_size.grph.surface_size.width = fb->width; + plane_state->plane_size.grph.surface_size.height = fb->height; + plane_state->plane_size.grph.surface_pitch = + fb->pitches[0] / fb->format->cpp[0]; + /* TODO: unhardcode */ + plane_state->color_space = COLOR_SPACE_SRGB; + + } else { + awidth = ALIGN(fb->width, 64); + plane_state->address.type = PLN_ADDR_TYPE_VIDEO_PROGRESSIVE; + plane_state->address.video_progressive.luma_addr.low_part + = lower_32_bits(fb_location); + plane_state->address.video_progressive.luma_addr.high_part + = upper_32_bits(fb_location); + chroma_addr = fb_location + (u64)(awidth * fb->height); + plane_state->address.video_progressive.chroma_addr.low_part + = lower_32_bits(chroma_addr); + plane_state->address.video_progressive.chroma_addr.high_part + = upper_32_bits(chroma_addr); + plane_state->plane_size.video.luma_size.x = 0; + plane_state->plane_size.video.luma_size.y = 0; + plane_state->plane_size.video.luma_size.width = awidth; + plane_state->plane_size.video.luma_size.height = fb->height; + /* TODO: unhardcode */ + plane_state->plane_size.video.luma_pitch = awidth; + + plane_state->plane_size.video.chroma_size.x = 0; + plane_state->plane_size.video.chroma_size.y = 0; + plane_state->plane_size.video.chroma_size.width = awidth; + plane_state->plane_size.video.chroma_size.height = fb->height; + plane_state->plane_size.video.chroma_pitch = awidth / 2; + + /* TODO: unhardcode */ + plane_state->color_space = COLOR_SPACE_YCBCR709; + } + + memset(&plane_state->tiling_info, 0, sizeof(plane_state->tiling_info)); + + /* Fill GFX8 params */ + if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == DC_ARRAY_2D_TILED_THIN1) { + unsigned int bankw, bankh, mtaspect, tile_split, num_banks; + + bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH); + bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT); + mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT); + tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT); + num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS); + + /* XXX fix me for VI */ + plane_state->tiling_info.gfx8.num_banks = num_banks; + plane_state->tiling_info.gfx8.array_mode = + DC_ARRAY_2D_TILED_THIN1; + plane_state->tiling_info.gfx8.tile_split = tile_split; + plane_state->tiling_info.gfx8.bank_width = bankw; + plane_state->tiling_info.gfx8.bank_height = bankh; + plane_state->tiling_info.gfx8.tile_aspect = mtaspect; + plane_state->tiling_info.gfx8.tile_mode = + DC_ADDR_SURF_MICRO_TILING_DISPLAY; + } else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) + == DC_ARRAY_1D_TILED_THIN1) { + plane_state->tiling_info.gfx8.array_mode = DC_ARRAY_1D_TILED_THIN1; + } + + plane_state->tiling_info.gfx8.pipe_config = + AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); + + if (adev->asic_type == CHIP_VEGA10 || + adev->asic_type == CHIP_RAVEN) { + /* Fill GFX9 params */ + plane_state->tiling_info.gfx9.num_pipes = + adev->gfx.config.gb_addr_config_fields.num_pipes; + plane_state->tiling_info.gfx9.num_banks = + adev->gfx.config.gb_addr_config_fields.num_banks; + plane_state->tiling_info.gfx9.pipe_interleave = + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size; + plane_state->tiling_info.gfx9.num_shader_engines = + adev->gfx.config.gb_addr_config_fields.num_se; + plane_state->tiling_info.gfx9.max_compressed_frags = + adev->gfx.config.gb_addr_config_fields.max_compress_frags; + plane_state->tiling_info.gfx9.num_rb_per_se = + adev->gfx.config.gb_addr_config_fields.num_rb_per_se; + plane_state->tiling_info.gfx9.swizzle = + AMDGPU_TILING_GET(tiling_flags, SWIZZLE_MODE); + plane_state->tiling_info.gfx9.shaderEnable = 1; + } + + plane_state->visible = true; + plane_state->scaling_quality.h_taps_c = 0; + plane_state->scaling_quality.v_taps_c = 0; + + /* is this needed? is plane_state zeroed at allocation? */ + plane_state->scaling_quality.h_taps = 0; + plane_state->scaling_quality.v_taps = 0; + plane_state->stereo_format = PLANE_STEREO_FORMAT_NONE; + + return ret; + +} + +static void fill_gamma_from_crtc_state(const struct drm_crtc_state *crtc_state, + struct dc_plane_state *plane_state) +{ + int i; + struct dc_gamma *gamma; + struct drm_color_lut *lut = + (struct drm_color_lut *) crtc_state->gamma_lut->data; + + gamma = dc_create_gamma(); + + if (gamma == NULL) { + WARN_ON(1); + return; + } + + gamma->type = GAMMA_RGB_256; + gamma->num_entries = GAMMA_RGB_256_ENTRIES; + for (i = 0; i < GAMMA_RGB_256_ENTRIES; i++) { + gamma->entries.red[i] = dal_fixed31_32_from_int(lut[i].red); + gamma->entries.green[i] = dal_fixed31_32_from_int(lut[i].green); + gamma->entries.blue[i] = dal_fixed31_32_from_int(lut[i].blue); + } + + plane_state->gamma_correction = gamma; +} + +static int fill_plane_attributes(struct amdgpu_device *adev, + struct dc_plane_state *dc_plane_state, + struct drm_plane_state *plane_state, + struct drm_crtc_state *crtc_state, + bool addrReq) +{ + const struct amdgpu_framebuffer *amdgpu_fb = + to_amdgpu_framebuffer(plane_state->fb); + const struct drm_crtc *crtc = plane_state->crtc; + struct dc_transfer_func *input_tf; + int ret = 0; + + if (!fill_rects_from_plane_state(plane_state, dc_plane_state)) + return -EINVAL; + + ret = fill_plane_attributes_from_fb( + crtc->dev->dev_private, + dc_plane_state, + amdgpu_fb, + addrReq); + + if (ret) + return ret; + + input_tf = dc_create_transfer_func(); + + if (input_tf == NULL) + return -ENOMEM; + + input_tf->type = TF_TYPE_PREDEFINED; + input_tf->tf = TRANSFER_FUNCTION_SRGB; + + dc_plane_state->in_transfer_func = input_tf; + + /* In case of gamma set, update gamma value */ + if (crtc_state->gamma_lut) + fill_gamma_from_crtc_state(crtc_state, dc_plane_state); + + return ret; +} + +/*****************************************************************************/ + +static void update_stream_scaling_settings(const struct drm_display_mode *mode, + const struct dm_connector_state *dm_state, + struct dc_stream_state *stream) +{ + enum amdgpu_rmx_type rmx_type; + + struct rect src = { 0 }; /* viewport in composition space*/ + struct rect dst = { 0 }; /* stream addressable area */ + + /* no mode. nothing to be done */ + if (!mode) + return; + + /* Full screen scaling by default */ + src.width = mode->hdisplay; + src.height = mode->vdisplay; + dst.width = stream->timing.h_addressable; + dst.height = stream->timing.v_addressable; + + rmx_type = dm_state->scaling; + if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) { + if (src.width * dst.height < + src.height * dst.width) { + /* height needs less upscaling/more downscaling */ + dst.width = src.width * + dst.height / src.height; + } else { + /* width needs less upscaling/more downscaling */ + dst.height = src.height * + dst.width / src.width; + } + } else if (rmx_type == RMX_CENTER) { + dst = src; + } + + dst.x = (stream->timing.h_addressable - dst.width) / 2; + dst.y = (stream->timing.v_addressable - dst.height) / 2; + + if (dm_state->underscan_enable) { + dst.x += dm_state->underscan_hborder / 2; + dst.y += dm_state->underscan_vborder / 2; + dst.width -= dm_state->underscan_hborder; + dst.height -= dm_state->underscan_vborder; + } + + stream->src = src; + stream->dst = dst; + + DRM_DEBUG_DRIVER("Destination Rectangle x:%d y:%d width:%d height:%d\n", + dst.x, dst.y, dst.width, dst.height); + +} + +static enum dc_color_depth +convert_color_depth_from_display_info(const struct drm_connector *connector) +{ + uint32_t bpc = connector->display_info.bpc; + + /* Limited color depth to 8bit + * TODO: Still need to handle deep color + */ + if (bpc > 8) + bpc = 8; + + switch (bpc) { + case 0: + /* Temporary Work around, DRM don't parse color depth for + * EDID revision before 1.4 + * TODO: Fix edid parsing + */ + return COLOR_DEPTH_888; + case 6: + return COLOR_DEPTH_666; + case 8: + return COLOR_DEPTH_888; + case 10: + return COLOR_DEPTH_101010; + case 12: + return COLOR_DEPTH_121212; + case 14: + return COLOR_DEPTH_141414; + case 16: + return COLOR_DEPTH_161616; + default: + return COLOR_DEPTH_UNDEFINED; + } +} + +static enum dc_aspect_ratio +get_aspect_ratio(const struct drm_display_mode *mode_in) +{ + int32_t width = mode_in->crtc_hdisplay * 9; + int32_t height = mode_in->crtc_vdisplay * 16; + + if ((width - height) < 10 && (width - height) > -10) + return ASPECT_RATIO_16_9; + else + return ASPECT_RATIO_4_3; +} + +static enum dc_color_space +get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing) +{ + enum dc_color_space color_space = COLOR_SPACE_SRGB; + + switch (dc_crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + case PIXEL_ENCODING_YCBCR444: + case PIXEL_ENCODING_YCBCR420: + { + /* + * 27030khz is the separation point between HDTV and SDTV + * according to HDMI spec, we use YCbCr709 and YCbCr601 + * respectively + */ + if (dc_crtc_timing->pix_clk_khz > 27030) { + if (dc_crtc_timing->flags.Y_ONLY) + color_space = + COLOR_SPACE_YCBCR709_LIMITED; + else + color_space = COLOR_SPACE_YCBCR709; + } else { + if (dc_crtc_timing->flags.Y_ONLY) + color_space = + COLOR_SPACE_YCBCR601_LIMITED; + else + color_space = COLOR_SPACE_YCBCR601; + } + + } + break; + case PIXEL_ENCODING_RGB: + color_space = COLOR_SPACE_SRGB; + break; + + default: + WARN_ON(1); + break; + } + + return color_space; +} + +/*****************************************************************************/ + +static void +fill_stream_properties_from_drm_display_mode(struct dc_stream_state *stream, + const struct drm_display_mode *mode_in, + const struct drm_connector *connector) +{ + struct dc_crtc_timing *timing_out = &stream->timing; + + memset(timing_out, 0, sizeof(struct dc_crtc_timing)); + + timing_out->h_border_left = 0; + timing_out->h_border_right = 0; + timing_out->v_border_top = 0; + timing_out->v_border_bottom = 0; + /* TODO: un-hardcode */ + + if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444) + && stream->sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; + else + timing_out->pixel_encoding = PIXEL_ENCODING_RGB; + + timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE; + timing_out->display_color_depth = convert_color_depth_from_display_info( + connector); + timing_out->scan_type = SCANNING_TYPE_NODATA; + timing_out->hdmi_vic = 0; + timing_out->vic = drm_match_cea_mode(mode_in); + + timing_out->h_addressable = mode_in->crtc_hdisplay; + timing_out->h_total = mode_in->crtc_htotal; + timing_out->h_sync_width = + mode_in->crtc_hsync_end - mode_in->crtc_hsync_start; + timing_out->h_front_porch = + mode_in->crtc_hsync_start - mode_in->crtc_hdisplay; + timing_out->v_total = mode_in->crtc_vtotal; + timing_out->v_addressable = mode_in->crtc_vdisplay; + timing_out->v_front_porch = + mode_in->crtc_vsync_start - mode_in->crtc_vdisplay; + timing_out->v_sync_width = + mode_in->crtc_vsync_end - mode_in->crtc_vsync_start; + timing_out->pix_clk_khz = mode_in->crtc_clock; + timing_out->aspect_ratio = get_aspect_ratio(mode_in); + if (mode_in->flags & DRM_MODE_FLAG_PHSYNC) + timing_out->flags.HSYNC_POSITIVE_POLARITY = 1; + if (mode_in->flags & DRM_MODE_FLAG_PVSYNC) + timing_out->flags.VSYNC_POSITIVE_POLARITY = 1; + + stream->output_color_space = get_output_color_space(timing_out); + + { + struct dc_transfer_func *tf = dc_create_transfer_func(); + + tf->type = TF_TYPE_PREDEFINED; + tf->tf = TRANSFER_FUNCTION_SRGB; + stream->out_transfer_func = tf; + } +} + +static void fill_audio_info(struct audio_info *audio_info, + const struct drm_connector *drm_connector, + const struct dc_sink *dc_sink) +{ + int i = 0; + int cea_revision = 0; + const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps; + + audio_info->manufacture_id = edid_caps->manufacturer_id; + audio_info->product_id = edid_caps->product_id; + + cea_revision = drm_connector->display_info.cea_rev; + + strncpy(audio_info->display_name, + edid_caps->display_name, + AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS - 1); + + if (cea_revision >= 3) { + audio_info->mode_count = edid_caps->audio_mode_count; + + for (i = 0; i < audio_info->mode_count; ++i) { + audio_info->modes[i].format_code = + (enum audio_format_code) + (edid_caps->audio_modes[i].format_code); + audio_info->modes[i].channel_count = + edid_caps->audio_modes[i].channel_count; + audio_info->modes[i].sample_rates.all = + edid_caps->audio_modes[i].sample_rate; + audio_info->modes[i].sample_size = + edid_caps->audio_modes[i].sample_size; + } + } + + audio_info->flags.all = edid_caps->speaker_flags; + + /* TODO: We only check for the progressive mode, check for interlace mode too */ + if (drm_connector->latency_present[0]) { + audio_info->video_latency = drm_connector->video_latency[0]; + audio_info->audio_latency = drm_connector->audio_latency[0]; + } + + /* TODO: For DP, video and audio latency should be calculated from DPCD caps */ + +} + +static void +copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode, + struct drm_display_mode *dst_mode) +{ + dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay; + dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay; + dst_mode->crtc_clock = src_mode->crtc_clock; + dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start; + dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end; + dst_mode->crtc_hsync_start = src_mode->crtc_hsync_start; + dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end; + dst_mode->crtc_htotal = src_mode->crtc_htotal; + dst_mode->crtc_hskew = src_mode->crtc_hskew; + dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start; + dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end; + dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start; + dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end; + dst_mode->crtc_vtotal = src_mode->crtc_vtotal; +} + +static void +decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode, + const struct drm_display_mode *native_mode, + bool scale_enabled) +{ + if (scale_enabled) { + copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode); + } else if (native_mode->clock == drm_mode->clock && + native_mode->htotal == drm_mode->htotal && + native_mode->vtotal == drm_mode->vtotal) { + copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode); + } else { + /* no scaling nor amdgpu inserted, no need to patch */ + } +} + +static int create_fake_sink(struct amdgpu_dm_connector *aconnector) +{ + struct dc_sink *sink = NULL; + struct dc_sink_init_data sink_init_data = { 0 }; + + sink_init_data.link = aconnector->dc_link; + sink_init_data.sink_signal = aconnector->dc_link->connector_signal; + + sink = dc_sink_create(&sink_init_data); + if (!sink) { + DRM_ERROR("Failed to create sink!\n"); + return -ENOMEM; + } + + sink->sink_signal = SIGNAL_TYPE_VIRTUAL; + aconnector->fake_enable = true; + + aconnector->dc_sink = sink; + aconnector->dc_link->local_sink = sink; + + return 0; +} + +static struct dc_stream_state * +create_stream_for_sink(struct amdgpu_dm_connector *aconnector, + const struct drm_display_mode *drm_mode, + const struct dm_connector_state *dm_state) +{ + struct drm_display_mode *preferred_mode = NULL; + const struct drm_connector *drm_connector; + struct dc_stream_state *stream = NULL; + struct drm_display_mode mode = *drm_mode; + bool native_mode_found = false; + + if (aconnector == NULL) { + DRM_ERROR("aconnector is NULL!\n"); + goto drm_connector_null; + } + + if (dm_state == NULL) { + DRM_ERROR("dm_state is NULL!\n"); + goto dm_state_null; + } + + drm_connector = &aconnector->base; + + if (!aconnector->dc_sink) { + /* + * Exclude MST from creating fake_sink + * TODO: need to enable MST into fake_sink feature + */ + if (aconnector->mst_port) + goto stream_create_fail; + + if (create_fake_sink(aconnector)) + goto stream_create_fail; + } + + stream = dc_create_stream_for_sink(aconnector->dc_sink); + + if (stream == NULL) { + DRM_ERROR("Failed to create stream for sink!\n"); + goto stream_create_fail; + } + + list_for_each_entry(preferred_mode, &aconnector->base.modes, head) { + /* Search for preferred mode */ + if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) { + native_mode_found = true; + break; + } + } + if (!native_mode_found) + preferred_mode = list_first_entry_or_null( + &aconnector->base.modes, + struct drm_display_mode, + head); + + if (preferred_mode == NULL) { + /* This may not be an error, the use case is when we we have no + * usermode calls to reset and set mode upon hotplug. In this + * case, we call set mode ourselves to restore the previous mode + * and the modelist may not be filled in in time. + */ + DRM_DEBUG_DRIVER("No preferred mode found\n"); + } else { + decide_crtc_timing_for_drm_display_mode( + &mode, preferred_mode, + dm_state->scaling != RMX_OFF); + } + + fill_stream_properties_from_drm_display_mode(stream, + &mode, &aconnector->base); + update_stream_scaling_settings(&mode, dm_state, stream); + + fill_audio_info( + &stream->audio_info, + drm_connector, + aconnector->dc_sink); + +stream_create_fail: +dm_state_null: +drm_connector_null: + return stream; +} + +static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); + kfree(crtc); +} + +static void dm_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct dm_crtc_state *cur = to_dm_crtc_state(state); + + /* TODO Destroy dc_stream objects are stream object is flattened */ + if (cur->stream) + dc_stream_release(cur->stream); + + + __drm_atomic_helper_crtc_destroy_state(state); + + + kfree(state); +} + +static void dm_crtc_reset_state(struct drm_crtc *crtc) +{ + struct dm_crtc_state *state; + + if (crtc->state) + dm_crtc_destroy_state(crtc, crtc->state); + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (WARN_ON(!state)) + return; + + crtc->state = &state->base; + crtc->state->crtc = crtc; + +} + +static struct drm_crtc_state * +dm_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct dm_crtc_state *state, *cur; + + cur = to_dm_crtc_state(crtc->state); + + if (WARN_ON(!crtc->state)) + return NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + if (cur->stream) { + state->stream = cur->stream; + dc_stream_retain(state->stream); + } + + /* TODO Duplicate dc_stream after objects are stream object is flattened */ + + return &state->base; +} + +/* Implemented only the options currently availible for the driver */ +static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { + .reset = dm_crtc_reset_state, + .destroy = amdgpu_dm_crtc_destroy, + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = dm_crtc_duplicate_state, + .atomic_destroy_state = dm_crtc_destroy_state, +}; + +static enum drm_connector_status +amdgpu_dm_connector_detect(struct drm_connector *connector, bool force) +{ + bool connected; + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + /* Notes: + * 1. This interface is NOT called in context of HPD irq. + * 2. This interface *is called* in context of user-mode ioctl. Which + * makes it a bad place for *any* MST-related activit. */ + + if (aconnector->base.force == DRM_FORCE_UNSPECIFIED && + !aconnector->fake_enable) + connected = (aconnector->dc_sink != NULL); + else + connected = (aconnector->base.force == DRM_FORCE_ON); + + return (connected ? connector_status_connected : + connector_status_disconnected); +} + +int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *connector_state, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = dev->dev_private; + struct dm_connector_state *dm_old_state = + to_dm_connector_state(connector->state); + struct dm_connector_state *dm_new_state = + to_dm_connector_state(connector_state); + + int ret = -EINVAL; + + if (property == dev->mode_config.scaling_mode_property) { + enum amdgpu_rmx_type rmx_type; + + switch (val) { + case DRM_MODE_SCALE_CENTER: + rmx_type = RMX_CENTER; + break; + case DRM_MODE_SCALE_ASPECT: + rmx_type = RMX_ASPECT; + break; + case DRM_MODE_SCALE_FULLSCREEN: + rmx_type = RMX_FULL; + break; + case DRM_MODE_SCALE_NONE: + default: + rmx_type = RMX_OFF; + break; + } + + if (dm_old_state->scaling == rmx_type) + return 0; + + dm_new_state->scaling = rmx_type; + ret = 0; + } else if (property == adev->mode_info.underscan_hborder_property) { + dm_new_state->underscan_hborder = val; + ret = 0; + } else if (property == adev->mode_info.underscan_vborder_property) { + dm_new_state->underscan_vborder = val; + ret = 0; + } else if (property == adev->mode_info.underscan_property) { + dm_new_state->underscan_enable = val; + ret = 0; + } + + return ret; +} + +int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = dev->dev_private; + struct dm_connector_state *dm_state = + to_dm_connector_state(state); + int ret = -EINVAL; + + if (property == dev->mode_config.scaling_mode_property) { + switch (dm_state->scaling) { + case RMX_CENTER: + *val = DRM_MODE_SCALE_CENTER; + break; + case RMX_ASPECT: + *val = DRM_MODE_SCALE_ASPECT; + break; + case RMX_FULL: + *val = DRM_MODE_SCALE_FULLSCREEN; + break; + case RMX_OFF: + default: + *val = DRM_MODE_SCALE_NONE; + break; + } + ret = 0; + } else if (property == adev->mode_info.underscan_hborder_property) { + *val = dm_state->underscan_hborder; + ret = 0; + } else if (property == adev->mode_info.underscan_vborder_property) { + *val = dm_state->underscan_vborder; + ret = 0; + } else if (property == adev->mode_info.underscan_property) { + *val = dm_state->underscan_enable; + ret = 0; + } + return ret; +} + +static void amdgpu_dm_connector_destroy(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + const struct dc_link *link = aconnector->dc_link; + struct amdgpu_device *adev = connector->dev->dev_private; + struct amdgpu_display_manager *dm = &adev->dm; +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\ + defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) + + if (link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) { + amdgpu_dm_register_backlight_device(dm); + + if (dm->backlight_dev) { + backlight_device_unregister(dm->backlight_dev); + dm->backlight_dev = NULL; + } + + } +#endif + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector) +{ + struct dm_connector_state *state = + to_dm_connector_state(connector->state); + + kfree(state); + + state = kzalloc(sizeof(*state), GFP_KERNEL); + + if (state) { + state->scaling = RMX_OFF; + state->underscan_enable = false; + state->underscan_hborder = 0; + state->underscan_vborder = 0; + + connector->state = &state->base; + connector->state->connector = connector; + } +} + +struct drm_connector_state * +amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector) +{ + struct dm_connector_state *state = + to_dm_connector_state(connector->state); + + struct dm_connector_state *new_state = + kmemdup(state, sizeof(*state), GFP_KERNEL); + + if (new_state) { + __drm_atomic_helper_connector_duplicate_state(connector, + &new_state->base); + return &new_state->base; + } + + return NULL; +} + +static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { + .reset = amdgpu_dm_connector_funcs_reset, + .detect = amdgpu_dm_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = amdgpu_dm_connector_destroy, + .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = amdgpu_dm_connector_atomic_set_property, + .atomic_get_property = amdgpu_dm_connector_atomic_get_property +}; + +static struct drm_encoder *best_encoder(struct drm_connector *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + + DRM_DEBUG_DRIVER("Finding the best encoder\n"); + + /* pick the encoder ids */ + if (enc_id) { + obj = drm_mode_object_find(connector->dev, NULL, enc_id, DRM_MODE_OBJECT_ENCODER); + if (!obj) { + DRM_ERROR("Couldn't find a matching encoder for our connector\n"); + return NULL; + } + encoder = obj_to_encoder(obj); + return encoder; + } + DRM_ERROR("No encoder id\n"); + return NULL; +} + +static int get_modes(struct drm_connector *connector) +{ + return amdgpu_dm_connector_get_modes(connector); +} + +static void create_eml_sink(struct amdgpu_dm_connector *aconnector) +{ + struct dc_sink_init_data init_params = { + .link = aconnector->dc_link, + .sink_signal = SIGNAL_TYPE_VIRTUAL + }; + struct edid *edid = (struct edid *) aconnector->base.edid_blob_ptr->data; + + if (!aconnector->base.edid_blob_ptr || + !aconnector->base.edid_blob_ptr->data) { + DRM_ERROR("No EDID firmware found on connector: %s ,forcing to OFF!\n", + aconnector->base.name); + + aconnector->base.force = DRM_FORCE_OFF; + aconnector->base.override_edid = false; + return; + } + + aconnector->edid = edid; + + aconnector->dc_em_sink = dc_link_add_remote_sink( + aconnector->dc_link, + (uint8_t *)edid, + (edid->extensions + 1) * EDID_LENGTH, + &init_params); + + if (aconnector->base.force == DRM_FORCE_ON) + aconnector->dc_sink = aconnector->dc_link->local_sink ? + aconnector->dc_link->local_sink : + aconnector->dc_em_sink; +} + +static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) +{ + struct dc_link *link = (struct dc_link *)aconnector->dc_link; + + /* In case of headless boot with force on for DP managed connector + * Those settings have to be != 0 to get initial modeset + */ + if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) { + link->verified_link_cap.lane_count = LANE_COUNT_FOUR; + link->verified_link_cap.link_rate = LINK_RATE_HIGH2; + } + + + aconnector->base.override_edid = true; + create_eml_sink(aconnector); +} + +int amdgpu_dm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int result = MODE_ERROR; + struct dc_sink *dc_sink; + struct amdgpu_device *adev = connector->dev->dev_private; + /* TODO: Unhardcode stream count */ + struct dc_stream_state *stream; + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) || + (mode->flags & DRM_MODE_FLAG_DBLSCAN)) + return result; + + /* Only run this the first time mode_valid is called to initilialize + * EDID mgmt + */ + if (aconnector->base.force != DRM_FORCE_UNSPECIFIED && + !aconnector->dc_em_sink) + handle_edid_mgmt(aconnector); + + dc_sink = to_amdgpu_dm_connector(connector)->dc_sink; + + if (dc_sink == NULL) { + DRM_ERROR("dc_sink is NULL!\n"); + goto fail; + } + + stream = dc_create_stream_for_sink(dc_sink); + if (stream == NULL) { + DRM_ERROR("Failed to create stream for sink!\n"); + goto fail; + } + + drm_mode_set_crtcinfo(mode, 0); + fill_stream_properties_from_drm_display_mode(stream, mode, connector); + + stream->src.width = mode->hdisplay; + stream->src.height = mode->vdisplay; + stream->dst = stream->src; + + if (dc_validate_stream(adev->dm.dc, stream) == DC_OK) + result = MODE_OK; + + dc_stream_release(stream); + +fail: + /* TODO: error handling*/ + return result; +} + +static const struct drm_connector_helper_funcs +amdgpu_dm_connector_helper_funcs = { + /* + * If hotplug a second bigger display in FB Con mode, bigger resolution + * modes will be filtered by drm_mode_validate_size(), and those modes + * is missing after user start lightdm. So we need to renew modes list. + * in get_modes call back, not just return the modes count + */ + .get_modes = get_modes, + .mode_valid = amdgpu_dm_connector_mode_valid, + .best_encoder = best_encoder +}; + +static void dm_crtc_helper_disable(struct drm_crtc *crtc) +{ +} + +static int dm_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct amdgpu_device *adev = crtc->dev->dev_private; + struct dc *dc = adev->dm.dc; + struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(state); + int ret = -EINVAL; + + if (unlikely(!dm_crtc_state->stream && + modeset_required(state, NULL, dm_crtc_state->stream))) { + WARN_ON(1); + return ret; + } + + /* In some use cases, like reset, no stream is attached */ + if (!dm_crtc_state->stream) + return 0; + + if (dc_validate_stream(dc, dm_crtc_state->stream) == DC_OK) + return 0; + + return ret; +} + +static bool dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static const struct drm_crtc_helper_funcs amdgpu_dm_crtc_helper_funcs = { + .disable = dm_crtc_helper_disable, + .atomic_check = dm_crtc_helper_atomic_check, + .mode_fixup = dm_crtc_helper_mode_fixup +}; + +static void dm_encoder_helper_disable(struct drm_encoder *encoder) +{ + +} + +static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return 0; +} + +const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = { + .disable = dm_encoder_helper_disable, + .atomic_check = dm_encoder_helper_atomic_check +}; + +static void dm_drm_plane_reset(struct drm_plane *plane) +{ + struct dm_plane_state *amdgpu_state = NULL; + + if (plane->state) + plane->funcs->atomic_destroy_state(plane, plane->state); + + amdgpu_state = kzalloc(sizeof(*amdgpu_state), GFP_KERNEL); + WARN_ON(amdgpu_state == NULL); + + if (amdgpu_state) { + plane->state = &amdgpu_state->base; + plane->state->plane = plane; + plane->state->rotation = DRM_MODE_ROTATE_0; + } +} + +static struct drm_plane_state * +dm_drm_plane_duplicate_state(struct drm_plane *plane) +{ + struct dm_plane_state *dm_plane_state, *old_dm_plane_state; + + old_dm_plane_state = to_dm_plane_state(plane->state); + dm_plane_state = kzalloc(sizeof(*dm_plane_state), GFP_KERNEL); + if (!dm_plane_state) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, &dm_plane_state->base); + + if (old_dm_plane_state->dc_state) { + dm_plane_state->dc_state = old_dm_plane_state->dc_state; + dc_plane_state_retain(dm_plane_state->dc_state); + } + + return &dm_plane_state->base; +} + +void dm_drm_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct dm_plane_state *dm_plane_state = to_dm_plane_state(state); + + if (dm_plane_state->dc_state) + dc_plane_state_release(dm_plane_state->dc_state); + + drm_atomic_helper_plane_destroy_state(plane, state); +} + +static const struct drm_plane_funcs dm_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = dm_drm_plane_reset, + .atomic_duplicate_state = dm_drm_plane_duplicate_state, + .atomic_destroy_state = dm_drm_plane_destroy_state, +}; + +static int dm_plane_helper_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct amdgpu_framebuffer *afb; + struct drm_gem_object *obj; + struct amdgpu_bo *rbo; + uint64_t chroma_addr = 0; + int r; + struct dm_plane_state *dm_plane_state_new, *dm_plane_state_old; + unsigned int awidth; + + dm_plane_state_old = to_dm_plane_state(plane->state); + dm_plane_state_new = to_dm_plane_state(new_state); + + if (!new_state->fb) { + DRM_DEBUG_DRIVER("No FB bound\n"); + return 0; + } + + afb = to_amdgpu_framebuffer(new_state->fb); + + obj = afb->obj; + rbo = gem_to_amdgpu_bo(obj); + r = amdgpu_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + + r = amdgpu_bo_pin(rbo, AMDGPU_GEM_DOMAIN_VRAM, &afb->address); + + + amdgpu_bo_unreserve(rbo); + + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) + DRM_ERROR("Failed to pin framebuffer with error %d\n", r); + return r; + } + + amdgpu_bo_ref(rbo); + + if (dm_plane_state_new->dc_state && + dm_plane_state_old->dc_state != dm_plane_state_new->dc_state) { + struct dc_plane_state *plane_state = dm_plane_state_new->dc_state; + + if (plane_state->format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + plane_state->address.grph.addr.low_part = lower_32_bits(afb->address); + plane_state->address.grph.addr.high_part = upper_32_bits(afb->address); + } else { + awidth = ALIGN(new_state->fb->width, 64); + plane_state->address.type = PLN_ADDR_TYPE_VIDEO_PROGRESSIVE; + plane_state->address.video_progressive.luma_addr.low_part + = lower_32_bits(afb->address); + plane_state->address.video_progressive.luma_addr.high_part + = upper_32_bits(afb->address); + chroma_addr = afb->address + (u64)(awidth * new_state->fb->height); + plane_state->address.video_progressive.chroma_addr.low_part + = lower_32_bits(chroma_addr); + plane_state->address.video_progressive.chroma_addr.high_part + = upper_32_bits(chroma_addr); + } + } + + /* It's a hack for s3 since in 4.9 kernel filter out cursor buffer + * prepare and cleanup in drm_atomic_helper_prepare_planes + * and drm_atomic_helper_cleanup_planes because fb doens't in s3. + * IN 4.10 kernel this code should be removed and amdgpu_device_suspend + * code touching fram buffers should be avoided for DC. + */ + if (plane->type == DRM_PLANE_TYPE_CURSOR) { + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_state->crtc); + + acrtc->cursor_bo = obj; + } + return 0; +} + +static void dm_plane_helper_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct amdgpu_bo *rbo; + struct amdgpu_framebuffer *afb; + int r; + + if (!old_state->fb) + return; + + afb = to_amdgpu_framebuffer(old_state->fb); + rbo = gem_to_amdgpu_bo(afb->obj); + r = amdgpu_bo_reserve(rbo, false); + if (unlikely(r)) { + DRM_ERROR("failed to reserve rbo before unpin\n"); + return; + } + + amdgpu_bo_unpin(rbo); + amdgpu_bo_unreserve(rbo); + amdgpu_bo_unref(&rbo); +} + +static int dm_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct amdgpu_device *adev = plane->dev->dev_private; + struct dc *dc = adev->dm.dc; + struct dm_plane_state *dm_plane_state = to_dm_plane_state(state); + + if (!dm_plane_state->dc_state) + return 0; + + if (dc_validate_plane(dc, dm_plane_state->dc_state) == DC_OK) + return 0; + + return -EINVAL; +} + +static const struct drm_plane_helper_funcs dm_plane_helper_funcs = { + .prepare_fb = dm_plane_helper_prepare_fb, + .cleanup_fb = dm_plane_helper_cleanup_fb, + .atomic_check = dm_plane_atomic_check, +}; + +/* + * TODO: these are currently initialized to rgb formats only. + * For future use cases we should either initialize them dynamically based on + * plane capabilities, or initialize this array to all formats, so internal drm + * check will succeed, and let DC to implement proper check + */ +static const uint32_t rgb_formats[] = { + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, +}; + +static const uint32_t yuv_formats[] = { + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, +}; + +static const u32 cursor_formats[] = { + DRM_FORMAT_ARGB8888 +}; + +static int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, + struct amdgpu_plane *aplane, + unsigned long possible_crtcs) +{ + int res = -EPERM; + + switch (aplane->base.type) { + case DRM_PLANE_TYPE_PRIMARY: + aplane->base.format_default = true; + + res = drm_universal_plane_init( + dm->adev->ddev, + &aplane->base, + possible_crtcs, + &dm_plane_funcs, + rgb_formats, + ARRAY_SIZE(rgb_formats), + NULL, aplane->base.type, NULL); + break; + case DRM_PLANE_TYPE_OVERLAY: + res = drm_universal_plane_init( + dm->adev->ddev, + &aplane->base, + possible_crtcs, + &dm_plane_funcs, + yuv_formats, + ARRAY_SIZE(yuv_formats), + NULL, aplane->base.type, NULL); + break; + case DRM_PLANE_TYPE_CURSOR: + res = drm_universal_plane_init( + dm->adev->ddev, + &aplane->base, + possible_crtcs, + &dm_plane_funcs, + cursor_formats, + ARRAY_SIZE(cursor_formats), + NULL, aplane->base.type, NULL); + break; + } + + drm_plane_helper_add(&aplane->base, &dm_plane_helper_funcs); + + /* Create (reset) the plane state */ + if (aplane->base.funcs->reset) + aplane->base.funcs->reset(&aplane->base); + + + return res; +} + +static int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm, + struct drm_plane *plane, + uint32_t crtc_index) +{ + struct amdgpu_crtc *acrtc = NULL; + struct amdgpu_plane *cursor_plane; + + int res = -ENOMEM; + + cursor_plane = kzalloc(sizeof(*cursor_plane), GFP_KERNEL); + if (!cursor_plane) + goto fail; + + cursor_plane->base.type = DRM_PLANE_TYPE_CURSOR; + res = amdgpu_dm_plane_init(dm, cursor_plane, 0); + + acrtc = kzalloc(sizeof(struct amdgpu_crtc), GFP_KERNEL); + if (!acrtc) + goto fail; + + res = drm_crtc_init_with_planes( + dm->ddev, + &acrtc->base, + plane, + &cursor_plane->base, + &amdgpu_dm_crtc_funcs, NULL); + + if (res) + goto fail; + + drm_crtc_helper_add(&acrtc->base, &amdgpu_dm_crtc_helper_funcs); + + /* Create (reset) the plane state */ + if (acrtc->base.funcs->reset) + acrtc->base.funcs->reset(&acrtc->base); + + acrtc->max_cursor_width = dm->adev->dm.dc->caps.max_cursor_size; + acrtc->max_cursor_height = dm->adev->dm.dc->caps.max_cursor_size; + + acrtc->crtc_id = crtc_index; + acrtc->base.enabled = false; + + dm->adev->mode_info.crtcs[crtc_index] = acrtc; + drm_mode_crtc_set_gamma_size(&acrtc->base, 256); + + return 0; + +fail: + kfree(acrtc); + kfree(cursor_plane); + return res; +} + + +static int to_drm_connector_type(enum signal_type st) +{ + switch (st) { + case SIGNAL_TYPE_HDMI_TYPE_A: + return DRM_MODE_CONNECTOR_HDMIA; + case SIGNAL_TYPE_EDP: + return DRM_MODE_CONNECTOR_eDP; + case SIGNAL_TYPE_RGB: + return DRM_MODE_CONNECTOR_VGA; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + return DRM_MODE_CONNECTOR_DisplayPort; + case SIGNAL_TYPE_DVI_DUAL_LINK: + case SIGNAL_TYPE_DVI_SINGLE_LINK: + return DRM_MODE_CONNECTOR_DVID; + case SIGNAL_TYPE_VIRTUAL: + return DRM_MODE_CONNECTOR_VIRTUAL; + + default: + return DRM_MODE_CONNECTOR_Unknown; + } +} + +static void amdgpu_dm_get_native_mode(struct drm_connector *connector) +{ + const struct drm_connector_helper_funcs *helper = + connector->helper_private; + struct drm_encoder *encoder; + struct amdgpu_encoder *amdgpu_encoder; + + encoder = helper->best_encoder(connector); + + if (encoder == NULL) + return; + + amdgpu_encoder = to_amdgpu_encoder(encoder); + + amdgpu_encoder->native_mode.clock = 0; + + if (!list_empty(&connector->probed_modes)) { + struct drm_display_mode *preferred_mode = NULL; + + list_for_each_entry(preferred_mode, + &connector->probed_modes, + head) { + if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) + amdgpu_encoder->native_mode = *preferred_mode; + + break; + } + + } +} + +static struct drm_display_mode * +amdgpu_dm_create_common_mode(struct drm_encoder *encoder, + char *name, + int hdisplay, int vdisplay) +{ + struct drm_device *dev = encoder->dev; + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; + + mode = drm_mode_duplicate(dev, native_mode); + + if (mode == NULL) + return NULL; + + mode->hdisplay = hdisplay; + mode->vdisplay = vdisplay; + mode->type &= ~DRM_MODE_TYPE_PREFERRED; + strncpy(mode->name, name, DRM_DISPLAY_MODE_LEN); + + return mode; + +} + +static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + int i; + int n; + struct mode_size { + char name[DRM_DISPLAY_MODE_LEN]; + int w; + int h; + } common_modes[] = { + { "640x480", 640, 480}, + { "800x600", 800, 600}, + { "1024x768", 1024, 768}, + { "1280x720", 1280, 720}, + { "1280x800", 1280, 800}, + {"1280x1024", 1280, 1024}, + { "1440x900", 1440, 900}, + {"1680x1050", 1680, 1050}, + {"1600x1200", 1600, 1200}, + {"1920x1080", 1920, 1080}, + {"1920x1200", 1920, 1200} + }; + + n = ARRAY_SIZE(common_modes); + + for (i = 0; i < n; i++) { + struct drm_display_mode *curmode = NULL; + bool mode_existed = false; + + if (common_modes[i].w > native_mode->hdisplay || + common_modes[i].h > native_mode->vdisplay || + (common_modes[i].w == native_mode->hdisplay && + common_modes[i].h == native_mode->vdisplay)) + continue; + + list_for_each_entry(curmode, &connector->probed_modes, head) { + if (common_modes[i].w == curmode->hdisplay && + common_modes[i].h == curmode->vdisplay) { + mode_existed = true; + break; + } + } + + if (mode_existed) + continue; + + mode = amdgpu_dm_create_common_mode(encoder, + common_modes[i].name, common_modes[i].w, + common_modes[i].h); + drm_mode_probed_add(connector, mode); + amdgpu_dm_connector->num_modes++; + } +} + +static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, + struct edid *edid) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + + if (edid) { + /* empty probed_modes */ + INIT_LIST_HEAD(&connector->probed_modes); + amdgpu_dm_connector->num_modes = + drm_add_edid_modes(connector, edid); + + drm_edid_to_eld(connector, edid); + + amdgpu_dm_get_native_mode(connector); + } else { + amdgpu_dm_connector->num_modes = 0; + } +} + +static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) +{ + const struct drm_connector_helper_funcs *helper = + connector->helper_private; + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + struct drm_encoder *encoder; + struct edid *edid = amdgpu_dm_connector->edid; + + encoder = helper->best_encoder(connector); + + amdgpu_dm_connector_ddc_get_modes(connector, edid); + amdgpu_dm_connector_add_common_modes(encoder, connector); + return amdgpu_dm_connector->num_modes; +} + +void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector, + int connector_type, + struct dc_link *link, + int link_index) +{ + struct amdgpu_device *adev = dm->ddev->dev_private; + + aconnector->connector_id = link_index; + aconnector->dc_link = link; + aconnector->base.interlace_allowed = false; + aconnector->base.doublescan_allowed = false; + aconnector->base.stereo_allowed = false; + aconnector->base.dpms = DRM_MODE_DPMS_OFF; + aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */ + + mutex_init(&aconnector->hpd_lock); + + /* configure support HPD hot plug connector_>polled default value is 0 + * which means HPD hot plug not supported + */ + switch (connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + break; + case DRM_MODE_CONNECTOR_DisplayPort: + aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + break; + case DRM_MODE_CONNECTOR_DVID: + aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + break; + default: + break; + } + + drm_object_attach_property(&aconnector->base.base, + dm->ddev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_NONE); + + drm_object_attach_property(&aconnector->base.base, + adev->mode_info.underscan_property, + UNDERSCAN_OFF); + drm_object_attach_property(&aconnector->base.base, + adev->mode_info.underscan_hborder_property, + 0); + drm_object_attach_property(&aconnector->base.base, + adev->mode_info.underscan_vborder_property, + 0); + +} + +static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); + struct ddc_service *ddc_service = i2c->ddc_service; + struct i2c_command cmd; + int i; + int result = -EIO; + + cmd.payloads = kcalloc(num, sizeof(struct i2c_payload), GFP_KERNEL); + + if (!cmd.payloads) + return result; + + cmd.number_of_payloads = num; + cmd.engine = I2C_COMMAND_ENGINE_DEFAULT; + cmd.speed = 100; + + for (i = 0; i < num; i++) { + cmd.payloads[i].write = !(msgs[i].flags & I2C_M_RD); + cmd.payloads[i].address = msgs[i].addr; + cmd.payloads[i].length = msgs[i].len; + cmd.payloads[i].data = msgs[i].buf; + } + + if (dal_i2caux_submit_i2c_command( + ddc_service->ctx->i2caux, + ddc_service->ddc_pin, + &cmd)) + result = num; + + kfree(cmd.payloads); + return result; +} + +static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm amdgpu_dm_i2c_algo = { + .master_xfer = amdgpu_dm_i2c_xfer, + .functionality = amdgpu_dm_i2c_func, +}; + +static struct amdgpu_i2c_adapter * +create_i2c(struct ddc_service *ddc_service, + int link_index, + int *res) +{ + struct amdgpu_device *adev = ddc_service->ctx->driver_context; + struct amdgpu_i2c_adapter *i2c; + + i2c = kzalloc(sizeof(struct amdgpu_i2c_adapter), GFP_KERNEL); + if (!i2c) + return NULL; + i2c->base.owner = THIS_MODULE; + i2c->base.class = I2C_CLASS_DDC; + i2c->base.dev.parent = &adev->pdev->dev; + i2c->base.algo = &amdgpu_dm_i2c_algo; + snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d", link_index); + i2c_set_adapdata(&i2c->base, i2c); + i2c->ddc_service = ddc_service; + + return i2c; +} + +/* Note: this function assumes that dc_link_detect() was called for the + * dc_link which will be represented by this aconnector. + */ +static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector, + uint32_t link_index, + struct amdgpu_encoder *aencoder) +{ + int res = 0; + int connector_type; + struct dc *dc = dm->dc; + struct dc_link *link = dc_get_link_at_index(dc, link_index); + struct amdgpu_i2c_adapter *i2c; + + link->priv = aconnector; + + DRM_DEBUG_DRIVER("%s()\n", __func__); + + i2c = create_i2c(link->ddc, link->link_index, &res); + if (!i2c) { + DRM_ERROR("Failed to create i2c adapter data\n"); + return -ENOMEM; + } + + aconnector->i2c = i2c; + res = i2c_add_adapter(&i2c->base); + + if (res) { + DRM_ERROR("Failed to register hw i2c %d\n", link->link_index); + goto out_free; + } + + connector_type = to_drm_connector_type(link->connector_signal); + + res = drm_connector_init( + dm->ddev, + &aconnector->base, + &amdgpu_dm_connector_funcs, + connector_type); + + if (res) { + DRM_ERROR("connector_init failed\n"); + aconnector->connector_id = -1; + goto out_free; + } + + drm_connector_helper_add( + &aconnector->base, + &amdgpu_dm_connector_helper_funcs); + + if (aconnector->base.funcs->reset) + aconnector->base.funcs->reset(&aconnector->base); + + amdgpu_dm_connector_init_helper( + dm, + aconnector, + connector_type, + link, + link_index); + + drm_mode_connector_attach_encoder( + &aconnector->base, &aencoder->base); + + drm_connector_register(&aconnector->base); + + if (connector_type == DRM_MODE_CONNECTOR_DisplayPort + || connector_type == DRM_MODE_CONNECTOR_eDP) + amdgpu_dm_initialize_dp_connector(dm, aconnector); + +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\ + defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) + + /* NOTE: this currently will create backlight device even if a panel + * is not connected to the eDP/LVDS connector. + * + * This is less than ideal but we don't have sink information at this + * stage since detection happens after. We can't do detection earlier + * since MST detection needs connectors to be created first. + */ + if (link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) { + /* Event if registration failed, we should continue with + * DM initialization because not having a backlight control + * is better then a black screen. + */ + amdgpu_dm_register_backlight_device(dm); + + if (dm->backlight_dev) + dm->backlight_link = link; + } +#endif + +out_free: + if (res) { + kfree(i2c); + aconnector->i2c = NULL; + } + return res; +} + +int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev) +{ + switch (adev->mode_info.num_crtc) { + case 1: + return 0x1; + case 2: + return 0x3; + case 3: + return 0x7; + case 4: + return 0xf; + case 5: + return 0x1f; + case 6: + default: + return 0x3f; + } +} + +static int amdgpu_dm_encoder_init(struct drm_device *dev, + struct amdgpu_encoder *aencoder, + uint32_t link_index) +{ + struct amdgpu_device *adev = dev->dev_private; + + int res = drm_encoder_init(dev, + &aencoder->base, + &amdgpu_dm_encoder_funcs, + DRM_MODE_ENCODER_TMDS, + NULL); + + aencoder->base.possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev); + + if (!res) + aencoder->encoder_id = link_index; + else + aencoder->encoder_id = -1; + + drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs); + + return res; +} + +static void manage_dm_interrupts(struct amdgpu_device *adev, + struct amdgpu_crtc *acrtc, + bool enable) +{ + /* + * this is not correct translation but will work as soon as VBLANK + * constant is the same as PFLIP + */ + int irq_type = + amdgpu_crtc_idx_to_irq_type( + adev, + acrtc->crtc_id); + + if (enable) { + drm_crtc_vblank_on(&acrtc->base); + amdgpu_irq_get( + adev, + &adev->pageflip_irq, + irq_type); + } else { + + amdgpu_irq_put( + adev, + &adev->pageflip_irq, + irq_type); + drm_crtc_vblank_off(&acrtc->base); + } +} + +static bool +is_scaling_state_different(const struct dm_connector_state *dm_state, + const struct dm_connector_state *old_dm_state) +{ + if (dm_state->scaling != old_dm_state->scaling) + return true; + if (!dm_state->underscan_enable && old_dm_state->underscan_enable) { + if (old_dm_state->underscan_hborder != 0 && old_dm_state->underscan_vborder != 0) + return true; + } else if (dm_state->underscan_enable && !old_dm_state->underscan_enable) { + if (dm_state->underscan_hborder != 0 && dm_state->underscan_vborder != 0) + return true; + } else if (dm_state->underscan_hborder != old_dm_state->underscan_hborder || + dm_state->underscan_vborder != old_dm_state->underscan_vborder) + return true; + return false; +} + +static void remove_stream(struct amdgpu_device *adev, + struct amdgpu_crtc *acrtc, + struct dc_stream_state *stream) +{ + /* this is the update mode case */ + if (adev->dm.freesync_module) + mod_freesync_remove_stream(adev->dm.freesync_module, stream); + + acrtc->otg_inst = -1; + acrtc->enabled = false; +} + +static int get_cursor_position(struct drm_plane *plane, struct drm_crtc *crtc, + struct dc_cursor_position *position) +{ + struct amdgpu_crtc *amdgpu_crtc = amdgpu_crtc = to_amdgpu_crtc(crtc); + int x, y; + int xorigin = 0, yorigin = 0; + + if (!crtc || !plane->state->fb) { + position->enable = false; + position->x = 0; + position->y = 0; + return 0; + } + + if ((plane->state->crtc_w > amdgpu_crtc->max_cursor_width) || + (plane->state->crtc_h > amdgpu_crtc->max_cursor_height)) { + DRM_ERROR("%s: bad cursor width or height %d x %d\n", + __func__, + plane->state->crtc_w, + plane->state->crtc_h); + return -EINVAL; + } + + x = plane->state->crtc_x; + y = plane->state->crtc_y; + /* avivo cursor are offset into the total surface */ + x += crtc->primary->state->src_x >> 16; + y += crtc->primary->state->src_y >> 16; + if (x < 0) { + xorigin = min(-x, amdgpu_crtc->max_cursor_width - 1); + x = 0; + } + if (y < 0) { + yorigin = min(-y, amdgpu_crtc->max_cursor_height - 1); + y = 0; + } + position->enable = true; + position->x = x; + position->y = y; + position->x_hotspot = xorigin; + position->y_hotspot = yorigin; + + return 0; +} + +static void handle_cursor_update(struct drm_plane *plane, + struct drm_plane_state *old_plane_state) +{ + struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(plane->state->fb); + struct drm_crtc *crtc = afb ? plane->state->crtc : old_plane_state->crtc; + struct dm_crtc_state *crtc_state = crtc ? to_dm_crtc_state(crtc->state) : NULL; + struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + uint64_t address = afb ? afb->address : 0; + struct dc_cursor_position position; + struct dc_cursor_attributes attributes; + int ret; + + if (!plane->state->fb && !old_plane_state->fb) + return; + + DRM_DEBUG_DRIVER("%s: crtc_id=%d with size %d to %d\n", + __func__, + amdgpu_crtc->crtc_id, + plane->state->crtc_w, + plane->state->crtc_h); + + ret = get_cursor_position(plane, crtc, &position); + if (ret) + return; + + if (!position.enable) { + /* turn off cursor */ + if (crtc_state && crtc_state->stream) + dc_stream_set_cursor_position(crtc_state->stream, + &position); + return; + } + + amdgpu_crtc->cursor_width = plane->state->crtc_w; + amdgpu_crtc->cursor_height = plane->state->crtc_h; + + attributes.address.high_part = upper_32_bits(address); + attributes.address.low_part = lower_32_bits(address); + attributes.width = plane->state->crtc_w; + attributes.height = plane->state->crtc_h; + attributes.color_format = CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA; + attributes.rotation_angle = 0; + attributes.attribute_flags.value = 0; + + attributes.pitch = attributes.width; + + if (crtc_state->stream) { + if (!dc_stream_set_cursor_attributes(crtc_state->stream, + &attributes)) + DRM_ERROR("DC failed to set cursor attributes\n"); + + if (!dc_stream_set_cursor_position(crtc_state->stream, + &position)) + DRM_ERROR("DC failed to set cursor position\n"); + } +} + +static void prepare_flip_isr(struct amdgpu_crtc *acrtc) +{ + + assert_spin_locked(&acrtc->base.dev->event_lock); + WARN_ON(acrtc->event); + + acrtc->event = acrtc->base.state->event; + + /* Set the flip status */ + acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; + + /* Mark this event as consumed */ + acrtc->base.state->event = NULL; + + DRM_DEBUG_DRIVER("crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", + acrtc->crtc_id); +} + +/* + * Executes flip + * + * Waits on all BO's fences and for proper vblank count + */ +static void amdgpu_dm_do_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + uint32_t target, + struct dc_state *state) +{ + unsigned long flags; + uint32_t target_vblank; + int r, vpos, hpos; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(fb); + struct amdgpu_bo *abo = gem_to_amdgpu_bo(afb->obj); + struct amdgpu_device *adev = crtc->dev->dev_private; + bool async_flip = (crtc->state->pageflip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0; + struct dc_flip_addrs addr = { {0} }; + /* TODO eliminate or rename surface_update */ + struct dc_surface_update surface_updates[1] = { {0} }; + struct dm_crtc_state *acrtc_state = to_dm_crtc_state(crtc->state); + + + /* Prepare wait for target vblank early - before the fence-waits */ + target_vblank = target - drm_crtc_vblank_count(crtc) + + amdgpu_get_vblank_counter_kms(crtc->dev, acrtc->crtc_id); + + /* TODO This might fail and hence better not used, wait + * explicitly on fences instead + * and in general should be called for + * blocking commit to as per framework helpers + */ + r = amdgpu_bo_reserve(abo, true); + if (unlikely(r != 0)) { + DRM_ERROR("failed to reserve buffer before flip\n"); + WARN_ON(1); + } + + /* Wait for all fences on this FB */ + WARN_ON(reservation_object_wait_timeout_rcu(abo->tbo.resv, true, false, + MAX_SCHEDULE_TIMEOUT) < 0); + + amdgpu_bo_unreserve(abo); + + /* Wait until we're out of the vertical blank period before the one + * targeted by the flip + */ + while ((acrtc->enabled && + (amdgpu_get_crtc_scanoutpos(adev->ddev, acrtc->crtc_id, 0, + &vpos, &hpos, NULL, NULL, + &crtc->hwmode) + & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == + (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && + (int)(target_vblank - + amdgpu_get_vblank_counter_kms(adev->ddev, acrtc->crtc_id)) > 0)) { + usleep_range(1000, 1100); + } + + /* Flip */ + spin_lock_irqsave(&crtc->dev->event_lock, flags); + /* update crtc fb */ + crtc->primary->fb = fb; + + WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); + WARN_ON(!acrtc_state->stream); + + addr.address.grph.addr.low_part = lower_32_bits(afb->address); + addr.address.grph.addr.high_part = upper_32_bits(afb->address); + addr.flip_immediate = async_flip; + + + if (acrtc->base.state->event) + prepare_flip_isr(acrtc); + + surface_updates->surface = dc_stream_get_status(acrtc_state->stream)->plane_states[0]; + surface_updates->flip_addr = &addr; + + + dc_commit_updates_for_stream(adev->dm.dc, + surface_updates, + 1, + acrtc_state->stream, + NULL, + &surface_updates->surface, + state); + + DRM_DEBUG_DRIVER("%s Flipping to hi: 0x%x, low: 0x%x \n", + __func__, + addr.address.grph.addr.high_part, + addr.address.grph.addr.low_part); + + + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); +} + +static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, + struct drm_device *dev, + struct amdgpu_display_manager *dm, + struct drm_crtc *pcrtc, + bool *wait_for_vblank) +{ + uint32_t i; + struct drm_plane *plane; + struct drm_plane_state *old_plane_state, *new_plane_state; + struct dc_stream_state *dc_stream_attach; + struct dc_plane_state *plane_states_constructed[MAX_SURFACES]; + struct amdgpu_crtc *acrtc_attach = to_amdgpu_crtc(pcrtc); + struct drm_crtc_state *new_pcrtc_state = + drm_atomic_get_new_crtc_state(state, pcrtc); + struct dm_crtc_state *acrtc_state = to_dm_crtc_state(new_pcrtc_state); + struct dm_atomic_state *dm_state = to_dm_atomic_state(state); + int planes_count = 0; + unsigned long flags; + + /* update planes when needed */ + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state; + struct drm_framebuffer *fb = new_plane_state->fb; + bool pflip_needed; + struct dm_plane_state *dm_new_plane_state = to_dm_plane_state(new_plane_state); + + if (plane->type == DRM_PLANE_TYPE_CURSOR) { + handle_cursor_update(plane, old_plane_state); + continue; + } + + if (!fb || !crtc || pcrtc != crtc) + continue; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!new_crtc_state->active) + continue; + + pflip_needed = !state->allow_modeset; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (acrtc_attach->pflip_status != AMDGPU_FLIP_NONE) { + DRM_ERROR("%s: acrtc %d, already busy\n", + __func__, + acrtc_attach->crtc_id); + /* In commit tail framework this cannot happen */ + WARN_ON(1); + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + if (!pflip_needed) { + WARN_ON(!dm_new_plane_state->dc_state); + + plane_states_constructed[planes_count] = dm_new_plane_state->dc_state; + + dc_stream_attach = acrtc_state->stream; + planes_count++; + + } else if (new_crtc_state->planes_changed) { + /* Assume even ONE crtc with immediate flip means + * entire can't wait for VBLANK + * TODO Check if it's correct + */ + *wait_for_vblank = + new_pcrtc_state->pageflip_flags & DRM_MODE_PAGE_FLIP_ASYNC ? + false : true; + + /* TODO: Needs rework for multiplane flip */ + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + drm_crtc_vblank_get(crtc); + + amdgpu_dm_do_flip( + crtc, + fb, + drm_crtc_vblank_count(crtc) + *wait_for_vblank, + dm_state->context); + } + + } + + if (planes_count) { + unsigned long flags; + + if (new_pcrtc_state->event) { + + drm_crtc_vblank_get(pcrtc); + + spin_lock_irqsave(&pcrtc->dev->event_lock, flags); + prepare_flip_isr(acrtc_attach); + spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); + } + + if (false == dc_commit_planes_to_stream(dm->dc, + plane_states_constructed, + planes_count, + dc_stream_attach, + dm_state->context)) + dm_error("%s: Failed to attach plane!\n", __func__); + } else { + /*TODO BUG Here should go disable planes on CRTC. */ + } +} + + +static int amdgpu_dm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool nonblock) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct amdgpu_device *adev = dev->dev_private; + int i; + + /* + * We evade vblanks and pflips on crtc that + * should be changed. We do it here to flush & disable + * interrupts before drm_swap_state is called in drm_atomic_helper_commit + * it will update crtc->dm_crtc_state->stream pointer which is used in + * the ISRs. + */ + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + struct dm_crtc_state *dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + if (drm_atomic_crtc_needs_modeset(new_crtc_state) && dm_old_crtc_state->stream) + manage_dm_interrupts(adev, acrtc, false); + } + /* Add check here for SoC's that support hardware cursor plane, to + * unset legacy_cursor_update */ + + return drm_atomic_helper_commit(dev, state, nonblock); + + /*TODO Handle EINTR, reenable IRQ*/ +} + +static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct amdgpu_device *adev = dev->dev_private; + struct amdgpu_display_manager *dm = &adev->dm; + struct dm_atomic_state *dm_state; + uint32_t i, j; + uint32_t new_crtcs_count = 0; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct amdgpu_crtc *new_crtcs[MAX_STREAMS]; + struct dc_stream_state *new_stream = NULL; + unsigned long flags; + bool wait_for_vblank = true; + struct drm_connector *connector; + struct drm_connector_state *old_con_state, *new_con_state; + struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; + + drm_atomic_helper_update_legacy_modeset_state(dev, state); + + dm_state = to_dm_atomic_state(state); + + /* update changed items */ + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); + + DRM_DEBUG_DRIVER( + "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, " + "planes_changed:%d, mode_changed:%d,active_changed:%d," + "connectors_changed:%d\n", + acrtc->crtc_id, + new_crtc_state->enable, + new_crtc_state->active, + new_crtc_state->planes_changed, + new_crtc_state->mode_changed, + new_crtc_state->active_changed, + new_crtc_state->connectors_changed); + + /* handles headless hotplug case, updating new_state and + * aconnector as needed + */ + + if (modeset_required(new_crtc_state, dm_new_crtc_state->stream, dm_old_crtc_state->stream)) { + + DRM_DEBUG_DRIVER("Atomic commit: SET crtc id %d: [%p]\n", acrtc->crtc_id, acrtc); + + if (!dm_new_crtc_state->stream) { + /* + * this could happen because of issues with + * userspace notifications delivery. + * In this case userspace tries to set mode on + * display which is disconnect in fact. + * dc_sink in NULL in this case on aconnector. + * We expect reset mode will come soon. + * + * This can also happen when unplug is done + * during resume sequence ended + * + * In this case, we want to pretend we still + * have a sink to keep the pipe running so that + * hw state is consistent with the sw state + */ + DRM_DEBUG_DRIVER("%s: Failed to create new stream for crtc %d\n", + __func__, acrtc->base.base.id); + continue; + } + + + if (dm_old_crtc_state->stream) + remove_stream(adev, acrtc, dm_old_crtc_state->stream); + + + /* + * this loop saves set mode crtcs + * we needed to enable vblanks once all + * resources acquired in dc after dc_commit_streams + */ + + /*TODO move all this into dm_crtc_state, get rid of + * new_crtcs array and use old and new atomic states + * instead + */ + new_crtcs[new_crtcs_count] = acrtc; + new_crtcs_count++; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + acrtc->enabled = true; + acrtc->hw_mode = new_crtc_state->mode; + crtc->hwmode = new_crtc_state->mode; + } else if (modereset_required(new_crtc_state)) { + DRM_DEBUG_DRIVER("Atomic commit: RESET. crtc id %d:[%p]\n", acrtc->crtc_id, acrtc); + + /* i.e. reset mode */ + if (dm_old_crtc_state->stream) + remove_stream(adev, acrtc, dm_old_crtc_state->stream); + } + } /* for_each_crtc_in_state() */ + + /* + * Add streams after required streams from new and replaced streams + * are removed from freesync module + */ + if (adev->dm.freesync_module) { + for (i = 0; i < new_crtcs_count; i++) { + struct amdgpu_dm_connector *aconnector = NULL; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, + &new_crtcs[i]->base); + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + + new_stream = dm_new_crtc_state->stream; + aconnector = amdgpu_dm_find_first_crtc_matching_connector( + state, + &new_crtcs[i]->base); + if (!aconnector) { + DRM_DEBUG_DRIVER("Atomic commit: Failed to find connector for acrtc id:%d " + "skipping freesync init\n", + new_crtcs[i]->crtc_id); + continue; + } + + mod_freesync_add_stream(adev->dm.freesync_module, + new_stream, &aconnector->caps); + } + } + + if (dm_state->context) + WARN_ON(!dc_commit_state(dm->dc, dm_state->context)); + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + + if (dm_new_crtc_state->stream != NULL) { + const struct dc_stream_status *status = + dc_stream_get_status(dm_new_crtc_state->stream); + + if (!status) + DC_ERR("got no status for stream %p on acrtc%p\n", dm_new_crtc_state->stream, acrtc); + else + acrtc->otg_inst = status->primary_otg_inst; + } + } + + /* Handle scaling and underscan changes*/ + for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { + struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state); + struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state); + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc); + struct dc_stream_status *status = NULL; + + if (acrtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base); + + /* Skip any modesets/resets */ + if (!acrtc || drm_atomic_crtc_needs_modeset(new_crtc_state)) + continue; + + /* Skip any thing not scale or underscan changes */ + if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state)) + continue; + + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + + update_stream_scaling_settings(&dm_new_con_state->base.crtc->mode, + dm_new_con_state, (struct dc_stream_state *)dm_new_crtc_state->stream); + + status = dc_stream_get_status(dm_new_crtc_state->stream); + WARN_ON(!status); + WARN_ON(!status->plane_count); + + if (!dm_new_crtc_state->stream) + continue; + + /*TODO How it works with MPO ?*/ + if (!dc_commit_planes_to_stream( + dm->dc, + status->plane_states, + status->plane_count, + dm_new_crtc_state->stream, + dm_state->context)) + dm_error("%s: Failed to update stream scaling!\n", __func__); + } + + for (i = 0; i < new_crtcs_count; i++) { + /* + * loop to enable interrupts on newly arrived crtc + */ + struct amdgpu_crtc *acrtc = new_crtcs[i]; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base); + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + + if (adev->dm.freesync_module) + mod_freesync_notify_mode_change( + adev->dm.freesync_module, &dm_new_crtc_state->stream, 1); + + manage_dm_interrupts(adev, acrtc, true); + } + + /* update planes when needed per crtc*/ + for_each_new_crtc_in_state(state, crtc, new_crtc_state, j) { + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + + if (dm_new_crtc_state->stream) + amdgpu_dm_commit_planes(state, dev, dm, crtc, &wait_for_vblank); + } + + + /* + * send vblank event on all events not handled in flip and + * mark consumed event for drm_atomic_helper_commit_hw_done + */ + spin_lock_irqsave(&adev->ddev->event_lock, flags); + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + + if (new_crtc_state->event) + drm_send_event_locked(dev, &new_crtc_state->event->base); + + new_crtc_state->event = NULL; + } + spin_unlock_irqrestore(&adev->ddev->event_lock, flags); + + /* Signal HW programming completion */ + drm_atomic_helper_commit_hw_done(state); + + if (wait_for_vblank) + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); +} + + +static int dm_force_atomic_commit(struct drm_connector *connector) +{ + int ret = 0; + struct drm_device *ddev = connector->dev; + struct drm_atomic_state *state = drm_atomic_state_alloc(ddev); + struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc); + struct drm_plane *plane = disconnected_acrtc->base.primary; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_plane_state *plane_state; + + if (!state) + return -ENOMEM; + + state->acquire_ctx = ddev->mode_config.acquire_ctx; + + /* Construct an atomic state to restore previous display setting */ + + /* + * Attach connectors to drm_atomic_state + */ + conn_state = drm_atomic_get_connector_state(state, connector); + + ret = PTR_ERR_OR_ZERO(conn_state); + if (ret) + goto err; + + /* Attach crtc to drm_atomic_state*/ + crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base); + + ret = PTR_ERR_OR_ZERO(crtc_state); + if (ret) + goto err; + + /* force a restore */ + crtc_state->mode_changed = true; + + /* Attach plane to drm_atomic_state */ + plane_state = drm_atomic_get_plane_state(state, plane); + + ret = PTR_ERR_OR_ZERO(plane_state); + if (ret) + goto err; + + + /* Call commit internally with the state we just constructed */ + ret = drm_atomic_commit(state); + if (!ret) + return 0; + +err: + DRM_ERROR("Restoring old state failed with %i\n", ret); + drm_atomic_state_put(state); + + return ret; +} + +/* + * This functions handle all cases when set mode does not come upon hotplug. + * This include when the same display is unplugged then plugged back into the + * same port and when we are running without usermode desktop manager supprot + */ +void dm_restore_drm_connector_state(struct drm_device *dev, + struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + struct amdgpu_crtc *disconnected_acrtc; + struct dm_crtc_state *acrtc_state; + + if (!aconnector->dc_sink || !connector->state || !connector->encoder) + return; + + disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc); + acrtc_state = to_dm_crtc_state(disconnected_acrtc->base.state); + + if (!disconnected_acrtc || !acrtc_state->stream) + return; + + /* + * If the previous sink is not released and different from the current, + * we deduce we are in a state where we can not rely on usermode call + * to turn on the display, so we do it here + */ + if (acrtc_state->stream->sink != aconnector->dc_sink) + dm_force_atomic_commit(&aconnector->base); +} + +/*` + * Grabs all modesetting locks to serialize against any blocking commits, + * Waits for completion of all non blocking commits. + */ +static int do_aquire_global_lock(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_commit *commit; + long ret; + + /* Adding all modeset locks to aquire_ctx will + * ensure that when the framework release it the + * extra locks we are locking here will get released to + */ + ret = drm_modeset_lock_all_ctx(dev, state->acquire_ctx); + if (ret) + return ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + spin_lock(&crtc->commit_lock); + commit = list_first_entry_or_null(&crtc->commit_list, + struct drm_crtc_commit, commit_entry); + if (commit) + drm_crtc_commit_get(commit); + spin_unlock(&crtc->commit_lock); + + if (!commit) + continue; + + /* Make sure all pending HW programming completed and + * page flips done + */ + ret = wait_for_completion_interruptible_timeout(&commit->hw_done, 10*HZ); + + if (ret > 0) + ret = wait_for_completion_interruptible_timeout( + &commit->flip_done, 10*HZ); + + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] hw_done or flip_done " + "timed out\n", crtc->base.id, crtc->name); + + drm_crtc_commit_put(commit); + } + + return ret < 0 ? ret : 0; +} + +static int dm_update_crtcs_state(struct dc *dc, + struct drm_atomic_state *state, + bool enable, + bool *lock_and_validation_needed) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + int i; + struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; + struct dm_atomic_state *dm_state = to_dm_atomic_state(state); + struct dc_stream_state *new_stream; + int ret = 0; + + /*TODO Move this code into dm_crtc_atomic_check once we get rid of dc_validation_set */ + /* update changed items */ + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + struct amdgpu_crtc *acrtc = NULL; + struct amdgpu_dm_connector *aconnector = NULL; + struct drm_connector_state *new_con_state = NULL; + struct dm_connector_state *dm_conn_state = NULL; + + new_stream = NULL; + + dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + acrtc = to_amdgpu_crtc(crtc); + + aconnector = amdgpu_dm_find_first_crtc_matching_connector(state, crtc); + + /* TODO This hack should go away */ + if (aconnector && enable) { + // Make sure fake sink is created in plug-in scenario + new_con_state = drm_atomic_get_connector_state(state, + &aconnector->base); + + if (IS_ERR(new_con_state)) { + ret = PTR_ERR_OR_ZERO(new_con_state); + break; + } + + dm_conn_state = to_dm_connector_state(new_con_state); + + new_stream = create_stream_for_sink(aconnector, + &new_crtc_state->mode, + dm_conn_state); + + /* + * we can have no stream on ACTION_SET if a display + * was disconnected during S3, in this case it not and + * error, the OS will be updated after detection, and + * do the right thing on next atomic commit + */ + + if (!new_stream) { + DRM_DEBUG_DRIVER("%s: Failed to create new stream for crtc %d\n", + __func__, acrtc->base.base.id); + break; + } + } + + if (dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) && + dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream)) { + + new_crtc_state->mode_changed = false; + + DRM_DEBUG_DRIVER("Mode change not required, setting mode_changed to %d", + new_crtc_state->mode_changed); + } + + + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) + goto next_crtc; + + DRM_DEBUG_DRIVER( + "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, " + "planes_changed:%d, mode_changed:%d,active_changed:%d," + "connectors_changed:%d\n", + acrtc->crtc_id, + new_crtc_state->enable, + new_crtc_state->active, + new_crtc_state->planes_changed, + new_crtc_state->mode_changed, + new_crtc_state->active_changed, + new_crtc_state->connectors_changed); + + /* Remove stream for any changed/disabled CRTC */ + if (!enable) { + + if (!dm_old_crtc_state->stream) + goto next_crtc; + + DRM_DEBUG_DRIVER("Disabling DRM crtc: %d\n", + crtc->base.id); + + /* i.e. reset mode */ + if (dc_remove_stream_from_ctx( + dc, + dm_state->context, + dm_old_crtc_state->stream) != DC_OK) { + ret = -EINVAL; + goto fail; + } + + dc_stream_release(dm_old_crtc_state->stream); + dm_new_crtc_state->stream = NULL; + + *lock_and_validation_needed = true; + + } else {/* Add stream for any updated/enabled CRTC */ + /* + * Quick fix to prevent NULL pointer on new_stream when + * added MST connectors not found in existing crtc_state in the chained mode + * TODO: need to dig out the root cause of that + */ + if (!aconnector || (!aconnector->dc_sink && aconnector->mst_port)) + goto next_crtc; + + if (modereset_required(new_crtc_state)) + goto next_crtc; + + if (modeset_required(new_crtc_state, new_stream, + dm_old_crtc_state->stream)) { + + WARN_ON(dm_new_crtc_state->stream); + + dm_new_crtc_state->stream = new_stream; + dc_stream_retain(new_stream); + + DRM_DEBUG_DRIVER("Enabling DRM crtc: %d\n", + crtc->base.id); + + if (dc_add_stream_to_ctx( + dc, + dm_state->context, + dm_new_crtc_state->stream) != DC_OK) { + ret = -EINVAL; + goto fail; + } + + *lock_and_validation_needed = true; + } + } + +next_crtc: + /* Release extra reference */ + if (new_stream) + dc_stream_release(new_stream); + } + + return ret; + +fail: + if (new_stream) + dc_stream_release(new_stream); + return ret; +} + +static int dm_update_planes_state(struct dc *dc, + struct drm_atomic_state *state, + bool enable, + bool *lock_and_validation_needed) +{ + struct drm_crtc *new_plane_crtc, *old_plane_crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct drm_plane *plane; + struct drm_plane_state *old_plane_state, *new_plane_state; + struct dm_crtc_state *dm_new_crtc_state, *dm_old_crtc_state; + struct dm_atomic_state *dm_state = to_dm_atomic_state(state); + struct dm_plane_state *dm_new_plane_state, *dm_old_plane_state; + int i ; + /* TODO return page_flip_needed() function */ + bool pflip_needed = !state->allow_modeset; + int ret = 0; + + if (pflip_needed) + return ret; + + /* Add new planes */ + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { + new_plane_crtc = new_plane_state->crtc; + old_plane_crtc = old_plane_state->crtc; + dm_new_plane_state = to_dm_plane_state(new_plane_state); + dm_old_plane_state = to_dm_plane_state(old_plane_state); + + /*TODO Implement atomic check for cursor plane */ + if (plane->type == DRM_PLANE_TYPE_CURSOR) + continue; + + /* Remove any changed/removed planes */ + if (!enable) { + + if (!old_plane_crtc) + continue; + + old_crtc_state = drm_atomic_get_old_crtc_state( + state, old_plane_crtc); + dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); + + if (!dm_old_crtc_state->stream) + continue; + + DRM_DEBUG_DRIVER("Disabling DRM plane: %d on DRM crtc %d\n", + plane->base.id, old_plane_crtc->base.id); + + if (!dc_remove_plane_from_context( + dc, + dm_old_crtc_state->stream, + dm_old_plane_state->dc_state, + dm_state->context)) { + + ret = EINVAL; + return ret; + } + + + dc_plane_state_release(dm_old_plane_state->dc_state); + dm_new_plane_state->dc_state = NULL; + + *lock_and_validation_needed = true; + + } else { /* Add new planes */ + + if (drm_atomic_plane_disabling(plane->state, new_plane_state)) + continue; + + if (!new_plane_crtc) + continue; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_crtc); + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + + if (!dm_new_crtc_state->stream) + continue; + + + WARN_ON(dm_new_plane_state->dc_state); + + dm_new_plane_state->dc_state = dc_create_plane_state(dc); + + DRM_DEBUG_DRIVER("Enabling DRM plane: %d on DRM crtc %d\n", + plane->base.id, new_plane_crtc->base.id); + + if (!dm_new_plane_state->dc_state) { + ret = -EINVAL; + return ret; + } + + ret = fill_plane_attributes( + new_plane_crtc->dev->dev_private, + dm_new_plane_state->dc_state, + new_plane_state, + new_crtc_state, + false); + if (ret) + return ret; + + + if (!dc_add_plane_to_context( + dc, + dm_new_crtc_state->stream, + dm_new_plane_state->dc_state, + dm_state->context)) { + + ret = -EINVAL; + return ret; + } + + *lock_and_validation_needed = true; + } + } + + + return ret; +} + +static int amdgpu_dm_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int i; + int ret; + struct amdgpu_device *adev = dev->dev_private; + struct dc *dc = adev->dm.dc; + struct dm_atomic_state *dm_state = to_dm_atomic_state(state); + struct drm_connector *connector; + struct drm_connector_state *old_con_state, *new_con_state; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + + /* + * This bool will be set for true for any modeset/reset + * or plane update which implies non fast surface update. + */ + bool lock_and_validation_needed = false; + + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + goto fail; + + /* + * legacy_cursor_update should be made false for SoC's having + * a dedicated hardware plane for cursor in amdgpu_dm_atomic_commit(), + * otherwise for software cursor plane, + * we should not add it to list of affected planes. + */ + if (state->legacy_cursor_update) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + if (new_crtc_state->color_mgmt_changed) { + ret = drm_atomic_add_affected_planes(state, crtc); + if (ret) + goto fail; + } + } + } else { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) + continue; + + if (!new_crtc_state->enable) + continue; + + ret = drm_atomic_add_affected_connectors(state, crtc); + if (ret) + return ret; + + ret = drm_atomic_add_affected_planes(state, crtc); + if (ret) + goto fail; + } + } + + dm_state->context = dc_create_state(); + ASSERT(dm_state->context); + dc_resource_state_copy_construct_current(dc, dm_state->context); + + /* Remove exiting planes if they are modified */ + ret = dm_update_planes_state(dc, state, false, &lock_and_validation_needed); + if (ret) { + goto fail; + } + + /* Disable all crtcs which require disable */ + ret = dm_update_crtcs_state(dc, state, false, &lock_and_validation_needed); + if (ret) { + goto fail; + } + + /* Enable all crtcs which require enable */ + ret = dm_update_crtcs_state(dc, state, true, &lock_and_validation_needed); + if (ret) { + goto fail; + } + + /* Add new/modified planes */ + ret = dm_update_planes_state(dc, state, true, &lock_and_validation_needed); + if (ret) { + goto fail; + } + + /* Run this here since we want to validate the streams we created */ + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + goto fail; + + /* Check scaling and underscan changes*/ + /*TODO Removed scaling changes validation due to inability to commit + * new stream into context w\o causing full reset. Need to + * decide how to handle. + */ + for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { + struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state); + struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state); + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc); + + /* Skip any modesets/resets */ + if (!acrtc || drm_atomic_crtc_needs_modeset( + drm_atomic_get_new_crtc_state(state, &acrtc->base))) + continue; + + /* Skip any thing not scale or underscan changes */ + if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state)) + continue; + + lock_and_validation_needed = true; + } + + /* + * For full updates case when + * removing/adding/updating streams on once CRTC while flipping + * on another CRTC, + * acquiring global lock will guarantee that any such full + * update commit + * will wait for completion of any outstanding flip using DRMs + * synchronization events. + */ + + if (lock_and_validation_needed) { + + ret = do_aquire_global_lock(dev, state); + if (ret) + goto fail; + + if (dc_validate_global_state(dc, dm_state->context) != DC_OK) { + ret = -EINVAL; + goto fail; + } + } + + /* Must be success */ + WARN_ON(ret); + return ret; + +fail: + if (ret == -EDEADLK) + DRM_DEBUG_DRIVER("Atomic check stopped to avoid deadlock.\n"); + else if (ret == -EINTR || ret == -EAGAIN || ret == -ERESTARTSYS) + DRM_DEBUG_DRIVER("Atomic check stopped due to signal.\n"); + else + DRM_DEBUG_DRIVER("Atomic check failed with err: %d \n", ret); + + return ret; +} + +static bool is_dp_capable_without_timing_msa(struct dc *dc, + struct amdgpu_dm_connector *amdgpu_dm_connector) +{ + uint8_t dpcd_data; + bool capable = false; + + if (amdgpu_dm_connector->dc_link && + dm_helpers_dp_read_dpcd( + NULL, + amdgpu_dm_connector->dc_link, + DP_DOWN_STREAM_PORT_COUNT, + &dpcd_data, + sizeof(dpcd_data))) { + capable = (dpcd_data & DP_MSA_TIMING_PAR_IGNORED) ? true:false; + } + + return capable; +} +void amdgpu_dm_add_sink_to_freesync_module(struct drm_connector *connector, + struct edid *edid) +{ + int i; + uint64_t val_capable; + bool edid_check_required; + struct detailed_timing *timing; + struct detailed_non_pixel *data; + struct detailed_data_monitor_range *range; + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = dev->dev_private; + + edid_check_required = false; + if (!amdgpu_dm_connector->dc_sink) { + DRM_ERROR("dc_sink NULL, could not add free_sync module.\n"); + return; + } + if (!adev->dm.freesync_module) + return; + /* + * if edid non zero restrict freesync only for dp and edp + */ + if (edid) { + if (amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT + || amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_EDP) { + edid_check_required = is_dp_capable_without_timing_msa( + adev->dm.dc, + amdgpu_dm_connector); + } + } + val_capable = 0; + if (edid_check_required == true && (edid->version > 1 || + (edid->version == 1 && edid->revision > 1))) { + for (i = 0; i < 4; i++) { + + timing = &edid->detailed_timings[i]; + data = &timing->data.other_data; + range = &data->data.range; + /* + * Check if monitor has continuous frequency mode + */ + if (data->type != EDID_DETAIL_MONITOR_RANGE) + continue; + /* + * Check for flag range limits only. If flag == 1 then + * no additional timing information provided. + * Default GTF, GTF Secondary curve and CVT are not + * supported + */ + if (range->flags != 1) + continue; + + amdgpu_dm_connector->min_vfreq = range->min_vfreq; + amdgpu_dm_connector->max_vfreq = range->max_vfreq; + amdgpu_dm_connector->pixel_clock_mhz = + range->pixel_clock_mhz * 10; + break; + } + + if (amdgpu_dm_connector->max_vfreq - + amdgpu_dm_connector->min_vfreq > 10) { + amdgpu_dm_connector->caps.supported = true; + amdgpu_dm_connector->caps.min_refresh_in_micro_hz = + amdgpu_dm_connector->min_vfreq * 1000000; + amdgpu_dm_connector->caps.max_refresh_in_micro_hz = + amdgpu_dm_connector->max_vfreq * 1000000; + val_capable = 1; + } + } + + /* + * TODO figure out how to notify user-mode or DRM of freesync caps + * once we figure out how to deal with freesync in an upstreamable + * fashion + */ + +} + +void amdgpu_dm_remove_sink_from_freesync_module(struct drm_connector *connector) +{ + /* + * TODO fill in once we figure out how to deal with freesync in + * an upstreamable fashion + */ +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h new file mode 100644 index 000000000000..117521c6a6ed --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -0,0 +1,259 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __AMDGPU_DM_H__ +#define __AMDGPU_DM_H__ + +#include +#include +#include "dc.h" + +/* + * This file contains the definition for amdgpu_display_manager + * and its API for amdgpu driver's use. + * This component provides all the display related functionality + * and this is the only component that calls DAL API. + * The API contained here intended for amdgpu driver use. + * The API that is called directly from KMS framework is located + * in amdgpu_dm_kms.h file + */ + +#define AMDGPU_DM_MAX_DISPLAY_INDEX 31 +/* +#include "include/amdgpu_dal_power_if.h" +#include "amdgpu_dm_irq.h" +*/ + +#include "irq_types.h" +#include "signal_types.h" + +/* Forward declarations */ +struct amdgpu_device; +struct drm_device; +struct amdgpu_dm_irq_handler_data; + +struct amdgpu_dm_prev_state { + struct drm_framebuffer *fb; + int32_t x; + int32_t y; + struct drm_display_mode mode; +}; + +struct common_irq_params { + struct amdgpu_device *adev; + enum dc_irq_source irq_src; +}; + +struct irq_list_head { + struct list_head head; + /* In case this interrupt needs post-processing, 'work' will be queued*/ + struct work_struct work; +}; + +#if defined(CONFIG_DRM_AMD_DC_FBC) +struct dm_comressor_info { + void *cpu_addr; + struct amdgpu_bo *bo_ptr; + uint64_t gpu_addr; +}; +#endif + + +struct amdgpu_display_manager { + struct dal *dal; + struct dc *dc; + struct cgs_device *cgs_device; + /* lock to be used when DAL is called from SYNC IRQ context */ + spinlock_t dal_lock; + + struct amdgpu_device *adev; /*AMD base driver*/ + struct drm_device *ddev; /*DRM base driver*/ + u16 display_indexes_num; + + struct amdgpu_dm_prev_state prev_state; + + /* + * 'irq_source_handler_table' holds a list of handlers + * per (DAL) IRQ source. + * + * Each IRQ source may need to be handled at different contexts. + * By 'context' we mean, for example: + * - The ISR context, which is the direct interrupt handler. + * - The 'deferred' context - this is the post-processing of the + * interrupt, but at a lower priority. + * + * Note that handlers are called in the same order as they were + * registered (FIFO). + */ + struct irq_list_head irq_handler_list_low_tab[DAL_IRQ_SOURCES_NUMBER]; + struct list_head irq_handler_list_high_tab[DAL_IRQ_SOURCES_NUMBER]; + + struct common_irq_params + pflip_params[DC_IRQ_SOURCE_PFLIP_LAST - DC_IRQ_SOURCE_PFLIP_FIRST + 1]; + + struct common_irq_params + vblank_params[DC_IRQ_SOURCE_VBLANK6 - DC_IRQ_SOURCE_VBLANK1 + 1]; + + /* this spin lock synchronizes access to 'irq_handler_list_table' */ + spinlock_t irq_handler_list_table_lock; + + /* Timer-related data. */ + struct list_head timer_handler_list; + struct workqueue_struct *timer_workqueue; + + /* Use dal_mutex for any activity which is NOT syncronized by + * DRM mode setting locks. + * For example: amdgpu_dm_hpd_low_irq() calls into DAL *without* + * DRM mode setting locks being acquired. This is where dal_mutex + * is acquired before calling into DAL. */ + struct mutex dal_mutex; + + struct backlight_device *backlight_dev; + + const struct dc_link *backlight_link; + + struct work_struct mst_hotplug_work; + + struct mod_freesync *freesync_module; + + /** + * Caches device atomic state for suspend/resume + */ + struct drm_atomic_state *cached_state; +#if defined(CONFIG_DRM_AMD_DC_FBC) + struct dm_comressor_info compressor; +#endif +}; + +struct amdgpu_dm_connector { + + struct drm_connector base; + uint32_t connector_id; + + /* we need to mind the EDID between detect + and get modes due to analog/digital/tvencoder */ + struct edid *edid; + + /* shared with amdgpu */ + struct amdgpu_hpd hpd; + + /* number of modes generated from EDID at 'dc_sink' */ + int num_modes; + + /* The 'old' sink - before an HPD. + * The 'current' sink is in dc_link->sink. */ + struct dc_sink *dc_sink; + struct dc_link *dc_link; + struct dc_sink *dc_em_sink; + + /* DM only */ + struct drm_dp_mst_topology_mgr mst_mgr; + struct amdgpu_dm_dp_aux dm_dp_aux; + struct drm_dp_mst_port *port; + struct amdgpu_dm_connector *mst_port; + struct amdgpu_encoder *mst_encoder; + + /* TODO see if we can merge with ddc_bus or make a dm_connector */ + struct amdgpu_i2c_adapter *i2c; + + /* Monitor range limits */ + int min_vfreq ; + int max_vfreq ; + int pixel_clock_mhz; + + /*freesync caps*/ + struct mod_freesync_caps caps; + + struct mutex hpd_lock; + + bool fake_enable; +}; + +#define to_amdgpu_dm_connector(x) container_of(x, struct amdgpu_dm_connector, base) + +extern const struct amdgpu_ip_block_version dm_ip_block; + +struct amdgpu_framebuffer; +struct amdgpu_display_manager; +struct dc_validation_set; +struct dc_plane_state; + +struct dm_plane_state { + struct drm_plane_state base; + struct dc_plane_state *dc_state; +}; + +struct dm_crtc_state { + struct drm_crtc_state base; + struct dc_stream_state *stream; +}; + +#define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base) + +struct dm_atomic_state { + struct drm_atomic_state base; + + struct dc_state *context; +}; + +#define to_dm_atomic_state(x) container_of(x, struct dm_atomic_state, base) + + +void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector); +struct drm_connector_state * +amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector); +int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val); + +int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val); + +int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev); + +void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector, + int connector_type, + struct dc_link *link, + int link_index); + +int amdgpu_dm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); + +void dm_restore_drm_connector_state(struct drm_device *dev, + struct drm_connector *connector); + +void amdgpu_dm_add_sink_to_freesync_module(struct drm_connector *connector, + struct edid *edid); + +void +amdgpu_dm_remove_sink_from_freesync_module(struct drm_connector *connector); + +extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs; + +#endif /* __AMDGPU_DM_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c new file mode 100644 index 000000000000..9bd142f65f9b --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -0,0 +1,498 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dm_services.h" +#include "amdgpu.h" +#include "dc.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_irq.h" + +#include "dm_helpers.h" + +/* dm_helpers_parse_edid_caps + * + * Parse edid caps + * + * @edid: [in] pointer to edid + * edid_caps: [in] pointer to edid caps + * @return + * void + * */ +enum dc_edid_status dm_helpers_parse_edid_caps( + struct dc_context *ctx, + const struct dc_edid *edid, + struct dc_edid_caps *edid_caps) +{ + struct edid *edid_buf = (struct edid *) edid->raw_edid; + struct cea_sad *sads; + int sad_count = -1; + int sadb_count = -1; + int i = 0; + int j = 0; + uint8_t *sadb = NULL; + + enum dc_edid_status result = EDID_OK; + + if (!edid_caps || !edid) + return EDID_BAD_INPUT; + + if (!drm_edid_is_valid(edid_buf)) + result = EDID_BAD_CHECKSUM; + + edid_caps->manufacturer_id = (uint16_t) edid_buf->mfg_id[0] | + ((uint16_t) edid_buf->mfg_id[1])<<8; + edid_caps->product_id = (uint16_t) edid_buf->prod_code[0] | + ((uint16_t) edid_buf->prod_code[1])<<8; + edid_caps->serial_number = edid_buf->serial; + edid_caps->manufacture_week = edid_buf->mfg_week; + edid_caps->manufacture_year = edid_buf->mfg_year; + + /* One of the four detailed_timings stores the monitor name. It's + * stored in an array of length 13. */ + for (i = 0; i < 4; i++) { + if (edid_buf->detailed_timings[i].data.other_data.type == 0xfc) { + while (j < 13 && edid_buf->detailed_timings[i].data.other_data.data.str.str[j]) { + if (edid_buf->detailed_timings[i].data.other_data.data.str.str[j] == '\n') + break; + + edid_caps->display_name[j] = + edid_buf->detailed_timings[i].data.other_data.data.str.str[j]; + j++; + } + } + } + + edid_caps->edid_hdmi = drm_detect_hdmi_monitor( + (struct edid *) edid->raw_edid); + + sad_count = drm_edid_to_sad((struct edid *) edid->raw_edid, &sads); + if (sad_count <= 0) { + DRM_INFO("SADs count is: %d, don't need to read it\n", + sad_count); + return result; + } + + edid_caps->audio_mode_count = sad_count < DC_MAX_AUDIO_DESC_COUNT ? sad_count : DC_MAX_AUDIO_DESC_COUNT; + for (i = 0; i < edid_caps->audio_mode_count; ++i) { + struct cea_sad *sad = &sads[i]; + + edid_caps->audio_modes[i].format_code = sad->format; + edid_caps->audio_modes[i].channel_count = sad->channels; + edid_caps->audio_modes[i].sample_rate = sad->freq; + edid_caps->audio_modes[i].sample_size = sad->byte2; + } + + sadb_count = drm_edid_to_speaker_allocation((struct edid *) edid->raw_edid, &sadb); + + if (sadb_count < 0) { + DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sadb_count); + sadb_count = 0; + } + + if (sadb_count) + edid_caps->speaker_flags = sadb[0]; + else + edid_caps->speaker_flags = DEFAULT_SPEAKER_LOCATION; + + kfree(sads); + kfree(sadb); + + return result; +} + +static void get_payload_table( + struct amdgpu_dm_connector *aconnector, + struct dp_mst_stream_allocation_table *proposed_table) +{ + int i; + struct drm_dp_mst_topology_mgr *mst_mgr = + &aconnector->mst_port->mst_mgr; + + mutex_lock(&mst_mgr->payload_lock); + + proposed_table->stream_count = 0; + + /* number of active streams */ + for (i = 0; i < mst_mgr->max_payloads; i++) { + if (mst_mgr->payloads[i].num_slots == 0) + break; /* end of vcp_id table */ + + ASSERT(mst_mgr->payloads[i].payload_state != + DP_PAYLOAD_DELETE_LOCAL); + + if (mst_mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL || + mst_mgr->payloads[i].payload_state == + DP_PAYLOAD_REMOTE) { + + struct dp_mst_stream_allocation *sa = + &proposed_table->stream_allocations[ + proposed_table->stream_count]; + + sa->slot_count = mst_mgr->payloads[i].num_slots; + sa->vcp_id = mst_mgr->proposed_vcpis[i]->vcpi; + proposed_table->stream_count++; + } + } + + mutex_unlock(&mst_mgr->payload_lock); +} + +/* + * Writes payload allocation table in immediate downstream device. + */ +bool dm_helpers_dp_mst_write_payload_allocation_table( + struct dc_context *ctx, + const struct dc_stream_state *stream, + struct dp_mst_stream_allocation_table *proposed_table, + bool enable) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_dp_mst_topology_mgr *mst_mgr; + struct drm_dp_mst_port *mst_port; + int slots = 0; + bool ret; + int clock; + int bpp = 0; + int pbn = 0; + + aconnector = stream->sink->priv; + + if (!aconnector || !aconnector->mst_port) + return false; + + mst_mgr = &aconnector->mst_port->mst_mgr; + + if (!mst_mgr->mst_state) + return false; + + mst_port = aconnector->port; + + if (enable) { + clock = stream->timing.pix_clk_khz; + + switch (stream->timing.display_color_depth) { + + case COLOR_DEPTH_666: + bpp = 6; + break; + case COLOR_DEPTH_888: + bpp = 8; + break; + case COLOR_DEPTH_101010: + bpp = 10; + break; + case COLOR_DEPTH_121212: + bpp = 12; + break; + case COLOR_DEPTH_141414: + bpp = 14; + break; + case COLOR_DEPTH_161616: + bpp = 16; + break; + default: + ASSERT(bpp != 0); + break; + } + + bpp = bpp * 3; + + /* TODO need to know link rate */ + + pbn = drm_dp_calc_pbn_mode(clock, bpp); + + slots = drm_dp_find_vcpi_slots(mst_mgr, pbn); + ret = drm_dp_mst_allocate_vcpi(mst_mgr, mst_port, pbn, slots); + + if (!ret) + return false; + + } else { + drm_dp_mst_reset_vcpi_slots(mst_mgr, mst_port); + } + + ret = drm_dp_update_payload_part1(mst_mgr); + + /* mst_mgr->->payloads are VC payload notify MST branch using DPCD or + * AUX message. The sequence is slot 1-63 allocated sequence for each + * stream. AMD ASIC stream slot allocation should follow the same + * sequence. copy DRM MST allocation to dc */ + + get_payload_table(aconnector, proposed_table); + + if (ret) + return false; + + return true; +} + +/* + * Polls for ACT (allocation change trigger) handled and sends + * ALLOCATE_PAYLOAD message. + */ +bool dm_helpers_dp_mst_poll_for_allocation_change_trigger( + struct dc_context *ctx, + const struct dc_stream_state *stream) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_dp_mst_topology_mgr *mst_mgr; + int ret; + + aconnector = stream->sink->priv; + + if (!aconnector || !aconnector->mst_port) + return false; + + mst_mgr = &aconnector->mst_port->mst_mgr; + + if (!mst_mgr->mst_state) + return false; + + ret = drm_dp_check_act_status(mst_mgr); + + if (ret) + return false; + + return true; +} + +bool dm_helpers_dp_mst_send_payload_allocation( + struct dc_context *ctx, + const struct dc_stream_state *stream, + bool enable) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_dp_mst_topology_mgr *mst_mgr; + struct drm_dp_mst_port *mst_port; + int ret; + + aconnector = stream->sink->priv; + + if (!aconnector || !aconnector->mst_port) + return false; + + mst_port = aconnector->port; + + mst_mgr = &aconnector->mst_port->mst_mgr; + + if (!mst_mgr->mst_state) + return false; + + ret = drm_dp_update_payload_part2(mst_mgr); + + if (ret) + return false; + + if (!enable) + drm_dp_mst_deallocate_vcpi(mst_mgr, mst_port); + + return true; +} + +bool dm_helpers_dc_conn_log(struct dc_context *ctx, struct log_entry *entry, enum dc_log_type event) +{ + return true; +} + +void dm_dtn_log_begin(struct dc_context *ctx) +{} + +void dm_dtn_log_append_v(struct dc_context *ctx, + const char *pMsg, ...) +{} + +void dm_dtn_log_end(struct dc_context *ctx) +{} + +bool dm_helpers_dp_mst_start_top_mgr( + struct dc_context *ctx, + const struct dc_link *link, + bool boot) +{ + struct amdgpu_dm_connector *aconnector = link->priv; + + if (!aconnector) { + DRM_ERROR("Failed to found connector for link!"); + return false; + } + + if (boot) { + DRM_INFO("DM_MST: Differing MST start on aconnector: %p [id: %d]\n", + aconnector, aconnector->base.base.id); + return true; + } + + DRM_INFO("DM_MST: starting TM on aconnector: %p [id: %d]\n", + aconnector, aconnector->base.base.id); + + return (drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true) == 0); +} + +void dm_helpers_dp_mst_stop_top_mgr( + struct dc_context *ctx, + const struct dc_link *link) +{ + struct amdgpu_dm_connector *aconnector = link->priv; + + if (!aconnector) { + DRM_ERROR("Failed to found connector for link!"); + return; + } + + DRM_INFO("DM_MST: stopping TM on aconnector: %p [id: %d]\n", + aconnector, aconnector->base.base.id); + + if (aconnector->mst_mgr.mst_state == true) + drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, false); +} + +bool dm_helpers_dp_read_dpcd( + struct dc_context *ctx, + const struct dc_link *link, + uint32_t address, + uint8_t *data, + uint32_t size) +{ + + struct amdgpu_dm_connector *aconnector = link->priv; + + if (!aconnector) { + DRM_ERROR("Failed to found connector for link!"); + return false; + } + + return drm_dp_dpcd_read(&aconnector->dm_dp_aux.aux, address, + data, size) > 0; +} + +bool dm_helpers_dp_write_dpcd( + struct dc_context *ctx, + const struct dc_link *link, + uint32_t address, + const uint8_t *data, + uint32_t size) +{ + struct amdgpu_dm_connector *aconnector = link->priv; + + if (!aconnector) { + DRM_ERROR("Failed to found connector for link!"); + return false; + } + + return drm_dp_dpcd_write(&aconnector->dm_dp_aux.aux, + address, (uint8_t *)data, size) > 0; +} + +bool dm_helpers_submit_i2c( + struct dc_context *ctx, + const struct dc_link *link, + struct i2c_command *cmd) +{ + struct amdgpu_dm_connector *aconnector = link->priv; + struct i2c_msg *msgs; + int i = 0; + int num = cmd->number_of_payloads; + bool result; + + if (!aconnector) { + DRM_ERROR("Failed to found connector for link!"); + return false; + } + + msgs = kzalloc(num * sizeof(struct i2c_msg), GFP_KERNEL); + + if (!msgs) + return false; + + for (i = 0; i < num; i++) { + msgs[i].flags = cmd->payloads[i].write ? 0 : I2C_M_RD; + msgs[i].addr = cmd->payloads[i].address; + msgs[i].len = cmd->payloads[i].length; + msgs[i].buf = cmd->payloads[i].data; + } + + result = i2c_transfer(&aconnector->i2c->base, msgs, num) == num; + + kfree(msgs); + + return result; +} + +enum dc_edid_status dm_helpers_read_local_edid( + struct dc_context *ctx, + struct dc_link *link, + struct dc_sink *sink) +{ + struct amdgpu_dm_connector *aconnector = link->priv; + struct i2c_adapter *ddc; + int retry = 3; + enum dc_edid_status edid_status; + struct edid *edid; + + if (link->aux_mode) + ddc = &aconnector->dm_dp_aux.aux.ddc; + else + ddc = &aconnector->i2c->base; + + /* some dongles read edid incorrectly the first time, + * do check sum and retry to make sure read correct edid. + */ + do { + + edid = drm_get_edid(&aconnector->base, ddc); + + if (!edid) + return EDID_NO_RESPONSE; + + sink->dc_edid.length = EDID_LENGTH * (edid->extensions + 1); + memmove(sink->dc_edid.raw_edid, (uint8_t *)edid, sink->dc_edid.length); + + /* We don't need the original edid anymore */ + kfree(edid); + + edid_status = dm_helpers_parse_edid_caps( + ctx, + &sink->dc_edid, + &sink->edid_caps); + + } while (edid_status == EDID_BAD_CHECKSUM && --retry > 0); + + if (edid_status != EDID_OK) + DRM_ERROR("EDID err: %d, on connector: %s", + edid_status, + aconnector->base.name); + + return edid_status; +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c new file mode 100644 index 000000000000..ca5d0d1581dc --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -0,0 +1,755 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include + +#include "dm_services_types.h" +#include "dc.h" + +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_irq.h" + +/****************************************************************************** + * Private declarations. + *****************************************************************************/ + +struct handler_common_data { + struct list_head list; + interrupt_handler handler; + void *handler_arg; + + /* DM which this handler belongs to */ + struct amdgpu_display_manager *dm; +}; + +struct amdgpu_dm_irq_handler_data { + struct handler_common_data hcd; + /* DAL irq source which registered for this interrupt. */ + enum dc_irq_source irq_source; +}; + +struct amdgpu_dm_timer_handler_data { + struct handler_common_data hcd; + struct delayed_work d_work; +}; + +#define DM_IRQ_TABLE_LOCK(adev, flags) \ + spin_lock_irqsave(&adev->dm.irq_handler_list_table_lock, flags) + +#define DM_IRQ_TABLE_UNLOCK(adev, flags) \ + spin_unlock_irqrestore(&adev->dm.irq_handler_list_table_lock, flags) + +/****************************************************************************** + * Private functions. + *****************************************************************************/ + +static void init_handler_common_data(struct handler_common_data *hcd, + void (*ih)(void *), + void *args, + struct amdgpu_display_manager *dm) +{ + hcd->handler = ih; + hcd->handler_arg = args; + hcd->dm = dm; +} + +/** + * dm_irq_work_func - Handle an IRQ outside of the interrupt handler proper. + * + * @work: work struct + */ +static void dm_irq_work_func(struct work_struct *work) +{ + struct list_head *entry; + struct irq_list_head *irq_list_head = + container_of(work, struct irq_list_head, work); + struct list_head *handler_list = &irq_list_head->head; + struct amdgpu_dm_irq_handler_data *handler_data; + + list_for_each(entry, handler_list) { + handler_data = + list_entry( + entry, + struct amdgpu_dm_irq_handler_data, + hcd.list); + + DRM_DEBUG_KMS("DM_IRQ: work_func: for dal_src=%d\n", + handler_data->irq_source); + + DRM_DEBUG_KMS("DM_IRQ: schedule_work: for dal_src=%d\n", + handler_data->irq_source); + + handler_data->hcd.handler(handler_data->hcd.handler_arg); + } + + /* Call a DAL subcomponent which registered for interrupt notification + * at INTERRUPT_LOW_IRQ_CONTEXT. + * (The most common use is HPD interrupt) */ +} + +/** + * Remove a handler and return a pointer to hander list from which the + * handler was removed. + */ +static struct list_head *remove_irq_handler(struct amdgpu_device *adev, + void *ih, + const struct dc_interrupt_params *int_params) +{ + struct list_head *hnd_list; + struct list_head *entry, *tmp; + struct amdgpu_dm_irq_handler_data *handler; + unsigned long irq_table_flags; + bool handler_removed = false; + enum dc_irq_source irq_source; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + irq_source = int_params->irq_source; + + switch (int_params->int_context) { + case INTERRUPT_HIGH_IRQ_CONTEXT: + hnd_list = &adev->dm.irq_handler_list_high_tab[irq_source]; + break; + case INTERRUPT_LOW_IRQ_CONTEXT: + default: + hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source].head; + break; + } + + list_for_each_safe(entry, tmp, hnd_list) { + + handler = list_entry(entry, struct amdgpu_dm_irq_handler_data, + hcd.list); + + if (ih == handler) { + /* Found our handler. Remove it from the list. */ + list_del(&handler->hcd.list); + handler_removed = true; + break; + } + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + + if (handler_removed == false) { + /* Not necessarily an error - caller may not + * know the context. */ + return NULL; + } + + kfree(handler); + + DRM_DEBUG_KMS( + "DM_IRQ: removed irq handler: %p for: dal_src=%d, irq context=%d\n", + ih, int_params->irq_source, int_params->int_context); + + return hnd_list; +} + +/* If 'handler_in == NULL' then remove ALL handlers. */ +static void remove_timer_handler(struct amdgpu_device *adev, + struct amdgpu_dm_timer_handler_data *handler_in) +{ + struct amdgpu_dm_timer_handler_data *handler_temp; + struct list_head *handler_list; + struct list_head *entry, *tmp; + unsigned long irq_table_flags; + bool handler_removed = false; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + handler_list = &adev->dm.timer_handler_list; + + list_for_each_safe(entry, tmp, handler_list) { + /* Note that list_for_each_safe() guarantees that + * handler_temp is NOT null. */ + handler_temp = list_entry(entry, + struct amdgpu_dm_timer_handler_data, hcd.list); + + if (handler_in == NULL || handler_in == handler_temp) { + list_del(&handler_temp->hcd.list); + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + + DRM_DEBUG_KMS("DM_IRQ: removing timer handler: %p\n", + handler_temp); + + if (handler_in == NULL) { + /* Since it is still in the queue, it must + * be cancelled. */ + cancel_delayed_work_sync(&handler_temp->d_work); + } + + kfree(handler_temp); + handler_removed = true; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + } + + /* Remove ALL handlers. */ + if (handler_in == NULL) + continue; + + /* Remove a SPECIFIC handler. + * Found our handler - we can stop here. */ + if (handler_in == handler_temp) + break; + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + + if (handler_in != NULL && handler_removed == false) + DRM_ERROR("DM_IRQ: handler: %p is not in the list!\n", + handler_in); +} + +static bool +validate_irq_registration_params(struct dc_interrupt_params *int_params, + void (*ih)(void *)) +{ + if (NULL == int_params || NULL == ih) { + DRM_ERROR("DM_IRQ: invalid input!\n"); + return false; + } + + if (int_params->int_context >= INTERRUPT_CONTEXT_NUMBER) { + DRM_ERROR("DM_IRQ: invalid context: %d!\n", + int_params->int_context); + return false; + } + + if (!DAL_VALID_IRQ_SRC_NUM(int_params->irq_source)) { + DRM_ERROR("DM_IRQ: invalid irq_source: %d!\n", + int_params->irq_source); + return false; + } + + return true; +} + +static bool validate_irq_unregistration_params(enum dc_irq_source irq_source, + irq_handler_idx handler_idx) +{ + if (DAL_INVALID_IRQ_HANDLER_IDX == handler_idx) { + DRM_ERROR("DM_IRQ: invalid handler_idx==NULL!\n"); + return false; + } + + if (!DAL_VALID_IRQ_SRC_NUM(irq_source)) { + DRM_ERROR("DM_IRQ: invalid irq_source:%d!\n", irq_source); + return false; + } + + return true; +} +/****************************************************************************** + * Public functions. + * + * Note: caller is responsible for input validation. + *****************************************************************************/ + +void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev, + struct dc_interrupt_params *int_params, + void (*ih)(void *), + void *handler_args) +{ + struct list_head *hnd_list; + struct amdgpu_dm_irq_handler_data *handler_data; + unsigned long irq_table_flags; + enum dc_irq_source irq_source; + + if (false == validate_irq_registration_params(int_params, ih)) + return DAL_INVALID_IRQ_HANDLER_IDX; + + handler_data = kzalloc(sizeof(*handler_data), GFP_KERNEL); + if (!handler_data) { + DRM_ERROR("DM_IRQ: failed to allocate irq handler!\n"); + return DAL_INVALID_IRQ_HANDLER_IDX; + } + + memset(handler_data, 0, sizeof(*handler_data)); + + init_handler_common_data(&handler_data->hcd, ih, handler_args, + &adev->dm); + + irq_source = int_params->irq_source; + + handler_data->irq_source = irq_source; + + /* Lock the list, add the handler. */ + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + switch (int_params->int_context) { + case INTERRUPT_HIGH_IRQ_CONTEXT: + hnd_list = &adev->dm.irq_handler_list_high_tab[irq_source]; + break; + case INTERRUPT_LOW_IRQ_CONTEXT: + default: + hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source].head; + break; + } + + list_add_tail(&handler_data->hcd.list, hnd_list); + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + + /* This pointer will be stored by code which requested interrupt + * registration. + * The same pointer will be needed in order to unregister the + * interrupt. */ + + DRM_DEBUG_KMS( + "DM_IRQ: added irq handler: %p for: dal_src=%d, irq context=%d\n", + handler_data, + irq_source, + int_params->int_context); + + return handler_data; +} + +void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, + enum dc_irq_source irq_source, + void *ih) +{ + struct list_head *handler_list; + struct dc_interrupt_params int_params; + int i; + + if (false == validate_irq_unregistration_params(irq_source, ih)) + return; + + memset(&int_params, 0, sizeof(int_params)); + + int_params.irq_source = irq_source; + + for (i = 0; i < INTERRUPT_CONTEXT_NUMBER; i++) { + + int_params.int_context = i; + + handler_list = remove_irq_handler(adev, ih, &int_params); + + if (handler_list != NULL) + break; + } + + if (handler_list == NULL) { + /* If we got here, it means we searched all irq contexts + * for this irq source, but the handler was not found. */ + DRM_ERROR( + "DM_IRQ: failed to find irq handler:%p for irq_source:%d!\n", + ih, irq_source); + } +} + +int amdgpu_dm_irq_init(struct amdgpu_device *adev) +{ + int src; + struct irq_list_head *lh; + + DRM_DEBUG_KMS("DM_IRQ\n"); + + spin_lock_init(&adev->dm.irq_handler_list_table_lock); + + for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { + /* low context handler list init */ + lh = &adev->dm.irq_handler_list_low_tab[src]; + INIT_LIST_HEAD(&lh->head); + INIT_WORK(&lh->work, dm_irq_work_func); + + /* high context handler init */ + INIT_LIST_HEAD(&adev->dm.irq_handler_list_high_tab[src]); + } + + INIT_LIST_HEAD(&adev->dm.timer_handler_list); + + /* allocate and initialize the workqueue for DM timer */ + adev->dm.timer_workqueue = create_singlethread_workqueue( + "dm_timer_queue"); + if (adev->dm.timer_workqueue == NULL) { + DRM_ERROR("DM_IRQ: unable to create timer queue!\n"); + return -1; + } + + return 0; +} + +/* DM IRQ and timer resource release */ +void amdgpu_dm_irq_fini(struct amdgpu_device *adev) +{ + int src; + struct irq_list_head *lh; + DRM_DEBUG_KMS("DM_IRQ: releasing resources.\n"); + + for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { + + /* The handler was removed from the table, + * it means it is safe to flush all the 'work' + * (because no code can schedule a new one). */ + lh = &adev->dm.irq_handler_list_low_tab[src]; + flush_work(&lh->work); + } + + /* Cancel ALL timers and release handlers (if any). */ + remove_timer_handler(adev, NULL); + /* Release the queue itself. */ + destroy_workqueue(adev->dm.timer_workqueue); +} + +int amdgpu_dm_irq_suspend(struct amdgpu_device *adev) +{ + int src; + struct list_head *hnd_list_h; + struct list_head *hnd_list_l; + unsigned long irq_table_flags; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + DRM_DEBUG_KMS("DM_IRQ: suspend\n"); + + /** + * Disable HW interrupt for HPD and HPDRX only since FLIP and VBLANK + * will be disabled from manage_dm_interrupts on disable CRTC. + */ + for (src = DC_IRQ_SOURCE_HPD1; src <= DC_IRQ_SOURCE_HPD6RX; src++) { + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) + dc_interrupt_set(adev->dm.dc, src, false); + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + flush_work(&adev->dm.irq_handler_list_low_tab[src].work); + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + return 0; +} + +int amdgpu_dm_irq_resume_early(struct amdgpu_device *adev) +{ + int src; + struct list_head *hnd_list_h, *hnd_list_l; + unsigned long irq_table_flags; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + DRM_DEBUG_KMS("DM_IRQ: early resume\n"); + + /* re-enable short pulse interrupts HW interrupt */ + for (src = DC_IRQ_SOURCE_HPD1RX; src <= DC_IRQ_SOURCE_HPD6RX; src++) { + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) + dc_interrupt_set(adev->dm.dc, src, true); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + + return 0; +} + +int amdgpu_dm_irq_resume_late(struct amdgpu_device *adev) +{ + int src; + struct list_head *hnd_list_h, *hnd_list_l; + unsigned long irq_table_flags; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + DRM_DEBUG_KMS("DM_IRQ: resume\n"); + + /** + * Renable HW interrupt for HPD and only since FLIP and VBLANK + * will be enabled from manage_dm_interrupts on enable CRTC. + */ + for (src = DC_IRQ_SOURCE_HPD1; src <= DC_IRQ_SOURCE_HPD6; src++) { + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) + dc_interrupt_set(adev->dm.dc, src, true); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + return 0; +} + +/** + * amdgpu_dm_irq_schedule_work - schedule all work items registered for the + * "irq_source". + */ +static void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev, + enum dc_irq_source irq_source) +{ + unsigned long irq_table_flags; + struct work_struct *work = NULL; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + if (!list_empty(&adev->dm.irq_handler_list_low_tab[irq_source].head)) + work = &adev->dm.irq_handler_list_low_tab[irq_source].work; + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + + if (work) { + if (!schedule_work(work)) + DRM_INFO("amdgpu_dm_irq_schedule_work FAILED src %d\n", + irq_source); + } + +} + +/** amdgpu_dm_irq_immediate_work + * Callback high irq work immediately, don't send to work queue + */ +static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev, + enum dc_irq_source irq_source) +{ + struct amdgpu_dm_irq_handler_data *handler_data; + struct list_head *entry; + unsigned long irq_table_flags; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + list_for_each( + entry, + &adev->dm.irq_handler_list_high_tab[irq_source]) { + + handler_data = + list_entry( + entry, + struct amdgpu_dm_irq_handler_data, + hcd.list); + + /* Call a subcomponent which registered for immediate + * interrupt notification */ + handler_data->hcd.handler(handler_data->hcd.handler_arg); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); +} + +/* + * amdgpu_dm_irq_handler + * + * Generic IRQ handler, calls all registered high irq work immediately, and + * schedules work for low irq + */ +static int amdgpu_dm_irq_handler(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + struct amdgpu_iv_entry *entry) +{ + + enum dc_irq_source src = + dc_interrupt_to_irq_source( + adev->dm.dc, + entry->src_id, + entry->src_data[0]); + + dc_interrupt_ack(adev->dm.dc, src); + + /* Call high irq work immediately */ + amdgpu_dm_irq_immediate_work(adev, src); + /*Schedule low_irq work */ + amdgpu_dm_irq_schedule_work(adev, src); + + return 0; +} + +static enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned type) +{ + switch (type) { + case AMDGPU_HPD_1: + return DC_IRQ_SOURCE_HPD1; + case AMDGPU_HPD_2: + return DC_IRQ_SOURCE_HPD2; + case AMDGPU_HPD_3: + return DC_IRQ_SOURCE_HPD3; + case AMDGPU_HPD_4: + return DC_IRQ_SOURCE_HPD4; + case AMDGPU_HPD_5: + return DC_IRQ_SOURCE_HPD5; + case AMDGPU_HPD_6: + return DC_IRQ_SOURCE_HPD6; + default: + return DC_IRQ_SOURCE_INVALID; + } +} + +static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + unsigned type, + enum amdgpu_interrupt_state state) +{ + enum dc_irq_source src = amdgpu_dm_hpd_to_dal_irq_source(type); + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + + dc_interrupt_set(adev->dm.dc, src, st); + return 0; +} + +static inline int dm_irq_state(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + unsigned crtc_id, + enum amdgpu_interrupt_state state, + const enum irq_type dal_irq_type, + const char *func) +{ + bool st; + enum dc_irq_source irq_source; + + struct amdgpu_crtc *acrtc = adev->mode_info.crtcs[crtc_id]; + + if (!acrtc) { + DRM_ERROR( + "%s: crtc is NULL at id :%d\n", + func, + crtc_id); + return 0; + } + + irq_source = dal_irq_type + acrtc->otg_inst; + + st = (state == AMDGPU_IRQ_STATE_ENABLE); + + dc_interrupt_set(adev->dm.dc, irq_source, st); + return 0; +} + +static int amdgpu_dm_set_pflip_irq_state(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + unsigned crtc_id, + enum amdgpu_interrupt_state state) +{ + return dm_irq_state( + adev, + source, + crtc_id, + state, + IRQ_TYPE_PFLIP, + __func__); +} + +static int amdgpu_dm_set_crtc_irq_state(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + unsigned crtc_id, + enum amdgpu_interrupt_state state) +{ + return dm_irq_state( + adev, + source, + crtc_id, + state, + IRQ_TYPE_VBLANK, + __func__); +} + +static const struct amdgpu_irq_src_funcs dm_crtc_irq_funcs = { + .set = amdgpu_dm_set_crtc_irq_state, + .process = amdgpu_dm_irq_handler, +}; + +static const struct amdgpu_irq_src_funcs dm_pageflip_irq_funcs = { + .set = amdgpu_dm_set_pflip_irq_state, + .process = amdgpu_dm_irq_handler, +}; + +static const struct amdgpu_irq_src_funcs dm_hpd_irq_funcs = { + .set = amdgpu_dm_set_hpd_irq_state, + .process = amdgpu_dm_irq_handler, +}; + +void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev) +{ + adev->crtc_irq.num_types = AMDGPU_CRTC_IRQ_LAST; + adev->crtc_irq.funcs = &dm_crtc_irq_funcs; + + adev->pageflip_irq.num_types = AMDGPU_PAGEFLIP_IRQ_LAST; + adev->pageflip_irq.funcs = &dm_pageflip_irq_funcs; + + adev->hpd_irq.num_types = AMDGPU_HPD_LAST; + adev->hpd_irq.funcs = &dm_hpd_irq_funcs; +} + +/* + * amdgpu_dm_hpd_init - hpd setup callback. + * + * @adev: amdgpu_device pointer + * + * Setup the hpd pins used by the card (evergreen+). + * Enable the pin, set the polarity, and enable the hpd interrupts. + */ +void amdgpu_dm_hpd_init(struct amdgpu_device *adev) +{ + struct drm_device *dev = adev->ddev; + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + + const struct dc_link *dc_link = amdgpu_dm_connector->dc_link; + + if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd) { + dc_interrupt_set(adev->dm.dc, + dc_link->irq_source_hpd, + true); + } + + if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd_rx) { + dc_interrupt_set(adev->dm.dc, + dc_link->irq_source_hpd_rx, + true); + } + } +} + +/** + * amdgpu_dm_hpd_fini - hpd tear down callback. + * + * @adev: amdgpu_device pointer + * + * Tear down the hpd pins used by the card (evergreen+). + * Disable the hpd interrupts. + */ +void amdgpu_dm_hpd_fini(struct amdgpu_device *adev) +{ + struct drm_device *dev = adev->ddev; + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + const struct dc_link *dc_link = amdgpu_dm_connector->dc_link; + + dc_interrupt_set(adev->dm.dc, dc_link->irq_source_hpd, false); + + if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd_rx) { + dc_interrupt_set(adev->dm.dc, + dc_link->irq_source_hpd_rx, + false); + } + } +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h new file mode 100644 index 000000000000..82f8e761beca --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h @@ -0,0 +1,102 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __AMDGPU_DM_IRQ_H__ +#define __AMDGPU_DM_IRQ_H__ + +#include "irq_types.h" /* DAL irq definitions */ + +/* + * Display Manager IRQ-related interfaces (for use by DAL). + */ + +/** + * amdgpu_dm_irq_init - Initialize internal structures of 'amdgpu_dm_irq'. + * + * This function should be called exactly once - during DM initialization. + * + * Returns: + * 0 - success + * non-zero - error + */ +int amdgpu_dm_irq_init(struct amdgpu_device *adev); + +/** + * amdgpu_dm_irq_fini - deallocate internal structures of 'amdgpu_dm_irq'. + * + * This function should be called exactly once - during DM destruction. + * + */ +void amdgpu_dm_irq_fini(struct amdgpu_device *adev); + +/** + * amdgpu_dm_irq_register_interrupt - register irq handler for Display block. + * + * @adev: AMD DRM device + * @int_params: parameters for the irq + * @ih: pointer to the irq hander function + * @handler_args: arguments which will be passed to ih + * + * Returns: + * IRQ Handler Index on success. + * NULL on failure. + * + * Cannot be called from an interrupt handler. + */ +void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev, + struct dc_interrupt_params *int_params, + void (*ih)(void *), + void *handler_args); + +/** + * amdgpu_dm_irq_unregister_interrupt - unregister handler which was registered + * by amdgpu_dm_irq_register_interrupt(). + * + * @adev: AMD DRM device. + * @ih_index: irq handler index which was returned by + * amdgpu_dm_irq_register_interrupt + */ +void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, + enum dc_irq_source irq_source, + void *ih_index); + +void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev); + +void amdgpu_dm_hpd_init(struct amdgpu_device *adev); +void amdgpu_dm_hpd_fini(struct amdgpu_device *adev); + +/** + * amdgpu_dm_irq_suspend - disable ASIC interrupt during suspend. + * + */ +int amdgpu_dm_irq_suspend(struct amdgpu_device *adev); + +/** + * amdgpu_dm_irq_resume_early - enable HPDRX ASIC interrupts during resume. + * amdgpu_dm_irq_resume - enable ASIC interrupt during resume. + * + */ +int amdgpu_dm_irq_resume_early(struct amdgpu_device *adev); +int amdgpu_dm_irq_resume_late(struct amdgpu_device *adev); + +#endif /* __AMDGPU_DM_IRQ_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c new file mode 100644 index 000000000000..f8efb98b1fa7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -0,0 +1,446 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include +#include +#include "dm_services.h" +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_mst_types.h" + +#include "dc.h" +#include "dm_helpers.h" + +#include "dc_link_ddc.h" + +/* #define TRACE_DPCD */ + +#ifdef TRACE_DPCD +#define SIDE_BAND_MSG(address) (address >= DP_SIDEBAND_MSG_DOWN_REQ_BASE && address < DP_SINK_COUNT_ESI) + +static inline char *side_band_msg_type_to_str(uint32_t address) +{ + static char str[10] = {0}; + + if (address < DP_SIDEBAND_MSG_UP_REP_BASE) + strcpy(str, "DOWN_REQ"); + else if (address < DP_SIDEBAND_MSG_DOWN_REP_BASE) + strcpy(str, "UP_REP"); + else if (address < DP_SIDEBAND_MSG_UP_REQ_BASE) + strcpy(str, "DOWN_REP"); + else + strcpy(str, "UP_REQ"); + + return str; +} + +static void log_dpcd(uint8_t type, + uint32_t address, + uint8_t *data, + uint32_t size, + bool res) +{ + DRM_DEBUG_KMS("Op: %s, addr: %04x, SideBand Msg: %s, Op res: %s\n", + (type == DP_AUX_NATIVE_READ) || + (type == DP_AUX_I2C_READ) ? + "Read" : "Write", + address, + SIDE_BAND_MSG(address) ? + side_band_msg_type_to_str(address) : "Nop", + res ? "OK" : "Fail"); + + if (res) { + print_hex_dump(KERN_INFO, "Body: ", DUMP_PREFIX_NONE, 16, 1, data, size, false); + } +} +#endif + +static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + enum i2c_mot_mode mot = (msg->request & DP_AUX_I2C_MOT) ? + I2C_MOT_TRUE : I2C_MOT_FALSE; + enum ddc_result res; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_READ: + res = dal_ddc_service_read_dpcd_data( + TO_DM_AUX(aux)->ddc_service, + false, + I2C_MOT_UNDEF, + msg->address, + msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_WRITE: + res = dal_ddc_service_write_dpcd_data( + TO_DM_AUX(aux)->ddc_service, + false, + I2C_MOT_UNDEF, + msg->address, + msg->buffer, + msg->size); + break; + case DP_AUX_I2C_READ: + res = dal_ddc_service_read_dpcd_data( + TO_DM_AUX(aux)->ddc_service, + true, + mot, + msg->address, + msg->buffer, + msg->size); + break; + case DP_AUX_I2C_WRITE: + res = dal_ddc_service_write_dpcd_data( + TO_DM_AUX(aux)->ddc_service, + true, + mot, + msg->address, + msg->buffer, + msg->size); + break; + default: + return 0; + } + +#ifdef TRACE_DPCD + log_dpcd(msg->request, + msg->address, + msg->buffer, + msg->size, + r == DDC_RESULT_SUCESSFULL); +#endif + + return msg->size; +} + +static enum drm_connector_status +dm_dp_mst_detect(struct drm_connector *connector, bool force) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + struct amdgpu_dm_connector *master = aconnector->mst_port; + + enum drm_connector_status status = + drm_dp_mst_detect_port( + connector, + &master->mst_mgr, + aconnector->port); + + return status; +} + +static void +dm_dp_mst_connector_destroy(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); + struct amdgpu_encoder *amdgpu_encoder = amdgpu_dm_connector->mst_encoder; + + drm_encoder_cleanup(&amdgpu_encoder->base); + kfree(amdgpu_encoder); + drm_connector_cleanup(connector); + kfree(amdgpu_dm_connector); +} + +static const struct drm_connector_funcs dm_dp_mst_connector_funcs = { + .detect = dm_dp_mst_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = dm_dp_mst_connector_destroy, + .reset = amdgpu_dm_connector_funcs_reset, + .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = amdgpu_dm_connector_atomic_set_property, + .atomic_get_property = amdgpu_dm_connector_atomic_get_property +}; + +static int dm_connector_update_modes(struct drm_connector *connector, + struct edid *edid) +{ + int ret; + + ret = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); + + return ret; +} + +static int dm_dp_mst_get_modes(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + int ret = 0; + + if (!aconnector) + return dm_connector_update_modes(connector, NULL); + + if (!aconnector->edid) { + struct edid *edid; + struct dc_sink *dc_sink; + struct dc_sink_init_data init_params = { + .link = aconnector->dc_link, + .sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST }; + edid = drm_dp_mst_get_edid(connector, &aconnector->mst_port->mst_mgr, aconnector->port); + + if (!edid) { + drm_mode_connector_update_edid_property( + &aconnector->base, + NULL); + return ret; + } + + aconnector->edid = edid; + + dc_sink = dc_link_add_remote_sink( + aconnector->dc_link, + (uint8_t *)edid, + (edid->extensions + 1) * EDID_LENGTH, + &init_params); + + dc_sink->priv = aconnector; + aconnector->dc_sink = dc_sink; + + if (aconnector->dc_sink) + amdgpu_dm_add_sink_to_freesync_module( + connector, edid); + + drm_mode_connector_update_edid_property( + &aconnector->base, edid); + } + + ret = dm_connector_update_modes(connector, aconnector->edid); + + return ret; +} + +static struct drm_encoder *dm_mst_best_encoder(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); + + return &amdgpu_dm_connector->mst_encoder->base; +} + +static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs = { + .get_modes = dm_dp_mst_get_modes, + .mode_valid = amdgpu_dm_connector_mode_valid, + .best_encoder = dm_mst_best_encoder, +}; + +static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = { + .destroy = amdgpu_dm_encoder_destroy, +}; + +static struct amdgpu_encoder * +dm_dp_create_fake_mst_encoder(struct amdgpu_dm_connector *connector) +{ + struct drm_device *dev = connector->base.dev; + struct amdgpu_device *adev = dev->dev_private; + struct amdgpu_encoder *amdgpu_encoder; + struct drm_encoder *encoder; + const struct drm_connector_helper_funcs *connector_funcs = + connector->base.helper_private; + struct drm_encoder *enc_master = + connector_funcs->best_encoder(&connector->base); + + DRM_DEBUG_KMS("enc master is %p\n", enc_master); + amdgpu_encoder = kzalloc(sizeof(*amdgpu_encoder), GFP_KERNEL); + if (!amdgpu_encoder) + return NULL; + + encoder = &amdgpu_encoder->base; + encoder->possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev); + + drm_encoder_init( + dev, + &amdgpu_encoder->base, + &amdgpu_dm_encoder_funcs, + DRM_MODE_ENCODER_DPMST, + NULL); + + drm_encoder_helper_add(encoder, &amdgpu_dm_encoder_helper_funcs); + + return amdgpu_encoder; +} + +static struct drm_connector * +dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + const char *pathprop) +{ + struct amdgpu_dm_connector *master = container_of(mgr, struct amdgpu_dm_connector, mst_mgr); + struct drm_device *dev = master->base.dev; + struct amdgpu_device *adev = dev->dev_private; + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->mst_port == master + && !aconnector->port) { + DRM_INFO("DM_MST: reusing connector: %p [id: %d] [master: %p]\n", + aconnector, connector->base.id, aconnector->mst_port); + + aconnector->port = port; + drm_mode_connector_set_path_property(connector, pathprop); + + drm_connector_list_iter_end(&conn_iter); + return &aconnector->base; + } + } + drm_connector_list_iter_end(&conn_iter); + + aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL); + if (!aconnector) + return NULL; + + connector = &aconnector->base; + aconnector->port = port; + aconnector->mst_port = master; + + if (drm_connector_init( + dev, + connector, + &dm_dp_mst_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort)) { + kfree(aconnector); + return NULL; + } + drm_connector_helper_add(connector, &dm_dp_mst_connector_helper_funcs); + + amdgpu_dm_connector_init_helper( + &adev->dm, + aconnector, + DRM_MODE_CONNECTOR_DisplayPort, + master->dc_link, + master->connector_id); + + aconnector->mst_encoder = dm_dp_create_fake_mst_encoder(master); + + /* + * TODO: understand why this one is needed + */ + drm_object_attach_property( + &connector->base, + dev->mode_config.path_property, + 0); + drm_object_attach_property( + &connector->base, + dev->mode_config.tile_property, + 0); + + drm_mode_connector_set_path_property(connector, pathprop); + + /* + * Initialize connector state before adding the connectror to drm and + * framebuffer lists + */ + amdgpu_dm_connector_funcs_reset(connector); + + DRM_INFO("DM_MST: added connector: %p [id: %d] [master: %p]\n", + aconnector, connector->base.id, aconnector->mst_port); + + DRM_DEBUG_KMS(":%d\n", connector->base.id); + + return connector; +} + +static void dm_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, + struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + DRM_INFO("DM_MST: Disabling connector: %p [id: %d] [master: %p]\n", + aconnector, connector->base.id, aconnector->mst_port); + + aconnector->port = NULL; + if (aconnector->dc_sink) { + amdgpu_dm_remove_sink_from_freesync_module(connector); + dc_link_remove_remote_sink(aconnector->dc_link, aconnector->dc_sink); + dc_sink_release(aconnector->dc_sink); + aconnector->dc_sink = NULL; + } + if (aconnector->edid) { + kfree(aconnector->edid); + aconnector->edid = NULL; + } + + drm_mode_connector_update_edid_property( + &aconnector->base, + NULL); +} + +static void dm_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) +{ + struct amdgpu_dm_connector *master = container_of(mgr, struct amdgpu_dm_connector, mst_mgr); + struct drm_device *dev = master->base.dev; + + drm_kms_helper_hotplug_event(dev); +} + +static void dm_dp_mst_register_connector(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = dev->dev_private; + + if (adev->mode_info.rfbdev) + drm_fb_helper_add_one_connector(&adev->mode_info.rfbdev->helper, connector); + else + DRM_ERROR("adev->mode_info.rfbdev is NULL\n"); + + drm_connector_register(connector); + +} + +static const struct drm_dp_mst_topology_cbs dm_mst_cbs = { + .add_connector = dm_dp_add_mst_connector, + .destroy_connector = dm_dp_destroy_mst_connector, + .hotplug = dm_dp_mst_hotplug, + .register_connector = dm_dp_mst_register_connector +}; + +void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector) +{ + aconnector->dm_dp_aux.aux.name = "dmdc"; + aconnector->dm_dp_aux.aux.dev = dm->adev->dev; + aconnector->dm_dp_aux.aux.transfer = dm_dp_aux_transfer; + aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc; + + drm_dp_aux_register(&aconnector->dm_dp_aux.aux); + aconnector->mst_mgr.cbs = &dm_mst_cbs; + drm_dp_mst_topology_mgr_init( + &aconnector->mst_mgr, + dm->adev->ddev, + &aconnector->dm_dp_aux.aux, + 16, + 4, + aconnector->connector_id); +} + diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h new file mode 100644 index 000000000000..2da851b40042 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -0,0 +1,35 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_AMDGPU_DM_MST_TYPES_H__ +#define __DAL_AMDGPU_DM_MST_TYPES_H__ + +struct amdgpu_display_manager; +struct amdgpu_dm_connector; + +void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector); + +#endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c new file mode 100644 index 000000000000..5df8fd5b537c --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c @@ -0,0 +1,379 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include +#include + +#include +#include +#include +#include "dm_services.h" +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_irq.h" +#include "amdgpu_pm.h" + +unsigned long long dm_get_timestamp(struct dc_context *ctx) +{ + /* TODO: return actual timestamp */ + return 0; +} + +bool dm_write_persistent_data(struct dc_context *ctx, + const struct dc_sink *sink, + const char *module_name, + const char *key_name, + void *params, + unsigned int size, + struct persistent_data_flag *flag) +{ + /*TODO implement*/ + return false; +} + +bool dm_read_persistent_data(struct dc_context *ctx, + const struct dc_sink *sink, + const char *module_name, + const char *key_name, + void *params, + unsigned int size, + struct persistent_data_flag *flag) +{ + /*TODO implement*/ + return false; +} + +/**** power component interfaces ****/ + +bool dm_pp_pre_dce_clock_change( + struct dc_context *ctx, + struct dm_pp_gpu_clock_range *requested_state, + struct dm_pp_gpu_clock_range *actual_state) +{ + /*TODO*/ + return false; +} + +bool dm_pp_apply_display_requirements( + const struct dc_context *ctx, + const struct dm_pp_display_configuration *pp_display_cfg) +{ + struct amdgpu_device *adev = ctx->driver_context; + + if (adev->pm.dpm_enabled) { + + memset(&adev->pm.pm_display_cfg, 0, + sizeof(adev->pm.pm_display_cfg)); + + adev->pm.pm_display_cfg.cpu_cc6_disable = + pp_display_cfg->cpu_cc6_disable; + + adev->pm.pm_display_cfg.cpu_pstate_disable = + pp_display_cfg->cpu_pstate_disable; + + adev->pm.pm_display_cfg.cpu_pstate_separation_time = + pp_display_cfg->cpu_pstate_separation_time; + + adev->pm.pm_display_cfg.nb_pstate_switch_disable = + pp_display_cfg->nb_pstate_switch_disable; + + adev->pm.pm_display_cfg.num_display = + pp_display_cfg->display_count; + adev->pm.pm_display_cfg.num_path_including_non_display = + pp_display_cfg->display_count; + + adev->pm.pm_display_cfg.min_core_set_clock = + pp_display_cfg->min_engine_clock_khz/10; + adev->pm.pm_display_cfg.min_core_set_clock_in_sr = + pp_display_cfg->min_engine_clock_deep_sleep_khz/10; + adev->pm.pm_display_cfg.min_mem_set_clock = + pp_display_cfg->min_memory_clock_khz/10; + + adev->pm.pm_display_cfg.multi_monitor_in_sync = + pp_display_cfg->all_displays_in_sync; + adev->pm.pm_display_cfg.min_vblank_time = + pp_display_cfg->avail_mclk_switch_time_us; + + adev->pm.pm_display_cfg.display_clk = + pp_display_cfg->disp_clk_khz/10; + + adev->pm.pm_display_cfg.dce_tolerable_mclk_in_active_latency = + pp_display_cfg->avail_mclk_switch_time_in_disp_active_us; + + adev->pm.pm_display_cfg.crtc_index = pp_display_cfg->crtc_index; + adev->pm.pm_display_cfg.line_time_in_us = + pp_display_cfg->line_time_in_us; + + adev->pm.pm_display_cfg.vrefresh = pp_display_cfg->disp_configs[0].v_refresh; + adev->pm.pm_display_cfg.crossfire_display_index = -1; + adev->pm.pm_display_cfg.min_bus_bandwidth = 0; + + /* TODO: complete implementation of + * amd_powerplay_display_configuration_change(). + * Follow example of: + * PHM_StoreDALConfigurationData - powerplay\hwmgr\hardwaremanager.c + * PP_IRI_DisplayConfigurationChange - powerplay\eventmgr\iri.c */ + amd_powerplay_display_configuration_change( + adev->powerplay.pp_handle, + &adev->pm.pm_display_cfg); + + /* TODO: replace by a separate call to 'apply display cfg'? */ + amdgpu_pm_compute_clocks(adev); + } + + return true; +} + +bool dc_service_get_system_clocks_range( + const struct dc_context *ctx, + struct dm_pp_gpu_clock_range *sys_clks) +{ + struct amdgpu_device *adev = ctx->driver_context; + + /* Default values, in case PPLib is not compiled-in. */ + sys_clks->mclk.max_khz = 800000; + sys_clks->mclk.min_khz = 800000; + + sys_clks->sclk.max_khz = 600000; + sys_clks->sclk.min_khz = 300000; + + if (adev->pm.dpm_enabled) { + sys_clks->mclk.max_khz = amdgpu_dpm_get_mclk(adev, false); + sys_clks->mclk.min_khz = amdgpu_dpm_get_mclk(adev, true); + + sys_clks->sclk.max_khz = amdgpu_dpm_get_sclk(adev, false); + sys_clks->sclk.min_khz = amdgpu_dpm_get_sclk(adev, true); + } + + return true; +} + +static void get_default_clock_levels( + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels *clks) +{ + uint32_t disp_clks_in_khz[6] = { + 300000, 400000, 496560, 626090, 685720, 757900 }; + uint32_t sclks_in_khz[6] = { + 300000, 360000, 423530, 514290, 626090, 720000 }; + uint32_t mclks_in_khz[2] = { 333000, 800000 }; + + switch (clk_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + clks->num_levels = 6; + memmove(clks->clocks_in_khz, disp_clks_in_khz, + sizeof(disp_clks_in_khz)); + break; + case DM_PP_CLOCK_TYPE_ENGINE_CLK: + clks->num_levels = 6; + memmove(clks->clocks_in_khz, sclks_in_khz, + sizeof(sclks_in_khz)); + break; + case DM_PP_CLOCK_TYPE_MEMORY_CLK: + clks->num_levels = 2; + memmove(clks->clocks_in_khz, mclks_in_khz, + sizeof(mclks_in_khz)); + break; + default: + clks->num_levels = 0; + break; + } +} + +static enum amd_pp_clock_type dc_to_pp_clock_type( + enum dm_pp_clock_type dm_pp_clk_type) +{ + enum amd_pp_clock_type amd_pp_clk_type = 0; + + switch (dm_pp_clk_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + amd_pp_clk_type = amd_pp_disp_clock; + break; + case DM_PP_CLOCK_TYPE_ENGINE_CLK: + amd_pp_clk_type = amd_pp_sys_clock; + break; + case DM_PP_CLOCK_TYPE_MEMORY_CLK: + amd_pp_clk_type = amd_pp_mem_clock; + break; + default: + DRM_ERROR("DM_PPLIB: invalid clock type: %d!\n", + dm_pp_clk_type); + break; + } + + return amd_pp_clk_type; +} + +static void pp_to_dc_clock_levels( + const struct amd_pp_clocks *pp_clks, + struct dm_pp_clock_levels *dc_clks, + enum dm_pp_clock_type dc_clk_type) +{ + uint32_t i; + + if (pp_clks->count > DM_PP_MAX_CLOCK_LEVELS) { + DRM_INFO("DM_PPLIB: Warning: %s clock: number of levels %d exceeds maximum of %d!\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type), + pp_clks->count, + DM_PP_MAX_CLOCK_LEVELS); + + dc_clks->num_levels = DM_PP_MAX_CLOCK_LEVELS; + } else + dc_clks->num_levels = pp_clks->count; + + DRM_INFO("DM_PPLIB: values for %s clock\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type)); + + for (i = 0; i < dc_clks->num_levels; i++) { + DRM_INFO("DM_PPLIB:\t %d\n", pp_clks->clock[i]); + /* translate 10kHz to kHz */ + dc_clks->clocks_in_khz[i] = pp_clks->clock[i] * 10; + } +} + +bool dm_pp_get_clock_levels_by_type( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels *dc_clks) +{ + struct amdgpu_device *adev = ctx->driver_context; + void *pp_handle = adev->powerplay.pp_handle; + struct amd_pp_clocks pp_clks = { 0 }; + struct amd_pp_simple_clock_info validation_clks = { 0 }; + uint32_t i; + + if (amd_powerplay_get_clock_by_type(pp_handle, + dc_to_pp_clock_type(clk_type), &pp_clks)) { + /* Error in pplib. Provide default values. */ + get_default_clock_levels(clk_type, dc_clks); + return true; + } + + pp_to_dc_clock_levels(&pp_clks, dc_clks, clk_type); + + if (amd_powerplay_get_display_mode_validation_clocks(pp_handle, + &validation_clks)) { + /* Error in pplib. Provide default values. */ + DRM_INFO("DM_PPLIB: Warning: using default validation clocks!\n"); + validation_clks.engine_max_clock = 72000; + validation_clks.memory_max_clock = 80000; + validation_clks.level = 0; + } + + DRM_INFO("DM_PPLIB: Validation clocks:\n"); + DRM_INFO("DM_PPLIB: engine_max_clock: %d\n", + validation_clks.engine_max_clock); + DRM_INFO("DM_PPLIB: memory_max_clock: %d\n", + validation_clks.memory_max_clock); + DRM_INFO("DM_PPLIB: level : %d\n", + validation_clks.level); + + /* Translate 10 kHz to kHz. */ + validation_clks.engine_max_clock *= 10; + validation_clks.memory_max_clock *= 10; + + /* Determine the highest non-boosted level from the Validation Clocks */ + if (clk_type == DM_PP_CLOCK_TYPE_ENGINE_CLK) { + for (i = 0; i < dc_clks->num_levels; i++) { + if (dc_clks->clocks_in_khz[i] > validation_clks.engine_max_clock) { + /* This clock is higher the validation clock. + * Than means the previous one is the highest + * non-boosted one. */ + DRM_INFO("DM_PPLIB: reducing engine clock level from %d to %d\n", + dc_clks->num_levels, i); + dc_clks->num_levels = i > 0 ? i : 1; + break; + } + } + } else if (clk_type == DM_PP_CLOCK_TYPE_MEMORY_CLK) { + for (i = 0; i < dc_clks->num_levels; i++) { + if (dc_clks->clocks_in_khz[i] > validation_clks.memory_max_clock) { + DRM_INFO("DM_PPLIB: reducing memory clock level from %d to %d\n", + dc_clks->num_levels, i); + dc_clks->num_levels = i > 0 ? i : 1; + break; + } + } + } + + return true; +} + +bool dm_pp_get_clock_levels_by_type_with_latency( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels_with_latency *clk_level_info) +{ + /* TODO: to be implemented */ + return false; +} + +bool dm_pp_get_clock_levels_by_type_with_voltage( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels_with_voltage *clk_level_info) +{ + /* TODO: to be implemented */ + return false; +} + +bool dm_pp_notify_wm_clock_changes( + const struct dc_context *ctx, + struct dm_pp_wm_sets_with_clock_ranges *wm_with_clock_ranges) +{ + /* TODO: to be implemented */ + return false; +} + +bool dm_pp_apply_power_level_change_request( + const struct dc_context *ctx, + struct dm_pp_power_level_change_request *level_change_req) +{ + /* TODO: to be implemented */ + return false; +} + +bool dm_pp_apply_clock_for_voltage_request( + const struct dc_context *ctx, + struct dm_pp_clock_for_voltage_req *clock_for_voltage_req) +{ + /* TODO: to be implemented */ + return false; +} + +bool dm_pp_get_static_clocks( + const struct dc_context *ctx, + struct dm_pp_static_clock_info *static_clk_info) +{ + /* TODO: to be implemented */ + return false; +} + +void dm_pp_get_funcs_rv( + struct dc_context *ctx, + struct pp_smu_funcs_rv *funcs) +{} + +/**** end of power component interfaces ****/ diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile new file mode 100644 index 000000000000..4f83e3011743 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/Makefile @@ -0,0 +1,33 @@ +# +# Makefile for Display Core (dc) component. +# + +DC_LIBS = basics bios calcs dce gpio i2caux irq virtual + +ifdef CONFIG_DRM_AMD_DC_DCN1_0 +DC_LIBS += dcn10 dml +endif + +DC_LIBS += dce120 + +DC_LIBS += dce112 +DC_LIBS += dce110 +DC_LIBS += dce100 +DC_LIBS += dce80 + +AMD_DC = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/dc/,$(DC_LIBS))) + +include $(AMD_DC) + +DISPLAY_CORE = dc.o dc_link.o dc_resource.o dc_hw_sequencer.o dc_sink.o \ +dc_surface.o dc_link_hwss.o dc_link_dp.o dc_link_ddc.o dc_debug.o dc_stream.o + +AMD_DISPLAY_CORE = $(addprefix $(AMDDALPATH)/dc/core/,$(DISPLAY_CORE)) + +AMD_DM_REG_UPDATE = $(addprefix $(AMDDALPATH)/dc/,dc_helper.o) + +AMD_DISPLAY_FILES += $(AMD_DISPLAY_CORE) +AMD_DISPLAY_FILES += $(AMD_DM_REG_UPDATE) + + + diff --git a/drivers/gpu/drm/amd/display/dc/basics/Makefile b/drivers/gpu/drm/amd/display/dc/basics/Makefile new file mode 100644 index 000000000000..43c5ccdeeb72 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the 'utils' sub-component of DAL. +# It provides the general basic services required by other DAL +# subcomponents. + +BASICS = conversion.o fixpt31_32.o fixpt32_32.o grph_object_id.o \ + logger.o log_helpers.o vector.o + +AMD_DAL_BASICS = $(addprefix $(AMDDALPATH)/dc/basics/,$(BASICS)) + +AMD_DISPLAY_FILES += $(AMD_DAL_BASICS) diff --git a/drivers/gpu/drm/amd/display/dc/basics/conversion.c b/drivers/gpu/drm/amd/display/dc/basics/conversion.c new file mode 100644 index 000000000000..23c9a0ec0181 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/conversion.c @@ -0,0 +1,104 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#define DIVIDER 10000 + +/* S2D13 value in [-3.00...0.9999] */ +#define S2D13_MIN (-3 * DIVIDER) +#define S2D13_MAX (3 * DIVIDER) + +uint16_t fixed_point_to_int_frac( + struct fixed31_32 arg, + uint8_t integer_bits, + uint8_t fractional_bits) +{ + int32_t numerator; + int32_t divisor = 1 << fractional_bits; + + uint16_t result; + + uint16_t d = (uint16_t)dal_fixed31_32_floor( + dal_fixed31_32_abs( + arg)); + + if (d <= (uint16_t)(1 << integer_bits) - (1 / (uint16_t)divisor)) + numerator = (uint16_t)dal_fixed31_32_floor( + dal_fixed31_32_mul_int( + arg, + divisor)); + else { + numerator = dal_fixed31_32_floor( + dal_fixed31_32_sub( + dal_fixed31_32_from_int( + 1LL << integer_bits), + dal_fixed31_32_recip( + dal_fixed31_32_from_int( + divisor)))); + } + + if (numerator >= 0) + result = (uint16_t)numerator; + else + result = (uint16_t)( + (1 << (integer_bits + fractional_bits + 1)) + numerator); + + if ((result != 0) && dal_fixed31_32_lt( + arg, dal_fixed31_32_zero)) + result |= 1 << (integer_bits + fractional_bits); + + return result; +} +/** +* convert_float_matrix +* This converts a double into HW register spec defined format S2D13. +* @param : +* @return None +*/ +void convert_float_matrix( + uint16_t *matrix, + struct fixed31_32 *flt, + uint32_t buffer_size) +{ + const struct fixed31_32 min_2_13 = + dal_fixed31_32_from_fraction(S2D13_MIN, DIVIDER); + const struct fixed31_32 max_2_13 = + dal_fixed31_32_from_fraction(S2D13_MAX, DIVIDER); + uint32_t i; + + for (i = 0; i < buffer_size; ++i) { + uint32_t reg_value = + fixed_point_to_int_frac( + dal_fixed31_32_clamp( + flt[i], + min_2_13, + max_2_13), + 2, + 13); + + matrix[i] = (uint16_t)reg_value; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/basics/conversion.h b/drivers/gpu/drm/amd/display/dc/basics/conversion.h new file mode 100644 index 000000000000..ade785c4fdc7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/conversion.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_CONVERSION_H__ +#define __DAL_CONVERSION_H__ + +#include "include/fixed31_32.h" + +uint16_t fixed_point_to_int_frac( + struct fixed31_32 arg, + uint8_t integer_bits, + uint8_t fractional_bits); + +void convert_float_matrix( + uint16_t *matrix, + struct fixed31_32 *flt, + uint32_t buffer_size); + +static inline unsigned int log_2(unsigned int num) +{ + return ilog2(num); +} + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c b/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c new file mode 100644 index 000000000000..26936892c6f5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c @@ -0,0 +1,567 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/fixed31_32.h" + +static inline uint64_t abs_i64( + int64_t arg) +{ + if (arg > 0) + return (uint64_t)arg; + else + return (uint64_t)(-arg); +} + +/* + * @brief + * result = dividend / divisor + * *remainder = dividend % divisor + */ +static inline uint64_t complete_integer_division_u64( + uint64_t dividend, + uint64_t divisor, + uint64_t *remainder) +{ + uint64_t result; + + ASSERT(divisor); + + result = div64_u64_rem(dividend, divisor, remainder); + + return result; +} + + +#define FRACTIONAL_PART_MASK \ + ((1ULL << FIXED31_32_BITS_PER_FRACTIONAL_PART) - 1) + +#define GET_INTEGER_PART(x) \ + ((x) >> FIXED31_32_BITS_PER_FRACTIONAL_PART) + +#define GET_FRACTIONAL_PART(x) \ + (FRACTIONAL_PART_MASK & (x)) + +struct fixed31_32 dal_fixed31_32_from_fraction( + int64_t numerator, + int64_t denominator) +{ + struct fixed31_32 res; + + bool arg1_negative = numerator < 0; + bool arg2_negative = denominator < 0; + + uint64_t arg1_value = arg1_negative ? -numerator : numerator; + uint64_t arg2_value = arg2_negative ? -denominator : denominator; + + uint64_t remainder; + + /* determine integer part */ + + uint64_t res_value = complete_integer_division_u64( + arg1_value, arg2_value, &remainder); + + ASSERT(res_value <= LONG_MAX); + + /* determine fractional part */ + { + uint32_t i = FIXED31_32_BITS_PER_FRACTIONAL_PART; + + do { + remainder <<= 1; + + res_value <<= 1; + + if (remainder >= arg2_value) { + res_value |= 1; + remainder -= arg2_value; + } + } while (--i != 0); + } + + /* round up LSB */ + { + uint64_t summand = (remainder << 1) >= arg2_value; + + ASSERT(res_value <= LLONG_MAX - summand); + + res_value += summand; + } + + res.value = (int64_t)res_value; + + if (arg1_negative ^ arg2_negative) + res.value = -res.value; + + return res; +} + +struct fixed31_32 dal_fixed31_32_from_int_nonconst( + int64_t arg) +{ + struct fixed31_32 res; + + ASSERT((LONG_MIN <= arg) && (arg <= LONG_MAX)); + + res.value = arg << FIXED31_32_BITS_PER_FRACTIONAL_PART; + + return res; +} + +struct fixed31_32 dal_fixed31_32_shl( + struct fixed31_32 arg, + uint8_t shift) +{ + struct fixed31_32 res; + + ASSERT(((arg.value >= 0) && (arg.value <= LLONG_MAX >> shift)) || + ((arg.value < 0) && (arg.value >= LLONG_MIN >> shift))); + + res.value = arg.value << shift; + + return res; +} + +struct fixed31_32 dal_fixed31_32_add( + struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + struct fixed31_32 res; + + ASSERT(((arg1.value >= 0) && (LLONG_MAX - arg1.value >= arg2.value)) || + ((arg1.value < 0) && (LLONG_MIN - arg1.value <= arg2.value))); + + res.value = arg1.value + arg2.value; + + return res; +} + +struct fixed31_32 dal_fixed31_32_sub( + struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + struct fixed31_32 res; + + ASSERT(((arg2.value >= 0) && (LLONG_MIN + arg2.value <= arg1.value)) || + ((arg2.value < 0) && (LLONG_MAX + arg2.value >= arg1.value))); + + res.value = arg1.value - arg2.value; + + return res; +} + +struct fixed31_32 dal_fixed31_32_mul( + struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + struct fixed31_32 res; + + bool arg1_negative = arg1.value < 0; + bool arg2_negative = arg2.value < 0; + + uint64_t arg1_value = arg1_negative ? -arg1.value : arg1.value; + uint64_t arg2_value = arg2_negative ? -arg2.value : arg2.value; + + uint64_t arg1_int = GET_INTEGER_PART(arg1_value); + uint64_t arg2_int = GET_INTEGER_PART(arg2_value); + + uint64_t arg1_fra = GET_FRACTIONAL_PART(arg1_value); + uint64_t arg2_fra = GET_FRACTIONAL_PART(arg2_value); + + uint64_t tmp; + + res.value = arg1_int * arg2_int; + + ASSERT(res.value <= LONG_MAX); + + res.value <<= FIXED31_32_BITS_PER_FRACTIONAL_PART; + + tmp = arg1_int * arg2_fra; + + ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); + + res.value += tmp; + + tmp = arg2_int * arg1_fra; + + ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); + + res.value += tmp; + + tmp = arg1_fra * arg2_fra; + + tmp = (tmp >> FIXED31_32_BITS_PER_FRACTIONAL_PART) + + (tmp >= (uint64_t)dal_fixed31_32_half.value); + + ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); + + res.value += tmp; + + if (arg1_negative ^ arg2_negative) + res.value = -res.value; + + return res; +} + +struct fixed31_32 dal_fixed31_32_sqr( + struct fixed31_32 arg) +{ + struct fixed31_32 res; + + uint64_t arg_value = abs_i64(arg.value); + + uint64_t arg_int = GET_INTEGER_PART(arg_value); + + uint64_t arg_fra = GET_FRACTIONAL_PART(arg_value); + + uint64_t tmp; + + res.value = arg_int * arg_int; + + ASSERT(res.value <= LONG_MAX); + + res.value <<= FIXED31_32_BITS_PER_FRACTIONAL_PART; + + tmp = arg_int * arg_fra; + + ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); + + res.value += tmp; + + ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); + + res.value += tmp; + + tmp = arg_fra * arg_fra; + + tmp = (tmp >> FIXED31_32_BITS_PER_FRACTIONAL_PART) + + (tmp >= (uint64_t)dal_fixed31_32_half.value); + + ASSERT(tmp <= (uint64_t)(LLONG_MAX - res.value)); + + res.value += tmp; + + return res; +} + +struct fixed31_32 dal_fixed31_32_recip( + struct fixed31_32 arg) +{ + /* + * @note + * Good idea to use Newton's method + */ + + ASSERT(arg.value); + + return dal_fixed31_32_from_fraction( + dal_fixed31_32_one.value, + arg.value); +} + +struct fixed31_32 dal_fixed31_32_sinc( + struct fixed31_32 arg) +{ + struct fixed31_32 square; + + struct fixed31_32 res = dal_fixed31_32_one; + + int32_t n = 27; + + struct fixed31_32 arg_norm = arg; + + if (dal_fixed31_32_le( + dal_fixed31_32_two_pi, + dal_fixed31_32_abs(arg))) { + arg_norm = dal_fixed31_32_sub( + arg_norm, + dal_fixed31_32_mul_int( + dal_fixed31_32_two_pi, + (int32_t)div64_s64( + arg_norm.value, + dal_fixed31_32_two_pi.value))); + } + + square = dal_fixed31_32_sqr(arg_norm); + + do { + res = dal_fixed31_32_sub( + dal_fixed31_32_one, + dal_fixed31_32_div_int( + dal_fixed31_32_mul( + square, + res), + n * (n - 1))); + + n -= 2; + } while (n > 2); + + if (arg.value != arg_norm.value) + res = dal_fixed31_32_div( + dal_fixed31_32_mul(res, arg_norm), + arg); + + return res; +} + +struct fixed31_32 dal_fixed31_32_sin( + struct fixed31_32 arg) +{ + return dal_fixed31_32_mul( + arg, + dal_fixed31_32_sinc(arg)); +} + +struct fixed31_32 dal_fixed31_32_cos( + struct fixed31_32 arg) +{ + /* TODO implement argument normalization */ + + const struct fixed31_32 square = dal_fixed31_32_sqr(arg); + + struct fixed31_32 res = dal_fixed31_32_one; + + int32_t n = 26; + + do { + res = dal_fixed31_32_sub( + dal_fixed31_32_one, + dal_fixed31_32_div_int( + dal_fixed31_32_mul( + square, + res), + n * (n - 1))); + + n -= 2; + } while (n != 0); + + return res; +} + +/* + * @brief + * result = exp(arg), + * where abs(arg) < 1 + * + * Calculated as Taylor series. + */ +static struct fixed31_32 fixed31_32_exp_from_taylor_series( + struct fixed31_32 arg) +{ + uint32_t n = 9; + + struct fixed31_32 res = dal_fixed31_32_from_fraction( + n + 2, + n + 1); + /* TODO find correct res */ + + ASSERT(dal_fixed31_32_lt(arg, dal_fixed31_32_one)); + + do + res = dal_fixed31_32_add( + dal_fixed31_32_one, + dal_fixed31_32_div_int( + dal_fixed31_32_mul( + arg, + res), + n)); + while (--n != 1); + + return dal_fixed31_32_add( + dal_fixed31_32_one, + dal_fixed31_32_mul( + arg, + res)); +} + +struct fixed31_32 dal_fixed31_32_exp( + struct fixed31_32 arg) +{ + /* + * @brief + * Main equation is: + * exp(x) = exp(r + m * ln(2)) = (1 << m) * exp(r), + * where m = round(x / ln(2)), r = x - m * ln(2) + */ + + if (dal_fixed31_32_le( + dal_fixed31_32_ln2_div_2, + dal_fixed31_32_abs(arg))) { + int32_t m = dal_fixed31_32_round( + dal_fixed31_32_div( + arg, + dal_fixed31_32_ln2)); + + struct fixed31_32 r = dal_fixed31_32_sub( + arg, + dal_fixed31_32_mul_int( + dal_fixed31_32_ln2, + m)); + + ASSERT(m != 0); + + ASSERT(dal_fixed31_32_lt( + dal_fixed31_32_abs(r), + dal_fixed31_32_one)); + + if (m > 0) + return dal_fixed31_32_shl( + fixed31_32_exp_from_taylor_series(r), + (uint8_t)m); + else + return dal_fixed31_32_div_int( + fixed31_32_exp_from_taylor_series(r), + 1LL << -m); + } else if (arg.value != 0) + return fixed31_32_exp_from_taylor_series(arg); + else + return dal_fixed31_32_one; +} + +struct fixed31_32 dal_fixed31_32_log( + struct fixed31_32 arg) +{ + struct fixed31_32 res = dal_fixed31_32_neg(dal_fixed31_32_one); + /* TODO improve 1st estimation */ + + struct fixed31_32 error; + + ASSERT(arg.value > 0); + /* TODO if arg is negative, return NaN */ + /* TODO if arg is zero, return -INF */ + + do { + struct fixed31_32 res1 = dal_fixed31_32_add( + dal_fixed31_32_sub( + res, + dal_fixed31_32_one), + dal_fixed31_32_div( + arg, + dal_fixed31_32_exp(res))); + + error = dal_fixed31_32_sub( + res, + res1); + + res = res1; + /* TODO determine max_allowed_error based on quality of exp() */ + } while (abs_i64(error.value) > 100ULL); + + return res; +} + +struct fixed31_32 dal_fixed31_32_pow( + struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + return dal_fixed31_32_exp( + dal_fixed31_32_mul( + dal_fixed31_32_log(arg1), + arg2)); +} + +int32_t dal_fixed31_32_floor( + struct fixed31_32 arg) +{ + uint64_t arg_value = abs_i64(arg.value); + + if (arg.value >= 0) + return (int32_t)GET_INTEGER_PART(arg_value); + else + return -(int32_t)GET_INTEGER_PART(arg_value); +} + +int32_t dal_fixed31_32_round( + struct fixed31_32 arg) +{ + uint64_t arg_value = abs_i64(arg.value); + + const int64_t summand = dal_fixed31_32_half.value; + + ASSERT(LLONG_MAX - (int64_t)arg_value >= summand); + + arg_value += summand; + + if (arg.value >= 0) + return (int32_t)GET_INTEGER_PART(arg_value); + else + return -(int32_t)GET_INTEGER_PART(arg_value); +} + +int32_t dal_fixed31_32_ceil( + struct fixed31_32 arg) +{ + uint64_t arg_value = abs_i64(arg.value); + + const int64_t summand = dal_fixed31_32_one.value - + dal_fixed31_32_epsilon.value; + + ASSERT(LLONG_MAX - (int64_t)arg_value >= summand); + + arg_value += summand; + + if (arg.value >= 0) + return (int32_t)GET_INTEGER_PART(arg_value); + else + return -(int32_t)GET_INTEGER_PART(arg_value); +} + +/* this function is a generic helper to translate fixed point value to + * specified integer format that will consist of integer_bits integer part and + * fractional_bits fractional part. For example it is used in + * dal_fixed31_32_u2d19 to receive 2 bits integer part and 19 bits fractional + * part in 32 bits. It is used in hw programming (scaler) + */ + +static inline uint32_t ux_dy( + int64_t value, + uint32_t integer_bits, + uint32_t fractional_bits) +{ + /* 1. create mask of integer part */ + uint32_t result = (1 << integer_bits) - 1; + /* 2. mask out fractional part */ + uint32_t fractional_part = FRACTIONAL_PART_MASK & value; + /* 3. shrink fixed point integer part to be of integer_bits width*/ + result &= GET_INTEGER_PART(value); + /* 4. make space for fractional part to be filled in after integer */ + result <<= fractional_bits; + /* 5. shrink fixed point fractional part to of fractional_bits width*/ + fractional_part >>= FIXED31_32_BITS_PER_FRACTIONAL_PART - fractional_bits; + /* 6. merge the result */ + return result | fractional_part; +} + +uint32_t dal_fixed31_32_u2d19( + struct fixed31_32 arg) +{ + return ux_dy(arg.value, 2, 19); +} + +uint32_t dal_fixed31_32_u0d19( + struct fixed31_32 arg) +{ + return ux_dy(arg.value, 0, 19); +} diff --git a/drivers/gpu/drm/amd/display/dc/basics/fixpt32_32.c b/drivers/gpu/drm/amd/display/dc/basics/fixpt32_32.c new file mode 100644 index 000000000000..4d3aaa82a07b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/fixpt32_32.c @@ -0,0 +1,161 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/fixed32_32.h" + +static uint64_t u64_div(uint64_t n, uint64_t d) +{ + uint32_t i = 0; + uint64_t r; + uint64_t q = div64_u64_rem(n, d, &r); + + for (i = 0; i < 32; ++i) { + uint64_t sbit = q & (1ULL<<63); + + r <<= 1; + r |= sbit ? 1 : 0; + q <<= 1; + if (r >= d) { + r -= d; + q |= 1; + } + } + + if (2*r >= d) + q += 1; + return q; +} + +struct fixed32_32 dal_fixed32_32_from_fraction(uint32_t n, uint32_t d) +{ + struct fixed32_32 fx; + + fx.value = u64_div((uint64_t)n << 32, (uint64_t)d << 32); + return fx; +} + +struct fixed32_32 dal_fixed32_32_add( + struct fixed32_32 lhs, + struct fixed32_32 rhs) +{ + struct fixed32_32 fx = {lhs.value + rhs.value}; + + ASSERT(fx.value >= rhs.value); + return fx; +} + +struct fixed32_32 dal_fixed32_32_add_int(struct fixed32_32 lhs, uint32_t rhs) +{ + struct fixed32_32 fx = {lhs.value + ((uint64_t)rhs << 32)}; + + ASSERT(fx.value >= (uint64_t)rhs << 32); + return fx; + +} +struct fixed32_32 dal_fixed32_32_sub( + struct fixed32_32 lhs, + struct fixed32_32 rhs) +{ + struct fixed32_32 fx; + + ASSERT(lhs.value >= rhs.value); + fx.value = lhs.value - rhs.value; + return fx; +} + +struct fixed32_32 dal_fixed32_32_sub_int(struct fixed32_32 lhs, uint32_t rhs) +{ + struct fixed32_32 fx; + + ASSERT(lhs.value >= ((uint64_t)rhs<<32)); + fx.value = lhs.value - ((uint64_t)rhs<<32); + return fx; +} + +struct fixed32_32 dal_fixed32_32_mul( + struct fixed32_32 lhs, + struct fixed32_32 rhs) +{ + struct fixed32_32 fx; + uint64_t lhs_int = lhs.value>>32; + uint64_t lhs_frac = (uint32_t)lhs.value; + uint64_t rhs_int = rhs.value>>32; + uint64_t rhs_frac = (uint32_t)rhs.value; + uint64_t ahbh = lhs_int * rhs_int; + uint64_t ahbl = lhs_int * rhs_frac; + uint64_t albh = lhs_frac * rhs_int; + uint64_t albl = lhs_frac * rhs_frac; + + ASSERT((ahbh>>32) == 0); + + fx.value = (ahbh<<32) + ahbl + albh + (albl>>32); + return fx; + +} + +struct fixed32_32 dal_fixed32_32_mul_int(struct fixed32_32 lhs, uint32_t rhs) +{ + struct fixed32_32 fx; + uint64_t lhsi = (lhs.value>>32) * (uint64_t)rhs; + uint64_t lhsf; + + ASSERT((lhsi>>32) == 0); + lhsf = ((uint32_t)lhs.value) * (uint64_t)rhs; + ASSERT((lhsi<<32) + lhsf >= lhsf); + fx.value = (lhsi<<32) + lhsf; + return fx; +} + +struct fixed32_32 dal_fixed32_32_div( + struct fixed32_32 lhs, + struct fixed32_32 rhs) +{ + struct fixed32_32 fx; + + fx.value = u64_div(lhs.value, rhs.value); + return fx; +} + +struct fixed32_32 dal_fixed32_32_div_int(struct fixed32_32 lhs, uint32_t rhs) +{ + struct fixed32_32 fx; + + fx.value = u64_div(lhs.value, (uint64_t)rhs << 32); + return fx; +} + +uint32_t dal_fixed32_32_ceil(struct fixed32_32 v) +{ + ASSERT((uint32_t)v.value ? (v.value >> 32) + 1 >= 1 : true); + return (v.value>>32) + ((uint32_t)v.value ? 1 : 0); +} + +uint32_t dal_fixed32_32_round(struct fixed32_32 v) +{ + ASSERT(v.value + (1ULL<<31) >= (1ULL<<31)); + return (v.value + (1ULL<<31))>>32; +} + diff --git a/drivers/gpu/drm/amd/display/dc/basics/grph_object_id.c b/drivers/gpu/drm/amd/display/dc/basics/grph_object_id.c new file mode 100644 index 000000000000..147822545252 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/grph_object_id.c @@ -0,0 +1,75 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/grph_object_id.h" + +static bool dal_graphics_object_id_is_valid(struct graphics_object_id id) +{ + bool rc = true; + + switch (id.type) { + case OBJECT_TYPE_UNKNOWN: + rc = false; + break; + case OBJECT_TYPE_GPU: + case OBJECT_TYPE_ENGINE: + /* do NOT check for id.id == 0 */ + if (id.enum_id == ENUM_ID_UNKNOWN) + rc = false; + break; + default: + if (id.id == 0 || id.enum_id == ENUM_ID_UNKNOWN) + rc = false; + break; + } + + return rc; +} + +bool dal_graphics_object_id_is_equal( + struct graphics_object_id id1, + struct graphics_object_id id2) +{ + if (false == dal_graphics_object_id_is_valid(id1)) { + dm_output_to_console( + "%s: Warning: comparing invalid object 'id1'!\n", __func__); + return false; + } + + if (false == dal_graphics_object_id_is_valid(id2)) { + dm_output_to_console( + "%s: Warning: comparing invalid object 'id2'!\n", __func__); + return false; + } + + if (id1.id == id2.id && id1.enum_id == id2.enum_id + && id1.type == id2.type) + return true; + + return false; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/basics/log_helpers.c b/drivers/gpu/drm/amd/display/dc/basics/log_helpers.c new file mode 100644 index 000000000000..785b943b60ed --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/log_helpers.c @@ -0,0 +1,102 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "core_types.h" +#include "logger.h" +#include "include/logger_interface.h" +#include "dm_helpers.h" + +#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) + +struct dc_signal_type_info { + enum signal_type type; + char name[MAX_NAME_LEN]; +}; + +static const struct dc_signal_type_info signal_type_info_tbl[] = { + {SIGNAL_TYPE_NONE, "NC"}, + {SIGNAL_TYPE_DVI_SINGLE_LINK, "DVI"}, + {SIGNAL_TYPE_DVI_DUAL_LINK, "DDVI"}, + {SIGNAL_TYPE_HDMI_TYPE_A, "HDMIA"}, + {SIGNAL_TYPE_LVDS, "LVDS"}, + {SIGNAL_TYPE_RGB, "VGA"}, + {SIGNAL_TYPE_DISPLAY_PORT, "DP"}, + {SIGNAL_TYPE_DISPLAY_PORT_MST, "MST"}, + {SIGNAL_TYPE_EDP, "eDP"}, + {SIGNAL_TYPE_VIRTUAL, "Virtual"} +}; + +void dc_conn_log(struct dc_context *ctx, + const struct dc_link *link, + uint8_t *hex_data, + int hex_data_count, + enum dc_log_type event, + const char *msg, + ...) +{ + int i; + va_list args; + struct log_entry entry = { 0 }; + enum signal_type signal; + + if (link->local_sink) + signal = link->local_sink->sink_signal; + else + signal = link->connector_signal; + + if (link->type == dc_connection_mst_branch) + signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + + dm_logger_open(ctx->logger, &entry, event); + + for (i = 0; i < NUM_ELEMENTS(signal_type_info_tbl); i++) + if (signal == signal_type_info_tbl[i].type) + break; + + dm_logger_append(&entry, "[%s][ConnIdx:%d] ", + signal_type_info_tbl[i].name, + link->link_index); + + va_start(args, msg); + entry.buf_offset += dm_log_to_buffer( + &entry.buf[entry.buf_offset], + LOG_MAX_LINE_SIZE - entry.buf_offset, + msg, args); + + if (entry.buf[strlen(entry.buf) - 1] == '\n') { + entry.buf[strlen(entry.buf) - 1] = '\0'; + entry.buf_offset--; + } + + if (hex_data) + for (i = 0; i < hex_data_count; i++) + dm_logger_append(&entry, "%2.2X ", hex_data[i]); + + dm_logger_append(&entry, "^\n"); + dm_helpers_dc_conn_log(ctx, &entry, event); + dm_logger_close(&entry); + + va_end(args); +} diff --git a/drivers/gpu/drm/amd/display/dc/basics/logger.c b/drivers/gpu/drm/amd/display/dc/basics/logger.c new file mode 100644 index 000000000000..e04e8ecd4874 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/logger.c @@ -0,0 +1,397 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" +#include "include/logger_interface.h" +#include "logger.h" + + +#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) + +static const struct dc_log_type_info log_type_info_tbl[] = { + {LOG_ERROR, "Error"}, + {LOG_WARNING, "Warning"}, + {LOG_DEBUG, "Debug"}, + {LOG_DC, "DC_Interface"}, + {LOG_SURFACE, "Surface"}, + {LOG_HW_HOTPLUG, "HW_Hotplug"}, + {LOG_HW_LINK_TRAINING, "HW_LKTN"}, + {LOG_HW_SET_MODE, "HW_Mode"}, + {LOG_HW_RESUME_S3, "HW_Resume"}, + {LOG_HW_AUDIO, "HW_Audio"}, + {LOG_HW_HPD_IRQ, "HW_HPDIRQ"}, + {LOG_MST, "MST"}, + {LOG_SCALER, "Scaler"}, + {LOG_BIOS, "BIOS"}, + {LOG_BANDWIDTH_CALCS, "BWCalcs"}, + {LOG_BANDWIDTH_VALIDATION, "BWValidation"}, + {LOG_I2C_AUX, "I2C_AUX"}, + {LOG_SYNC, "Sync"}, + {LOG_BACKLIGHT, "Backlight"}, + {LOG_FEATURE_OVERRIDE, "Override"}, + {LOG_DETECTION_EDID_PARSER, "Edid"}, + {LOG_DETECTION_DP_CAPS, "DP_Caps"}, + {LOG_RESOURCE, "Resource"}, + {LOG_DML, "DML"}, + {LOG_EVENT_MODE_SET, "Mode"}, + {LOG_EVENT_DETECTION, "Detect"}, + {LOG_EVENT_LINK_TRAINING, "LKTN"}, + {LOG_EVENT_LINK_LOSS, "LinkLoss"}, + {LOG_EVENT_UNDERFLOW, "Underflow"}, + {LOG_IF_TRACE, "InterfaceTrace"}, + {LOG_DTN, "DTN"} +}; + + +/* ----------- Object init and destruction ----------- */ +static bool construct(struct dc_context *ctx, struct dal_logger *logger, + uint32_t log_mask) +{ + /* malloc buffer and init offsets */ + logger->log_buffer_size = DAL_LOGGER_BUFFER_MAX_SIZE; + logger->log_buffer = (char *)kzalloc(logger->log_buffer_size * sizeof(char), + GFP_KERNEL); + + if (!logger->log_buffer) + return false; + + /* Initialize both offsets to start of buffer (empty) */ + logger->buffer_read_offset = 0; + logger->buffer_write_offset = 0; + + logger->open_count = 0; + + logger->flags.bits.ENABLE_CONSOLE = 1; + logger->flags.bits.ENABLE_BUFFER = 0; + + logger->ctx = ctx; + + logger->mask = log_mask; + + return true; +} + +static void destruct(struct dal_logger *logger) +{ + if (logger->log_buffer) { + kfree(logger->log_buffer); + logger->log_buffer = NULL; + } +} + +struct dal_logger *dal_logger_create(struct dc_context *ctx, uint32_t log_mask) +{ + /* malloc struct */ + struct dal_logger *logger = kzalloc(sizeof(struct dal_logger), + GFP_KERNEL); + + if (!logger) + return NULL; + if (!construct(ctx, logger, log_mask)) { + kfree(logger); + return NULL; + } + + return logger; +} + +uint32_t dal_logger_destroy(struct dal_logger **logger) +{ + if (logger == NULL || *logger == NULL) + return 1; + destruct(*logger); + kfree(*logger); + *logger = NULL; + + return 0; +} + +/* ------------------------------------------------------------------------ */ + + +static bool dal_logger_should_log( + struct dal_logger *logger, + enum dc_log_type log_type) +{ + if (logger->mask & (1 << log_type)) + return true; + + return false; +} + +static void log_to_debug_console(struct log_entry *entry) +{ + struct dal_logger *logger = entry->logger; + + if (logger->flags.bits.ENABLE_CONSOLE == 0) + return; + + if (entry->buf_offset) { + switch (entry->type) { + case LOG_ERROR: + dm_error("%s", entry->buf); + break; + default: + dm_output_to_console("%s", entry->buf); + break; + } + } +} + +/* Print everything unread existing in log_buffer to debug console*/ +void dm_logger_flush_buffer(struct dal_logger *logger, bool should_warn) +{ + char *string_start = &logger->log_buffer[logger->buffer_read_offset]; + + if (should_warn) + dm_output_to_console( + "---------------- FLUSHING LOG BUFFER ----------------\n"); + while (logger->buffer_read_offset < logger->buffer_write_offset) { + + if (logger->log_buffer[logger->buffer_read_offset] == '\0') { + dm_output_to_console("%s", string_start); + string_start = logger->log_buffer + logger->buffer_read_offset + 1; + } + logger->buffer_read_offset++; + } + if (should_warn) + dm_output_to_console( + "-------------- END FLUSHING LOG BUFFER --------------\n\n"); +} + +static void log_to_internal_buffer(struct log_entry *entry) +{ + + uint32_t size = entry->buf_offset; + struct dal_logger *logger = entry->logger; + + if (logger->flags.bits.ENABLE_BUFFER == 0) + return; + + if (logger->log_buffer == NULL) + return; + + if (size > 0 && size < logger->log_buffer_size) { + + int buffer_space = logger->log_buffer_size - + logger->buffer_write_offset; + + if (logger->buffer_write_offset == logger->buffer_read_offset) { + /* Buffer is empty, start writing at beginning */ + buffer_space = logger->log_buffer_size; + logger->buffer_write_offset = 0; + logger->buffer_read_offset = 0; + } + + if (buffer_space > size) { + /* No wrap around, copy 'size' bytes + * from 'entry->buf' to 'log_buffer' + */ + memmove(logger->log_buffer + + logger->buffer_write_offset, + entry->buf, size); + logger->buffer_write_offset += size; + + } else { + /* Not enough room remaining, we should flush + * existing logs */ + + /* Flush existing unread logs to console */ + dm_logger_flush_buffer(logger, true); + + /* Start writing to beginning of buffer */ + memmove(logger->log_buffer, entry->buf, size); + logger->buffer_write_offset = size; + logger->buffer_read_offset = 0; + } + + } +} + +static void log_heading(struct log_entry *entry) +{ + int j; + + for (j = 0; j < NUM_ELEMENTS(log_type_info_tbl); j++) { + + const struct dc_log_type_info *info = &log_type_info_tbl[j]; + + if (info->type == entry->type) + dm_logger_append(entry, "[%s]\t", info->name); + } +} + +static void append_entry( + struct log_entry *entry, + char *buffer, + uint32_t buf_size) +{ + if (!entry->buf || + entry->buf_offset + buf_size > entry->max_buf_bytes + ) { + BREAK_TO_DEBUGGER(); + return; + } + + /* Todo: check if off by 1 byte due to \0 anywhere */ + memmove(entry->buf + entry->buf_offset, buffer, buf_size); + entry->buf_offset += buf_size; +} + +/* ------------------------------------------------------------------------ */ + +/* Warning: Be careful that 'msg' is null terminated and the total size is + * less than DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE (256) including '\0' + */ +void dm_logger_write( + struct dal_logger *logger, + enum dc_log_type log_type, + const char *msg, + ...) +{ + if (logger && dal_logger_should_log(logger, log_type)) { + uint32_t size; + va_list args; + char buffer[LOG_MAX_LINE_SIZE]; + struct log_entry entry; + + va_start(args, msg); + + entry.logger = logger; + + entry.buf = buffer; + + entry.buf_offset = 0; + entry.max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char); + + entry.type = log_type; + + log_heading(&entry); + + size = dm_log_to_buffer( + buffer, LOG_MAX_LINE_SIZE - 1, msg, args); + + buffer[entry.buf_offset + size] = '\0'; + entry.buf_offset += size + 1; + + /* --Flush log_entry buffer-- */ + /* print to kernel console */ + log_to_debug_console(&entry); + /* log internally for dsat */ + log_to_internal_buffer(&entry); + + va_end(args); + } +} + +/* Same as dm_logger_write, except without open() and close(), which must + * be done separately. + */ +void dm_logger_append( + struct log_entry *entry, + const char *msg, + ...) +{ + struct dal_logger *logger; + + if (!entry) { + BREAK_TO_DEBUGGER(); + return; + } + + logger = entry->logger; + + if (logger && logger->open_count > 0 && + dal_logger_should_log(logger, entry->type)) { + + uint32_t size; + va_list args; + char buffer[LOG_MAX_LINE_SIZE]; + + va_start(args, msg); + + size = dm_log_to_buffer( + buffer, LOG_MAX_LINE_SIZE, msg, args); + + if (size < LOG_MAX_LINE_SIZE - 1) { + append_entry(entry, buffer, size); + } else { + append_entry(entry, "LOG_ERROR, line too long\n", 27); + } + + va_end(args); + } +} + +void dm_logger_open( + struct dal_logger *logger, + struct log_entry *entry, /* out */ + enum dc_log_type log_type) +{ + if (!entry) { + BREAK_TO_DEBUGGER(); + return; + } + + entry->type = log_type; + entry->logger = logger; + + entry->buf = kzalloc(DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char), + GFP_KERNEL); + + entry->buf_offset = 0; + entry->max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char); + + logger->open_count++; + + log_heading(entry); +} + +void dm_logger_close(struct log_entry *entry) +{ + struct dal_logger *logger = entry->logger; + + if (logger && logger->open_count > 0) { + logger->open_count--; + } else { + BREAK_TO_DEBUGGER(); + goto cleanup; + } + + /* --Flush log_entry buffer-- */ + /* print to kernel console */ + log_to_debug_console(entry); + /* log internally for dsat */ + log_to_internal_buffer(entry); + + /* TODO: Write end heading */ + +cleanup: + if (entry->buf) { + kfree(entry->buf); + entry->buf = NULL; + entry->buf_offset = 0; + entry->max_buf_bytes = 0; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/basics/logger.h b/drivers/gpu/drm/amd/display/dc/basics/logger.h new file mode 100644 index 000000000000..09722f0f8aa3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/logger.h @@ -0,0 +1,30 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_LOGGER_H__ +#define __DAL_LOGGER_H__ + + +#endif /* __DAL_LOGGER_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c new file mode 100644 index 000000000000..217b8f1f7bf6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c @@ -0,0 +1,307 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/vector.h" + +bool dal_vector_construct( + struct vector *vector, + struct dc_context *ctx, + uint32_t capacity, + uint32_t struct_size) +{ + vector->container = NULL; + + if (!struct_size || !capacity) { + /* Container must be non-zero size*/ + BREAK_TO_DEBUGGER(); + return false; + } + + vector->container = kzalloc(struct_size * capacity, GFP_KERNEL); + if (vector->container == NULL) + return false; + vector->capacity = capacity; + vector->struct_size = struct_size; + vector->count = 0; + vector->ctx = ctx; + return true; +} + +bool dal_vector_presized_costruct( + struct vector *vector, + struct dc_context *ctx, + uint32_t count, + void *initial_value, + uint32_t struct_size) +{ + uint32_t i; + + vector->container = NULL; + + if (!struct_size || !count) { + /* Container must be non-zero size*/ + BREAK_TO_DEBUGGER(); + return false; + } + + vector->container = kzalloc(struct_size * count, GFP_KERNEL); + + if (vector->container == NULL) + return false; + + /* If caller didn't supply initial value then the default + * of all zeros is expected, which is exactly what dal_alloc() + * initialises the memory to. */ + if (NULL != initial_value) { + for (i = 0; i < count; ++i) + memmove( + vector->container + i * struct_size, + initial_value, + struct_size); + } + + vector->capacity = count; + vector->struct_size = struct_size; + vector->count = count; + return true; +} + +struct vector *dal_vector_presized_create( + struct dc_context *ctx, + uint32_t size, + void *initial_value, + uint32_t struct_size) +{ + struct vector *vector = kzalloc(sizeof(struct vector), GFP_KERNEL); + + if (vector == NULL) + return NULL; + + if (dal_vector_presized_costruct( + vector, ctx, size, initial_value, struct_size)) + return vector; + + BREAK_TO_DEBUGGER(); + kfree(vector); + return NULL; +} + +struct vector *dal_vector_create( + struct dc_context *ctx, + uint32_t capacity, + uint32_t struct_size) +{ + struct vector *vector = kzalloc(sizeof(struct vector), GFP_KERNEL); + + if (vector == NULL) + return NULL; + + if (dal_vector_construct(vector, ctx, capacity, struct_size)) + return vector; + + BREAK_TO_DEBUGGER(); + kfree(vector); + return NULL; +} + +void dal_vector_destruct( + struct vector *vector) +{ + kfree(vector->container); + vector->count = 0; + vector->capacity = 0; +} + +void dal_vector_destroy( + struct vector **vector) +{ + if (vector == NULL || *vector == NULL) + return; + dal_vector_destruct(*vector); + kfree(*vector); + *vector = NULL; +} + +uint32_t dal_vector_get_count( + const struct vector *vector) +{ + return vector->count; +} + +void *dal_vector_at_index( + const struct vector *vector, + uint32_t index) +{ + if (vector->container == NULL || index >= vector->count) + return NULL; + return vector->container + (index * vector->struct_size); +} + +bool dal_vector_remove_at_index( + struct vector *vector, + uint32_t index) +{ + if (index >= vector->count) + return false; + + if (index != vector->count - 1) + memmove( + vector->container + (index * vector->struct_size), + vector->container + ((index + 1) * vector->struct_size), + (vector->count - index - 1) * vector->struct_size); + vector->count -= 1; + + return true; +} + +void dal_vector_set_at_index( + const struct vector *vector, + const void *what, + uint32_t index) +{ + void *where = dal_vector_at_index(vector, index); + + if (!where) { + BREAK_TO_DEBUGGER(); + return; + } + memmove( + where, + what, + vector->struct_size); +} + +static inline uint32_t calc_increased_capacity( + uint32_t old_capacity) +{ + return old_capacity * 2; +} + +bool dal_vector_insert_at( + struct vector *vector, + const void *what, + uint32_t position) +{ + uint8_t *insert_address; + + if (vector->count == vector->capacity) { + if (!dal_vector_reserve( + vector, + calc_increased_capacity(vector->capacity))) + return false; + } + + insert_address = vector->container + (vector->struct_size * position); + + if (vector->count && position < vector->count) + memmove( + insert_address + vector->struct_size, + insert_address, + vector->struct_size * (vector->count - position)); + + memmove( + insert_address, + what, + vector->struct_size); + + vector->count++; + + return true; +} + +bool dal_vector_append( + struct vector *vector, + const void *item) +{ + return dal_vector_insert_at(vector, item, vector->count); +} + +struct vector *dal_vector_clone( + const struct vector *vector) +{ + struct vector *vec_cloned; + uint32_t count; + + /* create new vector */ + count = dal_vector_get_count(vector); + + if (count == 0) + /* when count is 0 we still want to create clone of the vector + */ + vec_cloned = dal_vector_create( + vector->ctx, + vector->capacity, + vector->struct_size); + else + /* Call "presized create" version, independently of how the + * original vector was created. + * The owner of original vector must know how to treat the new + * vector - as "presized" or as "regular". + * But from vector point of view it doesn't matter. */ + vec_cloned = dal_vector_presized_create(vector->ctx, count, + NULL,/* no initial value */ + vector->struct_size); + + if (NULL == vec_cloned) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + /* copy vector's data */ + memmove(vec_cloned->container, vector->container, + vec_cloned->struct_size * vec_cloned->capacity); + + return vec_cloned; +} + +uint32_t dal_vector_capacity(const struct vector *vector) +{ + return vector->capacity; +} + +bool dal_vector_reserve(struct vector *vector, uint32_t capacity) +{ + void *new_container; + + if (capacity <= vector->capacity) + return true; + + new_container = krealloc(vector->container, + capacity * vector->struct_size, GFP_KERNEL); + + if (new_container) { + vector->container = new_container; + vector->capacity = capacity; + return true; + } + + return false; +} + +void dal_vector_clear(struct vector *vector) +{ + vector->count = 0; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/Makefile b/drivers/gpu/drm/amd/display/dc/bios/Makefile new file mode 100644 index 000000000000..6ec815dce9cc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the 'bios' sub-component of DAL. +# It provides the parsing and executing controls for atom bios image. + +BIOS = bios_parser.o bios_parser_interface.o bios_parser_helper.o command_table.o command_table_helper.o bios_parser_common.o + +BIOS += command_table2.o command_table_helper2.o bios_parser2.o + +AMD_DAL_BIOS = $(addprefix $(AMDDALPATH)/dc/bios/,$(BIOS)) + +AMD_DISPLAY_FILES += $(AMD_DAL_BIOS) + +############################################################################### +# DCE 8x +############################################################################### +# All DCE8.x are derived from DCE8.0, so 8.0 MUST be defined if ANY of +# DCE8.x is compiled. +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce80/command_table_helper_dce80.o + +############################################################################### +# DCE 11x +############################################################################### +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce110/command_table_helper_dce110.o + +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce112/command_table_helper_dce112.o + +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce112/command_table_helper2_dce112.o diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c new file mode 100644 index 000000000000..aaaebd06d7ee --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -0,0 +1,3871 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "dc_bios_types.h" +#include "include/gpio_service_interface.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/bios_parser_interface.h" +#include "include/i2caux_interface.h" +#include "include/logger_interface.h" + +#include "command_table.h" +#include "bios_parser_helper.h" +#include "command_table_helper.h" +#include "bios_parser.h" +#include "bios_parser_types_internal.h" +#include "bios_parser_interface.h" + +#include "bios_parser_common.h" +/* TODO remove - only needed for default i2c speed */ +#include "dc.h" + +#define THREE_PERCENT_OF_10000 300 + +#define LAST_RECORD_TYPE 0xff + +/* GUID to validate external display connection info table (aka OPM module) */ +static const uint8_t ext_display_connection_guid[NUMBER_OF_UCHAR_FOR_GUID] = { + 0x91, 0x6E, 0x57, 0x09, + 0x3F, 0x6D, 0xD2, 0x11, + 0x39, 0x8E, 0x00, 0xA0, + 0xC9, 0x69, 0x72, 0x3B}; + +#define DATA_TABLES(table) (bp->master_data_tbl->ListOfDataTables.table) + +static void get_atom_data_table_revision( + ATOM_COMMON_TABLE_HEADER *atom_data_tbl, + struct atom_data_revision *tbl_revision); +static uint32_t get_dst_number_from_object(struct bios_parser *bp, + ATOM_OBJECT *object); +static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, + uint16_t **id_list); +static uint32_t get_dest_obj_list(struct bios_parser *bp, + ATOM_OBJECT *object, uint16_t **id_list); +static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, + struct graphics_object_id id); +static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, + ATOM_I2C_RECORD *record, + struct graphics_object_i2c_info *info); +static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + ATOM_OBJECT *object); +static struct device_id device_type_from_device_id(uint16_t device_id); +static uint32_t signal_to_ss_id(enum as_signal_type signal); +static uint32_t get_support_mask_for_device_id(struct device_id device_id); +static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + struct bios_parser *bp, + ATOM_OBJECT *object); + +#define BIOS_IMAGE_SIZE_OFFSET 2 +#define BIOS_IMAGE_SIZE_UNIT 512 + +/*****************************************************************************/ +static bool bios_parser_construct( + struct bios_parser *bp, + struct bp_init_data *init, + enum dce_version dce_version); + +static uint8_t bios_parser_get_connectors_number( + struct dc_bios *dcb); + +static enum bp_result bios_parser_get_embedded_panel_info( + struct dc_bios *dcb, + struct embedded_panel_info *info); + +/*****************************************************************************/ + +struct dc_bios *bios_parser_create( + struct bp_init_data *init, + enum dce_version dce_version) +{ + struct bios_parser *bp = NULL; + + bp = kzalloc(sizeof(struct bios_parser), GFP_KERNEL); + if (!bp) + return NULL; + + if (bios_parser_construct(bp, init, dce_version)) + return &bp->base; + + kfree(bp); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void destruct(struct bios_parser *bp) +{ + kfree(bp->base.bios_local_image); + kfree(bp->base.integrated_info); +} + +static void bios_parser_destroy(struct dc_bios **dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(*dcb); + + if (!bp) { + BREAK_TO_DEBUGGER(); + return; + } + + destruct(bp); + + kfree(bp); + *dcb = NULL; +} + +static uint8_t get_number_of_objects(struct bios_parser *bp, uint32_t offset) +{ + ATOM_OBJECT_TABLE *table; + + uint32_t object_table_offset = bp->object_info_tbl_offset + offset; + + table = GET_IMAGE(ATOM_OBJECT_TABLE, object_table_offset); + + if (!table) + return 0; + else + return table->ucNumberOfObjects; +} + +static uint8_t bios_parser_get_connectors_number(struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + return get_number_of_objects(bp, + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset)); +} + +static struct graphics_object_id bios_parser_get_encoder_id( + struct dc_bios *dcb, + uint32_t i) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct graphics_object_id object_id = dal_graphics_object_id_init( + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); + + uint32_t encoder_table_offset = bp->object_info_tbl_offset + + le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); + + ATOM_OBJECT_TABLE *tbl = + GET_IMAGE(ATOM_OBJECT_TABLE, encoder_table_offset); + + if (tbl && tbl->ucNumberOfObjects > i) { + const uint16_t id = le16_to_cpu(tbl->asObjects[i].usObjectID); + + object_id = object_id_from_bios_object_id(id); + } + + return object_id; +} + +static struct graphics_object_id bios_parser_get_connector_id( + struct dc_bios *dcb, + uint8_t i) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct graphics_object_id object_id = dal_graphics_object_id_init( + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); + + uint32_t connector_table_offset = bp->object_info_tbl_offset + + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); + + ATOM_OBJECT_TABLE *tbl = + GET_IMAGE(ATOM_OBJECT_TABLE, connector_table_offset); + + if (tbl && tbl->ucNumberOfObjects > i) { + const uint16_t id = le16_to_cpu(tbl->asObjects[i].usObjectID); + + object_id = object_id_from_bios_object_id(id); + } + + return object_id; +} + +static uint32_t bios_parser_get_dst_number(struct dc_bios *dcb, + struct graphics_object_id id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_OBJECT *object = get_bios_object(bp, id); + + return get_dst_number_from_object(bp, object); +} + +static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *src_object_id) +{ + uint32_t number; + uint16_t *id; + ATOM_OBJECT *object; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!src_object_id) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, object_id); + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + + number = get_src_obj_list(bp, object, &id); + + if (number <= index) + return BP_RESULT_BADINPUT; + + *src_object_id = object_id_from_bios_object_id(id[index]); + + return BP_RESULT_OK; +} + +static enum bp_result bios_parser_get_dst_obj(struct dc_bios *dcb, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *dest_object_id) +{ + uint32_t number; + uint16_t *id; + ATOM_OBJECT *object; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!dest_object_id) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, object_id); + + number = get_dest_obj_list(bp, object, &id); + + if (number <= index) + return BP_RESULT_BADINPUT; + + *dest_object_id = object_id_from_bios_object_id(id[index]); + + return BP_RESULT_OK; +} + +static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_i2c_info *info) +{ + uint32_t offset; + ATOM_OBJECT *object; + ATOM_COMMON_RECORD_HEADER *header; + ATOM_I2C_RECORD *record; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_I2C_RECORD_TYPE == header->ucRecordType + && sizeof(ATOM_I2C_RECORD) <= header->ucRecordSize) { + /* get the I2C info */ + record = (ATOM_I2C_RECORD *) header; + + if (get_gpio_i2c_info(bp, record, info) == BP_RESULT_OK) + return BP_RESULT_OK; + } + + offset += header->ucRecordSize; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result get_voltage_ddc_info_v1(uint8_t *i2c_line, + ATOM_COMMON_TABLE_HEADER *header, + uint8_t *address) +{ + enum bp_result result = BP_RESULT_NORECORD; + ATOM_VOLTAGE_OBJECT_INFO *info = + (ATOM_VOLTAGE_OBJECT_INFO *) address; + + uint8_t *voltage_current_object = (uint8_t *) &info->asVoltageObj[0]; + + while ((address + le16_to_cpu(header->usStructureSize)) > voltage_current_object) { + ATOM_VOLTAGE_OBJECT *object = + (ATOM_VOLTAGE_OBJECT *) voltage_current_object; + + if ((object->ucVoltageType == SET_VOLTAGE_INIT_MODE) && + (object->ucVoltageType & + VOLTAGE_CONTROLLED_BY_I2C_MASK)) { + + *i2c_line = object->asControl.ucVoltageControlI2cLine + ^ 0x90; + result = BP_RESULT_OK; + break; + } + + voltage_current_object += object->ucSize; + } + return result; +} + +static enum bp_result get_voltage_ddc_info_v3(uint8_t *i2c_line, + uint32_t index, + ATOM_COMMON_TABLE_HEADER *header, + uint8_t *address) +{ + enum bp_result result = BP_RESULT_NORECORD; + ATOM_VOLTAGE_OBJECT_INFO_V3_1 *info = + (ATOM_VOLTAGE_OBJECT_INFO_V3_1 *) address; + + uint8_t *voltage_current_object = + (uint8_t *) (&(info->asVoltageObj[0])); + + while ((address + le16_to_cpu(header->usStructureSize)) > voltage_current_object) { + ATOM_I2C_VOLTAGE_OBJECT_V3 *object = + (ATOM_I2C_VOLTAGE_OBJECT_V3 *) voltage_current_object; + + if (object->sHeader.ucVoltageMode == + ATOM_INIT_VOLTAGE_REGULATOR) { + if (object->sHeader.ucVoltageType == index) { + *i2c_line = object->ucVoltageControlI2cLine + ^ 0x90; + result = BP_RESULT_OK; + break; + } + } + + voltage_current_object += le16_to_cpu(object->sHeader.usSize); + } + return result; +} + +static enum bp_result bios_parser_get_thermal_ddc_info( + struct dc_bios *dcb, + uint32_t i2c_channel_id, + struct graphics_object_i2c_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_I2C_ID_CONFIG_ACCESS *config; + ATOM_I2C_RECORD record; + + if (!info) + return BP_RESULT_BADINPUT; + + config = (ATOM_I2C_ID_CONFIG_ACCESS *) &i2c_channel_id; + + record.sucI2cId.bfHW_Capable = config->sbfAccess.bfHW_Capable; + record.sucI2cId.bfI2C_LineMux = config->sbfAccess.bfI2C_LineMux; + record.sucI2cId.bfHW_EngineID = config->sbfAccess.bfHW_EngineID; + + return get_gpio_i2c_info(bp, &record, info); +} + +static enum bp_result bios_parser_get_voltage_ddc_info(struct dc_bios *dcb, + uint32_t index, + struct graphics_object_i2c_info *info) +{ + uint8_t i2c_line = 0; + enum bp_result result = BP_RESULT_NORECORD; + uint8_t *voltage_info_address; + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision revision = {0}; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!DATA_TABLES(VoltageObjectInfo)) + return result; + + voltage_info_address = bios_get_image(&bp->base, DATA_TABLES(VoltageObjectInfo), sizeof(ATOM_COMMON_TABLE_HEADER)); + + header = (ATOM_COMMON_TABLE_HEADER *) voltage_info_address; + + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 1: + case 2: + result = get_voltage_ddc_info_v1(&i2c_line, header, + voltage_info_address); + break; + case 3: + if (revision.minor != 1) + break; + result = get_voltage_ddc_info_v3(&i2c_line, index, header, + voltage_info_address); + break; + } + + if (result == BP_RESULT_OK) + result = bios_parser_get_thermal_ddc_info(dcb, + i2c_line, info); + + return result; +} + +/* TODO: temporary commented out to suppress 'defined but not used' warning */ +#if 0 +static enum bp_result bios_parser_get_ddc_info_for_i2c_line( + struct bios_parser *bp, + uint8_t i2c_line, struct graphics_object_i2c_info *info) +{ + uint32_t offset; + ATOM_OBJECT *object; + ATOM_OBJECT_TABLE *table; + uint32_t i; + + if (!info) + return BP_RESULT_BADINPUT; + + offset = le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); + + offset += bp->object_info_tbl_offset; + + table = GET_IMAGE(ATOM_OBJECT_TABLE, offset); + + if (!table) + return BP_RESULT_BADBIOSTABLE; + + for (i = 0; i < table->ucNumberOfObjects; i++) { + object = &table->asObjects[i]; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + ATOM_COMMON_RECORD_HEADER *header = + GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + offset += header->ucRecordSize; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_I2C_RECORD_TYPE == header->ucRecordType + && sizeof(ATOM_I2C_RECORD) <= + header->ucRecordSize) { + ATOM_I2C_RECORD *record = + (ATOM_I2C_RECORD *) header; + + if (i2c_line != record->sucI2cId.bfI2C_LineMux) + continue; + + /* get the I2C info */ + if (get_gpio_i2c_info(bp, record, info) == + BP_RESULT_OK) + return BP_RESULT_OK; + } + } + } + + return BP_RESULT_NORECORD; +} +#endif + +static enum bp_result bios_parser_get_hpd_info(struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_hpd_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_OBJECT *object; + ATOM_HPD_INT_RECORD *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + record = get_hpd_record(bp, object); + + if (record != NULL) { + info->hpd_int_gpio_uid = record->ucHPDIntGPIOID; + info->hpd_active = record->ucPlugged_PinState; + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result bios_parser_get_device_tag_record( + struct bios_parser *bp, + ATOM_OBJECT *object, + ATOM_CONNECTOR_DEVICE_TAG_RECORD **record) +{ + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + offset += header->ucRecordSize; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE != + header->ucRecordType) + continue; + + if (sizeof(ATOM_CONNECTOR_DEVICE_TAG) > header->ucRecordSize) + continue; + + *record = (ATOM_CONNECTOR_DEVICE_TAG_RECORD *) header; + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result bios_parser_get_device_tag( + struct dc_bios *dcb, + struct graphics_object_id connector_object_id, + uint32_t device_tag_index, + struct connector_device_tag_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_OBJECT *object; + ATOM_CONNECTOR_DEVICE_TAG_RECORD *record = NULL; + ATOM_CONNECTOR_DEVICE_TAG *device_tag; + + if (!info) + return BP_RESULT_BADINPUT; + + /* getBiosObject will return MXM object */ + object = get_bios_object(bp, connector_object_id); + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + + if (bios_parser_get_device_tag_record(bp, object, &record) + != BP_RESULT_OK) + return BP_RESULT_NORECORD; + + if (device_tag_index >= record->ucNumberOfDevice) + return BP_RESULT_NORECORD; + + device_tag = &record->asDeviceTag[device_tag_index]; + + info->acpi_device = le32_to_cpu(device_tag->ulACPIDeviceEnum); + info->dev_id = + device_type_from_device_id(le16_to_cpu(device_tag->usDeviceID)); + + return BP_RESULT_OK; +} + +static enum bp_result get_firmware_info_v1_4( + struct bios_parser *bp, + struct dc_firmware_info *info); +static enum bp_result get_firmware_info_v2_1( + struct bios_parser *bp, + struct dc_firmware_info *info); +static enum bp_result get_firmware_info_v2_2( + struct bios_parser *bp, + struct dc_firmware_info *info); + +static enum bp_result bios_parser_get_firmware_info( + struct dc_bios *dcb, + struct dc_firmware_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + enum bp_result result = BP_RESULT_BADBIOSTABLE; + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision revision; + + if (info && DATA_TABLES(FirmwareInfo)) { + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(FirmwareInfo)); + get_atom_data_table_revision(header, &revision); + switch (revision.major) { + case 1: + switch (revision.minor) { + case 4: + result = get_firmware_info_v1_4(bp, info); + break; + default: + break; + } + break; + + case 2: + switch (revision.minor) { + case 1: + result = get_firmware_info_v2_1(bp, info); + break; + case 2: + result = get_firmware_info_v2_2(bp, info); + break; + default: + break; + } + break; + default: + break; + } + } + + return result; +} + +static enum bp_result get_firmware_info_v1_4( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + ATOM_FIRMWARE_INFO_V1_4 *firmware_info = + GET_IMAGE(ATOM_FIRMWARE_INFO_V1_4, + DATA_TABLES(FirmwareInfo)); + + if (!info) + return BP_RESULT_BADINPUT; + + if (!firmware_info) + return BP_RESULT_BADBIOSTABLE; + + memset(info, 0, sizeof(*info)); + + /* Pixel clock pll information. We need to convert from 10KHz units into + * KHz units */ + info->pll_info.crystal_frequency = + le16_to_cpu(firmware_info->usReferenceClock) * 10; + info->pll_info.min_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMinPixelClockPLL_Input) * 10; + info->pll_info.max_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMaxPixelClockPLL_Input) * 10; + info->pll_info.min_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMinPixelClockPLL_Output) * 10; + info->pll_info.max_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMaxPixelClockPLL_Output) * 10; + + if (firmware_info->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) + /* Since there is no information on the SS, report conservative + * value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; + + if (firmware_info->usFirmwareCapability.sbfAccess.EngineClockSS_Support) + /* Since there is no information on the SS,report conservative + * value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; + + return BP_RESULT_OK; +} + +static enum bp_result get_ss_info_v3_1( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info); + +static enum bp_result get_firmware_info_v2_1( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + ATOM_FIRMWARE_INFO_V2_1 *firmwareInfo = + GET_IMAGE(ATOM_FIRMWARE_INFO_V2_1, DATA_TABLES(FirmwareInfo)); + struct spread_spectrum_info internalSS; + uint32_t index; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!firmwareInfo) + return BP_RESULT_BADBIOSTABLE; + + memset(info, 0, sizeof(*info)); + + /* Pixel clock pll information. We need to convert from 10KHz units into + * KHz units */ + info->pll_info.crystal_frequency = + le16_to_cpu(firmwareInfo->usCoreReferenceClock) * 10; + info->pll_info.min_input_pxl_clk_pll_frequency = + le16_to_cpu(firmwareInfo->usMinPixelClockPLL_Input) * 10; + info->pll_info.max_input_pxl_clk_pll_frequency = + le16_to_cpu(firmwareInfo->usMaxPixelClockPLL_Input) * 10; + info->pll_info.min_output_pxl_clk_pll_frequency = + le32_to_cpu(firmwareInfo->ulMinPixelClockPLL_Output) * 10; + info->pll_info.max_output_pxl_clk_pll_frequency = + le32_to_cpu(firmwareInfo->ulMaxPixelClockPLL_Output) * 10; + info->default_display_engine_pll_frequency = + le32_to_cpu(firmwareInfo->ulDefaultDispEngineClkFreq) * 10; + info->external_clock_source_frequency_for_dp = + le16_to_cpu(firmwareInfo->usUniphyDPModeExtClkFreq) * 10; + info->min_allowed_bl_level = firmwareInfo->ucMinAllowedBL_Level; + + /* There should be only one entry in the SS info table for Memory Clock + */ + index = 0; + if (firmwareInfo->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_MEMORY_SS, index, &internalSS) == BP_RESULT_OK) { + if (internalSS.spread_spectrum_percentage) { + info->feature.memory_clk_ss_percentage = + internalSS.spread_spectrum_percentage; + if (internalSS.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.memory_clk_ss_percentage; + info->feature.memory_clk_ss_percentage /= 2; + } + } + } + + /* There should be only one entry in the SS info table for Engine Clock + */ + index = 1; + if (firmwareInfo->usFirmwareCapability.sbfAccess.EngineClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_ENGINE_SS, index, &internalSS) == BP_RESULT_OK) { + if (internalSS.spread_spectrum_percentage) { + info->feature.engine_clk_ss_percentage = + internalSS.spread_spectrum_percentage; + if (internalSS.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.engine_clk_ss_percentage; + info->feature.engine_clk_ss_percentage /= 2; + } + } + } + + return BP_RESULT_OK; +} + +static enum bp_result get_firmware_info_v2_2( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + ATOM_FIRMWARE_INFO_V2_2 *firmware_info; + struct spread_spectrum_info internal_ss; + uint32_t index; + + if (!info) + return BP_RESULT_BADINPUT; + + firmware_info = GET_IMAGE(ATOM_FIRMWARE_INFO_V2_2, + DATA_TABLES(FirmwareInfo)); + + if (!firmware_info) + return BP_RESULT_BADBIOSTABLE; + + memset(info, 0, sizeof(*info)); + + /* Pixel clock pll information. We need to convert from 10KHz units into + * KHz units */ + info->pll_info.crystal_frequency = + le16_to_cpu(firmware_info->usCoreReferenceClock) * 10; + info->pll_info.min_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMinPixelClockPLL_Input) * 10; + info->pll_info.max_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMaxPixelClockPLL_Input) * 10; + info->pll_info.min_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMinPixelClockPLL_Output) * 10; + info->pll_info.max_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMaxPixelClockPLL_Output) * 10; + info->default_display_engine_pll_frequency = + le32_to_cpu(firmware_info->ulDefaultDispEngineClkFreq) * 10; + info->external_clock_source_frequency_for_dp = + le16_to_cpu(firmware_info->usUniphyDPModeExtClkFreq) * 10; + + /* There should be only one entry in the SS info table for Memory Clock + */ + index = 0; + if (firmware_info->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_MEMORY_SS, index, &internal_ss) == BP_RESULT_OK) { + if (internal_ss.spread_spectrum_percentage) { + info->feature.memory_clk_ss_percentage = + internal_ss.spread_spectrum_percentage; + if (internal_ss.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.memory_clk_ss_percentage; + info->feature.memory_clk_ss_percentage /= 2; + } + } + } + + /* There should be only one entry in the SS info table for Engine Clock + */ + index = 1; + if (firmware_info->usFirmwareCapability.sbfAccess.EngineClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_ENGINE_SS, index, &internal_ss) == BP_RESULT_OK) { + if (internal_ss.spread_spectrum_percentage) { + info->feature.engine_clk_ss_percentage = + internal_ss.spread_spectrum_percentage; + if (internal_ss.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.engine_clk_ss_percentage; + info->feature.engine_clk_ss_percentage /= 2; + } + } + } + + /* Remote Display */ + info->remote_display_config = firmware_info->ucRemoteDisplayConfig; + + /* Is allowed minimum BL level */ + info->min_allowed_bl_level = firmware_info->ucMinAllowedBL_Level; + /* Used starting from CI */ + info->smu_gpu_pll_output_freq = + (uint32_t) (le32_to_cpu(firmware_info->ulGPUPLL_OutputFreq) * 10); + + return BP_RESULT_OK; +} + +static enum bp_result get_ss_info_v3_1( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + ATOM_ASIC_INTERNAL_SS_INFO_V3 *ss_table_header_include; + ATOM_ASIC_SS_ASSIGNMENT_V3 *tbl; + uint32_t table_size; + uint32_t i; + uint32_t table_index = 0; + + if (!ss_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return BP_RESULT_UNSUPPORTED; + + ss_table_header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V3, + DATA_TABLES(ASIC_InternalSS_Info)); + table_size = + (le16_to_cpu(ss_table_header_include->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V3 *) + &ss_table_header_include->asSpreadSpectrum[0]; + + memset(ss_info, 0, sizeof(struct spread_spectrum_info)); + + for (i = 0; i < table_size; i++) { + if (tbl[i].ucClockIndication != (uint8_t) id) + continue; + + if (table_index != index) { + table_index++; + continue; + } + /* VBIOS introduced new defines for Version 3, same values as + * before, so now use these new ones for Version 3. + * Shouldn't affect field VBIOS's V3 as define values are still + * same. + * #define SS_MODE_V3_CENTRE_SPREAD_MASK 0x01 + * #define SS_MODE_V3_EXTERNAL_SS_MASK 0x02 + + * Old VBIOS defines: + * #define ATOM_SS_CENTRE_SPREAD_MODE_MASK 0x00000001 + * #define ATOM_EXTERNAL_SS_MASK 0x00000002 + */ + + if (SS_MODE_V3_EXTERNAL_SS_MASK & tbl[i].ucSpreadSpectrumMode) + ss_info->type.EXTERNAL = true; + + if (SS_MODE_V3_CENTRE_SPREAD_MASK & tbl[i].ucSpreadSpectrumMode) + ss_info->type.CENTER_MODE = true; + + /* Older VBIOS (in field) always provides SS percentage in 0.01% + * units set Divider to 100 */ + ss_info->spread_percentage_divider = 100; + + /* #define SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK 0x10 */ + if (SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK + & tbl[i].ucSpreadSpectrumMode) + ss_info->spread_percentage_divider = 1000; + + ss_info->type.STEP_AND_DELAY_INFO = false; + /* convert [10KHz] into [KHz] */ + ss_info->target_clock_range = + le32_to_cpu(tbl[i].ulTargetClockRange) * 10; + ss_info->spread_spectrum_percentage = + (uint32_t)le16_to_cpu(tbl[i].usSpreadSpectrumPercentage); + ss_info->spread_spectrum_range = + (uint32_t)(le16_to_cpu(tbl[i].usSpreadRateIn10Hz) * 10); + + return BP_RESULT_OK; + } + return BP_RESULT_NORECORD; +} + +static enum bp_result bios_parser_transmitter_control( + struct dc_bios *dcb, + struct bp_transmitter_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.transmitter_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.transmitter_control(bp, cntl); +} + +static enum bp_result bios_parser_encoder_control( + struct dc_bios *dcb, + struct bp_encoder_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.dig_encoder_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.dig_encoder_control(bp, cntl); +} + +static enum bp_result bios_parser_adjust_pixel_clock( + struct dc_bios *dcb, + struct bp_adjust_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.adjust_display_pll) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.adjust_display_pll(bp, bp_params); +} + +static enum bp_result bios_parser_set_pixel_clock( + struct dc_bios *dcb, + struct bp_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_pixel_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_pixel_clock(bp, bp_params); +} + +static enum bp_result bios_parser_set_dce_clock( + struct dc_bios *dcb, + struct bp_set_dce_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_dce_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_dce_clock(bp, bp_params); +} + +static enum bp_result bios_parser_enable_spread_spectrum_on_ppll( + struct dc_bios *dcb, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_spread_spectrum_on_ppll) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_spread_spectrum_on_ppll( + bp, bp_params, enable); + +} + +static enum bp_result bios_parser_program_crtc_timing( + struct dc_bios *dcb, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_crtc_timing) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_crtc_timing(bp, bp_params); +} + +static enum bp_result bios_parser_program_display_engine_pll( + struct dc_bios *dcb, + struct bp_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.program_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.program_clock(bp, bp_params); + +} + + +static enum bp_result bios_parser_enable_crtc( + struct dc_bios *dcb, + enum controller_id id, + bool enable) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_crtc) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_crtc(bp, id, enable); +} + +static enum bp_result bios_parser_crtc_source_select( + struct dc_bios *dcb, + struct bp_crtc_source_select *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.select_crtc_source) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.select_crtc_source(bp, bp_params); +} + +static enum bp_result bios_parser_enable_disp_power_gating( + struct dc_bios *dcb, + enum controller_id controller_id, + enum bp_pipe_control_action action) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_disp_power_gating) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_disp_power_gating(bp, controller_id, + action); +} + +static bool bios_parser_is_device_id_supported( + struct dc_bios *dcb, + struct device_id id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + uint32_t mask = get_support_mask_for_device_id(id); + + return (le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport) & mask) != 0; +} + +static enum bp_result bios_parser_crt_control( + struct dc_bios *dcb, + enum engine_id engine_id, + bool enable, + uint32_t pixel_clock) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + uint8_t standard; + + if (!bp->cmd_tbl.dac1_encoder_control && + engine_id == ENGINE_ID_DACA) + return BP_RESULT_FAILURE; + if (!bp->cmd_tbl.dac2_encoder_control && + engine_id == ENGINE_ID_DACB) + return BP_RESULT_FAILURE; + /* validate params */ + switch (engine_id) { + case ENGINE_ID_DACA: + case ENGINE_ID_DACB: + break; + default: + /* unsupported engine */ + return BP_RESULT_FAILURE; + } + + standard = ATOM_DAC1_PS2; /* == ATOM_DAC2_PS2 */ + + if (enable) { + if (engine_id == ENGINE_ID_DACA) { + bp->cmd_tbl.dac1_encoder_control(bp, enable, + pixel_clock, standard); + if (bp->cmd_tbl.dac1_output_control != NULL) + bp->cmd_tbl.dac1_output_control(bp, enable); + } else { + bp->cmd_tbl.dac2_encoder_control(bp, enable, + pixel_clock, standard); + if (bp->cmd_tbl.dac2_output_control != NULL) + bp->cmd_tbl.dac2_output_control(bp, enable); + } + } else { + if (engine_id == ENGINE_ID_DACA) { + if (bp->cmd_tbl.dac1_output_control != NULL) + bp->cmd_tbl.dac1_output_control(bp, enable); + bp->cmd_tbl.dac1_encoder_control(bp, enable, + pixel_clock, standard); + } else { + if (bp->cmd_tbl.dac2_output_control != NULL) + bp->cmd_tbl.dac2_output_control(bp, enable); + bp->cmd_tbl.dac2_encoder_control(bp, enable, + pixel_clock, standard); + } + } + + return BP_RESULT_OK; +} + +static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + ATOM_OBJECT *object) +{ + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return NULL; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_HPD_INT_RECORD_TYPE == header->ucRecordType + && sizeof(ATOM_HPD_INT_RECORD) <= header->ucRecordSize) + return (ATOM_HPD_INT_RECORD *) header; + + offset += header->ucRecordSize; + } + + return NULL; +} + +/** + * Get I2C information of input object id + * + * search all records to find the ATOM_I2C_RECORD_TYPE record IR + */ +static ATOM_I2C_RECORD *get_i2c_record( + struct bios_parser *bp, + ATOM_OBJECT *object) +{ + uint32_t offset; + ATOM_COMMON_RECORD_HEADER *record_header; + + if (!object) { + BREAK_TO_DEBUGGER(); + /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!record_header) + return NULL; + + if (LAST_RECORD_TYPE == record_header->ucRecordType || + 0 == record_header->ucRecordSize) + break; + + if (ATOM_I2C_RECORD_TYPE == record_header->ucRecordType && + sizeof(ATOM_I2C_RECORD) <= + record_header->ucRecordSize) { + return (ATOM_I2C_RECORD *)record_header; + } + + offset += record_header->ucRecordSize; + } + + return NULL; +} + +static enum bp_result get_ss_info_from_ss_info_table( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info); +static enum bp_result get_ss_info_from_tbl( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info); +/** + * bios_parser_get_spread_spectrum_info + * Get spread spectrum information from the ASIC_InternalSS_Info(ver 2.1 or + * ver 3.1) or SS_Info table from the VBIOS. Currently ASIC_InternalSS_Info + * ver 2.1 can co-exist with SS_Info table. Expect ASIC_InternalSS_Info ver 3.1, + * there is only one entry for each signal /ss id. However, there is + * no planning of supporting multiple spread Sprectum entry for EverGreen + * @param [in] this + * @param [in] signal, ASSignalType to be converted to info index + * @param [in] index, number of entries that match the converted info index + * @param [out] ss_info, sprectrum information structure, + * @return Bios parser result code + */ +static enum bp_result bios_parser_get_spread_spectrum_info( + struct dc_bios *dcb, + enum as_signal_type signal, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + enum bp_result result = BP_RESULT_UNSUPPORTED; + uint32_t clk_id_ss = 0; + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision tbl_revision; + + if (!ss_info) /* check for bad input */ + return BP_RESULT_BADINPUT; + /* signal translation */ + clk_id_ss = signal_to_ss_id(signal); + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + if (!index) + return get_ss_info_from_ss_info_table(bp, clk_id_ss, + ss_info); + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(ASIC_InternalSS_Info)); + get_atom_data_table_revision(header, &tbl_revision); + + switch (tbl_revision.major) { + case 2: + switch (tbl_revision.minor) { + case 1: + /* there can not be more then one entry for Internal + * SS Info table version 2.1 */ + if (!index) + return get_ss_info_from_tbl(bp, clk_id_ss, + ss_info); + break; + default: + break; + } + break; + + case 3: + switch (tbl_revision.minor) { + case 1: + return get_ss_info_v3_1(bp, clk_id_ss, index, ss_info); + default: + break; + } + break; + default: + break; + } + /* there can not be more then one entry for SS Info table */ + return result; +} + +static enum bp_result get_ss_info_from_internal_ss_info_tbl_V2_1( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *info); + +/** + * get_ss_info_from_table + * Get spread sprectrum information from the ASIC_InternalSS_Info Ver 2.1 or + * SS_Info table from the VBIOS + * There can not be more than 1 entry for ASIC_InternalSS_Info Ver 2.1 or + * SS_Info. + * + * @param this + * @param id, spread sprectrum info index + * @param pSSinfo, sprectrum information structure, + * @return Bios parser result code + */ +static enum bp_result get_ss_info_from_tbl( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info) +{ + if (!ss_info) /* check for bad input, if ss_info is not NULL */ + return BP_RESULT_BADINPUT; + /* for SS_Info table only support DP and LVDS */ + if (id == ASIC_INTERNAL_SS_ON_DP || id == ASIC_INTERNAL_SS_ON_LVDS) + return get_ss_info_from_ss_info_table(bp, id, ss_info); + else + return get_ss_info_from_internal_ss_info_tbl_V2_1(bp, id, + ss_info); +} + +/** + * get_ss_info_from_internal_ss_info_tbl_V2_1 + * Get spread sprectrum information from the ASIC_InternalSS_Info table Ver 2.1 + * from the VBIOS + * There will not be multiple entry for Ver 2.1 + * + * @param id, spread sprectrum info index + * @param pSSinfo, sprectrum information structure, + * @return Bios parser result code + */ +static enum bp_result get_ss_info_from_internal_ss_info_tbl_V2_1( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *info) +{ + enum bp_result result = BP_RESULT_UNSUPPORTED; + ATOM_ASIC_INTERNAL_SS_INFO_V2 *header; + ATOM_ASIC_SS_ASSIGNMENT_V2 *tbl; + uint32_t tbl_size, i; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return result; + + header = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V2, + DATA_TABLES(ASIC_InternalSS_Info)); + + memset(info, 0, sizeof(struct spread_spectrum_info)); + + tbl_size = (le16_to_cpu(header->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V2 *) + &(header->asSpreadSpectrum[0]); + for (i = 0; i < tbl_size; i++) { + result = BP_RESULT_NORECORD; + + if (tbl[i].ucClockIndication != (uint8_t)id) + continue; + + if (ATOM_EXTERNAL_SS_MASK + & tbl[i].ucSpreadSpectrumMode) { + info->type.EXTERNAL = true; + } + if (ATOM_SS_CENTRE_SPREAD_MODE_MASK + & tbl[i].ucSpreadSpectrumMode) { + info->type.CENTER_MODE = true; + } + info->type.STEP_AND_DELAY_INFO = false; + /* convert [10KHz] into [KHz] */ + info->target_clock_range = + le32_to_cpu(tbl[i].ulTargetClockRange) * 10; + info->spread_spectrum_percentage = + (uint32_t)le16_to_cpu(tbl[i].usSpreadSpectrumPercentage); + info->spread_spectrum_range = + (uint32_t)(le16_to_cpu(tbl[i].usSpreadRateIn10Hz) * 10); + result = BP_RESULT_OK; + break; + } + + return result; + +} + +/** + * get_ss_info_from_ss_info_table + * Get spread sprectrum information from the SS_Info table from the VBIOS + * if the pointer to info is NULL, indicate the caller what to know the number + * of entries that matches the id + * for, the SS_Info table, there should not be more than 1 entry match. + * + * @param [in] id, spread sprectrum id + * @param [out] pSSinfo, sprectrum information structure, + * @return Bios parser result code + */ +static enum bp_result get_ss_info_from_ss_info_table( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info) +{ + enum bp_result result = BP_RESULT_UNSUPPORTED; + ATOM_SPREAD_SPECTRUM_INFO *tbl; + ATOM_COMMON_TABLE_HEADER *header; + uint32_t table_size; + uint32_t i; + uint32_t id_local = SS_ID_UNKNOWN; + struct atom_data_revision revision; + + /* exist of the SS_Info table */ + /* check for bad input, pSSinfo can not be NULL */ + if (!DATA_TABLES(SS_Info) || !ss_info) + return result; + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, DATA_TABLES(SS_Info)); + get_atom_data_table_revision(header, &revision); + + tbl = GET_IMAGE(ATOM_SPREAD_SPECTRUM_INFO, DATA_TABLES(SS_Info)); + + if (1 != revision.major || 2 > revision.minor) + return result; + + /* have to convert from Internal_SS format to SS_Info format */ + switch (id) { + case ASIC_INTERNAL_SS_ON_DP: + id_local = SS_ID_DP1; + break; + case ASIC_INTERNAL_SS_ON_LVDS: + { + struct embedded_panel_info panel_info; + + if (bios_parser_get_embedded_panel_info(&bp->base, &panel_info) + == BP_RESULT_OK) + id_local = panel_info.ss_id; + break; + } + default: + break; + } + + if (id_local == SS_ID_UNKNOWN) + return result; + + table_size = (le16_to_cpu(tbl->sHeader.usStructureSize) - + sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); + + for (i = 0; i < table_size; i++) { + if (id_local != (uint32_t)tbl->asSS_Info[i].ucSS_Id) + continue; + + memset(ss_info, 0, sizeof(struct spread_spectrum_info)); + + if (ATOM_EXTERNAL_SS_MASK & + tbl->asSS_Info[i].ucSpreadSpectrumType) + ss_info->type.EXTERNAL = true; + + if (ATOM_SS_CENTRE_SPREAD_MODE_MASK & + tbl->asSS_Info[i].ucSpreadSpectrumType) + ss_info->type.CENTER_MODE = true; + + ss_info->type.STEP_AND_DELAY_INFO = true; + ss_info->spread_spectrum_percentage = + (uint32_t)le16_to_cpu(tbl->asSS_Info[i].usSpreadSpectrumPercentage); + ss_info->step_and_delay_info.step = tbl->asSS_Info[i].ucSS_Step; + ss_info->step_and_delay_info.delay = + tbl->asSS_Info[i].ucSS_Delay; + ss_info->step_and_delay_info.recommended_ref_div = + tbl->asSS_Info[i].ucRecommendedRef_Div; + ss_info->spread_spectrum_range = + (uint32_t)tbl->asSS_Info[i].ucSS_Range * 10000; + + /* there will be only one entry for each display type in SS_info + * table */ + result = BP_RESULT_OK; + break; + } + + return result; +} +static enum bp_result get_embedded_panel_info_v1_2( + struct bios_parser *bp, + struct embedded_panel_info *info); +static enum bp_result get_embedded_panel_info_v1_3( + struct bios_parser *bp, + struct embedded_panel_info *info); + +static enum bp_result bios_parser_get_embedded_panel_info( + struct dc_bios *dcb, + struct embedded_panel_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_COMMON_TABLE_HEADER *hdr; + + if (!DATA_TABLES(LCD_Info)) + return BP_RESULT_FAILURE; + + hdr = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, DATA_TABLES(LCD_Info)); + + if (!hdr) + return BP_RESULT_BADBIOSTABLE; + + switch (hdr->ucTableFormatRevision) { + case 1: + switch (hdr->ucTableContentRevision) { + case 0: + case 1: + case 2: + return get_embedded_panel_info_v1_2(bp, info); + case 3: + return get_embedded_panel_info_v1_3(bp, info); + default: + break; + } + default: + break; + } + + return BP_RESULT_FAILURE; +} + +static enum bp_result get_embedded_panel_info_v1_2( + struct bios_parser *bp, + struct embedded_panel_info *info) +{ + ATOM_LVDS_INFO_V12 *lvds; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(LVDS_Info)) + return BP_RESULT_UNSUPPORTED; + + lvds = + GET_IMAGE(ATOM_LVDS_INFO_V12, DATA_TABLES(LVDS_Info)); + + if (!lvds) + return BP_RESULT_BADBIOSTABLE; + + if (1 != lvds->sHeader.ucTableFormatRevision + || 2 > lvds->sHeader.ucTableContentRevision) + return BP_RESULT_UNSUPPORTED; + + memset(info, 0, sizeof(struct embedded_panel_info)); + + /* We need to convert from 10KHz units into KHz units*/ + info->lcd_timing.pixel_clk = + le16_to_cpu(lvds->sLCDTiming.usPixClk) * 10; + /* usHActive does not include borders, according to VBIOS team*/ + info->lcd_timing.horizontal_addressable = + le16_to_cpu(lvds->sLCDTiming.usHActive); + /* usHBlanking_Time includes borders, so we should really be subtracting + * borders duing this translation, but LVDS generally*/ + /* doesn't have borders, so we should be okay leaving this as is for + * now. May need to revisit if we ever have LVDS with borders*/ + info->lcd_timing.horizontal_blanking_time = + le16_to_cpu(lvds->sLCDTiming.usHBlanking_Time); + /* usVActive does not include borders, according to VBIOS team*/ + info->lcd_timing.vertical_addressable = + le16_to_cpu(lvds->sLCDTiming.usVActive); + /* usVBlanking_Time includes borders, so we should really be subtracting + * borders duing this translation, but LVDS generally*/ + /* doesn't have borders, so we should be okay leaving this as is for + * now. May need to revisit if we ever have LVDS with borders*/ + info->lcd_timing.vertical_blanking_time = + le16_to_cpu(lvds->sLCDTiming.usVBlanking_Time); + info->lcd_timing.horizontal_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usHSyncOffset); + info->lcd_timing.horizontal_sync_width = + le16_to_cpu(lvds->sLCDTiming.usHSyncWidth); + info->lcd_timing.vertical_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usVSyncOffset); + info->lcd_timing.vertical_sync_width = + le16_to_cpu(lvds->sLCDTiming.usVSyncWidth); + info->lcd_timing.horizontal_border = lvds->sLCDTiming.ucHBorder; + info->lcd_timing.vertical_border = lvds->sLCDTiming.ucVBorder; + info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HorizontalCutOff; + info->lcd_timing.misc_info.H_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HSyncPolarity; + info->lcd_timing.misc_info.V_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VSyncPolarity; + info->lcd_timing.misc_info.VERTICAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VerticalCutOff; + info->lcd_timing.misc_info.H_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.H_ReplicationBy2; + info->lcd_timing.misc_info.V_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.V_ReplicationBy2; + info->lcd_timing.misc_info.COMPOSITE_SYNC = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.CompositeSync; + info->lcd_timing.misc_info.INTERLACE = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.Interlace; + info->lcd_timing.misc_info.DOUBLE_CLOCK = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.DoubleClock; + info->ss_id = lvds->ucSS_Id; + + { + uint8_t rr = le16_to_cpu(lvds->usSupportedRefreshRate); + /* Get minimum supported refresh rate*/ + if (SUPPORTED_LCD_REFRESHRATE_30Hz & rr) + info->supported_rr.REFRESH_RATE_30HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_40Hz & rr) + info->supported_rr.REFRESH_RATE_40HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_48Hz & rr) + info->supported_rr.REFRESH_RATE_48HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_50Hz & rr) + info->supported_rr.REFRESH_RATE_50HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_60Hz & rr) + info->supported_rr.REFRESH_RATE_60HZ = 1; + } + + /*Drr panel support can be reported by VBIOS*/ + if (LCDPANEL_CAP_DRR_SUPPORTED + & lvds->ucLCDPanel_SpecialHandlingCap) + info->drr_enabled = 1; + + if (ATOM_PANEL_MISC_DUAL & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.DOUBLE_CLOCK = true; + + if (ATOM_PANEL_MISC_888RGB & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.RGB888 = true; + + info->lcd_timing.misc_info.GREY_LEVEL = + (uint32_t) (ATOM_PANEL_MISC_GREY_LEVEL & + lvds->ucLVDS_Misc) >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT; + + if (ATOM_PANEL_MISC_SPATIAL & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.SPATIAL = true; + + if (ATOM_PANEL_MISC_TEMPORAL & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.TEMPORAL = true; + + if (ATOM_PANEL_MISC_API_ENABLED & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.API_ENABLED = true; + + return BP_RESULT_OK; +} + +static enum bp_result get_embedded_panel_info_v1_3( + struct bios_parser *bp, + struct embedded_panel_info *info) +{ + ATOM_LCD_INFO_V13 *lvds; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(LCD_Info)) + return BP_RESULT_UNSUPPORTED; + + lvds = GET_IMAGE(ATOM_LCD_INFO_V13, DATA_TABLES(LCD_Info)); + + if (!lvds) + return BP_RESULT_BADBIOSTABLE; + + if (!((1 == lvds->sHeader.ucTableFormatRevision) + && (3 <= lvds->sHeader.ucTableContentRevision))) + return BP_RESULT_UNSUPPORTED; + + memset(info, 0, sizeof(struct embedded_panel_info)); + + /* We need to convert from 10KHz units into KHz units */ + info->lcd_timing.pixel_clk = + le16_to_cpu(lvds->sLCDTiming.usPixClk) * 10; + /* usHActive does not include borders, according to VBIOS team */ + info->lcd_timing.horizontal_addressable = + le16_to_cpu(lvds->sLCDTiming.usHActive); + /* usHBlanking_Time includes borders, so we should really be subtracting + * borders duing this translation, but LVDS generally*/ + /* doesn't have borders, so we should be okay leaving this as is for + * now. May need to revisit if we ever have LVDS with borders*/ + info->lcd_timing.horizontal_blanking_time = + le16_to_cpu(lvds->sLCDTiming.usHBlanking_Time); + /* usVActive does not include borders, according to VBIOS team*/ + info->lcd_timing.vertical_addressable = + le16_to_cpu(lvds->sLCDTiming.usVActive); + /* usVBlanking_Time includes borders, so we should really be subtracting + * borders duing this translation, but LVDS generally*/ + /* doesn't have borders, so we should be okay leaving this as is for + * now. May need to revisit if we ever have LVDS with borders*/ + info->lcd_timing.vertical_blanking_time = + le16_to_cpu(lvds->sLCDTiming.usVBlanking_Time); + info->lcd_timing.horizontal_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usHSyncOffset); + info->lcd_timing.horizontal_sync_width = + le16_to_cpu(lvds->sLCDTiming.usHSyncWidth); + info->lcd_timing.vertical_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usVSyncOffset); + info->lcd_timing.vertical_sync_width = + le16_to_cpu(lvds->sLCDTiming.usVSyncWidth); + info->lcd_timing.horizontal_border = lvds->sLCDTiming.ucHBorder; + info->lcd_timing.vertical_border = lvds->sLCDTiming.ucVBorder; + info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HorizontalCutOff; + info->lcd_timing.misc_info.H_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HSyncPolarity; + info->lcd_timing.misc_info.V_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VSyncPolarity; + info->lcd_timing.misc_info.VERTICAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VerticalCutOff; + info->lcd_timing.misc_info.H_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.H_ReplicationBy2; + info->lcd_timing.misc_info.V_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.V_ReplicationBy2; + info->lcd_timing.misc_info.COMPOSITE_SYNC = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.CompositeSync; + info->lcd_timing.misc_info.INTERLACE = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.Interlace; + info->lcd_timing.misc_info.DOUBLE_CLOCK = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.DoubleClock; + info->ss_id = lvds->ucSS_Id; + + /* Drr panel support can be reported by VBIOS*/ + if (LCDPANEL_CAP_V13_DRR_SUPPORTED + & lvds->ucLCDPanel_SpecialHandlingCap) + info->drr_enabled = 1; + + /* Get supported refresh rate*/ + if (info->drr_enabled == 1) { + uint8_t min_rr = + lvds->sRefreshRateSupport.ucMinRefreshRateForDRR; + uint8_t rr = lvds->sRefreshRateSupport.ucSupportedRefreshRate; + + if (min_rr != 0) { + if (SUPPORTED_LCD_REFRESHRATE_30Hz & min_rr) + info->supported_rr.REFRESH_RATE_30HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_40Hz & min_rr) + info->supported_rr.REFRESH_RATE_40HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_48Hz & min_rr) + info->supported_rr.REFRESH_RATE_48HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_50Hz & min_rr) + info->supported_rr.REFRESH_RATE_50HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_60Hz & min_rr) + info->supported_rr.REFRESH_RATE_60HZ = 1; + } else { + if (SUPPORTED_LCD_REFRESHRATE_30Hz & rr) + info->supported_rr.REFRESH_RATE_30HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_40Hz & rr) + info->supported_rr.REFRESH_RATE_40HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_48Hz & rr) + info->supported_rr.REFRESH_RATE_48HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_50Hz & rr) + info->supported_rr.REFRESH_RATE_50HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_60Hz & rr) + info->supported_rr.REFRESH_RATE_60HZ = 1; + } + } + + if (ATOM_PANEL_MISC_V13_DUAL & lvds->ucLCD_Misc) + info->lcd_timing.misc_info.DOUBLE_CLOCK = true; + + if (ATOM_PANEL_MISC_V13_8BIT_PER_COLOR & lvds->ucLCD_Misc) + info->lcd_timing.misc_info.RGB888 = true; + + info->lcd_timing.misc_info.GREY_LEVEL = + (uint32_t) (ATOM_PANEL_MISC_V13_GREY_LEVEL & + lvds->ucLCD_Misc) >> ATOM_PANEL_MISC_V13_GREY_LEVEL_SHIFT; + + return BP_RESULT_OK; +} + +/** + * bios_parser_get_encoder_cap_info + * + * @brief + * Get encoder capability information of input object id + * + * @param object_id, Object id + * @param object_id, encoder cap information structure + * + * @return Bios parser result code + * + */ +static enum bp_result bios_parser_get_encoder_cap_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_encoder_cap_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_OBJECT *object; + ATOM_ENCODER_CAP_RECORD_V2 *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, object_id); + + if (!object) + return BP_RESULT_BADINPUT; + + record = get_encoder_cap_record(bp, object); + if (!record) + return BP_RESULT_NORECORD; + + info->DP_HBR2_EN = record->usHBR2En; + info->DP_HBR3_EN = record->usHBR3En; + info->HDMI_6GB_EN = record->usHDMI6GEn; + return BP_RESULT_OK; +} + +/** + * get_encoder_cap_record + * + * @brief + * Get encoder cap record for the object + * + * @param object, ATOM object + * + * @return atom encoder cap record + * + * @note + * search all records to find the ATOM_ENCODER_CAP_RECORD_V2 record + */ +static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + struct bios_parser *bp, + ATOM_OBJECT *object) +{ + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return NULL; + + offset += header->ucRecordSize; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_ENCODER_CAP_RECORD_TYPE != header->ucRecordType) + continue; + + if (sizeof(ATOM_ENCODER_CAP_RECORD_V2) <= header->ucRecordSize) + return (ATOM_ENCODER_CAP_RECORD_V2 *)header; + } + + return NULL; +} + +static uint32_t get_ss_entry_number( + struct bios_parser *bp, + uint32_t id); +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_v2_1( + struct bios_parser *bp, + uint32_t id); +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_V3_1( + struct bios_parser *bp, + uint32_t id); +static uint32_t get_ss_entry_number_from_ss_info_tbl( + struct bios_parser *bp, + uint32_t id); + +/** + * BiosParserObject::GetNumberofSpreadSpectrumEntry + * Get Number of SpreadSpectrum Entry from the ASIC_InternalSS_Info table from + * the VBIOS that match the SSid (to be converted from signal) + * + * @param[in] signal, ASSignalType to be converted to SSid + * @return number of SS Entry that match the signal + */ +static uint32_t bios_parser_get_ss_entry_number( + struct dc_bios *dcb, + enum as_signal_type signal) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + uint32_t ss_id = 0; + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision revision; + + ss_id = signal_to_ss_id(signal); + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return get_ss_entry_number_from_ss_info_tbl(bp, ss_id); + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(ASIC_InternalSS_Info)); + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 2: + switch (revision.minor) { + case 1: + return get_ss_entry_number(bp, ss_id); + default: + break; + } + break; + case 3: + switch (revision.minor) { + case 1: + return + get_ss_entry_number_from_internal_ss_info_tbl_V3_1( + bp, ss_id); + default: + break; + } + break; + default: + break; + } + + return 0; +} + +/** + * get_ss_entry_number_from_ss_info_tbl + * Get Number of spread spectrum entry from the SS_Info table from the VBIOS. + * + * @note There can only be one entry for each id for SS_Info Table + * + * @param [in] id, spread spectrum id + * @return number of SS Entry that match the id + */ +static uint32_t get_ss_entry_number_from_ss_info_tbl( + struct bios_parser *bp, + uint32_t id) +{ + ATOM_SPREAD_SPECTRUM_INFO *tbl; + ATOM_COMMON_TABLE_HEADER *header; + uint32_t table_size; + uint32_t i; + uint32_t number = 0; + uint32_t id_local = SS_ID_UNKNOWN; + struct atom_data_revision revision; + + /* SS_Info table exist */ + if (!DATA_TABLES(SS_Info)) + return number; + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(SS_Info)); + get_atom_data_table_revision(header, &revision); + + tbl = GET_IMAGE(ATOM_SPREAD_SPECTRUM_INFO, + DATA_TABLES(SS_Info)); + + if (1 != revision.major || 2 > revision.minor) + return number; + + /* have to convert from Internal_SS format to SS_Info format */ + switch (id) { + case ASIC_INTERNAL_SS_ON_DP: + id_local = SS_ID_DP1; + break; + case ASIC_INTERNAL_SS_ON_LVDS: { + struct embedded_panel_info panel_info; + + if (bios_parser_get_embedded_panel_info(&bp->base, &panel_info) + == BP_RESULT_OK) + id_local = panel_info.ss_id; + break; + } + default: + break; + } + + if (id_local == SS_ID_UNKNOWN) + return number; + + table_size = (le16_to_cpu(tbl->sHeader.usStructureSize) - + sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); + + for (i = 0; i < table_size; i++) + if (id_local == (uint32_t)tbl->asSS_Info[i].ucSS_Id) { + number = 1; + break; + } + + return number; +} + +/** + * get_ss_entry_number + * Get spread sprectrum information from the ASIC_InternalSS_Info Ver 2.1 or + * SS_Info table from the VBIOS + * There can not be more than 1 entry for ASIC_InternalSS_Info Ver 2.1 or + * SS_Info. + * + * @param id, spread sprectrum info index + * @return Bios parser result code + */ +static uint32_t get_ss_entry_number(struct bios_parser *bp, uint32_t id) +{ + if (id == ASIC_INTERNAL_SS_ON_DP || id == ASIC_INTERNAL_SS_ON_LVDS) + return get_ss_entry_number_from_ss_info_tbl(bp, id); + + return get_ss_entry_number_from_internal_ss_info_tbl_v2_1(bp, id); +} + +/** + * get_ss_entry_number_from_internal_ss_info_tbl_v2_1 + * Get NUmber of spread sprectrum entry from the ASIC_InternalSS_Info table + * Ver 2.1 from the VBIOS + * There will not be multiple entry for Ver 2.1 + * + * @param id, spread sprectrum info index + * @return number of SS Entry that match the id + */ +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_v2_1( + struct bios_parser *bp, + uint32_t id) +{ + ATOM_ASIC_INTERNAL_SS_INFO_V2 *header_include; + ATOM_ASIC_SS_ASSIGNMENT_V2 *tbl; + uint32_t size; + uint32_t i; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return 0; + + header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V2, + DATA_TABLES(ASIC_InternalSS_Info)); + + size = (le16_to_cpu(header_include->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V2 *) + &header_include->asSpreadSpectrum[0]; + for (i = 0; i < size; i++) + if (tbl[i].ucClockIndication == (uint8_t)id) + return 1; + + return 0; +} +/** + * get_ss_entry_number_from_internal_ss_info_table_V3_1 + * Get Number of SpreadSpectrum Entry from the ASIC_InternalSS_Info table of + * the VBIOS that matches id + * + * @param[in] id, spread sprectrum id + * @return number of SS Entry that match the id + */ +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_V3_1( + struct bios_parser *bp, + uint32_t id) +{ + uint32_t number = 0; + ATOM_ASIC_INTERNAL_SS_INFO_V3 *header_include; + ATOM_ASIC_SS_ASSIGNMENT_V3 *tbl; + uint32_t size; + uint32_t i; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return number; + + header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V3, + DATA_TABLES(ASIC_InternalSS_Info)); + size = (le16_to_cpu(header_include->sHeader.usStructureSize) - + sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V3 *) + &header_include->asSpreadSpectrum[0]; + + for (i = 0; i < size; i++) + if (tbl[i].ucClockIndication == (uint8_t)id) + number++; + + return number; +} + +/** + * bios_parser_get_gpio_pin_info + * Get GpioPin information of input gpio id + * + * @param gpio_id, GPIO ID + * @param info, GpioPin information structure + * @return Bios parser result code + * @note + * to get the GPIO PIN INFO, we need: + * 1. get the GPIO_ID from other object table, see GetHPDInfo() + * 2. in DATA_TABLE.GPIO_Pin_LUT, search all records, to get the registerA + * offset/mask + */ +static enum bp_result bios_parser_get_gpio_pin_info( + struct dc_bios *dcb, + uint32_t gpio_id, + struct gpio_pin_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_GPIO_PIN_LUT *header; + uint32_t count = 0; + uint32_t i = 0; + + if (!DATA_TABLES(GPIO_Pin_LUT)) + return BP_RESULT_BADBIOSTABLE; + + header = GET_IMAGE(ATOM_GPIO_PIN_LUT, DATA_TABLES(GPIO_Pin_LUT)); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(ATOM_COMMON_TABLE_HEADER) + sizeof(ATOM_GPIO_PIN_LUT) + > le16_to_cpu(header->sHeader.usStructureSize)) + return BP_RESULT_BADBIOSTABLE; + + if (1 != header->sHeader.ucTableContentRevision) + return BP_RESULT_UNSUPPORTED; + + count = (le16_to_cpu(header->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_GPIO_PIN_ASSIGNMENT); + for (i = 0; i < count; ++i) { + if (header->asGPIO_Pin[i].ucGPIO_ID != gpio_id) + continue; + + info->offset = + (uint32_t) le16_to_cpu(header->asGPIO_Pin[i].usGpioPin_AIndex); + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask = (uint32_t) (1 << + header->asGPIO_Pin[i].ucGpioPinBitShift); + info->mask_y = info->mask + 2; + info->mask_en = info->mask + 1; + info->mask_mask = info->mask - 1; + + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, + ATOM_I2C_RECORD *record, + struct graphics_object_i2c_info *info) +{ + ATOM_GPIO_I2C_INFO *header; + uint32_t count = 0; + + if (!info) + return BP_RESULT_BADINPUT; + + /* get the GPIO_I2C info */ + if (!DATA_TABLES(GPIO_I2C_Info)) + return BP_RESULT_BADBIOSTABLE; + + header = GET_IMAGE(ATOM_GPIO_I2C_INFO, DATA_TABLES(GPIO_I2C_Info)); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(ATOM_COMMON_TABLE_HEADER) + sizeof(ATOM_GPIO_I2C_ASSIGMENT) + > le16_to_cpu(header->sHeader.usStructureSize)) + return BP_RESULT_BADBIOSTABLE; + + if (1 != header->sHeader.ucTableContentRevision) + return BP_RESULT_UNSUPPORTED; + + /* get data count */ + count = (le16_to_cpu(header->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_GPIO_I2C_ASSIGMENT); + if (count < record->sucI2cId.bfI2C_LineMux) + return BP_RESULT_BADBIOSTABLE; + + /* get the GPIO_I2C_INFO */ + info->i2c_hw_assist = record->sucI2cId.bfHW_Capable; + info->i2c_line = record->sucI2cId.bfI2C_LineMux; + info->i2c_engine_id = record->sucI2cId.bfHW_EngineID; + info->i2c_slave_address = record->ucI2CAddr; + + info->gpio_info.clk_mask_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkMaskRegisterIndex); + info->gpio_info.clk_en_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkEnRegisterIndex); + info->gpio_info.clk_y_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkY_RegisterIndex); + info->gpio_info.clk_a_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkA_RegisterIndex); + info->gpio_info.data_mask_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataMaskRegisterIndex); + info->gpio_info.data_en_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataEnRegisterIndex); + info->gpio_info.data_y_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataY_RegisterIndex); + info->gpio_info.data_a_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataA_RegisterIndex); + + info->gpio_info.clk_mask_shift = + header->asGPIO_Info[info->i2c_line].ucClkMaskShift; + info->gpio_info.clk_en_shift = + header->asGPIO_Info[info->i2c_line].ucClkEnShift; + info->gpio_info.clk_y_shift = + header->asGPIO_Info[info->i2c_line].ucClkY_Shift; + info->gpio_info.clk_a_shift = + header->asGPIO_Info[info->i2c_line].ucClkA_Shift; + info->gpio_info.data_mask_shift = + header->asGPIO_Info[info->i2c_line].ucDataMaskShift; + info->gpio_info.data_en_shift = + header->asGPIO_Info[info->i2c_line].ucDataEnShift; + info->gpio_info.data_y_shift = + header->asGPIO_Info[info->i2c_line].ucDataY_Shift; + info->gpio_info.data_a_shift = + header->asGPIO_Info[info->i2c_line].ucDataA_Shift; + + return BP_RESULT_OK; +} + +static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, + struct graphics_object_id id) +{ + uint32_t offset; + ATOM_OBJECT_TABLE *tbl; + uint32_t i; + + switch (id.type) { + case OBJECT_TYPE_ENCODER: + offset = le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); + break; + + case OBJECT_TYPE_CONNECTOR: + offset = le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); + break; + + case OBJECT_TYPE_ROUTER: + offset = le16_to_cpu(bp->object_info_tbl.v1_1->usRouterObjectTableOffset); + break; + + case OBJECT_TYPE_GENERIC: + if (bp->object_info_tbl.revision.minor < 3) + return NULL; + offset = le16_to_cpu(bp->object_info_tbl.v1_3->usMiscObjectTableOffset); + break; + + default: + return NULL; + } + + offset += bp->object_info_tbl_offset; + + tbl = GET_IMAGE(ATOM_OBJECT_TABLE, offset); + if (!tbl) + return NULL; + + for (i = 0; i < tbl->ucNumberOfObjects; i++) + if (dal_graphics_object_id_is_equal(id, + object_id_from_bios_object_id( + le16_to_cpu(tbl->asObjects[i].usObjectID)))) + return &tbl->asObjects[i]; + + return NULL; +} + +static uint32_t get_dest_obj_list(struct bios_parser *bp, + ATOM_OBJECT *object, uint16_t **id_list) +{ + uint32_t offset; + uint8_t *number; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return 0; + } + + offset = le16_to_cpu(object->usSrcDstTableOffset) + + bp->object_info_tbl_offset; + + number = GET_IMAGE(uint8_t, offset); + if (!number) + return 0; + + offset += sizeof(uint8_t); + offset += sizeof(uint16_t) * (*number); + + number = GET_IMAGE(uint8_t, offset); + if ((!number) || (!*number)) + return 0; + + offset += sizeof(uint8_t); + *id_list = (uint16_t *)bios_get_image(&bp->base, offset, *number * sizeof(uint16_t)); + + if (!*id_list) + return 0; + + return *number; +} + +static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, + uint16_t **id_list) +{ + uint32_t offset; + uint8_t *number; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return 0; + } + + offset = le16_to_cpu(object->usSrcDstTableOffset) + + bp->object_info_tbl_offset; + + number = GET_IMAGE(uint8_t, offset); + if (!number) + return 0; + + offset += sizeof(uint8_t); + *id_list = (uint16_t *)bios_get_image(&bp->base, offset, *number * sizeof(uint16_t)); + + if (!*id_list) + return 0; + + return *number; +} + +static uint32_t get_dst_number_from_object(struct bios_parser *bp, + ATOM_OBJECT *object) +{ + uint32_t offset; + uint8_t *number; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid encoder object id*/ + return 0; + } + + offset = le16_to_cpu(object->usSrcDstTableOffset) + + bp->object_info_tbl_offset; + + number = GET_IMAGE(uint8_t, offset); + if (!number) + return 0; + + offset += sizeof(uint8_t); + offset += sizeof(uint16_t) * (*number); + + number = GET_IMAGE(uint8_t, offset); + + if (!number) + return 0; + + return *number; +} + +static struct device_id device_type_from_device_id(uint16_t device_id) +{ + + struct device_id result_device_id; + + switch (device_id) { + case ATOM_DEVICE_LCD1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_LCD; + result_device_id.enum_id = 1; + break; + + case ATOM_DEVICE_LCD2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_LCD; + result_device_id.enum_id = 2; + break; + + case ATOM_DEVICE_CRT1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_CRT; + result_device_id.enum_id = 1; + break; + + case ATOM_DEVICE_CRT2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_CRT; + result_device_id.enum_id = 2; + break; + + case ATOM_DEVICE_DFP1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 1; + break; + + case ATOM_DEVICE_DFP2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 2; + break; + + case ATOM_DEVICE_DFP3_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 3; + break; + + case ATOM_DEVICE_DFP4_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 4; + break; + + case ATOM_DEVICE_DFP5_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 5; + break; + + case ATOM_DEVICE_DFP6_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 6; + break; + + default: + BREAK_TO_DEBUGGER(); /* Invalid device Id */ + result_device_id.device_type = DEVICE_TYPE_UNKNOWN; + result_device_id.enum_id = 0; + } + return result_device_id; +} + +static void get_atom_data_table_revision( + ATOM_COMMON_TABLE_HEADER *atom_data_tbl, + struct atom_data_revision *tbl_revision) +{ + if (!tbl_revision) + return; + + /* initialize the revision to 0 which is invalid revision */ + tbl_revision->major = 0; + tbl_revision->minor = 0; + + if (!atom_data_tbl) + return; + + tbl_revision->major = + (uint32_t) GET_DATA_TABLE_MAJOR_REVISION(atom_data_tbl); + tbl_revision->minor = + (uint32_t) GET_DATA_TABLE_MINOR_REVISION(atom_data_tbl); +} + +static uint32_t signal_to_ss_id(enum as_signal_type signal) +{ + uint32_t clk_id_ss = 0; + + switch (signal) { + case AS_SIGNAL_TYPE_DVI: + clk_id_ss = ASIC_INTERNAL_SS_ON_TMDS; + break; + case AS_SIGNAL_TYPE_HDMI: + clk_id_ss = ASIC_INTERNAL_SS_ON_HDMI; + break; + case AS_SIGNAL_TYPE_LVDS: + clk_id_ss = ASIC_INTERNAL_SS_ON_LVDS; + break; + case AS_SIGNAL_TYPE_DISPLAY_PORT: + clk_id_ss = ASIC_INTERNAL_SS_ON_DP; + break; + case AS_SIGNAL_TYPE_GPU_PLL: + clk_id_ss = ASIC_INTERNAL_GPUPLL_SS; + break; + default: + break; + } + return clk_id_ss; +} + +static uint32_t get_support_mask_for_device_id(struct device_id device_id) +{ + enum dal_device_type device_type = device_id.device_type; + uint32_t enum_id = device_id.enum_id; + + switch (device_type) { + case DEVICE_TYPE_LCD: + switch (enum_id) { + case 1: + return ATOM_DEVICE_LCD1_SUPPORT; + case 2: + return ATOM_DEVICE_LCD2_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_CRT: + switch (enum_id) { + case 1: + return ATOM_DEVICE_CRT1_SUPPORT; + case 2: + return ATOM_DEVICE_CRT2_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_DFP: + switch (enum_id) { + case 1: + return ATOM_DEVICE_DFP1_SUPPORT; + case 2: + return ATOM_DEVICE_DFP2_SUPPORT; + case 3: + return ATOM_DEVICE_DFP3_SUPPORT; + case 4: + return ATOM_DEVICE_DFP4_SUPPORT; + case 5: + return ATOM_DEVICE_DFP5_SUPPORT; + case 6: + return ATOM_DEVICE_DFP6_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_CV: + switch (enum_id) { + case 1: + return ATOM_DEVICE_CV_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_TV: + switch (enum_id) { + case 1: + return ATOM_DEVICE_TV1_SUPPORT; + default: + break; + } + break; + default: + break; + }; + + /* Unidentified device ID, return empty support mask. */ + return 0; +} + +/** + * HwContext interface for writing MM registers + */ + +static bool i2c_read( + struct bios_parser *bp, + struct graphics_object_i2c_info *i2c_info, + uint8_t *buffer, + uint32_t length) +{ + struct ddc *ddc; + uint8_t offset[2] = { 0, 0 }; + bool result = false; + struct i2c_command cmd; + struct gpio_ddc_hw_info hw_info = { + i2c_info->i2c_hw_assist, + i2c_info->i2c_line }; + + ddc = dal_gpio_create_ddc(bp->base.ctx->gpio_service, + i2c_info->gpio_info.clk_a_register_index, + (1 << i2c_info->gpio_info.clk_a_shift), &hw_info); + + if (!ddc) + return result; + + /*Using SW engine */ + cmd.engine = I2C_COMMAND_ENGINE_SW; + cmd.speed = ddc->ctx->dc->caps.i2c_speed_in_khz; + + { + struct i2c_payload payloads[] = { + { + .address = i2c_info->i2c_slave_address >> 1, + .data = offset, + .length = sizeof(offset), + .write = true + }, + { + .address = i2c_info->i2c_slave_address >> 1, + .data = buffer, + .length = length, + .write = false + } + }; + + cmd.payloads = payloads; + cmd.number_of_payloads = ARRAY_SIZE(payloads); + + /* TODO route this through drm i2c_adapter */ + result = dal_i2caux_submit_i2c_command( + ddc->ctx->i2caux, + ddc, + &cmd); + } + + dal_gpio_destroy_ddc(&ddc); + + return result; +} + +/** + * Read external display connection info table through i2c. + * validate the GUID and checksum. + * + * @return enum bp_result whether all data was sucessfully read + */ +static enum bp_result get_ext_display_connection_info( + struct bios_parser *bp, + ATOM_OBJECT *opm_object, + ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO *ext_display_connection_info_tbl) +{ + bool config_tbl_present = false; + ATOM_I2C_RECORD *i2c_record = NULL; + uint32_t i = 0; + + if (opm_object == NULL) + return BP_RESULT_BADINPUT; + + i2c_record = get_i2c_record(bp, opm_object); + + if (i2c_record != NULL) { + ATOM_GPIO_I2C_INFO *gpio_i2c_header; + struct graphics_object_i2c_info i2c_info; + + gpio_i2c_header = GET_IMAGE(ATOM_GPIO_I2C_INFO, + bp->master_data_tbl->ListOfDataTables.GPIO_I2C_Info); + + if (NULL == gpio_i2c_header) + return BP_RESULT_BADBIOSTABLE; + + if (get_gpio_i2c_info(bp, i2c_record, &i2c_info) != + BP_RESULT_OK) + return BP_RESULT_BADBIOSTABLE; + + if (i2c_read(bp, + &i2c_info, + (uint8_t *)ext_display_connection_info_tbl, + sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO))) { + config_tbl_present = true; + } + } + + /* Validate GUID */ + if (config_tbl_present) + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; i++) { + if (ext_display_connection_info_tbl->ucGuid[i] + != ext_display_connection_guid[i]) { + config_tbl_present = false; + break; + } + } + + /* Validate checksum */ + if (config_tbl_present) { + uint8_t check_sum = 0; + uint8_t *buf = + (uint8_t *)ext_display_connection_info_tbl; + + for (i = 0; i < sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO); + i++) { + check_sum += buf[i]; + } + + if (check_sum != 0) + config_tbl_present = false; + } + + if (config_tbl_present) + return BP_RESULT_OK; + else + return BP_RESULT_FAILURE; +} + +/* + * Gets the first device ID in the same group as the given ID for enumerating. + * For instance, if any DFP device ID is passed, returns the device ID for DFP1. + * + * The first device ID in the same group as the passed device ID, or 0 if no + * matching device group found. + */ +static uint32_t enum_first_device_id(uint32_t dev_id) +{ + /* Return the first in the group that this ID belongs to. */ + if (dev_id & ATOM_DEVICE_CRT_SUPPORT) + return ATOM_DEVICE_CRT1_SUPPORT; + else if (dev_id & ATOM_DEVICE_DFP_SUPPORT) + return ATOM_DEVICE_DFP1_SUPPORT; + else if (dev_id & ATOM_DEVICE_LCD_SUPPORT) + return ATOM_DEVICE_LCD1_SUPPORT; + else if (dev_id & ATOM_DEVICE_TV_SUPPORT) + return ATOM_DEVICE_TV1_SUPPORT; + else if (dev_id & ATOM_DEVICE_CV_SUPPORT) + return ATOM_DEVICE_CV_SUPPORT; + + /* No group found for this device ID. */ + + dm_error("%s: incorrect input %d\n", __func__, dev_id); + /* No matching support flag for given device ID */ + return 0; +} + +/* + * Gets the next device ID in the group for a given device ID. + * + * The current device ID being enumerated on. + * + * The next device ID in the group, or 0 if no device exists. + */ +static uint32_t enum_next_dev_id(uint32_t dev_id) +{ + /* Get next device ID in the group. */ + switch (dev_id) { + case ATOM_DEVICE_CRT1_SUPPORT: + return ATOM_DEVICE_CRT2_SUPPORT; + case ATOM_DEVICE_LCD1_SUPPORT: + return ATOM_DEVICE_LCD2_SUPPORT; + case ATOM_DEVICE_DFP1_SUPPORT: + return ATOM_DEVICE_DFP2_SUPPORT; + case ATOM_DEVICE_DFP2_SUPPORT: + return ATOM_DEVICE_DFP3_SUPPORT; + case ATOM_DEVICE_DFP3_SUPPORT: + return ATOM_DEVICE_DFP4_SUPPORT; + case ATOM_DEVICE_DFP4_SUPPORT: + return ATOM_DEVICE_DFP5_SUPPORT; + case ATOM_DEVICE_DFP5_SUPPORT: + return ATOM_DEVICE_DFP6_SUPPORT; + } + + /* Done enumerating through devices. */ + return 0; +} + +/* + * Returns the new device tag record for patched BIOS object. + * + * [IN] pExtDisplayPath - External display path to copy device tag from. + * [IN] deviceSupport - Bit vector for device ID support flags. + * [OUT] pDeviceTag - Device tag structure to fill with patched data. + * + * True if a compatible device ID was found, false otherwise. + */ +static bool get_patched_device_tag( + struct bios_parser *bp, + EXT_DISPLAY_PATH *ext_display_path, + uint32_t device_support, + ATOM_CONNECTOR_DEVICE_TAG *device_tag) +{ + uint32_t dev_id; + /* Use fallback behaviour if not supported. */ + if (!bp->remap_device_tags) { + device_tag->ulACPIDeviceEnum = + cpu_to_le32((uint32_t) le16_to_cpu(ext_display_path->usDeviceACPIEnum)); + device_tag->usDeviceID = + cpu_to_le16(le16_to_cpu(ext_display_path->usDeviceTag)); + return true; + } + + /* Find the first unused in the same group. */ + dev_id = enum_first_device_id(le16_to_cpu(ext_display_path->usDeviceTag)); + while (dev_id != 0) { + /* Assign this device ID if supported. */ + if ((device_support & dev_id) != 0) { + device_tag->ulACPIDeviceEnum = + cpu_to_le32((uint32_t) le16_to_cpu(ext_display_path->usDeviceACPIEnum)); + device_tag->usDeviceID = cpu_to_le16((USHORT) dev_id); + return true; + } + + dev_id = enum_next_dev_id(dev_id); + } + + /* No compatible device ID found. */ + return false; +} + +/* + * Adds a device tag to a BIOS object's device tag record if there is + * matching device ID supported. + * + * pObject - Pointer to the BIOS object to add the device tag to. + * pExtDisplayPath - Display path to retrieve base device ID from. + * pDeviceSupport - Pointer to bit vector for supported device IDs. + */ +static void add_device_tag_from_ext_display_path( + struct bios_parser *bp, + ATOM_OBJECT *object, + EXT_DISPLAY_PATH *ext_display_path, + uint32_t *device_support) +{ + /* Get device tag record for object. */ + ATOM_CONNECTOR_DEVICE_TAG *device_tag = NULL; + ATOM_CONNECTOR_DEVICE_TAG_RECORD *device_tag_record = NULL; + enum bp_result result = + bios_parser_get_device_tag_record( + bp, object, &device_tag_record); + + if ((le16_to_cpu(ext_display_path->usDeviceTag) != CONNECTOR_OBJECT_ID_NONE) + && (result == BP_RESULT_OK)) { + uint8_t index; + + if ((device_tag_record->ucNumberOfDevice == 1) && + (le16_to_cpu(device_tag_record->asDeviceTag[0].usDeviceID) == 0)) { + /*Workaround bug in current VBIOS releases where + * ucNumberOfDevice = 1 but there is no actual device + * tag data. This w/a is temporary until the updated + * VBIOS is distributed. */ + device_tag_record->ucNumberOfDevice = + device_tag_record->ucNumberOfDevice - 1; + } + + /* Attempt to find a matching device ID. */ + index = device_tag_record->ucNumberOfDevice; + device_tag = &device_tag_record->asDeviceTag[index]; + if (get_patched_device_tag( + bp, + ext_display_path, + *device_support, + device_tag)) { + /* Update cached device support to remove assigned ID. + */ + *device_support &= ~le16_to_cpu(device_tag->usDeviceID); + device_tag_record->ucNumberOfDevice++; + } + } +} + +/* + * Read out a single EXT_DISPLAY_PATH from the external display connection info + * table. The specific entry in the table is determined by the enum_id passed + * in. + * + * EXT_DISPLAY_PATH describing a single Configuration table entry + */ + +#define INVALID_CONNECTOR 0xffff + +static EXT_DISPLAY_PATH *get_ext_display_path_entry( + ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO *config_table, + uint32_t bios_object_id) +{ + EXT_DISPLAY_PATH *ext_display_path; + uint32_t ext_display_path_index = + ((bios_object_id & ENUM_ID_MASK) >> ENUM_ID_SHIFT) - 1; + + if (ext_display_path_index >= MAX_NUMBER_OF_EXT_DISPLAY_PATH) + return NULL; + + ext_display_path = &config_table->sPath[ext_display_path_index]; + + if (le16_to_cpu(ext_display_path->usDeviceConnector) == INVALID_CONNECTOR) + ext_display_path->usDeviceConnector = cpu_to_le16(0); + + return ext_display_path; +} + +/* + * Get AUX/DDC information of input object id + * + * search all records to find the ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE record + * IR + */ +static ATOM_CONNECTOR_AUXDDC_LUT_RECORD *get_ext_connector_aux_ddc_lut_record( + struct bios_parser *bp, + ATOM_OBJECT *object) +{ + uint32_t offset; + ATOM_COMMON_RECORD_HEADER *header; + + if (!object) { + BREAK_TO_DEBUGGER(); + /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return NULL; + + if (LAST_RECORD_TYPE == header->ucRecordType || + 0 == header->ucRecordSize) + break; + + if (ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE == + header->ucRecordType && + sizeof(ATOM_CONNECTOR_AUXDDC_LUT_RECORD) <= + header->ucRecordSize) + return (ATOM_CONNECTOR_AUXDDC_LUT_RECORD *)(header); + + offset += header->ucRecordSize; + } + + return NULL; +} + +/* + * Get AUX/DDC information of input object id + * + * search all records to find the ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE record + * IR + */ +static ATOM_CONNECTOR_HPDPIN_LUT_RECORD *get_ext_connector_hpd_pin_lut_record( + struct bios_parser *bp, + ATOM_OBJECT *object) +{ + uint32_t offset; + ATOM_COMMON_RECORD_HEADER *header; + + if (!object) { + BREAK_TO_DEBUGGER(); + /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return NULL; + + if (LAST_RECORD_TYPE == header->ucRecordType || + 0 == header->ucRecordSize) + break; + + if (ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE == + header->ucRecordType && + sizeof(ATOM_CONNECTOR_HPDPIN_LUT_RECORD) <= + header->ucRecordSize) + return (ATOM_CONNECTOR_HPDPIN_LUT_RECORD *)header; + + offset += header->ucRecordSize; + } + + return NULL; +} + +/* + * Check whether we need to patch the VBIOS connector info table with + * data from an external display connection info table. This is + * necessary to support MXM boards with an OPM (output personality + * module). With these designs, the VBIOS connector info table + * specifies an MXM_CONNECTOR with a unique ID. The driver retrieves + * the external connection info table through i2c and then looks up the + * connector ID to find the real connector type (e.g. DFP1). + * + */ +static enum bp_result patch_bios_image_from_ext_display_connection_info( + struct bios_parser *bp) +{ + ATOM_OBJECT_TABLE *connector_tbl; + uint32_t connector_tbl_offset; + struct graphics_object_id object_id; + ATOM_OBJECT *object; + ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO ext_display_connection_info_tbl; + EXT_DISPLAY_PATH *ext_display_path; + ATOM_CONNECTOR_AUXDDC_LUT_RECORD *aux_ddc_lut_record = NULL; + ATOM_I2C_RECORD *i2c_record = NULL; + ATOM_CONNECTOR_HPDPIN_LUT_RECORD *hpd_pin_lut_record = NULL; + ATOM_HPD_INT_RECORD *hpd_record = NULL; + ATOM_OBJECT_TABLE *encoder_table; + uint32_t encoder_table_offset; + ATOM_OBJECT *opm_object = NULL; + uint32_t i = 0; + struct graphics_object_id opm_object_id = + dal_graphics_object_id_init( + GENERIC_ID_MXM_OPM, + ENUM_ID_1, + OBJECT_TYPE_GENERIC); + ATOM_CONNECTOR_DEVICE_TAG_RECORD *dev_tag_record; + uint32_t cached_device_support = + le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport); + + uint32_t dst_number; + uint16_t *dst_object_id_list; + + opm_object = get_bios_object(bp, opm_object_id); + if (!opm_object) + return BP_RESULT_UNSUPPORTED; + + memset(&ext_display_connection_info_tbl, 0, + sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO)); + + connector_tbl_offset = bp->object_info_tbl_offset + + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); + connector_tbl = GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); + + /* Read Connector info table from EEPROM through i2c */ + if (get_ext_display_connection_info(bp, + opm_object, + &ext_display_connection_info_tbl) != BP_RESULT_OK) { + + dm_logger_write(bp->base.ctx->logger, LOG_WARNING, + "%s: Failed to read Connection Info Table", __func__); + return BP_RESULT_UNSUPPORTED; + } + + /* Get pointer to AUX/DDC and HPD LUTs */ + aux_ddc_lut_record = + get_ext_connector_aux_ddc_lut_record(bp, opm_object); + hpd_pin_lut_record = + get_ext_connector_hpd_pin_lut_record(bp, opm_object); + + if ((aux_ddc_lut_record == NULL) || (hpd_pin_lut_record == NULL)) + return BP_RESULT_UNSUPPORTED; + + /* Cache support bits for currently unmapped device types. */ + if (bp->remap_device_tags) { + for (i = 0; i < connector_tbl->ucNumberOfObjects; ++i) { + uint32_t j; + /* Remove support for all non-MXM connectors. */ + object = &connector_tbl->asObjects[i]; + object_id = object_id_from_bios_object_id( + le16_to_cpu(object->usObjectID)); + if ((OBJECT_TYPE_CONNECTOR != object_id.type) || + (CONNECTOR_ID_MXM == object_id.id)) + continue; + + /* Remove support for all device tags. */ + if (bios_parser_get_device_tag_record( + bp, object, &dev_tag_record) != BP_RESULT_OK) + continue; + + for (j = 0; j < dev_tag_record->ucNumberOfDevice; ++j) { + ATOM_CONNECTOR_DEVICE_TAG *device_tag = + &dev_tag_record->asDeviceTag[j]; + cached_device_support &= + ~le16_to_cpu(device_tag->usDeviceID); + } + } + } + + /* Find all MXM connector objects and patch them with connector info + * from the external display connection info table. */ + for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { + uint32_t j; + + object = &connector_tbl->asObjects[i]; + object_id = object_id_from_bios_object_id(le16_to_cpu(object->usObjectID)); + if ((OBJECT_TYPE_CONNECTOR != object_id.type) || + (CONNECTOR_ID_MXM != object_id.id)) + continue; + + /* Get the correct connection info table entry based on the enum + * id. */ + ext_display_path = get_ext_display_path_entry( + &ext_display_connection_info_tbl, + le16_to_cpu(object->usObjectID)); + if (!ext_display_path) + return BP_RESULT_FAILURE; + + /* Patch device connector ID */ + object->usObjectID = + cpu_to_le16(le16_to_cpu(ext_display_path->usDeviceConnector)); + + /* Patch device tag, ulACPIDeviceEnum. */ + add_device_tag_from_ext_display_path( + bp, + object, + ext_display_path, + &cached_device_support); + + /* Patch HPD info */ + if (ext_display_path->ucExtHPDPINLutIndex < + MAX_NUMBER_OF_EXT_HPDPIN_LUT_ENTRIES) { + hpd_record = get_hpd_record(bp, object); + if (hpd_record) { + uint8_t index = + ext_display_path->ucExtHPDPINLutIndex; + hpd_record->ucHPDIntGPIOID = + hpd_pin_lut_record->ucHPDPINMap[index]; + } else { + BREAK_TO_DEBUGGER(); + /* Invalid hpd record */ + return BP_RESULT_FAILURE; + } + } + + /* Patch I2C/AUX info */ + if (ext_display_path->ucExtHPDPINLutIndex < + MAX_NUMBER_OF_EXT_AUXDDC_LUT_ENTRIES) { + i2c_record = get_i2c_record(bp, object); + if (i2c_record) { + uint8_t index = + ext_display_path->ucExtAUXDDCLutIndex; + i2c_record->sucI2cId = + aux_ddc_lut_record->ucAUXDDCMap[index]; + } else { + BREAK_TO_DEBUGGER(); + /* Invalid I2C record */ + return BP_RESULT_FAILURE; + } + } + + /* Merge with other MXM connectors that map to the same physical + * connector. */ + for (j = i + 1; + j < connector_tbl->ucNumberOfObjects; j++) { + ATOM_OBJECT *next_object; + struct graphics_object_id next_object_id; + EXT_DISPLAY_PATH *next_ext_display_path; + + next_object = &connector_tbl->asObjects[j]; + next_object_id = object_id_from_bios_object_id( + le16_to_cpu(next_object->usObjectID)); + + if ((OBJECT_TYPE_CONNECTOR != next_object_id.type) && + (CONNECTOR_ID_MXM == next_object_id.id)) + continue; + + next_ext_display_path = get_ext_display_path_entry( + &ext_display_connection_info_tbl, + le16_to_cpu(next_object->usObjectID)); + + if (next_ext_display_path == NULL) + return BP_RESULT_FAILURE; + + /* Merge if using same connector. */ + if ((le16_to_cpu(next_ext_display_path->usDeviceConnector) == + le16_to_cpu(ext_display_path->usDeviceConnector)) && + (le16_to_cpu(ext_display_path->usDeviceConnector) != 0)) { + /* Clear duplicate connector from table. */ + next_object->usObjectID = cpu_to_le16(0); + add_device_tag_from_ext_display_path( + bp, + object, + ext_display_path, + &cached_device_support); + } + } + } + + /* Find all encoders which have an MXM object as their destination. + * Replace the MXM object with the real connector Id from the external + * display connection info table */ + + encoder_table_offset = bp->object_info_tbl_offset + + le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); + encoder_table = GET_IMAGE(ATOM_OBJECT_TABLE, encoder_table_offset); + + for (i = 0; i < encoder_table->ucNumberOfObjects; i++) { + uint32_t j; + + object = &encoder_table->asObjects[i]; + + dst_number = get_dest_obj_list(bp, object, &dst_object_id_list); + + for (j = 0; j < dst_number; j++) { + object_id = object_id_from_bios_object_id( + dst_object_id_list[j]); + + if ((OBJECT_TYPE_CONNECTOR != object_id.type) || + (CONNECTOR_ID_MXM != object_id.id)) + continue; + + /* Get the correct connection info table entry based on + * the enum id. */ + ext_display_path = + get_ext_display_path_entry( + &ext_display_connection_info_tbl, + dst_object_id_list[j]); + + if (ext_display_path == NULL) + return BP_RESULT_FAILURE; + + dst_object_id_list[j] = + le16_to_cpu(ext_display_path->usDeviceConnector); + } + } + + return BP_RESULT_OK; +} + +/* + * Check whether we need to patch the VBIOS connector info table with + * data from an external display connection info table. This is + * necessary to support MXM boards with an OPM (output personality + * module). With these designs, the VBIOS connector info table + * specifies an MXM_CONNECTOR with a unique ID. The driver retrieves + * the external connection info table through i2c and then looks up the + * connector ID to find the real connector type (e.g. DFP1). + * + */ + +static void process_ext_display_connection_info(struct bios_parser *bp) +{ + ATOM_OBJECT_TABLE *connector_tbl; + uint32_t connector_tbl_offset; + struct graphics_object_id object_id; + ATOM_OBJECT *object; + bool mxm_connector_found = false; + bool null_entry_found = false; + uint32_t i = 0; + + connector_tbl_offset = bp->object_info_tbl_offset + + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); + connector_tbl = GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); + + /* Look for MXM connectors to determine whether we need patch the VBIOS + * connector info table. Look for null entries to determine whether we + * need to compact connector table. */ + for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { + object = &connector_tbl->asObjects[i]; + object_id = object_id_from_bios_object_id(le16_to_cpu(object->usObjectID)); + + if ((OBJECT_TYPE_CONNECTOR == object_id.type) && + (CONNECTOR_ID_MXM == object_id.id)) { + /* Once we found MXM connector - we can break */ + mxm_connector_found = true; + break; + } else if (OBJECT_TYPE_CONNECTOR != object_id.type) { + /* We need to continue looping - to check if MXM + * connector present */ + null_entry_found = true; + } + } + + /* Patch BIOS image */ + if (mxm_connector_found || null_entry_found) { + uint32_t connectors_num = 0; + uint8_t *original_bios; + /* Step 1: Replace bios image with the new copy which will be + * patched */ + bp->base.bios_local_image = kzalloc(bp->base.bios_size, + GFP_KERNEL); + if (bp->base.bios_local_image == NULL) { + BREAK_TO_DEBUGGER(); + /* Failed to alloc bp->base.bios_local_image */ + return; + } + + memmove(bp->base.bios_local_image, bp->base.bios, bp->base.bios_size); + original_bios = bp->base.bios; + bp->base.bios = bp->base.bios_local_image; + connector_tbl = + GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); + + /* Step 2: (only if MXM connector found) Patch BIOS image with + * info from external module */ + if (mxm_connector_found && + patch_bios_image_from_ext_display_connection_info(bp) != + BP_RESULT_OK) { + /* Patching the bios image has failed. We will copy + * again original image provided and afterwards + * only remove null entries */ + memmove( + bp->base.bios_local_image, + original_bios, + bp->base.bios_size); + } + + /* Step 3: Compact connector table (remove null entries, valid + * entries moved to beginning) */ + for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { + object = &connector_tbl->asObjects[i]; + object_id = object_id_from_bios_object_id( + le16_to_cpu(object->usObjectID)); + + if (OBJECT_TYPE_CONNECTOR != object_id.type) + continue; + + if (i != connectors_num) { + memmove( + &connector_tbl-> + asObjects[connectors_num], + object, + sizeof(ATOM_OBJECT)); + } + ++connectors_num; + } + connector_tbl->ucNumberOfObjects = (uint8_t)connectors_num; + } +} + +static void bios_parser_post_init(struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + process_ext_display_connection_info(bp); +} + +/** + * bios_parser_set_scratch_critical_state + * + * @brief + * update critical state bit in VBIOS scratch register + * + * @param + * bool - to set or reset state + */ +static void bios_parser_set_scratch_critical_state( + struct dc_bios *dcb, + bool state) +{ + bios_set_scratch_critical_state(dcb, state); +} + +/* + * get_integrated_info_v8 + * + * @brief + * Get V8 integrated BIOS information + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * @return + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result get_integrated_info_v8( + struct bios_parser *bp, + struct integrated_info *info) +{ + ATOM_INTEGRATED_SYSTEM_INFO_V1_8 *info_v8; + uint32_t i; + + info_v8 = GET_IMAGE(ATOM_INTEGRATED_SYSTEM_INFO_V1_8, + bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); + + if (info_v8 == NULL) + return BP_RESULT_BADBIOSTABLE; + info->boot_up_engine_clock = le32_to_cpu(info_v8->ulBootUpEngineClock) * 10; + info->dentist_vco_freq = le32_to_cpu(info_v8->ulDentistVCOFreq) * 10; + info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10; + + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + /* Convert [10KHz] into [KHz] */ + info->disp_clk_voltage[i].max_supported_clk = + le32_to_cpu(info_v8->sDISPCLK_Voltage[i]. + ulMaximumSupportedCLK) * 10; + info->disp_clk_voltage[i].voltage_index = + le32_to_cpu(info_v8->sDISPCLK_Voltage[i].ulVoltageIndex); + } + + info->boot_up_req_display_vector = + le32_to_cpu(info_v8->ulBootUpReqDisplayVector); + info->gpu_cap_info = + le32_to_cpu(info_v8->ulGPUCapInfo); + + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v8->ulSystemConfig); + info->cpu_cap_info = le32_to_cpu(info_v8->ulCPUCapInfo); + info->boot_up_nb_voltage = + le16_to_cpu(info_v8->usBootUpNBVoltage); + info->ext_disp_conn_info_offset = + le16_to_cpu(info_v8->usExtDispConnInfoOffset); + info->memory_type = info_v8->ucMemoryType; + info->ma_channel_number = info_v8->ucUMAChannelNumber; + info->gmc_restore_reset_time = + le32_to_cpu(info_v8->ulGMCRestoreResetTime); + + info->minimum_n_clk = + le32_to_cpu(info_v8->ulNbpStateNClkFreq[0]); + for (i = 1; i < 4; ++i) + info->minimum_n_clk = + info->minimum_n_clk < le32_to_cpu(info_v8->ulNbpStateNClkFreq[i]) ? + info->minimum_n_clk : le32_to_cpu(info_v8->ulNbpStateNClkFreq[i]); + + info->idle_n_clk = le32_to_cpu(info_v8->ulIdleNClk); + info->ddr_dll_power_up_time = + le32_to_cpu(info_v8->ulDDR_DLL_PowerUpTime); + info->ddr_pll_power_up_time = + le32_to_cpu(info_v8->ulDDR_PLL_PowerUpTime); + info->pcie_clk_ss_type = le16_to_cpu(info_v8->usPCIEClkSSType); + info->lvds_ss_percentage = + le16_to_cpu(info_v8->usLvdsSSPercentage); + info->lvds_sspread_rate_in_10hz = + le16_to_cpu(info_v8->usLvdsSSpreadRateIn10Hz); + info->hdmi_ss_percentage = + le16_to_cpu(info_v8->usHDMISSPercentage); + info->hdmi_sspread_rate_in_10hz = + le16_to_cpu(info_v8->usHDMISSpreadRateIn10Hz); + info->dvi_ss_percentage = + le16_to_cpu(info_v8->usDVISSPercentage); + info->dvi_sspread_rate_in_10_hz = + le16_to_cpu(info_v8->usDVISSpreadRateIn10Hz); + + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v8->usMaxLVDSPclkFreqInSingleLink); + info->lvds_misc = info_v8->ucLvdsMisc; + info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = + info_v8->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = + info_v8->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = + info_v8->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = + info_v8->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + info->lvds_pwr_off_seq_de_to_dig_on_in4ms = + info_v8->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = + info_v8->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + info->lvds_off_to_on_delay_in_4ms = + info_v8->ucLVDSOffToOnDelay_in4Ms; + info->lvds_bit_depth_control_val = + le32_to_cpu(info_v8->ulLCDBitDepthControlVal); + + for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { + /* Convert [10KHz] into [KHz] */ + info->avail_s_clk[i].supported_s_clk = + le32_to_cpu(info_v8->sAvail_SCLK[i].ulSupportedSCLK) * 10; + info->avail_s_clk[i].voltage_index = + le16_to_cpu(info_v8->sAvail_SCLK[i].usVoltageIndex); + info->avail_s_clk[i].voltage_id = + le16_to_cpu(info_v8->sAvail_SCLK[i].usVoltageID); + } + + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v8->sExtDispConnInfo.ucGuid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceConnector)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usExtEncoderObjId)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceTag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceACPIEnum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v8->sExtDispConnInfo.sPath[i].ucExtAUXDDCLutIndex; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v8->sExtDispConnInfo.sPath[i].ucExtHPDPINLutIndex; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v8->sExtDispConnInfo.sPath[i].ucChannelMapping; + } + info->ext_disp_conn_info.checksum = + info_v8->sExtDispConnInfo.ucChecksum; + + return BP_RESULT_OK; +} + +/* + * get_integrated_info_v8 + * + * @brief + * Get V8 integrated BIOS information + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * @return + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result get_integrated_info_v9( + struct bios_parser *bp, + struct integrated_info *info) +{ + ATOM_INTEGRATED_SYSTEM_INFO_V1_9 *info_v9; + uint32_t i; + + info_v9 = GET_IMAGE(ATOM_INTEGRATED_SYSTEM_INFO_V1_9, + bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); + + if (!info_v9) + return BP_RESULT_BADBIOSTABLE; + + info->boot_up_engine_clock = le32_to_cpu(info_v9->ulBootUpEngineClock) * 10; + info->dentist_vco_freq = le32_to_cpu(info_v9->ulDentistVCOFreq) * 10; + info->boot_up_uma_clock = le32_to_cpu(info_v9->ulBootUpUMAClock) * 10; + + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + /* Convert [10KHz] into [KHz] */ + info->disp_clk_voltage[i].max_supported_clk = + le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulMaximumSupportedCLK) * 10; + info->disp_clk_voltage[i].voltage_index = + le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulVoltageIndex); + } + + info->boot_up_req_display_vector = + le32_to_cpu(info_v9->ulBootUpReqDisplayVector); + info->gpu_cap_info = le32_to_cpu(info_v9->ulGPUCapInfo); + + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v9->ulSystemConfig); + info->cpu_cap_info = le32_to_cpu(info_v9->ulCPUCapInfo); + info->boot_up_nb_voltage = le16_to_cpu(info_v9->usBootUpNBVoltage); + info->ext_disp_conn_info_offset = le16_to_cpu(info_v9->usExtDispConnInfoOffset); + info->memory_type = info_v9->ucMemoryType; + info->ma_channel_number = info_v9->ucUMAChannelNumber; + info->gmc_restore_reset_time = le32_to_cpu(info_v9->ulGMCRestoreResetTime); + + info->minimum_n_clk = le32_to_cpu(info_v9->ulNbpStateNClkFreq[0]); + for (i = 1; i < 4; ++i) + info->minimum_n_clk = + info->minimum_n_clk < le32_to_cpu(info_v9->ulNbpStateNClkFreq[i]) ? + info->minimum_n_clk : le32_to_cpu(info_v9->ulNbpStateNClkFreq[i]); + + info->idle_n_clk = le32_to_cpu(info_v9->ulIdleNClk); + info->ddr_dll_power_up_time = le32_to_cpu(info_v9->ulDDR_DLL_PowerUpTime); + info->ddr_pll_power_up_time = le32_to_cpu(info_v9->ulDDR_PLL_PowerUpTime); + info->pcie_clk_ss_type = le16_to_cpu(info_v9->usPCIEClkSSType); + info->lvds_ss_percentage = le16_to_cpu(info_v9->usLvdsSSPercentage); + info->lvds_sspread_rate_in_10hz = le16_to_cpu(info_v9->usLvdsSSpreadRateIn10Hz); + info->hdmi_ss_percentage = le16_to_cpu(info_v9->usHDMISSPercentage); + info->hdmi_sspread_rate_in_10hz = le16_to_cpu(info_v9->usHDMISSpreadRateIn10Hz); + info->dvi_ss_percentage = le16_to_cpu(info_v9->usDVISSPercentage); + info->dvi_sspread_rate_in_10_hz = le16_to_cpu(info_v9->usDVISSpreadRateIn10Hz); + + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v9->usMaxLVDSPclkFreqInSingleLink); + info->lvds_misc = info_v9->ucLvdsMisc; + info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = + info_v9->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = + info_v9->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = + info_v9->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = + info_v9->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + info->lvds_pwr_off_seq_de_to_dig_on_in4ms = + info_v9->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = + info_v9->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + info->lvds_off_to_on_delay_in_4ms = + info_v9->ucLVDSOffToOnDelay_in4Ms; + info->lvds_bit_depth_control_val = + le32_to_cpu(info_v9->ulLCDBitDepthControlVal); + + for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { + /* Convert [10KHz] into [KHz] */ + info->avail_s_clk[i].supported_s_clk = + le32_to_cpu(info_v9->sAvail_SCLK[i].ulSupportedSCLK) * 10; + info->avail_s_clk[i].voltage_index = + le16_to_cpu(info_v9->sAvail_SCLK[i].usVoltageIndex); + info->avail_s_clk[i].voltage_id = + le16_to_cpu(info_v9->sAvail_SCLK[i].usVoltageID); + } + + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v9->sExtDispConnInfo.ucGuid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceConnector)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usExtEncoderObjId)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceTag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceACPIEnum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v9->sExtDispConnInfo.sPath[i].ucExtAUXDDCLutIndex; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v9->sExtDispConnInfo.sPath[i].ucExtHPDPINLutIndex; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v9->sExtDispConnInfo.sPath[i].ucChannelMapping; + } + info->ext_disp_conn_info.checksum = + info_v9->sExtDispConnInfo.ucChecksum; + + return BP_RESULT_OK; +} + +/* + * construct_integrated_info + * + * @brief + * Get integrated BIOS information based on table revision + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * @return + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result construct_integrated_info( + struct bios_parser *bp, + struct integrated_info *info) +{ + enum bp_result result = BP_RESULT_BADBIOSTABLE; + + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision revision; + + if (bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo) { + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); + + get_atom_data_table_revision(header, &revision); + + /* Don't need to check major revision as they are all 1 */ + switch (revision.minor) { + case 8: + result = get_integrated_info_v8(bp, info); + break; + case 9: + result = get_integrated_info_v9(bp, info); + break; + default: + return result; + + } + } + + /* Sort voltage table from low to high*/ + if (result == BP_RESULT_OK) { + struct clock_voltage_caps temp = {0, 0}; + uint32_t i; + uint32_t j; + + for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + for (j = i; j > 0; --j) { + if ( + info->disp_clk_voltage[j].max_supported_clk < + info->disp_clk_voltage[j-1].max_supported_clk) { + /* swap j and j - 1*/ + temp = info->disp_clk_voltage[j-1]; + info->disp_clk_voltage[j-1] = + info->disp_clk_voltage[j]; + info->disp_clk_voltage[j] = temp; + } + } + } + + } + + return result; +} + +static struct integrated_info *bios_parser_create_integrated_info( + struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct integrated_info *info = NULL; + + info = kzalloc(sizeof(struct integrated_info), GFP_KERNEL); + + if (info == NULL) { + ASSERT_CRITICAL(0); + return NULL; + } + + if (construct_integrated_info(bp, info) == BP_RESULT_OK) + return info; + + kfree(info); + + return NULL; +} + +/******************************************************************************/ + +static const struct dc_vbios_funcs vbios_funcs = { + .get_connectors_number = bios_parser_get_connectors_number, + + .get_encoder_id = bios_parser_get_encoder_id, + + .get_connector_id = bios_parser_get_connector_id, + + .get_dst_number = bios_parser_get_dst_number, + + .get_src_obj = bios_parser_get_src_obj, + + .get_dst_obj = bios_parser_get_dst_obj, + + .get_i2c_info = bios_parser_get_i2c_info, + + .get_voltage_ddc_info = bios_parser_get_voltage_ddc_info, + + .get_thermal_ddc_info = bios_parser_get_thermal_ddc_info, + + .get_hpd_info = bios_parser_get_hpd_info, + + .get_device_tag = bios_parser_get_device_tag, + + .get_firmware_info = bios_parser_get_firmware_info, + + .get_spread_spectrum_info = bios_parser_get_spread_spectrum_info, + + .get_ss_entry_number = bios_parser_get_ss_entry_number, + + .get_embedded_panel_info = bios_parser_get_embedded_panel_info, + + .get_gpio_pin_info = bios_parser_get_gpio_pin_info, + + .get_embedded_panel_info = bios_parser_get_embedded_panel_info, + + .get_gpio_pin_info = bios_parser_get_gpio_pin_info, + + .get_encoder_cap_info = bios_parser_get_encoder_cap_info, + + /* bios scratch register communication */ + .is_accelerated_mode = bios_is_accelerated_mode, + + .set_scratch_critical_state = bios_parser_set_scratch_critical_state, + + .is_device_id_supported = bios_parser_is_device_id_supported, + + /* COMMANDS */ + .encoder_control = bios_parser_encoder_control, + + .transmitter_control = bios_parser_transmitter_control, + + .crt_control = bios_parser_crt_control, /* not used in DAL3. keep for now in case we need to support VGA on Bonaire */ + + .enable_crtc = bios_parser_enable_crtc, + + .adjust_pixel_clock = bios_parser_adjust_pixel_clock, + + .set_pixel_clock = bios_parser_set_pixel_clock, + + .set_dce_clock = bios_parser_set_dce_clock, + + .enable_spread_spectrum_on_ppll = bios_parser_enable_spread_spectrum_on_ppll, + + .program_crtc_timing = bios_parser_program_crtc_timing, /* still use. should probably retire and program directly */ + + .crtc_source_select = bios_parser_crtc_source_select, /* still use. should probably retire and program directly */ + + .program_display_engine_pll = bios_parser_program_display_engine_pll, + + .enable_disp_power_gating = bios_parser_enable_disp_power_gating, + + /* SW init and patch */ + .post_init = bios_parser_post_init, /* patch vbios table for mxm module by reading i2c */ + + .bios_parser_destroy = bios_parser_destroy, +}; + +static bool bios_parser_construct( + struct bios_parser *bp, + struct bp_init_data *init, + enum dce_version dce_version) +{ + uint16_t *rom_header_offset = NULL; + ATOM_ROM_HEADER *rom_header = NULL; + ATOM_OBJECT_HEADER *object_info_tbl; + struct atom_data_revision tbl_rev = {0}; + + if (!init) + return false; + + if (!init->bios) + return false; + + bp->base.funcs = &vbios_funcs; + bp->base.bios = init->bios; + bp->base.bios_size = bp->base.bios[BIOS_IMAGE_SIZE_OFFSET] * BIOS_IMAGE_SIZE_UNIT; + + bp->base.ctx = init->ctx; + bp->base.bios_local_image = NULL; + + rom_header_offset = + GET_IMAGE(uint16_t, OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER); + + if (!rom_header_offset) + return false; + + rom_header = GET_IMAGE(ATOM_ROM_HEADER, *rom_header_offset); + + if (!rom_header) + return false; + + get_atom_data_table_revision(&rom_header->sHeader, &tbl_rev); + if (tbl_rev.major >= 2 && tbl_rev.minor >= 2) + return false; + + bp->master_data_tbl = + GET_IMAGE(ATOM_MASTER_DATA_TABLE, + rom_header->usMasterDataTableOffset); + + if (!bp->master_data_tbl) + return false; + + bp->object_info_tbl_offset = DATA_TABLES(Object_Header); + + if (!bp->object_info_tbl_offset) + return false; + + object_info_tbl = + GET_IMAGE(ATOM_OBJECT_HEADER, bp->object_info_tbl_offset); + + if (!object_info_tbl) + return false; + + get_atom_data_table_revision(&object_info_tbl->sHeader, + &bp->object_info_tbl.revision); + + if (bp->object_info_tbl.revision.major == 1 + && bp->object_info_tbl.revision.minor >= 3) { + ATOM_OBJECT_HEADER_V3 *tbl_v3; + + tbl_v3 = GET_IMAGE(ATOM_OBJECT_HEADER_V3, + bp->object_info_tbl_offset); + if (!tbl_v3) + return false; + + bp->object_info_tbl.v1_3 = tbl_v3; + } else if (bp->object_info_tbl.revision.major == 1 + && bp->object_info_tbl.revision.minor >= 1) + bp->object_info_tbl.v1_1 = object_info_tbl; + else + return false; + + dal_bios_parser_init_cmd_tbl(bp); + dal_bios_parser_init_cmd_tbl_helper(&bp->cmd_helper, dce_version); + + bp->base.integrated_info = bios_parser_create_integrated_info(&bp->base); + + return true; +} + +/******************************************************************************/ diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.h new file mode 100644 index 000000000000..d6f16275048f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BIOS_PARSER_H__ +#define __DAL_BIOS_PARSER_H__ + +struct dc_bios *bios_parser_create( + struct bp_init_data *init, + enum dce_version dce_version); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c new file mode 100644 index 000000000000..1ee1717f2e6f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -0,0 +1,1934 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "ObjectID.h" +#include "atomfirmware.h" + +#include "dc_bios_types.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/bios_parser_interface.h" +#include "include/i2caux_interface.h" +#include "include/logger_interface.h" + +#include "command_table2.h" + +#include "bios_parser_helper.h" +#include "command_table_helper2.h" +#include "bios_parser2.h" +#include "bios_parser_types_internal2.h" +#include "bios_parser_interface.h" + +#include "bios_parser_common.h" +#define LAST_RECORD_TYPE 0xff + + +struct i2c_id_config_access { + uint8_t bfI2C_LineMux:4; + uint8_t bfHW_EngineID:3; + uint8_t bfHW_Capable:1; + uint8_t ucAccess; +}; + +static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, + struct atom_i2c_record *record, + struct graphics_object_i2c_info *info); + +static enum bp_result bios_parser_get_firmware_info( + struct dc_bios *dcb, + struct dc_firmware_info *info); + +static enum bp_result bios_parser_get_encoder_cap_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_encoder_cap_info *info); + +static enum bp_result get_firmware_info_v3_1( + struct bios_parser *bp, + struct dc_firmware_info *info); + +static struct atom_hpd_int_record *get_hpd_record(struct bios_parser *bp, + struct atom_display_object_path_v2 *object); + +static struct atom_encoder_caps_record *get_encoder_cap_record( + struct bios_parser *bp, + struct atom_display_object_path_v2 *object); + +#define BIOS_IMAGE_SIZE_OFFSET 2 +#define BIOS_IMAGE_SIZE_UNIT 512 + +#define DATA_TABLES(table) (bp->master_data_tbl->listOfdatatables.table) + + +static void destruct(struct bios_parser *bp) +{ + kfree(bp->base.bios_local_image); + kfree(bp->base.integrated_info); +} + +static void firmware_parser_destroy(struct dc_bios **dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(*dcb); + + if (!bp) { + BREAK_TO_DEBUGGER(); + return; + } + + destruct(bp); + + kfree(bp); + *dcb = NULL; +} + +static void get_atom_data_table_revision( + struct atom_common_table_header *atom_data_tbl, + struct atom_data_revision *tbl_revision) +{ + if (!tbl_revision) + return; + + /* initialize the revision to 0 which is invalid revision */ + tbl_revision->major = 0; + tbl_revision->minor = 0; + + if (!atom_data_tbl) + return; + + tbl_revision->major = + (uint32_t) atom_data_tbl->format_revision & 0x3f; + tbl_revision->minor = + (uint32_t) atom_data_tbl->content_revision & 0x3f; +} + +/* BIOS oject table displaypath is per connector. + * There is extra path not for connector. BIOS fill its encoderid as 0 + */ +static uint8_t bios_parser_get_connectors_number(struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + unsigned int count = 0; + unsigned int i; + + for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) { + if (bp->object_info_tbl.v1_4->display_path[i].encoderobjid != 0) + count++; + } + return count; +} + +static struct graphics_object_id bios_parser_get_encoder_id( + struct dc_bios *dcb, + uint32_t i) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct graphics_object_id object_id = dal_graphics_object_id_init( + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); + + if (bp->object_info_tbl.v1_4->number_of_path > i) + object_id = object_id_from_bios_object_id( + bp->object_info_tbl.v1_4->display_path[i].encoderobjid); + + return object_id; +} + +static struct graphics_object_id bios_parser_get_connector_id( + struct dc_bios *dcb, + uint8_t i) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct graphics_object_id object_id = dal_graphics_object_id_init( + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); + struct object_info_table *tbl = &bp->object_info_tbl; + struct display_object_info_table_v1_4 *v1_4 = tbl->v1_4; + + if (v1_4->number_of_path > i) { + /* If display_objid is generic object id, the encoderObj + * /extencoderobjId should be 0 + */ + if (v1_4->display_path[i].encoderobjid != 0 && + v1_4->display_path[i].display_objid != 0) + object_id = object_id_from_bios_object_id( + v1_4->display_path[i].display_objid); + } + + return object_id; +} + + +/* TODO: GetNumberOfSrc*/ + +static uint32_t bios_parser_get_dst_number(struct dc_bios *dcb, + struct graphics_object_id id) +{ + /* connector has 1 Dest, encoder has 0 Dest */ + switch (id.type) { + case OBJECT_TYPE_ENCODER: + return 0; + case OBJECT_TYPE_CONNECTOR: + return 1; + default: + return 0; + } +} + +/* removed getSrcObjList, getDestObjList*/ + + +static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *src_object_id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + unsigned int i; + enum bp_result bp_result = BP_RESULT_BADINPUT; + struct graphics_object_id obj_id = {0}; + struct object_info_table *tbl = &bp->object_info_tbl; + + if (!src_object_id) + return bp_result; + + switch (object_id.type) { + /* Encoder's Source is GPU. BIOS does not provide GPU, since all + * displaypaths point to same GPU (0x1100). Hardcode GPU object type + */ + case OBJECT_TYPE_ENCODER: + /* TODO: since num of src must be less than 2. + * If found in for loop, should break. + * DAL2 implementation may be changed too + */ + for (i = 0; i < tbl->v1_4->number_of_path; i++) { + obj_id = object_id_from_bios_object_id( + tbl->v1_4->display_path[i].encoderobjid); + if (object_id.type == obj_id.type && + object_id.id == obj_id.id && + object_id.enum_id == + obj_id.enum_id) { + *src_object_id = + object_id_from_bios_object_id(0x1100); + /* break; */ + } + } + bp_result = BP_RESULT_OK; + break; + case OBJECT_TYPE_CONNECTOR: + for (i = 0; i < tbl->v1_4->number_of_path; i++) { + obj_id = object_id_from_bios_object_id( + tbl->v1_4->display_path[i].display_objid); + + if (object_id.type == obj_id.type && + object_id.id == obj_id.id && + object_id.enum_id == obj_id.enum_id) { + *src_object_id = + object_id_from_bios_object_id( + tbl->v1_4->display_path[i].encoderobjid); + /* break; */ + } + } + bp_result = BP_RESULT_OK; + break; + default: + break; + } + + return bp_result; +} + +static enum bp_result bios_parser_get_dst_obj(struct dc_bios *dcb, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *dest_object_id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + unsigned int i; + enum bp_result bp_result = BP_RESULT_BADINPUT; + struct graphics_object_id obj_id = {0}; + struct object_info_table *tbl = &bp->object_info_tbl; + + if (!dest_object_id) + return BP_RESULT_BADINPUT; + + switch (object_id.type) { + case OBJECT_TYPE_ENCODER: + /* TODO: since num of src must be less than 2. + * If found in for loop, should break. + * DAL2 implementation may be changed too + */ + for (i = 0; i < tbl->v1_4->number_of_path; i++) { + obj_id = object_id_from_bios_object_id( + tbl->v1_4->display_path[i].encoderobjid); + if (object_id.type == obj_id.type && + object_id.id == obj_id.id && + object_id.enum_id == + obj_id.enum_id) { + *dest_object_id = + object_id_from_bios_object_id( + tbl->v1_4->display_path[i].display_objid); + /* break; */ + } + } + bp_result = BP_RESULT_OK; + break; + default: + break; + } + + return bp_result; +} + + +/* from graphics_object_id, find display path which includes the object_id */ +static struct atom_display_object_path_v2 *get_bios_object( + struct bios_parser *bp, + struct graphics_object_id id) +{ + unsigned int i; + struct graphics_object_id obj_id = {0}; + + switch (id.type) { + case OBJECT_TYPE_ENCODER: + for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) { + obj_id = object_id_from_bios_object_id( + bp->object_info_tbl.v1_4->display_path[i].encoderobjid); + if (id.type == obj_id.type && + id.id == obj_id.id && + id.enum_id == obj_id.enum_id) + return + &bp->object_info_tbl.v1_4->display_path[i]; + } + case OBJECT_TYPE_CONNECTOR: + case OBJECT_TYPE_GENERIC: + /* Both Generic and Connector Object ID + * will be stored on display_objid + */ + for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) { + obj_id = object_id_from_bios_object_id( + bp->object_info_tbl.v1_4->display_path[i].display_objid + ); + if (id.type == obj_id.type && + id.id == obj_id.id && + id.enum_id == obj_id.enum_id) + return + &bp->object_info_tbl.v1_4->display_path[i]; + } + default: + return NULL; + } +} + +static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_i2c_info *info) +{ + uint32_t offset; + struct atom_display_object_path_v2 *object; + struct atom_common_record_header *header; + struct atom_i2c_record *record; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (header->record_type == LAST_RECORD_TYPE || + !header->record_size) + break; + + if (header->record_type == ATOM_I2C_RECORD_TYPE + && sizeof(struct atom_i2c_record) <= + header->record_size) { + /* get the I2C info */ + record = (struct atom_i2c_record *) header; + + if (get_gpio_i2c_info(bp, record, info) == + BP_RESULT_OK) + return BP_RESULT_OK; + } + + offset += header->record_size; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result get_gpio_i2c_info( + struct bios_parser *bp, + struct atom_i2c_record *record, + struct graphics_object_i2c_info *info) +{ + struct atom_gpio_pin_lut_v2_1 *header; + uint32_t count = 0; + unsigned int table_index = 0; + + if (!info) + return BP_RESULT_BADINPUT; + + /* get the GPIO_I2C info */ + if (!DATA_TABLES(gpio_pin_lut)) + return BP_RESULT_BADBIOSTABLE; + + header = GET_IMAGE(struct atom_gpio_pin_lut_v2_1, + DATA_TABLES(gpio_pin_lut)); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(struct atom_common_table_header) + + sizeof(struct atom_gpio_pin_assignment) > + le16_to_cpu(header->table_header.structuresize)) + return BP_RESULT_BADBIOSTABLE; + + /* TODO: is version change? */ + if (header->table_header.content_revision != 1) + return BP_RESULT_UNSUPPORTED; + + /* get data count */ + count = (le16_to_cpu(header->table_header.structuresize) + - sizeof(struct atom_common_table_header)) + / sizeof(struct atom_gpio_pin_assignment); + + table_index = record->i2c_id & I2C_HW_LANE_MUX; + + if (count < table_index) { + bool find_valid = false; + + for (table_index = 0; table_index < count; table_index++) { + if (((record->i2c_id & I2C_HW_CAP) == ( + header->gpio_pin[table_index].gpio_id & + I2C_HW_CAP)) && + ((record->i2c_id & I2C_HW_ENGINE_ID_MASK) == + (header->gpio_pin[table_index].gpio_id & + I2C_HW_ENGINE_ID_MASK)) && + ((record->i2c_id & I2C_HW_LANE_MUX) == + (header->gpio_pin[table_index].gpio_id & + I2C_HW_LANE_MUX))) { + /* still valid */ + find_valid = true; + break; + } + } + /* If we don't find the entry that we are looking for then + * we will return BP_Result_BadBiosTable. + */ + if (find_valid == false) + return BP_RESULT_BADBIOSTABLE; + } + + /* get the GPIO_I2C_INFO */ + info->i2c_hw_assist = (record->i2c_id & I2C_HW_CAP) ? true : false; + info->i2c_line = record->i2c_id & I2C_HW_LANE_MUX; + info->i2c_engine_id = (record->i2c_id & I2C_HW_ENGINE_ID_MASK) >> 4; + info->i2c_slave_address = record->i2c_slave_addr; + + /* TODO: check how to get register offset for en, Y, etc. */ + info->gpio_info.clk_a_register_index = + le16_to_cpu( + header->gpio_pin[table_index].data_a_reg_index); + info->gpio_info.clk_a_shift = + header->gpio_pin[table_index].gpio_bitshift; + + return BP_RESULT_OK; +} + +static enum bp_result get_voltage_ddc_info_v4( + uint8_t *i2c_line, + uint32_t index, + struct atom_common_table_header *header, + uint8_t *address) +{ + enum bp_result result = BP_RESULT_NORECORD; + struct atom_voltage_objects_info_v4_1 *info = + (struct atom_voltage_objects_info_v4_1 *) address; + + uint8_t *voltage_current_object = + (uint8_t *) (&(info->voltage_object[0])); + + while ((address + le16_to_cpu(header->structuresize)) > + voltage_current_object) { + struct atom_i2c_voltage_object_v4 *object = + (struct atom_i2c_voltage_object_v4 *) + voltage_current_object; + + if (object->header.voltage_mode == + ATOM_INIT_VOLTAGE_REGULATOR) { + if (object->header.voltage_type == index) { + *i2c_line = object->i2c_id ^ 0x90; + result = BP_RESULT_OK; + break; + } + } + + voltage_current_object += + le16_to_cpu(object->header.object_size); + } + return result; +} + +static enum bp_result bios_parser_get_thermal_ddc_info( + struct dc_bios *dcb, + uint32_t i2c_channel_id, + struct graphics_object_i2c_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct i2c_id_config_access *config; + struct atom_i2c_record record; + + if (!info) + return BP_RESULT_BADINPUT; + + config = (struct i2c_id_config_access *) &i2c_channel_id; + + record.i2c_id = config->bfHW_Capable; + record.i2c_id |= config->bfI2C_LineMux; + record.i2c_id |= config->bfHW_EngineID; + + return get_gpio_i2c_info(bp, &record, info); +} + +static enum bp_result bios_parser_get_voltage_ddc_info(struct dc_bios *dcb, + uint32_t index, + struct graphics_object_i2c_info *info) +{ + uint8_t i2c_line = 0; + enum bp_result result = BP_RESULT_NORECORD; + uint8_t *voltage_info_address; + struct atom_common_table_header *header; + struct atom_data_revision revision = {0}; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!DATA_TABLES(voltageobject_info)) + return result; + + voltage_info_address = bios_get_image(&bp->base, + DATA_TABLES(voltageobject_info), + sizeof(struct atom_common_table_header)); + + header = (struct atom_common_table_header *) voltage_info_address; + + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 4: + if (revision.minor != 1) + break; + result = get_voltage_ddc_info_v4(&i2c_line, index, header, + voltage_info_address); + break; + } + + if (result == BP_RESULT_OK) + result = bios_parser_get_thermal_ddc_info(dcb, + i2c_line, info); + + return result; +} + +static enum bp_result bios_parser_get_hpd_info( + struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_hpd_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v2 *object; + struct atom_hpd_int_record *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + record = get_hpd_record(bp, object); + + if (record != NULL) { + info->hpd_int_gpio_uid = record->pin_id; + info->hpd_active = record->plugin_pin_state; + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static struct atom_hpd_int_record *get_hpd_record( + struct bios_parser *bp, + struct atom_display_object_path_v2 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->disp_recordoffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + if (header->record_type == LAST_RECORD_TYPE || + !header->record_size) + break; + + if (header->record_type == ATOM_HPD_INT_RECORD_TYPE + && sizeof(struct atom_hpd_int_record) <= + header->record_size) + return (struct atom_hpd_int_record *) header; + + offset += header->record_size; + } + + return NULL; +} + +/** + * bios_parser_get_gpio_pin_info + * Get GpioPin information of input gpio id + * + * @param gpio_id, GPIO ID + * @param info, GpioPin information structure + * @return Bios parser result code + * @note + * to get the GPIO PIN INFO, we need: + * 1. get the GPIO_ID from other object table, see GetHPDInfo() + * 2. in DATA_TABLE.GPIO_Pin_LUT, search all records, + * to get the registerA offset/mask + */ +static enum bp_result bios_parser_get_gpio_pin_info( + struct dc_bios *dcb, + uint32_t gpio_id, + struct gpio_pin_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_gpio_pin_lut_v2_1 *header; + uint32_t count = 0; + uint32_t i = 0; + + if (!DATA_TABLES(gpio_pin_lut)) + return BP_RESULT_BADBIOSTABLE; + + header = GET_IMAGE(struct atom_gpio_pin_lut_v2_1, + DATA_TABLES(gpio_pin_lut)); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(struct atom_common_table_header) + + sizeof(struct atom_gpio_pin_lut_v2_1) + > le16_to_cpu(header->table_header.structuresize)) + return BP_RESULT_BADBIOSTABLE; + + if (header->table_header.content_revision != 1) + return BP_RESULT_UNSUPPORTED; + + /* Temporary hard code gpio pin info */ +#if defined(FOR_SIMNOW_BOOT) + { + struct atom_gpio_pin_assignment gpio_pin[8] = { + {0x5db5, 0, 0, 1, 0}, + {0x5db5, 8, 8, 2, 0}, + {0x5db5, 0x10, 0x10, 3, 0}, + {0x5db5, 0x18, 0x14, 4, 0}, + {0x5db5, 0x1A, 0x18, 5, 0}, + {0x5db5, 0x1C, 0x1C, 6, 0}, + }; + + count = 6; + memmove(header->gpio_pin, gpio_pin, sizeof(gpio_pin)); + } +#else + count = (le16_to_cpu(header->table_header.structuresize) + - sizeof(struct atom_common_table_header)) + / sizeof(struct atom_gpio_pin_assignment); +#endif + for (i = 0; i < count; ++i) { + if (header->gpio_pin[i].gpio_id != gpio_id) + continue; + + info->offset = + (uint32_t) le16_to_cpu( + header->gpio_pin[i].data_a_reg_index); + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask = (uint32_t) (1 << + header->gpio_pin[i].gpio_bitshift); + info->mask_y = info->mask + 2; + info->mask_en = info->mask + 1; + info->mask_mask = info->mask - 1; + + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static struct device_id device_type_from_device_id(uint16_t device_id) +{ + + struct device_id result_device_id; + + result_device_id.raw_device_tag = device_id; + + switch (device_id) { + case ATOM_DISPLAY_LCD1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_LCD; + result_device_id.enum_id = 1; + break; + + case ATOM_DISPLAY_DFP1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 1; + break; + + case ATOM_DISPLAY_DFP2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 2; + break; + + case ATOM_DISPLAY_DFP3_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 3; + break; + + case ATOM_DISPLAY_DFP4_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 4; + break; + + case ATOM_DISPLAY_DFP5_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 5; + break; + + case ATOM_DISPLAY_DFP6_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 6; + break; + + default: + BREAK_TO_DEBUGGER(); /* Invalid device Id */ + result_device_id.device_type = DEVICE_TYPE_UNKNOWN; + result_device_id.enum_id = 0; + } + return result_device_id; +} + +static enum bp_result bios_parser_get_device_tag( + struct dc_bios *dcb, + struct graphics_object_id connector_object_id, + uint32_t device_tag_index, + struct connector_device_tag_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v2 *object; + + if (!info) + return BP_RESULT_BADINPUT; + + /* getBiosObject will return MXM object */ + object = get_bios_object(bp, connector_object_id); + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + + info->acpi_device = 0; /* BIOS no longer provides this */ + info->dev_id = device_type_from_device_id(object->device_tag); + + return BP_RESULT_OK; +} + +static enum bp_result get_ss_info_v4_1( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_1 *disp_cntl_tbl = NULL; + + if (!ss_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_1, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + ss_info->type.STEP_AND_DELAY_INFO = false; + ss_info->spread_percentage_divider = 1000; + /* BIOS no longer uses target clock. Always enable for now */ + ss_info->target_clock_range = 0xffffffff; + + switch (id) { + case AS_SIGNAL_TYPE_DVI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->dvi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->dvi_ss_rate_10hz * 10; + if (disp_cntl_tbl->dvi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + break; + case AS_SIGNAL_TYPE_HDMI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->hdmi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->hdmi_ss_rate_10hz * 10; + if (disp_cntl_tbl->hdmi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + break; + /* TODO LVDS not support anymore? */ + case AS_SIGNAL_TYPE_DISPLAY_PORT: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->dp_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->dp_ss_rate_10hz * 10; + if (disp_cntl_tbl->dp_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + break; + case AS_SIGNAL_TYPE_GPU_PLL: + /* atom_firmware: DAL only get data from dce_info table. + * if data within smu_info is needed for DAL, VBIOS should + * copy it into dce_info + */ + result = BP_RESULT_UNSUPPORTED; + break; + default: + result = BP_RESULT_UNSUPPORTED; + } + + return result; +} + +static enum bp_result get_ss_info_v4_2( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_2 *disp_cntl_tbl = NULL; + struct atom_smu_info_v3_1 *smu_info = NULL; + + if (!ss_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + if (!DATA_TABLES(smu_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_2, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + smu_info = GET_IMAGE(struct atom_smu_info_v3_1, DATA_TABLES(smu_info)); + if (!smu_info) + return BP_RESULT_BADBIOSTABLE; + + ss_info->type.STEP_AND_DELAY_INFO = false; + ss_info->spread_percentage_divider = 1000; + /* BIOS no longer uses target clock. Always enable for now */ + ss_info->target_clock_range = 0xffffffff; + + switch (id) { + case AS_SIGNAL_TYPE_DVI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->dvi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->dvi_ss_rate_10hz * 10; + if (disp_cntl_tbl->dvi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + break; + case AS_SIGNAL_TYPE_HDMI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->hdmi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->hdmi_ss_rate_10hz * 10; + if (disp_cntl_tbl->hdmi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + break; + /* TODO LVDS not support anymore? */ + case AS_SIGNAL_TYPE_DISPLAY_PORT: + ss_info->spread_spectrum_percentage = + smu_info->gpuclk_ss_percentage; + ss_info->spread_spectrum_range = + smu_info->gpuclk_ss_rate_10hz * 10; + if (smu_info->gpuclk_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + break; + case AS_SIGNAL_TYPE_GPU_PLL: + /* atom_firmware: DAL only get data from dce_info table. + * if data within smu_info is needed for DAL, VBIOS should + * copy it into dce_info + */ + result = BP_RESULT_UNSUPPORTED; + break; + default: + result = BP_RESULT_UNSUPPORTED; + } + + return result; +} + +/** + * bios_parser_get_spread_spectrum_info + * Get spread spectrum information from the ASIC_InternalSS_Info(ver 2.1 or + * ver 3.1) or SS_Info table from the VBIOS. Currently ASIC_InternalSS_Info + * ver 2.1 can co-exist with SS_Info table. Expect ASIC_InternalSS_Info + * ver 3.1, + * there is only one entry for each signal /ss id. However, there is + * no planning of supporting multiple spread Sprectum entry for EverGreen + * @param [in] this + * @param [in] signal, ASSignalType to be converted to info index + * @param [in] index, number of entries that match the converted info index + * @param [out] ss_info, sprectrum information structure, + * @return Bios parser result code + */ +static enum bp_result bios_parser_get_spread_spectrum_info( + struct dc_bios *dcb, + enum as_signal_type signal, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + enum bp_result result = BP_RESULT_UNSUPPORTED; + struct atom_common_table_header *header; + struct atom_data_revision tbl_revision; + + if (!ss_info) /* check for bad input */ + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_UNSUPPORTED; + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(dce_info)); + get_atom_data_table_revision(header, &tbl_revision); + + switch (tbl_revision.major) { + case 4: + switch (tbl_revision.minor) { + case 1: + return get_ss_info_v4_1(bp, signal, index, ss_info); + case 2: + return get_ss_info_v4_2(bp, signal, index, ss_info); + default: + break; + } + break; + default: + break; + } + /* there can not be more then one entry for SS Info table */ + return result; +} + +static enum bp_result get_embedded_panel_info_v2_1( + struct bios_parser *bp, + struct embedded_panel_info *info) +{ + struct lcd_info_v2_1 *lvds; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(lcd_info)) + return BP_RESULT_UNSUPPORTED; + + lvds = GET_IMAGE(struct lcd_info_v2_1, DATA_TABLES(lcd_info)); + + if (!lvds) + return BP_RESULT_BADBIOSTABLE; + + /* TODO: previous vv1_3, should v2_1 */ + if (!((lvds->table_header.format_revision == 2) + && (lvds->table_header.content_revision >= 1))) + return BP_RESULT_UNSUPPORTED; + + memset(info, 0, sizeof(struct embedded_panel_info)); + + /* We need to convert from 10KHz units into KHz units */ + info->lcd_timing.pixel_clk = + le16_to_cpu(lvds->lcd_timing.pixclk) * 10; + /* usHActive does not include borders, according to VBIOS team */ + info->lcd_timing.horizontal_addressable = + le16_to_cpu(lvds->lcd_timing.h_active); + /* usHBlanking_Time includes borders, so we should really be + * subtractingborders duing this translation, but LVDS generally + * doesn't have borders, so we should be okay leaving this as is for + * now. May need to revisit if we ever have LVDS with borders + */ + info->lcd_timing.horizontal_blanking_time = + le16_to_cpu(lvds->lcd_timing.h_blanking_time); + /* usVActive does not include borders, according to VBIOS team*/ + info->lcd_timing.vertical_addressable = + le16_to_cpu(lvds->lcd_timing.v_active); + /* usVBlanking_Time includes borders, so we should really be + * subtracting borders duing this translation, but LVDS generally + * doesn't have borders, so we should be okay leaving this as is for + * now. May need to revisit if we ever have LVDS with borders + */ + info->lcd_timing.vertical_blanking_time = + le16_to_cpu(lvds->lcd_timing.v_blanking_time); + info->lcd_timing.horizontal_sync_offset = + le16_to_cpu(lvds->lcd_timing.h_sync_offset); + info->lcd_timing.horizontal_sync_width = + le16_to_cpu(lvds->lcd_timing.h_sync_width); + info->lcd_timing.vertical_sync_offset = + le16_to_cpu(lvds->lcd_timing.v_sync_offset); + info->lcd_timing.vertical_sync_width = + le16_to_cpu(lvds->lcd_timing.v_syncwidth); + info->lcd_timing.horizontal_border = lvds->lcd_timing.h_border; + info->lcd_timing.vertical_border = lvds->lcd_timing.v_border; + + /* not provided by VBIOS */ + info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = 0; + + info->lcd_timing.misc_info.H_SYNC_POLARITY = + ~(uint32_t) + (lvds->lcd_timing.miscinfo & ATOM_HSYNC_POLARITY); + info->lcd_timing.misc_info.V_SYNC_POLARITY = + ~(uint32_t) + (lvds->lcd_timing.miscinfo & ATOM_VSYNC_POLARITY); + + /* not provided by VBIOS */ + info->lcd_timing.misc_info.VERTICAL_CUT_OFF = 0; + + info->lcd_timing.misc_info.H_REPLICATION_BY2 = + !!(lvds->lcd_timing.miscinfo & ATOM_H_REPLICATIONBY2); + info->lcd_timing.misc_info.V_REPLICATION_BY2 = + !!(lvds->lcd_timing.miscinfo & ATOM_V_REPLICATIONBY2); + info->lcd_timing.misc_info.COMPOSITE_SYNC = + !!(lvds->lcd_timing.miscinfo & ATOM_COMPOSITESYNC); + info->lcd_timing.misc_info.INTERLACE = + !!(lvds->lcd_timing.miscinfo & ATOM_INTERLACE); + + /* not provided by VBIOS*/ + info->lcd_timing.misc_info.DOUBLE_CLOCK = 0; + /* not provided by VBIOS*/ + info->ss_id = 0; + + info->realtek_eDPToLVDS = + !!(lvds->dplvdsrxid == eDP_TO_LVDS_REALTEK_ID); + + return BP_RESULT_OK; +} + +static enum bp_result bios_parser_get_embedded_panel_info( + struct dc_bios *dcb, + struct embedded_panel_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_common_table_header *header; + struct atom_data_revision tbl_revision; + + if (!DATA_TABLES(lcd_info)) + return BP_RESULT_FAILURE; + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(lcd_info)); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + get_atom_data_table_revision(header, &tbl_revision); + + + switch (tbl_revision.major) { + case 2: + switch (tbl_revision.minor) { + case 1: + return get_embedded_panel_info_v2_1(bp, info); + default: + break; + } + default: + break; + } + + return BP_RESULT_FAILURE; +} + +static uint32_t get_support_mask_for_device_id(struct device_id device_id) +{ + enum dal_device_type device_type = device_id.device_type; + uint32_t enum_id = device_id.enum_id; + + switch (device_type) { + case DEVICE_TYPE_LCD: + switch (enum_id) { + case 1: + return ATOM_DISPLAY_LCD1_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_DFP: + switch (enum_id) { + case 1: + return ATOM_DISPLAY_DFP1_SUPPORT; + case 2: + return ATOM_DISPLAY_DFP2_SUPPORT; + case 3: + return ATOM_DISPLAY_DFP3_SUPPORT; + case 4: + return ATOM_DISPLAY_DFP4_SUPPORT; + case 5: + return ATOM_DISPLAY_DFP5_SUPPORT; + case 6: + return ATOM_DISPLAY_DFP6_SUPPORT; + default: + break; + } + break; + default: + break; + }; + + /* Unidentified device ID, return empty support mask. */ + return 0; +} + +static bool bios_parser_is_device_id_supported( + struct dc_bios *dcb, + struct device_id id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + uint32_t mask = get_support_mask_for_device_id(id); + + return (le16_to_cpu(bp->object_info_tbl.v1_4->supporteddevices) & + mask) != 0; +} + +static void bios_parser_post_init( + struct dc_bios *dcb) +{ + /* TODO for OPM module. Need implement later */ +} + +static uint32_t bios_parser_get_ss_entry_number( + struct dc_bios *dcb, + enum as_signal_type signal) +{ + /* TODO: DAL2 atomfirmware implementation does not need this. + * why DAL3 need this? + */ + return 1; +} + +static enum bp_result bios_parser_transmitter_control( + struct dc_bios *dcb, + struct bp_transmitter_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.transmitter_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.transmitter_control(bp, cntl); +} + +static enum bp_result bios_parser_encoder_control( + struct dc_bios *dcb, + struct bp_encoder_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.dig_encoder_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.dig_encoder_control(bp, cntl); +} + +static enum bp_result bios_parser_set_pixel_clock( + struct dc_bios *dcb, + struct bp_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_pixel_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_pixel_clock(bp, bp_params); +} + +static enum bp_result bios_parser_set_dce_clock( + struct dc_bios *dcb, + struct bp_set_dce_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_dce_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_dce_clock(bp, bp_params); +} + +static unsigned int bios_parser_get_smu_clock_info( + struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.get_smu_clock_info) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.get_smu_clock_info(bp); +} + +static enum bp_result bios_parser_program_crtc_timing( + struct dc_bios *dcb, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_crtc_timing) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_crtc_timing(bp, bp_params); +} + +static enum bp_result bios_parser_enable_crtc( + struct dc_bios *dcb, + enum controller_id id, + bool enable) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_crtc) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_crtc(bp, id, enable); +} + +static enum bp_result bios_parser_crtc_source_select( + struct dc_bios *dcb, + struct bp_crtc_source_select *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.select_crtc_source) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.select_crtc_source(bp, bp_params); +} + +static enum bp_result bios_parser_enable_disp_power_gating( + struct dc_bios *dcb, + enum controller_id controller_id, + enum bp_pipe_control_action action) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_disp_power_gating) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_disp_power_gating(bp, controller_id, + action); +} + +static bool bios_parser_is_accelerated_mode( + struct dc_bios *dcb) +{ + return bios_is_accelerated_mode(dcb); +} + + +/** + * bios_parser_set_scratch_critical_state + * + * @brief + * update critical state bit in VBIOS scratch register + * + * @param + * bool - to set or reset state + */ +static void bios_parser_set_scratch_critical_state( + struct dc_bios *dcb, + bool state) +{ + bios_set_scratch_critical_state(dcb, state); +} + +static enum bp_result bios_parser_get_firmware_info( + struct dc_bios *dcb, + struct dc_firmware_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + enum bp_result result = BP_RESULT_BADBIOSTABLE; + struct atom_common_table_header *header; + + struct atom_data_revision revision; + + if (info && DATA_TABLES(firmwareinfo)) { + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(firmwareinfo)); + get_atom_data_table_revision(header, &revision); + switch (revision.major) { + case 3: + switch (revision.minor) { + case 1: + result = get_firmware_info_v3_1(bp, info); + break; + default: + break; + } + break; + default: + break; + } + } + + return result; +} + +static enum bp_result get_firmware_info_v3_1( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + struct atom_firmware_info_v3_1 *firmware_info; + struct atom_display_controller_info_v4_1 *dce_info = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + firmware_info = GET_IMAGE(struct atom_firmware_info_v3_1, + DATA_TABLES(firmwareinfo)); + + dce_info = GET_IMAGE(struct atom_display_controller_info_v4_1, + DATA_TABLES(dce_info)); + + if (!firmware_info || !dce_info) + return BP_RESULT_BADBIOSTABLE; + + memset(info, 0, sizeof(*info)); + + /* Pixel clock pll information. */ + /* We need to convert from 10KHz units into KHz units */ + info->default_memory_clk = firmware_info->bootup_mclk_in10khz * 10; + info->default_engine_clk = firmware_info->bootup_sclk_in10khz * 10; + + /* 27MHz for Vega10: */ + info->pll_info.crystal_frequency = dce_info->dce_refclk_10khz * 10; + + /* Hardcode frequency if BIOS gives no DCE Ref Clk */ + if (info->pll_info.crystal_frequency == 0) + info->pll_info.crystal_frequency = 27000; + /*dp_phy_ref_clk is not correct for atom_display_controller_info_v4_2, but we don't use it*/ + info->dp_phy_ref_clk = dce_info->dpphy_refclk_10khz * 10; + info->i2c_engine_ref_clk = dce_info->i2c_engine_refclk_10khz * 10; + + /* Get GPU PLL VCO Clock */ + + if (bp->cmd_tbl.get_smu_clock_info != NULL) { + /* VBIOS gives in 10KHz */ + info->smu_gpu_pll_output_freq = + bp->cmd_tbl.get_smu_clock_info(bp) * 10; + } + + return BP_RESULT_OK; +} + +static enum bp_result bios_parser_get_encoder_cap_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_encoder_cap_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v2 *object; + struct atom_encoder_caps_record *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, object_id); + + if (!object) + return BP_RESULT_BADINPUT; + + record = get_encoder_cap_record(bp, object); + if (!record) + return BP_RESULT_NORECORD; + + info->DP_HBR2_CAP = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HBR2) ? 1 : 0; + info->DP_HBR2_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HBR2_EN) ? 1 : 0; + info->DP_HBR3_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HBR3_EN) ? 1 : 0; + info->HDMI_6GB_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HDMI6Gbps_EN) ? 1 : 0; + + return BP_RESULT_OK; +} + + +static struct atom_encoder_caps_record *get_encoder_cap_record( + struct bios_parser *bp, + struct atom_display_object_path_v2 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = object->encoder_recordoffset + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + offset += header->record_size; + + if (header->record_type == LAST_RECORD_TYPE || + !header->record_size) + break; + + if (header->record_type != ATOM_ENCODER_CAP_RECORD_TYPE) + continue; + + if (sizeof(struct atom_encoder_caps_record) <= + header->record_size) + return (struct atom_encoder_caps_record *)header; + } + + return NULL; +} + +/* + * get_integrated_info_v11 + * + * @brief + * Get V8 integrated BIOS information + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * @return + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result get_integrated_info_v11( + struct bios_parser *bp, + struct integrated_info *info) +{ + struct atom_integrated_system_info_v1_11 *info_v11; + uint32_t i; + + info_v11 = GET_IMAGE(struct atom_integrated_system_info_v1_11, + DATA_TABLES(integratedsysteminfo)); + + if (info_v11 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->gpu_cap_info = + le32_to_cpu(info_v11->gpucapinfo); + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v11->system_config); + info->cpu_cap_info = le32_to_cpu(info_v11->cpucapinfo); + info->memory_type = info_v11->memorytype; + info->ma_channel_number = info_v11->umachannelnumber; + info->lvds_ss_percentage = + le16_to_cpu(info_v11->lvds_ss_percentage); + info->lvds_sspread_rate_in_10hz = + le16_to_cpu(info_v11->lvds_ss_rate_10hz); + info->hdmi_ss_percentage = + le16_to_cpu(info_v11->hdmi_ss_percentage); + info->hdmi_sspread_rate_in_10hz = + le16_to_cpu(info_v11->hdmi_ss_rate_10hz); + info->dvi_ss_percentage = + le16_to_cpu(info_v11->dvi_ss_percentage); + info->dvi_sspread_rate_in_10_hz = + le16_to_cpu(info_v11->dvi_ss_rate_10hz); + info->lvds_misc = info_v11->lvds_misc; + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v11->extdispconninfo.guid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v11->extdispconninfo.path[i].connectorobjid)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu( + info_v11->extdispconninfo.path[i].ext_encoder_objid)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu( + info_v11->extdispconninfo.path[i].device_tag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu( + info_v11->extdispconninfo.path[i].device_acpi_enum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v11->extdispconninfo.path[i].auxddclut_index; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v11->extdispconninfo.path[i].hpdlut_index; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v11->extdispconninfo.path[i].channelmapping; + info->ext_disp_conn_info.path[i].caps = + le16_to_cpu(info_v11->extdispconninfo.path[i].caps); + } + info->ext_disp_conn_info.checksum = + info_v11->extdispconninfo.checksum; + + info->dp0_ext_hdmi_slv_addr = info_v11->dp0_retimer_set.HdmiSlvAddr; + info->dp0_ext_hdmi_reg_num = info_v11->dp0_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { + info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp0_ext_hdmi_6g_reg_num = info_v11->dp0_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { + info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + info->dp1_ext_hdmi_slv_addr = info_v11->dp1_retimer_set.HdmiSlvAddr; + info->dp1_ext_hdmi_reg_num = info_v11->dp1_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { + info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp1_ext_hdmi_6g_reg_num = info_v11->dp1_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { + info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + info->dp2_ext_hdmi_slv_addr = info_v11->dp2_retimer_set.HdmiSlvAddr; + info->dp2_ext_hdmi_reg_num = info_v11->dp2_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { + info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp2_ext_hdmi_6g_reg_num = info_v11->dp2_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { + info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + info->dp3_ext_hdmi_slv_addr = info_v11->dp3_retimer_set.HdmiSlvAddr; + info->dp3_ext_hdmi_reg_num = info_v11->dp3_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { + info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp3_ext_hdmi_6g_reg_num = info_v11->dp3_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { + info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + + /** TODO - review **/ + #if 0 + info->boot_up_engine_clock = le32_to_cpu(info_v11->ulBootUpEngineClock) + * 10; + info->dentist_vco_freq = le32_to_cpu(info_v11->ulDentistVCOFreq) * 10; + info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10; + + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + /* Convert [10KHz] into [KHz] */ + info->disp_clk_voltage[i].max_supported_clk = + le32_to_cpu(info_v11->sDISPCLK_Voltage[i]. + ulMaximumSupportedCLK) * 10; + info->disp_clk_voltage[i].voltage_index = + le32_to_cpu(info_v11->sDISPCLK_Voltage[i].ulVoltageIndex); + } + + info->boot_up_req_display_vector = + le32_to_cpu(info_v11->ulBootUpReqDisplayVector); + info->boot_up_nb_voltage = + le16_to_cpu(info_v11->usBootUpNBVoltage); + info->ext_disp_conn_info_offset = + le16_to_cpu(info_v11->usExtDispConnInfoOffset); + info->gmc_restore_reset_time = + le32_to_cpu(info_v11->ulGMCRestoreResetTime); + info->minimum_n_clk = + le32_to_cpu(info_v11->ulNbpStateNClkFreq[0]); + for (i = 1; i < 4; ++i) + info->minimum_n_clk = + info->minimum_n_clk < + le32_to_cpu(info_v11->ulNbpStateNClkFreq[i]) ? + info->minimum_n_clk : le32_to_cpu( + info_v11->ulNbpStateNClkFreq[i]); + + info->idle_n_clk = le32_to_cpu(info_v11->ulIdleNClk); + info->ddr_dll_power_up_time = + le32_to_cpu(info_v11->ulDDR_DLL_PowerUpTime); + info->ddr_pll_power_up_time = + le32_to_cpu(info_v11->ulDDR_PLL_PowerUpTime); + info->pcie_clk_ss_type = le16_to_cpu(info_v11->usPCIEClkSSType); + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v11->usMaxLVDSPclkFreqInSingleLink); + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v11->usMaxLVDSPclkFreqInSingleLink); + info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = + info_v11->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = + info_v11->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = + info_v11->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = + info_v11->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + info->lvds_pwr_off_seq_de_to_dig_on_in4ms = + info_v11->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = + info_v11->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + info->lvds_off_to_on_delay_in_4ms = + info_v11->ucLVDSOffToOnDelay_in4Ms; + info->lvds_bit_depth_control_val = + le32_to_cpu(info_v11->ulLCDBitDepthControlVal); + + for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { + /* Convert [10KHz] into [KHz] */ + info->avail_s_clk[i].supported_s_clk = + le32_to_cpu(info_v11->sAvail_SCLK[i].ulSupportedSCLK) + * 10; + info->avail_s_clk[i].voltage_index = + le16_to_cpu(info_v11->sAvail_SCLK[i].usVoltageIndex); + info->avail_s_clk[i].voltage_id = + le16_to_cpu(info_v11->sAvail_SCLK[i].usVoltageID); + } + #endif /* TODO*/ + + return BP_RESULT_OK; +} + + +/* + * construct_integrated_info + * + * @brief + * Get integrated BIOS information based on table revision + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * @return + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result construct_integrated_info( + struct bios_parser *bp, + struct integrated_info *info) +{ + enum bp_result result = BP_RESULT_BADBIOSTABLE; + + struct atom_common_table_header *header; + struct atom_data_revision revision; + + struct clock_voltage_caps temp = {0, 0}; + uint32_t i; + uint32_t j; + + if (info && DATA_TABLES(integratedsysteminfo)) { + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(integratedsysteminfo)); + + get_atom_data_table_revision(header, &revision); + + /* Don't need to check major revision as they are all 1 */ + switch (revision.minor) { + case 11: + result = get_integrated_info_v11(bp, info); + break; + default: + return result; + } + } + + if (result != BP_RESULT_OK) + return result; + + /* Sort voltage table from low to high*/ + for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + for (j = i; j > 0; --j) { + if (info->disp_clk_voltage[j].max_supported_clk < + info->disp_clk_voltage[j-1].max_supported_clk + ) { + /* swap j and j - 1*/ + temp = info->disp_clk_voltage[j-1]; + info->disp_clk_voltage[j-1] = + info->disp_clk_voltage[j]; + info->disp_clk_voltage[j] = temp; + } + } + } + + return result; +} + +static struct integrated_info *bios_parser_create_integrated_info( + struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct integrated_info *info = NULL; + + info = kzalloc(sizeof(struct integrated_info), GFP_KERNEL); + + if (info == NULL) { + ASSERT_CRITICAL(0); + return NULL; + } + + if (construct_integrated_info(bp, info) == BP_RESULT_OK) + return info; + + kfree(info); + + return NULL; +} + +static const struct dc_vbios_funcs vbios_funcs = { + .get_connectors_number = bios_parser_get_connectors_number, + + .get_encoder_id = bios_parser_get_encoder_id, + + .get_connector_id = bios_parser_get_connector_id, + + .get_dst_number = bios_parser_get_dst_number, + + .get_src_obj = bios_parser_get_src_obj, + + .get_dst_obj = bios_parser_get_dst_obj, + + .get_i2c_info = bios_parser_get_i2c_info, + + .get_voltage_ddc_info = bios_parser_get_voltage_ddc_info, + + .get_thermal_ddc_info = bios_parser_get_thermal_ddc_info, + + .get_hpd_info = bios_parser_get_hpd_info, + + .get_device_tag = bios_parser_get_device_tag, + + .get_firmware_info = bios_parser_get_firmware_info, + + .get_spread_spectrum_info = bios_parser_get_spread_spectrum_info, + + .get_ss_entry_number = bios_parser_get_ss_entry_number, + + .get_embedded_panel_info = bios_parser_get_embedded_panel_info, + + .get_gpio_pin_info = bios_parser_get_gpio_pin_info, + + .get_encoder_cap_info = bios_parser_get_encoder_cap_info, + + .is_device_id_supported = bios_parser_is_device_id_supported, + + + + .is_accelerated_mode = bios_parser_is_accelerated_mode, + + .set_scratch_critical_state = bios_parser_set_scratch_critical_state, + + +/* COMMANDS */ + .encoder_control = bios_parser_encoder_control, + + .transmitter_control = bios_parser_transmitter_control, + + .enable_crtc = bios_parser_enable_crtc, + + .set_pixel_clock = bios_parser_set_pixel_clock, + + .set_dce_clock = bios_parser_set_dce_clock, + + .program_crtc_timing = bios_parser_program_crtc_timing, + + /* .blank_crtc = bios_parser_blank_crtc, */ + + .crtc_source_select = bios_parser_crtc_source_select, + + /* .external_encoder_control = bios_parser_external_encoder_control, */ + + .enable_disp_power_gating = bios_parser_enable_disp_power_gating, + + .post_init = bios_parser_post_init, + + .bios_parser_destroy = firmware_parser_destroy, + + .get_smu_clock_info = bios_parser_get_smu_clock_info, +}; + +static bool bios_parser_construct( + struct bios_parser *bp, + struct bp_init_data *init, + enum dce_version dce_version) +{ + uint16_t *rom_header_offset = NULL; + struct atom_rom_header_v2_2 *rom_header = NULL; + struct display_object_info_table_v1_4 *object_info_tbl; + struct atom_data_revision tbl_rev = {0}; + + if (!init) + return false; + + if (!init->bios) + return false; + + bp->base.funcs = &vbios_funcs; + bp->base.bios = init->bios; + bp->base.bios_size = bp->base.bios[OFFSET_TO_ATOM_ROM_IMAGE_SIZE] * BIOS_IMAGE_SIZE_UNIT; + + bp->base.ctx = init->ctx; + + bp->base.bios_local_image = NULL; + + rom_header_offset = + GET_IMAGE(uint16_t, OFFSET_TO_ATOM_ROM_HEADER_POINTER); + + if (!rom_header_offset) + return false; + + rom_header = GET_IMAGE(struct atom_rom_header_v2_2, *rom_header_offset); + + if (!rom_header) + return false; + + get_atom_data_table_revision(&rom_header->table_header, &tbl_rev); + if (!(tbl_rev.major >= 2 && tbl_rev.minor >= 2)) + return false; + + bp->master_data_tbl = + GET_IMAGE(struct atom_master_data_table_v2_1, + rom_header->masterdatatable_offset); + + if (!bp->master_data_tbl) + return false; + + bp->object_info_tbl_offset = DATA_TABLES(displayobjectinfo); + + if (!bp->object_info_tbl_offset) + return false; + + object_info_tbl = + GET_IMAGE(struct display_object_info_table_v1_4, + bp->object_info_tbl_offset); + + if (!object_info_tbl) + return false; + + get_atom_data_table_revision(&object_info_tbl->table_header, + &bp->object_info_tbl.revision); + + if (bp->object_info_tbl.revision.major == 1 + && bp->object_info_tbl.revision.minor >= 4) { + struct display_object_info_table_v1_4 *tbl_v1_4; + + tbl_v1_4 = GET_IMAGE(struct display_object_info_table_v1_4, + bp->object_info_tbl_offset); + if (!tbl_v1_4) + return false; + + bp->object_info_tbl.v1_4 = tbl_v1_4; + } else + return false; + + dal_firmware_parser_init_cmd_tbl(bp); + dal_bios_parser_init_cmd_tbl_helper2(&bp->cmd_helper, dce_version); + + bp->base.integrated_info = bios_parser_create_integrated_info(&bp->base); + + return true; +} + +struct dc_bios *firmware_parser_create( + struct bp_init_data *init, + enum dce_version dce_version) +{ + struct bios_parser *bp = NULL; + + bp = kzalloc(sizeof(struct bios_parser), GFP_KERNEL); + if (!bp) + return NULL; + + if (bios_parser_construct(bp, init, dce_version)) + return &bp->base; + + kfree(bp); + return NULL; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.h new file mode 100644 index 000000000000..cb40546cdafe --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BIOS_PARSER2_H__ +#define __DAL_BIOS_PARSER2_H__ + +struct dc_bios *firmware_parser_create( + struct bp_init_data *init, + enum dce_version dce_version); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.c new file mode 100644 index 000000000000..a8cb039d2572 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.c @@ -0,0 +1,288 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "bios_parser_common.h" +#include "include/grph_object_ctrl_defs.h" + +static enum object_type object_type_from_bios_object_id(uint32_t bios_object_id) +{ + uint32_t bios_object_type = (bios_object_id & OBJECT_TYPE_MASK) + >> OBJECT_TYPE_SHIFT; + enum object_type object_type; + + switch (bios_object_type) { + case GRAPH_OBJECT_TYPE_GPU: + object_type = OBJECT_TYPE_GPU; + break; + case GRAPH_OBJECT_TYPE_ENCODER: + object_type = OBJECT_TYPE_ENCODER; + break; + case GRAPH_OBJECT_TYPE_CONNECTOR: + object_type = OBJECT_TYPE_CONNECTOR; + break; + case GRAPH_OBJECT_TYPE_ROUTER: + object_type = OBJECT_TYPE_ROUTER; + break; + case GRAPH_OBJECT_TYPE_GENERIC: + object_type = OBJECT_TYPE_GENERIC; + break; + default: + object_type = OBJECT_TYPE_UNKNOWN; + break; + } + + return object_type; +} + +static enum object_enum_id enum_id_from_bios_object_id(uint32_t bios_object_id) +{ + uint32_t bios_enum_id = + (bios_object_id & ENUM_ID_MASK) >> ENUM_ID_SHIFT; + enum object_enum_id id; + + switch (bios_enum_id) { + case GRAPH_OBJECT_ENUM_ID1: + id = ENUM_ID_1; + break; + case GRAPH_OBJECT_ENUM_ID2: + id = ENUM_ID_2; + break; + case GRAPH_OBJECT_ENUM_ID3: + id = ENUM_ID_3; + break; + case GRAPH_OBJECT_ENUM_ID4: + id = ENUM_ID_4; + break; + case GRAPH_OBJECT_ENUM_ID5: + id = ENUM_ID_5; + break; + case GRAPH_OBJECT_ENUM_ID6: + id = ENUM_ID_6; + break; + case GRAPH_OBJECT_ENUM_ID7: + id = ENUM_ID_7; + break; + default: + id = ENUM_ID_UNKNOWN; + break; + } + + return id; +} + +static uint32_t gpu_id_from_bios_object_id(uint32_t bios_object_id) +{ + return (bios_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; +} + +static enum encoder_id encoder_id_from_bios_object_id(uint32_t bios_object_id) +{ + uint32_t bios_encoder_id = gpu_id_from_bios_object_id(bios_object_id); + enum encoder_id id; + + switch (bios_encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + id = ENCODER_ID_INTERNAL_LVDS; + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + id = ENCODER_ID_INTERNAL_TMDS1; + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS2: + id = ENCODER_ID_INTERNAL_TMDS2; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + id = ENCODER_ID_INTERNAL_DAC1; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + id = ENCODER_ID_INTERNAL_DAC2; + break; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + id = ENCODER_ID_INTERNAL_LVTM1; + break; + case ENCODER_OBJECT_ID_HDMI_INTERNAL: + id = ENCODER_ID_INTERNAL_HDMI; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + id = ENCODER_ID_INTERNAL_KLDSCP_TMDS1; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + id = ENCODER_ID_INTERNAL_KLDSCP_DAC1; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + id = ENCODER_ID_INTERNAL_KLDSCP_DAC2; + break; + case ENCODER_OBJECT_ID_MVPU_FPGA: + id = ENCODER_ID_EXTERNAL_MVPU_FPGA; + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + id = ENCODER_ID_INTERNAL_DDI; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + id = ENCODER_ID_INTERNAL_UNIPHY; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + id = ENCODER_ID_INTERNAL_KLDSCP_LVTMA; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + id = ENCODER_ID_INTERNAL_UNIPHY1; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + id = ENCODER_ID_INTERNAL_UNIPHY2; + break; + case ENCODER_OBJECT_ID_ALMOND: /* ENCODER_OBJECT_ID_NUTMEG */ + id = ENCODER_ID_EXTERNAL_NUTMEG; + break; + case ENCODER_OBJECT_ID_TRAVIS: + id = ENCODER_ID_EXTERNAL_TRAVIS; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: + id = ENCODER_ID_INTERNAL_UNIPHY3; + break; + default: + id = ENCODER_ID_UNKNOWN; + ASSERT(0); + break; + } + + return id; +} + +static enum connector_id connector_id_from_bios_object_id( + uint32_t bios_object_id) +{ + uint32_t bios_connector_id = gpu_id_from_bios_object_id(bios_object_id); + + enum connector_id id; + + switch (bios_connector_id) { + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I: + id = CONNECTOR_ID_SINGLE_LINK_DVII; + break; + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I: + id = CONNECTOR_ID_DUAL_LINK_DVII; + break; + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D: + id = CONNECTOR_ID_SINGLE_LINK_DVID; + break; + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D: + id = CONNECTOR_ID_DUAL_LINK_DVID; + break; + case CONNECTOR_OBJECT_ID_VGA: + id = CONNECTOR_ID_VGA; + break; + case CONNECTOR_OBJECT_ID_HDMI_TYPE_A: + id = CONNECTOR_ID_HDMI_TYPE_A; + break; + case CONNECTOR_OBJECT_ID_LVDS: + id = CONNECTOR_ID_LVDS; + break; + case CONNECTOR_OBJECT_ID_PCIE_CONNECTOR: + id = CONNECTOR_ID_PCIE; + break; + case CONNECTOR_OBJECT_ID_HARDCODE_DVI: + id = CONNECTOR_ID_HARDCODE_DVI; + break; + case CONNECTOR_OBJECT_ID_DISPLAYPORT: + id = CONNECTOR_ID_DISPLAY_PORT; + break; + case CONNECTOR_OBJECT_ID_eDP: + id = CONNECTOR_ID_EDP; + break; + case CONNECTOR_OBJECT_ID_MXM: + id = CONNECTOR_ID_MXM; + break; + default: + id = CONNECTOR_ID_UNKNOWN; + break; + } + + return id; +} + +static enum generic_id generic_id_from_bios_object_id(uint32_t bios_object_id) +{ + uint32_t bios_generic_id = gpu_id_from_bios_object_id(bios_object_id); + + enum generic_id id; + + switch (bios_generic_id) { + case GENERIC_OBJECT_ID_MXM_OPM: + id = GENERIC_ID_MXM_OPM; + break; + case GENERIC_OBJECT_ID_GLSYNC: + id = GENERIC_ID_GLSYNC; + break; + case GENERIC_OBJECT_ID_STEREO_PIN: + id = GENERIC_ID_STEREO; + break; + default: + id = GENERIC_ID_UNKNOWN; + break; + } + + return id; +} + +static uint32_t id_from_bios_object_id(enum object_type type, + uint32_t bios_object_id) +{ + switch (type) { + case OBJECT_TYPE_GPU: + return gpu_id_from_bios_object_id(bios_object_id); + case OBJECT_TYPE_ENCODER: + return (uint32_t)encoder_id_from_bios_object_id(bios_object_id); + case OBJECT_TYPE_CONNECTOR: + return (uint32_t)connector_id_from_bios_object_id( + bios_object_id); + case OBJECT_TYPE_GENERIC: + return generic_id_from_bios_object_id(bios_object_id); + default: + return 0; + } +} + +struct graphics_object_id object_id_from_bios_object_id(uint32_t bios_object_id) +{ + enum object_type type; + enum object_enum_id enum_id; + struct graphics_object_id go_id = { 0 }; + + type = object_type_from_bios_object_id(bios_object_id); + + if (OBJECT_TYPE_UNKNOWN == type) + return go_id; + + enum_id = enum_id_from_bios_object_id(bios_object_id); + + if (ENUM_ID_UNKNOWN == enum_id) + return go_id; + + go_id = dal_graphics_object_id_init( + id_from_bios_object_id(type, bios_object_id), enum_id, type); + + return go_id; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.h new file mode 100644 index 000000000000..a076c61dfae4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __BIOS_PARSER_COMMON_H__ +#define __BIOS_PARSER_COMMON_H__ + +#include "dm_services.h" +#include "ObjectID.h" + +struct graphics_object_id object_id_from_bios_object_id(uint32_t bios_object_id); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c new file mode 100644 index 000000000000..5c9e5108c32c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c @@ -0,0 +1,82 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" +#include "bios_parser_helper.h" +#include "command_table_helper.h" +#include "command_table.h" +#include "bios_parser_types_internal.h" + +uint8_t *bios_get_image(struct dc_bios *bp, + uint32_t offset, + uint32_t size) +{ + if (bp->bios && offset + size < bp->bios_size) + return bp->bios + offset; + else + return NULL; +} + +#include "reg_helper.h" + +#define CTX \ + bios->ctx +#define REG(reg)\ + (bios->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + ATOM_ ## field_name ## _SHIFT, ATOM_ ## field_name + +bool bios_is_accelerated_mode( + struct dc_bios *bios) +{ + uint32_t acc_mode; + REG_GET(BIOS_SCRATCH_6, S6_ACC_MODE, &acc_mode); + return (acc_mode == 1); +} + + +void bios_set_scratch_acc_mode_change( + struct dc_bios *bios) +{ + REG_UPDATE(BIOS_SCRATCH_6, S6_ACC_MODE, 1); +} + + +void bios_set_scratch_critical_state( + struct dc_bios *bios, + bool state) +{ + uint32_t critial_state = state ? 1 : 0; + REG_UPDATE(BIOS_SCRATCH_6, S6_CRITICAL_STATE, critial_state); +} + + + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h new file mode 100644 index 000000000000..c0047efeb006 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h @@ -0,0 +1,40 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BIOS_PARSER_HELPER_H__ +#define __DAL_BIOS_PARSER_HELPER_H__ + +struct bios_parser; + +uint8_t *bios_get_image(struct dc_bios *bp, uint32_t offset, + uint32_t size); + +bool bios_is_accelerated_mode(struct dc_bios *bios); +void bios_set_scratch_acc_mode_change(struct dc_bios *bios); +void bios_set_scratch_critical_state(struct dc_bios *bios, bool state); + +#define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_interface.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_interface.c new file mode 100644 index 000000000000..0079a1e26efd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_interface.c @@ -0,0 +1,56 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/logger_interface.h" + +#include "bios_parser_interface.h" +#include "bios_parser.h" + +#include "bios_parser2.h" + + +struct dc_bios *dal_bios_parser_create( + struct bp_init_data *init, + enum dce_version dce_version) +{ + struct dc_bios *bios = NULL; + + bios = firmware_parser_create(init, dce_version); + + /* Fall back to old bios parser for older asics */ + if (bios == NULL) + bios = bios_parser_create(init, dce_version); + + return bios; +} + +void dal_bios_parser_destroy(struct dc_bios **dcb) +{ + struct dc_bios *bios = *dcb; + + bios->funcs->bios_parser_destroy(dcb); +} + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal.h new file mode 100644 index 000000000000..5918923bfb93 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal.h @@ -0,0 +1,72 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BIOS_PARSER_TYPES_BIOS_H__ +#define __DAL_BIOS_PARSER_TYPES_BIOS_H__ + +#include "dc_bios_types.h" +#include "bios_parser_helper.h" + +struct atom_data_revision { + uint32_t major; + uint32_t minor; +}; + +struct object_info_table { + struct atom_data_revision revision; + union { + ATOM_OBJECT_HEADER *v1_1; + ATOM_OBJECT_HEADER_V3 *v1_3; + }; +}; + +enum spread_spectrum_id { + SS_ID_UNKNOWN = 0, + SS_ID_DP1 = 0xf1, + SS_ID_DP2 = 0xf2, + SS_ID_LVLINK_2700MHZ = 0xf3, + SS_ID_LVLINK_1620MHZ = 0xf4 +}; + +struct bios_parser { + struct dc_bios base; + + struct object_info_table object_info_tbl; + uint32_t object_info_tbl_offset; + ATOM_MASTER_DATA_TABLE *master_data_tbl; + + const struct bios_parser_helper *bios_helper; + + const struct command_table_helper *cmd_helper; + struct cmd_tbl cmd_tbl; + + bool remap_device_tags; +}; + +/* Bios Parser from DC Bios */ +#define BP_FROM_DCB(dc_bios) \ + container_of(dc_bios, struct bios_parser, base) + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal2.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal2.h new file mode 100644 index 000000000000..bf1f5c86e65c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal2.h @@ -0,0 +1,74 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BIOS_PARSER_TYPES_BIOS2_H__ +#define __DAL_BIOS_PARSER_TYPES_BIOS2_H__ + +#include "dc_bios_types.h" +#include "bios_parser_helper.h" + +/* use atomfirmware_bringup.h only. Not atombios.h anymore */ + +struct atom_data_revision { + uint32_t major; + uint32_t minor; +}; + +struct object_info_table { + struct atom_data_revision revision; + union { + struct display_object_info_table_v1_4 *v1_4; + }; +}; + +enum spread_spectrum_id { + SS_ID_UNKNOWN = 0, + SS_ID_DP1 = 0xf1, + SS_ID_DP2 = 0xf2, + SS_ID_LVLINK_2700MHZ = 0xf3, + SS_ID_LVLINK_1620MHZ = 0xf4 +}; + +struct bios_parser { + struct dc_bios base; + + struct object_info_table object_info_tbl; + uint32_t object_info_tbl_offset; + struct atom_master_data_table_v2_1 *master_data_tbl; + + + const struct bios_parser_helper *bios_helper; + + const struct command_table_helper *cmd_helper; + struct cmd_tbl cmd_tbl; + + bool remap_device_tags; +}; + +/* Bios Parser from DC Bios */ +#define BP_FROM_DCB(dc_bios) \ + container_of(dc_bios, struct bios_parser, base) + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.c b/drivers/gpu/drm/amd/display/dc/bios/command_table.c new file mode 100644 index 000000000000..3f7b2dabc2b0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.c @@ -0,0 +1,2424 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_interface.h" + +#include "command_table.h" +#include "command_table_helper.h" +#include "bios_parser_helper.h" +#include "bios_parser_types_internal.h" + +#define EXEC_BIOS_CMD_TABLE(command, params)\ + (cgs_atom_exec_cmd_table(bp->base.ctx->cgs_device, \ + GetIndexIntoMasterTable(COMMAND, command), \ + ¶ms) == 0) + +#define BIOS_CMD_TABLE_REVISION(command, frev, crev)\ + cgs_atom_get_cmd_table_revs(bp->base.ctx->cgs_device, \ + GetIndexIntoMasterTable(COMMAND, command), &frev, &crev) + +#define BIOS_CMD_TABLE_PARA_REVISION(command)\ + bios_cmd_table_para_revision(bp->base.ctx->cgs_device, \ + GetIndexIntoMasterTable(COMMAND, command)) + +static void init_dig_encoder_control(struct bios_parser *bp); +static void init_transmitter_control(struct bios_parser *bp); +static void init_set_pixel_clock(struct bios_parser *bp); +static void init_enable_spread_spectrum_on_ppll(struct bios_parser *bp); +static void init_adjust_display_pll(struct bios_parser *bp); +static void init_dac_encoder_control(struct bios_parser *bp); +static void init_dac_output_control(struct bios_parser *bp); +static void init_set_crtc_timing(struct bios_parser *bp); +static void init_select_crtc_source(struct bios_parser *bp); +static void init_enable_crtc(struct bios_parser *bp); +static void init_enable_crtc_mem_req(struct bios_parser *bp); +static void init_external_encoder_control(struct bios_parser *bp); +static void init_enable_disp_power_gating(struct bios_parser *bp); +static void init_program_clock(struct bios_parser *bp); +static void init_set_dce_clock(struct bios_parser *bp); + +void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp) +{ + init_dig_encoder_control(bp); + init_transmitter_control(bp); + init_set_pixel_clock(bp); + init_enable_spread_spectrum_on_ppll(bp); + init_adjust_display_pll(bp); + init_dac_encoder_control(bp); + init_dac_output_control(bp); + init_set_crtc_timing(bp); + init_select_crtc_source(bp); + init_enable_crtc(bp); + init_enable_crtc_mem_req(bp); + init_program_clock(bp); + init_external_encoder_control(bp); + init_enable_disp_power_gating(bp); + init_set_dce_clock(bp); +} + +static uint32_t bios_cmd_table_para_revision(void *cgs_device, + uint32_t index) +{ + uint8_t frev, crev; + + if (cgs_atom_get_cmd_table_revs(cgs_device, + index, + &frev, &crev) != 0) + return 0; + return crev; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** D I G E N C O D E R C O N T R O L + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result encoder_control_digx_v3( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static enum bp_result encoder_control_digx_v4( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static enum bp_result encoder_control_digx_v5( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static void init_encoder_control_dig_v1(struct bios_parser *bp); + +static void init_dig_encoder_control(struct bios_parser *bp) +{ + uint32_t version = + BIOS_CMD_TABLE_PARA_REVISION(DIGxEncoderControl); + + switch (version) { + case 2: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v3; + break; + case 4: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v4; + break; + + case 5: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v5; + break; + + default: + init_encoder_control_dig_v1(bp); + break; + } +} + +static enum bp_result encoder_control_dig_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl); +static enum bp_result encoder_control_dig1_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl); +static enum bp_result encoder_control_dig2_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static void init_encoder_control_dig_v1(struct bios_parser *bp) +{ + struct cmd_tbl *cmd_tbl = &bp->cmd_tbl; + + if (1 == BIOS_CMD_TABLE_PARA_REVISION(DIG1EncoderControl)) + cmd_tbl->encoder_control_dig1 = encoder_control_dig1_v1; + else + cmd_tbl->encoder_control_dig1 = NULL; + + if (1 == BIOS_CMD_TABLE_PARA_REVISION(DIG2EncoderControl)) + cmd_tbl->encoder_control_dig2 = encoder_control_dig2_v1; + else + cmd_tbl->encoder_control_dig2 = NULL; + + cmd_tbl->dig_encoder_control = encoder_control_dig_v1; +} + +static enum bp_result encoder_control_dig_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct cmd_tbl *cmd_tbl = &bp->cmd_tbl; + + if (cntl != NULL) + switch (cntl->engine_id) { + case ENGINE_ID_DIGA: + if (cmd_tbl->encoder_control_dig1 != NULL) + result = + cmd_tbl->encoder_control_dig1(bp, cntl); + break; + case ENGINE_ID_DIGB: + if (cmd_tbl->encoder_control_dig2 != NULL) + result = + cmd_tbl->encoder_control_dig2(bp, cntl); + break; + + default: + break; + } + + return result; +} + +static enum bp_result encoder_control_dig1_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V2 params = {0}; + + bp->cmd_helper->assign_control_parameter(bp->cmd_helper, cntl, ¶ms); + + if (EXEC_BIOS_CMD_TABLE(DIG1EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_dig2_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V2 params = {0}; + + bp->cmd_helper->assign_control_parameter(bp->cmd_helper, cntl, ¶ms); + + if (EXEC_BIOS_CMD_TABLE(DIG2EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_digx_v3( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V3 params = {0}; + + if (LANE_COUNT_FOUR < cntl->lanes_number) + params.acConfig.ucDPLinkRate = 1; /* dual link 2.7GHz */ + else + params.acConfig.ucDPLinkRate = 0; /* single link 1.62GHz */ + + params.acConfig.ucDigSel = (uint8_t)(cntl->engine_id); + + /* We need to convert from KHz units into 10KHz units */ + params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); + params.usPixelClock = cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + params.ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + + if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_digx_v4( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V4 params = {0}; + + if (LANE_COUNT_FOUR < cntl->lanes_number) + params.acConfig.ucDPLinkRate = 1; /* dual link 2.7GHz */ + else + params.acConfig.ucDPLinkRate = 0; /* single link 1.62GHz */ + + params.acConfig.ucDigSel = (uint8_t)(cntl->engine_id); + + /* We need to convert from KHz units into 10KHz units */ + params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); + params.usPixelClock = cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + params.ucEncoderMode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio)); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + + if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_digx_v5( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENCODER_STREAM_SETUP_PARAMETERS_V5 params = {0}; + + params.ucDigId = (uint8_t)(cntl->engine_id); + params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); + + params.ulPixelClock = cntl->pixel_clock / 10; + params.ucDigMode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio)); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + + switch (cntl->color_depth) { + case COLOR_DEPTH_888: + params.ucBitPerColor = PANEL_8BIT_PER_COLOR; + break; + case COLOR_DEPTH_101010: + params.ucBitPerColor = PANEL_10BIT_PER_COLOR; + break; + case COLOR_DEPTH_121212: + params.ucBitPerColor = PANEL_12BIT_PER_COLOR; + break; + case COLOR_DEPTH_161616: + params.ucBitPerColor = PANEL_16BIT_PER_COLOR; + break; + default: + break; + } + + if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) + switch (cntl->color_depth) { + case COLOR_DEPTH_101010: + params.ulPixelClock = + (params.ulPixelClock * 30) / 24; + break; + case COLOR_DEPTH_121212: + params.ulPixelClock = + (params.ulPixelClock * 36) / 24; + break; + case COLOR_DEPTH_161616: + params.ulPixelClock = + (params.ulPixelClock * 48) / 24; + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** TRANSMITTER CONTROL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result transmitter_control_v2( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v3( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v4( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v1_5( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); + +static void init_transmitter_control(struct bios_parser *bp) +{ + uint8_t frev; + uint8_t crev; + + if (BIOS_CMD_TABLE_REVISION(UNIPHYTransmitterControl, + frev, crev) != 0) + BREAK_TO_DEBUGGER(); + switch (crev) { + case 2: + bp->cmd_tbl.transmitter_control = transmitter_control_v2; + break; + case 3: + bp->cmd_tbl.transmitter_control = transmitter_control_v3; + break; + case 4: + bp->cmd_tbl.transmitter_control = transmitter_control_v4; + break; + case 5: + bp->cmd_tbl.transmitter_control = transmitter_control_v1_5; + break; + case 6: + bp->cmd_tbl.transmitter_control = transmitter_control_v1_6; + break; + default: + bp->cmd_tbl.transmitter_control = NULL; + break; + } +} + +static enum bp_result transmitter_control_v2( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 params; + enum connector_id connector_id = + dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); + + memset(¶ms, 0, sizeof(params)); + + switch (cntl->transmitter) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + case TRANSMITTER_TRAVIS_LCD: + break; + default: + return BP_RESULT_BADINPUT; + } + + switch (cntl->action) { + case TRANSMITTER_CONTROL_INIT: + if ((CONNECTOR_ID_DUAL_LINK_DVII == connector_id) || + (CONNECTOR_ID_DUAL_LINK_DVID == connector_id)) + /* on INIT this bit should be set according to the + * phisycal connector + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* connector object id */ + params.usInitInfo = + cpu_to_le16((uint8_t)cntl->connector_obj_id.id); + break; + case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: + /* votage swing and pre-emphsis */ + params.asMode.ucLaneSel = (uint8_t)cntl->lane_select; + params.asMode.ucLaneSet = (uint8_t)cntl->lane_settings; + break; + default: + /* if dual-link */ + if (LANE_COUNT_FOUR < cntl->lanes_number) { + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* link rate, half for dual link + * We need to convert from KHz units into 20KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); + } else + /* link rate, half for dual link + * We need to convert from KHz units into 10KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + break; + } + + /* 00 - coherent mode + * 01 - incoherent mode + */ + + params.acConfig.fCoherentMode = cntl->coherent; + + if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) + || (TRANSMITTER_UNIPHY_D == cntl->transmitter) + || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) + /* Bit2: Transmitter Link selection + * =0 when bit0=0, single link A/C/E, when bit0=1, + * master link A/C/E + * =1 when bit0=0, single link B/D/F, when bit0=1, + * master link B/D/F + */ + params.acConfig.ucLinkSel = 1; + + if (ENGINE_ID_DIGB == cntl->engine_id) + /* Bit3: Transmitter data source selection + * =0 DIGA is data source. + * =1 DIGB is data source. + * This bit is only useful when ucAction= ATOM_ENABLE + */ + params.acConfig.ucEncoderSel = 1; + + if (CONNECTOR_ID_DISPLAY_PORT == connector_id) + /* Bit4: DP connector flag + * =0 connector is none-DP connector + * =1 connector is DP connector + */ + params.acConfig.fDPConnector = 1; + + /* Bit[7:6]: Transmitter selection + * =0 UNIPHY_ENCODER: UNIPHYA/B + * =1 UNIPHY1_ENCODER: UNIPHYC/D + * =2 UNIPHY2_ENCODER: UNIPHYE/F + * =3 reserved + */ + params.acConfig.ucTransmitterSel = + (uint8_t)bp->cmd_helper->transmitter_bp_to_atom( + cntl->transmitter); + + params.ucAction = (uint8_t)cntl->action; + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v3( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 params; + uint32_t pll_id; + enum connector_id conn_id = + dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); + const struct command_table_helper *cmd = bp->cmd_helper; + bool dual_link_conn = (CONNECTOR_ID_DUAL_LINK_DVII == conn_id) + || (CONNECTOR_ID_DUAL_LINK_DVID == conn_id); + + memset(¶ms, 0, sizeof(params)); + + switch (cntl->transmitter) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + case TRANSMITTER_TRAVIS_LCD: + break; + default: + return BP_RESULT_BADINPUT; + } + + if (!cmd->clock_source_id_to_atom(cntl->pll_id, &pll_id)) + return BP_RESULT_BADINPUT; + + /* fill information based on the action */ + switch (cntl->action) { + case TRANSMITTER_CONTROL_INIT: + if (dual_link_conn) { + /* on INIT this bit should be set according to the + * phisycal connector + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + } + + /* connector object id */ + params.usInitInfo = + cpu_to_le16((uint8_t)(cntl->connector_obj_id.id)); + break; + case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: + /* votage swing and pre-emphsis */ + params.asMode.ucLaneSel = (uint8_t)cntl->lane_select; + params.asMode.ucLaneSet = (uint8_t)cntl->lane_settings; + break; + default: + if (dual_link_conn && cntl->multi_path) + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* if dual-link */ + if (LANE_COUNT_FOUR < cntl->lanes_number) { + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* link rate, half for dual link + * We need to convert from KHz units into 20KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); + } else { + /* link rate, half for dual link + * We need to convert from KHz units into 10KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + } + break; + } + + /* 00 - coherent mode + * 01 - incoherent mode + */ + + params.acConfig.fCoherentMode = cntl->coherent; + + if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) + || (TRANSMITTER_UNIPHY_D == cntl->transmitter) + || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) + /* Bit2: Transmitter Link selection + * =0 when bit0=0, single link A/C/E, when bit0=1, + * master link A/C/E + * =1 when bit0=0, single link B/D/F, when bit0=1, + * master link B/D/F + */ + params.acConfig.ucLinkSel = 1; + + if (ENGINE_ID_DIGB == cntl->engine_id) + /* Bit3: Transmitter data source selection + * =0 DIGA is data source. + * =1 DIGB is data source. + * This bit is only useful when ucAction= ATOM_ENABLE + */ + params.acConfig.ucEncoderSel = 1; + + /* Bit[7:6]: Transmitter selection + * =0 UNIPHY_ENCODER: UNIPHYA/B + * =1 UNIPHY1_ENCODER: UNIPHYC/D + * =2 UNIPHY2_ENCODER: UNIPHYE/F + * =3 reserved + */ + params.acConfig.ucTransmitterSel = + (uint8_t)cmd->transmitter_bp_to_atom(cntl->transmitter); + + params.ucLaneNum = (uint8_t)cntl->lanes_number; + + params.acConfig.ucRefClkSource = (uint8_t)pll_id; + + params.ucAction = (uint8_t)cntl->action; + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v4( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 params; + uint32_t ref_clk_src_id; + enum connector_id conn_id = + dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); + const struct command_table_helper *cmd = bp->cmd_helper; + + memset(¶ms, 0, sizeof(params)); + + switch (cntl->transmitter) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + case TRANSMITTER_TRAVIS_LCD: + break; + default: + return BP_RESULT_BADINPUT; + } + + if (!cmd->clock_source_id_to_ref_clk_src(cntl->pll_id, &ref_clk_src_id)) + return BP_RESULT_BADINPUT; + + switch (cntl->action) { + case TRANSMITTER_CONTROL_INIT: + { + if ((CONNECTOR_ID_DUAL_LINK_DVII == conn_id) || + (CONNECTOR_ID_DUAL_LINK_DVID == conn_id)) + /* on INIT this bit should be set according to the + * phisycal connector + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* connector object id */ + params.usInitInfo = + cpu_to_le16((uint8_t)(cntl->connector_obj_id.id)); + } + break; + case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: + /* votage swing and pre-emphsis */ + params.asMode.ucLaneSel = (uint8_t)(cntl->lane_select); + params.asMode.ucLaneSet = (uint8_t)(cntl->lane_settings); + break; + default: + if ((CONNECTOR_ID_DUAL_LINK_DVII == conn_id) || + (CONNECTOR_ID_DUAL_LINK_DVID == conn_id)) + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* if dual-link */ + if (LANE_COUNT_FOUR < cntl->lanes_number) + /* link rate, half for dual link + * We need to convert from KHz units into 20KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); + else { + /* link rate, half for dual link + * We need to convert from KHz units into 10KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + } + break; + } + + /* 00 - coherent mode + * 01 - incoherent mode + */ + + params.acConfig.fCoherentMode = cntl->coherent; + + if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) + || (TRANSMITTER_UNIPHY_D == cntl->transmitter) + || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) + /* Bit2: Transmitter Link selection + * =0 when bit0=0, single link A/C/E, when bit0=1, + * master link A/C/E + * =1 when bit0=0, single link B/D/F, when bit0=1, + * master link B/D/F + */ + params.acConfig.ucLinkSel = 1; + + if (ENGINE_ID_DIGB == cntl->engine_id) + /* Bit3: Transmitter data source selection + * =0 DIGA is data source. + * =1 DIGB is data source. + * This bit is only useful when ucAction= ATOM_ENABLE + */ + params.acConfig.ucEncoderSel = 1; + + /* Bit[7:6]: Transmitter selection + * =0 UNIPHY_ENCODER: UNIPHYA/B + * =1 UNIPHY1_ENCODER: UNIPHYC/D + * =2 UNIPHY2_ENCODER: UNIPHYE/F + * =3 reserved + */ + params.acConfig.ucTransmitterSel = + (uint8_t)(cmd->transmitter_bp_to_atom(cntl->transmitter)); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + params.acConfig.ucRefClkSource = (uint8_t)(ref_clk_src_id); + params.ucAction = (uint8_t)(cntl->action); + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v1_5( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + const struct command_table_helper *cmd = bp->cmd_helper; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 params; + + memset(¶ms, 0, sizeof(params)); + params.ucPhyId = cmd->phy_id_to_atom(cntl->transmitter); + params.ucAction = (uint8_t)cntl->action; + params.ucLaneNum = (uint8_t)cntl->lanes_number; + params.ucConnObjId = (uint8_t)cntl->connector_obj_id.id; + + params.ucDigMode = + cmd->signal_type_to_atom_dig_mode(cntl->signal); + params.asConfig.ucPhyClkSrcId = + cmd->clock_source_id_to_atom_phy_clk_src_id(cntl->pll_id); + /* 00 - coherent mode */ + params.asConfig.ucCoherentMode = cntl->coherent; + params.asConfig.ucHPDSel = + cmd->hpd_sel_to_atom(cntl->hpd_sel); + params.ucDigEncoderSel = + cmd->dig_encoder_sel_to_atom(cntl->engine_id); + params.ucDPLaneSet = (uint8_t) cntl->lane_settings; + params.usSymClock = cpu_to_le16((uint16_t) (cntl->pixel_clock / 10)); + /* + * In SI/TN case, caller have to set usPixelClock as following: + * DP mode: usPixelClock = DP_LINK_CLOCK/10 + * (DP_LINK_CLOCK = 1.62GHz, 2.7GHz, 5.4GHz) + * DVI single link mode: usPixelClock = pixel clock + * DVI dual link mode: usPixelClock = pixel clock + * HDMI mode: usPixelClock = pixel clock * deep_color_ratio + * (=1: 8bpp, =1.25: 10bpp, =1.5:12bpp, =2: 16bpp) + * LVDS mode: usPixelClock = pixel clock + */ + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + const struct command_table_helper *cmd = bp->cmd_helper; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_6 params; + + memset(¶ms, 0, sizeof(params)); + params.ucPhyId = cmd->phy_id_to_atom(cntl->transmitter); + params.ucAction = (uint8_t)cntl->action; + + if (cntl->action == TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS) + params.ucDPLaneSet = (uint8_t)cntl->lane_settings; + else + params.ucDigMode = cmd->signal_type_to_atom_dig_mode(cntl->signal); + + params.ucLaneNum = (uint8_t)cntl->lanes_number; + params.ucHPDSel = cmd->hpd_sel_to_atom(cntl->hpd_sel); + params.ucDigEncoderSel = cmd->dig_encoder_sel_to_atom(cntl->engine_id); + params.ucConnObjId = (uint8_t)cntl->connector_obj_id.id; + params.ulSymClock = cntl->pixel_clock/10; + + /* + * In SI/TN case, caller have to set usPixelClock as following: + * DP mode: usPixelClock = DP_LINK_CLOCK/10 + * (DP_LINK_CLOCK = 1.62GHz, 2.7GHz, 5.4GHz) + * DVI single link mode: usPixelClock = pixel clock + * DVI dual link mode: usPixelClock = pixel clock + * HDMI mode: usPixelClock = pixel clock * deep_color_ratio + * (=1: 8bpp, =1.25: 10bpp, =1.5:12bpp, =2: 16bpp) + * LVDS mode: usPixelClock = pixel clock + */ + switch (cntl->signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + switch (cntl->color_depth) { + case COLOR_DEPTH_101010: + params.ulSymClock = + cpu_to_le16((le16_to_cpu(params.ulSymClock) * 30) / 24); + break; + case COLOR_DEPTH_121212: + params.ulSymClock = + cpu_to_le16((le16_to_cpu(params.ulSymClock) * 36) / 24); + break; + case COLOR_DEPTH_161616: + params.ulSymClock = + cpu_to_le16((le16_to_cpu(params.ulSymClock) * 48) / 24); + break; + default: + break; + } + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** SET PIXEL CLOCK + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result set_pixel_clock_v3( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result set_pixel_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result set_pixel_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + +static void init_set_pixel_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)) { + case 3: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v3; + break; + case 5: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v5; + break; + case 6: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v6; + break; + case 7: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v7; + break; + default: + bp->cmd_tbl.set_pixel_clock = NULL; + break; + } +} + +static enum bp_result set_pixel_clock_v3( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + PIXEL_CLOCK_PARAMETERS_V3 *params; + SET_PIXEL_CLOCK_PS_ALLOCATION allocation; + + memset(&allocation, 0, sizeof(allocation)); + + if (CLOCK_SOURCE_ID_PLL1 == bp_params->pll_id) + allocation.sPCLKInput.ucPpll = ATOM_PPLL1; + else if (CLOCK_SOURCE_ID_PLL2 == bp_params->pll_id) + allocation.sPCLKInput.ucPpll = ATOM_PPLL2; + else + return BP_RESULT_BADINPUT; + + allocation.sPCLKInput.usRefDiv = + cpu_to_le16((uint16_t)bp_params->reference_divider); + allocation.sPCLKInput.usFbDiv = + cpu_to_le16((uint16_t)bp_params->feedback_divider); + allocation.sPCLKInput.ucFracFbDiv = + (uint8_t)bp_params->fractional_feedback_divider; + allocation.sPCLKInput.ucPostDiv = + (uint8_t)bp_params->pixel_clock_post_divider; + + /* We need to convert from KHz units into 10KHz units */ + allocation.sPCLKInput.usPixelClock = + cpu_to_le16((uint16_t)(bp_params->target_pixel_clock / 10)); + + params = (PIXEL_CLOCK_PARAMETERS_V3 *)&allocation.sPCLKInput; + params->ucTransmitterId = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + params->ucEncoderMode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false)); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + params->ucMiscInfo |= PIXEL_CLOCK_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.USE_E_CLOCK_AS_SOURCE_FOR_D_CLOCK) + params->ucMiscInfo |= PIXEL_CLOCK_MISC_USE_ENGINE_FOR_DISPCLK; + + if (CONTROLLER_ID_D1 != bp_params->controller_id) + params->ucMiscInfo |= PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, allocation)) + result = BP_RESULT_OK; + + return result; +} + +#ifndef SET_PIXEL_CLOCK_PS_ALLOCATION_V5 +/* video bios did not define this: */ +typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION_V5 { + PIXEL_CLOCK_PARAMETERS_V5 sPCLKInput; + /* Caller doesn't need to init this portion */ + ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved; +} SET_PIXEL_CLOCK_PS_ALLOCATION_V5; +#endif + +#ifndef SET_PIXEL_CLOCK_PS_ALLOCATION_V6 +/* video bios did not define this: */ +typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION_V6 { + PIXEL_CLOCK_PARAMETERS_V6 sPCLKInput; + /* Caller doesn't need to init this portion */ + ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved; +} SET_PIXEL_CLOCK_PS_ALLOCATION_V6; +#endif + +static enum bp_result set_pixel_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_PIXEL_CLOCK_PS_ALLOCATION_V5 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &controller_id)) { + clk.sPCLKInput.ucCRTC = controller_id; + clk.sPCLKInput.ucPpll = (uint8_t)pll_id; + clk.sPCLKInput.ucRefDiv = + (uint8_t)(bp_params->reference_divider); + clk.sPCLKInput.usFbDiv = + cpu_to_le16((uint16_t)(bp_params->feedback_divider)); + clk.sPCLKInput.ulFbDivDecFrac = + cpu_to_le32(bp_params->fractional_feedback_divider); + clk.sPCLKInput.ucPostDiv = + (uint8_t)(bp_params->pixel_clock_post_divider); + clk.sPCLKInput.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + clk.sPCLKInput.ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + /* We need to convert from KHz units into 10KHz units */ + clk.sPCLKInput.usPixelClock = + cpu_to_le16((uint16_t)(bp_params->target_pixel_clock / 10)); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_MISC_REF_DIV_SRC; + + /* clkV5.ucMiscInfo bit[3:2]= HDMI panel bit depth: =0: 24bpp + * =1:30bpp, =2:32bpp + * driver choose program it itself, i.e. here we program it + * to 888 by default. + */ + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) + result = BP_RESULT_OK; + } + + return result; +} + +static enum bp_result set_pixel_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_PIXEL_CLOCK_PS_ALLOCATION_V6 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &controller_id)) { + /* Note: VBIOS still wants to use ucCRTC name which is now + * 1 byte in ULONG + *typedef struct _CRTC_PIXEL_CLOCK_FREQ + *{ + * target the pixel clock to drive the CRTC timing. + * ULONG ulPixelClock:24; + * 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to + * previous version. + * ATOM_CRTC1~6, indicate the CRTC controller to + * ULONG ucCRTC:8; + * drive the pixel clock. not used for DCPLL case. + *}CRTC_PIXEL_CLOCK_FREQ; + *union + *{ + * pixel clock and CRTC id frequency + * CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; + * ULONG ulDispEngClkFreq; dispclk frequency + *}; + */ + clk.sPCLKInput.ulCrtcPclkFreq.ucCRTC = controller_id; + clk.sPCLKInput.ucPpll = (uint8_t) pll_id; + clk.sPCLKInput.ucRefDiv = + (uint8_t) bp_params->reference_divider; + clk.sPCLKInput.usFbDiv = + cpu_to_le16((uint16_t) bp_params->feedback_divider); + clk.sPCLKInput.ulFbDivDecFrac = + cpu_to_le32(bp_params->fractional_feedback_divider); + clk.sPCLKInput.ucPostDiv = + (uint8_t) bp_params->pixel_clock_post_divider; + clk.sPCLKInput.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + clk.sPCLKInput.ucEncoderMode = + (uint8_t) bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + /* We need to convert from KHz units into 10KHz units */ + clk.sPCLKInput.ulCrtcPclkFreq.ulPixelClock = + cpu_to_le32(bp_params->target_pixel_clock / 10); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) { + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_V6_MISC_FORCE_PROG_PPLL; + } + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) { + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; + } + + /* clkV6.ucMiscInfo bit[3:2]= HDMI panel bit depth: =0: + * 24bpp =1:30bpp, =2:32bpp + * driver choose program it itself, i.e. here we pass required + * target rate that includes deep color. + */ + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) + result = BP_RESULT_OK; + } + + return result; +} + +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + PIXEL_CLOCK_PARAMETERS_V7 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, &controller_id)) { + /* Note: VBIOS still wants to use ucCRTC name which is now + * 1 byte in ULONG + *typedef struct _CRTC_PIXEL_CLOCK_FREQ + *{ + * target the pixel clock to drive the CRTC timing. + * ULONG ulPixelClock:24; + * 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to + * previous version. + * ATOM_CRTC1~6, indicate the CRTC controller to + * ULONG ucCRTC:8; + * drive the pixel clock. not used for DCPLL case. + *}CRTC_PIXEL_CLOCK_FREQ; + *union + *{ + * pixel clock and CRTC id frequency + * CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; + * ULONG ulDispEngClkFreq; dispclk frequency + *}; + */ + clk.ucCRTC = controller_id; + clk.ucPpll = (uint8_t) pll_id; + clk.ucTransmitterID = bp->cmd_helper->encoder_id_to_atom(dal_graphics_object_id_get_encoder_id(bp_params->encoder_object_id)); + clk.ucEncoderMode = (uint8_t) bp->cmd_helper->encoder_mode_bp_to_atom(bp_params->signal_type, false); + + /* We need to convert from KHz units into 10KHz units */ + clk.ulPixelClock = cpu_to_le32(bp_params->target_pixel_clock * 10); + + clk.ucDeepColorRatio = (uint8_t) bp->cmd_helper->transmitter_color_depth_to_atom(bp_params->color_depth); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC; + + if (bp_params->flags.PROGRAM_PHY_PLL_ONLY) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_PROG_PHYPLL; + + if (bp_params->flags.SUPPORT_YUV_420) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_YUV420_MODE; + + if (bp_params->flags.SET_XTALIN_REF_SRC) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_XTALIN; + + if (bp_params->flags.SET_GENLOCK_REF_DIV_SRC) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_GENLK; + + if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_DVI_DUALLINK_EN; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) + result = BP_RESULT_OK; + } + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE PIXEL CLOCK SS + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result enable_spread_spectrum_on_ppll_v1( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); +static enum bp_result enable_spread_spectrum_on_ppll_v2( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); +static enum bp_result enable_spread_spectrum_on_ppll_v3( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); + +static void init_enable_spread_spectrum_on_ppll(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableSpreadSpectrumOnPPLL)) { + case 1: + bp->cmd_tbl.enable_spread_spectrum_on_ppll = + enable_spread_spectrum_on_ppll_v1; + break; + case 2: + bp->cmd_tbl.enable_spread_spectrum_on_ppll = + enable_spread_spectrum_on_ppll_v2; + break; + case 3: + bp->cmd_tbl.enable_spread_spectrum_on_ppll = + enable_spread_spectrum_on_ppll_v3; + break; + default: + bp->cmd_tbl.enable_spread_spectrum_on_ppll = NULL; + break; + } +} + +static enum bp_result enable_spread_spectrum_on_ppll_v1( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENABLE_SPREAD_SPECTRUM_ON_PPLL params; + + memset(¶ms, 0, sizeof(params)); + + if ((enable == true) && (bp_params->percentage > 0)) + params.ucEnable = ATOM_ENABLE; + else + params.ucEnable = ATOM_DISABLE; + + params.usSpreadSpectrumPercentage = + cpu_to_le16((uint16_t)bp_params->percentage); + params.ucSpreadSpectrumStep = + (uint8_t)bp_params->ver1.step; + params.ucSpreadSpectrumDelay = + (uint8_t)bp_params->ver1.delay; + /* convert back to unit of 10KHz */ + params.ucSpreadSpectrumRange = + (uint8_t)(bp_params->ver1.range / 10000); + + if (bp_params->flags.EXTERNAL_SS) + params.ucSpreadSpectrumType |= ATOM_EXTERNAL_SS_MASK; + + if (bp_params->flags.CENTER_SPREAD) + params.ucSpreadSpectrumType |= ATOM_SS_CENTRE_SPREAD_MODE; + + if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL1) + params.ucPpll = ATOM_PPLL1; + else if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL2) + params.ucPpll = ATOM_PPLL2; + else + BREAK_TO_DEBUGGER(); /* Unexpected PLL value!! */ + + if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result enable_spread_spectrum_on_ppll_v2( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 params; + + memset(¶ms, 0, sizeof(params)); + + if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL1) + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V2_P1PLL; + else if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL2) + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V2_P2PLL; + else + BREAK_TO_DEBUGGER(); /* Unexpected PLL value!! */ + + if ((enable == true) && (bp_params->percentage > 0)) { + params.ucEnable = ATOM_ENABLE; + + params.usSpreadSpectrumPercentage = + cpu_to_le16((uint16_t)(bp_params->percentage)); + params.usSpreadSpectrumStep = + cpu_to_le16((uint16_t)(bp_params->ds.ds_frac_size)); + + if (bp_params->flags.EXTERNAL_SS) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V2_EXT_SPREAD; + + if (bp_params->flags.CENTER_SPREAD) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD; + + /* Both amounts need to be left shifted first before bit + * comparison. Otherwise, the result will always be zero here + */ + params.usSpreadSpectrumAmount = cpu_to_le16((uint16_t)( + ((bp_params->ds.feedback_amount << + ATOM_PPLL_SS_AMOUNT_V2_FBDIV_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK) | + ((bp_params->ds.nfrac_amount << + ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK))); + } else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result enable_spread_spectrum_on_ppll_v3( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 params; + + memset(¶ms, 0, sizeof(params)); + + switch (bp_params->pll_id) { + case CLOCK_SOURCE_ID_PLL0: + /* ATOM_PPLL_SS_TYPE_V3_P0PLL; this is pixel clock only, + * not for SI display clock. + */ + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_DCPLL; + break; + case CLOCK_SOURCE_ID_PLL1: + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_P1PLL; + break; + + case CLOCK_SOURCE_ID_PLL2: + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_P2PLL; + break; + + case CLOCK_SOURCE_ID_DCPLL: + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_DCPLL; + break; + + default: + BREAK_TO_DEBUGGER(); + /* Unexpected PLL value!! */ + return result; + } + + if (enable == true) { + params.ucEnable = ATOM_ENABLE; + + params.usSpreadSpectrumAmountFrac = + cpu_to_le16((uint16_t)(bp_params->ds_frac_amount)); + params.usSpreadSpectrumStep = + cpu_to_le16((uint16_t)(bp_params->ds.ds_frac_size)); + + if (bp_params->flags.EXTERNAL_SS) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V3_EXT_SPREAD; + if (bp_params->flags.CENTER_SPREAD) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V3_CENTRE_SPREAD; + + /* Both amounts need to be left shifted first before bit + * comparison. Otherwise, the result will always be zero here + */ + params.usSpreadSpectrumAmount = cpu_to_le16((uint16_t)( + ((bp_params->ds.feedback_amount << + ATOM_PPLL_SS_AMOUNT_V3_FBDIV_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V3_FBDIV_MASK) | + ((bp_params->ds.nfrac_amount << + ATOM_PPLL_SS_AMOUNT_V3_NFRAC_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V3_NFRAC_MASK))); + } else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ADJUST DISPLAY PLL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result adjust_display_pll_v2( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); +static enum bp_result adjust_display_pll_v3( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); + +static void init_adjust_display_pll(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(AdjustDisplayPll)) { + case 2: + bp->cmd_tbl.adjust_display_pll = adjust_display_pll_v2; + break; + case 3: + bp->cmd_tbl.adjust_display_pll = adjust_display_pll_v3; + break; + default: + bp->cmd_tbl.adjust_display_pll = NULL; + break; + } +} + +static enum bp_result adjust_display_pll_v2( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + ADJUST_DISPLAY_PLL_PS_ALLOCATION params = { 0 }; + + /* We need to convert from KHz units into 10KHz units and then convert + * output pixel clock back 10KHz-->KHz */ + uint32_t pixel_clock_10KHz_in = bp_params->pixel_clock / 10; + + params.usPixelClock = cpu_to_le16((uint16_t)(pixel_clock_10KHz_in)); + params.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + params.ucEncodeMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + return result; +} + +static enum bp_result adjust_display_pll_v3( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 params; + uint32_t pixel_clk_10_kHz_in = bp_params->pixel_clock / 10; + + memset(¶ms, 0, sizeof(params)); + + /* We need to convert from KHz units into 10KHz units and then convert + * output pixel clock back 10KHz-->KHz */ + params.sInput.usPixelClock = cpu_to_le16((uint16_t)pixel_clk_10_kHz_in); + params.sInput.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + params.sInput.ucEncodeMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + if (bp_params->ss_enable == true) + params.sInput.ucDispPllConfig |= DISPPLL_CONFIG_SS_ENABLE; + + if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK) + params.sInput.ucDispPllConfig |= DISPPLL_CONFIG_DUAL_LINK; + + if (EXEC_BIOS_CMD_TABLE(AdjustDisplayPll, params)) { + /* Convert output pixel clock back 10KHz-->KHz: multiply + * original pixel clock in KHz by ratio + * [output pxlClk/input pxlClk] */ + uint64_t pixel_clk_10_khz_out = + (uint64_t)le32_to_cpu(params.sOutput.ulDispPllFreq); + uint64_t pixel_clk = (uint64_t)bp_params->pixel_clock; + + if (pixel_clk_10_kHz_in != 0) { + bp_params->adjusted_pixel_clock = + div_u64(pixel_clk * pixel_clk_10_khz_out, + pixel_clk_10_kHz_in); + } else { + bp_params->adjusted_pixel_clock = 0; + BREAK_TO_DEBUGGER(); + } + + bp_params->reference_divider = params.sOutput.ucRefDiv; + bp_params->pixel_clock_post_divider = params.sOutput.ucPostDiv; + + result = BP_RESULT_OK; + } + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** DAC ENCODER CONTROL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result dac1_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); +static enum bp_result dac2_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + +static void init_dac_encoder_control(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC1EncoderControl)) { + case 1: + bp->cmd_tbl.dac1_encoder_control = dac1_encoder_control_v1; + break; + default: + bp->cmd_tbl.dac1_encoder_control = NULL; + break; + } + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC2EncoderControl)) { + case 1: + bp->cmd_tbl.dac2_encoder_control = dac2_encoder_control_v1; + break; + default: + bp->cmd_tbl.dac2_encoder_control = NULL; + break; + } +} + +static void dac_encoder_control_prepare_params( + DAC_ENCODER_CONTROL_PS_ALLOCATION *params, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard) +{ + params->ucDacStandard = dac_standard; + if (enable) + params->ucAction = ATOM_ENABLE; + else + params->ucAction = ATOM_DISABLE; + + /* We need to convert from KHz units into 10KHz units + * it looks as if the TvControl do not care about pixel clock + */ + params->usPixelClock = cpu_to_le16((uint16_t)(pixel_clock / 10)); +} + +static enum bp_result dac1_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard) +{ + enum bp_result result = BP_RESULT_FAILURE; + DAC_ENCODER_CONTROL_PS_ALLOCATION params; + + dac_encoder_control_prepare_params( + ¶ms, + enable, + pixel_clock, + dac_standard); + + if (EXEC_BIOS_CMD_TABLE(DAC1EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result dac2_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard) +{ + enum bp_result result = BP_RESULT_FAILURE; + DAC_ENCODER_CONTROL_PS_ALLOCATION params; + + dac_encoder_control_prepare_params( + ¶ms, + enable, + pixel_clock, + dac_standard); + + if (EXEC_BIOS_CMD_TABLE(DAC2EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** DAC OUTPUT CONTROL + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result dac1_output_control_v1( + struct bios_parser *bp, + bool enable); +static enum bp_result dac2_output_control_v1( + struct bios_parser *bp, + bool enable); + +static void init_dac_output_control(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC1OutputControl)) { + case 1: + bp->cmd_tbl.dac1_output_control = dac1_output_control_v1; + break; + default: + bp->cmd_tbl.dac1_output_control = NULL; + break; + } + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC2OutputControl)) { + case 1: + bp->cmd_tbl.dac2_output_control = dac2_output_control_v1; + break; + default: + bp->cmd_tbl.dac2_output_control = NULL; + break; + } +} + +static enum bp_result dac1_output_control_v1( + struct bios_parser *bp, bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION params; + + if (enable) + params.ucAction = ATOM_ENABLE; + else + params.ucAction = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(DAC1OutputControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result dac2_output_control_v1( + struct bios_parser *bp, bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION params; + + if (enable) + params.ucAction = ATOM_ENABLE; + else + params.ucAction = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(DAC2OutputControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** SET CRTC TIMING + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); +static enum bp_result set_crtc_timing_v1( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + +static void init_set_crtc_timing(struct bios_parser *bp) +{ + uint32_t dtd_version = + BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_UsingDTDTiming); + if (dtd_version > 2) + switch (dtd_version) { + case 3: + bp->cmd_tbl.set_crtc_timing = + set_crtc_using_dtd_timing_v3; + break; + default: + bp->cmd_tbl.set_crtc_timing = NULL; + break; + } + else + switch (BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_Timing)) { + case 1: + bp->cmd_tbl.set_crtc_timing = set_crtc_timing_v1; + break; + default: + bp->cmd_tbl.set_crtc_timing = NULL; + break; + } +} + +static enum bp_result set_crtc_timing_v1( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION params = {0}; + uint8_t atom_controller_id; + + if (bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &atom_controller_id)) + params.ucCRTC = atom_controller_id; + + params.usH_Total = cpu_to_le16((uint16_t)(bp_params->h_total)); + params.usH_Disp = cpu_to_le16((uint16_t)(bp_params->h_addressable)); + params.usH_SyncStart = cpu_to_le16((uint16_t)(bp_params->h_sync_start)); + params.usH_SyncWidth = cpu_to_le16((uint16_t)(bp_params->h_sync_width)); + params.usV_Total = cpu_to_le16((uint16_t)(bp_params->v_total)); + params.usV_Disp = cpu_to_le16((uint16_t)(bp_params->v_addressable)); + params.usV_SyncStart = + cpu_to_le16((uint16_t)(bp_params->v_sync_start)); + params.usV_SyncWidth = + cpu_to_le16((uint16_t)(bp_params->v_sync_width)); + + /* VBIOS does not expect any value except zero into this call, for + * underscan use another entry ProgramOverscan call but when mode + * 1776x1000 with the overscan 72x44 .e.i. 1920x1080 @30 DAL2 is ok, + * but when same ,but 60 Hz there is corruption + * DAL1 does not allow the mode 1776x1000@60 + */ + params.ucOverscanRight = (uint8_t)bp_params->h_overscan_right; + params.ucOverscanLeft = (uint8_t)bp_params->h_overscan_left; + params.ucOverscanBottom = (uint8_t)bp_params->v_overscan_bottom; + params.ucOverscanTop = (uint8_t)bp_params->v_overscan_top; + + if (0 == bp_params->flags.HSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_HSYNC_POLARITY); + + if (0 == bp_params->flags.VSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_VSYNC_POLARITY); + + if (bp_params->flags.INTERLACE) { + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_INTERLACE); + + /* original DAL code has this condition to apply tis for + * non-TV/CV only due to complex MV testing for possible + * impact + * if (pACParameters->signal != SignalType_YPbPr && + * pACParameters->signal != SignalType_Composite && + * pACParameters->signal != SignalType_SVideo) + */ + /* HW will deduct 0.5 line from 2nd feild. + * i.e. for 1080i, it is 2 lines for 1st field, 2.5 + * lines for the 2nd feild. we need input as 5 instead + * of 4, but it is 4 either from Edid data + * (spec CEA 861) or CEA timing table. + */ + params.usV_SyncStart = + cpu_to_le16((uint16_t)(bp_params->v_sync_start + 1)); + } + + if (bp_params->flags.HORZ_COUNT_BY_TWO) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_DOUBLE_CLOCK_MODE); + + if (EXEC_BIOS_CMD_TABLE(SetCRTC_Timing, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_CRTC_USING_DTD_TIMING_PARAMETERS params = {0}; + uint8_t atom_controller_id; + + if (bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &atom_controller_id)) + params.ucCRTC = atom_controller_id; + + /* bios usH_Size wants h addressable size */ + params.usH_Size = cpu_to_le16((uint16_t)bp_params->h_addressable); + /* bios usH_Blanking_Time wants borders included in blanking */ + params.usH_Blanking_Time = + cpu_to_le16((uint16_t)(bp_params->h_total - bp_params->h_addressable)); + /* bios usV_Size wants v addressable size */ + params.usV_Size = cpu_to_le16((uint16_t)bp_params->v_addressable); + /* bios usV_Blanking_Time wants borders included in blanking */ + params.usV_Blanking_Time = + cpu_to_le16((uint16_t)(bp_params->v_total - bp_params->v_addressable)); + /* bios usHSyncOffset is the offset from the end of h addressable, + * our horizontalSyncStart is the offset from the beginning + * of h addressable */ + params.usH_SyncOffset = + cpu_to_le16((uint16_t)(bp_params->h_sync_start - bp_params->h_addressable)); + params.usH_SyncWidth = cpu_to_le16((uint16_t)bp_params->h_sync_width); + /* bios usHSyncOffset is the offset from the end of v addressable, + * our verticalSyncStart is the offset from the beginning of + * v addressable */ + params.usV_SyncOffset = + cpu_to_le16((uint16_t)(bp_params->v_sync_start - bp_params->v_addressable)); + params.usV_SyncWidth = cpu_to_le16((uint16_t)bp_params->v_sync_width); + + /* we assume that overscan from original timing does not get bigger + * than 255 + * we will program all the borders in the Set CRTC Overscan call below + */ + + if (0 == bp_params->flags.HSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_HSYNC_POLARITY); + + if (0 == bp_params->flags.VSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_VSYNC_POLARITY); + + if (bp_params->flags.INTERLACE) { + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_INTERLACE); + + /* original DAL code has this condition to apply this + * for non-TV/CV only + * due to complex MV testing for possible impact + * if ( pACParameters->signal != SignalType_YPbPr && + * pACParameters->signal != SignalType_Composite && + * pACParameters->signal != SignalType_SVideo) + */ + { + /* HW will deduct 0.5 line from 2nd feild. + * i.e. for 1080i, it is 2 lines for 1st field, + * 2.5 lines for the 2nd feild. we need input as 5 + * instead of 4. + * but it is 4 either from Edid data (spec CEA 861) + * or CEA timing table. + */ + params.usV_SyncOffset = + cpu_to_le16(le16_to_cpu(params.usV_SyncOffset) + 1); + + } + } + + if (bp_params->flags.HORZ_COUNT_BY_TWO) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_DOUBLE_CLOCK_MODE); + + if (EXEC_BIOS_CMD_TABLE(SetCRTC_UsingDTDTiming, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** SELECT CRTC SOURCE + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result select_crtc_source_v2( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params); +static enum bp_result select_crtc_source_v3( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params); + +static void init_select_crtc_source(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(SelectCRTC_Source)) { + case 2: + bp->cmd_tbl.select_crtc_source = select_crtc_source_v2; + break; + case 3: + bp->cmd_tbl.select_crtc_source = select_crtc_source_v3; + break; + default: + bp->cmd_tbl.select_crtc_source = NULL; + break; + } +} + +static enum bp_result select_crtc_source_v2( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SELECT_CRTC_SOURCE_PARAMETERS_V2 params; + uint8_t atom_controller_id; + uint32_t atom_engine_id; + enum signal_type s = bp_params->signal; + + memset(¶ms, 0, sizeof(params)); + + /* set controller id */ + if (bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &atom_controller_id)) + params.ucCRTC = atom_controller_id; + else + return BP_RESULT_FAILURE; + + /* set encoder id */ + if (bp->cmd_helper->engine_bp_to_atom( + bp_params->engine_id, &atom_engine_id)) + params.ucEncoderID = (uint8_t)atom_engine_id; + else + return BP_RESULT_FAILURE; + + if (SIGNAL_TYPE_EDP == s || + (SIGNAL_TYPE_DISPLAY_PORT == s && + SIGNAL_TYPE_LVDS == bp_params->sink_signal)) + s = SIGNAL_TYPE_LVDS; + + params.ucEncodeMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + s, bp_params->enable_dp_audio); + + if (EXEC_BIOS_CMD_TABLE(SelectCRTC_Source, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result select_crtc_source_v3( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params) +{ + bool result = BP_RESULT_FAILURE; + SELECT_CRTC_SOURCE_PARAMETERS_V3 params; + uint8_t atom_controller_id; + uint32_t atom_engine_id; + enum signal_type s = bp_params->signal; + + memset(¶ms, 0, sizeof(params)); + + if (bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, + &atom_controller_id)) + params.ucCRTC = atom_controller_id; + else + return result; + + if (bp->cmd_helper->engine_bp_to_atom(bp_params->engine_id, + &atom_engine_id)) + params.ucEncoderID = (uint8_t)atom_engine_id; + else + return result; + + if (SIGNAL_TYPE_EDP == s || + (SIGNAL_TYPE_DISPLAY_PORT == s && + SIGNAL_TYPE_LVDS == bp_params->sink_signal)) + s = SIGNAL_TYPE_LVDS; + + params.ucEncodeMode = + bp->cmd_helper->encoder_mode_bp_to_atom( + s, bp_params->enable_dp_audio); + /* Needed for VBIOS Random Spatial Dithering feature */ + params.ucDstBpc = (uint8_t)(bp_params->display_output_bit_depth); + + if (EXEC_BIOS_CMD_TABLE(SelectCRTC_Source, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE CRTC + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + +static void init_enable_crtc(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableCRTC)) { + case 1: + bp->cmd_tbl.enable_crtc = enable_crtc_v1; + break; + default: + bp->cmd_tbl.enable_crtc = NULL; + break; + } +} + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable) +{ + bool result = BP_RESULT_FAILURE; + ENABLE_CRTC_PARAMETERS params = {0}; + uint8_t id; + + if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) + params.ucCRTC = id; + else + return BP_RESULT_BADINPUT; + + if (enable) + params.ucEnable = ATOM_ENABLE; + else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableCRTC, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE CRTC MEM REQ + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result enable_crtc_mem_req_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + +static void init_enable_crtc_mem_req(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableCRTCMemReq)) { + case 1: + bp->cmd_tbl.enable_crtc_mem_req = enable_crtc_mem_req_v1; + break; + default: + bp->cmd_tbl.enable_crtc_mem_req = NULL; + break; + } +} + +static enum bp_result enable_crtc_mem_req_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable) +{ + bool result = BP_RESULT_BADINPUT; + ENABLE_CRTC_PARAMETERS params = {0}; + uint8_t id; + + if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) { + params.ucCRTC = id; + + if (enable) + params.ucEnable = ATOM_ENABLE; + else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableCRTCMemReq, params)) + result = BP_RESULT_OK; + else + result = BP_RESULT_FAILURE; + } + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** DISPLAY PLL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result program_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result program_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + +static void init_program_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)) { + case 5: + bp->cmd_tbl.program_clock = program_clock_v5; + break; + case 6: + bp->cmd_tbl.program_clock = program_clock_v6; + break; + default: + bp->cmd_tbl.program_clock = NULL; + break; + } +} + +static enum bp_result program_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + SET_PIXEL_CLOCK_PS_ALLOCATION_V5 params; + uint32_t atom_pll_id; + + memset(¶ms, 0, sizeof(params)); + if (!bp->cmd_helper->clock_source_id_to_atom( + bp_params->pll_id, &atom_pll_id)) { + BREAK_TO_DEBUGGER(); /* Invalid Inpute!! */ + return BP_RESULT_BADINPUT; + } + + /* We need to convert from KHz units into 10KHz units */ + params.sPCLKInput.ucPpll = (uint8_t) atom_pll_id; + params.sPCLKInput.usPixelClock = + cpu_to_le16((uint16_t) (bp_params->target_pixel_clock / 10)); + params.sPCLKInput.ucCRTC = (uint8_t) ATOM_CRTC_INVALID; + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result program_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + SET_PIXEL_CLOCK_PS_ALLOCATION_V6 params; + uint32_t atom_pll_id; + + memset(¶ms, 0, sizeof(params)); + + if (!bp->cmd_helper->clock_source_id_to_atom( + bp_params->pll_id, &atom_pll_id)) { + BREAK_TO_DEBUGGER(); /*Invalid Input!!*/ + return BP_RESULT_BADINPUT; + } + + /* We need to convert from KHz units into 10KHz units */ + params.sPCLKInput.ucPpll = (uint8_t)atom_pll_id; + params.sPCLKInput.ulDispEngClkFreq = + cpu_to_le32(bp_params->target_pixel_clock / 10); + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, params)) { + /* True display clock is returned by VBIOS if DFS bypass + * is enabled. */ + bp_params->dfs_bypass_display_clock = + (uint32_t)(le32_to_cpu(params.sPCLKInput.ulDispEngClkFreq) * 10); + result = BP_RESULT_OK; + } + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** EXTERNAL ENCODER CONTROL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + +static void init_external_encoder_control( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(ExternalEncoderControl)) { + case 3: + bp->cmd_tbl.external_encoder_control = + external_encoder_control_v3; + break; + default: + bp->cmd_tbl.external_encoder_control = NULL; + break; + } +} + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + + /* we need use _PS_Alloc struct */ + EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 params; + EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 *cntl_params; + struct graphics_object_id encoder; + bool is_input_signal_dp = false; + + memset(¶ms, 0, sizeof(params)); + + cntl_params = ¶ms.sExtEncoder; + + encoder = cntl->encoder_id; + + /* check if encoder supports external encoder control table */ + switch (dal_graphics_object_id_get_encoder_id(encoder)) { + case ENCODER_ID_EXTERNAL_NUTMEG: + case ENCODER_ID_EXTERNAL_TRAVIS: + is_input_signal_dp = true; + break; + + default: + BREAK_TO_DEBUGGER(); + return BP_RESULT_BADINPUT; + } + + /* Fill information based on the action + * + * Bit[6:4]: indicate external encoder, applied to all functions. + * =0: external encoder1, mapped to external encoder enum id1 + * =1: external encoder2, mapped to external encoder enum id2 + * + * enum ObjectEnumId + * { + * EnumId_Unknown = 0, + * EnumId_1, + * EnumId_2, + * }; + */ + cntl_params->ucConfig = (uint8_t)((encoder.enum_id - 1) << 4); + + switch (cntl->action) { + case EXTERNAL_ENCODER_CONTROL_INIT: + /* output display connector type. Only valid in encoder + * initialization */ + cntl_params->usConnectorId = + cpu_to_le16((uint16_t)cntl->connector_obj_id.id); + break; + case EXTERNAL_ENCODER_CONTROL_SETUP: + /* EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 pixel clock unit in + * 10KHz + * output display device pixel clock frequency in unit of 10KHz. + * Only valid in setup and enableoutput + */ + cntl_params->usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + /* Indicate display output signal type drive by external + * encoder, only valid in setup and enableoutput */ + cntl_params->ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, false); + + if (is_input_signal_dp) { + /* Bit[0]: indicate link rate, =1: 2.7Ghz, =0: 1.62Ghz, + * only valid in encoder setup with DP mode. */ + if (LINK_RATE_HIGH == cntl->link_rate) + cntl_params->ucConfig |= 1; + /* output color depth Indicate encoder data bpc format + * in DP mode, only valid in encoder setup in DP mode. + */ + cntl_params->ucBitPerColor = + (uint8_t)(cntl->color_depth); + } + /* Indicate how many lanes used by external encoder, only valid + * in encoder setup and enableoutput. */ + cntl_params->ucLaneNum = (uint8_t)(cntl->lanes_number); + break; + case EXTERNAL_ENCODER_CONTROL_ENABLE: + cntl_params->usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + cntl_params->ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, false); + cntl_params->ucLaneNum = (uint8_t)cntl->lanes_number; + break; + default: + break; + } + + cntl_params->ucAction = (uint8_t)cntl->action; + + if (EXEC_BIOS_CMD_TABLE(ExternalEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE DISPLAY POWER GATING + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + +static void init_enable_disp_power_gating( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableDispPowerGating)) { + case 1: + bp->cmd_tbl.enable_disp_power_gating = + enable_disp_power_gating_v2_1; + break; + default: + bp->cmd_tbl.enable_disp_power_gating = NULL; + break; + } +} + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action) +{ + enum bp_result result = BP_RESULT_FAILURE; + + ENABLE_DISP_POWER_GATING_PS_ALLOCATION params = {0}; + uint8_t atom_crtc_id; + + if (bp->cmd_helper->controller_id_to_atom(crtc_id, &atom_crtc_id)) + params.ucDispPipeId = atom_crtc_id; + else + return BP_RESULT_BADINPUT; + + params.ucEnable = + bp->cmd_helper->disp_power_gating_action_to_atom(action); + + if (EXEC_BIOS_CMD_TABLE(EnableDispPowerGating, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** SET DCE CLOCK + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); + +static void init_set_dce_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(SetDCEClock)) { + case 1: + bp->cmd_tbl.set_dce_clock = set_dce_clock_v2_1; + break; + default: + bp->cmd_tbl.set_dce_clock = NULL; + break; + } +} + +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + SET_DCE_CLOCK_PS_ALLOCATION_V2_1 params; + uint32_t atom_pll_id; + uint32_t atom_clock_type; + const struct command_table_helper *cmd = bp->cmd_helper; + + memset(¶ms, 0, sizeof(params)); + + if (!cmd->clock_source_id_to_atom(bp_params->pll_id, &atom_pll_id) || + !cmd->dc_clock_type_to_atom(bp_params->clock_type, &atom_clock_type)) + return BP_RESULT_BADINPUT; + + params.asParam.ucDCEClkSrc = atom_pll_id; + params.asParam.ucDCEClkType = atom_clock_type; + + if (bp_params->clock_type == DCECLOCK_TYPE_DPREFCLK) { + if (bp_params->flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENLK; + + if (bp_params->flags.USE_PCIE_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_PCIE; + + if (bp_params->flags.USE_XTALIN_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_XTALIN; + + if (bp_params->flags.USE_GENERICA_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENERICA; + } + else + /* only program clock frequency if display clock is used; VBIOS will program DPREFCLK */ + /* We need to convert from KHz units into 10KHz units */ + params.asParam.ulDCEClkFreq = cpu_to_le32(bp_params->target_clock_frequency / 10); + + if (EXEC_BIOS_CMD_TABLE(SetDCEClock, params)) { + /* Convert from 10KHz units back to KHz */ + bp_params->target_clock_frequency = le32_to_cpu(params.asParam.ulDCEClkFreq) * 10; + result = BP_RESULT_OK; + } + + return result; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.h b/drivers/gpu/drm/amd/display/dc/bios/command_table.h new file mode 100644 index 000000000000..94f3d43a7471 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.h @@ -0,0 +1,102 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE_H__ +#define __DAL_COMMAND_TABLE_H__ + +struct bios_parser; +struct bp_encoder_control; + +struct cmd_tbl { + enum bp_result (*dig_encoder_control)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig1)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig2)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*transmitter_control)( + struct bios_parser *bp, + struct bp_transmitter_control *control); + enum bp_result (*set_pixel_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*enable_spread_spectrum_on_ppll)( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); + enum bp_result (*adjust_display_pll)( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); + enum bp_result (*dac1_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac2_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac1_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*dac2_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*set_crtc_timing)( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + enum bp_result (*select_crtc_source)( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params); + enum bp_result (*enable_crtc)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*enable_crtc_mem_req)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*program_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*external_encoder_control)( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + enum bp_result (*enable_disp_power_gating)( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + enum bp_result (*set_dce_clock)( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); +}; + +void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c new file mode 100644 index 000000000000..ba68693758a7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c @@ -0,0 +1,812 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "ObjectID.h" +#include "atomfirmware.h" + +#include "include/bios_parser_interface.h" + +#include "command_table2.h" +#include "command_table_helper2.h" +#include "bios_parser_helper.h" +#include "bios_parser_types_internal2.h" + +#define GET_INDEX_INTO_MASTER_TABLE(MasterOrData, FieldName)\ + (((char *)(&((\ + struct atom_master_list_of_##MasterOrData##_functions_v2_1 *)0)\ + ->FieldName)-(char *)0)/sizeof(uint16_t)) + +#define EXEC_BIOS_CMD_TABLE(fname, params)\ + (cgs_atom_exec_cmd_table(bp->base.ctx->cgs_device, \ + GET_INDEX_INTO_MASTER_TABLE(command, fname), \ + ¶ms) == 0) + +#define BIOS_CMD_TABLE_REVISION(fname, frev, crev)\ + cgs_atom_get_cmd_table_revs(bp->base.ctx->cgs_device, \ + GET_INDEX_INTO_MASTER_TABLE(command, fname), &frev, &crev) + +#define BIOS_CMD_TABLE_PARA_REVISION(fname)\ + bios_cmd_table_para_revision(bp->base.ctx->cgs_device, \ + GET_INDEX_INTO_MASTER_TABLE(command, fname)) + +static void init_dig_encoder_control(struct bios_parser *bp); +static void init_transmitter_control(struct bios_parser *bp); +static void init_set_pixel_clock(struct bios_parser *bp); + +static void init_set_crtc_timing(struct bios_parser *bp); + +static void init_select_crtc_source(struct bios_parser *bp); +static void init_enable_crtc(struct bios_parser *bp); + +static void init_external_encoder_control(struct bios_parser *bp); +static void init_enable_disp_power_gating(struct bios_parser *bp); +static void init_set_dce_clock(struct bios_parser *bp); +static void init_get_smu_clock_info(struct bios_parser *bp); + +void dal_firmware_parser_init_cmd_tbl(struct bios_parser *bp) +{ + init_dig_encoder_control(bp); + init_transmitter_control(bp); + init_set_pixel_clock(bp); + + init_set_crtc_timing(bp); + + init_select_crtc_source(bp); + init_enable_crtc(bp); + + init_external_encoder_control(bp); + init_enable_disp_power_gating(bp); + init_set_dce_clock(bp); + init_get_smu_clock_info(bp); +} + +static uint32_t bios_cmd_table_para_revision(void *cgs_device, + uint32_t index) +{ + uint8_t frev, crev; + + if (cgs_atom_get_cmd_table_revs(cgs_device, + index, + &frev, &crev) != 0) + return 0; + return crev; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** D I G E N C O D E R C O N T R O L + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result encoder_control_digx_v1_5( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static void init_dig_encoder_control(struct bios_parser *bp) +{ + uint32_t version = + BIOS_CMD_TABLE_PARA_REVISION(digxencodercontrol); + + switch (version) { + case 5: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v1_5; + break; + default: + bp->cmd_tbl.dig_encoder_control = NULL; + break; + } +} + +static enum bp_result encoder_control_digx_v1_5( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct dig_encoder_stream_setup_parameters_v1_5 params = {0}; + + params.digid = (uint8_t)(cntl->engine_id); + params.action = bp->cmd_helper->encoder_action_to_atom(cntl->action); + + params.pclk_10khz = cntl->pixel_clock / 10; + params.digmode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio)); + params.lanenum = (uint8_t)(cntl->lanes_number); + + switch (cntl->color_depth) { + case COLOR_DEPTH_888: + params.bitpercolor = PANEL_8BIT_PER_COLOR; + break; + case COLOR_DEPTH_101010: + params.bitpercolor = PANEL_10BIT_PER_COLOR; + break; + case COLOR_DEPTH_121212: + params.bitpercolor = PANEL_12BIT_PER_COLOR; + break; + case COLOR_DEPTH_161616: + params.bitpercolor = PANEL_16BIT_PER_COLOR; + break; + default: + break; + } + + if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) + switch (cntl->color_depth) { + case COLOR_DEPTH_101010: + params.pclk_10khz = + (params.pclk_10khz * 30) / 24; + break; + case COLOR_DEPTH_121212: + params.pclk_10khz = + (params.pclk_10khz * 36) / 24; + break; + case COLOR_DEPTH_161616: + params.pclk_10khz = + (params.pclk_10khz * 48) / 24; + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(digxencodercontrol, params)) + result = BP_RESULT_OK; + + return result; +} + +/***************************************************************************** + ****************************************************************************** + ** + ** TRANSMITTER CONTROL + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); + +static void init_transmitter_control(struct bios_parser *bp) +{ + uint8_t frev; + uint8_t crev; + + if (BIOS_CMD_TABLE_REVISION(dig1transmittercontrol, frev, crev) != 0) + BREAK_TO_DEBUGGER(); + switch (crev) { + case 6: + bp->cmd_tbl.transmitter_control = transmitter_control_v1_6; + break; + default: + bp->cmd_tbl.transmitter_control = NULL; + break; + } +} + +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + const struct command_table_helper *cmd = bp->cmd_helper; + struct dig_transmitter_control_ps_allocation_v1_6 ps = { { 0 } }; + + ps.param.phyid = cmd->phy_id_to_atom(cntl->transmitter); + ps.param.action = (uint8_t)cntl->action; + + if (cntl->action == TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS) + ps.param.mode_laneset.dplaneset = (uint8_t)cntl->lane_settings; + else + ps.param.mode_laneset.digmode = + cmd->signal_type_to_atom_dig_mode(cntl->signal); + + ps.param.lanenum = (uint8_t)cntl->lanes_number; + ps.param.hpdsel = cmd->hpd_sel_to_atom(cntl->hpd_sel); + ps.param.digfe_sel = cmd->dig_encoder_sel_to_atom(cntl->engine_id); + ps.param.connobj_id = (uint8_t)cntl->connector_obj_id.id; + ps.param.symclk_10khz = cntl->pixel_clock/10; + + + if (cntl->action == TRANSMITTER_CONTROL_ENABLE || + cntl->action == TRANSMITTER_CONTROL_ACTIAVATE || + cntl->action == TRANSMITTER_CONTROL_DEACTIVATE) { + dm_logger_write(bp->base.ctx->logger, LOG_BIOS,\ + "%s:ps.param.symclk_10khz = %d\n",\ + __func__, ps.param.symclk_10khz); + } + + +/*color_depth not used any more, driver has deep color factor in the Phyclk*/ + if (EXEC_BIOS_CMD_TABLE(dig1transmittercontrol, ps)) + result = BP_RESULT_OK; + return result; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** SET PIXEL CLOCK + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + +static void init_set_pixel_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(setpixelclock)) { + case 7: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v7; + break; + default: + bp->cmd_tbl.set_pixel_clock = NULL; + break; + } +} + + + +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct set_pixel_clock_parameter_v1_7 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom(bp_params-> + controller_id, &controller_id)) { + /* Note: VBIOS still wants to use ucCRTC name which is now + * 1 byte in ULONG + *typedef struct _CRTC_PIXEL_CLOCK_FREQ + *{ + * target the pixel clock to drive the CRTC timing. + * ULONG ulPixelClock:24; + * 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to + * previous version. + * ATOM_CRTC1~6, indicate the CRTC controller to + * ULONG ucCRTC:8; + * drive the pixel clock. not used for DCPLL case. + *}CRTC_PIXEL_CLOCK_FREQ; + *union + *{ + * pixel clock and CRTC id frequency + * CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; + * ULONG ulDispEngClkFreq; dispclk frequency + *}; + */ + clk.crtc_id = controller_id; + clk.pll_id = (uint8_t) pll_id; + clk.encoderobjid = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + + clk.encoder_mode = (uint8_t) bp-> + cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + /* We need to convert from KHz units into 10KHz units */ + clk.pixclk_100hz = cpu_to_le32(bp_params->target_pixel_clock * + 10); + + clk.deep_color_ratio = + (uint8_t) bp->cmd_helper-> + transmitter_color_depth_to_atom( + bp_params->color_depth); + dm_logger_write(bp->base.ctx->logger, LOG_BIOS,\ + "%s:program display clock = %d"\ + "colorDepth = %d\n", __func__,\ + bp_params->target_pixel_clock, bp_params->color_depth); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.PROGRAM_PHY_PLL_ONLY) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_PROG_PHYPLL; + + if (bp_params->flags.SUPPORT_YUV_420) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_YUV420_MODE; + + if (bp_params->flags.SET_XTALIN_REF_SRC) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_XTALIN; + + if (bp_params->flags.SET_GENLOCK_REF_DIV_SRC) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_GENLK; + + if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_DVI_DUALLINK_EN; + + if (EXEC_BIOS_CMD_TABLE(setpixelclock, clk)) + result = BP_RESULT_OK; + } + return result; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** SET CRTC TIMING + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + +static void init_set_crtc_timing(struct bios_parser *bp) +{ + uint32_t dtd_version = + BIOS_CMD_TABLE_PARA_REVISION(setcrtc_usingdtdtiming); + + switch (dtd_version) { + case 3: + bp->cmd_tbl.set_crtc_timing = + set_crtc_using_dtd_timing_v3; + break; + default: + bp->cmd_tbl.set_crtc_timing = NULL; + break; + } +} + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct set_crtc_using_dtd_timing_parameters params = {0}; + uint8_t atom_controller_id; + + if (bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &atom_controller_id)) + params.crtc_id = atom_controller_id; + + /* bios usH_Size wants h addressable size */ + params.h_size = cpu_to_le16((uint16_t)bp_params->h_addressable); + /* bios usH_Blanking_Time wants borders included in blanking */ + params.h_blanking_time = + cpu_to_le16((uint16_t)(bp_params->h_total - + bp_params->h_addressable)); + /* bios usV_Size wants v addressable size */ + params.v_size = cpu_to_le16((uint16_t)bp_params->v_addressable); + /* bios usV_Blanking_Time wants borders included in blanking */ + params.v_blanking_time = + cpu_to_le16((uint16_t)(bp_params->v_total - + bp_params->v_addressable)); + /* bios usHSyncOffset is the offset from the end of h addressable, + * our horizontalSyncStart is the offset from the beginning + * of h addressable + */ + params.h_syncoffset = + cpu_to_le16((uint16_t)(bp_params->h_sync_start - + bp_params->h_addressable)); + params.h_syncwidth = cpu_to_le16((uint16_t)bp_params->h_sync_width); + /* bios usHSyncOffset is the offset from the end of v addressable, + * our verticalSyncStart is the offset from the beginning of + * v addressable + */ + params.v_syncoffset = + cpu_to_le16((uint16_t)(bp_params->v_sync_start - + bp_params->v_addressable)); + params.v_syncwidth = cpu_to_le16((uint16_t)bp_params->v_sync_width); + + /* we assume that overscan from original timing does not get bigger + * than 255 + * we will program all the borders in the Set CRTC Overscan call below + */ + + if (bp_params->flags.HSYNC_POSITIVE_POLARITY == 0) + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + ATOM_HSYNC_POLARITY); + + if (bp_params->flags.VSYNC_POSITIVE_POLARITY == 0) + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + ATOM_VSYNC_POLARITY); + + if (bp_params->flags.INTERLACE) { + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + ATOM_INTERLACE); + + /* original DAL code has this condition to apply this + * for non-TV/CV only + * due to complex MV testing for possible impact + * if ( pACParameters->signal != SignalType_YPbPr && + * pACParameters->signal != SignalType_Composite && + * pACParameters->signal != SignalType_SVideo) + */ + { + /* HW will deduct 0.5 line from 2nd feild. + * i.e. for 1080i, it is 2 lines for 1st field, + * 2.5 lines for the 2nd feild. we need input as 5 + * instead of 4. + * but it is 4 either from Edid data (spec CEA 861) + * or CEA timing table. + */ + params.v_syncoffset = + cpu_to_le16(le16_to_cpu(params.v_syncoffset) + + 1); + + } + } + + if (bp_params->flags.HORZ_COUNT_BY_TWO) + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + 0x100); /* ATOM_DOUBLE_CLOCK_MODE */ + + if (EXEC_BIOS_CMD_TABLE(setcrtc_usingdtdtiming, params)) + result = BP_RESULT_OK; + + return result; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** SELECT CRTC SOURCE + ** + ****************************************************************************** + *****************************************************************************/ + + +static enum bp_result select_crtc_source_v3( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params); + +static void init_select_crtc_source(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(selectcrtc_source)) { + case 3: + bp->cmd_tbl.select_crtc_source = select_crtc_source_v3; + break; + default: + bp->cmd_tbl.select_crtc_source = NULL; + break; + } +} + + +static enum bp_result select_crtc_source_v3( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params) +{ + bool result = BP_RESULT_FAILURE; + struct select_crtc_source_parameters_v2_3 params; + uint8_t atom_controller_id; + uint32_t atom_engine_id; + enum signal_type s = bp_params->signal; + + memset(¶ms, 0, sizeof(params)); + + if (bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, + &atom_controller_id)) + params.crtc_id = atom_controller_id; + else + return result; + + if (bp->cmd_helper->engine_bp_to_atom(bp_params->engine_id, + &atom_engine_id)) + params.encoder_id = (uint8_t)atom_engine_id; + else + return result; + + if (s == SIGNAL_TYPE_EDP || + (s == SIGNAL_TYPE_DISPLAY_PORT && bp_params->sink_signal == + SIGNAL_TYPE_LVDS)) + s = SIGNAL_TYPE_LVDS; + + params.encode_mode = + bp->cmd_helper->encoder_mode_bp_to_atom( + s, bp_params->enable_dp_audio); + /* Needed for VBIOS Random Spatial Dithering feature */ + params.dst_bpc = (uint8_t)(bp_params->display_output_bit_depth); + + if (EXEC_BIOS_CMD_TABLE(selectcrtc_source, params)) + result = BP_RESULT_OK; + + return result; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** ENABLE CRTC + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + +static void init_enable_crtc(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(enablecrtc)) { + case 1: + bp->cmd_tbl.enable_crtc = enable_crtc_v1; + break; + default: + bp->cmd_tbl.enable_crtc = NULL; + break; + } +} + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable) +{ + bool result = BP_RESULT_FAILURE; + struct enable_crtc_parameters params = {0}; + uint8_t id; + + if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) + params.crtc_id = id; + else + return BP_RESULT_BADINPUT; + + if (enable) + params.enable = ATOM_ENABLE; + else + params.enable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(enablecrtc, params)) + result = BP_RESULT_OK; + + return result; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** DISPLAY PLL + ** + ****************************************************************************** + *****************************************************************************/ + + + +/****************************************************************************** + ****************************************************************************** + ** + ** EXTERNAL ENCODER CONTROL + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + +static void init_external_encoder_control( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(externalencodercontrol)) { + case 3: + bp->cmd_tbl.external_encoder_control = + external_encoder_control_v3; + break; + default: + bp->cmd_tbl.external_encoder_control = NULL; + break; + } +} + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl) +{ + /* TODO */ + return BP_RESULT_OK; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** ENABLE DISPLAY POWER GATING + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + +static void init_enable_disp_power_gating( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(enabledisppowergating)) { + case 1: + bp->cmd_tbl.enable_disp_power_gating = + enable_disp_power_gating_v2_1; + break; + default: + bp->cmd_tbl.enable_disp_power_gating = NULL; + break; + } +} + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action) +{ + enum bp_result result = BP_RESULT_FAILURE; + + + struct enable_disp_power_gating_ps_allocation ps = { { 0 } }; + uint8_t atom_crtc_id; + + if (bp->cmd_helper->controller_id_to_atom(crtc_id, &atom_crtc_id)) + ps.param.disp_pipe_id = atom_crtc_id; + else + return BP_RESULT_BADINPUT; + + ps.param.enable = + bp->cmd_helper->disp_power_gating_action_to_atom(action); + + if (EXEC_BIOS_CMD_TABLE(enabledisppowergating, ps.param)) + result = BP_RESULT_OK; + + return result; +} + +/****************************************************************************** +******************************************************************************* + ** + ** SET DCE CLOCK + ** +******************************************************************************* +*******************************************************************************/ + +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); + +static void init_set_dce_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(setdceclock)) { + case 1: + bp->cmd_tbl.set_dce_clock = set_dce_clock_v2_1; + break; + default: + bp->cmd_tbl.set_dce_clock = NULL; + break; + } +} + +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + struct set_dce_clock_ps_allocation_v2_1 params; + uint32_t atom_pll_id; + uint32_t atom_clock_type; + const struct command_table_helper *cmd = bp->cmd_helper; + + memset(¶ms, 0, sizeof(params)); + + if (!cmd->clock_source_id_to_atom(bp_params->pll_id, &atom_pll_id) || + !cmd->dc_clock_type_to_atom(bp_params->clock_type, + &atom_clock_type)) + return BP_RESULT_BADINPUT; + + params.param.dceclksrc = atom_pll_id; + params.param.dceclktype = atom_clock_type; + + if (bp_params->clock_type == DCECLOCK_TYPE_DPREFCLK) { + if (bp_params->flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENLK; + + if (bp_params->flags.USE_PCIE_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_PCIE; + + if (bp_params->flags.USE_XTALIN_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_XTALIN; + + if (bp_params->flags.USE_GENERICA_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENERICA; + } else + /* only program clock frequency if display clock is used; + * VBIOS will program DPREFCLK + * We need to convert from KHz units into 10KHz units + */ + params.param.dceclk_10khz = cpu_to_le32( + bp_params->target_clock_frequency / 10); + dm_logger_write(bp->base.ctx->logger, LOG_BIOS, + "%s:target_clock_frequency = %d"\ + "clock_type = %d \n", __func__,\ + bp_params->target_clock_frequency,\ + bp_params->clock_type); + + if (EXEC_BIOS_CMD_TABLE(setdceclock, params)) { + /* Convert from 10KHz units back to KHz */ + bp_params->target_clock_frequency = le32_to_cpu( + params.param.dceclk_10khz) * 10; + result = BP_RESULT_OK; + } + + return result; +} + + +/****************************************************************************** + ****************************************************************************** + ** + ** GET SMU CLOCK INFO + ** + ****************************************************************************** + *****************************************************************************/ + +static unsigned int get_smu_clock_info_v3_1(struct bios_parser *bp); + +static void init_get_smu_clock_info(struct bios_parser *bp) +{ + /* TODO add switch for table vrsion */ + bp->cmd_tbl.get_smu_clock_info = get_smu_clock_info_v3_1; + +} + +static unsigned int get_smu_clock_info_v3_1(struct bios_parser *bp) +{ + struct atom_get_smu_clock_info_parameters_v3_1 smu_input = {0}; + struct atom_get_smu_clock_info_output_parameters_v3_1 smu_output; + + smu_input.command = GET_SMU_CLOCK_INFO_V3_1_GET_PLLVCO_FREQ; + + /* Get Specific Clock */ + if (EXEC_BIOS_CMD_TABLE(getsmuclockinfo, smu_input)) { + memmove(&smu_output, &smu_input, sizeof( + struct atom_get_smu_clock_info_parameters_v3_1)); + return smu_output.atom_smu_outputclkfreq.syspllvcofreq_10khz; + } + + return 0; +} + diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table2.h b/drivers/gpu/drm/amd/display/dc/bios/command_table2.h new file mode 100644 index 000000000000..59061b806df5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table2.h @@ -0,0 +1,105 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE2_H__ +#define __DAL_COMMAND_TABLE2_H__ + +struct bios_parser; +struct bp_encoder_control; + +struct cmd_tbl { + enum bp_result (*dig_encoder_control)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig1)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig2)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*transmitter_control)( + struct bios_parser *bp, + struct bp_transmitter_control *control); + enum bp_result (*set_pixel_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*enable_spread_spectrum_on_ppll)( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); + enum bp_result (*adjust_display_pll)( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); + enum bp_result (*dac1_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac2_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac1_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*dac2_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*set_crtc_timing)( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + enum bp_result (*select_crtc_source)( + struct bios_parser *bp, + struct bp_crtc_source_select *bp_params); + enum bp_result (*enable_crtc)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*enable_crtc_mem_req)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*program_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*external_encoder_control)( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + enum bp_result (*enable_disp_power_gating)( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + enum bp_result (*set_dce_clock)( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); + unsigned int (*get_smu_clock_info)( + struct bios_parser *bp); + +}; + +void dal_firmware_parser_init_cmd_tbl(struct bios_parser *bp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.c b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.c new file mode 100644 index 000000000000..2979358c6a55 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.c @@ -0,0 +1,290 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "command_table_helper.h" + +bool dal_bios_parser_init_cmd_tbl_helper( + const struct command_table_helper **h, + enum dce_version dce) +{ + switch (dce) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + *h = dal_cmd_tbl_helper_dce80_get_table(); + return true; + + case DCE_VERSION_10_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_2: + *h = dal_cmd_tbl_helper_dce112_get_table(); + return true; + + default: + /* Unsupported DCE */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/* real implementations */ + +bool dal_cmd_table_helper_controller_id_to_atom( + enum controller_id id, + uint8_t *atom_id) +{ + if (atom_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CONTROLLER_ID_D0: + *atom_id = ATOM_CRTC1; + return true; + case CONTROLLER_ID_D1: + *atom_id = ATOM_CRTC2; + return true; + case CONTROLLER_ID_D2: + *atom_id = ATOM_CRTC3; + return true; + case CONTROLLER_ID_D3: + *atom_id = ATOM_CRTC4; + return true; + case CONTROLLER_ID_D4: + *atom_id = ATOM_CRTC5; + return true; + case CONTROLLER_ID_D5: + *atom_id = ATOM_CRTC6; + return true; + case CONTROLLER_ID_UNDERLAY0: + *atom_id = ATOM_UNDERLAY_PIPE0; + return true; + case CONTROLLER_ID_UNDEFINED: + *atom_id = ATOM_CRTC_INVALID; + return true; + default: + /* Wrong controller id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/** +* translate_transmitter_bp_to_atom +* +* @brief +* Translate the Transmitter to the corresponding ATOM BIOS value +* +* @param +* input transmitter +* output digitalTransmitter +* // =00: Digital Transmitter1 ( UNIPHY linkAB ) +* // =01: Digital Transmitter2 ( UNIPHY linkCD ) +* // =02: Digital Transmitter3 ( UNIPHY linkEF ) +*/ +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom( + enum transmitter t) +{ + switch (t) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_TRAVIS_LCD: + return 0; + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + return 1; + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + return 2; + default: + /* Invalid Transmitter Type! */ + BREAK_TO_DEBUGGER(); + return 0; + } +} + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom( + enum signal_type s, + bool enable_dp_audio) +{ + switch (s) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + return ATOM_ENCODER_MODE_DVI; + case SIGNAL_TYPE_HDMI_TYPE_A: + return ATOM_ENCODER_MODE_HDMI; + case SIGNAL_TYPE_LVDS: + return ATOM_ENCODER_MODE_LVDS; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_VIRTUAL: + if (enable_dp_audio) + return ATOM_ENCODER_MODE_DP_AUDIO; + else + return ATOM_ENCODER_MODE_DP; + case SIGNAL_TYPE_RGB: + return ATOM_ENCODER_MODE_CRT; + default: + return ATOM_ENCODER_MODE_CRT; + } +} + +void dal_cmd_table_helper_assign_control_parameter( + const struct command_table_helper *h, + struct bp_encoder_control *control, + DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param) +{ + /* there are three transmitter blocks, each one has two links 4-lanes + * each, A+B, C+D, E+F, Uniphy A, C and E are enumerated as link 0 in + * each transmitter block B, D and F as link 1, third transmitter block + * has non splitable links (UniphyE and UniphyF can not be configured + * separately to drive two different streams) + */ + if ((control->transmitter == TRANSMITTER_UNIPHY_B) || + (control->transmitter == TRANSMITTER_UNIPHY_D) || + (control->transmitter == TRANSMITTER_UNIPHY_F)) { + /* Bit2: Link Select + * =0: PHY linkA/C/E + * =1: PHY linkB/D/F + */ + ctrl_param->acConfig.ucLinkSel = 1; + } + + /* Bit[4:3]: Transmitter Selection + * =00: Digital Transmitter1 ( UNIPHY linkAB ) + * =01: Digital Transmitter2 ( UNIPHY linkCD ) + * =02: Digital Transmitter3 ( UNIPHY linkEF ) + * =03: Reserved + */ + ctrl_param->acConfig.ucTransmitterSel = + (uint8_t)(h->transmitter_bp_to_atom(control->transmitter)); + + /* We need to convert from KHz units into 10KHz units */ + ctrl_param->ucAction = h->encoder_action_to_atom(control->action); + ctrl_param->usPixelClock = cpu_to_le16((uint16_t)(control->pixel_clock / 10)); + ctrl_param->ucEncoderMode = + (uint8_t)(h->encoder_mode_bp_to_atom( + control->signal, control->enable_dp_audio)); + ctrl_param->ucLaneNum = (uint8_t)(control->lanes_number); +} + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src( + enum clock_source_id id, + uint32_t *ref_clk_src_id) +{ + if (ref_clk_src_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CLOCK_SOURCE_ID_PLL1: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P1PLL; + return true; + case CLOCK_SOURCE_ID_PLL2: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P2PLL; + return true; + case CLOCK_SOURCE_ID_DCPLL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_DCPLL; + return true; + case CLOCK_SOURCE_ID_EXTERNAL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_EXTCLK; + return true; + case CLOCK_SOURCE_ID_UNDEFINED: + *ref_clk_src_id = ENCODER_REFCLK_SRC_INVALID; + return true; + default: + /* Unsupported clock source id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +uint8_t dal_cmd_table_helper_encoder_id_to_atom( + enum encoder_id id) +{ + switch (id) { + case ENCODER_ID_INTERNAL_LVDS: + return ENCODER_OBJECT_ID_INTERNAL_LVDS; + case ENCODER_ID_INTERNAL_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_TMDS1; + case ENCODER_ID_INTERNAL_TMDS2: + return ENCODER_OBJECT_ID_INTERNAL_TMDS2; + case ENCODER_ID_INTERNAL_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_DAC1; + case ENCODER_ID_INTERNAL_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_DAC2; + case ENCODER_ID_INTERNAL_LVTM1: + return ENCODER_OBJECT_ID_INTERNAL_LVTM1; + case ENCODER_ID_INTERNAL_HDMI: + return ENCODER_OBJECT_ID_HDMI_INTERNAL; + case ENCODER_ID_EXTERNAL_TRAVIS: + return ENCODER_OBJECT_ID_TRAVIS; + case ENCODER_ID_EXTERNAL_NUTMEG: + return ENCODER_OBJECT_ID_NUTMEG; + case ENCODER_ID_INTERNAL_KLDSCP_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2; + case ENCODER_ID_EXTERNAL_MVPU_FPGA: + return ENCODER_OBJECT_ID_MVPU_FPGA; + case ENCODER_ID_INTERNAL_DDI: + return ENCODER_OBJECT_ID_INTERNAL_DDI; + case ENCODER_ID_INTERNAL_UNIPHY: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY; + case ENCODER_ID_INTERNAL_KLDSCP_LVTMA: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA; + case ENCODER_ID_INTERNAL_UNIPHY1: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY1; + case ENCODER_ID_INTERNAL_UNIPHY2: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY2; + case ENCODER_ID_INTERNAL_UNIPHY3: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY3; + case ENCODER_ID_INTERNAL_WIRELESS: + return ENCODER_OBJECT_ID_INTERNAL_VCE; + case ENCODER_ID_UNKNOWN: + return ENCODER_OBJECT_ID_NONE; + default: + /* Invalid encoder id */ + BREAK_TO_DEBUGGER(); + return ENCODER_OBJECT_ID_NONE; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.h b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.h new file mode 100644 index 000000000000..1fab634b66be --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.h @@ -0,0 +1,90 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_H__ +#define __DAL_COMMAND_TABLE_HELPER_H__ + +#include "dce80/command_table_helper_dce80.h" +#include "dce110/command_table_helper_dce110.h" +#include "dce112/command_table_helper_dce112.h" + +struct command_table_helper { + bool (*controller_id_to_atom)(enum controller_id id, uint8_t *atom_id); + uint8_t (*encoder_action_to_atom)( + enum bp_encoder_control_action action); + uint32_t (*encoder_mode_bp_to_atom)(enum signal_type s, + bool enable_dp_audio); + bool (*engine_bp_to_atom)(enum engine_id engine_id, + uint32_t *atom_engine_id); + void (*assign_control_parameter)( + const struct command_table_helper *h, + struct bp_encoder_control *control, + DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param); + bool (*clock_source_id_to_atom)(enum clock_source_id id, + uint32_t *atom_pll_id); + bool (*clock_source_id_to_ref_clk_src)( + enum clock_source_id id, + uint32_t *ref_clk_src_id); + uint8_t (*transmitter_bp_to_atom)(enum transmitter t); + uint8_t (*encoder_id_to_atom)(enum encoder_id id); + uint8_t (*clock_source_id_to_atom_phy_clk_src_id)( + enum clock_source_id id); + uint8_t (*signal_type_to_atom_dig_mode)(enum signal_type s); + uint8_t (*hpd_sel_to_atom)(enum hpd_source_id id); + uint8_t (*dig_encoder_sel_to_atom)(enum engine_id engine_id); + uint8_t (*phy_id_to_atom)(enum transmitter t); + uint8_t (*disp_power_gating_action_to_atom)( + enum bp_pipe_control_action action); + bool (*dc_clock_type_to_atom)(enum bp_dce_clock_type id, + uint32_t *atom_clock_type); + uint8_t (*transmitter_color_depth_to_atom)(enum transmitter_color_depth id); +}; + +bool dal_bios_parser_init_cmd_tbl_helper(const struct command_table_helper **h, + enum dce_version dce); + +bool dal_cmd_table_helper_controller_id_to_atom( + enum controller_id id, + uint8_t *atom_id); + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom( + enum signal_type s, + bool enable_dp_audio); + +void dal_cmd_table_helper_assign_control_parameter( + const struct command_table_helper *h, + struct bp_encoder_control *control, +DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param); + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src( + enum clock_source_id id, + uint32_t *ref_clk_src_id); + +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom( + enum transmitter t); + +uint8_t dal_cmd_table_helper_encoder_id_to_atom( + enum encoder_id id); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c new file mode 100644 index 000000000000..9a4d30dd4969 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c @@ -0,0 +1,265 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "ObjectID.h" +#include "atomfirmware.h" + +#include "include/bios_parser_types.h" + +#include "command_table_helper2.h" + +bool dal_bios_parser_init_cmd_tbl_helper2( + const struct command_table_helper **h, + enum dce_version dce) +{ + switch (dce) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + *h = dal_cmd_tbl_helper_dce80_get_table(); + return true; + + case DCE_VERSION_10_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_2: + *h = dal_cmd_tbl_helper_dce112_get_table2(); + return true; +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case DCN_VERSION_1_0: + *h = dal_cmd_tbl_helper_dce112_get_table2(); + return true; +#endif + + case DCE_VERSION_12_0: + *h = dal_cmd_tbl_helper_dce112_get_table2(); + return true; + + default: + /* Unsupported DCE */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/* real implementations */ + +bool dal_cmd_table_helper_controller_id_to_atom2( + enum controller_id id, + uint8_t *atom_id) +{ + if (atom_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CONTROLLER_ID_D0: + *atom_id = ATOM_CRTC1; + return true; + case CONTROLLER_ID_D1: + *atom_id = ATOM_CRTC2; + return true; + case CONTROLLER_ID_D2: + *atom_id = ATOM_CRTC3; + return true; + case CONTROLLER_ID_D3: + *atom_id = ATOM_CRTC4; + return true; + case CONTROLLER_ID_D4: + *atom_id = ATOM_CRTC5; + return true; + case CONTROLLER_ID_D5: + *atom_id = ATOM_CRTC6; + return true; + /* TODO :case CONTROLLER_ID_UNDERLAY0: + *atom_id = ATOM_UNDERLAY_PIPE0; + return true; + */ + case CONTROLLER_ID_UNDEFINED: + *atom_id = ATOM_CRTC_INVALID; + return true; + default: + /* Wrong controller id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/** +* translate_transmitter_bp_to_atom +* +* @brief +* Translate the Transmitter to the corresponding ATOM BIOS value +* +* @param +* input transmitter +* output digitalTransmitter +* // =00: Digital Transmitter1 ( UNIPHY linkAB ) +* // =01: Digital Transmitter2 ( UNIPHY linkCD ) +* // =02: Digital Transmitter3 ( UNIPHY linkEF ) +*/ +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom2( + enum transmitter t) +{ + switch (t) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_TRAVIS_LCD: + return 0; + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + return 1; + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + return 2; + default: + /* Invalid Transmitter Type! */ + BREAK_TO_DEBUGGER(); + return 0; + } +} + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom2( + enum signal_type s, + bool enable_dp_audio) +{ + switch (s) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + return ATOM_ENCODER_MODE_DVI; + case SIGNAL_TYPE_HDMI_TYPE_A: + return ATOM_ENCODER_MODE_HDMI; + case SIGNAL_TYPE_LVDS: + return ATOM_ENCODER_MODE_LVDS; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_VIRTUAL: + if (enable_dp_audio) + return ATOM_ENCODER_MODE_DP_AUDIO; + else + return ATOM_ENCODER_MODE_DP; + case SIGNAL_TYPE_RGB: + return ATOM_ENCODER_MODE_CRT; + default: + return ATOM_ENCODER_MODE_CRT; + } +} + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src2( + enum clock_source_id id, + uint32_t *ref_clk_src_id) +{ + if (ref_clk_src_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CLOCK_SOURCE_ID_PLL1: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P1PLL; + return true; + case CLOCK_SOURCE_ID_PLL2: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P2PLL; + return true; + /*TODO:case CLOCK_SOURCE_ID_DCPLL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_DCPLL; + return true; + */ + case CLOCK_SOURCE_ID_EXTERNAL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_EXTCLK; + return true; + case CLOCK_SOURCE_ID_UNDEFINED: + *ref_clk_src_id = ENCODER_REFCLK_SRC_INVALID; + return true; + default: + /* Unsupported clock source id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +uint8_t dal_cmd_table_helper_encoder_id_to_atom2( + enum encoder_id id) +{ + switch (id) { + case ENCODER_ID_INTERNAL_LVDS: + return ENCODER_OBJECT_ID_INTERNAL_LVDS; + case ENCODER_ID_INTERNAL_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_TMDS1; + case ENCODER_ID_INTERNAL_TMDS2: + return ENCODER_OBJECT_ID_INTERNAL_TMDS2; + case ENCODER_ID_INTERNAL_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_DAC1; + case ENCODER_ID_INTERNAL_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_DAC2; + case ENCODER_ID_INTERNAL_LVTM1: + return ENCODER_OBJECT_ID_INTERNAL_LVTM1; + case ENCODER_ID_INTERNAL_HDMI: + return ENCODER_OBJECT_ID_HDMI_INTERNAL; + case ENCODER_ID_EXTERNAL_TRAVIS: + return ENCODER_OBJECT_ID_TRAVIS; + case ENCODER_ID_EXTERNAL_NUTMEG: + return ENCODER_OBJECT_ID_NUTMEG; + case ENCODER_ID_INTERNAL_KLDSCP_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2; + case ENCODER_ID_EXTERNAL_MVPU_FPGA: + return ENCODER_OBJECT_ID_MVPU_FPGA; + case ENCODER_ID_INTERNAL_DDI: + return ENCODER_OBJECT_ID_INTERNAL_DDI; + case ENCODER_ID_INTERNAL_UNIPHY: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY; + case ENCODER_ID_INTERNAL_KLDSCP_LVTMA: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA; + case ENCODER_ID_INTERNAL_UNIPHY1: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY1; + case ENCODER_ID_INTERNAL_UNIPHY2: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY2; + case ENCODER_ID_INTERNAL_UNIPHY3: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY3; + case ENCODER_ID_INTERNAL_WIRELESS: + return ENCODER_OBJECT_ID_INTERNAL_VCE; + case ENCODER_ID_INTERNAL_VIRTUAL: + return ENCODER_OBJECT_ID_NONE; + case ENCODER_ID_UNKNOWN: + return ENCODER_OBJECT_ID_NONE; + default: + /* Invalid encoder id */ + BREAK_TO_DEBUGGER(); + return ENCODER_OBJECT_ID_NONE; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.h b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.h new file mode 100644 index 000000000000..9f587c91d843 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.h @@ -0,0 +1,82 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER2_H__ +#define __DAL_COMMAND_TABLE_HELPER2_H__ + +#include "dce80/command_table_helper_dce80.h" +#include "dce110/command_table_helper_dce110.h" +#include "dce112/command_table_helper2_dce112.h" + +struct command_table_helper { + bool (*controller_id_to_atom)(enum controller_id id, uint8_t *atom_id); + uint8_t (*encoder_action_to_atom)( + enum bp_encoder_control_action action); + uint32_t (*encoder_mode_bp_to_atom)(enum signal_type s, + bool enable_dp_audio); + bool (*engine_bp_to_atom)(enum engine_id engine_id, + uint32_t *atom_engine_id); + bool (*clock_source_id_to_atom)(enum clock_source_id id, + uint32_t *atom_pll_id); + bool (*clock_source_id_to_ref_clk_src)( + enum clock_source_id id, + uint32_t *ref_clk_src_id); + uint8_t (*transmitter_bp_to_atom)(enum transmitter t); + uint8_t (*encoder_id_to_atom)(enum encoder_id id); + uint8_t (*clock_source_id_to_atom_phy_clk_src_id)( + enum clock_source_id id); + uint8_t (*signal_type_to_atom_dig_mode)(enum signal_type s); + uint8_t (*hpd_sel_to_atom)(enum hpd_source_id id); + uint8_t (*dig_encoder_sel_to_atom)(enum engine_id engine_id); + uint8_t (*phy_id_to_atom)(enum transmitter t); + uint8_t (*disp_power_gating_action_to_atom)( + enum bp_pipe_control_action action); + bool (*dc_clock_type_to_atom)(enum bp_dce_clock_type id, + uint32_t *atom_clock_type); + uint8_t (*transmitter_color_depth_to_atom)( + enum transmitter_color_depth id); +}; + +bool dal_bios_parser_init_cmd_tbl_helper2(const struct command_table_helper **h, + enum dce_version dce); + +bool dal_cmd_table_helper_controller_id_to_atom2( + enum controller_id id, + uint8_t *atom_id); + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom2( + enum signal_type s, + bool enable_dp_audio); + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src2( + enum clock_source_id id, + uint32_t *ref_clk_src_id); + +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom2( + enum transmitter t); + +uint8_t dal_cmd_table_helper_encoder_id_to_atom2( + enum encoder_id id); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c new file mode 100644 index 000000000000..ca24154468c7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c @@ -0,0 +1,364 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "../command_table_helper.h" + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + break; + case SIGNAL_TYPE_LVDS: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_LVDS; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel >> 4; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + uint8_t atom_dig_encoder_sel = 0; + + switch (id) { + case ENGINE_ID_DIGA: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + case ENGINE_ID_DIGB: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGB_SEL; + break; + case ENGINE_ID_DIGC: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGC_SEL; + break; + case ENGINE_ID_DIGD: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGD_SEL; + break; + case ENGINE_ID_DIGE: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGE_SEL; + break; + case ENGINE_ID_DIGF: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGF_SEL; + break; + case ENGINE_ID_DIGG: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGG_SEL; + break; + case ENGINE_ID_UNKNOWN: + /* No DIG_FRONT is associated to DIG_BACKEND */ + atom_dig_encoder_sel = 0; + break; + default: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + } + + return 0; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_PLL1: + *atom_pll_id = ATOM_PPLL1; + break; + case CLOCK_SOURCE_ID_PLL2: + *atom_pll_id = ATOM_PPLL2; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_EXT_PLL1; + break; + case CLOCK_SOURCE_ID_VCE: + /* for VCE encoding, + * we need to pass in ATOM_PPLL_INVALID + */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DP_DTO: + /* When programming DP DTO PLL ID should be invalid */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + /* Should not happen */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +/* function table */ +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .assign_control_parameter = NULL, + .clock_source_id_to_ref_clk_src = NULL, + .transmitter_bp_to_atom = NULL, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, + .encoder_mode_bp_to_atom = dal_cmd_table_helper_encoder_mode_bp_to_atom, +}; + +/* + * dal_cmd_tbl_helper_dce110_get_table + * + * @brief + * Initialize command table helper functions + * + * @param + * const struct command_table_helper **h - [out] struct of functions + * + */ +const struct command_table_helper *dal_cmd_tbl_helper_dce110_get_table(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.h b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.h new file mode 100644 index 000000000000..eb60c2ead992 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_DCE110_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE110_H__ + +struct command_table_helper; + +/* Initialize command table helper functions */ +const struct command_table_helper *dal_cmd_tbl_helper_dce110_get_table(void); + +#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c new file mode 100644 index 000000000000..0237ae575068 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c @@ -0,0 +1,418 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "../command_table_helper2.h" + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + uint8_t atom_dig_encoder_sel = 0; + + switch (id) { + case ENGINE_ID_DIGA: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGA_SEL; + break; + case ENGINE_ID_DIGB: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGB_SEL; + break; + case ENGINE_ID_DIGC: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGC_SEL; + break; + case ENGINE_ID_DIGD: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGD_SEL; + break; + case ENGINE_ID_DIGE: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGE_SEL; + break; + case ENGINE_ID_DIGF: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGF_SEL; + break; + case ENGINE_ID_DIGG: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGG_SEL; + break; + case ENGINE_ID_UNKNOWN: + /* No DIG_FRONT is associated to DIG_BACKEND */ + atom_dig_encoder_sel = 0; + break; + default: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGA_SEL; + break; + } + + return 0; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_COMBO_PHY_PLL0: + *atom_pll_id = ATOM_COMBOPHY_PLL0; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL1: + *atom_pll_id = ATOM_COMBOPHY_PLL1; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL2: + *atom_pll_id = ATOM_COMBOPHY_PLL2; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL3: + *atom_pll_id = ATOM_COMBOPHY_PLL3; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL4: + *atom_pll_id = ATOM_COMBOPHY_PLL4; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL5: + *atom_pll_id = ATOM_COMBOPHY_PLL5; + break; + case CLOCK_SOURCE_COMBO_DISPLAY_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_GCK_DFS; + break; + case CLOCK_SOURCE_ID_VCE: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_DP_DTO: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + /* Should not happen */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_STREAM_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +static bool dc_clock_type_to_atom( + enum bp_dce_clock_type id, + uint32_t *atom_clock_type) +{ + bool retCode = true; + + if (atom_clock_type != NULL) { + switch (id) { + case DCECLOCK_TYPE_DISPLAY_CLOCK: + *atom_clock_type = DCE_CLOCK_TYPE_DISPCLK; + break; + + case DCECLOCK_TYPE_DPREFCLK: + *atom_clock_type = DCE_CLOCK_TYPE_DPREFCLK; + break; + + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + } + + return retCode; +} + +static uint8_t transmitter_color_depth_to_atom(enum transmitter_color_depth id) +{ + uint8_t atomColorDepth = 0; + + switch (id) { + case TRANSMITTER_COLOR_DEPTH_24: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_DIS; + break; + case TRANSMITTER_COLOR_DEPTH_30: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_5_4; + break; + case TRANSMITTER_COLOR_DEPTH_36: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_3_2; + break; + case TRANSMITTER_COLOR_DEPTH_48: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_2_1; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atomColorDepth; +} + +/* function table */ +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom2, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .clock_source_id_to_ref_clk_src = NULL, + .transmitter_bp_to_atom = NULL, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom2, + .encoder_mode_bp_to_atom = + dal_cmd_table_helper_encoder_mode_bp_to_atom2, + .dc_clock_type_to_atom = dc_clock_type_to_atom, + .transmitter_color_depth_to_atom = transmitter_color_depth_to_atom, +}; + +/* + * dal_cmd_tbl_helper_dce110_get_table + * + * @brief + * Initialize command table helper functions + * + * @param + * const struct command_table_helper **h - [out] struct of functions + * + */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table2(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.h b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.h new file mode 100644 index 000000000000..abf28a06f5bc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER2_DCE112_H__ +#define __DAL_COMMAND_TABLE_HELPER2_DCE112_H__ + +struct command_table_helper; + +/* Initialize command table helper functions */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table2(void); + +#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c new file mode 100644 index 000000000000..452034f83e4c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c @@ -0,0 +1,418 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "../command_table_helper.h" + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + uint8_t atom_dig_encoder_sel = 0; + + switch (id) { + case ENGINE_ID_DIGA: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGA_SEL; + break; + case ENGINE_ID_DIGB: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGB_SEL; + break; + case ENGINE_ID_DIGC: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGC_SEL; + break; + case ENGINE_ID_DIGD: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGD_SEL; + break; + case ENGINE_ID_DIGE: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGE_SEL; + break; + case ENGINE_ID_DIGF: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGF_SEL; + break; + case ENGINE_ID_DIGG: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGG_SEL; + break; + case ENGINE_ID_UNKNOWN: + /* No DIG_FRONT is associated to DIG_BACKEND */ + atom_dig_encoder_sel = 0; + break; + default: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V6__DIGA_SEL; + break; + } + + return 0; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_COMBO_PHY_PLL0: + *atom_pll_id = ATOM_COMBOPHY_PLL0; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL1: + *atom_pll_id = ATOM_COMBOPHY_PLL1; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL2: + *atom_pll_id = ATOM_COMBOPHY_PLL2; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL3: + *atom_pll_id = ATOM_COMBOPHY_PLL3; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL4: + *atom_pll_id = ATOM_COMBOPHY_PLL4; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL5: + *atom_pll_id = ATOM_COMBOPHY_PLL5; + break; + case CLOCK_SOURCE_COMBO_DISPLAY_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_GCK_DFS; + break; + case CLOCK_SOURCE_ID_VCE: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_DP_DTO: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + /* Should not happen */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_STREAM_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +static bool dc_clock_type_to_atom( + enum bp_dce_clock_type id, + uint32_t *atom_clock_type) +{ + bool retCode = true; + + if (atom_clock_type != NULL) { + switch (id) { + case DCECLOCK_TYPE_DISPLAY_CLOCK: + *atom_clock_type = DCE_CLOCK_TYPE_DISPCLK; + break; + + case DCECLOCK_TYPE_DPREFCLK: + *atom_clock_type = DCE_CLOCK_TYPE_DPREFCLK; + break; + + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + } + + return retCode; +} + +static uint8_t transmitter_color_depth_to_atom(enum transmitter_color_depth id) +{ + uint8_t atomColorDepth = 0; + + switch (id) { + case TRANSMITTER_COLOR_DEPTH_24: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_DIS; + break; + case TRANSMITTER_COLOR_DEPTH_30: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_5_4; + break; + case TRANSMITTER_COLOR_DEPTH_36: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_3_2; + break; + case TRANSMITTER_COLOR_DEPTH_48: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_2_1; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atomColorDepth; +} + +/* function table */ +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .assign_control_parameter = NULL, + .clock_source_id_to_ref_clk_src = NULL, + .transmitter_bp_to_atom = NULL, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, + .encoder_mode_bp_to_atom = dal_cmd_table_helper_encoder_mode_bp_to_atom, + .dc_clock_type_to_atom = dc_clock_type_to_atom, + .transmitter_color_depth_to_atom = transmitter_color_depth_to_atom, +}; + +/* + * dal_cmd_tbl_helper_dce110_get_table + * + * @brief + * Initialize command table helper functions + * + * @param + * const struct command_table_helper **h - [out] struct of functions + * + */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.h b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.h new file mode 100644 index 000000000000..dc3660951355 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_DCE112_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE112_H__ + +struct command_table_helper; + +/* Initialize command table helper functions */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table(void); + +#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c new file mode 100644 index 000000000000..8b30b558cf1f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c @@ -0,0 +1,354 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/grph_object_id.h" +#include "include/grph_object_defs.h" +#include "include/bios_parser_types.h" + +#include "../command_table_helper.h" + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_PLL1: + *atom_pll_id = ATOM_PPLL1; + break; + case CLOCK_SOURCE_ID_PLL2: + *atom_pll_id = ATOM_PPLL2; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_EXT_PLL1; + break; + case CLOCK_SOURCE_ID_VCE: + /* for VCE encoding, + * we need to pass in ATOM_PPLL_INVALID + */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DP_DTO: + /* When programming DP DTO PLL ID should be invalid */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + BREAK_TO_DEBUGGER(); /* check when this will happen! */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + break; + case SIGNAL_TYPE_LVDS: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_LVDS; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel >> 4; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + uint8_t atom_dig_encoder_sel = 0; + + switch (id) { + case ENGINE_ID_DIGA: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + case ENGINE_ID_DIGB: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGB_SEL; + break; + case ENGINE_ID_DIGC: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGC_SEL; + break; + case ENGINE_ID_DIGD: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGD_SEL; + break; + case ENGINE_ID_DIGE: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGE_SEL; + break; + case ENGINE_ID_DIGF: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGF_SEL; + break; + case ENGINE_ID_DIGG: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGG_SEL; + break; + default: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + } + + return atom_dig_encoder_sel; +} + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .assign_control_parameter = + dal_cmd_table_helper_assign_control_parameter, + .clock_source_id_to_ref_clk_src = + dal_cmd_table_helper_clock_source_id_to_ref_clk_src, + .transmitter_bp_to_atom = dal_cmd_table_helper_transmitter_bp_to_atom, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, + .encoder_mode_bp_to_atom = + dal_cmd_table_helper_encoder_mode_bp_to_atom, +}; + +const struct command_table_helper *dal_cmd_tbl_helper_dce80_get_table(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.h b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.h new file mode 100644 index 000000000000..e675c359e306 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_DCE80_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE80_H__ + +struct command_table_helper; + +const struct command_table_helper *dal_cmd_tbl_helper_dce80_get_table(void); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/calcs/Makefile b/drivers/gpu/drm/amd/display/dc/calcs/Makefile new file mode 100644 index 000000000000..41ef35995b02 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the 'calcs' sub-component of DAL. +# It calculates Bandwidth and Watermarks values for HW programming +# + +CFLAGS_dcn_calcs.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_dcn_calc_auto.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_dcn_calc_math.o := -mhard-float -msse -mpreferred-stack-boundary=4 -Wno-tautological-compare + +BW_CALCS = dce_calcs.o bw_fixed.o custom_float.o + +ifdef CONFIG_DRM_AMD_DC_DCN1_0 +BW_CALCS += dcn_calcs.o dcn_calc_math.o dcn_calc_auto.o +endif + +AMD_DAL_BW_CALCS = $(addprefix $(AMDDALPATH)/dc/calcs/,$(BW_CALCS)) + +AMD_DISPLAY_FILES += $(AMD_DAL_BW_CALCS) diff --git a/drivers/gpu/drm/amd/display/dc/calcs/bw_fixed.c b/drivers/gpu/drm/amd/display/dc/calcs/bw_fixed.c new file mode 100644 index 000000000000..6ca288fb5fb9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/bw_fixed.c @@ -0,0 +1,191 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" +#include "bw_fixed.h" + + +#define MIN_I64 \ + (int64_t)(-(1LL << 63)) + +#define MAX_I64 \ + (int64_t)((1ULL << 63) - 1) + +#define FRACTIONAL_PART_MASK \ + ((1ULL << BW_FIXED_BITS_PER_FRACTIONAL_PART) - 1) + +#define GET_FRACTIONAL_PART(x) \ + (FRACTIONAL_PART_MASK & (x)) + +static uint64_t abs_i64(int64_t arg) +{ + if (arg >= 0) + return (uint64_t)(arg); + else + return (uint64_t)(-arg); +} + +struct bw_fixed bw_int_to_fixed_nonconst(int64_t value) +{ + struct bw_fixed res; + ASSERT(value < BW_FIXED_MAX_I32 && value > BW_FIXED_MIN_I32); + res.value = value << BW_FIXED_BITS_PER_FRACTIONAL_PART; + return res; +} + +struct bw_fixed bw_frc_to_fixed(int64_t numerator, int64_t denominator) +{ + struct bw_fixed res; + bool arg1_negative = numerator < 0; + bool arg2_negative = denominator < 0; + uint64_t arg1_value; + uint64_t arg2_value; + uint64_t remainder; + + /* determine integer part */ + uint64_t res_value; + + ASSERT(denominator != 0); + + arg1_value = abs_i64(numerator); + arg2_value = abs_i64(denominator); + res_value = div64_u64_rem(arg1_value, arg2_value, &remainder); + + ASSERT(res_value <= BW_FIXED_MAX_I32); + + /* determine fractional part */ + { + uint32_t i = BW_FIXED_BITS_PER_FRACTIONAL_PART; + + do + { + remainder <<= 1; + + res_value <<= 1; + + if (remainder >= arg2_value) + { + res_value |= 1; + remainder -= arg2_value; + } + } while (--i != 0); + } + + /* round up LSB */ + { + uint64_t summand = (remainder << 1) >= arg2_value; + + ASSERT(res_value <= MAX_I64 - summand); + + res_value += summand; + } + + res.value = (int64_t)(res_value); + + if (arg1_negative ^ arg2_negative) + res.value = -res.value; + return res; +} + +struct bw_fixed bw_floor2( + const struct bw_fixed arg, + const struct bw_fixed significance) +{ + struct bw_fixed result; + int64_t multiplicand; + + multiplicand = div64_s64(arg.value, abs_i64(significance.value)); + result.value = abs_i64(significance.value) * multiplicand; + ASSERT(abs_i64(result.value) <= abs_i64(arg.value)); + return result; +} + +struct bw_fixed bw_ceil2( + const struct bw_fixed arg, + const struct bw_fixed significance) +{ + struct bw_fixed result; + int64_t multiplicand; + + multiplicand = div64_s64(arg.value, abs_i64(significance.value)); + result.value = abs_i64(significance.value) * multiplicand; + if (abs_i64(result.value) < abs_i64(arg.value)) { + if (arg.value < 0) + result.value -= abs_i64(significance.value); + else + result.value += abs_i64(significance.value); + } + return result; +} + +struct bw_fixed bw_mul(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + struct bw_fixed res; + + bool arg1_negative = arg1.value < 0; + bool arg2_negative = arg2.value < 0; + + uint64_t arg1_value = abs_i64(arg1.value); + uint64_t arg2_value = abs_i64(arg2.value); + + uint64_t arg1_int = BW_FIXED_GET_INTEGER_PART(arg1_value); + uint64_t arg2_int = BW_FIXED_GET_INTEGER_PART(arg2_value); + + uint64_t arg1_fra = GET_FRACTIONAL_PART(arg1_value); + uint64_t arg2_fra = GET_FRACTIONAL_PART(arg2_value); + + uint64_t tmp; + + res.value = arg1_int * arg2_int; + + ASSERT(res.value <= BW_FIXED_MAX_I32); + + res.value <<= BW_FIXED_BITS_PER_FRACTIONAL_PART; + + tmp = arg1_int * arg2_fra; + + ASSERT(tmp <= (uint64_t)(MAX_I64 - res.value)); + + res.value += tmp; + + tmp = arg2_int * arg1_fra; + + ASSERT(tmp <= (uint64_t)(MAX_I64 - res.value)); + + res.value += tmp; + + tmp = arg1_fra * arg2_fra; + + tmp = (tmp >> BW_FIXED_BITS_PER_FRACTIONAL_PART) + + (tmp >= (uint64_t)(bw_frc_to_fixed(1, 2).value)); + + ASSERT(tmp <= (uint64_t)(MAX_I64 - res.value)); + + res.value += tmp; + + if (arg1_negative ^ arg2_negative) + res.value = -res.value; + return res; +} + diff --git a/drivers/gpu/drm/amd/display/dc/calcs/custom_float.c b/drivers/gpu/drm/amd/display/dc/calcs/custom_float.c new file mode 100644 index 000000000000..7243c37f569e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/custom_float.c @@ -0,0 +1,197 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" +#include "custom_float.h" + + +static bool build_custom_float( + struct fixed31_32 value, + const struct custom_float_format *format, + bool *negative, + uint32_t *mantissa, + uint32_t *exponenta) +{ + uint32_t exp_offset = (1 << (format->exponenta_bits - 1)) - 1; + + const struct fixed31_32 mantissa_constant_plus_max_fraction = + dal_fixed31_32_from_fraction( + (1LL << (format->mantissa_bits + 1)) - 1, + 1LL << format->mantissa_bits); + + struct fixed31_32 mantiss; + + if (dal_fixed31_32_eq( + value, + dal_fixed31_32_zero)) { + *negative = false; + *mantissa = 0; + *exponenta = 0; + return true; + } + + if (dal_fixed31_32_lt( + value, + dal_fixed31_32_zero)) { + *negative = format->sign; + value = dal_fixed31_32_neg(value); + } else { + *negative = false; + } + + if (dal_fixed31_32_lt( + value, + dal_fixed31_32_one)) { + uint32_t i = 1; + + do { + value = dal_fixed31_32_shl(value, 1); + ++i; + } while (dal_fixed31_32_lt( + value, + dal_fixed31_32_one)); + + --i; + + if (exp_offset <= i) { + *mantissa = 0; + *exponenta = 0; + return true; + } + + *exponenta = exp_offset - i; + } else if (dal_fixed31_32_le( + mantissa_constant_plus_max_fraction, + value)) { + uint32_t i = 1; + + do { + value = dal_fixed31_32_shr(value, 1); + ++i; + } while (dal_fixed31_32_lt( + mantissa_constant_plus_max_fraction, + value)); + + *exponenta = exp_offset + i - 1; + } else { + *exponenta = exp_offset; + } + + mantiss = dal_fixed31_32_sub( + value, + dal_fixed31_32_one); + + if (dal_fixed31_32_lt( + mantiss, + dal_fixed31_32_zero) || + dal_fixed31_32_lt( + dal_fixed31_32_one, + mantiss)) + mantiss = dal_fixed31_32_zero; + else + mantiss = dal_fixed31_32_shl( + mantiss, + format->mantissa_bits); + + *mantissa = dal_fixed31_32_floor(mantiss); + + return true; +} + +static bool setup_custom_float( + const struct custom_float_format *format, + bool negative, + uint32_t mantissa, + uint32_t exponenta, + uint32_t *result) +{ + uint32_t i = 0; + uint32_t j = 0; + + uint32_t value = 0; + + /* verification code: + * once calculation is ok we can remove it + */ + + const uint32_t mantissa_mask = + (1 << (format->mantissa_bits + 1)) - 1; + + const uint32_t exponenta_mask = + (1 << (format->exponenta_bits + 1)) - 1; + + if (mantissa & ~mantissa_mask) { + BREAK_TO_DEBUGGER(); + mantissa = mantissa_mask; + } + + if (exponenta & ~exponenta_mask) { + BREAK_TO_DEBUGGER(); + exponenta = exponenta_mask; + } + + /* end of verification code */ + + while (i < format->mantissa_bits) { + uint32_t mask = 1 << i; + + if (mantissa & mask) + value |= mask; + + ++i; + } + + while (j < format->exponenta_bits) { + uint32_t mask = 1 << j; + + if (exponenta & mask) + value |= mask << i; + + ++j; + } + + if (negative && format->sign) + value |= 1 << (i + j); + + *result = value; + + return true; +} + +bool convert_to_custom_float_format( + struct fixed31_32 value, + const struct custom_float_format *format, + uint32_t *result) +{ + uint32_t mantissa; + uint32_t exponenta; + bool negative; + + return build_custom_float( + value, format, &negative, &mantissa, &exponenta) && + setup_custom_float( + format, negative, mantissa, exponenta, result); +} + + diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dce_calcs.c b/drivers/gpu/drm/amd/display/dc/calcs/dce_calcs.c new file mode 100644 index 000000000000..6347712db834 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/dce_calcs.c @@ -0,0 +1,3257 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dce_calcs.h" +#include "dc.h" +#include "core_types.h" +#include "dal_asic_id.h" + +/******************************************************************************* + * Private Functions + ******************************************************************************/ + +static enum bw_calcs_version bw_calcs_version_from_asic_id(struct hw_asic_id asic_id) +{ + switch (asic_id.chip_family) { + + case FAMILY_CZ: + if (ASIC_REV_IS_STONEY(asic_id.hw_internal_rev)) + return BW_CALCS_VERSION_STONEY; + return BW_CALCS_VERSION_CARRIZO; + + case FAMILY_VI: + if (ASIC_REV_IS_POLARIS10_P(asic_id.hw_internal_rev)) + return BW_CALCS_VERSION_POLARIS10; + if (ASIC_REV_IS_POLARIS11_M(asic_id.hw_internal_rev) || + ASIC_REV_IS_POLARIS12_V(asic_id.hw_internal_rev)) + return BW_CALCS_VERSION_POLARIS11; + return BW_CALCS_VERSION_INVALID; + + case FAMILY_AI: + return BW_CALCS_VERSION_VEGA10; + + default: + return BW_CALCS_VERSION_INVALID; + } +} + +static void calculate_bandwidth( + const struct bw_calcs_dceip *dceip, + const struct bw_calcs_vbios *vbios, + struct bw_calcs_data *data) + +{ + const int32_t pixels_per_chunk = 512; + const int32_t high = 2; + const int32_t mid = 1; + const int32_t low = 0; + const uint32_t s_low = 0; + const uint32_t s_mid1 = 1; + const uint32_t s_mid2 = 2; + const uint32_t s_mid3 = 3; + const uint32_t s_mid4 = 4; + const uint32_t s_mid5 = 5; + const uint32_t s_mid6 = 6; + const uint32_t s_high = 7; + const uint32_t bus_efficiency = 1; + const uint32_t dmif_chunk_buff_margin = 1; + + uint32_t max_chunks_fbc_mode; + int32_t num_cursor_lines; + + int32_t i, j, k; + struct bw_fixed yclk[3]; + struct bw_fixed sclk[8]; + bool d0_underlay_enable; + bool d1_underlay_enable; + bool fbc_enabled; + bool lpt_enabled; + enum bw_defines sclk_message; + enum bw_defines yclk_message; + enum bw_defines v_filter_init_mode[maximum_number_of_surfaces]; + enum bw_defines tiling_mode[maximum_number_of_surfaces]; + enum bw_defines surface_type[maximum_number_of_surfaces]; + enum bw_defines voltage; + enum bw_defines pipe_check; + enum bw_defines hsr_check; + enum bw_defines vsr_check; + enum bw_defines lb_size_check; + enum bw_defines fbc_check; + enum bw_defines rotation_check; + enum bw_defines mode_check; + enum bw_defines nbp_state_change_enable_blank; + /*initialize variables*/ + int32_t number_of_displays_enabled = 0; + int32_t number_of_displays_enabled_with_margin = 0; + int32_t number_of_aligned_displays_with_no_margin = 0; + + yclk[low] = vbios->low_yclk; + yclk[mid] = vbios->mid_yclk; + yclk[high] = vbios->high_yclk; + sclk[s_low] = vbios->low_sclk; + sclk[s_mid1] = vbios->mid1_sclk; + sclk[s_mid2] = vbios->mid2_sclk; + sclk[s_mid3] = vbios->mid3_sclk; + sclk[s_mid4] = vbios->mid4_sclk; + sclk[s_mid5] = vbios->mid5_sclk; + sclk[s_mid6] = vbios->mid6_sclk; + sclk[s_high] = vbios->high_sclk; + /*''''''''''''''''''*/ + /* surface assignment:*/ + /* 0: d0 underlay or underlay luma*/ + /* 1: d0 underlay chroma*/ + /* 2: d1 underlay or underlay luma*/ + /* 3: d1 underlay chroma*/ + /* 4: d0 graphics*/ + /* 5: d1 graphics*/ + /* 6: d2 graphics*/ + /* 7: d3 graphics, same mode as d2*/ + /* 8: d4 graphics, same mode as d2*/ + /* 9: d5 graphics, same mode as d2*/ + /* ...*/ + /* maximum_number_of_surfaces-2: d1 display_write_back420 luma*/ + /* maximum_number_of_surfaces-1: d1 display_write_back420 chroma*/ + /* underlay luma and chroma surface parameters from spreadsheet*/ + + + + + if (data->d0_underlay_mode == bw_def_none) { d0_underlay_enable = 0; } + else { + d0_underlay_enable = 1; + } + if (data->d1_underlay_mode == bw_def_none) { d1_underlay_enable = 0; } + else { + d1_underlay_enable = 1; + } + data->number_of_underlay_surfaces = d0_underlay_enable + d1_underlay_enable; + switch (data->underlay_surface_type) { + case bw_def_420: + surface_type[0] = bw_def_underlay420_luma; + surface_type[2] = bw_def_underlay420_luma; + data->bytes_per_pixel[0] = 1; + data->bytes_per_pixel[2] = 1; + surface_type[1] = bw_def_underlay420_chroma; + surface_type[3] = bw_def_underlay420_chroma; + data->bytes_per_pixel[1] = 2; + data->bytes_per_pixel[3] = 2; + data->lb_size_per_component[0] = dceip->underlay420_luma_lb_size_per_component; + data->lb_size_per_component[1] = dceip->underlay420_chroma_lb_size_per_component; + data->lb_size_per_component[2] = dceip->underlay420_luma_lb_size_per_component; + data->lb_size_per_component[3] = dceip->underlay420_chroma_lb_size_per_component; + break; + case bw_def_422: + surface_type[0] = bw_def_underlay422; + surface_type[2] = bw_def_underlay422; + data->bytes_per_pixel[0] = 2; + data->bytes_per_pixel[2] = 2; + data->lb_size_per_component[0] = dceip->underlay422_lb_size_per_component; + data->lb_size_per_component[2] = dceip->underlay422_lb_size_per_component; + break; + default: + surface_type[0] = bw_def_underlay444; + surface_type[2] = bw_def_underlay444; + data->bytes_per_pixel[0] = 4; + data->bytes_per_pixel[2] = 4; + data->lb_size_per_component[0] = dceip->lb_size_per_component444; + data->lb_size_per_component[2] = dceip->lb_size_per_component444; + break; + } + if (d0_underlay_enable) { + switch (data->underlay_surface_type) { + case bw_def_420: + data->enable[0] = 1; + data->enable[1] = 1; + break; + default: + data->enable[0] = 1; + data->enable[1] = 0; + break; + } + } + else { + data->enable[0] = 0; + data->enable[1] = 0; + } + if (d1_underlay_enable) { + switch (data->underlay_surface_type) { + case bw_def_420: + data->enable[2] = 1; + data->enable[3] = 1; + break; + default: + data->enable[2] = 1; + data->enable[3] = 0; + break; + } + } + else { + data->enable[2] = 0; + data->enable[3] = 0; + } + data->use_alpha[0] = 0; + data->use_alpha[1] = 0; + data->use_alpha[2] = 0; + data->use_alpha[3] = 0; + data->scatter_gather_enable_for_pipe[0] = vbios->scatter_gather_enable; + data->scatter_gather_enable_for_pipe[1] = vbios->scatter_gather_enable; + data->scatter_gather_enable_for_pipe[2] = vbios->scatter_gather_enable; + data->scatter_gather_enable_for_pipe[3] = vbios->scatter_gather_enable; + /*underlay0 same and graphics display pipe0*/ + data->interlace_mode[0] = data->interlace_mode[4]; + data->interlace_mode[1] = data->interlace_mode[4]; + /*underlay1 same and graphics display pipe1*/ + data->interlace_mode[2] = data->interlace_mode[5]; + data->interlace_mode[3] = data->interlace_mode[5]; + /*underlay0 same and graphics display pipe0*/ + data->h_total[0] = data->h_total[4]; + data->v_total[0] = data->v_total[4]; + data->h_total[1] = data->h_total[4]; + data->v_total[1] = data->v_total[4]; + /*underlay1 same and graphics display pipe1*/ + data->h_total[2] = data->h_total[5]; + data->v_total[2] = data->v_total[5]; + data->h_total[3] = data->h_total[5]; + data->v_total[3] = data->v_total[5]; + /*underlay0 same and graphics display pipe0*/ + data->pixel_rate[0] = data->pixel_rate[4]; + data->pixel_rate[1] = data->pixel_rate[4]; + /*underlay1 same and graphics display pipe1*/ + data->pixel_rate[2] = data->pixel_rate[5]; + data->pixel_rate[3] = data->pixel_rate[5]; + if ((data->underlay_tiling_mode == bw_def_array_linear_general || data->underlay_tiling_mode == bw_def_array_linear_aligned)) { + tiling_mode[0] = bw_def_linear; + tiling_mode[1] = bw_def_linear; + tiling_mode[2] = bw_def_linear; + tiling_mode[3] = bw_def_linear; + } + else { + tiling_mode[0] = bw_def_landscape; + tiling_mode[1] = bw_def_landscape; + tiling_mode[2] = bw_def_landscape; + tiling_mode[3] = bw_def_landscape; + } + data->lb_bpc[0] = data->underlay_lb_bpc; + data->lb_bpc[1] = data->underlay_lb_bpc; + data->lb_bpc[2] = data->underlay_lb_bpc; + data->lb_bpc[3] = data->underlay_lb_bpc; + data->compression_rate[0] = bw_int_to_fixed(1); + data->compression_rate[1] = bw_int_to_fixed(1); + data->compression_rate[2] = bw_int_to_fixed(1); + data->compression_rate[3] = bw_int_to_fixed(1); + data->access_one_channel_only[0] = 0; + data->access_one_channel_only[1] = 0; + data->access_one_channel_only[2] = 0; + data->access_one_channel_only[3] = 0; + data->cursor_width_pixels[0] = bw_int_to_fixed(0); + data->cursor_width_pixels[1] = bw_int_to_fixed(0); + data->cursor_width_pixels[2] = bw_int_to_fixed(0); + data->cursor_width_pixels[3] = bw_int_to_fixed(0); + /* graphics surface parameters from spreadsheet*/ + fbc_enabled = 0; + lpt_enabled = 0; + for (i = 4; i <= maximum_number_of_surfaces - 3; i++) { + if (i < data->number_of_displays + 4) { + if (i == 4 && data->d0_underlay_mode == bw_def_underlay_only) { + data->enable[i] = 0; + data->use_alpha[i] = 0; + } + else if (i == 4 && data->d0_underlay_mode == bw_def_blend) { + data->enable[i] = 1; + data->use_alpha[i] = 1; + } + else if (i == 4) { + data->enable[i] = 1; + data->use_alpha[i] = 0; + } + else if (i == 5 && data->d1_underlay_mode == bw_def_underlay_only) { + data->enable[i] = 0; + data->use_alpha[i] = 0; + } + else if (i == 5 && data->d1_underlay_mode == bw_def_blend) { + data->enable[i] = 1; + data->use_alpha[i] = 1; + } + else { + data->enable[i] = 1; + data->use_alpha[i] = 0; + } + } + else { + data->enable[i] = 0; + data->use_alpha[i] = 0; + } + data->scatter_gather_enable_for_pipe[i] = vbios->scatter_gather_enable; + surface_type[i] = bw_def_graphics; + data->lb_size_per_component[i] = dceip->lb_size_per_component444; + if (data->graphics_tiling_mode == bw_def_array_linear_general || data->graphics_tiling_mode == bw_def_array_linear_aligned) { + tiling_mode[i] = bw_def_linear; + } + else { + tiling_mode[i] = bw_def_tiled; + } + data->lb_bpc[i] = data->graphics_lb_bpc; + if ((data->fbc_en[i] == 1 && (dceip->argb_compression_support || data->d0_underlay_mode != bw_def_blended))) { + data->compression_rate[i] = bw_int_to_fixed(vbios->average_compression_rate); + data->access_one_channel_only[i] = data->lpt_en[i]; + } + else { + data->compression_rate[i] = bw_int_to_fixed(1); + data->access_one_channel_only[i] = 0; + } + if (data->fbc_en[i] == 1) { + fbc_enabled = 1; + if (data->lpt_en[i] == 1) { + lpt_enabled = 1; + } + } + data->cursor_width_pixels[i] = bw_int_to_fixed(vbios->cursor_width); + } + /* display_write_back420*/ + data->scatter_gather_enable_for_pipe[maximum_number_of_surfaces - 2] = 0; + data->scatter_gather_enable_for_pipe[maximum_number_of_surfaces - 1] = 0; + if (data->d1_display_write_back_dwb_enable == 1) { + data->enable[maximum_number_of_surfaces - 2] = 1; + data->enable[maximum_number_of_surfaces - 1] = 1; + } + else { + data->enable[maximum_number_of_surfaces - 2] = 0; + data->enable[maximum_number_of_surfaces - 1] = 0; + } + surface_type[maximum_number_of_surfaces - 2] = bw_def_display_write_back420_luma; + surface_type[maximum_number_of_surfaces - 1] = bw_def_display_write_back420_chroma; + data->lb_size_per_component[maximum_number_of_surfaces - 2] = dceip->underlay420_luma_lb_size_per_component; + data->lb_size_per_component[maximum_number_of_surfaces - 1] = dceip->underlay420_chroma_lb_size_per_component; + data->bytes_per_pixel[maximum_number_of_surfaces - 2] = 1; + data->bytes_per_pixel[maximum_number_of_surfaces - 1] = 2; + data->interlace_mode[maximum_number_of_surfaces - 2] = data->interlace_mode[5]; + data->interlace_mode[maximum_number_of_surfaces - 1] = data->interlace_mode[5]; + data->h_taps[maximum_number_of_surfaces - 2] = bw_int_to_fixed(1); + data->h_taps[maximum_number_of_surfaces - 1] = bw_int_to_fixed(1); + data->v_taps[maximum_number_of_surfaces - 2] = bw_int_to_fixed(1); + data->v_taps[maximum_number_of_surfaces - 1] = bw_int_to_fixed(1); + data->rotation_angle[maximum_number_of_surfaces - 2] = bw_int_to_fixed(0); + data->rotation_angle[maximum_number_of_surfaces - 1] = bw_int_to_fixed(0); + tiling_mode[maximum_number_of_surfaces - 2] = bw_def_linear; + tiling_mode[maximum_number_of_surfaces - 1] = bw_def_linear; + data->lb_bpc[maximum_number_of_surfaces - 2] = 8; + data->lb_bpc[maximum_number_of_surfaces - 1] = 8; + data->compression_rate[maximum_number_of_surfaces - 2] = bw_int_to_fixed(1); + data->compression_rate[maximum_number_of_surfaces - 1] = bw_int_to_fixed(1); + data->access_one_channel_only[maximum_number_of_surfaces - 2] = 0; + data->access_one_channel_only[maximum_number_of_surfaces - 1] = 0; + /*assume display pipe1 has dwb enabled*/ + data->h_total[maximum_number_of_surfaces - 2] = data->h_total[5]; + data->h_total[maximum_number_of_surfaces - 1] = data->h_total[5]; + data->v_total[maximum_number_of_surfaces - 2] = data->v_total[5]; + data->v_total[maximum_number_of_surfaces - 1] = data->v_total[5]; + data->pixel_rate[maximum_number_of_surfaces - 2] = data->pixel_rate[5]; + data->pixel_rate[maximum_number_of_surfaces - 1] = data->pixel_rate[5]; + data->src_width[maximum_number_of_surfaces - 2] = data->src_width[5]; + data->src_width[maximum_number_of_surfaces - 1] = data->src_width[5]; + data->src_height[maximum_number_of_surfaces - 2] = data->src_height[5]; + data->src_height[maximum_number_of_surfaces - 1] = data->src_height[5]; + data->pitch_in_pixels[maximum_number_of_surfaces - 2] = data->src_width[5]; + data->pitch_in_pixels[maximum_number_of_surfaces - 1] = data->src_width[5]; + data->h_scale_ratio[maximum_number_of_surfaces - 2] = bw_int_to_fixed(1); + data->h_scale_ratio[maximum_number_of_surfaces - 1] = bw_int_to_fixed(1); + data->v_scale_ratio[maximum_number_of_surfaces - 2] = bw_int_to_fixed(1); + data->v_scale_ratio[maximum_number_of_surfaces - 1] = bw_int_to_fixed(1); + data->stereo_mode[maximum_number_of_surfaces - 2] = bw_def_mono; + data->stereo_mode[maximum_number_of_surfaces - 1] = bw_def_mono; + data->cursor_width_pixels[maximum_number_of_surfaces - 2] = bw_int_to_fixed(0); + data->cursor_width_pixels[maximum_number_of_surfaces - 1] = bw_int_to_fixed(0); + data->use_alpha[maximum_number_of_surfaces - 2] = 0; + data->use_alpha[maximum_number_of_surfaces - 1] = 0; + /*mode check calculations:*/ + /* mode within dce ip capabilities*/ + /* fbc*/ + /* hsr*/ + /* vsr*/ + /* lb size*/ + /*effective scaling source and ratios:*/ + /*for graphics, non-stereo, non-interlace surfaces when the size of the source and destination are the same, only one tap is used*/ + /*420 chroma has half the width, height, horizontal and vertical scaling ratios than luma*/ + /*rotating a graphic or underlay surface swaps the width, height, horizontal and vertical scaling ratios*/ + /*in top-bottom stereo mode there is 2:1 vertical downscaling for each eye*/ + /*in side-by-side stereo mode there is 2:1 horizontal downscaling for each eye*/ + /*in interlace mode there is 2:1 vertical downscaling for each field*/ + /*in panning or bezel adjustment mode the source width has an extra 128 pixels*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_equ(data->h_scale_ratio[i], bw_int_to_fixed(1)) && bw_equ(data->v_scale_ratio[i], bw_int_to_fixed(1)) && surface_type[i] == bw_def_graphics && data->stereo_mode[i] == bw_def_mono && data->interlace_mode[i] == 0) { + data->h_taps[i] = bw_int_to_fixed(1); + data->v_taps[i] = bw_int_to_fixed(1); + } + if (surface_type[i] == bw_def_display_write_back420_chroma || surface_type[i] == bw_def_underlay420_chroma) { + data->pitch_in_pixels_after_surface_type[i] = bw_div(data->pitch_in_pixels[i], bw_int_to_fixed(2)); + data->src_width_after_surface_type = bw_div(data->src_width[i], bw_int_to_fixed(2)); + data->src_height_after_surface_type = bw_div(data->src_height[i], bw_int_to_fixed(2)); + data->hsr_after_surface_type = bw_div(data->h_scale_ratio[i], bw_int_to_fixed(2)); + data->vsr_after_surface_type = bw_div(data->v_scale_ratio[i], bw_int_to_fixed(2)); + } + else { + data->pitch_in_pixels_after_surface_type[i] = data->pitch_in_pixels[i]; + data->src_width_after_surface_type = data->src_width[i]; + data->src_height_after_surface_type = data->src_height[i]; + data->hsr_after_surface_type = data->h_scale_ratio[i]; + data->vsr_after_surface_type = data->v_scale_ratio[i]; + } + if ((bw_equ(data->rotation_angle[i], bw_int_to_fixed(90)) || bw_equ(data->rotation_angle[i], bw_int_to_fixed(270))) && surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + data->src_width_after_rotation = data->src_height_after_surface_type; + data->src_height_after_rotation = data->src_width_after_surface_type; + data->hsr_after_rotation = data->vsr_after_surface_type; + data->vsr_after_rotation = data->hsr_after_surface_type; + } + else { + data->src_width_after_rotation = data->src_width_after_surface_type; + data->src_height_after_rotation = data->src_height_after_surface_type; + data->hsr_after_rotation = data->hsr_after_surface_type; + data->vsr_after_rotation = data->vsr_after_surface_type; + } + switch (data->stereo_mode[i]) { + case bw_def_top_bottom: + data->source_width_pixels[i] = data->src_width_after_rotation; + data->source_height_pixels = bw_mul(bw_int_to_fixed(2), data->src_height_after_rotation); + data->hsr_after_stereo = data->hsr_after_rotation; + data->vsr_after_stereo = bw_mul(bw_int_to_fixed(1), data->vsr_after_rotation); + break; + case bw_def_side_by_side: + data->source_width_pixels[i] = bw_mul(bw_int_to_fixed(2), data->src_width_after_rotation); + data->source_height_pixels = data->src_height_after_rotation; + data->hsr_after_stereo = bw_mul(bw_int_to_fixed(1), data->hsr_after_rotation); + data->vsr_after_stereo = data->vsr_after_rotation; + break; + default: + data->source_width_pixels[i] = data->src_width_after_rotation; + data->source_height_pixels = data->src_height_after_rotation; + data->hsr_after_stereo = data->hsr_after_rotation; + data->vsr_after_stereo = data->vsr_after_rotation; + break; + } + data->hsr[i] = data->hsr_after_stereo; + if (data->interlace_mode[i]) { + data->vsr[i] = bw_mul(data->vsr_after_stereo, bw_int_to_fixed(2)); + } + else { + data->vsr[i] = data->vsr_after_stereo; + } + if (data->panning_and_bezel_adjustment != bw_def_none) { + data->source_width_rounded_up_to_chunks[i] = bw_add(bw_floor2(bw_sub(data->source_width_pixels[i], bw_int_to_fixed(1)), bw_int_to_fixed(128)), bw_int_to_fixed(256)); + } + else { + data->source_width_rounded_up_to_chunks[i] = bw_ceil2(data->source_width_pixels[i], bw_int_to_fixed(128)); + } + data->source_height_rounded_up_to_chunks[i] = data->source_height_pixels; + } + } + /*mode support checks:*/ + /*the number of graphics and underlay pipes is limited by the ip support*/ + /*maximum horizontal and vertical scale ratio is 4, and should not exceed the number of taps*/ + /*for downscaling with the pre-downscaler, the horizontal scale ratio must be more than the ceiling of one quarter of the number of taps*/ + /*the pre-downscaler reduces the line buffer source by the horizontal scale ratio*/ + /*the number of lines in the line buffer has to exceed the number of vertical taps*/ + /*the size of the line in the line buffer is the product of the source width and the bits per component, rounded up to a multiple of 48*/ + /*the size of the line in the line buffer in the case of 10 bit per component is the product of the source width rounded up to multiple of 8 and 30.023438 / 3, rounded up to a multiple of 48*/ + /*the size of the line in the line buffer in the case of 8 bit per component is the product of the source width rounded up to multiple of 8 and 30.023438 / 3, rounded up to a multiple of 48*/ + /*frame buffer compression is not supported with stereo mode, rotation, or non- 888 formats*/ + /*rotation is not supported with linear of stereo modes*/ + if (dceip->number_of_graphics_pipes >= data->number_of_displays && dceip->number_of_underlay_pipes >= data->number_of_underlay_surfaces && !(dceip->display_write_back_supported == 0 && data->d1_display_write_back_dwb_enable == 1)) { + pipe_check = bw_def_ok; + } + else { + pipe_check = bw_def_notok; + } + hsr_check = bw_def_ok; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_neq(data->hsr[i], bw_int_to_fixed(1))) { + if (bw_mtn(data->hsr[i], bw_int_to_fixed(4))) { + hsr_check = bw_def_hsr_mtn_4; + } + else { + if (bw_mtn(data->hsr[i], data->h_taps[i])) { + hsr_check = bw_def_hsr_mtn_h_taps; + } + else { + if (dceip->pre_downscaler_enabled == 1 && bw_mtn(data->hsr[i], bw_int_to_fixed(1)) && bw_leq(data->hsr[i], bw_ceil2(bw_div(data->h_taps[i], bw_int_to_fixed(4)), bw_int_to_fixed(1)))) { + hsr_check = bw_def_ceiling__h_taps_div_4___meq_hsr; + } + } + } + } + } + } + vsr_check = bw_def_ok; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_neq(data->vsr[i], bw_int_to_fixed(1))) { + if (bw_mtn(data->vsr[i], bw_int_to_fixed(4))) { + vsr_check = bw_def_vsr_mtn_4; + } + else { + if (bw_mtn(data->vsr[i], data->v_taps[i])) { + vsr_check = bw_def_vsr_mtn_v_taps; + } + } + } + } + } + lb_size_check = bw_def_ok; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((dceip->pre_downscaler_enabled && bw_mtn(data->hsr[i], bw_int_to_fixed(1)))) { + data->source_width_in_lb = bw_div(data->source_width_pixels[i], data->hsr[i]); + } + else { + data->source_width_in_lb = data->source_width_pixels[i]; + } + switch (data->lb_bpc[i]) { + case 8: + data->lb_line_pitch = bw_ceil2(bw_mul(bw_div(bw_frc_to_fixed(2401171875ul, 100000000), bw_int_to_fixed(3)), bw_ceil2(data->source_width_in_lb, bw_int_to_fixed(8))), bw_int_to_fixed(48)); + break; + case 10: + data->lb_line_pitch = bw_ceil2(bw_mul(bw_div(bw_frc_to_fixed(300234375, 10000000), bw_int_to_fixed(3)), bw_ceil2(data->source_width_in_lb, bw_int_to_fixed(8))), bw_int_to_fixed(48)); + break; + default: + data->lb_line_pitch = bw_ceil2(bw_mul(bw_int_to_fixed(data->lb_bpc[i]), data->source_width_in_lb), bw_int_to_fixed(48)); + break; + } + data->lb_partitions[i] = bw_floor2(bw_div(data->lb_size_per_component[i], data->lb_line_pitch), bw_int_to_fixed(1)); + /*clamp the partitions to the maxium number supported by the lb*/ + if ((surface_type[i] != bw_def_graphics || dceip->graphics_lb_nodownscaling_multi_line_prefetching == 1)) { + data->lb_partitions_max[i] = bw_int_to_fixed(10); + } + else { + data->lb_partitions_max[i] = bw_int_to_fixed(7); + } + data->lb_partitions[i] = bw_min2(data->lb_partitions_max[i], data->lb_partitions[i]); + if (bw_mtn(bw_add(data->v_taps[i], bw_int_to_fixed(1)), data->lb_partitions[i])) { + lb_size_check = bw_def_notok; + } + } + } + fbc_check = bw_def_ok; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i] && data->fbc_en[i] == 1 && (bw_equ(data->rotation_angle[i], bw_int_to_fixed(90)) || bw_equ(data->rotation_angle[i], bw_int_to_fixed(270)) || data->stereo_mode[i] != bw_def_mono || data->bytes_per_pixel[i] != 4)) { + fbc_check = bw_def_invalid_rotation_or_bpp_or_stereo; + } + } + rotation_check = bw_def_ok; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((bw_equ(data->rotation_angle[i], bw_int_to_fixed(90)) || bw_equ(data->rotation_angle[i], bw_int_to_fixed(270))) && (tiling_mode[i] == bw_def_linear || data->stereo_mode[i] != bw_def_mono)) { + rotation_check = bw_def_invalid_linear_or_stereo_mode; + } + } + } + if (pipe_check == bw_def_ok && hsr_check == bw_def_ok && vsr_check == bw_def_ok && lb_size_check == bw_def_ok && fbc_check == bw_def_ok && rotation_check == bw_def_ok) { + mode_check = bw_def_ok; + } + else { + mode_check = bw_def_notok; + } + /*number of memory channels for write-back client*/ + data->number_of_dram_wrchannels = vbios->number_of_dram_channels; + data->number_of_dram_channels = vbios->number_of_dram_channels; + /*modify number of memory channels if lpt mode is enabled*/ + /* low power tiling mode register*/ + /* 0 = use channel 0*/ + /* 1 = use channel 0 and 1*/ + /* 2 = use channel 0,1,2,3*/ + if ((fbc_enabled == 1 && lpt_enabled == 1)) { + data->dram_efficiency = bw_int_to_fixed(1); + if (dceip->low_power_tiling_mode == 0) { + data->number_of_dram_channels = 1; + } + else if (dceip->low_power_tiling_mode == 1) { + data->number_of_dram_channels = 2; + } + else if (dceip->low_power_tiling_mode == 2) { + data->number_of_dram_channels = 4; + } + else { + data->number_of_dram_channels = 1; + } + } + else { + data->dram_efficiency = bw_frc_to_fixed(8, 10); + } + /*memory request size and latency hiding:*/ + /*request size is normally 64 byte, 2-line interleaved, with full latency hiding*/ + /*the display write-back requests are single line*/ + /*for tiled graphics surfaces, or undelay surfaces with width higher than the maximum size for full efficiency, request size is 32 byte in 8 and 16 bpp or if the rotation is orthogonal to the tiling grain. only half is useful of the bytes in the request size in 8 bpp or in 32 bpp if the rotation is orthogonal to the tiling grain.*/ + /*for undelay surfaces with width lower than the maximum size for full efficiency, requests are 4-line interleaved in 16bpp if the rotation is parallel to the tiling grain, and 8-line interleaved with 4-line latency hiding in 8bpp or if the rotation is orthogonal to the tiling grain.*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((bw_equ(data->rotation_angle[i], bw_int_to_fixed(90)) || bw_equ(data->rotation_angle[i], bw_int_to_fixed(270)))) { + if ((i < 4)) { + /*underlay portrait tiling mode is not supported*/ + data->orthogonal_rotation[i] = 1; + } + else { + /*graphics portrait tiling mode*/ + if ((data->graphics_micro_tile_mode == bw_def_rotated_micro_tiling)) { + data->orthogonal_rotation[i] = 0; + } + else { + data->orthogonal_rotation[i] = 1; + } + } + } + else { + if ((i < 4)) { + /*underlay landscape tiling mode is only supported*/ + if ((data->underlay_micro_tile_mode == bw_def_display_micro_tiling)) { + data->orthogonal_rotation[i] = 0; + } + else { + data->orthogonal_rotation[i] = 1; + } + } + else { + /*graphics landscape tiling mode*/ + if ((data->graphics_micro_tile_mode == bw_def_display_micro_tiling)) { + data->orthogonal_rotation[i] = 0; + } + else { + data->orthogonal_rotation[i] = 1; + } + } + } + if (bw_equ(data->rotation_angle[i], bw_int_to_fixed(90)) || bw_equ(data->rotation_angle[i], bw_int_to_fixed(270))) { + data->underlay_maximum_source_efficient_for_tiling = dceip->underlay_maximum_height_efficient_for_tiling; + } + else { + data->underlay_maximum_source_efficient_for_tiling = dceip->underlay_maximum_width_efficient_for_tiling; + } + if (surface_type[i] == bw_def_display_write_back420_luma || surface_type[i] == bw_def_display_write_back420_chroma) { + data->bytes_per_request[i] = bw_int_to_fixed(64); + data->useful_bytes_per_request[i] = bw_int_to_fixed(64); + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(1); + data->latency_hiding_lines[i] = bw_int_to_fixed(1); + } + else if (tiling_mode[i] == bw_def_linear) { + data->bytes_per_request[i] = bw_int_to_fixed(64); + data->useful_bytes_per_request[i] = bw_int_to_fixed(64); + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(2); + data->latency_hiding_lines[i] = bw_int_to_fixed(2); + } + else { + if (surface_type[i] == bw_def_graphics || (bw_mtn(data->source_width_rounded_up_to_chunks[i], bw_ceil2(data->underlay_maximum_source_efficient_for_tiling, bw_int_to_fixed(256))))) { + switch (data->bytes_per_pixel[i]) { + case 8: + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(2); + data->latency_hiding_lines[i] = bw_int_to_fixed(2); + if (data->orthogonal_rotation[i]) { + data->bytes_per_request[i] = bw_int_to_fixed(32); + data->useful_bytes_per_request[i] = bw_int_to_fixed(32); + } + else { + data->bytes_per_request[i] = bw_int_to_fixed(64); + data->useful_bytes_per_request[i] = bw_int_to_fixed(64); + } + break; + case 4: + if (data->orthogonal_rotation[i]) { + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(2); + data->latency_hiding_lines[i] = bw_int_to_fixed(2); + data->bytes_per_request[i] = bw_int_to_fixed(32); + data->useful_bytes_per_request[i] = bw_int_to_fixed(16); + } + else { + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(2); + data->latency_hiding_lines[i] = bw_int_to_fixed(2); + data->bytes_per_request[i] = bw_int_to_fixed(64); + data->useful_bytes_per_request[i] = bw_int_to_fixed(64); + } + break; + case 2: + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(2); + data->latency_hiding_lines[i] = bw_int_to_fixed(2); + data->bytes_per_request[i] = bw_int_to_fixed(32); + data->useful_bytes_per_request[i] = bw_int_to_fixed(32); + break; + default: + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(2); + data->latency_hiding_lines[i] = bw_int_to_fixed(2); + data->bytes_per_request[i] = bw_int_to_fixed(32); + data->useful_bytes_per_request[i] = bw_int_to_fixed(16); + break; + } + } + else { + data->bytes_per_request[i] = bw_int_to_fixed(64); + data->useful_bytes_per_request[i] = bw_int_to_fixed(64); + if (data->orthogonal_rotation[i]) { + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(8); + data->latency_hiding_lines[i] = bw_int_to_fixed(4); + } + else { + switch (data->bytes_per_pixel[i]) { + case 4: + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(2); + data->latency_hiding_lines[i] = bw_int_to_fixed(2); + break; + case 2: + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(4); + data->latency_hiding_lines[i] = bw_int_to_fixed(4); + break; + default: + data->lines_interleaved_in_mem_access[i] = bw_int_to_fixed(8); + data->latency_hiding_lines[i] = bw_int_to_fixed(4); + break; + } + } + } + } + } + } + /*requested peak bandwidth:*/ + /*the peak request-per-second bandwidth is the product of the maximum source lines in per line out in the beginning*/ + /*and in the middle of the frame, the ratio of the source width to the line time, the ratio of line interleaving*/ + /*in memory to lines of latency hiding, and the ratio of bytes per pixel to useful bytes per request.*/ + /**/ + /*if the dmif data buffer size holds more than vta_ps worth of source lines, then only vsr is used.*/ + /*the peak bandwidth is the peak request-per-second bandwidth times the request size.*/ + /**/ + /*the line buffer lines in per line out in the beginning of the frame is the vertical filter initialization value*/ + /*rounded up to even and divided by the line times for initialization, which is normally three.*/ + /*the line buffer lines in per line out in the middle of the frame is at least one, or the vertical scale ratio,*/ + /*rounded up to line pairs if not doing line buffer prefetching.*/ + /**/ + /*the non-prefetching rounding up of the vertical scale ratio can also be done up to 1 (for a 0,2 pattern), 4/3 (for a 0,2,2 pattern),*/ + /*6/4 (for a 0,2,2,2 pattern), or 3 (for a 2,4 pattern).*/ + /**/ + /*the scaler vertical filter initialization value is calculated by the hardware as the floor of the average of the*/ + /*vertical scale ratio and the number of vertical taps increased by one. add one more for possible odd line*/ + /*panning/bezel adjustment mode.*/ + /**/ + /*for the bottom interlace field an extra 50% of the vertical scale ratio is considered for this calculation.*/ + /*in top-bottom stereo mode software has to set the filter initialization value manually and explicitly limit it to 4.*/ + /*furthermore, there is only one line time for initialization.*/ + /**/ + /*line buffer prefetching is done when the number of lines in the line buffer exceeds the number of taps plus*/ + /*the ceiling of the vertical scale ratio.*/ + /**/ + /*multi-line buffer prefetching is only done in the graphics pipe when the scaler is disabled or when upscaling and the vsr <= 0.8.'*/ + /**/ + /*the horizontal blank and chunk granularity factor is indirectly used indicate the interval of time required to transfer the source pixels.*/ + /*the denominator of this term represents the total number of destination output pixels required for the input source pixels.*/ + /*it applies when the lines in per line out is not 2 or 4. it does not apply when there is a line buffer between the scl and blnd.*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->v_filter_init[i] = bw_floor2(bw_div((bw_add(bw_add(bw_add(bw_int_to_fixed(1), data->v_taps[i]), data->vsr[i]), bw_mul(bw_mul(bw_int_to_fixed(data->interlace_mode[i]), bw_frc_to_fixed(5, 10)), data->vsr[i]))), bw_int_to_fixed(2)), bw_int_to_fixed(1)); + if (data->panning_and_bezel_adjustment == bw_def_any_lines) { + data->v_filter_init[i] = bw_add(data->v_filter_init[i], bw_int_to_fixed(1)); + } + if (data->stereo_mode[i] == bw_def_top_bottom) { + v_filter_init_mode[i] = bw_def_manual; + data->v_filter_init[i] = bw_min2(data->v_filter_init[i], bw_int_to_fixed(4)); + } + else { + v_filter_init_mode[i] = bw_def_auto; + } + if (data->stereo_mode[i] == bw_def_top_bottom) { + data->num_lines_at_frame_start = bw_int_to_fixed(1); + } + else { + data->num_lines_at_frame_start = bw_int_to_fixed(3); + } + if ((bw_mtn(data->vsr[i], bw_int_to_fixed(1)) && surface_type[i] == bw_def_graphics) || data->panning_and_bezel_adjustment == bw_def_any_lines) { + data->line_buffer_prefetch[i] = 0; + } + else if ((((dceip->underlay_downscale_prefetch_enabled == 1 && surface_type[i] != bw_def_graphics) || surface_type[i] == bw_def_graphics) && (bw_mtn(data->lb_partitions[i], bw_add(data->v_taps[i], bw_ceil2(data->vsr[i], bw_int_to_fixed(1))))))) { + data->line_buffer_prefetch[i] = 1; + } + else { + data->line_buffer_prefetch[i] = 0; + } + data->lb_lines_in_per_line_out_in_beginning_of_frame[i] = bw_div(bw_ceil2(data->v_filter_init[i], bw_int_to_fixed(dceip->lines_interleaved_into_lb)), data->num_lines_at_frame_start); + if (data->line_buffer_prefetch[i] == 1) { + data->lb_lines_in_per_line_out_in_middle_of_frame[i] = bw_max2(bw_int_to_fixed(1), data->vsr[i]); + } + else if (bw_leq(data->vsr[i], bw_int_to_fixed(1))) { + data->lb_lines_in_per_line_out_in_middle_of_frame[i] = bw_int_to_fixed(1); + } else if (bw_leq(data->vsr[i], + bw_frc_to_fixed(4, 3))) { + data->lb_lines_in_per_line_out_in_middle_of_frame[i] = bw_div(bw_int_to_fixed(4), bw_int_to_fixed(3)); + } else if (bw_leq(data->vsr[i], + bw_frc_to_fixed(6, 4))) { + data->lb_lines_in_per_line_out_in_middle_of_frame[i] = bw_div(bw_int_to_fixed(6), bw_int_to_fixed(4)); + } + else if (bw_leq(data->vsr[i], bw_int_to_fixed(2))) { + data->lb_lines_in_per_line_out_in_middle_of_frame[i] = bw_int_to_fixed(2); + } + else if (bw_leq(data->vsr[i], bw_int_to_fixed(3))) { + data->lb_lines_in_per_line_out_in_middle_of_frame[i] = bw_int_to_fixed(3); + } + else { + data->lb_lines_in_per_line_out_in_middle_of_frame[i] = bw_int_to_fixed(4); + } + if (data->line_buffer_prefetch[i] == 1 || bw_equ(data->lb_lines_in_per_line_out_in_middle_of_frame[i], bw_int_to_fixed(2)) || bw_equ(data->lb_lines_in_per_line_out_in_middle_of_frame[i], bw_int_to_fixed(4))) { + data->horizontal_blank_and_chunk_granularity_factor[i] = bw_int_to_fixed(1); + } + else { + data->horizontal_blank_and_chunk_granularity_factor[i] = bw_div(data->h_total[i], (bw_div((bw_add(data->h_total[i], bw_div((bw_sub(data->source_width_pixels[i], bw_int_to_fixed(dceip->chunk_width))), data->hsr[i]))), bw_int_to_fixed(2)))); + } + data->request_bandwidth[i] = bw_div(bw_mul(bw_div(bw_mul(bw_div(bw_mul(bw_max2(data->lb_lines_in_per_line_out_in_beginning_of_frame[i], data->lb_lines_in_per_line_out_in_middle_of_frame[i]), data->source_width_rounded_up_to_chunks[i]), (bw_div(data->h_total[i], data->pixel_rate[i]))), bw_int_to_fixed(data->bytes_per_pixel[i])), data->useful_bytes_per_request[i]), data->lines_interleaved_in_mem_access[i]), data->latency_hiding_lines[i]); + data->display_bandwidth[i] = bw_mul(data->request_bandwidth[i], data->bytes_per_request[i]); + } + } + /*outstanding chunk request limit*/ + /*if underlay buffer sharing is enabled, the data buffer size for underlay in 422 or 444 is the sum of the luma and chroma data buffer sizes.*/ + /*underlay buffer sharing mode is only permitted in orthogonal rotation modes.*/ + /**/ + /*if there is only one display enabled, the dmif data buffer size for the graphics surface is increased by concatenating the adjacent buffers.*/ + /**/ + /*the memory chunk size in bytes is 1024 for the writeback, and 256 times the memory line interleaving and the bytes per pixel for graphics*/ + /*and underlay.*/ + /**/ + /*the pipe chunk size uses 2 for line interleaving, except for the write back, in which case it is 1.*/ + /*graphics and underlay data buffer size is adjusted (limited) using the outstanding chunk request limit if there is more than one*/ + /*display enabled or if the dmif request buffer is not large enough for the total data buffer size.*/ + /*the outstanding chunk request limit is the ceiling of the adjusted data buffer size divided by the chunk size in bytes*/ + /*the adjusted data buffer size is the product of the display bandwidth and the minimum effective data buffer size in terms of time,*/ + /*rounded up to the chunk size in bytes, but should not exceed the original data buffer size*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((dceip->dmif_pipe_en_fbc_chunk_tracker + 3 == i && fbc_enabled == 0 && tiling_mode[i] != bw_def_linear)) { + data->max_chunks_non_fbc_mode[i] = 128 - dmif_chunk_buff_margin; + } + else { + data->max_chunks_non_fbc_mode[i] = 16 - dmif_chunk_buff_margin; + } + } + if (data->fbc_en[i] == 1) { + max_chunks_fbc_mode = 128 - dmif_chunk_buff_margin; + } + } + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + switch (surface_type[i]) { + case bw_def_display_write_back420_luma: + data->data_buffer_size[i] = bw_int_to_fixed(dceip->display_write_back420_luma_mcifwr_buffer_size); + break; + case bw_def_display_write_back420_chroma: + data->data_buffer_size[i] = bw_int_to_fixed(dceip->display_write_back420_chroma_mcifwr_buffer_size); + break; + case bw_def_underlay420_luma: + data->data_buffer_size[i] = bw_int_to_fixed(dceip->underlay_luma_dmif_size); + break; + case bw_def_underlay420_chroma: + data->data_buffer_size[i] = bw_div(bw_int_to_fixed(dceip->underlay_chroma_dmif_size), bw_int_to_fixed(2)); + break; + case bw_def_underlay422:case bw_def_underlay444: + if (data->orthogonal_rotation[i] == 0) { + data->data_buffer_size[i] = bw_int_to_fixed(dceip->underlay_luma_dmif_size); + } + else { + data->data_buffer_size[i] = bw_add(bw_int_to_fixed(dceip->underlay_luma_dmif_size), bw_int_to_fixed(dceip->underlay_chroma_dmif_size)); + } + break; + default: + if (data->fbc_en[i] == 1) { + /*data_buffer_size(i) = max_dmif_buffer_allocated * graphics_dmif_size*/ + if (data->number_of_displays == 1) { + data->data_buffer_size[i] = bw_min2(bw_mul(bw_mul(bw_int_to_fixed(max_chunks_fbc_mode), bw_int_to_fixed(pixels_per_chunk)), bw_int_to_fixed(data->bytes_per_pixel[i])), bw_mul(bw_int_to_fixed(dceip->max_dmif_buffer_allocated), bw_int_to_fixed(dceip->graphics_dmif_size))); + } + else { + data->data_buffer_size[i] = bw_min2(bw_mul(bw_mul(bw_int_to_fixed(max_chunks_fbc_mode), bw_int_to_fixed(pixels_per_chunk)), bw_int_to_fixed(data->bytes_per_pixel[i])), bw_int_to_fixed(dceip->graphics_dmif_size)); + } + } + else { + /*the effective dmif buffer size in non-fbc mode is limited by the 16 entry chunk tracker*/ + if (data->number_of_displays == 1) { + data->data_buffer_size[i] = bw_min2(bw_mul(bw_mul(bw_int_to_fixed(data->max_chunks_non_fbc_mode[i]), bw_int_to_fixed(pixels_per_chunk)), bw_int_to_fixed(data->bytes_per_pixel[i])), bw_mul(bw_int_to_fixed(dceip->max_dmif_buffer_allocated), bw_int_to_fixed(dceip->graphics_dmif_size))); + } + else { + data->data_buffer_size[i] = bw_min2(bw_mul(bw_mul(bw_int_to_fixed(data->max_chunks_non_fbc_mode[i]), bw_int_to_fixed(pixels_per_chunk)), bw_int_to_fixed(data->bytes_per_pixel[i])), bw_int_to_fixed(dceip->graphics_dmif_size)); + } + } + break; + } + if (surface_type[i] == bw_def_display_write_back420_luma || surface_type[i] == bw_def_display_write_back420_chroma) { + data->memory_chunk_size_in_bytes[i] = bw_int_to_fixed(1024); + data->pipe_chunk_size_in_bytes[i] = bw_int_to_fixed(1024); + } + else { + data->memory_chunk_size_in_bytes[i] = bw_mul(bw_mul(bw_int_to_fixed(dceip->chunk_width), data->lines_interleaved_in_mem_access[i]), bw_int_to_fixed(data->bytes_per_pixel[i])); + data->pipe_chunk_size_in_bytes[i] = bw_mul(bw_mul(bw_int_to_fixed(dceip->chunk_width), bw_int_to_fixed(dceip->lines_interleaved_into_lb)), bw_int_to_fixed(data->bytes_per_pixel[i])); + } + } + } + data->min_dmif_size_in_time = bw_int_to_fixed(9999); + data->min_mcifwr_size_in_time = bw_int_to_fixed(9999); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + if (bw_ltn(bw_div(bw_div(bw_mul(data->data_buffer_size[i], data->bytes_per_request[i]), data->useful_bytes_per_request[i]), data->display_bandwidth[i]), data->min_dmif_size_in_time)) { + data->min_dmif_size_in_time = bw_div(bw_div(bw_mul(data->data_buffer_size[i], data->bytes_per_request[i]), data->useful_bytes_per_request[i]), data->display_bandwidth[i]); + } + } + else { + if (bw_ltn(bw_div(bw_div(bw_mul(data->data_buffer_size[i], data->bytes_per_request[i]), data->useful_bytes_per_request[i]), data->display_bandwidth[i]), data->min_mcifwr_size_in_time)) { + data->min_mcifwr_size_in_time = bw_div(bw_div(bw_mul(data->data_buffer_size[i], data->bytes_per_request[i]), data->useful_bytes_per_request[i]), data->display_bandwidth[i]); + } + } + } + } + data->total_requests_for_dmif_size = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i] && surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + data->total_requests_for_dmif_size = bw_add(data->total_requests_for_dmif_size, bw_div(data->data_buffer_size[i], data->useful_bytes_per_request[i])); + } + } + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma && dceip->limit_excessive_outstanding_dmif_requests && (data->number_of_displays > 1 || bw_mtn(data->total_requests_for_dmif_size, dceip->dmif_request_buffer_size))) { + data->adjusted_data_buffer_size[i] = bw_min2(data->data_buffer_size[i], bw_ceil2(bw_mul(data->min_dmif_size_in_time, data->display_bandwidth[i]), data->memory_chunk_size_in_bytes[i])); + } + else { + data->adjusted_data_buffer_size[i] = data->data_buffer_size[i]; + } + } + } + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((data->number_of_displays == 1 && data->number_of_underlay_surfaces == 0)) { + /*set maximum chunk limit if only one graphic pipe is enabled*/ + data->outstanding_chunk_request_limit[i] = bw_int_to_fixed(127); + } + else { + data->outstanding_chunk_request_limit[i] = bw_ceil2(bw_div(data->adjusted_data_buffer_size[i], data->pipe_chunk_size_in_bytes[i]), bw_int_to_fixed(1)); + /*clamp maximum chunk limit in the graphic display pipe*/ + if ((i >= 4)) { + data->outstanding_chunk_request_limit[i] = bw_max2(bw_int_to_fixed(127), data->outstanding_chunk_request_limit[i]); + } + } + } + } + /*outstanding pte request limit*/ + /*in tiling mode with no rotation the sg pte requests are 8 useful pt_es, the sg row height is the page height and the sg page width x height is 64x64 for 8bpp, 64x32 for 16 bpp, 32x32 for 32 bpp*/ + /*in tiling mode with rotation the sg pte requests are only one useful pte, and the sg row height is also the page height, but the sg page width and height are swapped*/ + /*in linear mode the pte requests are 8 useful pt_es, the sg page width is 4096 divided by the bytes per pixel, the sg page height is 1, but there is just one row whose height is the lines of pte prefetching*/ + /*the outstanding pte request limit is obtained by multiplying the outstanding chunk request limit by the peak pte request to eviction limiting ratio, rounding up to integer, multiplying by the pte requests per chunk, and rounding up to integer again*/ + /*if not using peak pte request to eviction limiting, the outstanding pte request limit is the pte requests in the vblank*/ + /*the pte requests in the vblank is the product of the number of pte request rows times the number of pte requests in a row*/ + /*the number of pte requests in a row is the quotient of the source width divided by 256, multiplied by the pte requests per chunk, rounded up to even, multiplied by the scatter-gather row height and divided by the scatter-gather page height*/ + /*the pte requests per chunk is 256 divided by the scatter-gather page width and the useful pt_es per pte request*/ + if (data->number_of_displays > 1 || (bw_neq(data->rotation_angle[4], bw_int_to_fixed(0)) && bw_neq(data->rotation_angle[4], bw_int_to_fixed(180)))) { + data->peak_pte_request_to_eviction_ratio_limiting = dceip->peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display; + } + else { + data->peak_pte_request_to_eviction_ratio_limiting = dceip->peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation; + } + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i] && data->scatter_gather_enable_for_pipe[i] == 1) { + if (tiling_mode[i] == bw_def_linear) { + data->useful_pte_per_pte_request = bw_int_to_fixed(8); + data->scatter_gather_page_width[i] = bw_div(bw_int_to_fixed(4096), bw_int_to_fixed(data->bytes_per_pixel[i])); + data->scatter_gather_page_height[i] = bw_int_to_fixed(1); + data->scatter_gather_pte_request_rows = bw_int_to_fixed(1); + data->scatter_gather_row_height = bw_int_to_fixed(dceip->scatter_gather_lines_of_pte_prefetching_in_linear_mode); + } + else if (bw_equ(data->rotation_angle[i], bw_int_to_fixed(0)) || bw_equ(data->rotation_angle[i], bw_int_to_fixed(180))) { + data->useful_pte_per_pte_request = bw_int_to_fixed(8); + switch (data->bytes_per_pixel[i]) { + case 4: + data->scatter_gather_page_width[i] = bw_int_to_fixed(32); + data->scatter_gather_page_height[i] = bw_int_to_fixed(32); + break; + case 2: + data->scatter_gather_page_width[i] = bw_int_to_fixed(64); + data->scatter_gather_page_height[i] = bw_int_to_fixed(32); + break; + default: + data->scatter_gather_page_width[i] = bw_int_to_fixed(64); + data->scatter_gather_page_height[i] = bw_int_to_fixed(64); + break; + } + data->scatter_gather_pte_request_rows = bw_int_to_fixed(dceip->scatter_gather_pte_request_rows_in_tiling_mode); + data->scatter_gather_row_height = data->scatter_gather_page_height[i]; + } + else { + data->useful_pte_per_pte_request = bw_int_to_fixed(1); + switch (data->bytes_per_pixel[i]) { + case 4: + data->scatter_gather_page_width[i] = bw_int_to_fixed(32); + data->scatter_gather_page_height[i] = bw_int_to_fixed(32); + break; + case 2: + data->scatter_gather_page_width[i] = bw_int_to_fixed(32); + data->scatter_gather_page_height[i] = bw_int_to_fixed(64); + break; + default: + data->scatter_gather_page_width[i] = bw_int_to_fixed(64); + data->scatter_gather_page_height[i] = bw_int_to_fixed(64); + break; + } + data->scatter_gather_pte_request_rows = bw_int_to_fixed(dceip->scatter_gather_pte_request_rows_in_tiling_mode); + data->scatter_gather_row_height = data->scatter_gather_page_height[i]; + } + data->pte_request_per_chunk[i] = bw_div(bw_div(bw_int_to_fixed(dceip->chunk_width), data->scatter_gather_page_width[i]), data->useful_pte_per_pte_request); + data->scatter_gather_pte_requests_in_row[i] = bw_div(bw_mul(bw_ceil2(bw_mul(bw_div(data->source_width_rounded_up_to_chunks[i], bw_int_to_fixed(dceip->chunk_width)), data->pte_request_per_chunk[i]), bw_int_to_fixed(1)), data->scatter_gather_row_height), data->scatter_gather_page_height[i]); + data->scatter_gather_pte_requests_in_vblank = bw_mul(data->scatter_gather_pte_request_rows, data->scatter_gather_pte_requests_in_row[i]); + if (bw_equ(data->peak_pte_request_to_eviction_ratio_limiting, bw_int_to_fixed(0))) { + data->scatter_gather_pte_request_limit[i] = data->scatter_gather_pte_requests_in_vblank; + } + else { + data->scatter_gather_pte_request_limit[i] = bw_max2(dceip->minimum_outstanding_pte_request_limit, bw_min2(data->scatter_gather_pte_requests_in_vblank, bw_ceil2(bw_mul(bw_mul(bw_div(bw_ceil2(data->adjusted_data_buffer_size[i], data->memory_chunk_size_in_bytes[i]), data->memory_chunk_size_in_bytes[i]), data->pte_request_per_chunk[i]), data->peak_pte_request_to_eviction_ratio_limiting), bw_int_to_fixed(1)))); + } + } + } + /*pitch padding recommended for efficiency in linear mode*/ + /*in linear mode graphics or underlay with scatter gather, a pitch that is a multiple of the channel interleave (256 bytes) times the channel-bank rotation is not efficient*/ + /*if that is the case it is recommended to pad the pitch by at least 256 pixels*/ + data->inefficient_linear_pitch_in_bytes = bw_mul(bw_mul(bw_int_to_fixed(256), bw_int_to_fixed(vbios->number_of_dram_banks)), bw_int_to_fixed(data->number_of_dram_channels)); + + /*pixel transfer time*/ + /*the dmif and mcifwr yclk(pclk) required is the one that allows the transfer of all pipe's data buffer size in memory in the time for data transfer*/ + /*for dmif, pte and cursor requests have to be included.*/ + /*the dram data requirement is doubled when the data request size in bytes is less than the dram channel width times the burst size (8)*/ + /*the dram data requirement is also multiplied by the number of channels in the case of low power tiling*/ + /*the page close-open time is determined by trc and the number of page close-opens*/ + /*in tiled mode graphics or underlay with scatter-gather enabled the bytes per page close-open is the product of the memory line interleave times the maximum of the scatter-gather page width and the product of the tile width (8 pixels) times the number of channels times the number of banks.*/ + /*in linear mode graphics or underlay with scatter-gather enabled and inefficient pitch, the bytes per page close-open is the line request alternation slice, because different lines are in completely different 4k address bases.*/ + /*otherwise, the bytes page close-open is the chunk size because that is the arbitration slice.*/ + /*pte requests are grouped by pte requests per chunk if that is more than 1. each group costs a page close-open time for dmif reads*/ + /*cursor requests outstanding are limited to a group of two source lines. each group costs a page close-open time for dmif reads*/ + /*the display reads and writes time for data transfer is the minimum data or cursor buffer size in time minus the mc urgent latency*/ + /*the mc urgent latency is experienced more than one time if the number of dmif requests in the data buffer exceeds the request buffer size plus the request slots reserved for dmif in the dram channel arbiter queues*/ + /*the dispclk required is the maximum for all surfaces of the maximum of the source pixels for first output pixel times the throughput factor, divided by the pixels per dispclk, and divided by the minimum latency hiding minus the dram speed/p-state change latency minus the burst time, and the source pixels for last output pixel, times the throughput factor, divided by the pixels per dispclk, and divided by the minimum latency hiding minus the dram speed/p-state change latency minus the burst time, plus the active time.*/ + /*the data burst time is the maximum of the total page close-open time, total dmif/mcifwr buffer size in memory divided by the dram bandwidth, and the total dmif/mcifwr buffer size in memory divided by the 32 byte sclk data bus bandwidth, each multiplied by its efficiency.*/ + /*the source line transfer time is the maximum for all surfaces of the maximum of the burst time plus the urgent latency times the floor of the data required divided by the buffer size for the fist pixel, and the burst time plus the urgent latency times the floor of the data required divided by the buffer size for the last pixel plus the active time.*/ + /*the source pixels for the first output pixel is 512 if the scaler vertical filter initialization value is greater than 2, and it is 4 times the source width if it is greater than 4.*/ + /*the source pixels for the last output pixel is the source width times the scaler vertical filter initialization value rounded up to even*/ + /*the source data for these pixels is the number of pixels times the bytes per pixel times the bytes per request divided by the useful bytes per request.*/ + data->cursor_total_data = bw_int_to_fixed(0); + data->cursor_total_request_groups = bw_int_to_fixed(0); + data->scatter_gather_total_pte_requests = bw_int_to_fixed(0); + data->scatter_gather_total_pte_request_groups = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->cursor_total_data = bw_add(data->cursor_total_data, bw_mul(bw_mul(bw_int_to_fixed(2), data->cursor_width_pixels[i]), bw_int_to_fixed(4))); + if (dceip->large_cursor == 1) { + data->cursor_total_request_groups = bw_add(data->cursor_total_request_groups, bw_int_to_fixed((dceip->cursor_max_outstanding_group_num + 1))); + } + else { + data->cursor_total_request_groups = bw_add(data->cursor_total_request_groups, bw_ceil2(bw_div(data->cursor_width_pixels[i], dceip->cursor_chunk_width), bw_int_to_fixed(1))); + } + if (data->scatter_gather_enable_for_pipe[i]) { + data->scatter_gather_total_pte_requests = bw_add(data->scatter_gather_total_pte_requests, data->scatter_gather_pte_request_limit[i]); + data->scatter_gather_total_pte_request_groups = bw_add(data->scatter_gather_total_pte_request_groups, bw_ceil2(bw_div(data->scatter_gather_pte_request_limit[i], bw_ceil2(data->pte_request_per_chunk[i], bw_int_to_fixed(1))), bw_int_to_fixed(1))); + } + } + } + data->tile_width_in_pixels = bw_int_to_fixed(8); + data->dmif_total_number_of_data_request_page_close_open = bw_int_to_fixed(0); + data->mcifwr_total_number_of_data_request_page_close_open = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (data->scatter_gather_enable_for_pipe[i] == 1 && tiling_mode[i] != bw_def_linear) { + data->bytes_per_page_close_open = bw_mul(data->lines_interleaved_in_mem_access[i], bw_max2(bw_mul(bw_mul(bw_mul(bw_int_to_fixed(data->bytes_per_pixel[i]), data->tile_width_in_pixels), bw_int_to_fixed(vbios->number_of_dram_banks)), bw_int_to_fixed(data->number_of_dram_channels)), bw_mul(bw_int_to_fixed(data->bytes_per_pixel[i]), data->scatter_gather_page_width[i]))); + } + else if (data->scatter_gather_enable_for_pipe[i] == 1 && tiling_mode[i] == bw_def_linear && bw_equ(bw_mod((bw_mul(data->pitch_in_pixels_after_surface_type[i], bw_int_to_fixed(data->bytes_per_pixel[i]))), data->inefficient_linear_pitch_in_bytes), bw_int_to_fixed(0))) { + data->bytes_per_page_close_open = dceip->linear_mode_line_request_alternation_slice; + } + else { + data->bytes_per_page_close_open = data->memory_chunk_size_in_bytes[i]; + } + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + data->dmif_total_number_of_data_request_page_close_open = bw_add(data->dmif_total_number_of_data_request_page_close_open, bw_div(bw_ceil2(data->adjusted_data_buffer_size[i], data->memory_chunk_size_in_bytes[i]), data->bytes_per_page_close_open)); + } + else { + data->mcifwr_total_number_of_data_request_page_close_open = bw_add(data->mcifwr_total_number_of_data_request_page_close_open, bw_div(bw_ceil2(data->adjusted_data_buffer_size[i], data->memory_chunk_size_in_bytes[i]), data->bytes_per_page_close_open)); + } + } + } + data->dmif_total_page_close_open_time = bw_div(bw_mul((bw_add(bw_add(data->dmif_total_number_of_data_request_page_close_open, data->scatter_gather_total_pte_request_groups), data->cursor_total_request_groups)), vbios->trc), bw_int_to_fixed(1000)); + data->mcifwr_total_page_close_open_time = bw_div(bw_mul(data->mcifwr_total_number_of_data_request_page_close_open, vbios->trc), bw_int_to_fixed(1000)); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->adjusted_data_buffer_size_in_memory[i] = bw_div(bw_mul(data->adjusted_data_buffer_size[i], data->bytes_per_request[i]), data->useful_bytes_per_request[i]); + } + } + data->total_requests_for_adjusted_dmif_size = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + data->total_requests_for_adjusted_dmif_size = bw_add(data->total_requests_for_adjusted_dmif_size, bw_div(data->adjusted_data_buffer_size[i], data->useful_bytes_per_request[i])); + } + } + } + data->total_dmifmc_urgent_trips = bw_ceil2(bw_div(data->total_requests_for_adjusted_dmif_size, (bw_add(dceip->dmif_request_buffer_size, bw_int_to_fixed(vbios->number_of_request_slots_gmc_reserves_for_dmif_per_channel * data->number_of_dram_channels)))), bw_int_to_fixed(1)); + data->total_dmifmc_urgent_latency = bw_mul(vbios->dmifmc_urgent_latency, data->total_dmifmc_urgent_trips); + data->total_display_reads_required_data = bw_int_to_fixed(0); + data->total_display_reads_required_dram_access_data = bw_int_to_fixed(0); + data->total_display_writes_required_data = bw_int_to_fixed(0); + data->total_display_writes_required_dram_access_data = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + data->display_reads_required_data = data->adjusted_data_buffer_size_in_memory[i]; + /*for hbm memories, each channel is split into 2 pseudo-channels that are each 64 bits in width. each*/ + /*pseudo-channel may be read independently of one another.*/ + /*the read burst length (bl) for hbm memories is 4, so each read command will access 32 bytes of data.*/ + /*the 64 or 32 byte sized data is stored in one pseudo-channel.*/ + /*it will take 4 memclk cycles or 8 yclk cycles to fetch 64 bytes of data from the hbm memory (2 read commands).*/ + /*it will take 2 memclk cycles or 4 yclk cycles to fetch 32 bytes of data from the hbm memory (1 read command).*/ + /*for gddr5/ddr4 memories, there is additional overhead if the size of the request is smaller than 64 bytes.*/ + /*the read burst length (bl) for gddr5/ddr4 memories is 8, regardless of the size of the data request.*/ + /*therefore it will require 8 cycles to fetch 64 or 32 bytes of data from the memory.*/ + /*the memory efficiency will be 50% for the 32 byte sized data.*/ + if (vbios->memory_type == bw_def_hbm) { + data->display_reads_required_dram_access_data = data->adjusted_data_buffer_size_in_memory[i]; + } + else { + data->display_reads_required_dram_access_data = bw_mul(data->adjusted_data_buffer_size_in_memory[i], bw_ceil2(bw_div(bw_int_to_fixed((8 * vbios->dram_channel_width_in_bits / 8)), data->bytes_per_request[i]), bw_int_to_fixed(1))); + } + data->total_display_reads_required_data = bw_add(data->total_display_reads_required_data, data->display_reads_required_data); + data->total_display_reads_required_dram_access_data = bw_add(data->total_display_reads_required_dram_access_data, data->display_reads_required_dram_access_data); + } + else { + data->total_display_writes_required_data = bw_add(data->total_display_writes_required_data, data->adjusted_data_buffer_size_in_memory[i]); + data->total_display_writes_required_dram_access_data = bw_add(data->total_display_writes_required_dram_access_data, bw_mul(data->adjusted_data_buffer_size_in_memory[i], bw_ceil2(bw_div(bw_int_to_fixed(vbios->dram_channel_width_in_bits), data->bytes_per_request[i]), bw_int_to_fixed(1)))); + } + } + } + data->total_display_reads_required_data = bw_add(bw_add(data->total_display_reads_required_data, data->cursor_total_data), bw_mul(data->scatter_gather_total_pte_requests, bw_int_to_fixed(64))); + data->total_display_reads_required_dram_access_data = bw_add(bw_add(data->total_display_reads_required_dram_access_data, data->cursor_total_data), bw_mul(data->scatter_gather_total_pte_requests, bw_int_to_fixed(64))); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_mtn(data->v_filter_init[i], bw_int_to_fixed(4))) { + data->src_pixels_for_first_output_pixel[i] = bw_mul(bw_int_to_fixed(4), data->source_width_rounded_up_to_chunks[i]); + } + else { + if (bw_mtn(data->v_filter_init[i], bw_int_to_fixed(2))) { + data->src_pixels_for_first_output_pixel[i] = bw_int_to_fixed(512); + } + else { + data->src_pixels_for_first_output_pixel[i] = bw_int_to_fixed(0); + } + } + data->src_data_for_first_output_pixel[i] = bw_div(bw_mul(bw_mul(data->src_pixels_for_first_output_pixel[i], bw_int_to_fixed(data->bytes_per_pixel[i])), data->bytes_per_request[i]), data->useful_bytes_per_request[i]); + data->src_pixels_for_last_output_pixel[i] = bw_mul(data->source_width_rounded_up_to_chunks[i], bw_max2(bw_ceil2(data->v_filter_init[i], bw_int_to_fixed(dceip->lines_interleaved_into_lb)), bw_mul(bw_ceil2(data->vsr[i], bw_int_to_fixed(dceip->lines_interleaved_into_lb)), data->horizontal_blank_and_chunk_granularity_factor[i]))); + data->src_data_for_last_output_pixel[i] = bw_div(bw_mul(bw_mul(bw_mul(data->source_width_rounded_up_to_chunks[i], bw_max2(bw_ceil2(data->v_filter_init[i], bw_int_to_fixed(dceip->lines_interleaved_into_lb)), data->lines_interleaved_in_mem_access[i])), bw_int_to_fixed(data->bytes_per_pixel[i])), data->bytes_per_request[i]), data->useful_bytes_per_request[i]); + data->active_time[i] = bw_div(bw_div(data->source_width_rounded_up_to_chunks[i], data->hsr[i]), data->pixel_rate[i]); + } + } + for (i = 0; i <= 2; i++) { + for (j = 0; j <= 7; j++) { + data->dmif_burst_time[i][j] = bw_max3(data->dmif_total_page_close_open_time, bw_div(data->total_display_reads_required_dram_access_data, (bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[i]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)))), bw_div(data->total_display_reads_required_data, (bw_mul(bw_mul(sclk[j], vbios->data_return_bus_width), bw_int_to_fixed(bus_efficiency))))); + if (data->d1_display_write_back_dwb_enable == 1) { + data->mcifwr_burst_time[i][j] = bw_max3(data->mcifwr_total_page_close_open_time, bw_div(data->total_display_writes_required_dram_access_data, (bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[i]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_wrchannels)))), bw_div(data->total_display_writes_required_data, (bw_mul(bw_mul(sclk[j], vbios->data_return_bus_width), bw_int_to_fixed(bus_efficiency))))); + } + } + } + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + for (j = 0; j <= 2; j++) { + for (k = 0; k <= 7; k++) { + if (data->enable[i]) { + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + /*time to transfer data from the dmif buffer to the lb. since the mc to dmif transfer time overlaps*/ + /*with the dmif to lb transfer time, only time to transfer the last chunk is considered.*/ + data->dmif_buffer_transfer_time[i] = bw_mul(data->source_width_rounded_up_to_chunks[i], (bw_div(dceip->lb_write_pixels_per_dispclk, (bw_div(vbios->low_voltage_max_dispclk, dceip->display_pipe_throughput_factor))))); + data->line_source_transfer_time[i][j][k] = bw_max2(bw_mul((bw_add(data->total_dmifmc_urgent_latency, data->dmif_burst_time[j][k])), bw_floor2(bw_div(data->src_data_for_first_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1))), bw_sub(bw_add(bw_mul((bw_add(data->total_dmifmc_urgent_latency, data->dmif_burst_time[j][k])), bw_floor2(bw_div(data->src_data_for_last_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1))), data->dmif_buffer_transfer_time[i]), data->active_time[i])); + /*during an mclk switch the requests from the dce ip are stored in the gmc/arb. these requests should be serviced immediately*/ + /*after the mclk switch sequence and not incur an urgent latency penalty. it is assumed that the gmc/arb can hold up to 256 requests*/ + /*per memory channel. if the dce ip is urgent after the mclk switch sequence, all pending requests and subsequent requests should be*/ + /*immediately serviced without a gap in the urgent requests.*/ + /*the latency incurred would be the time to issue the requests and return the data for the first or last output pixel.*/ + if (surface_type[i] == bw_def_graphics) { + switch (data->lb_bpc[i]) { + case 6: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency6_bit_per_component; + break; + case 8: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency8_bit_per_component; + break; + case 10: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency10_bit_per_component; + break; + default: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency12_bit_per_component; + break; + } + if (data->use_alpha[i] == 1) { + data->v_scaler_efficiency = bw_min2(data->v_scaler_efficiency, dceip->alpha_vscaler_efficiency); + } + } + else { + switch (data->lb_bpc[i]) { + case 6: + data->v_scaler_efficiency = dceip->underlay_vscaler_efficiency6_bit_per_component; + break; + case 8: + data->v_scaler_efficiency = dceip->underlay_vscaler_efficiency8_bit_per_component; + break; + case 10: + data->v_scaler_efficiency = dceip->underlay_vscaler_efficiency10_bit_per_component; + break; + default: + data->v_scaler_efficiency = bw_int_to_fixed(3); + break; + } + } + if (dceip->pre_downscaler_enabled && bw_mtn(data->hsr[i], bw_int_to_fixed(1))) { + data->scaler_limits_factor = bw_max2(bw_div(data->v_taps[i], data->v_scaler_efficiency), bw_div(data->source_width_rounded_up_to_chunks[i], data->h_total[i])); + } + else { + data->scaler_limits_factor = bw_max3(bw_int_to_fixed(1), bw_ceil2(bw_div(data->h_taps[i], bw_int_to_fixed(4)), bw_int_to_fixed(1)), bw_mul(data->hsr[i], bw_max2(bw_div(data->v_taps[i], data->v_scaler_efficiency), bw_int_to_fixed(1)))); + } + data->dram_speed_change_line_source_transfer_time[i][j][k] = bw_mul(bw_int_to_fixed(2), bw_max2((bw_add((bw_div(data->src_data_for_first_output_pixel[i], bw_min2(bw_mul(data->bytes_per_request[i], sclk[k]), bw_div(bw_mul(bw_mul(data->bytes_per_request[i], data->pixel_rate[i]), data->scaler_limits_factor), bw_int_to_fixed(2))))), (bw_mul(data->dmif_burst_time[j][k], bw_floor2(bw_div(data->src_data_for_first_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1)))))), (bw_add((bw_div(data->src_data_for_last_output_pixel[i], bw_min2(bw_mul(data->bytes_per_request[i], sclk[k]), bw_div(bw_mul(bw_mul(data->bytes_per_request[i], data->pixel_rate[i]), data->scaler_limits_factor), bw_int_to_fixed(2))))), (bw_sub(bw_mul(data->dmif_burst_time[j][k], bw_floor2(bw_div(data->src_data_for_last_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1))), data->active_time[i])))))); + } + else { + data->line_source_transfer_time[i][j][k] = bw_max2(bw_mul((bw_add(vbios->mcifwrmc_urgent_latency, data->mcifwr_burst_time[j][k])), bw_floor2(bw_div(data->src_data_for_first_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1))), bw_sub(bw_mul((bw_add(vbios->mcifwrmc_urgent_latency, data->mcifwr_burst_time[j][k])), bw_floor2(bw_div(data->src_data_for_last_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1))), data->active_time[i])); + /*during an mclk switch the requests from the dce ip are stored in the gmc/arb. these requests should be serviced immediately*/ + /*after the mclk switch sequence and not incur an urgent latency penalty. it is assumed that the gmc/arb can hold up to 256 requests*/ + /*per memory channel. if the dce ip is urgent after the mclk switch sequence, all pending requests and subsequent requests should be*/ + /*immediately serviced without a gap in the urgent requests.*/ + /*the latency incurred would be the time to issue the requests and return the data for the first or last output pixel.*/ + data->dram_speed_change_line_source_transfer_time[i][j][k] = bw_max2((bw_add((bw_div(data->src_data_for_first_output_pixel[i], bw_min2(bw_mul(data->bytes_per_request[i], sclk[k]), bw_div(bw_mul(data->bytes_per_request[i], vbios->low_voltage_max_dispclk), bw_int_to_fixed(2))))), (bw_mul(data->mcifwr_burst_time[j][k], bw_floor2(bw_div(data->src_data_for_first_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1)))))), (bw_add((bw_div(data->src_data_for_last_output_pixel[i], bw_min2(bw_mul(data->bytes_per_request[i], sclk[k]), bw_div(bw_mul(data->bytes_per_request[i], vbios->low_voltage_max_dispclk), bw_int_to_fixed(2))))), (bw_sub(bw_mul(data->mcifwr_burst_time[j][k], bw_floor2(bw_div(data->src_data_for_last_output_pixel[i], data->adjusted_data_buffer_size_in_memory[i]), bw_int_to_fixed(1))), data->active_time[i]))))); + } + } + } + } + } + /*cpu c-state and p-state change enable*/ + /*for cpu p-state change to be possible for a yclk(pclk) and sclk level the dispclk required has to be enough for the blackout duration*/ + /*for cpu c-state change to be possible for a yclk(pclk) and sclk level the dispclk required has to be enough for the blackout duration and recovery*/ + /*condition for the blackout duration:*/ + /* minimum latency hiding > blackout duration + dmif burst time + line source transfer time*/ + /*condition for the blackout recovery:*/ + /* recovery time > dmif burst time + 2 * urgent latency*/ + /* recovery time > (display bw * blackout duration + (2 * urgent latency + dmif burst time)*dispclk - dmif size )*/ + /* / (dispclk - display bw)*/ + /*the minimum latency hiding is the minimum for all pipes of one screen line time, plus one more line time if doing lb prefetch, plus the dmif data buffer size equivalent in time, minus the urgent latency.*/ + /*the minimum latency hiding is further limited by the cursor. the cursor latency hiding is the number of lines of the cursor buffer, minus one if the downscaling is less than two, or minus three if it is more*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((bw_equ(dceip->stutter_and_dram_clock_state_change_gated_before_cursor, bw_int_to_fixed(0)) && bw_mtn(data->cursor_width_pixels[i], bw_int_to_fixed(0)))) { + if (bw_ltn(data->vsr[i], bw_int_to_fixed(2))) { + data->cursor_latency_hiding[i] = bw_div(bw_div(bw_mul((bw_sub(dceip->cursor_dcp_buffer_lines, bw_int_to_fixed(1))), data->h_total[i]), data->vsr[i]), data->pixel_rate[i]); + } + else { + data->cursor_latency_hiding[i] = bw_div(bw_div(bw_mul((bw_sub(dceip->cursor_dcp_buffer_lines, bw_int_to_fixed(3))), data->h_total[i]), data->vsr[i]), data->pixel_rate[i]); + } + } + else { + data->cursor_latency_hiding[i] = bw_int_to_fixed(9999); + } + } + } + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (dceip->graphics_lb_nodownscaling_multi_line_prefetching == 1 && (bw_equ(data->vsr[i], bw_int_to_fixed(1)) || (bw_leq(data->vsr[i], bw_frc_to_fixed(8, 10)) && bw_leq(data->v_taps[i], bw_int_to_fixed(2)) && data->lb_bpc[i] == 8)) && surface_type[i] == bw_def_graphics) { + data->minimum_latency_hiding[i] = bw_sub(bw_div(bw_mul((bw_div((bw_add(bw_sub(data->lb_partitions[i], bw_int_to_fixed(1)), bw_div(bw_div(data->data_buffer_size[i], bw_int_to_fixed(data->bytes_per_pixel[i])), data->source_width_pixels[i]))), data->vsr[i])), data->h_total[i]), data->pixel_rate[i]), data->total_dmifmc_urgent_latency); + } + else { + data->minimum_latency_hiding[i] = bw_sub(bw_div(bw_mul((bw_div((bw_add(bw_int_to_fixed(1 + data->line_buffer_prefetch[i]), bw_div(bw_div(data->data_buffer_size[i], bw_int_to_fixed(data->bytes_per_pixel[i])), data->source_width_pixels[i]))), data->vsr[i])), data->h_total[i]), data->pixel_rate[i]), data->total_dmifmc_urgent_latency); + } + data->minimum_latency_hiding_with_cursor[i] = bw_min2(data->minimum_latency_hiding[i], data->cursor_latency_hiding[i]); + } + } + for (i = 0; i <= 2; i++) { + for (j = 0; j <= 7; j++) { + data->blackout_duration_margin[i][j] = bw_int_to_fixed(9999); + data->dispclk_required_for_blackout_duration[i][j] = bw_int_to_fixed(0); + data->dispclk_required_for_blackout_recovery[i][j] = bw_int_to_fixed(0); + for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { + if (data->enable[k] && bw_mtn(vbios->blackout_duration, bw_int_to_fixed(0))) { + if (surface_type[k] != bw_def_display_write_back420_luma && surface_type[k] != bw_def_display_write_back420_chroma) { + data->blackout_duration_margin[i][j] = bw_min2(data->blackout_duration_margin[i][j], bw_sub(bw_sub(bw_sub(data->minimum_latency_hiding_with_cursor[k], vbios->blackout_duration), data->dmif_burst_time[i][j]), data->line_source_transfer_time[k][i][j])); + data->dispclk_required_for_blackout_duration[i][j] = bw_max3(data->dispclk_required_for_blackout_duration[i][j], bw_div(bw_div(bw_mul(data->src_pixels_for_first_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_sub(bw_sub(data->minimum_latency_hiding_with_cursor[k], vbios->blackout_duration), data->dmif_burst_time[i][j]))), bw_div(bw_div(bw_mul(data->src_pixels_for_last_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_add(bw_sub(bw_sub(data->minimum_latency_hiding_with_cursor[k], vbios->blackout_duration), data->dmif_burst_time[i][j]), data->active_time[k])))); + if (bw_leq(vbios->maximum_blackout_recovery_time, bw_add(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[i][j]))) { + data->dispclk_required_for_blackout_recovery[i][j] = bw_int_to_fixed(9999); + } + else if (bw_ltn(data->adjusted_data_buffer_size[k], bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), (bw_add(vbios->blackout_duration, bw_add(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[i][j])))))) { + data->dispclk_required_for_blackout_recovery[i][j] = bw_max2(data->dispclk_required_for_blackout_recovery[i][j], bw_div(bw_mul(bw_div(bw_div((bw_sub(bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), (bw_add(vbios->blackout_duration, vbios->maximum_blackout_recovery_time))), data->adjusted_data_buffer_size[k])), bw_int_to_fixed(data->bytes_per_pixel[k])), (bw_sub(vbios->maximum_blackout_recovery_time, bw_sub(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[i][j])))), data->latency_hiding_lines[k]), data->lines_interleaved_in_mem_access[k])); + } + } + else { + data->blackout_duration_margin[i][j] = bw_min2(data->blackout_duration_margin[i][j], bw_sub(bw_sub(bw_sub(bw_sub(data->minimum_latency_hiding_with_cursor[k], vbios->blackout_duration), data->dmif_burst_time[i][j]), data->mcifwr_burst_time[i][j]), data->line_source_transfer_time[k][i][j])); + data->dispclk_required_for_blackout_duration[i][j] = bw_max3(data->dispclk_required_for_blackout_duration[i][j], bw_div(bw_div(bw_mul(data->src_pixels_for_first_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_sub(bw_sub(bw_sub(data->minimum_latency_hiding_with_cursor[k], vbios->blackout_duration), data->dmif_burst_time[i][j]), data->mcifwr_burst_time[i][j]))), bw_div(bw_div(bw_mul(data->src_pixels_for_last_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_add(bw_sub(bw_sub(bw_sub(data->minimum_latency_hiding_with_cursor[k], vbios->blackout_duration), data->dmif_burst_time[i][j]), data->mcifwr_burst_time[i][j]), data->active_time[k])))); + if (bw_ltn(vbios->maximum_blackout_recovery_time, bw_add(bw_add(bw_mul(bw_int_to_fixed(2), vbios->mcifwrmc_urgent_latency), data->dmif_burst_time[i][j]), data->mcifwr_burst_time[i][j]))) { + data->dispclk_required_for_blackout_recovery[i][j] = bw_int_to_fixed(9999); + } + else if (bw_ltn(data->adjusted_data_buffer_size[k], bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), (bw_add(vbios->blackout_duration, bw_add(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[i][j])))))) { + data->dispclk_required_for_blackout_recovery[i][j] = bw_max2(data->dispclk_required_for_blackout_recovery[i][j], bw_div(bw_mul(bw_div(bw_div((bw_sub(bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), (bw_add(vbios->blackout_duration, vbios->maximum_blackout_recovery_time))), data->adjusted_data_buffer_size[k])), bw_int_to_fixed(data->bytes_per_pixel[k])), (bw_sub(vbios->maximum_blackout_recovery_time, (bw_add(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[i][j]))))), data->latency_hiding_lines[k]), data->lines_interleaved_in_mem_access[k])); + } + } + } + } + } + } + if (bw_mtn(data->blackout_duration_margin[high][s_high], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[high][s_high], vbios->high_voltage_max_dispclk)) { + data->cpup_state_change_enable = bw_def_yes; + if (bw_ltn(data->dispclk_required_for_blackout_recovery[high][s_high], vbios->high_voltage_max_dispclk)) { + data->cpuc_state_change_enable = bw_def_yes; + } + else { + data->cpuc_state_change_enable = bw_def_no; + } + } + else { + data->cpup_state_change_enable = bw_def_no; + data->cpuc_state_change_enable = bw_def_no; + } + /*nb p-state change enable*/ + /*for dram speed/p-state change to be possible for a yclk(pclk) and sclk level there has to be positive margin and the dispclk required has to be*/ + /*below the maximum.*/ + /*the dram speed/p-state change margin is the minimum for all surfaces of the maximum latency hiding minus the dram speed/p-state change latency,*/ + /*minus the dmif burst time, minus the source line transfer time*/ + /*the maximum latency hiding is the minimum latency hiding plus one source line used for de-tiling in the line buffer, plus half the urgent latency*/ + /*if stutter and dram clock state change are gated before cursor then the cursor latency hiding does not limit stutter or dram clock state change*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((dceip->graphics_lb_nodownscaling_multi_line_prefetching == 1)) { + data->maximum_latency_hiding[i] = bw_add(data->minimum_latency_hiding[i], bw_mul(bw_frc_to_fixed(8, 10), data->total_dmifmc_urgent_latency)); + } + else { + /*maximum_latency_hiding(i) = minimum_latency_hiding(i) + 1 / vsr(i) * h_total(i) / pixel_rate(i) + 0.5 * total_dmifmc_urgent_latency*/ + data->maximum_latency_hiding[i] = bw_add(data->minimum_latency_hiding[i], bw_mul(bw_frc_to_fixed(8, 10), data->total_dmifmc_urgent_latency)); + } + data->maximum_latency_hiding_with_cursor[i] = bw_min2(data->maximum_latency_hiding[i], data->cursor_latency_hiding[i]); + } + } + /*initialize variables*/ + number_of_displays_enabled = 0; + number_of_displays_enabled_with_margin = 0; + for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { + if (data->enable[k]) { + number_of_displays_enabled = number_of_displays_enabled + 1; + } + data->display_pstate_change_enable[k] = 0; + } + for (i = 0; i <= 2; i++) { + for (j = 0; j <= 7; j++) { + data->min_dram_speed_change_margin[i][j] = bw_int_to_fixed(9999); + data->dram_speed_change_margin = bw_int_to_fixed(9999); + data->dispclk_required_for_dram_speed_change[i][j] = bw_int_to_fixed(0); + data->num_displays_with_margin[i][j] = 0; + for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { + if (data->enable[k]) { + if (surface_type[k] != bw_def_display_write_back420_luma && surface_type[k] != bw_def_display_write_back420_chroma) { + data->dram_speed_change_margin = bw_sub(bw_sub(bw_sub(data->maximum_latency_hiding_with_cursor[k], vbios->nbp_state_change_latency), data->dmif_burst_time[i][j]), data->dram_speed_change_line_source_transfer_time[k][i][j]); + if ((bw_mtn(data->dram_speed_change_margin, bw_int_to_fixed(0)) && bw_ltn(data->dram_speed_change_margin, bw_int_to_fixed(9999)))) { + /*determine the minimum dram clock change margin for each set of clock frequencies*/ + data->min_dram_speed_change_margin[i][j] = bw_min2(data->min_dram_speed_change_margin[i][j], data->dram_speed_change_margin); + /*compute the maximum clock frequuency required for the dram clock change at each set of clock frequencies*/ + data->dispclk_required_for_dram_speed_change[i][j] = bw_max3(data->dispclk_required_for_dram_speed_change[i][j], bw_div(bw_div(bw_mul(data->src_pixels_for_first_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_sub(bw_sub(bw_sub(data->maximum_latency_hiding_with_cursor[k], vbios->nbp_state_change_latency), data->dmif_burst_time[i][j]), data->dram_speed_change_line_source_transfer_time[k][i][j]))), bw_div(bw_div(bw_mul(data->src_pixels_for_last_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_add(bw_sub(bw_sub(bw_sub(data->maximum_latency_hiding_with_cursor[k], vbios->nbp_state_change_latency), data->dmif_burst_time[i][j]), data->dram_speed_change_line_source_transfer_time[k][i][j]), data->active_time[k])))); + if ((bw_ltn(data->dispclk_required_for_dram_speed_change[i][j], vbios->high_voltage_max_dispclk))) { + data->display_pstate_change_enable[k] = 1; + data->num_displays_with_margin[i][j] = data->num_displays_with_margin[i][j] + 1; + } + } + } + else { + data->dram_speed_change_margin = bw_sub(bw_sub(bw_sub(bw_sub(data->maximum_latency_hiding_with_cursor[k], vbios->nbp_state_change_latency), data->dmif_burst_time[i][j]), data->mcifwr_burst_time[i][j]), data->dram_speed_change_line_source_transfer_time[k][i][j]); + if ((bw_mtn(data->dram_speed_change_margin, bw_int_to_fixed(0)) && bw_ltn(data->dram_speed_change_margin, bw_int_to_fixed(9999)))) { + /*determine the minimum dram clock change margin for each display pipe*/ + data->min_dram_speed_change_margin[i][j] = bw_min2(data->min_dram_speed_change_margin[i][j], data->dram_speed_change_margin); + /*compute the maximum clock frequuency required for the dram clock change at each set of clock frequencies*/ + data->dispclk_required_for_dram_speed_change[i][j] = bw_max3(data->dispclk_required_for_dram_speed_change[i][j], bw_div(bw_div(bw_mul(data->src_pixels_for_first_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_sub(bw_sub(bw_sub(bw_sub(data->maximum_latency_hiding_with_cursor[k], vbios->nbp_state_change_latency), data->dmif_burst_time[i][j]), data->dram_speed_change_line_source_transfer_time[k][i][j]), data->mcifwr_burst_time[i][j]))), bw_div(bw_div(bw_mul(data->src_pixels_for_last_output_pixel[k], dceip->display_pipe_throughput_factor), dceip->lb_write_pixels_per_dispclk), (bw_add(bw_sub(bw_sub(bw_sub(bw_sub(data->maximum_latency_hiding_with_cursor[k], vbios->nbp_state_change_latency), data->dmif_burst_time[i][j]), data->dram_speed_change_line_source_transfer_time[k][i][j]), data->mcifwr_burst_time[i][j]), data->active_time[k])))); + if ((bw_ltn(data->dispclk_required_for_dram_speed_change[i][j], vbios->high_voltage_max_dispclk))) { + data->display_pstate_change_enable[k] = 1; + data->num_displays_with_margin[i][j] = data->num_displays_with_margin[i][j] + 1; + } + } + } + } + } + } + } + /*determine the number of displays with margin to switch in the v_active region*/ + for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { + if ((data->enable[k] == 1 && data->display_pstate_change_enable[k] == 1)) { + number_of_displays_enabled_with_margin = number_of_displays_enabled_with_margin + 1; + } + } + /*determine the number of displays that don't have any dram clock change margin, but*/ + /*have the same resolution. these displays can switch in a common vblank region if*/ + /*their frames are aligned.*/ + data->min_vblank_dram_speed_change_margin = bw_int_to_fixed(9999); + for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { + if (data->enable[k]) { + if (surface_type[k] != bw_def_display_write_back420_luma && surface_type[k] != bw_def_display_write_back420_chroma) { + data->v_blank_dram_speed_change_margin[k] = bw_sub(bw_sub(bw_sub(bw_div(bw_mul((bw_sub(data->v_total[k], bw_sub(bw_div(data->src_height[k], data->v_scale_ratio[k]), bw_int_to_fixed(4)))), data->h_total[k]), data->pixel_rate[k]), vbios->nbp_state_change_latency), data->dmif_burst_time[low][s_low]), data->dram_speed_change_line_source_transfer_time[k][low][s_low]); + data->min_vblank_dram_speed_change_margin = bw_min2(data->min_vblank_dram_speed_change_margin, data->v_blank_dram_speed_change_margin[k]); + } + else { + data->v_blank_dram_speed_change_margin[k] = bw_sub(bw_sub(bw_sub(bw_sub(bw_div(bw_mul((bw_sub(data->v_total[k], bw_sub(bw_div(data->src_height[k], data->v_scale_ratio[k]), bw_int_to_fixed(4)))), data->h_total[k]), data->pixel_rate[k]), vbios->nbp_state_change_latency), data->dmif_burst_time[low][s_low]), data->mcifwr_burst_time[low][s_low]), data->dram_speed_change_line_source_transfer_time[k][low][s_low]); + data->min_vblank_dram_speed_change_margin = bw_min2(data->min_vblank_dram_speed_change_margin, data->v_blank_dram_speed_change_margin[k]); + } + } + } + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + data->displays_with_same_mode[i] = bw_int_to_fixed(0); + if (data->enable[i] == 1 && data->display_pstate_change_enable[i] == 0 && bw_mtn(data->v_blank_dram_speed_change_margin[i], bw_int_to_fixed(0))) { + for (j = 0; j <= maximum_number_of_surfaces - 1; j++) { + if ((data->enable[j] == 1 && bw_equ(data->source_width_rounded_up_to_chunks[i], data->source_width_rounded_up_to_chunks[j]) && bw_equ(data->source_height_rounded_up_to_chunks[i], data->source_height_rounded_up_to_chunks[j]) && bw_equ(data->vsr[i], data->vsr[j]) && bw_equ(data->hsr[i], data->hsr[j]) && bw_equ(data->pixel_rate[i], data->pixel_rate[j]))) { + data->displays_with_same_mode[i] = bw_add(data->displays_with_same_mode[i], bw_int_to_fixed(1)); + } + } + } + } + /*compute the maximum number of aligned displays with no margin*/ + number_of_aligned_displays_with_no_margin = 0; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + number_of_aligned_displays_with_no_margin = bw_fixed_to_int(bw_max2(bw_int_to_fixed(number_of_aligned_displays_with_no_margin), data->displays_with_same_mode[i])); + } + /*dram clock change is possible, if all displays have positive margin except for one display or a group of*/ + /*aligned displays with the same timing.*/ + /*the display(s) with the negative margin can be switched in the v_blank region while the other*/ + /*displays are in v_blank or v_active.*/ + if ((number_of_displays_enabled_with_margin + number_of_aligned_displays_with_no_margin == number_of_displays_enabled && bw_mtn(data->min_dram_speed_change_margin[high][s_high], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[high][s_high], bw_int_to_fixed(9999)) && bw_ltn(data->dispclk_required_for_dram_speed_change[high][s_high], vbios->high_voltage_max_dispclk))) { + data->nbp_state_change_enable = bw_def_yes; + } + else { + data->nbp_state_change_enable = bw_def_no; + } + /*dram clock change is possible only in vblank if all displays are aligned and have no margin*/ + if ((number_of_aligned_displays_with_no_margin == number_of_displays_enabled)) { + nbp_state_change_enable_blank = bw_def_yes; + } + else { + nbp_state_change_enable_blank = bw_def_no; + } + /*required yclk(pclk)*/ + /*yclk requirement only makes sense if the dmif and mcifwr data total page close-open time is less than the time for data transfer and the total pte requests fit in the scatter-gather saw queque size*/ + /*if that is the case, the yclk requirement is the maximum of the ones required by dmif and mcifwr, and the high/low yclk(pclk) is chosen accordingly*/ + /*high yclk(pclk) has to be selected when dram speed/p-state change is not possible.*/ + data->min_cursor_memory_interface_buffer_size_in_time = bw_int_to_fixed(9999); + /* number of cursor lines stored in the cursor data return buffer*/ + num_cursor_lines = 0; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_mtn(data->cursor_width_pixels[i], bw_int_to_fixed(0))) { + /*compute number of cursor lines stored in data return buffer*/ + if (bw_leq(data->cursor_width_pixels[i], bw_int_to_fixed(64)) && dceip->large_cursor == 1) { + num_cursor_lines = 4; + } + else { + num_cursor_lines = 2; + } + data->min_cursor_memory_interface_buffer_size_in_time = bw_min2(data->min_cursor_memory_interface_buffer_size_in_time, bw_div(bw_mul(bw_div(bw_int_to_fixed(num_cursor_lines), data->vsr[i]), data->h_total[i]), data->pixel_rate[i])); + } + } + } + /*compute minimum time to read one chunk from the dmif buffer*/ + if ((number_of_displays_enabled > 2)) { + data->chunk_request_delay = 0; + } + else { + data->chunk_request_delay = bw_fixed_to_int(bw_div(bw_int_to_fixed(512), vbios->high_voltage_max_dispclk)); + } + data->min_read_buffer_size_in_time = bw_min2(data->min_cursor_memory_interface_buffer_size_in_time, data->min_dmif_size_in_time); + data->display_reads_time_for_data_transfer = bw_sub(bw_sub(data->min_read_buffer_size_in_time, data->total_dmifmc_urgent_latency), bw_int_to_fixed(data->chunk_request_delay)); + data->display_writes_time_for_data_transfer = bw_sub(data->min_mcifwr_size_in_time, vbios->mcifwrmc_urgent_latency); + data->dmif_required_dram_bandwidth = bw_div(data->total_display_reads_required_dram_access_data, data->display_reads_time_for_data_transfer); + data->mcifwr_required_dram_bandwidth = bw_div(data->total_display_writes_required_dram_access_data, data->display_writes_time_for_data_transfer); + data->required_dmifmc_urgent_latency_for_page_close_open = bw_div((bw_sub(data->min_read_buffer_size_in_time, data->dmif_total_page_close_open_time)), data->total_dmifmc_urgent_trips); + data->required_mcifmcwr_urgent_latency = bw_sub(data->min_mcifwr_size_in_time, data->mcifwr_total_page_close_open_time); + if (bw_mtn(data->scatter_gather_total_pte_requests, dceip->maximum_total_outstanding_pte_requests_allowed_by_saw)) { + data->required_dram_bandwidth_gbyte_per_second = bw_int_to_fixed(9999); + yclk_message = bw_def_exceeded_allowed_outstanding_pte_req_queue_size; + data->y_clk_level = high; + data->dram_bandwidth = bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[high]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)); + } + else if (bw_mtn(vbios->dmifmc_urgent_latency, data->required_dmifmc_urgent_latency_for_page_close_open) || bw_mtn(vbios->mcifwrmc_urgent_latency, data->required_mcifmcwr_urgent_latency)) { + data->required_dram_bandwidth_gbyte_per_second = bw_int_to_fixed(9999); + yclk_message = bw_def_exceeded_allowed_page_close_open; + data->y_clk_level = high; + data->dram_bandwidth = bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[high]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)); + } + else { + data->required_dram_bandwidth_gbyte_per_second = bw_div(bw_max2(data->dmif_required_dram_bandwidth, data->mcifwr_required_dram_bandwidth), bw_int_to_fixed(1000)); + if (bw_ltn(bw_mul(data->required_dram_bandwidth_gbyte_per_second, bw_int_to_fixed(1000)), bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[low]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels))) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[low][s_high], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[low][s_high], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[low][s_high], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[low][s_high], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[low][s_high], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[low][s_high], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[low][s_high], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[low][s_high], vbios->high_voltage_max_dispclk) && data->num_displays_with_margin[low][s_high] == number_of_displays_enabled_with_margin))) { + yclk_message = bw_fixed_to_int(vbios->low_yclk); + data->y_clk_level = low; + data->dram_bandwidth = bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[low]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)); + } + else if (bw_ltn(bw_mul(data->required_dram_bandwidth_gbyte_per_second, bw_int_to_fixed(1000)), bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[mid]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels))) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[mid][s_high], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[mid][s_high], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[mid][s_high], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[mid][s_high], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[mid][s_high], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[mid][s_high], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[mid][s_high], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[mid][s_high], vbios->high_voltage_max_dispclk) && data->num_displays_with_margin[mid][s_high] == number_of_displays_enabled_with_margin))) { + yclk_message = bw_fixed_to_int(vbios->mid_yclk); + data->y_clk_level = mid; + data->dram_bandwidth = bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[mid]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)); + } + else if (bw_ltn(bw_mul(data->required_dram_bandwidth_gbyte_per_second, bw_int_to_fixed(1000)), bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[high]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)))) { + yclk_message = bw_fixed_to_int(vbios->high_yclk); + data->y_clk_level = high; + data->dram_bandwidth = bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[high]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)); + } + else { + yclk_message = bw_def_exceeded_allowed_maximum_bw; + data->y_clk_level = high; + data->dram_bandwidth = bw_mul(bw_div(bw_mul(bw_mul(data->dram_efficiency, yclk[high]), bw_int_to_fixed(vbios->dram_channel_width_in_bits)), bw_int_to_fixed(8)), bw_int_to_fixed(data->number_of_dram_channels)); + } + } + /*required sclk*/ + /*sclk requirement only makes sense if the total pte requests fit in the scatter-gather saw queque size*/ + /*if that is the case, the sclk requirement is the maximum of the ones required by dmif and mcifwr, and the high/mid/low sclk is chosen accordingly, unless that choice results in foresaking dram speed/nb p-state change.*/ + /*the dmif and mcifwr sclk required is the one that allows the transfer of all pipe's data buffer size through the sclk bus in the time for data transfer*/ + /*for dmif, pte and cursor requests have to be included.*/ + data->dmif_required_sclk = bw_div(bw_div(data->total_display_reads_required_data, data->display_reads_time_for_data_transfer), (bw_mul(vbios->data_return_bus_width, bw_int_to_fixed(bus_efficiency)))); + data->mcifwr_required_sclk = bw_div(bw_div(data->total_display_writes_required_data, data->display_writes_time_for_data_transfer), (bw_mul(vbios->data_return_bus_width, bw_int_to_fixed(bus_efficiency)))); + if (bw_mtn(data->scatter_gather_total_pte_requests, dceip->maximum_total_outstanding_pte_requests_allowed_by_saw)) { + data->required_sclk = bw_int_to_fixed(9999); + sclk_message = bw_def_exceeded_allowed_outstanding_pte_req_queue_size; + data->sclk_level = s_high; + } + else if (bw_mtn(vbios->dmifmc_urgent_latency, data->required_dmifmc_urgent_latency_for_page_close_open) || bw_mtn(vbios->mcifwrmc_urgent_latency, data->required_mcifmcwr_urgent_latency)) { + data->required_sclk = bw_int_to_fixed(9999); + sclk_message = bw_def_exceeded_allowed_page_close_open; + data->sclk_level = s_high; + } + else { + data->required_sclk = bw_max2(data->dmif_required_sclk, data->mcifwr_required_sclk); + if (bw_ltn(data->required_sclk, sclk[s_low]) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_low], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_low], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_low], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_low], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[data->y_clk_level][s_low], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[data->y_clk_level][s_low], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[data->y_clk_level][s_low], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[data->y_clk_level][s_low], vbios->low_voltage_max_dispclk) && data->num_displays_with_margin[data->y_clk_level][s_low] == number_of_displays_enabled_with_margin))) { + sclk_message = bw_def_low; + data->sclk_level = s_low; + data->required_sclk = vbios->low_sclk; + } + else if (bw_ltn(data->required_sclk, sclk[s_mid1]) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid1], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid1], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid1], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid1], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[data->y_clk_level][s_mid1], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid1], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid1], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[data->y_clk_level][s_mid1], vbios->mid_voltage_max_dispclk) && data->num_displays_with_margin[data->y_clk_level][s_mid1] == number_of_displays_enabled_with_margin))) { + sclk_message = bw_def_mid; + data->sclk_level = s_mid1; + data->required_sclk = vbios->mid1_sclk; + } + else if (bw_ltn(data->required_sclk, sclk[s_mid2]) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid2], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid2], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid2], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid2], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[data->y_clk_level][s_mid2], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid2], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid2], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[data->y_clk_level][s_mid2], vbios->mid_voltage_max_dispclk) && data->num_displays_with_margin[data->y_clk_level][s_mid2] == number_of_displays_enabled_with_margin))) { + sclk_message = bw_def_mid; + data->sclk_level = s_mid2; + data->required_sclk = vbios->mid2_sclk; + } + else if (bw_ltn(data->required_sclk, sclk[s_mid3]) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid3], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid3], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid3], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid3], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[data->y_clk_level][s_mid3], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid3], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid3], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[data->y_clk_level][s_mid3], vbios->mid_voltage_max_dispclk) && data->num_displays_with_margin[data->y_clk_level][s_mid3] == number_of_displays_enabled_with_margin))) { + sclk_message = bw_def_mid; + data->sclk_level = s_mid3; + data->required_sclk = vbios->mid3_sclk; + } + else if (bw_ltn(data->required_sclk, sclk[s_mid4]) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid4], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid4], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid4], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid4], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[data->y_clk_level][s_mid4], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid4], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid4], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[data->y_clk_level][s_mid4], vbios->mid_voltage_max_dispclk) && data->num_displays_with_margin[data->y_clk_level][s_mid4] == number_of_displays_enabled_with_margin))) { + sclk_message = bw_def_mid; + data->sclk_level = s_mid4; + data->required_sclk = vbios->mid4_sclk; + } + else if (bw_ltn(data->required_sclk, sclk[s_mid5]) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid5], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid5], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid5], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid5], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[data->y_clk_level][s_mid5], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid5], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid5], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[data->y_clk_level][s_mid5], vbios->mid_voltage_max_dispclk) && data->num_displays_with_margin[data->y_clk_level][s_mid5] == number_of_displays_enabled_with_margin))) { + sclk_message = bw_def_mid; + data->sclk_level = s_mid5; + data->required_sclk = vbios->mid5_sclk; + } + else if (bw_ltn(data->required_sclk, sclk[s_mid6]) && (data->cpup_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid6], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid6], vbios->high_voltage_max_dispclk))) && (data->cpuc_state_change_enable == bw_def_no || (bw_mtn(data->blackout_duration_margin[data->y_clk_level][s_mid6], bw_int_to_fixed(0)) && bw_ltn(data->dispclk_required_for_blackout_duration[data->y_clk_level][s_mid6], vbios->high_voltage_max_dispclk) && bw_ltn(data->dispclk_required_for_blackout_recovery[data->y_clk_level][s_mid6], vbios->high_voltage_max_dispclk))) && (data->nbp_state_change_enable == bw_def_no || (bw_mtn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid6], bw_int_to_fixed(0)) && bw_ltn(data->min_dram_speed_change_margin[data->y_clk_level][s_mid6], bw_int_to_fixed(9999)) && bw_leq(data->dispclk_required_for_dram_speed_change[data->y_clk_level][s_mid6], vbios->high_voltage_max_dispclk) && data->num_displays_with_margin[data->y_clk_level][s_mid6] == number_of_displays_enabled_with_margin))) { + sclk_message = bw_def_mid; + data->sclk_level = s_mid6; + data->required_sclk = vbios->mid6_sclk; + } + else if (bw_ltn(data->required_sclk, sclk[s_high])) { + sclk_message = bw_def_high; + data->sclk_level = s_high; + data->required_sclk = vbios->high_sclk; + } + else { + sclk_message = bw_def_exceeded_allowed_maximum_sclk; + data->sclk_level = s_high; + /*required_sclk = high_sclk*/ + } + } + /*dispclk*/ + /*if dispclk is set to the maximum, ramping is not required. dispclk required without ramping is less than the dispclk required with ramping.*/ + /*if dispclk required without ramping is more than the maximum dispclk, that is the dispclk required, and the mode is not supported*/ + /*if that does not happen, but dispclk required with ramping is more than the maximum dispclk, dispclk required is just the maximum dispclk*/ + /*if that does not happen either, dispclk required is the dispclk required with ramping.*/ + /*dispclk required without ramping is the maximum of the one required for display pipe pixel throughput, for scaler throughput, for total read request thrrougput and for dram/np p-state change if enabled.*/ + /*the display pipe pixel throughput is the maximum of lines in per line out in the beginning of the frame and lines in per line out in the middle of the frame multiplied by the horizontal blank and chunk granularity factor, altogether multiplied by the ratio of the source width to the line time, divided by the line buffer pixels per dispclk throughput, and multiplied by the display pipe throughput factor.*/ + /*the horizontal blank and chunk granularity factor is the ratio of the line time divided by the line time minus half the horizontal blank and chunk time. it applies when the lines in per line out is not 2 or 4.*/ + /*the dispclk required for scaler throughput is the product of the pixel rate and the scaling limits factor.*/ + /*the dispclk required for total read request throughput is the product of the peak request-per-second bandwidth and the dispclk cycles per request, divided by the request efficiency.*/ + /*for the dispclk required with ramping, instead of multiplying just the pipe throughput by the display pipe throughput factor, we multiply the scaler and pipe throughput by the ramping factor.*/ + /*the scaling limits factor is the product of the horizontal scale ratio, and the ratio of the vertical taps divided by the scaler efficiency clamped to at least 1.*/ + /*the scaling limits factor itself it also clamped to at least 1*/ + /*if doing downscaling with the pre-downscaler enabled, the horizontal scale ratio should not be considered above (use "1")*/ + data->downspread_factor = bw_add(bw_int_to_fixed(1), bw_div(vbios->down_spread_percentage, bw_int_to_fixed(100))); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (surface_type[i] == bw_def_graphics) { + switch (data->lb_bpc[i]) { + case 6: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency6_bit_per_component; + break; + case 8: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency8_bit_per_component; + break; + case 10: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency10_bit_per_component; + break; + default: + data->v_scaler_efficiency = dceip->graphics_vscaler_efficiency12_bit_per_component; + break; + } + if (data->use_alpha[i] == 1) { + data->v_scaler_efficiency = bw_min2(data->v_scaler_efficiency, dceip->alpha_vscaler_efficiency); + } + } + else { + switch (data->lb_bpc[i]) { + case 6: + data->v_scaler_efficiency = dceip->underlay_vscaler_efficiency6_bit_per_component; + break; + case 8: + data->v_scaler_efficiency = dceip->underlay_vscaler_efficiency8_bit_per_component; + break; + case 10: + data->v_scaler_efficiency = dceip->underlay_vscaler_efficiency10_bit_per_component; + break; + default: + data->v_scaler_efficiency = dceip->underlay_vscaler_efficiency12_bit_per_component; + break; + } + } + if (dceip->pre_downscaler_enabled && bw_mtn(data->hsr[i], bw_int_to_fixed(1))) { + data->scaler_limits_factor = bw_max2(bw_div(data->v_taps[i], data->v_scaler_efficiency), bw_div(data->source_width_rounded_up_to_chunks[i], data->h_total[i])); + } + else { + data->scaler_limits_factor = bw_max3(bw_int_to_fixed(1), bw_ceil2(bw_div(data->h_taps[i], bw_int_to_fixed(4)), bw_int_to_fixed(1)), bw_mul(data->hsr[i], bw_max2(bw_div(data->v_taps[i], data->v_scaler_efficiency), bw_int_to_fixed(1)))); + } + data->display_pipe_pixel_throughput = bw_div(bw_div(bw_mul(bw_max2(data->lb_lines_in_per_line_out_in_beginning_of_frame[i], bw_mul(data->lb_lines_in_per_line_out_in_middle_of_frame[i], data->horizontal_blank_and_chunk_granularity_factor[i])), data->source_width_rounded_up_to_chunks[i]), (bw_div(data->h_total[i], data->pixel_rate[i]))), dceip->lb_write_pixels_per_dispclk); + data->dispclk_required_without_ramping[i] = bw_mul(data->downspread_factor, bw_max2(bw_mul(data->pixel_rate[i], data->scaler_limits_factor), bw_mul(dceip->display_pipe_throughput_factor, data->display_pipe_pixel_throughput))); + data->dispclk_required_with_ramping[i] = bw_mul(dceip->dispclk_ramping_factor, bw_max2(bw_mul(data->pixel_rate[i], data->scaler_limits_factor), data->display_pipe_pixel_throughput)); + } + } + data->total_dispclk_required_with_ramping = bw_int_to_fixed(0); + data->total_dispclk_required_without_ramping = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_ltn(data->total_dispclk_required_with_ramping, data->dispclk_required_with_ramping[i])) { + data->total_dispclk_required_with_ramping = data->dispclk_required_with_ramping[i]; + } + if (bw_ltn(data->total_dispclk_required_without_ramping, data->dispclk_required_without_ramping[i])) { + data->total_dispclk_required_without_ramping = data->dispclk_required_without_ramping[i]; + } + } + } + data->total_read_request_bandwidth = bw_int_to_fixed(0); + data->total_write_request_bandwidth = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + data->total_read_request_bandwidth = bw_add(data->total_read_request_bandwidth, data->request_bandwidth[i]); + } + else { + data->total_write_request_bandwidth = bw_add(data->total_write_request_bandwidth, data->request_bandwidth[i]); + } + } + } + data->dispclk_required_for_total_read_request_bandwidth = bw_div(bw_mul(data->total_read_request_bandwidth, dceip->dispclk_per_request), dceip->request_efficiency); + data->total_dispclk_required_with_ramping_with_request_bandwidth = bw_max2(data->total_dispclk_required_with_ramping, data->dispclk_required_for_total_read_request_bandwidth); + data->total_dispclk_required_without_ramping_with_request_bandwidth = bw_max2(data->total_dispclk_required_without_ramping, data->dispclk_required_for_total_read_request_bandwidth); + if (data->cpuc_state_change_enable == bw_def_yes) { + data->total_dispclk_required_with_ramping_with_request_bandwidth = bw_max3(data->total_dispclk_required_with_ramping_with_request_bandwidth, data->dispclk_required_for_blackout_duration[data->y_clk_level][data->sclk_level], data->dispclk_required_for_blackout_recovery[data->y_clk_level][data->sclk_level]); + data->total_dispclk_required_without_ramping_with_request_bandwidth = bw_max3(data->total_dispclk_required_without_ramping_with_request_bandwidth, data->dispclk_required_for_blackout_duration[data->y_clk_level][data->sclk_level], data->dispclk_required_for_blackout_recovery[data->y_clk_level][data->sclk_level]); + } + if (data->cpup_state_change_enable == bw_def_yes) { + data->total_dispclk_required_with_ramping_with_request_bandwidth = bw_max2(data->total_dispclk_required_with_ramping_with_request_bandwidth, data->dispclk_required_for_blackout_duration[data->y_clk_level][data->sclk_level]); + data->total_dispclk_required_without_ramping_with_request_bandwidth = bw_max2(data->total_dispclk_required_without_ramping_with_request_bandwidth, data->dispclk_required_for_blackout_duration[data->y_clk_level][data->sclk_level]); + } + if (data->nbp_state_change_enable == bw_def_yes) { + data->total_dispclk_required_with_ramping_with_request_bandwidth = bw_max2(data->total_dispclk_required_with_ramping_with_request_bandwidth, data->dispclk_required_for_dram_speed_change[data->y_clk_level][data->sclk_level]); + data->total_dispclk_required_without_ramping_with_request_bandwidth = bw_max2(data->total_dispclk_required_without_ramping_with_request_bandwidth, data->dispclk_required_for_dram_speed_change[data->y_clk_level][data->sclk_level]); + } + if (bw_ltn(data->total_dispclk_required_with_ramping_with_request_bandwidth, vbios->high_voltage_max_dispclk)) { + data->dispclk = data->total_dispclk_required_with_ramping_with_request_bandwidth; + } + else if (bw_ltn(data->total_dispclk_required_without_ramping_with_request_bandwidth, vbios->high_voltage_max_dispclk)) { + data->dispclk = vbios->high_voltage_max_dispclk; + } + else { + data->dispclk = data->total_dispclk_required_without_ramping_with_request_bandwidth; + } + /* required core voltage*/ + /* the core voltage required is low if sclk, yclk(pclk)and dispclk are within the low limits*/ + /* otherwise, the core voltage required is medium if yclk (pclk) is within the low limit and sclk and dispclk are within the medium limit*/ + /* otherwise, the core voltage required is high if the three clocks are within the high limits*/ + /* otherwise, or if the mode is not supported, core voltage requirement is not applicable*/ + if (pipe_check == bw_def_notok) { + voltage = bw_def_na; + } + else if (mode_check == bw_def_notok) { + voltage = bw_def_notok; + } + else if (bw_equ(bw_int_to_fixed(yclk_message), vbios->low_yclk) && sclk_message == bw_def_low && bw_ltn(data->dispclk, vbios->low_voltage_max_dispclk)) { + voltage = bw_def_0_72; + } + else if ((bw_equ(bw_int_to_fixed(yclk_message), vbios->low_yclk) || bw_equ(bw_int_to_fixed(yclk_message), vbios->mid_yclk)) && (sclk_message == bw_def_low || sclk_message == bw_def_mid) && bw_ltn(data->dispclk, vbios->mid_voltage_max_dispclk)) { + voltage = bw_def_0_8; + } + else if ((bw_equ(bw_int_to_fixed(yclk_message), vbios->low_yclk) || bw_equ(bw_int_to_fixed(yclk_message), vbios->mid_yclk) || bw_equ(bw_int_to_fixed(yclk_message), vbios->high_yclk)) && (sclk_message == bw_def_low || sclk_message == bw_def_mid || sclk_message == bw_def_high) && bw_leq(data->dispclk, vbios->high_voltage_max_dispclk)) { + if ((data->nbp_state_change_enable == bw_def_no && nbp_state_change_enable_blank == bw_def_no)) { + voltage = bw_def_high_no_nbp_state_change; + } + else { + voltage = bw_def_0_9; + } + } + else { + voltage = bw_def_notok; + } + if (voltage == bw_def_0_72) { + data->max_phyclk = vbios->low_voltage_max_phyclk; + } + else if (voltage == bw_def_0_8) { + data->max_phyclk = vbios->mid_voltage_max_phyclk; + } + else { + data->max_phyclk = vbios->high_voltage_max_phyclk; + } + /*required blackout recovery time*/ + data->blackout_recovery_time = bw_int_to_fixed(0); + for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { + if (data->enable[k] && bw_mtn(vbios->blackout_duration, bw_int_to_fixed(0)) && data->cpup_state_change_enable == bw_def_yes) { + if (surface_type[k] != bw_def_display_write_back420_luma && surface_type[k] != bw_def_display_write_back420_chroma) { + data->blackout_recovery_time = bw_max2(data->blackout_recovery_time, bw_add(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[data->y_clk_level][data->sclk_level])); + if (bw_ltn(data->adjusted_data_buffer_size[k], bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), (bw_add(vbios->blackout_duration, bw_add(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[data->y_clk_level][data->sclk_level])))))) { + data->blackout_recovery_time = bw_max2(data->blackout_recovery_time, bw_div((bw_add(bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), vbios->blackout_duration), bw_sub(bw_div(bw_mul(bw_mul(bw_mul((bw_add(bw_mul(bw_int_to_fixed(2), data->total_dmifmc_urgent_latency), data->dmif_burst_time[data->y_clk_level][data->sclk_level])), data->dispclk), bw_int_to_fixed(data->bytes_per_pixel[k])), data->lines_interleaved_in_mem_access[k]), data->latency_hiding_lines[k]), data->adjusted_data_buffer_size[k]))), (bw_sub(bw_div(bw_mul(bw_mul(data->dispclk, bw_int_to_fixed(data->bytes_per_pixel[k])), data->lines_interleaved_in_mem_access[k]), data->latency_hiding_lines[k]), bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]))))); + } + } + else { + data->blackout_recovery_time = bw_max2(data->blackout_recovery_time, bw_add(bw_mul(bw_int_to_fixed(2), vbios->mcifwrmc_urgent_latency), data->mcifwr_burst_time[data->y_clk_level][data->sclk_level])); + if (bw_ltn(data->adjusted_data_buffer_size[k], bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), (bw_add(vbios->blackout_duration, bw_add(bw_mul(bw_int_to_fixed(2), vbios->mcifwrmc_urgent_latency), data->mcifwr_burst_time[data->y_clk_level][data->sclk_level])))))) { + data->blackout_recovery_time = bw_max2(data->blackout_recovery_time, bw_div((bw_add(bw_mul(bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]), vbios->blackout_duration), bw_sub(bw_div(bw_mul(bw_mul(bw_mul((bw_add(bw_add(bw_mul(bw_int_to_fixed(2), vbios->mcifwrmc_urgent_latency), data->dmif_burst_time[data->y_clk_level][data->sclk_level]), data->mcifwr_burst_time[data->y_clk_level][data->sclk_level])), data->dispclk), bw_int_to_fixed(data->bytes_per_pixel[k])), data->lines_interleaved_in_mem_access[k]), data->latency_hiding_lines[k]), data->adjusted_data_buffer_size[k]))), (bw_sub(bw_div(bw_mul(bw_mul(data->dispclk, bw_int_to_fixed(data->bytes_per_pixel[k])), data->lines_interleaved_in_mem_access[k]), data->latency_hiding_lines[k]), bw_div(bw_mul(data->display_bandwidth[k], data->useful_bytes_per_request[k]), data->bytes_per_request[k]))))); + } + } + } + } + /*sclk deep sleep*/ + /*during self-refresh, sclk can be reduced to dispclk divided by the minimum pixels in the data fifo entry, with 15% margin, but shoudl not be set to less than the request bandwidth.*/ + /*the data fifo entry is 16 pixels for the writeback, 64 bytes/bytes_per_pixel for the graphics, 16 pixels for the parallel rotation underlay,*/ + /*and 16 bytes/bytes_per_pixel for the orthogonal rotation underlay.*/ + /*in parallel mode (underlay pipe), the data read from the dmifv buffer is variable and based on the pixel depth (8bbp - 16 bytes, 16 bpp - 32 bytes, 32 bpp - 64 bytes)*/ + /*in orthogonal mode (underlay pipe), the data read from the dmifv buffer is fixed at 16 bytes.*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (surface_type[i] == bw_def_display_write_back420_luma || surface_type[i] == bw_def_display_write_back420_chroma) { + data->pixels_per_data_fifo_entry[i] = bw_int_to_fixed(16); + } + else if (surface_type[i] == bw_def_graphics) { + data->pixels_per_data_fifo_entry[i] = bw_div(bw_int_to_fixed(64), bw_int_to_fixed(data->bytes_per_pixel[i])); + } + else if (data->orthogonal_rotation[i] == 0) { + data->pixels_per_data_fifo_entry[i] = bw_int_to_fixed(16); + } + else { + data->pixels_per_data_fifo_entry[i] = bw_div(bw_int_to_fixed(16), bw_int_to_fixed(data->bytes_per_pixel[i])); + } + } + } + data->min_pixels_per_data_fifo_entry = bw_int_to_fixed(9999); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_mtn(data->min_pixels_per_data_fifo_entry, data->pixels_per_data_fifo_entry[i])) { + data->min_pixels_per_data_fifo_entry = data->pixels_per_data_fifo_entry[i]; + } + } + } + data->sclk_deep_sleep = bw_max2(bw_div(bw_mul(data->dispclk, bw_frc_to_fixed(115, 100)), data->min_pixels_per_data_fifo_entry), data->total_read_request_bandwidth); + /*urgent, stutter and nb-p_state watermark*/ + /*the urgent watermark is the maximum of the urgent trip time plus the pixel transfer time, the urgent trip times to get data for the first pixel, and the urgent trip times to get data for the last pixel.*/ + /*the stutter exit watermark is the self refresh exit time plus the maximum of the data burst time plus the pixel transfer time, the data burst times to get data for the first pixel, and the data burst times to get data for the last pixel. it does not apply to the writeback.*/ + /*the nb p-state change watermark is the dram speed/p-state change time plus the maximum of the data burst time plus the pixel transfer time, the data burst times to get data for the first pixel, and the data burst times to get data for the last pixel.*/ + /*the pixel transfer time is the maximum of the time to transfer the source pixels required for the first output pixel, and the time to transfer the pixels for the last output pixel minus the active line time.*/ + /*blackout_duration is added to the urgent watermark*/ + data->chunk_request_time = bw_int_to_fixed(0); + data->cursor_request_time = bw_int_to_fixed(0); + /*compute total time to request one chunk from each active display pipe*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->chunk_request_time = bw_add(data->chunk_request_time, (bw_div((bw_div(bw_int_to_fixed(pixels_per_chunk * data->bytes_per_pixel[i]), data->useful_bytes_per_request[i])), bw_min2(sclk[data->sclk_level], bw_div(data->dispclk, bw_int_to_fixed(2)))))); + } + } + /*compute total time to request cursor data*/ + data->cursor_request_time = (bw_div(data->cursor_total_data, (bw_mul(bw_int_to_fixed(32), sclk[data->sclk_level])))); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->line_source_pixels_transfer_time = bw_max2(bw_div(bw_div(data->src_pixels_for_first_output_pixel[i], dceip->lb_write_pixels_per_dispclk), (bw_div(data->dispclk, dceip->display_pipe_throughput_factor))), bw_sub(bw_div(bw_div(data->src_pixels_for_last_output_pixel[i], dceip->lb_write_pixels_per_dispclk), (bw_div(data->dispclk, dceip->display_pipe_throughput_factor))), data->active_time[i])); + if (surface_type[i] != bw_def_display_write_back420_luma && surface_type[i] != bw_def_display_write_back420_chroma) { + data->urgent_watermark[i] = bw_add(bw_add(bw_add(bw_add(bw_add(data->total_dmifmc_urgent_latency, data->dmif_burst_time[data->y_clk_level][data->sclk_level]), bw_max2(data->line_source_pixels_transfer_time, data->line_source_transfer_time[i][data->y_clk_level][data->sclk_level])), vbios->blackout_duration), data->chunk_request_time), data->cursor_request_time); + data->stutter_exit_watermark[i] = bw_add(bw_sub(vbios->stutter_self_refresh_exit_latency, data->total_dmifmc_urgent_latency), data->urgent_watermark[i]); + data->stutter_entry_watermark[i] = bw_add(bw_sub(bw_add(vbios->stutter_self_refresh_exit_latency, vbios->stutter_self_refresh_entry_latency), data->total_dmifmc_urgent_latency), data->urgent_watermark[i]); + /*unconditionally remove black out time from the nb p_state watermark*/ + if ((data->display_pstate_change_enable[i] == 1)) { + data->nbp_state_change_watermark[i] = bw_add(bw_add(vbios->nbp_state_change_latency, data->dmif_burst_time[data->y_clk_level][data->sclk_level]), bw_max2(data->line_source_pixels_transfer_time, data->dram_speed_change_line_source_transfer_time[i][data->y_clk_level][data->sclk_level])); + } + else { + /*maximize the watermark to force the switch in the vb_lank region of the frame*/ + data->nbp_state_change_watermark[i] = bw_int_to_fixed(131000); + } + } + else { + data->urgent_watermark[i] = bw_add(bw_add(bw_add(bw_add(bw_add(vbios->mcifwrmc_urgent_latency, data->mcifwr_burst_time[data->y_clk_level][data->sclk_level]), bw_max2(data->line_source_pixels_transfer_time, data->line_source_transfer_time[i][data->y_clk_level][data->sclk_level])), vbios->blackout_duration), data->chunk_request_time), data->cursor_request_time); + data->stutter_exit_watermark[i] = bw_int_to_fixed(0); + data->stutter_entry_watermark[i] = bw_int_to_fixed(0); + if ((data->display_pstate_change_enable[i] == 1)) { + data->nbp_state_change_watermark[i] = bw_add(bw_add(vbios->nbp_state_change_latency, data->mcifwr_burst_time[data->y_clk_level][data->sclk_level]), bw_max2(data->line_source_pixels_transfer_time, data->dram_speed_change_line_source_transfer_time[i][data->y_clk_level][data->sclk_level])); + } + else { + /*maximize the watermark to force the switch in the vb_lank region of the frame*/ + data->nbp_state_change_watermark[i] = bw_int_to_fixed(131000); + } + } + } + } + /*stutter mode enable*/ + /*in the multi-display case the stutter exit or entry watermark cannot exceed the minimum latency hiding capabilities of the*/ + /*display pipe.*/ + data->stutter_mode_enable = data->cpuc_state_change_enable; + if (data->number_of_displays > 1) { + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if ((bw_mtn(data->stutter_exit_watermark[i], data->minimum_latency_hiding[i]) || bw_mtn(data->stutter_entry_watermark[i], data->minimum_latency_hiding[i]))) { + data->stutter_mode_enable = bw_def_no; + } + } + } + } + /*performance metrics*/ + /* display read access efficiency (%)*/ + /* display write back access efficiency (%)*/ + /* stutter efficiency (%)*/ + /* extra underlay pitch recommended for efficiency (pixels)*/ + /* immediate flip time (us)*/ + /* latency for other clients due to urgent display read (us)*/ + /* latency for other clients due to urgent display write (us)*/ + /* average bandwidth consumed by display (no compression) (gb/s)*/ + /* required dram bandwidth (gb/s)*/ + /* required sclk (m_hz)*/ + /* required rd urgent latency (us)*/ + /* nb p-state change margin (us)*/ + /*dmif and mcifwr dram access efficiency*/ + /*is the ratio between the ideal dram access time (which is the data buffer size in memory divided by the dram bandwidth), and the actual time which is the total page close-open time. but it cannot exceed the dram efficiency provided by the memory subsystem*/ + data->dmifdram_access_efficiency = bw_min2(bw_div(bw_div(data->total_display_reads_required_dram_access_data, data->dram_bandwidth), data->dmif_total_page_close_open_time), bw_int_to_fixed(1)); + if (bw_mtn(data->total_display_writes_required_dram_access_data, bw_int_to_fixed(0))) { + data->mcifwrdram_access_efficiency = bw_min2(bw_div(bw_div(data->total_display_writes_required_dram_access_data, data->dram_bandwidth), data->mcifwr_total_page_close_open_time), bw_int_to_fixed(1)); + } + else { + data->mcifwrdram_access_efficiency = bw_int_to_fixed(0); + } + /*average bandwidth*/ + /*the average bandwidth with no compression is the vertical active time is the source width times the bytes per pixel divided by the line time, multiplied by the vertical scale ratio and the ratio of bytes per request divided by the useful bytes per request.*/ + /*the average bandwidth with compression is the same, divided by the compression ratio*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->average_bandwidth_no_compression[i] = bw_div(bw_mul(bw_mul(bw_div(bw_mul(data->source_width_rounded_up_to_chunks[i], bw_int_to_fixed(data->bytes_per_pixel[i])), (bw_div(data->h_total[i], data->pixel_rate[i]))), data->vsr[i]), data->bytes_per_request[i]), data->useful_bytes_per_request[i]); + data->average_bandwidth[i] = bw_div(data->average_bandwidth_no_compression[i], data->compression_rate[i]); + } + } + data->total_average_bandwidth_no_compression = bw_int_to_fixed(0); + data->total_average_bandwidth = bw_int_to_fixed(0); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->total_average_bandwidth_no_compression = bw_add(data->total_average_bandwidth_no_compression, data->average_bandwidth_no_compression[i]); + data->total_average_bandwidth = bw_add(data->total_average_bandwidth, data->average_bandwidth[i]); + } + } + /*stutter efficiency*/ + /*the stutter efficiency is the frame-average time in self-refresh divided by the frame-average stutter cycle duration. only applies if the display write-back is not enabled.*/ + /*the frame-average stutter cycle used is the minimum for all pipes of the frame-average data buffer size in time, times the compression rate*/ + /*the frame-average time in self-refresh is the stutter cycle minus the self refresh exit latency and the burst time*/ + /*the stutter cycle is the dmif buffer size reduced by the excess of the stutter exit watermark over the lb size in time.*/ + /*the burst time is the data needed during the stutter cycle divided by the available bandwidth*/ + /*compute the time read all the data from the dmif buffer to the lb (dram refresh period)*/ + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->stutter_refresh_duration[i] = bw_sub(bw_mul(bw_div(bw_div(bw_mul(bw_div(bw_div(data->adjusted_data_buffer_size[i], bw_int_to_fixed(data->bytes_per_pixel[i])), data->source_width_rounded_up_to_chunks[i]), data->h_total[i]), data->vsr[i]), data->pixel_rate[i]), data->compression_rate[i]), bw_max2(bw_int_to_fixed(0), bw_sub(data->stutter_exit_watermark[i], bw_div(bw_mul((bw_sub(data->lb_partitions[i], bw_int_to_fixed(1))), data->h_total[i]), data->pixel_rate[i])))); + data->stutter_dmif_buffer_size[i] = bw_div(bw_mul(bw_mul(bw_div(bw_mul(bw_mul(data->stutter_refresh_duration[i], bw_int_to_fixed(data->bytes_per_pixel[i])), data->source_width_rounded_up_to_chunks[i]), data->h_total[i]), data->vsr[i]), data->pixel_rate[i]), data->compression_rate[i]); + } + } + data->min_stutter_refresh_duration = bw_int_to_fixed(9999); + data->total_stutter_dmif_buffer_size = 0; + data->total_bytes_requested = 0; + data->min_stutter_dmif_buffer_size = 9999; + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + if (bw_mtn(data->min_stutter_refresh_duration, data->stutter_refresh_duration[i])) { + data->min_stutter_refresh_duration = data->stutter_refresh_duration[i]; + data->total_bytes_requested = bw_fixed_to_int(bw_add(bw_int_to_fixed(data->total_bytes_requested), (bw_mul(bw_mul(data->source_height_rounded_up_to_chunks[i], data->source_width_rounded_up_to_chunks[i]), bw_int_to_fixed(data->bytes_per_pixel[i]))))); + data->min_stutter_dmif_buffer_size = bw_fixed_to_int(data->stutter_dmif_buffer_size[i]); + } + data->total_stutter_dmif_buffer_size = bw_fixed_to_int(bw_add(data->stutter_dmif_buffer_size[i], bw_int_to_fixed(data->total_stutter_dmif_buffer_size))); + } + } + data->stutter_burst_time = bw_div(bw_int_to_fixed(data->total_stutter_dmif_buffer_size), bw_min2(bw_mul(data->dram_bandwidth, data->dmifdram_access_efficiency), bw_mul(sclk[data->sclk_level], bw_int_to_fixed(32)))); + data->num_stutter_bursts = data->total_bytes_requested / data->min_stutter_dmif_buffer_size; + data->total_stutter_cycle_duration = bw_add(bw_add(data->min_stutter_refresh_duration, vbios->stutter_self_refresh_exit_latency), data->stutter_burst_time); + data->time_in_self_refresh = data->min_stutter_refresh_duration; + if (data->d1_display_write_back_dwb_enable == 1) { + data->stutter_efficiency = bw_int_to_fixed(0); + } + else if (bw_ltn(data->time_in_self_refresh, bw_int_to_fixed(0))) { + data->stutter_efficiency = bw_int_to_fixed(0); + } + else { + /*compute stutter efficiency assuming 60 hz refresh rate*/ + data->stutter_efficiency = bw_max2(bw_int_to_fixed(0), bw_mul((bw_sub(bw_int_to_fixed(1), (bw_div(bw_mul((bw_add(vbios->stutter_self_refresh_exit_latency, data->stutter_burst_time)), bw_int_to_fixed(data->num_stutter_bursts)), bw_frc_to_fixed(166666667, 10000))))), bw_int_to_fixed(100))); + } + /*immediate flip time*/ + /*if scatter gather is enabled, the immediate flip takes a number of urgent memory trips equivalent to the pte requests in a row divided by the pte request limit.*/ + /*otherwise, it may take just one urgenr memory trip*/ + data->worst_number_of_trips_to_memory = bw_int_to_fixed(1); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i] && data->scatter_gather_enable_for_pipe[i] == 1) { + data->number_of_trips_to_memory_for_getting_apte_row[i] = bw_ceil2(bw_div(data->scatter_gather_pte_requests_in_row[i], data->scatter_gather_pte_request_limit[i]), bw_int_to_fixed(1)); + if (bw_ltn(data->worst_number_of_trips_to_memory, data->number_of_trips_to_memory_for_getting_apte_row[i])) { + data->worst_number_of_trips_to_memory = data->number_of_trips_to_memory_for_getting_apte_row[i]; + } + } + } + data->immediate_flip_time = bw_mul(data->worst_number_of_trips_to_memory, data->total_dmifmc_urgent_latency); + /*worst latency for other clients*/ + /*it is the urgent latency plus the urgent burst time*/ + data->latency_for_non_dmif_clients = bw_add(data->total_dmifmc_urgent_latency, data->dmif_burst_time[data->y_clk_level][data->sclk_level]); + if (data->d1_display_write_back_dwb_enable == 1) { + data->latency_for_non_mcifwr_clients = bw_add(vbios->mcifwrmc_urgent_latency, dceip->mcifwr_all_surfaces_burst_time); + } + else { + data->latency_for_non_mcifwr_clients = bw_int_to_fixed(0); + } + /*dmif mc urgent latency suppported in high sclk and yclk*/ + data->dmifmc_urgent_latency_supported_in_high_sclk_and_yclk = bw_div((bw_sub(data->min_read_buffer_size_in_time, data->dmif_burst_time[high][s_high])), data->total_dmifmc_urgent_trips); + /*dram speed/p-state change margin*/ + /*in the multi-display case the nb p-state change watermark cannot exceed the average lb size plus the dmif size or the cursor dcp buffer size*/ + data->v_blank_nbp_state_dram_speed_change_latency_supported = bw_int_to_fixed(99999); + data->nbp_state_dram_speed_change_latency_supported = bw_int_to_fixed(99999); + for (i = 0; i <= maximum_number_of_surfaces - 1; i++) { + if (data->enable[i]) { + data->nbp_state_dram_speed_change_latency_supported = bw_min2(data->nbp_state_dram_speed_change_latency_supported, bw_add(bw_sub(data->maximum_latency_hiding_with_cursor[i], data->nbp_state_change_watermark[i]), vbios->nbp_state_change_latency)); + data->v_blank_nbp_state_dram_speed_change_latency_supported = bw_min2(data->v_blank_nbp_state_dram_speed_change_latency_supported, bw_add(bw_sub(bw_div(bw_mul((bw_sub(data->v_total[i], bw_sub(bw_div(data->src_height[i], data->v_scale_ratio[i]), bw_int_to_fixed(4)))), data->h_total[i]), data->pixel_rate[i]), data->nbp_state_change_watermark[i]), vbios->nbp_state_change_latency)); + } + } + /*sclk required vs urgent latency*/ + for (i = 1; i <= 5; i++) { + data->display_reads_time_for_data_transfer_and_urgent_latency = bw_sub(data->min_read_buffer_size_in_time, bw_mul(data->total_dmifmc_urgent_trips, bw_int_to_fixed(i))); + if (pipe_check == bw_def_ok && (bw_mtn(data->display_reads_time_for_data_transfer_and_urgent_latency, data->dmif_total_page_close_open_time))) { + data->dmif_required_sclk_for_urgent_latency[i] = bw_div(bw_div(data->total_display_reads_required_data, data->display_reads_time_for_data_transfer_and_urgent_latency), (bw_mul(vbios->data_return_bus_width, bw_int_to_fixed(bus_efficiency)))); + } + else { + data->dmif_required_sclk_for_urgent_latency[i] = bw_int_to_fixed(bw_def_na); + } + } + /*output link bit per pixel supported*/ + for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { + data->output_bpphdmi[k] = bw_def_na; + data->output_bppdp4_lane_hbr[k] = bw_def_na; + data->output_bppdp4_lane_hbr2[k] = bw_def_na; + data->output_bppdp4_lane_hbr3[k] = bw_def_na; + if (data->enable[k]) { + data->output_bpphdmi[k] = bw_fixed_to_int(bw_mul(bw_div(bw_min2(bw_int_to_fixed(600), data->max_phyclk), data->pixel_rate[k]), bw_int_to_fixed(24))); + if (bw_meq(data->max_phyclk, bw_int_to_fixed(270))) { + data->output_bppdp4_lane_hbr[k] = bw_fixed_to_int(bw_mul(bw_div(bw_mul(bw_int_to_fixed(270), bw_int_to_fixed(4)), data->pixel_rate[k]), bw_int_to_fixed(8))); + } + if (bw_meq(data->max_phyclk, bw_int_to_fixed(540))) { + data->output_bppdp4_lane_hbr2[k] = bw_fixed_to_int(bw_mul(bw_div(bw_mul(bw_int_to_fixed(540), bw_int_to_fixed(4)), data->pixel_rate[k]), bw_int_to_fixed(8))); + } + if (bw_meq(data->max_phyclk, bw_int_to_fixed(810))) { + data->output_bppdp4_lane_hbr3[k] = bw_fixed_to_int(bw_mul(bw_div(bw_mul(bw_int_to_fixed(810), bw_int_to_fixed(4)), data->pixel_rate[k]), bw_int_to_fixed(8))); + } + } + } +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ +void bw_calcs_init(struct bw_calcs_dceip *bw_dceip, + struct bw_calcs_vbios *bw_vbios, + struct hw_asic_id asic_id) +{ + struct bw_calcs_dceip dceip = { 0 }; + struct bw_calcs_vbios vbios = { 0 }; + + enum bw_calcs_version version = bw_calcs_version_from_asic_id(asic_id); + + dceip.version = version; + + switch (version) { + case BW_CALCS_VERSION_CARRIZO: + vbios.memory_type = bw_def_gddr5; + vbios.dram_channel_width_in_bits = 64; + vbios.number_of_dram_channels = asic_id.vram_width / vbios.dram_channel_width_in_bits; + vbios.number_of_dram_banks = 8; + vbios.high_yclk = bw_int_to_fixed(1600); + vbios.mid_yclk = bw_int_to_fixed(1600); + vbios.low_yclk = bw_frc_to_fixed(66666, 100); + vbios.low_sclk = bw_int_to_fixed(200); + vbios.mid1_sclk = bw_int_to_fixed(300); + vbios.mid2_sclk = bw_int_to_fixed(300); + vbios.mid3_sclk = bw_int_to_fixed(300); + vbios.mid4_sclk = bw_int_to_fixed(300); + vbios.mid5_sclk = bw_int_to_fixed(300); + vbios.mid6_sclk = bw_int_to_fixed(300); + vbios.high_sclk = bw_frc_to_fixed(62609, 100); + vbios.low_voltage_max_dispclk = bw_int_to_fixed(352); + vbios.mid_voltage_max_dispclk = bw_int_to_fixed(467); + vbios.high_voltage_max_dispclk = bw_int_to_fixed(643); + vbios.low_voltage_max_phyclk = bw_int_to_fixed(540); + vbios.mid_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.high_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.data_return_bus_width = bw_int_to_fixed(32); + vbios.trc = bw_int_to_fixed(50); + vbios.dmifmc_urgent_latency = bw_int_to_fixed(4); + vbios.stutter_self_refresh_exit_latency = bw_frc_to_fixed(153, 10); + vbios.stutter_self_refresh_entry_latency = bw_int_to_fixed(0); + vbios.nbp_state_change_latency = bw_frc_to_fixed(19649, 1000); + vbios.mcifwrmc_urgent_latency = bw_int_to_fixed(10); + vbios.scatter_gather_enable = true; + vbios.down_spread_percentage = bw_frc_to_fixed(5, 10); + vbios.cursor_width = 32; + vbios.average_compression_rate = 4; + vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = 256; + vbios.blackout_duration = bw_int_to_fixed(18); /* us */ + vbios.maximum_blackout_recovery_time = bw_int_to_fixed(20); + + dceip.large_cursor = false; + dceip.dmif_request_buffer_size = bw_int_to_fixed(768); + dceip.dmif_pipe_en_fbc_chunk_tracker = false; + dceip.cursor_max_outstanding_group_num = 1; + dceip.lines_interleaved_into_lb = 2; + dceip.chunk_width = 256; + dceip.number_of_graphics_pipes = 3; + dceip.number_of_underlay_pipes = 1; + dceip.low_power_tiling_mode = 0; + dceip.display_write_back_supported = false; + dceip.argb_compression_support = false; + dceip.underlay_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35556, 10000); + dceip.underlay_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.underlay_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.underlay_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.graphics_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35, 10); + dceip.graphics_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.graphics_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.graphics_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.alpha_vscaler_efficiency = bw_int_to_fixed(3); + dceip.max_dmif_buffer_allocated = 2; + dceip.graphics_dmif_size = 12288; + dceip.underlay_luma_dmif_size = 19456; + dceip.underlay_chroma_dmif_size = 23552; + dceip.pre_downscaler_enabled = true; + dceip.underlay_downscale_prefetch_enabled = true; + dceip.lb_write_pixels_per_dispclk = bw_int_to_fixed(1); + dceip.lb_size_per_component444 = bw_int_to_fixed(82176); + dceip.graphics_lb_nodownscaling_multi_line_prefetching = false; + dceip.stutter_and_dram_clock_state_change_gated_before_cursor = + bw_int_to_fixed(0); + dceip.underlay420_luma_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.underlay420_chroma_lb_size_per_component = + bw_int_to_fixed(164352); + dceip.underlay422_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.cursor_chunk_width = bw_int_to_fixed(64); + dceip.cursor_dcp_buffer_lines = bw_int_to_fixed(4); + dceip.underlay_maximum_width_efficient_for_tiling = + bw_int_to_fixed(1920); + dceip.underlay_maximum_height_efficient_for_tiling = + bw_int_to_fixed(1080); + dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = + bw_frc_to_fixed(3, 10); + dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = + bw_int_to_fixed(25); + dceip.minimum_outstanding_pte_request_limit = bw_int_to_fixed( + 2); + dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = + bw_int_to_fixed(128); + dceip.limit_excessive_outstanding_dmif_requests = true; + dceip.linear_mode_line_request_alternation_slice = + bw_int_to_fixed(64); + dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = + 32; + dceip.display_write_back420_luma_mcifwr_buffer_size = 12288; + dceip.display_write_back420_chroma_mcifwr_buffer_size = 8192; + dceip.request_efficiency = bw_frc_to_fixed(8, 10); + dceip.dispclk_per_request = bw_int_to_fixed(2); + dceip.dispclk_ramping_factor = bw_frc_to_fixed(105, 100); + dceip.display_pipe_throughput_factor = bw_frc_to_fixed(105, 100); + dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; + dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); /* todo: this is a bug*/ + break; + case BW_CALCS_VERSION_POLARIS10: + vbios.memory_type = bw_def_gddr5; + vbios.dram_channel_width_in_bits = 32; + vbios.number_of_dram_channels = asic_id.vram_width / vbios.dram_channel_width_in_bits; + vbios.number_of_dram_banks = 8; + vbios.high_yclk = bw_int_to_fixed(6000); + vbios.mid_yclk = bw_int_to_fixed(3200); + vbios.low_yclk = bw_int_to_fixed(1000); + vbios.low_sclk = bw_int_to_fixed(300); + vbios.mid1_sclk = bw_int_to_fixed(400); + vbios.mid2_sclk = bw_int_to_fixed(500); + vbios.mid3_sclk = bw_int_to_fixed(600); + vbios.mid4_sclk = bw_int_to_fixed(700); + vbios.mid5_sclk = bw_int_to_fixed(800); + vbios.mid6_sclk = bw_int_to_fixed(974); + vbios.high_sclk = bw_int_to_fixed(1154); + vbios.low_voltage_max_dispclk = bw_int_to_fixed(459); + vbios.mid_voltage_max_dispclk = bw_int_to_fixed(654); + vbios.high_voltage_max_dispclk = bw_int_to_fixed(1108); + vbios.low_voltage_max_phyclk = bw_int_to_fixed(540); + vbios.mid_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.high_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.data_return_bus_width = bw_int_to_fixed(32); + vbios.trc = bw_int_to_fixed(48); + vbios.dmifmc_urgent_latency = bw_int_to_fixed(3); + vbios.stutter_self_refresh_exit_latency = bw_int_to_fixed(5); + vbios.stutter_self_refresh_entry_latency = bw_int_to_fixed(0); + vbios.nbp_state_change_latency = bw_int_to_fixed(45); + vbios.mcifwrmc_urgent_latency = bw_int_to_fixed(10); + vbios.scatter_gather_enable = true; + vbios.down_spread_percentage = bw_frc_to_fixed(5, 10); + vbios.cursor_width = 32; + vbios.average_compression_rate = 4; + vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = 256; + vbios.blackout_duration = bw_int_to_fixed(0); /* us */ + vbios.maximum_blackout_recovery_time = bw_int_to_fixed(0); + + dceip.large_cursor = false; + dceip.dmif_request_buffer_size = bw_int_to_fixed(768); + dceip.dmif_pipe_en_fbc_chunk_tracker = false; + dceip.cursor_max_outstanding_group_num = 1; + dceip.lines_interleaved_into_lb = 2; + dceip.chunk_width = 256; + dceip.number_of_graphics_pipes = 6; + dceip.number_of_underlay_pipes = 0; + dceip.low_power_tiling_mode = 0; + dceip.display_write_back_supported = false; + dceip.argb_compression_support = true; + dceip.underlay_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35556, 10000); + dceip.underlay_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.underlay_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.underlay_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.graphics_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35, 10); + dceip.graphics_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.graphics_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.graphics_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.alpha_vscaler_efficiency = bw_int_to_fixed(3); + dceip.max_dmif_buffer_allocated = 4; + dceip.graphics_dmif_size = 12288; + dceip.underlay_luma_dmif_size = 19456; + dceip.underlay_chroma_dmif_size = 23552; + dceip.pre_downscaler_enabled = true; + dceip.underlay_downscale_prefetch_enabled = true; + dceip.lb_write_pixels_per_dispclk = bw_int_to_fixed(1); + dceip.lb_size_per_component444 = bw_int_to_fixed(245952); + dceip.graphics_lb_nodownscaling_multi_line_prefetching = true; + dceip.stutter_and_dram_clock_state_change_gated_before_cursor = + bw_int_to_fixed(1); + dceip.underlay420_luma_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.underlay420_chroma_lb_size_per_component = + bw_int_to_fixed(164352); + dceip.underlay422_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.cursor_chunk_width = bw_int_to_fixed(64); + dceip.cursor_dcp_buffer_lines = bw_int_to_fixed(4); + dceip.underlay_maximum_width_efficient_for_tiling = + bw_int_to_fixed(1920); + dceip.underlay_maximum_height_efficient_for_tiling = + bw_int_to_fixed(1080); + dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = + bw_frc_to_fixed(3, 10); + dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = + bw_int_to_fixed(25); + dceip.minimum_outstanding_pte_request_limit = bw_int_to_fixed( + 2); + dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = + bw_int_to_fixed(128); + dceip.limit_excessive_outstanding_dmif_requests = true; + dceip.linear_mode_line_request_alternation_slice = + bw_int_to_fixed(64); + dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = + 32; + dceip.display_write_back420_luma_mcifwr_buffer_size = 12288; + dceip.display_write_back420_chroma_mcifwr_buffer_size = 8192; + dceip.request_efficiency = bw_frc_to_fixed(8, 10); + dceip.dispclk_per_request = bw_int_to_fixed(2); + dceip.dispclk_ramping_factor = bw_frc_to_fixed(105, 100); + dceip.display_pipe_throughput_factor = bw_frc_to_fixed(105, 100); + dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; + dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); + break; + case BW_CALCS_VERSION_POLARIS11: + vbios.memory_type = bw_def_gddr5; + vbios.dram_channel_width_in_bits = 32; + vbios.number_of_dram_channels = asic_id.vram_width / vbios.dram_channel_width_in_bits; + vbios.number_of_dram_banks = 8; + vbios.high_yclk = bw_int_to_fixed(6000); + vbios.mid_yclk = bw_int_to_fixed(3200); + vbios.low_yclk = bw_int_to_fixed(1000); + vbios.low_sclk = bw_int_to_fixed(300); + vbios.mid1_sclk = bw_int_to_fixed(400); + vbios.mid2_sclk = bw_int_to_fixed(500); + vbios.mid3_sclk = bw_int_to_fixed(600); + vbios.mid4_sclk = bw_int_to_fixed(700); + vbios.mid5_sclk = bw_int_to_fixed(800); + vbios.mid6_sclk = bw_int_to_fixed(974); + vbios.high_sclk = bw_int_to_fixed(1154); + vbios.low_voltage_max_dispclk = bw_int_to_fixed(459); + vbios.mid_voltage_max_dispclk = bw_int_to_fixed(654); + vbios.high_voltage_max_dispclk = bw_int_to_fixed(1108); + vbios.low_voltage_max_phyclk = bw_int_to_fixed(540); + vbios.mid_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.high_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.data_return_bus_width = bw_int_to_fixed(32); + vbios.trc = bw_int_to_fixed(48); + if (vbios.number_of_dram_channels == 2) // 64-bit + vbios.dmifmc_urgent_latency = bw_int_to_fixed(4); + else + vbios.dmifmc_urgent_latency = bw_int_to_fixed(3); + vbios.stutter_self_refresh_exit_latency = bw_int_to_fixed(5); + vbios.stutter_self_refresh_entry_latency = bw_int_to_fixed(0); + vbios.nbp_state_change_latency = bw_int_to_fixed(45); + vbios.mcifwrmc_urgent_latency = bw_int_to_fixed(10); + vbios.scatter_gather_enable = true; + vbios.down_spread_percentage = bw_frc_to_fixed(5, 10); + vbios.cursor_width = 32; + vbios.average_compression_rate = 4; + vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = 256; + vbios.blackout_duration = bw_int_to_fixed(0); /* us */ + vbios.maximum_blackout_recovery_time = bw_int_to_fixed(0); + + dceip.large_cursor = false; + dceip.dmif_request_buffer_size = bw_int_to_fixed(768); + dceip.dmif_pipe_en_fbc_chunk_tracker = false; + dceip.cursor_max_outstanding_group_num = 1; + dceip.lines_interleaved_into_lb = 2; + dceip.chunk_width = 256; + dceip.number_of_graphics_pipes = 5; + dceip.number_of_underlay_pipes = 0; + dceip.low_power_tiling_mode = 0; + dceip.display_write_back_supported = false; + dceip.argb_compression_support = true; + dceip.underlay_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35556, 10000); + dceip.underlay_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.underlay_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.underlay_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.graphics_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35, 10); + dceip.graphics_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.graphics_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.graphics_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.alpha_vscaler_efficiency = bw_int_to_fixed(3); + dceip.max_dmif_buffer_allocated = 4; + dceip.graphics_dmif_size = 12288; + dceip.underlay_luma_dmif_size = 19456; + dceip.underlay_chroma_dmif_size = 23552; + dceip.pre_downscaler_enabled = true; + dceip.underlay_downscale_prefetch_enabled = true; + dceip.lb_write_pixels_per_dispclk = bw_int_to_fixed(1); + dceip.lb_size_per_component444 = bw_int_to_fixed(245952); + dceip.graphics_lb_nodownscaling_multi_line_prefetching = true; + dceip.stutter_and_dram_clock_state_change_gated_before_cursor = + bw_int_to_fixed(1); + dceip.underlay420_luma_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.underlay420_chroma_lb_size_per_component = + bw_int_to_fixed(164352); + dceip.underlay422_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.cursor_chunk_width = bw_int_to_fixed(64); + dceip.cursor_dcp_buffer_lines = bw_int_to_fixed(4); + dceip.underlay_maximum_width_efficient_for_tiling = + bw_int_to_fixed(1920); + dceip.underlay_maximum_height_efficient_for_tiling = + bw_int_to_fixed(1080); + dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = + bw_frc_to_fixed(3, 10); + dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = + bw_int_to_fixed(25); + dceip.minimum_outstanding_pte_request_limit = bw_int_to_fixed( + 2); + dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = + bw_int_to_fixed(128); + dceip.limit_excessive_outstanding_dmif_requests = true; + dceip.linear_mode_line_request_alternation_slice = + bw_int_to_fixed(64); + dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = + 32; + dceip.display_write_back420_luma_mcifwr_buffer_size = 12288; + dceip.display_write_back420_chroma_mcifwr_buffer_size = 8192; + dceip.request_efficiency = bw_frc_to_fixed(8, 10); + dceip.dispclk_per_request = bw_int_to_fixed(2); + dceip.dispclk_ramping_factor = bw_frc_to_fixed(105, 100); + dceip.display_pipe_throughput_factor = bw_frc_to_fixed(105, 100); + dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; + dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); + break; + case BW_CALCS_VERSION_STONEY: + vbios.memory_type = bw_def_gddr5; + vbios.dram_channel_width_in_bits = 64; + vbios.number_of_dram_channels = asic_id.vram_width / vbios.dram_channel_width_in_bits; + vbios.number_of_dram_banks = 8; + vbios.high_yclk = bw_int_to_fixed(1866); + vbios.mid_yclk = bw_int_to_fixed(1866); + vbios.low_yclk = bw_int_to_fixed(1333); + vbios.low_sclk = bw_int_to_fixed(200); + vbios.mid1_sclk = bw_int_to_fixed(600); + vbios.mid2_sclk = bw_int_to_fixed(600); + vbios.mid3_sclk = bw_int_to_fixed(600); + vbios.mid4_sclk = bw_int_to_fixed(600); + vbios.mid5_sclk = bw_int_to_fixed(600); + vbios.mid6_sclk = bw_int_to_fixed(600); + vbios.high_sclk = bw_int_to_fixed(800); + vbios.low_voltage_max_dispclk = bw_int_to_fixed(352); + vbios.mid_voltage_max_dispclk = bw_int_to_fixed(467); + vbios.high_voltage_max_dispclk = bw_int_to_fixed(643); + vbios.low_voltage_max_phyclk = bw_int_to_fixed(540); + vbios.mid_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.high_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.data_return_bus_width = bw_int_to_fixed(32); + vbios.trc = bw_int_to_fixed(50); + vbios.dmifmc_urgent_latency = bw_int_to_fixed(4); + vbios.stutter_self_refresh_exit_latency = bw_frc_to_fixed(158, 10); + vbios.stutter_self_refresh_entry_latency = bw_int_to_fixed(0); + vbios.nbp_state_change_latency = bw_frc_to_fixed(2008, 100); + vbios.mcifwrmc_urgent_latency = bw_int_to_fixed(10); + vbios.scatter_gather_enable = true; + vbios.down_spread_percentage = bw_frc_to_fixed(5, 10); + vbios.cursor_width = 32; + vbios.average_compression_rate = 4; + vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = 256; + vbios.blackout_duration = bw_int_to_fixed(18); /* us */ + vbios.maximum_blackout_recovery_time = bw_int_to_fixed(20); + + dceip.large_cursor = false; + dceip.dmif_request_buffer_size = bw_int_to_fixed(768); + dceip.dmif_pipe_en_fbc_chunk_tracker = false; + dceip.cursor_max_outstanding_group_num = 1; + dceip.lines_interleaved_into_lb = 2; + dceip.chunk_width = 256; + dceip.number_of_graphics_pipes = 2; + dceip.number_of_underlay_pipes = 1; + dceip.low_power_tiling_mode = 0; + dceip.display_write_back_supported = false; + dceip.argb_compression_support = true; + dceip.underlay_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35556, 10000); + dceip.underlay_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.underlay_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.underlay_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.graphics_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35, 10); + dceip.graphics_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.graphics_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.graphics_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.alpha_vscaler_efficiency = bw_int_to_fixed(3); + dceip.max_dmif_buffer_allocated = 2; + dceip.graphics_dmif_size = 12288; + dceip.underlay_luma_dmif_size = 19456; + dceip.underlay_chroma_dmif_size = 23552; + dceip.pre_downscaler_enabled = true; + dceip.underlay_downscale_prefetch_enabled = true; + dceip.lb_write_pixels_per_dispclk = bw_int_to_fixed(1); + dceip.lb_size_per_component444 = bw_int_to_fixed(82176); + dceip.graphics_lb_nodownscaling_multi_line_prefetching = false; + dceip.stutter_and_dram_clock_state_change_gated_before_cursor = + bw_int_to_fixed(0); + dceip.underlay420_luma_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.underlay420_chroma_lb_size_per_component = + bw_int_to_fixed(164352); + dceip.underlay422_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.cursor_chunk_width = bw_int_to_fixed(64); + dceip.cursor_dcp_buffer_lines = bw_int_to_fixed(4); + dceip.underlay_maximum_width_efficient_for_tiling = + bw_int_to_fixed(1920); + dceip.underlay_maximum_height_efficient_for_tiling = + bw_int_to_fixed(1080); + dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = + bw_frc_to_fixed(3, 10); + dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = + bw_int_to_fixed(25); + dceip.minimum_outstanding_pte_request_limit = bw_int_to_fixed( + 2); + dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = + bw_int_to_fixed(128); + dceip.limit_excessive_outstanding_dmif_requests = true; + dceip.linear_mode_line_request_alternation_slice = + bw_int_to_fixed(64); + dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = + 32; + dceip.display_write_back420_luma_mcifwr_buffer_size = 12288; + dceip.display_write_back420_chroma_mcifwr_buffer_size = 8192; + dceip.request_efficiency = bw_frc_to_fixed(8, 10); + dceip.dispclk_per_request = bw_int_to_fixed(2); + dceip.dispclk_ramping_factor = bw_frc_to_fixed(105, 100); + dceip.display_pipe_throughput_factor = bw_frc_to_fixed(105, 100); + dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; + dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); + break; + case BW_CALCS_VERSION_VEGA10: + vbios.memory_type = bw_def_hbm; + vbios.dram_channel_width_in_bits = 128; + vbios.number_of_dram_channels = asic_id.vram_width / vbios.dram_channel_width_in_bits; + vbios.number_of_dram_banks = 16; + vbios.high_yclk = bw_int_to_fixed(2400); + vbios.mid_yclk = bw_int_to_fixed(1700); + vbios.low_yclk = bw_int_to_fixed(1000); + vbios.low_sclk = bw_int_to_fixed(300); + vbios.mid1_sclk = bw_int_to_fixed(350); + vbios.mid2_sclk = bw_int_to_fixed(400); + vbios.mid3_sclk = bw_int_to_fixed(500); + vbios.mid4_sclk = bw_int_to_fixed(600); + vbios.mid5_sclk = bw_int_to_fixed(700); + vbios.mid6_sclk = bw_int_to_fixed(760); + vbios.high_sclk = bw_int_to_fixed(776); + vbios.low_voltage_max_dispclk = bw_int_to_fixed(460); + vbios.mid_voltage_max_dispclk = bw_int_to_fixed(670); + vbios.high_voltage_max_dispclk = bw_int_to_fixed(1133); + vbios.low_voltage_max_phyclk = bw_int_to_fixed(540); + vbios.mid_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.high_voltage_max_phyclk = bw_int_to_fixed(810); + vbios.data_return_bus_width = bw_int_to_fixed(32); + vbios.trc = bw_int_to_fixed(48); + vbios.dmifmc_urgent_latency = bw_int_to_fixed(3); + vbios.stutter_self_refresh_exit_latency = bw_frc_to_fixed(75, 10); + vbios.stutter_self_refresh_entry_latency = bw_frc_to_fixed(19, 10); + vbios.nbp_state_change_latency = bw_int_to_fixed(39); + vbios.mcifwrmc_urgent_latency = bw_int_to_fixed(10); + vbios.scatter_gather_enable = false; + vbios.down_spread_percentage = bw_frc_to_fixed(5, 10); + vbios.cursor_width = 32; + vbios.average_compression_rate = 4; + vbios.number_of_request_slots_gmc_reserves_for_dmif_per_channel = 8; + vbios.blackout_duration = bw_int_to_fixed(0); /* us */ + vbios.maximum_blackout_recovery_time = bw_int_to_fixed(0); + + dceip.large_cursor = false; + dceip.dmif_request_buffer_size = bw_int_to_fixed(2304); + dceip.dmif_pipe_en_fbc_chunk_tracker = true; + dceip.cursor_max_outstanding_group_num = 1; + dceip.lines_interleaved_into_lb = 2; + dceip.chunk_width = 256; + dceip.number_of_graphics_pipes = 6; + dceip.number_of_underlay_pipes = 0; + dceip.low_power_tiling_mode = 0; + dceip.display_write_back_supported = true; + dceip.argb_compression_support = true; + dceip.underlay_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35556, 10000); + dceip.underlay_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.underlay_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.underlay_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.graphics_vscaler_efficiency6_bit_per_component = + bw_frc_to_fixed(35, 10); + dceip.graphics_vscaler_efficiency8_bit_per_component = + bw_frc_to_fixed(34286, 10000); + dceip.graphics_vscaler_efficiency10_bit_per_component = + bw_frc_to_fixed(32, 10); + dceip.graphics_vscaler_efficiency12_bit_per_component = + bw_int_to_fixed(3); + dceip.alpha_vscaler_efficiency = bw_int_to_fixed(3); + dceip.max_dmif_buffer_allocated = 4; + dceip.graphics_dmif_size = 24576; + dceip.underlay_luma_dmif_size = 19456; + dceip.underlay_chroma_dmif_size = 23552; + dceip.pre_downscaler_enabled = true; + dceip.underlay_downscale_prefetch_enabled = false; + dceip.lb_write_pixels_per_dispclk = bw_int_to_fixed(1); + dceip.lb_size_per_component444 = bw_int_to_fixed(245952); + dceip.graphics_lb_nodownscaling_multi_line_prefetching = true; + dceip.stutter_and_dram_clock_state_change_gated_before_cursor = + bw_int_to_fixed(1); + dceip.underlay420_luma_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.underlay420_chroma_lb_size_per_component = + bw_int_to_fixed(164352); + dceip.underlay422_lb_size_per_component = bw_int_to_fixed( + 82176); + dceip.cursor_chunk_width = bw_int_to_fixed(64); + dceip.cursor_dcp_buffer_lines = bw_int_to_fixed(4); + dceip.underlay_maximum_width_efficient_for_tiling = + bw_int_to_fixed(1920); + dceip.underlay_maximum_height_efficient_for_tiling = + bw_int_to_fixed(1080); + dceip.peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display = + bw_frc_to_fixed(3, 10); + dceip.peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation = + bw_int_to_fixed(25); + dceip.minimum_outstanding_pte_request_limit = bw_int_to_fixed( + 2); + dceip.maximum_total_outstanding_pte_requests_allowed_by_saw = + bw_int_to_fixed(128); + dceip.limit_excessive_outstanding_dmif_requests = true; + dceip.linear_mode_line_request_alternation_slice = + bw_int_to_fixed(64); + dceip.scatter_gather_lines_of_pte_prefetching_in_linear_mode = + 32; + dceip.display_write_back420_luma_mcifwr_buffer_size = 12288; + dceip.display_write_back420_chroma_mcifwr_buffer_size = 8192; + dceip.request_efficiency = bw_frc_to_fixed(8, 10); + dceip.dispclk_per_request = bw_int_to_fixed(2); + dceip.dispclk_ramping_factor = bw_frc_to_fixed(105, 100); + dceip.display_pipe_throughput_factor = bw_frc_to_fixed(105, 100); + dceip.scatter_gather_pte_request_rows_in_tiling_mode = 2; + dceip.mcifwr_all_surfaces_burst_time = bw_int_to_fixed(0); + break; + default: + break; + } + *bw_dceip = dceip; + *bw_vbios = vbios; + +} + +/** + * Compare calculated (required) clocks against the clocks available at + * maximum voltage (max Performance Level). + */ +static bool is_display_configuration_supported( + const struct bw_calcs_vbios *vbios, + const struct dce_bw_output *calcs_output) +{ + uint32_t int_max_clk; + + int_max_clk = bw_fixed_to_int(vbios->high_voltage_max_dispclk); + int_max_clk *= 1000; /* MHz to kHz */ + if (calcs_output->dispclk_khz > int_max_clk) + return false; + + int_max_clk = bw_fixed_to_int(vbios->high_sclk); + int_max_clk *= 1000; /* MHz to kHz */ + if (calcs_output->sclk_khz > int_max_clk) + return false; + + return true; +} + +static void populate_initial_data( + const struct pipe_ctx pipe[], int pipe_count, struct bw_calcs_data *data) +{ + int i, j; + int num_displays = 0; + + data->underlay_surface_type = bw_def_420; + data->panning_and_bezel_adjustment = bw_def_none; + data->graphics_lb_bpc = 10; + data->underlay_lb_bpc = 8; + data->underlay_tiling_mode = bw_def_tiled; + data->graphics_tiling_mode = bw_def_tiled; + data->underlay_micro_tile_mode = bw_def_display_micro_tiling; + data->graphics_micro_tile_mode = bw_def_display_micro_tiling; + + /* Pipes with underlay first */ + for (i = 0; i < pipe_count; i++) { + if (!pipe[i].stream || !pipe[i].bottom_pipe) + continue; + + ASSERT(pipe[i].plane_state); + + if (num_displays == 0) { + if (!pipe[i].plane_state->visible) + data->d0_underlay_mode = bw_def_underlay_only; + else + data->d0_underlay_mode = bw_def_blend; + } else { + if (!pipe[i].plane_state->visible) + data->d1_underlay_mode = bw_def_underlay_only; + else + data->d1_underlay_mode = bw_def_blend; + } + + data->fbc_en[num_displays + 4] = false; + data->lpt_en[num_displays + 4] = false; + data->h_total[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.h_total); + data->v_total[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.v_total); + data->pixel_rate[num_displays + 4] = bw_frc_to_fixed(pipe[i].stream->timing.pix_clk_khz, 1000); + data->src_width[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.viewport.width); + data->pitch_in_pixels[num_displays + 4] = data->src_width[num_displays + 4]; + data->src_height[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.viewport.height); + data->h_taps[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.taps.h_taps); + data->v_taps[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.taps.v_taps); + data->h_scale_ratio[num_displays + 4] = fixed31_32_to_bw_fixed(pipe[i].plane_res.scl_data.ratios.horz.value); + data->v_scale_ratio[num_displays + 4] = fixed31_32_to_bw_fixed(pipe[i].plane_res.scl_data.ratios.vert.value); + switch (pipe[i].plane_state->rotation) { + case ROTATION_ANGLE_0: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(0); + break; + case ROTATION_ANGLE_90: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(90); + break; + case ROTATION_ANGLE_180: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(180); + break; + case ROTATION_ANGLE_270: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(270); + break; + default: + break; + } + switch (pipe[i].plane_state->format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + data->bytes_per_pixel[num_displays + 4] = 2; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + data->bytes_per_pixel[num_displays + 4] = 4; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + data->bytes_per_pixel[num_displays + 4] = 8; + break; + default: + data->bytes_per_pixel[num_displays + 4] = 4; + break; + } + data->interlace_mode[num_displays + 4] = false; + data->stereo_mode[num_displays + 4] = bw_def_mono; + + + for (j = 0; j < 2; j++) { + data->fbc_en[num_displays * 2 + j] = false; + data->lpt_en[num_displays * 2 + j] = false; + + data->src_height[num_displays * 2 + j] = bw_int_to_fixed(pipe[i].bottom_pipe->plane_res.scl_data.viewport.height); + data->src_width[num_displays * 2 + j] = bw_int_to_fixed(pipe[i].bottom_pipe->plane_res.scl_data.viewport.width); + data->pitch_in_pixels[num_displays * 2 + j] = bw_int_to_fixed( + pipe[i].bottom_pipe->plane_state->plane_size.grph.surface_pitch); + data->h_taps[num_displays * 2 + j] = bw_int_to_fixed(pipe[i].bottom_pipe->plane_res.scl_data.taps.h_taps); + data->v_taps[num_displays * 2 + j] = bw_int_to_fixed(pipe[i].bottom_pipe->plane_res.scl_data.taps.v_taps); + data->h_scale_ratio[num_displays * 2 + j] = fixed31_32_to_bw_fixed( + pipe[i].bottom_pipe->plane_res.scl_data.ratios.horz.value); + data->v_scale_ratio[num_displays * 2 + j] = fixed31_32_to_bw_fixed( + pipe[i].bottom_pipe->plane_res.scl_data.ratios.vert.value); + switch (pipe[i].bottom_pipe->plane_state->rotation) { + case ROTATION_ANGLE_0: + data->rotation_angle[num_displays * 2 + j] = bw_int_to_fixed(0); + break; + case ROTATION_ANGLE_90: + data->rotation_angle[num_displays * 2 + j] = bw_int_to_fixed(90); + break; + case ROTATION_ANGLE_180: + data->rotation_angle[num_displays * 2 + j] = bw_int_to_fixed(180); + break; + case ROTATION_ANGLE_270: + data->rotation_angle[num_displays * 2 + j] = bw_int_to_fixed(270); + break; + default: + break; + } + data->stereo_mode[num_displays * 2 + j] = bw_def_mono; + } + + num_displays++; + } + + /* Pipes without underlay after */ + for (i = 0; i < pipe_count; i++) { + if (!pipe[i].stream || pipe[i].bottom_pipe) + continue; + + + data->fbc_en[num_displays + 4] = false; + data->lpt_en[num_displays + 4] = false; + data->h_total[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.h_total); + data->v_total[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.v_total); + data->pixel_rate[num_displays + 4] = bw_frc_to_fixed(pipe[i].stream->timing.pix_clk_khz, 1000); + if (pipe[i].plane_state) { + data->src_width[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.viewport.width); + data->pitch_in_pixels[num_displays + 4] = data->src_width[num_displays + 4]; + data->src_height[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.viewport.height); + data->h_taps[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.taps.h_taps); + data->v_taps[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.taps.v_taps); + data->h_scale_ratio[num_displays + 4] = fixed31_32_to_bw_fixed(pipe[i].plane_res.scl_data.ratios.horz.value); + data->v_scale_ratio[num_displays + 4] = fixed31_32_to_bw_fixed(pipe[i].plane_res.scl_data.ratios.vert.value); + switch (pipe[i].plane_state->rotation) { + case ROTATION_ANGLE_0: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(0); + break; + case ROTATION_ANGLE_90: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(90); + break; + case ROTATION_ANGLE_180: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(180); + break; + case ROTATION_ANGLE_270: + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(270); + break; + default: + break; + } + switch (pipe[i].plane_state->format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + data->bytes_per_pixel[num_displays + 4] = 2; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + data->bytes_per_pixel[num_displays + 4] = 4; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + data->bytes_per_pixel[num_displays + 4] = 8; + break; + default: + data->bytes_per_pixel[num_displays + 4] = 4; + break; + } + } else { + data->src_width[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.h_addressable); + data->pitch_in_pixels[num_displays + 4] = data->src_width[num_displays + 4]; + data->src_height[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.v_addressable); + data->h_taps[num_displays + 4] = bw_int_to_fixed(1); + data->v_taps[num_displays + 4] = bw_int_to_fixed(1); + data->h_scale_ratio[num_displays + 4] = bw_int_to_fixed(1); + data->v_scale_ratio[num_displays + 4] = bw_int_to_fixed(1); + data->rotation_angle[num_displays + 4] = bw_int_to_fixed(0); + data->bytes_per_pixel[num_displays + 4] = 4; + } + + data->interlace_mode[num_displays + 4] = false; + data->stereo_mode[num_displays + 4] = bw_def_mono; + num_displays++; + } + + data->number_of_displays = num_displays; +} + +/** + * Return: + * true - Display(s) configuration supported. + * In this case 'calcs_output' contains data for HW programming + * false - Display(s) configuration not supported (not enough bandwidth). + */ + +bool bw_calcs(struct dc_context *ctx, + const struct bw_calcs_dceip *dceip, + const struct bw_calcs_vbios *vbios, + const struct pipe_ctx pipe[], + int pipe_count, + struct dce_bw_output *calcs_output) +{ + struct bw_calcs_data *data = kzalloc(sizeof(struct bw_calcs_data), + GFP_KERNEL); + if (!data) + return false; + + populate_initial_data(pipe, pipe_count, data); + + /*TODO: this should be taken out calcs output and assigned during timing sync for pplib use*/ + calcs_output->all_displays_in_sync = false; + + if (data->number_of_displays != 0) { + uint8_t yclk_lvl, sclk_lvl; + struct bw_fixed high_sclk = vbios->high_sclk; + struct bw_fixed mid1_sclk = vbios->mid1_sclk; + struct bw_fixed mid2_sclk = vbios->mid2_sclk; + struct bw_fixed mid3_sclk = vbios->mid3_sclk; + struct bw_fixed mid4_sclk = vbios->mid4_sclk; + struct bw_fixed mid5_sclk = vbios->mid5_sclk; + struct bw_fixed mid6_sclk = vbios->mid6_sclk; + struct bw_fixed low_sclk = vbios->low_sclk; + struct bw_fixed high_yclk = vbios->high_yclk; + struct bw_fixed mid_yclk = vbios->mid_yclk; + struct bw_fixed low_yclk = vbios->low_yclk; + + calculate_bandwidth(dceip, vbios, data); + + yclk_lvl = data->y_clk_level; + sclk_lvl = data->sclk_level; + + calcs_output->nbp_state_change_enable = + data->nbp_state_change_enable; + calcs_output->cpuc_state_change_enable = + data->cpuc_state_change_enable; + calcs_output->cpup_state_change_enable = + data->cpup_state_change_enable; + calcs_output->stutter_mode_enable = + data->stutter_mode_enable; + calcs_output->dispclk_khz = + bw_fixed_to_int(bw_mul(data->dispclk, + bw_int_to_fixed(1000))); + calcs_output->blackout_recovery_time_us = + bw_fixed_to_int(data->blackout_recovery_time); + calcs_output->sclk_khz = + bw_fixed_to_int(bw_mul(data->required_sclk, + bw_int_to_fixed(1000))); + calcs_output->sclk_deep_sleep_khz = + bw_fixed_to_int(bw_mul(data->sclk_deep_sleep, + bw_int_to_fixed(1000))); + if (yclk_lvl == 0) + calcs_output->yclk_khz = bw_fixed_to_int( + bw_mul(low_yclk, bw_int_to_fixed(1000))); + else if (yclk_lvl == 1) + calcs_output->yclk_khz = bw_fixed_to_int( + bw_mul(mid_yclk, bw_int_to_fixed(1000))); + else + calcs_output->yclk_khz = bw_fixed_to_int( + bw_mul(high_yclk, bw_int_to_fixed(1000))); + + /* units: nanosecond, 16bit storage. */ + + calcs_output->nbp_state_change_wm_ns[0].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[4], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[1].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[5], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[2].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[6], bw_int_to_fixed(1000))); + + if (ctx->dc->caps.max_slave_planes) { + calcs_output->nbp_state_change_wm_ns[3].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[0], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->nbp_state_change_wm_ns[3].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[7], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->nbp_state_change_wm_ns[5].a_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[9], bw_int_to_fixed(1000))); + + + + calcs_output->stutter_exit_wm_ns[0].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[4], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[1].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[5], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[2].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->stutter_exit_wm_ns[3].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[0], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->stutter_exit_wm_ns[3].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[7], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->stutter_exit_wm_ns[5].a_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[9], bw_int_to_fixed(1000))); + + + + calcs_output->urgent_wm_ns[0].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[4], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[1].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[5], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[2].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->urgent_wm_ns[3].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[0], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->urgent_wm_ns[3].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[7], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->urgent_wm_ns[5].a_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[9], bw_int_to_fixed(1000))); + + if (dceip->version != BW_CALCS_VERSION_CARRIZO) { + ((struct bw_calcs_vbios *)vbios)->low_sclk = mid3_sclk; + ((struct bw_calcs_vbios *)vbios)->mid1_sclk = mid3_sclk; + ((struct bw_calcs_vbios *)vbios)->mid2_sclk = mid3_sclk; + calculate_bandwidth(dceip, vbios, data); + + calcs_output->nbp_state_change_wm_ns[0].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[4],bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[1].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[5], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[2].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[6], bw_int_to_fixed(1000))); + + if (ctx->dc->caps.max_slave_planes) { + calcs_output->nbp_state_change_wm_ns[3].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[0], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->nbp_state_change_wm_ns[3].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[7], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->nbp_state_change_wm_ns[5].b_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[9], bw_int_to_fixed(1000))); + + + + calcs_output->stutter_exit_wm_ns[0].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[4], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[1].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[5], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[2].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->stutter_exit_wm_ns[3].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[0], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->stutter_exit_wm_ns[3].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[7], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->stutter_exit_wm_ns[5].b_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[9], bw_int_to_fixed(1000))); + + + + calcs_output->urgent_wm_ns[0].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[4], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[1].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[5], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[2].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->urgent_wm_ns[3].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[0], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->urgent_wm_ns[3].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[7], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->urgent_wm_ns[5].b_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[9], bw_int_to_fixed(1000))); + + ((struct bw_calcs_vbios *)vbios)->low_sclk = low_sclk; + ((struct bw_calcs_vbios *)vbios)->mid1_sclk = mid1_sclk; + ((struct bw_calcs_vbios *)vbios)->mid2_sclk = mid2_sclk; + ((struct bw_calcs_vbios *)vbios)->low_yclk = mid_yclk; + calculate_bandwidth(dceip, vbios, data); + + calcs_output->nbp_state_change_wm_ns[0].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[4], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[1].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[5], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[2].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->nbp_state_change_wm_ns[3].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[0], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->nbp_state_change_wm_ns[3].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[7], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->nbp_state_change_wm_ns[5].c_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[9], bw_int_to_fixed(1000))); + + + calcs_output->stutter_exit_wm_ns[0].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[4], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[1].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[5], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[2].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->stutter_exit_wm_ns[3].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[0], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->stutter_exit_wm_ns[3].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[7], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->stutter_exit_wm_ns[5].c_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[9], bw_int_to_fixed(1000))); + + calcs_output->urgent_wm_ns[0].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[4], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[1].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[5], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[2].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->urgent_wm_ns[3].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[0], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->urgent_wm_ns[3].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[7], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->urgent_wm_ns[5].c_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[9], bw_int_to_fixed(1000))); + } + + if (dceip->version == BW_CALCS_VERSION_CARRIZO) { + ((struct bw_calcs_vbios *)vbios)->low_yclk = high_yclk; + ((struct bw_calcs_vbios *)vbios)->mid_yclk = high_yclk; + ((struct bw_calcs_vbios *)vbios)->low_sclk = high_sclk; + ((struct bw_calcs_vbios *)vbios)->mid1_sclk = high_sclk; + ((struct bw_calcs_vbios *)vbios)->mid2_sclk = high_sclk; + ((struct bw_calcs_vbios *)vbios)->mid3_sclk = high_sclk; + ((struct bw_calcs_vbios *)vbios)->mid4_sclk = high_sclk; + ((struct bw_calcs_vbios *)vbios)->mid5_sclk = high_sclk; + ((struct bw_calcs_vbios *)vbios)->mid6_sclk = high_sclk; + } else { + ((struct bw_calcs_vbios *)vbios)->low_yclk = mid_yclk; + ((struct bw_calcs_vbios *)vbios)->low_sclk = mid3_sclk; + ((struct bw_calcs_vbios *)vbios)->mid1_sclk = mid3_sclk; + ((struct bw_calcs_vbios *)vbios)->mid2_sclk = mid3_sclk; + } + + calculate_bandwidth(dceip, vbios, data); + + calcs_output->nbp_state_change_wm_ns[0].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[4], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[1].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[5], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[2].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->nbp_state_change_wm_ns[3].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[0], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->nbp_state_change_wm_ns[3].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[7], bw_int_to_fixed(1000))); + calcs_output->nbp_state_change_wm_ns[4].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->nbp_state_change_wm_ns[5].d_mark = + bw_fixed_to_int(bw_mul(data-> + nbp_state_change_watermark[9], bw_int_to_fixed(1000))); + + calcs_output->stutter_exit_wm_ns[0].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[4], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[1].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[5], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[2].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->stutter_exit_wm_ns[3].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[0], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->stutter_exit_wm_ns[3].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[7], bw_int_to_fixed(1000))); + calcs_output->stutter_exit_wm_ns[4].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->stutter_exit_wm_ns[5].d_mark = + bw_fixed_to_int(bw_mul(data-> + stutter_exit_watermark[9], bw_int_to_fixed(1000))); + + + calcs_output->urgent_wm_ns[0].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[4], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[1].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[5], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[2].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[6], bw_int_to_fixed(1000))); + if (ctx->dc->caps.max_slave_planes) { + calcs_output->urgent_wm_ns[3].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[0], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[1], bw_int_to_fixed(1000))); + } else { + calcs_output->urgent_wm_ns[3].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[7], bw_int_to_fixed(1000))); + calcs_output->urgent_wm_ns[4].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[8], bw_int_to_fixed(1000))); + } + calcs_output->urgent_wm_ns[5].d_mark = + bw_fixed_to_int(bw_mul(data-> + urgent_watermark[9], bw_int_to_fixed(1000))); + + ((struct bw_calcs_vbios *)vbios)->low_yclk = low_yclk; + ((struct bw_calcs_vbios *)vbios)->mid_yclk = mid_yclk; + ((struct bw_calcs_vbios *)vbios)->low_sclk = low_sclk; + ((struct bw_calcs_vbios *)vbios)->mid1_sclk = mid1_sclk; + ((struct bw_calcs_vbios *)vbios)->mid2_sclk = mid2_sclk; + ((struct bw_calcs_vbios *)vbios)->mid3_sclk = mid3_sclk; + ((struct bw_calcs_vbios *)vbios)->mid4_sclk = mid4_sclk; + ((struct bw_calcs_vbios *)vbios)->mid5_sclk = mid5_sclk; + ((struct bw_calcs_vbios *)vbios)->mid6_sclk = mid6_sclk; + ((struct bw_calcs_vbios *)vbios)->high_sclk = high_sclk; + } else { + calcs_output->nbp_state_change_enable = true; + calcs_output->cpuc_state_change_enable = true; + calcs_output->cpup_state_change_enable = true; + calcs_output->stutter_mode_enable = true; + calcs_output->dispclk_khz = 0; + calcs_output->sclk_khz = 0; + } + + kfree(data); + + return is_display_configuration_supported(vbios, calcs_output); +} diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_auto.c b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_auto.c new file mode 100644 index 000000000000..626f9cf8aad2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_auto.c @@ -0,0 +1,1899 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dcn_calc_auto.h" +#include "dcn_calc_math.h" + +/*REVISION#250*/ +void scaler_settings_calculation(struct dcn_bw_internal_vars *v) +{ + int k; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->allow_different_hratio_vratio == dcn_bw_yes) { + if (v->source_scan[k] == dcn_bw_hor) { + v->h_ratio[k] = v->viewport_width[k] / v->scaler_rec_out_width[k]; + v->v_ratio[k] = v->viewport_height[k] / v->scaler_recout_height[k]; + } + else { + v->h_ratio[k] = v->viewport_height[k] / v->scaler_rec_out_width[k]; + v->v_ratio[k] = v->viewport_width[k] / v->scaler_recout_height[k]; + } + } + else { + if (v->source_scan[k] == dcn_bw_hor) { + v->h_ratio[k] =dcn_bw_max2(v->viewport_width[k] / v->scaler_rec_out_width[k], v->viewport_height[k] / v->scaler_recout_height[k]); + } + else { + v->h_ratio[k] =dcn_bw_max2(v->viewport_height[k] / v->scaler_rec_out_width[k], v->viewport_width[k] / v->scaler_recout_height[k]); + } + v->v_ratio[k] = v->h_ratio[k]; + } + if (v->interlace_output[k] == 1.0) { + v->v_ratio[k] = 2.0 * v->v_ratio[k]; + } + if ((v->underscan_output[k] == 1.0)) { + v->h_ratio[k] = v->h_ratio[k] * v->under_scan_factor; + v->v_ratio[k] = v->v_ratio[k] * v->under_scan_factor; + } + } + /*scaler taps calculation*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->h_ratio[k] > 1.0) { + v->acceptable_quality_hta_ps =dcn_bw_min2(v->max_hscl_taps, 2.0 *dcn_bw_ceil2(v->h_ratio[k], 1.0)); + } + else if (v->h_ratio[k] < 1.0) { + v->acceptable_quality_hta_ps = 4.0; + } + else { + v->acceptable_quality_hta_ps = 1.0; + } + if (v->ta_pscalculation == dcn_bw_override) { + v->htaps[k] = v->override_hta_ps[k]; + } + else { + v->htaps[k] = v->acceptable_quality_hta_ps; + } + if (v->v_ratio[k] > 1.0) { + v->acceptable_quality_vta_ps =dcn_bw_min2(v->max_vscl_taps, 2.0 *dcn_bw_ceil2(v->v_ratio[k], 1.0)); + } + else if (v->v_ratio[k] < 1.0) { + v->acceptable_quality_vta_ps = 4.0; + } + else { + v->acceptable_quality_vta_ps = 1.0; + } + if (v->ta_pscalculation == dcn_bw_override) { + v->vtaps[k] = v->override_vta_ps[k]; + } + else { + v->vtaps[k] = v->acceptable_quality_vta_ps; + } + if (v->source_pixel_format[k] == dcn_bw_rgb_sub_64 || v->source_pixel_format[k] == dcn_bw_rgb_sub_32 || v->source_pixel_format[k] == dcn_bw_rgb_sub_16) { + v->vta_pschroma[k] = 0.0; + v->hta_pschroma[k] = 0.0; + } + else { + if (v->ta_pscalculation == dcn_bw_override) { + v->vta_pschroma[k] = v->override_vta_pschroma[k]; + v->hta_pschroma[k] = v->override_hta_pschroma[k]; + } + else { + v->vta_pschroma[k] = v->acceptable_quality_vta_ps; + v->hta_pschroma[k] = v->acceptable_quality_hta_ps; + } + } + } +} + +void mode_support_and_system_configuration(struct dcn_bw_internal_vars *v) +{ + int i; + int j; + int k; + /*mode support, voltage state and soc configuration*/ + + /*scale ratio support check*/ + + v->scale_ratio_support = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->h_ratio[k] > v->max_hscl_ratio || v->v_ratio[k] > v->max_vscl_ratio || v->h_ratio[k] > v->htaps[k] || v->v_ratio[k] > v->vtaps[k] || (v->source_pixel_format[k] != dcn_bw_rgb_sub_64 && v->source_pixel_format[k] != dcn_bw_rgb_sub_32 && v->source_pixel_format[k] != dcn_bw_rgb_sub_16 && (v->h_ratio[k] / 2.0 > v->hta_pschroma[k] || v->v_ratio[k] / 2.0 > v->vta_pschroma[k]))) { + v->scale_ratio_support = dcn_bw_no; + } + } + /*source format, pixel format and scan support check*/ + + v->source_format_pixel_and_scan_support = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if ((v->source_surface_mode[k] == dcn_bw_sw_linear && v->source_scan[k] != dcn_bw_hor) || ((v->source_surface_mode[k] == dcn_bw_sw_4_kb_d || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_x || v->source_surface_mode[k] == dcn_bw_sw_var_d || v->source_surface_mode[k] == dcn_bw_sw_var_d_x) && v->source_pixel_format[k] != dcn_bw_rgb_sub_64)) { + v->source_format_pixel_and_scan_support = dcn_bw_no; + } + } + /*bandwidth support check*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->source_scan[k] == dcn_bw_hor) { + v->swath_width_ysingle_dpp[k] = v->viewport_width[k]; + } + else { + v->swath_width_ysingle_dpp[k] = v->viewport_height[k]; + } + if (v->source_pixel_format[k] == dcn_bw_rgb_sub_64) { + v->byte_per_pixel_in_dety[k] = 8.0; + v->byte_per_pixel_in_detc[k] = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_32) { + v->byte_per_pixel_in_dety[k] = 4.0; + v->byte_per_pixel_in_detc[k] = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_16) { + v->byte_per_pixel_in_dety[k] = 2.0; + v->byte_per_pixel_in_detc[k] = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8) { + v->byte_per_pixel_in_dety[k] = 1.0; + v->byte_per_pixel_in_detc[k] = 2.0; + } + else { + v->byte_per_pixel_in_dety[k] = 4.0f / 3.0f; + v->byte_per_pixel_in_detc[k] = 8.0f / 3.0f; + } + } + v->total_read_bandwidth_consumed_gbyte_per_second = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->read_bandwidth[k] = v->swath_width_ysingle_dpp[k] * (dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) * v->v_ratio[k] +dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 2.0 * v->v_ratio[k] / 2) / (v->htotal[k] / v->pixel_clock[k]); + if (v->dcc_enable[k] == dcn_bw_yes) { + v->read_bandwidth[k] = v->read_bandwidth[k] * (1 + 1 / 256); + } + if (v->pte_enable == dcn_bw_yes && v->source_scan[k] != dcn_bw_hor && (v->source_surface_mode[k] == dcn_bw_sw_4_kb_s || v->source_surface_mode[k] == dcn_bw_sw_4_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d_x)) { + v->read_bandwidth[k] = v->read_bandwidth[k] * (1 + 1 / 64); + } + else if (v->pte_enable == dcn_bw_yes && v->source_scan[k] == dcn_bw_hor && (v->source_pixel_format[k] == dcn_bw_rgb_sub_64 || v->source_pixel_format[k] == dcn_bw_rgb_sub_32) && (v->source_surface_mode[k] == dcn_bw_sw_64_kb_s || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_x)) { + v->read_bandwidth[k] = v->read_bandwidth[k] * (1 + 1 / 256); + } + else if (v->pte_enable == dcn_bw_yes) { + v->read_bandwidth[k] = v->read_bandwidth[k] * (1 + 1 / 512); + } + v->total_read_bandwidth_consumed_gbyte_per_second = v->total_read_bandwidth_consumed_gbyte_per_second + v->read_bandwidth[k] / 1000.0; + } + v->total_write_bandwidth_consumed_gbyte_per_second = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->output[k] == dcn_bw_writeback && v->output_format[k] == dcn_bw_444) { + v->write_bandwidth[k] = v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k]) * 4.0; + } + else if (v->output[k] == dcn_bw_writeback) { + v->write_bandwidth[k] = v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k]) * 1.5; + } + else { + v->write_bandwidth[k] = 0.0; + } + v->total_write_bandwidth_consumed_gbyte_per_second = v->total_write_bandwidth_consumed_gbyte_per_second + v->write_bandwidth[k] / 1000.0; + } + v->total_bandwidth_consumed_gbyte_per_second = v->total_read_bandwidth_consumed_gbyte_per_second + v->total_write_bandwidth_consumed_gbyte_per_second; + v->dcc_enabled_in_any_plane = dcn_bw_no; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->dcc_enable[k] == dcn_bw_yes) { + v->dcc_enabled_in_any_plane = dcn_bw_yes; + } + } + for (i = 0; i <= number_of_states_plus_one; i++) { + v->return_bw_todcn_per_state =dcn_bw_min2(v->return_bus_width * v->dcfclk_per_state[i], v->fabric_and_dram_bandwidth_per_state[i] * 1000.0 * v->percent_of_ideal_drambw_received_after_urg_latency / 100.0); + v->return_bw_per_state[i] = v->return_bw_todcn_per_state; + if (v->dcc_enabled_in_any_plane == dcn_bw_yes && v->return_bw_todcn_per_state > v->dcfclk_per_state[i] * v->return_bus_width / 4.0) { + v->return_bw_per_state[i] =dcn_bw_min2(v->return_bw_per_state[i], v->return_bw_todcn_per_state * 4.0 * (1.0 - v->urgent_latency / ((v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 / (v->return_bw_todcn_per_state - v->dcfclk_per_state[i] * v->return_bus_width / 4.0) + v->urgent_latency))); + } + v->critical_point = 2.0 * v->return_bus_width * v->dcfclk_per_state[i] * v->urgent_latency / (v->return_bw_todcn_per_state * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0); + if (v->dcc_enabled_in_any_plane == dcn_bw_yes && v->critical_point > 1.0 && v->critical_point < 4.0) { + v->return_bw_per_state[i] =dcn_bw_min2(v->return_bw_per_state[i], dcn_bw_pow(4.0 * v->return_bw_todcn_per_state * (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 * v->return_bus_width * v->dcfclk_per_state[i] * v->urgent_latency / (v->return_bw_todcn_per_state * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0), 2)); + } + v->return_bw_todcn_per_state =dcn_bw_min2(v->return_bus_width * v->dcfclk_per_state[i], v->fabric_and_dram_bandwidth_per_state[i] * 1000.0); + if (v->dcc_enabled_in_any_plane == dcn_bw_yes && v->return_bw_todcn_per_state > v->dcfclk_per_state[i] * v->return_bus_width / 4.0) { + v->return_bw_per_state[i] =dcn_bw_min2(v->return_bw_per_state[i], v->return_bw_todcn_per_state * 4.0 * (1.0 - v->urgent_latency / ((v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 / (v->return_bw_todcn_per_state - v->dcfclk_per_state[i] * v->return_bus_width / 4.0) + v->urgent_latency))); + } + v->critical_point = 2.0 * v->return_bus_width * v->dcfclk_per_state[i] * v->urgent_latency / (v->return_bw_todcn_per_state * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0); + if (v->dcc_enabled_in_any_plane == dcn_bw_yes && v->critical_point > 1.0 && v->critical_point < 4.0) { + v->return_bw_per_state[i] =dcn_bw_min2(v->return_bw_per_state[i], dcn_bw_pow(4.0 * v->return_bw_todcn_per_state * (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 * v->return_bus_width * v->dcfclk_per_state[i] * v->urgent_latency / (v->return_bw_todcn_per_state * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0), 2)); + } + } + for (i = 0; i <= number_of_states_plus_one; i++) { + if ((v->total_read_bandwidth_consumed_gbyte_per_second * 1000.0 <= v->return_bw_per_state[i]) && (v->total_bandwidth_consumed_gbyte_per_second * 1000.0 <= v->fabric_and_dram_bandwidth_per_state[i] * 1000.0 * v->percent_of_ideal_drambw_received_after_urg_latency / 100.0)) { + v->bandwidth_support[i] = dcn_bw_yes; + } + else { + v->bandwidth_support[i] = dcn_bw_no; + } + } + /*writeback latency support check*/ + + v->writeback_latency_support = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->output[k] == dcn_bw_writeback && v->output_format[k] == dcn_bw_444 && v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k]) * 4.0 > (v->writeback_luma_buffer_size + v->writeback_chroma_buffer_size) * 1024.0 / v->write_back_latency) { + v->writeback_latency_support = dcn_bw_no; + } + else if (v->output[k] == dcn_bw_writeback && v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k]) >dcn_bw_min2(v->writeback_luma_buffer_size, 2.0 * v->writeback_chroma_buffer_size) * 1024.0 / v->write_back_latency) { + v->writeback_latency_support = dcn_bw_no; + } + } + /*re-ordering buffer support check*/ + + for (i = 0; i <= number_of_states_plus_one; i++) { + v->urgent_round_trip_and_out_of_order_latency_per_state[i] = (v->round_trip_ping_latency_cycles + 32.0) / v->dcfclk_per_state[i] + v->urgent_out_of_order_return_per_channel * v->number_of_channels / v->return_bw_per_state[i]; + if ((v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 / v->return_bw_per_state[i] > v->urgent_round_trip_and_out_of_order_latency_per_state[i]) { + v->rob_support[i] = dcn_bw_yes; + } + else { + v->rob_support[i] = dcn_bw_no; + } + } + /*display io support check*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->output[k] == dcn_bw_dp && v->dsc_capability == dcn_bw_yes) { + if (v->output_format[k] == dcn_bw_420) { + v->required_output_bw = v->pixel_clock[k] / 2.0; + } + else { + v->required_output_bw = v->pixel_clock[k]; + } + } + else if (v->output_format[k] == dcn_bw_420) { + v->required_output_bw = v->pixel_clock[k] * 3.0 / 2.0; + } + else { + v->required_output_bw = v->pixel_clock[k] * 3.0; + } + if (v->output[k] == dcn_bw_hdmi) { + v->required_phyclk[k] = v->required_output_bw; + switch (v->output_deep_color[k]) { + case dcn_bw_encoder_10bpc: + v->required_phyclk[k] = v->required_phyclk[k] * 5.0 / 4; + break; + case dcn_bw_encoder_12bpc: + v->required_phyclk[k] = v->required_phyclk[k] * 3.0 / 2; + break; + default: + break; + } + v->required_phyclk[k] = v->required_phyclk[k] / 3.0; + } + else if (v->output[k] == dcn_bw_dp) { + v->required_phyclk[k] = v->required_output_bw / 4.0; + } + else { + v->required_phyclk[k] = 0.0; + } + } + for (i = 0; i <= number_of_states_plus_one; i++) { + v->dio_support[i] = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->required_phyclk[k] > v->phyclk_per_state[i] || (v->output[k] == dcn_bw_hdmi && v->required_phyclk[k] > 600.0)) { + v->dio_support[i] = dcn_bw_no; + } + } + } + /*total available writeback support check*/ + + v->total_number_of_active_writeback = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->output[k] == dcn_bw_writeback) { + v->total_number_of_active_writeback = v->total_number_of_active_writeback + 1.0; + } + } + if (v->total_number_of_active_writeback <= v->max_num_writeback) { + v->total_available_writeback_support = dcn_bw_yes; + } + else { + v->total_available_writeback_support = dcn_bw_no; + } + /*maximum dispclk/dppclk support check*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->h_ratio[k] > 1.0) { + v->pscl_factor[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput * v->h_ratio[k] /dcn_bw_ceil2(v->htaps[k] / 6.0, 1.0)); + } + else { + v->pscl_factor[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput); + } + if (v->byte_per_pixel_in_detc[k] == 0.0) { + v->pscl_factor_chroma[k] = 0.0; + v->min_dppclk_using_single_dpp[k] = v->pixel_clock[k] *dcn_bw_max3(v->vtaps[k] / 6.0 *dcn_bw_min2(1.0, v->h_ratio[k]), v->h_ratio[k] * v->v_ratio[k] / v->pscl_factor[k], 1.0); + } + else { + if (v->h_ratio[k] / 2.0 > 1.0) { + v->pscl_factor_chroma[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput * v->h_ratio[k] / 2.0 /dcn_bw_ceil2(v->hta_pschroma[k] / 6.0, 1.0)); + } + else { + v->pscl_factor_chroma[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput); + } + v->min_dppclk_using_single_dpp[k] = v->pixel_clock[k] *dcn_bw_max5(v->vtaps[k] / 6.0 *dcn_bw_min2(1.0, v->h_ratio[k]), v->h_ratio[k] * v->v_ratio[k] / v->pscl_factor[k], v->vta_pschroma[k] / 6.0 *dcn_bw_min2(1.0, v->h_ratio[k] / 2.0), v->h_ratio[k] * v->v_ratio[k] / 4.0 / v->pscl_factor_chroma[k], 1.0); + } + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if ((v->source_pixel_format[k] == dcn_bw_rgb_sub_64 || v->source_pixel_format[k] == dcn_bw_rgb_sub_32 || v->source_pixel_format[k] == dcn_bw_rgb_sub_16)) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->read256_block_height_y[k] = 1.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_64) { + v->read256_block_height_y[k] = 4.0; + } + else { + v->read256_block_height_y[k] = 8.0; + } + v->read256_block_width_y[k] = 256.0 /dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / v->read256_block_height_y[k]; + v->read256_block_height_c[k] = 0.0; + v->read256_block_width_c[k] = 0.0; + } + else { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->read256_block_height_y[k] = 1.0; + v->read256_block_height_c[k] = 1.0; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8) { + v->read256_block_height_y[k] = 16.0; + v->read256_block_height_c[k] = 8.0; + } + else { + v->read256_block_height_y[k] = 8.0; + v->read256_block_height_c[k] = 8.0; + } + v->read256_block_width_y[k] = 256.0 /dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / v->read256_block_height_y[k]; + v->read256_block_width_c[k] = 256.0 /dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / v->read256_block_height_c[k]; + } + if (v->source_scan[k] == dcn_bw_hor) { + v->max_swath_height_y[k] = v->read256_block_height_y[k]; + v->max_swath_height_c[k] = v->read256_block_height_c[k]; + } + else { + v->max_swath_height_y[k] = v->read256_block_width_y[k]; + v->max_swath_height_c[k] = v->read256_block_width_c[k]; + } + if ((v->source_pixel_format[k] == dcn_bw_rgb_sub_64 || v->source_pixel_format[k] == dcn_bw_rgb_sub_32 || v->source_pixel_format[k] == dcn_bw_rgb_sub_16)) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear || (v->source_pixel_format[k] == dcn_bw_rgb_sub_64 && (v->source_surface_mode[k] == dcn_bw_sw_4_kb_s || v->source_surface_mode[k] == dcn_bw_sw_4_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_var_s || v->source_surface_mode[k] == dcn_bw_sw_var_s_x) && v->source_scan[k] == dcn_bw_hor)) { + v->min_swath_height_y[k] = v->max_swath_height_y[k]; + } + else { + v->min_swath_height_y[k] = v->max_swath_height_y[k] / 2.0; + } + v->min_swath_height_c[k] = v->max_swath_height_c[k]; + } + else { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->min_swath_height_y[k] = v->max_swath_height_y[k]; + v->min_swath_height_c[k] = v->max_swath_height_c[k]; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8 && v->source_scan[k] == dcn_bw_hor) { + v->min_swath_height_y[k] = v->max_swath_height_y[k] / 2.0; + if (v->bug_forcing_luma_and_chroma_request_to_same_size_fixed == dcn_bw_yes) { + v->min_swath_height_c[k] = v->max_swath_height_c[k]; + } + else { + v->min_swath_height_c[k] = v->max_swath_height_c[k] / 2.0; + } + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_10 && v->source_scan[k] == dcn_bw_hor) { + v->min_swath_height_c[k] = v->max_swath_height_c[k] / 2.0; + if (v->bug_forcing_luma_and_chroma_request_to_same_size_fixed == dcn_bw_yes) { + v->min_swath_height_y[k] = v->max_swath_height_y[k]; + } + else { + v->min_swath_height_y[k] = v->max_swath_height_y[k] / 2.0; + } + } + else { + v->min_swath_height_y[k] = v->max_swath_height_y[k]; + v->min_swath_height_c[k] = v->max_swath_height_c[k]; + } + } + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->maximum_swath_width = 8192.0; + } + else { + v->maximum_swath_width = 5120.0; + } + v->number_of_dpp_required_for_det_size =dcn_bw_ceil2(v->swath_width_ysingle_dpp[k] /dcn_bw_min2(v->maximum_swath_width, v->det_buffer_size_in_kbyte * 1024.0 / 2.0 / (v->byte_per_pixel_in_dety[k] * v->min_swath_height_y[k] + v->byte_per_pixel_in_detc[k] / 2.0 * v->min_swath_height_c[k])), 1.0); + if (v->byte_per_pixel_in_detc[k] == 0.0) { + v->number_of_dpp_required_for_lb_size =dcn_bw_ceil2((v->vtaps[k] +dcn_bw_max2(dcn_bw_ceil2(v->v_ratio[k], 1.0) - 2, 0.0)) * v->swath_width_ysingle_dpp[k] /dcn_bw_max2(v->h_ratio[k], 1.0) * v->lb_bit_per_pixel[k] / v->line_buffer_size, 1.0); + } + else { + v->number_of_dpp_required_for_lb_size =dcn_bw_max2(dcn_bw_ceil2((v->vtaps[k] +dcn_bw_max2(dcn_bw_ceil2(v->v_ratio[k], 1.0) - 2, 0.0)) * v->swath_width_ysingle_dpp[k] /dcn_bw_max2(v->h_ratio[k], 1.0) * v->lb_bit_per_pixel[k] / v->line_buffer_size, 1.0),dcn_bw_ceil2((v->vta_pschroma[k] +dcn_bw_max2(dcn_bw_ceil2(v->v_ratio[k] / 2.0, 1.0) - 2, 0.0)) * v->swath_width_ysingle_dpp[k] / 2.0 /dcn_bw_max2(v->h_ratio[k] / 2.0, 1.0) * v->lb_bit_per_pixel[k] / v->line_buffer_size, 1.0)); + } + v->number_of_dpp_required_for_det_and_lb_size[k] =dcn_bw_max2(v->number_of_dpp_required_for_det_size, v->number_of_dpp_required_for_lb_size); + } + for (i = 0; i <= number_of_states_plus_one; i++) { + for (j = 0; j <= 1; j++) { + v->total_number_of_active_dpp[i][j] = 0.0; + v->required_dispclk[i][j] = 0.0; + v->dispclk_dppclk_support[i][j] = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->min_dispclk_using_single_dpp =dcn_bw_max2(v->pixel_clock[k], v->min_dppclk_using_single_dpp[k] * (j + 1)) * (1.0 + v->downspreading / 100.0); + if (v->odm_capability == dcn_bw_yes) { + v->min_dispclk_using_dual_dpp =dcn_bw_max2(v->pixel_clock[k] / 2.0, v->min_dppclk_using_single_dpp[k] / 2.0 * (j + 1)) * (1.0 + v->downspreading / 100.0); + } + else { + v->min_dispclk_using_dual_dpp =dcn_bw_max2(v->pixel_clock[k], v->min_dppclk_using_single_dpp[k] / 2.0 * (j + 1)) * (1.0 + v->downspreading / 100.0); + } + if (i < number_of_states) { + v->min_dispclk_using_single_dpp = v->min_dispclk_using_single_dpp * (1.0 + v->dispclk_ramping_margin / 100.0); + v->min_dispclk_using_dual_dpp = v->min_dispclk_using_dual_dpp * (1.0 + v->dispclk_ramping_margin / 100.0); + } + if (v->min_dispclk_using_single_dpp <=dcn_bw_min2(v->max_dispclk[i], (j + 1) * v->max_dppclk[i]) && v->number_of_dpp_required_for_det_and_lb_size[k] <= 1.0) { + v->no_of_dpp[i][j][k] = 1.0; + v->required_dispclk[i][j] =dcn_bw_max2(v->required_dispclk[i][j], v->min_dispclk_using_single_dpp); + } + else if (v->min_dispclk_using_dual_dpp <=dcn_bw_min2(v->max_dispclk[i], (j + 1) * v->max_dppclk[i])) { + v->no_of_dpp[i][j][k] = 2.0; + v->required_dispclk[i][j] =dcn_bw_max2(v->required_dispclk[i][j], v->min_dispclk_using_dual_dpp); + } + else { + v->no_of_dpp[i][j][k] = 2.0; + v->required_dispclk[i][j] =dcn_bw_max2(v->required_dispclk[i][j], v->min_dispclk_using_dual_dpp); + v->dispclk_dppclk_support[i][j] = dcn_bw_no; + } + v->total_number_of_active_dpp[i][j] = v->total_number_of_active_dpp[i][j] + v->no_of_dpp[i][j][k]; + } + if (v->total_number_of_active_dpp[i][j] > v->max_num_dpp) { + v->total_number_of_active_dpp[i][j] = 0.0; + v->required_dispclk[i][j] = 0.0; + v->dispclk_dppclk_support[i][j] = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->min_dispclk_using_single_dpp =dcn_bw_max2(v->pixel_clock[k], v->min_dppclk_using_single_dpp[k] * (j + 1)) * (1.0 + v->downspreading / 100.0); + v->min_dispclk_using_dual_dpp =dcn_bw_max2(v->pixel_clock[k], v->min_dppclk_using_single_dpp[k] / 2.0 * (j + 1)) * (1.0 + v->downspreading / 100.0); + if (i < number_of_states) { + v->min_dispclk_using_single_dpp = v->min_dispclk_using_single_dpp * (1.0 + v->dispclk_ramping_margin / 100.0); + v->min_dispclk_using_dual_dpp = v->min_dispclk_using_dual_dpp * (1.0 + v->dispclk_ramping_margin / 100.0); + } + if (v->number_of_dpp_required_for_det_and_lb_size[k] <= 1.0) { + v->no_of_dpp[i][j][k] = 1.0; + v->required_dispclk[i][j] =dcn_bw_max2(v->required_dispclk[i][j], v->min_dispclk_using_single_dpp); + if (v->min_dispclk_using_single_dpp >dcn_bw_min2(v->max_dispclk[i], (j + 1) * v->max_dppclk[i])) { + v->dispclk_dppclk_support[i][j] = dcn_bw_no; + } + } + else { + v->no_of_dpp[i][j][k] = 2.0; + v->required_dispclk[i][j] =dcn_bw_max2(v->required_dispclk[i][j], v->min_dispclk_using_dual_dpp); + if (v->min_dispclk_using_dual_dpp >dcn_bw_min2(v->max_dispclk[i], (j + 1) * v->max_dppclk[i])) { + v->dispclk_dppclk_support[i][j] = dcn_bw_no; + } + } + v->total_number_of_active_dpp[i][j] = v->total_number_of_active_dpp[i][j] + v->no_of_dpp[i][j][k]; + } + } + } + } + /*viewport size check*/ + + v->viewport_size_support = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->number_of_dpp_required_for_det_and_lb_size[k] > 2.0) { + v->viewport_size_support = dcn_bw_no; + } + } + /*total available pipes support check*/ + + for (i = 0; i <= number_of_states_plus_one; i++) { + for (j = 0; j <= 1; j++) { + if (v->total_number_of_active_dpp[i][j] <= v->max_num_dpp) { + v->total_available_pipes_support[i][j] = dcn_bw_yes; + } + else { + v->total_available_pipes_support[i][j] = dcn_bw_no; + } + } + } + /*urgent latency support check*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + for (i = 0; i <= number_of_states_plus_one; i++) { + for (j = 0; j <= 1; j++) { + v->swath_width_yper_state[i][j][k] = v->swath_width_ysingle_dpp[k] / v->no_of_dpp[i][j][k]; + v->swath_width_granularity_y = 256.0 /dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / v->max_swath_height_y[k]; + v->rounded_up_max_swath_size_bytes_y = (dcn_bw_ceil2(v->swath_width_yper_state[i][j][k] - 1.0, v->swath_width_granularity_y) + v->swath_width_granularity_y) * v->byte_per_pixel_in_dety[k] * v->max_swath_height_y[k]; + if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_10) { + v->rounded_up_max_swath_size_bytes_y =dcn_bw_ceil2(v->rounded_up_max_swath_size_bytes_y, 256.0) + 256; + } + if (v->max_swath_height_c[k] > 0.0) { + v->swath_width_granularity_c = 256.0 /dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / v->max_swath_height_c[k]; + } + v->rounded_up_max_swath_size_bytes_c = (dcn_bw_ceil2(v->swath_width_yper_state[i][j][k] / 2.0 - 1.0, v->swath_width_granularity_c) + v->swath_width_granularity_c) * v->byte_per_pixel_in_detc[k] * v->max_swath_height_c[k]; + if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_10) { + v->rounded_up_max_swath_size_bytes_c =dcn_bw_ceil2(v->rounded_up_max_swath_size_bytes_c, 256.0) + 256; + } + if (v->rounded_up_max_swath_size_bytes_y + v->rounded_up_max_swath_size_bytes_c <= v->det_buffer_size_in_kbyte * 1024.0 / 2.0) { + v->swath_height_yper_state[i][j][k] = v->max_swath_height_y[k]; + v->swath_height_cper_state[i][j][k] = v->max_swath_height_c[k]; + } + else { + v->swath_height_yper_state[i][j][k] = v->min_swath_height_y[k]; + v->swath_height_cper_state[i][j][k] = v->min_swath_height_c[k]; + } + if (v->byte_per_pixel_in_detc[k] == 0.0) { + v->lines_in_det_luma = v->det_buffer_size_in_kbyte * 1024.0 / v->byte_per_pixel_in_dety[k] / v->swath_width_yper_state[i][j][k]; + v->lines_in_det_chroma = 0.0; + } + else if (v->swath_height_yper_state[i][j][k] <= v->swath_height_cper_state[i][j][k]) { + v->lines_in_det_luma = v->det_buffer_size_in_kbyte * 1024.0 / 2.0 / v->byte_per_pixel_in_dety[k] / v->swath_width_yper_state[i][j][k]; + v->lines_in_det_chroma = v->det_buffer_size_in_kbyte * 1024.0 / 2.0 / v->byte_per_pixel_in_detc[k] / (v->swath_width_yper_state[i][j][k] / 2.0); + } + else { + v->lines_in_det_luma = v->det_buffer_size_in_kbyte * 1024.0 * 2.0 / 3.0 / v->byte_per_pixel_in_dety[k] / v->swath_width_yper_state[i][j][k]; + v->lines_in_det_chroma = v->det_buffer_size_in_kbyte * 1024.0 / 3.0 / v->byte_per_pixel_in_dety[k] / (v->swath_width_yper_state[i][j][k] / 2.0); + } + v->effective_lb_latency_hiding_source_lines_luma =dcn_bw_min2(v->max_line_buffer_lines,dcn_bw_floor2(v->line_buffer_size / v->lb_bit_per_pixel[k] / (v->swath_width_yper_state[i][j][k] /dcn_bw_max2(v->h_ratio[k], 1.0)), 1.0)) - (v->vtaps[k] - 1.0); + v->effective_lb_latency_hiding_source_lines_chroma =dcn_bw_min2(v->max_line_buffer_lines,dcn_bw_floor2(v->line_buffer_size / v->lb_bit_per_pixel[k] / (v->swath_width_yper_state[i][j][k] / 2.0 /dcn_bw_max2(v->h_ratio[k] / 2.0, 1.0)), 1.0)) - (v->vta_pschroma[k] - 1.0); + v->effective_detlb_lines_luma =dcn_bw_floor2(v->lines_in_det_luma +dcn_bw_min2(v->lines_in_det_luma * v->required_dispclk[i][j] * v->byte_per_pixel_in_dety[k] * v->pscl_factor[k] / v->return_bw_per_state[i], v->effective_lb_latency_hiding_source_lines_luma), v->swath_height_yper_state[i][j][k]); + v->effective_detlb_lines_chroma =dcn_bw_floor2(v->lines_in_det_chroma +dcn_bw_min2(v->lines_in_det_chroma * v->required_dispclk[i][j] * v->byte_per_pixel_in_detc[k] * v->pscl_factor_chroma[k] / v->return_bw_per_state[i], v->effective_lb_latency_hiding_source_lines_chroma), v->swath_height_cper_state[i][j][k]); + if (v->byte_per_pixel_in_detc[k] == 0.0) { + v->urgent_latency_support_us_per_state[i][j][k] = v->effective_detlb_lines_luma * (v->htotal[k] / v->pixel_clock[k]) / v->v_ratio[k] - v->effective_detlb_lines_luma * v->swath_width_yper_state[i][j][k] *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / (v->return_bw_per_state[i] / v->no_of_dpp[i][j][k]); + } + else { + v->urgent_latency_support_us_per_state[i][j][k] =dcn_bw_min2(v->effective_detlb_lines_luma * (v->htotal[k] / v->pixel_clock[k]) / v->v_ratio[k] - v->effective_detlb_lines_luma * v->swath_width_yper_state[i][j][k] *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / (v->return_bw_per_state[i] / v->no_of_dpp[i][j][k]), v->effective_detlb_lines_chroma * (v->htotal[k] / v->pixel_clock[k]) / (v->v_ratio[k] / 2.0) - v->effective_detlb_lines_chroma * v->swath_width_yper_state[i][j][k] / 2.0 *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / (v->return_bw_per_state[i] / v->no_of_dpp[i][j][k])); + } + } + } + } + for (i = 0; i <= number_of_states_plus_one; i++) { + for (j = 0; j <= 1; j++) { + v->urgent_latency_support[i][j] = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->urgent_latency_support_us_per_state[i][j][k] < v->urgent_latency / 1.0) { + v->urgent_latency_support[i][j] = dcn_bw_no; + } + } + } + } + /*prefetch check*/ + + for (i = 0; i <= number_of_states_plus_one; i++) { + for (j = 0; j <= 1; j++) { + v->total_number_of_dcc_active_dpp[i][j] = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->dcc_enable[k] == dcn_bw_yes) { + v->total_number_of_dcc_active_dpp[i][j] = v->total_number_of_dcc_active_dpp[i][j] + v->no_of_dpp[i][j][k]; + } + } + } + } + for (i = 0; i <= number_of_states_plus_one; i++) { + for (j = 0; j <= 1; j++) { + v->projected_dcfclk_deep_sleep = 8.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->projected_dcfclk_deep_sleep =dcn_bw_max2(v->projected_dcfclk_deep_sleep, v->pixel_clock[k] / 16.0); + if (v->byte_per_pixel_in_detc[k] == 0.0) { + if (v->v_ratio[k] <= 1.0) { + v->projected_dcfclk_deep_sleep =dcn_bw_max2(v->projected_dcfclk_deep_sleep, 1.1 *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / 64.0 * v->h_ratio[k] * v->pixel_clock[k] / v->no_of_dpp[i][j][k]); + } + else { + v->projected_dcfclk_deep_sleep =dcn_bw_max2(v->projected_dcfclk_deep_sleep, 1.1 *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / 64.0 * v->pscl_factor[k] * v->required_dispclk[i][j] / (1 + j)); + } + } + else { + if (v->v_ratio[k] <= 1.0) { + v->projected_dcfclk_deep_sleep =dcn_bw_max2(v->projected_dcfclk_deep_sleep, 1.1 *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / 32.0 * v->h_ratio[k] * v->pixel_clock[k] / v->no_of_dpp[i][j][k]); + } + else { + v->projected_dcfclk_deep_sleep =dcn_bw_max2(v->projected_dcfclk_deep_sleep, 1.1 *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / 32.0 * v->pscl_factor[k] * v->required_dispclk[i][j] / (1 + j)); + } + if (v->v_ratio[k] / 2.0 <= 1.0) { + v->projected_dcfclk_deep_sleep =dcn_bw_max2(v->projected_dcfclk_deep_sleep, 1.1 *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 32.0 * v->h_ratio[k] / 2.0 * v->pixel_clock[k] / v->no_of_dpp[i][j][k]); + } + else { + v->projected_dcfclk_deep_sleep =dcn_bw_max2(v->projected_dcfclk_deep_sleep, 1.1 *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 32.0 * v->pscl_factor_chroma[k] * v->required_dispclk[i][j] / (1 + j)); + } + } + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->dcc_enable[k] == dcn_bw_yes) { + v->meta_req_height_y = 8.0 * v->read256_block_height_y[k]; + v->meta_req_width_y = 64.0 * 256.0 /dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / v->meta_req_height_y; + v->meta_surface_width_y =dcn_bw_ceil2(v->viewport_width[k] / v->no_of_dpp[i][j][k] - 1.0, v->meta_req_width_y) + v->meta_req_width_y; + v->meta_surface_height_y =dcn_bw_ceil2(v->viewport_height[k] - 1.0, v->meta_req_height_y) + v->meta_req_height_y; + if (v->pte_enable == dcn_bw_yes) { + v->meta_pte_bytes_per_frame_y = (dcn_bw_ceil2((v->meta_surface_width_y * v->meta_surface_height_y *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / 256.0 - 4096.0) / 8.0 / 4096.0, 1.0) + 1) * 64.0; + } + else { + v->meta_pte_bytes_per_frame_y = 0.0; + } + if (v->source_scan[k] == dcn_bw_hor) { + v->meta_row_bytes_y = v->meta_surface_width_y * v->meta_req_height_y *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / 256.0; + } + else { + v->meta_row_bytes_y = v->meta_surface_height_y * v->meta_req_width_y *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / 256.0; + } + } + else { + v->meta_pte_bytes_per_frame_y = 0.0; + v->meta_row_bytes_y = 0.0; + } + if (v->pte_enable == dcn_bw_yes) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->macro_tile_block_size_bytes_y = 256.0; + v->macro_tile_block_height_y = 1.0; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_4_kb_s || v->source_surface_mode[k] == dcn_bw_sw_4_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d_x) { + v->macro_tile_block_size_bytes_y = 4096.0; + v->macro_tile_block_height_y = 4.0 * v->read256_block_height_y[k]; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_64_kb_s || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_x) { + v->macro_tile_block_size_bytes_y = 64.0 * 1024; + v->macro_tile_block_height_y = 16.0 * v->read256_block_height_y[k]; + } + else { + v->macro_tile_block_size_bytes_y = 256.0 * 1024; + v->macro_tile_block_height_y = 32.0 * v->read256_block_height_y[k]; + } + if (v->macro_tile_block_size_bytes_y <= 65536.0) { + v->data_pte_req_height_y = v->macro_tile_block_height_y; + } + else { + v->data_pte_req_height_y = 16.0 * v->read256_block_height_y[k]; + } + v->data_pte_req_width_y = 4096.0 /dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) / v->data_pte_req_height_y * 8; + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->dpte_bytes_per_row_y = 64.0 * (dcn_bw_ceil2((v->viewport_width[k] / v->no_of_dpp[i][j][k] *dcn_bw_min2(128.0, dcn_bw_pow(2.0,dcn_bw_floor2(dcn_bw_log(v->pte_buffer_size_in_requests * v->data_pte_req_width_y / (v->viewport_width[k] / v->no_of_dpp[i][j][k]), 2.0), 1.0))) - 1.0) / v->data_pte_req_width_y, 1.0) + 1); + } + else if (v->source_scan[k] == dcn_bw_hor) { + v->dpte_bytes_per_row_y = 64.0 * (dcn_bw_ceil2((v->viewport_width[k] / v->no_of_dpp[i][j][k] - 1.0) / v->data_pte_req_width_y, 1.0) + 1); + } + else { + v->dpte_bytes_per_row_y = 64.0 * (dcn_bw_ceil2((v->viewport_height[k] - 1.0) / v->data_pte_req_height_y, 1.0) + 1); + } + } + else { + v->dpte_bytes_per_row_y = 0.0; + } + if ((v->source_pixel_format[k] != dcn_bw_rgb_sub_64 && v->source_pixel_format[k] != dcn_bw_rgb_sub_32 && v->source_pixel_format[k] != dcn_bw_rgb_sub_16)) { + if (v->dcc_enable[k] == dcn_bw_yes) { + v->meta_req_height_c = 8.0 * v->read256_block_height_c[k]; + v->meta_req_width_c = 64.0 * 256.0 /dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / v->meta_req_height_c; + v->meta_surface_width_c =dcn_bw_ceil2(v->viewport_width[k] / v->no_of_dpp[i][j][k] / 2.0 - 1.0, v->meta_req_width_c) + v->meta_req_width_c; + v->meta_surface_height_c =dcn_bw_ceil2(v->viewport_height[k] / 2.0 - 1.0, v->meta_req_height_c) + v->meta_req_height_c; + if (v->pte_enable == dcn_bw_yes) { + v->meta_pte_bytes_per_frame_c = (dcn_bw_ceil2((v->meta_surface_width_c * v->meta_surface_height_c *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 256.0 - 4096.0) / 8.0 / 4096.0, 1.0) + 1) * 64.0; + } + else { + v->meta_pte_bytes_per_frame_c = 0.0; + } + if (v->source_scan[k] == dcn_bw_hor) { + v->meta_row_bytes_c = v->meta_surface_width_c * v->meta_req_height_c *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 256.0; + } + else { + v->meta_row_bytes_c = v->meta_surface_height_c * v->meta_req_width_c *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 256.0; + } + } + else { + v->meta_pte_bytes_per_frame_c = 0.0; + v->meta_row_bytes_c = 0.0; + } + if (v->pte_enable == dcn_bw_yes) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->macro_tile_block_size_bytes_c = 256.0; + v->macro_tile_block_height_c = 1.0; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_4_kb_s || v->source_surface_mode[k] == dcn_bw_sw_4_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d_x) { + v->macro_tile_block_size_bytes_c = 4096.0; + v->macro_tile_block_height_c = 4.0 * v->read256_block_height_c[k]; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_64_kb_s || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_x) { + v->macro_tile_block_size_bytes_c = 64.0 * 1024; + v->macro_tile_block_height_c = 16.0 * v->read256_block_height_c[k]; + } + else { + v->macro_tile_block_size_bytes_c = 256.0 * 1024; + v->macro_tile_block_height_c = 32.0 * v->read256_block_height_c[k]; + } + v->macro_tile_block_width_c = v->macro_tile_block_size_bytes_c /dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / v->macro_tile_block_height_c; + if (v->macro_tile_block_size_bytes_c <= 65536.0) { + v->data_pte_req_height_c = v->macro_tile_block_height_c; + } + else { + v->data_pte_req_height_c = 16.0 * v->read256_block_height_c[k]; + } + v->data_pte_req_width_c = 4096.0 /dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / v->data_pte_req_height_c * 8; + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->dpte_bytes_per_row_c = 64.0 * (dcn_bw_ceil2((v->viewport_width[k] / v->no_of_dpp[i][j][k] / 2.0 * dcn_bw_min2(128.0, dcn_bw_pow(2.0,dcn_bw_floor2(dcn_bw_log(v->pte_buffer_size_in_requests * v->data_pte_req_width_c / (v->viewport_width[k] / v->no_of_dpp[i][j][k] / 2.0), 2.0), 1.0))) - 1.0) / v->data_pte_req_width_c, 1.0) + 1); + } + else if (v->source_scan[k] == dcn_bw_hor) { + v->dpte_bytes_per_row_c = 64.0 * (dcn_bw_ceil2((v->viewport_width[k] / v->no_of_dpp[i][j][k] / 2.0 - 1.0) / v->data_pte_req_width_c, 1.0) + 1); + } + else { + v->dpte_bytes_per_row_c = 64.0 * (dcn_bw_ceil2((v->viewport_height[k] / 2.0 - 1.0) / v->data_pte_req_height_c, 1.0) + 1); + } + } + else { + v->dpte_bytes_per_row_c = 0.0; + } + } + else { + v->dpte_bytes_per_row_c = 0.0; + v->meta_pte_bytes_per_frame_c = 0.0; + v->meta_row_bytes_c = 0.0; + } + v->dpte_bytes_per_row[k] = v->dpte_bytes_per_row_y + v->dpte_bytes_per_row_c; + v->meta_pte_bytes_per_frame[k] = v->meta_pte_bytes_per_frame_y + v->meta_pte_bytes_per_frame_c; + v->meta_row_bytes[k] = v->meta_row_bytes_y + v->meta_row_bytes_c; + v->v_init_y = (v->v_ratio[k] + v->vtaps[k] + 1.0 + v->interlace_output[k] * 0.5 * v->v_ratio[k]) / 2.0; + v->prefill_y[k] =dcn_bw_floor2(v->v_init_y, 1.0); + v->max_num_sw_y[k] =dcn_bw_ceil2((v->prefill_y[k] - 1.0) / v->swath_height_yper_state[i][j][k], 1.0) + 1; + if (v->prefill_y[k] > 1.0) { + v->max_partial_sw_y =dcn_bw_mod((v->prefill_y[k] - 2.0), v->swath_height_yper_state[i][j][k]); + } + else { + v->max_partial_sw_y =dcn_bw_mod((v->prefill_y[k] + v->swath_height_yper_state[i][j][k] - 2.0), v->swath_height_yper_state[i][j][k]); + } + v->max_partial_sw_y =dcn_bw_max2(1.0, v->max_partial_sw_y); + v->prefetch_lines_y[k] = v->max_num_sw_y[k] * v->swath_height_yper_state[i][j][k] + v->max_partial_sw_y; + if ((v->source_pixel_format[k] != dcn_bw_rgb_sub_64 && v->source_pixel_format[k] != dcn_bw_rgb_sub_32 && v->source_pixel_format[k] != dcn_bw_rgb_sub_16)) { + v->v_init_c = (v->v_ratio[k] / 2.0 + v->vtaps[k] + 1.0 + v->interlace_output[k] * 0.5 * v->v_ratio[k] / 2.0) / 2.0; + v->prefill_c[k] =dcn_bw_floor2(v->v_init_c, 1.0); + v->max_num_sw_c[k] =dcn_bw_ceil2((v->prefill_c[k] - 1.0) / v->swath_height_cper_state[i][j][k], 1.0) + 1; + if (v->prefill_c[k] > 1.0) { + v->max_partial_sw_c =dcn_bw_mod((v->prefill_c[k] - 2.0), v->swath_height_cper_state[i][j][k]); + } + else { + v->max_partial_sw_c =dcn_bw_mod((v->prefill_c[k] + v->swath_height_cper_state[i][j][k] - 2.0), v->swath_height_cper_state[i][j][k]); + } + v->max_partial_sw_c =dcn_bw_max2(1.0, v->max_partial_sw_c); + v->prefetch_lines_c[k] = v->max_num_sw_c[k] * v->swath_height_cper_state[i][j][k] + v->max_partial_sw_c; + } + else { + v->prefetch_lines_c[k] = 0.0; + } + v->dst_x_after_scaler = 90.0 * v->pixel_clock[k] / (v->required_dispclk[i][j] / (j + 1)) + 42.0 * v->pixel_clock[k] / v->required_dispclk[i][j]; + if (v->no_of_dpp[i][j][k] > 1.0) { + v->dst_x_after_scaler = v->dst_x_after_scaler + v->scaler_rec_out_width[k] / 2.0; + } + if (v->output_format[k] == dcn_bw_420) { + v->dst_y_after_scaler = 1.0; + } + else { + v->dst_y_after_scaler = 0.0; + } + v->time_calc = 24.0 / v->projected_dcfclk_deep_sleep; + v->v_update_offset[k] =dcn_bw_ceil2(v->htotal[k] / 4.0, 1.0); + v->total_repeater_delay = v->max_inter_dcn_tile_repeaters * (2.0 / (v->required_dispclk[i][j] / (j + 1)) + 3.0 / v->required_dispclk[i][j]); + v->v_update_width[k] = (14.0 / v->projected_dcfclk_deep_sleep + 12.0 / (v->required_dispclk[i][j] / (j + 1)) + v->total_repeater_delay) * v->pixel_clock[k]; + v->v_ready_offset[k] =dcn_bw_max2(150.0 / (v->required_dispclk[i][j] / (j + 1)), v->total_repeater_delay + 20.0 / v->projected_dcfclk_deep_sleep + 10.0 / (v->required_dispclk[i][j] / (j + 1))) * v->pixel_clock[k]; + v->time_setup = (v->v_update_offset[k] + v->v_update_width[k] + v->v_ready_offset[k]) / v->pixel_clock[k]; + v->extra_latency = v->urgent_round_trip_and_out_of_order_latency_per_state[i] + (v->total_number_of_active_dpp[i][j] * v->pixel_chunk_size_in_kbyte + v->total_number_of_dcc_active_dpp[i][j] * v->meta_chunk_size) * 1024.0 / v->return_bw_per_state[i]; + if (v->pte_enable == dcn_bw_yes) { + v->extra_latency = v->extra_latency + v->total_number_of_active_dpp[i][j] * v->pte_chunk_size * 1024.0 / v->return_bw_per_state[i]; + } + if (v->can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one == dcn_bw_yes) { + v->maximum_vstartup = v->vtotal[k] - v->vactive[k] - 1.0; + } + else { + v->maximum_vstartup = v->v_sync_plus_back_porch[k] - 1.0; + } + v->line_times_for_prefetch[k] = v->maximum_vstartup - v->urgent_latency / (v->htotal[k] / v->pixel_clock[k]) - (v->time_calc + v->time_setup) / (v->htotal[k] / v->pixel_clock[k]) - (v->dst_y_after_scaler + v->dst_x_after_scaler / v->htotal[k]); + v->line_times_for_prefetch[k] =dcn_bw_floor2(4.0 * (v->line_times_for_prefetch[k] + 0.125), 1.0) / 4; + v->prefetch_bw[k] = (v->meta_pte_bytes_per_frame[k] + 2.0 * v->meta_row_bytes[k] + 2.0 * v->dpte_bytes_per_row[k] + v->prefetch_lines_y[k] * v->swath_width_yper_state[i][j][k] *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) + v->prefetch_lines_c[k] * v->swath_width_yper_state[i][j][k] / 2.0 *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0)) / (v->line_times_for_prefetch[k] * v->htotal[k] / v->pixel_clock[k]); + } + v->bw_available_for_immediate_flip = v->return_bw_per_state[i]; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->bw_available_for_immediate_flip = v->bw_available_for_immediate_flip -dcn_bw_max2(v->read_bandwidth[k], v->prefetch_bw[k]); + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->total_immediate_flip_bytes[k] = 0.0; + if ((v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10)) { + v->total_immediate_flip_bytes[k] = v->total_immediate_flip_bytes[k] + v->meta_pte_bytes_per_frame[k] + v->meta_row_bytes[k] + v->dpte_bytes_per_row[k]; + } + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->pte_enable == dcn_bw_yes && v->dcc_enable[k] == dcn_bw_yes) { + v->time_for_meta_pte_with_immediate_flip =dcn_bw_max5(v->meta_pte_bytes_per_frame[k] / v->prefetch_bw[k], v->meta_pte_bytes_per_frame[k] * v->total_immediate_flip_bytes[k] / (v->bw_available_for_immediate_flip * (v->meta_pte_bytes_per_frame[k] + v->meta_row_bytes[k] + v->dpte_bytes_per_row[k])), v->extra_latency, v->urgent_latency, v->htotal[k] / v->pixel_clock[k] / 4.0); + v->time_for_meta_pte_without_immediate_flip =dcn_bw_max3(v->meta_pte_bytes_per_frame[k] / v->prefetch_bw[k], v->extra_latency, v->htotal[k] / v->pixel_clock[k] / 4.0); + } + else { + v->time_for_meta_pte_with_immediate_flip = v->htotal[k] / v->pixel_clock[k] / 4.0; + v->time_for_meta_pte_without_immediate_flip = v->htotal[k] / v->pixel_clock[k] / 4.0; + } + if (v->pte_enable == dcn_bw_yes || v->dcc_enable[k] == dcn_bw_yes) { + v->time_for_meta_and_dpte_row_with_immediate_flip =dcn_bw_max5((v->meta_row_bytes[k] + v->dpte_bytes_per_row[k]) / v->prefetch_bw[k], (v->meta_row_bytes[k] + v->dpte_bytes_per_row[k]) * v->total_immediate_flip_bytes[k] / (v->bw_available_for_immediate_flip * (v->meta_pte_bytes_per_frame[k] + v->meta_row_bytes[k] + v->dpte_bytes_per_row[k])), v->htotal[k] / v->pixel_clock[k] - v->time_for_meta_pte_with_immediate_flip, v->extra_latency, 2.0 * v->urgent_latency); + v->time_for_meta_and_dpte_row_without_immediate_flip =dcn_bw_max3((v->meta_row_bytes[k] + v->dpte_bytes_per_row[k]) / v->prefetch_bw[k], v->htotal[k] / v->pixel_clock[k] - v->time_for_meta_pte_without_immediate_flip, v->extra_latency); + } + else { + v->time_for_meta_and_dpte_row_with_immediate_flip =dcn_bw_max2(v->htotal[k] / v->pixel_clock[k] - v->time_for_meta_pte_with_immediate_flip, v->extra_latency - v->time_for_meta_pte_with_immediate_flip); + v->time_for_meta_and_dpte_row_without_immediate_flip =dcn_bw_max2(v->htotal[k] / v->pixel_clock[k] - v->time_for_meta_pte_without_immediate_flip, v->extra_latency - v->time_for_meta_pte_without_immediate_flip); + } + v->lines_for_meta_pte_with_immediate_flip[k] =dcn_bw_floor2(4.0 * (v->time_for_meta_pte_with_immediate_flip / (v->htotal[k] / v->pixel_clock[k]) + 0.125), 1.0) / 4; + v->lines_for_meta_pte_without_immediate_flip[k] =dcn_bw_floor2(4.0 * (v->time_for_meta_pte_without_immediate_flip / (v->htotal[k] / v->pixel_clock[k]) + 0.125), 1.0) / 4; + v->lines_for_meta_and_dpte_row_with_immediate_flip[k] =dcn_bw_floor2(4.0 * (v->time_for_meta_and_dpte_row_with_immediate_flip / (v->htotal[k] / v->pixel_clock[k]) + 0.125), 1.0) / 4; + v->lines_for_meta_and_dpte_row_without_immediate_flip[k] =dcn_bw_floor2(4.0 * (v->time_for_meta_and_dpte_row_without_immediate_flip / (v->htotal[k] / v->pixel_clock[k]) + 0.125), 1.0) / 4; + v->line_times_to_request_prefetch_pixel_data_with_immediate_flip = v->line_times_for_prefetch[k] - v->lines_for_meta_pte_with_immediate_flip[k] - v->lines_for_meta_and_dpte_row_with_immediate_flip[k]; + v->line_times_to_request_prefetch_pixel_data_without_immediate_flip = v->line_times_for_prefetch[k] - v->lines_for_meta_pte_without_immediate_flip[k] - v->lines_for_meta_and_dpte_row_without_immediate_flip[k]; + if (v->line_times_to_request_prefetch_pixel_data_with_immediate_flip > 0.0) { + v->v_ratio_pre_ywith_immediate_flip[i][j][k] = v->prefetch_lines_y[k] / v->line_times_to_request_prefetch_pixel_data_with_immediate_flip; + if ((v->swath_height_yper_state[i][j][k] > 4.0)) { + if (v->line_times_to_request_prefetch_pixel_data_with_immediate_flip - (v->prefill_y[k] - 3.0) / 2.0 > 0.0) { + v->v_ratio_pre_ywith_immediate_flip[i][j][k] =dcn_bw_max2(v->v_ratio_pre_ywith_immediate_flip[i][j][k], (v->max_num_sw_y[k] * v->swath_height_yper_state[i][j][k]) / (v->line_times_to_request_prefetch_pixel_data_with_immediate_flip - (v->prefill_y[k] - 3.0) / 2.0)); + } + else { + v->v_ratio_pre_ywith_immediate_flip[i][j][k] = 999999.0; + } + } + v->v_ratio_pre_cwith_immediate_flip[i][j][k] = v->prefetch_lines_c[k] / v->line_times_to_request_prefetch_pixel_data_with_immediate_flip; + if ((v->swath_height_cper_state[i][j][k] > 4.0)) { + if (v->line_times_to_request_prefetch_pixel_data_with_immediate_flip - (v->prefill_c[k] - 3.0) / 2.0 > 0.0) { + v->v_ratio_pre_cwith_immediate_flip[i][j][k] =dcn_bw_max2(v->v_ratio_pre_cwith_immediate_flip[i][j][k], (v->max_num_sw_c[k] * v->swath_height_cper_state[i][j][k]) / (v->line_times_to_request_prefetch_pixel_data_with_immediate_flip - (v->prefill_c[k] - 3.0) / 2.0)); + } + else { + v->v_ratio_pre_cwith_immediate_flip[i][j][k] = 999999.0; + } + } + v->required_prefetch_pixel_data_bw_with_immediate_flip[i][j][k] = v->no_of_dpp[i][j][k] * (v->prefetch_lines_y[k] / v->line_times_to_request_prefetch_pixel_data_with_immediate_flip *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) + v->prefetch_lines_c[k] / v->line_times_to_request_prefetch_pixel_data_with_immediate_flip *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 2.0) * v->swath_width_yper_state[i][j][k] / (v->htotal[k] / v->pixel_clock[k]); + } + else { + v->v_ratio_pre_ywith_immediate_flip[i][j][k] = 999999.0; + v->v_ratio_pre_cwith_immediate_flip[i][j][k] = 999999.0; + v->required_prefetch_pixel_data_bw_with_immediate_flip[i][j][k] = 999999.0; + } + if (v->line_times_to_request_prefetch_pixel_data_without_immediate_flip > 0.0) { + v->v_ratio_pre_ywithout_immediate_flip[i][j][k] = v->prefetch_lines_y[k] / v->line_times_to_request_prefetch_pixel_data_without_immediate_flip; + if ((v->swath_height_yper_state[i][j][k] > 4.0)) { + if (v->line_times_to_request_prefetch_pixel_data_without_immediate_flip - (v->prefill_y[k] - 3.0) / 2.0 > 0.0) { + v->v_ratio_pre_ywithout_immediate_flip[i][j][k] =dcn_bw_max2(v->v_ratio_pre_ywithout_immediate_flip[i][j][k], (v->max_num_sw_y[k] * v->swath_height_yper_state[i][j][k]) / (v->line_times_to_request_prefetch_pixel_data_without_immediate_flip - (v->prefill_y[k] - 3.0) / 2.0)); + } + else { + v->v_ratio_pre_ywithout_immediate_flip[i][j][k] = 999999.0; + } + } + v->v_ratio_pre_cwithout_immediate_flip[i][j][k] = v->prefetch_lines_c[k] / v->line_times_to_request_prefetch_pixel_data_without_immediate_flip; + if ((v->swath_height_cper_state[i][j][k] > 4.0)) { + if (v->line_times_to_request_prefetch_pixel_data_without_immediate_flip - (v->prefill_c[k] - 3.0) / 2.0 > 0.0) { + v->v_ratio_pre_cwithout_immediate_flip[i][j][k] =dcn_bw_max2(v->v_ratio_pre_cwithout_immediate_flip[i][j][k], (v->max_num_sw_c[k] * v->swath_height_cper_state[i][j][k]) / (v->line_times_to_request_prefetch_pixel_data_without_immediate_flip - (v->prefill_c[k] - 3.0) / 2.0)); + } + else { + v->v_ratio_pre_cwithout_immediate_flip[i][j][k] = 999999.0; + } + } + v->required_prefetch_pixel_data_bw_without_immediate_flip[i][j][k] = v->no_of_dpp[i][j][k] * (v->prefetch_lines_y[k] / v->line_times_to_request_prefetch_pixel_data_without_immediate_flip *dcn_bw_ceil2(v->byte_per_pixel_in_dety[k], 1.0) + v->prefetch_lines_c[k] / v->line_times_to_request_prefetch_pixel_data_without_immediate_flip *dcn_bw_ceil2(v->byte_per_pixel_in_detc[k], 2.0) / 2.0) * v->swath_width_yper_state[i][j][k] / (v->htotal[k] / v->pixel_clock[k]); + } + else { + v->v_ratio_pre_ywithout_immediate_flip[i][j][k] = 999999.0; + v->v_ratio_pre_cwithout_immediate_flip[i][j][k] = 999999.0; + v->required_prefetch_pixel_data_bw_without_immediate_flip[i][j][k] = 999999.0; + } + } + v->maximum_read_bandwidth_with_prefetch_with_immediate_flip = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if ((v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10)) { + v->maximum_read_bandwidth_with_prefetch_with_immediate_flip = v->maximum_read_bandwidth_with_prefetch_with_immediate_flip +dcn_bw_max2(v->read_bandwidth[k], v->required_prefetch_pixel_data_bw_with_immediate_flip[i][j][k]) +dcn_bw_max2(v->meta_pte_bytes_per_frame[k] / (v->lines_for_meta_pte_with_immediate_flip[k] * v->htotal[k] / v->pixel_clock[k]), (v->meta_row_bytes[k] + v->dpte_bytes_per_row[k]) / (v->lines_for_meta_and_dpte_row_with_immediate_flip[k] * v->htotal[k] / v->pixel_clock[k])); + } + else { + v->maximum_read_bandwidth_with_prefetch_with_immediate_flip = v->maximum_read_bandwidth_with_prefetch_with_immediate_flip +dcn_bw_max2(v->read_bandwidth[k], v->required_prefetch_pixel_data_bw_without_immediate_flip[i][j][k]); + } + } + v->maximum_read_bandwidth_with_prefetch_without_immediate_flip = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->maximum_read_bandwidth_with_prefetch_without_immediate_flip = v->maximum_read_bandwidth_with_prefetch_without_immediate_flip +dcn_bw_max2(v->read_bandwidth[k], v->required_prefetch_pixel_data_bw_without_immediate_flip[i][j][k]); + } + v->prefetch_supported_with_immediate_flip[i][j] = dcn_bw_yes; + if (v->maximum_read_bandwidth_with_prefetch_with_immediate_flip > v->return_bw_per_state[i]) { + v->prefetch_supported_with_immediate_flip[i][j] = dcn_bw_no; + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->line_times_for_prefetch[k] < 2.0 || v->lines_for_meta_pte_with_immediate_flip[k] >= 8.0 || v->lines_for_meta_and_dpte_row_with_immediate_flip[k] >= 16.0) { + v->prefetch_supported_with_immediate_flip[i][j] = dcn_bw_no; + } + } + v->prefetch_supported_without_immediate_flip[i][j] = dcn_bw_yes; + if (v->maximum_read_bandwidth_with_prefetch_without_immediate_flip > v->return_bw_per_state[i]) { + v->prefetch_supported_without_immediate_flip[i][j] = dcn_bw_no; + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->line_times_for_prefetch[k] < 2.0 || v->lines_for_meta_pte_without_immediate_flip[k] >= 8.0 || v->lines_for_meta_and_dpte_row_without_immediate_flip[k] >= 16.0) { + v->prefetch_supported_without_immediate_flip[i][j] = dcn_bw_no; + } + } + } + } + for (i = 0; i <= number_of_states_plus_one; i++) { + for (j = 0; j <= 1; j++) { + v->v_ratio_in_prefetch_supported_with_immediate_flip[i][j] = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if ((((v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10) && (v->v_ratio_pre_ywith_immediate_flip[i][j][k] > 4.0 || v->v_ratio_pre_cwith_immediate_flip[i][j][k] > 4.0)) || ((v->source_pixel_format[k] == dcn_bw_yuv420_sub_8 || v->source_pixel_format[k] == dcn_bw_yuv420_sub_10) && (v->v_ratio_pre_ywithout_immediate_flip[i][j][k] > 4.0 || v->v_ratio_pre_cwithout_immediate_flip[i][j][k] > 4.0)))) { + v->v_ratio_in_prefetch_supported_with_immediate_flip[i][j] = dcn_bw_no; + } + } + v->v_ratio_in_prefetch_supported_without_immediate_flip[i][j] = dcn_bw_yes; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if ((v->v_ratio_pre_ywithout_immediate_flip[i][j][k] > 4.0 || v->v_ratio_pre_cwithout_immediate_flip[i][j][k] > 4.0)) { + v->v_ratio_in_prefetch_supported_without_immediate_flip[i][j] = dcn_bw_no; + } + } + } + } + /*mode support, voltage state and soc configuration*/ + + for (i = number_of_states_plus_one; i >= 0; i--) { + for (j = 0; j <= 1; j++) { + if (v->scale_ratio_support == dcn_bw_yes && v->source_format_pixel_and_scan_support == dcn_bw_yes && v->viewport_size_support == dcn_bw_yes && v->bandwidth_support[i] == dcn_bw_yes && v->dio_support[i] == dcn_bw_yes && v->urgent_latency_support[i][j] == dcn_bw_yes && v->rob_support[i] == dcn_bw_yes && v->dispclk_dppclk_support[i][j] == dcn_bw_yes && v->total_available_pipes_support[i][j] == dcn_bw_yes && v->total_available_writeback_support == dcn_bw_yes && v->writeback_latency_support == dcn_bw_yes) { + if (v->prefetch_supported_with_immediate_flip[i][j] == dcn_bw_yes && v->v_ratio_in_prefetch_supported_with_immediate_flip[i][j] == dcn_bw_yes) { + v->mode_support_with_immediate_flip[i][j] = dcn_bw_yes; + } + else { + v->mode_support_with_immediate_flip[i][j] = dcn_bw_no; + } + if (v->prefetch_supported_without_immediate_flip[i][j] == dcn_bw_yes && v->v_ratio_in_prefetch_supported_without_immediate_flip[i][j] == dcn_bw_yes) { + v->mode_support_without_immediate_flip[i][j] = dcn_bw_yes; + } + else { + v->mode_support_without_immediate_flip[i][j] = dcn_bw_no; + } + } + else { + v->mode_support_with_immediate_flip[i][j] = dcn_bw_no; + v->mode_support_without_immediate_flip[i][j] = dcn_bw_no; + } + } + } + for (i = number_of_states_plus_one; i >= 0; i--) { + if ((i == number_of_states_plus_one || v->mode_support_with_immediate_flip[i][1] == dcn_bw_yes || v->mode_support_with_immediate_flip[i][0] == dcn_bw_yes) && i >= v->voltage_override_level) { + v->voltage_level_with_immediate_flip = i; + } + } + for (i = number_of_states_plus_one; i >= 0; i--) { + if ((i == number_of_states_plus_one || v->mode_support_without_immediate_flip[i][1] == dcn_bw_yes || v->mode_support_without_immediate_flip[i][0] == dcn_bw_yes) && i >= v->voltage_override_level) { + v->voltage_level_without_immediate_flip = i; + } + } + if (v->voltage_level_with_immediate_flip == number_of_states_plus_one) { + v->immediate_flip_supported = dcn_bw_no; + v->voltage_level = v->voltage_level_without_immediate_flip; + } + else { + v->immediate_flip_supported = dcn_bw_yes; + v->voltage_level = v->voltage_level_with_immediate_flip; + } + v->dcfclk = v->dcfclk_per_state[v->voltage_level]; + v->fabric_and_dram_bandwidth = v->fabric_and_dram_bandwidth_per_state[v->voltage_level]; + for (j = 0; j <= 1; j++) { + v->required_dispclk_per_ratio[j] = v->required_dispclk[v->voltage_level][j]; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->dpp_per_plane_per_ratio[j][k] = v->no_of_dpp[v->voltage_level][j][k]; + } + v->dispclk_dppclk_support_per_ratio[j] = v->dispclk_dppclk_support[v->voltage_level][j]; + } + v->max_phyclk = v->phyclk_per_state[v->voltage_level]; +} +void display_pipe_configuration(struct dcn_bw_internal_vars *v) +{ + int j; + int k; + /*display pipe configuration*/ + + for (j = 0; j <= 1; j++) { + v->total_number_of_active_dpp_per_ratio[j] = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->total_number_of_active_dpp_per_ratio[j] = v->total_number_of_active_dpp_per_ratio[j] + v->dpp_per_plane_per_ratio[j][k]; + } + } + if ((v->dispclk_dppclk_support_per_ratio[0] == dcn_bw_yes && v->dispclk_dppclk_support_per_ratio[1] == dcn_bw_no) || (v->dispclk_dppclk_support_per_ratio[0] == v->dispclk_dppclk_support_per_ratio[1] && (v->total_number_of_active_dpp_per_ratio[0] < v->total_number_of_active_dpp_per_ratio[1] || (((v->total_number_of_active_dpp_per_ratio[0] == v->total_number_of_active_dpp_per_ratio[1]) && v->required_dispclk_per_ratio[0] <= 0.5 * v->required_dispclk_per_ratio[1]))))) { + v->dispclk_dppclk_ratio = 1; + v->final_error_message = v->error_message[0]; + } + else { + v->dispclk_dppclk_ratio = 2; + v->final_error_message = v->error_message[1]; + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->dpp_per_plane[k] = v->dpp_per_plane_per_ratio[v->dispclk_dppclk_ratio - 1][k]; + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->source_pixel_format[k] == dcn_bw_rgb_sub_64) { + v->byte_per_pix_dety = 8.0; + v->byte_per_pix_detc = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_32) { + v->byte_per_pix_dety = 4.0; + v->byte_per_pix_detc = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_16) { + v->byte_per_pix_dety = 2.0; + v->byte_per_pix_detc = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8) { + v->byte_per_pix_dety = 1.0; + v->byte_per_pix_detc = 2.0; + } + else { + v->byte_per_pix_dety = 4.0f / 3.0f; + v->byte_per_pix_detc = 8.0f / 3.0f; + } + if ((v->source_pixel_format[k] == dcn_bw_rgb_sub_64 || v->source_pixel_format[k] == dcn_bw_rgb_sub_32 || v->source_pixel_format[k] == dcn_bw_rgb_sub_16)) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->read256_bytes_block_height_y = 1.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_64) { + v->read256_bytes_block_height_y = 4.0; + } + else { + v->read256_bytes_block_height_y = 8.0; + } + v->read256_bytes_block_width_y = 256.0 /dcn_bw_ceil2(v->byte_per_pix_dety, 1.0) / v->read256_bytes_block_height_y; + v->read256_bytes_block_height_c = 0.0; + v->read256_bytes_block_width_c = 0.0; + } + else { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->read256_bytes_block_height_y = 1.0; + v->read256_bytes_block_height_c = 1.0; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8) { + v->read256_bytes_block_height_y = 16.0; + v->read256_bytes_block_height_c = 8.0; + } + else { + v->read256_bytes_block_height_y = 8.0; + v->read256_bytes_block_height_c = 8.0; + } + v->read256_bytes_block_width_y = 256.0 /dcn_bw_ceil2(v->byte_per_pix_dety, 1.0) / v->read256_bytes_block_height_y; + v->read256_bytes_block_width_c = 256.0 /dcn_bw_ceil2(v->byte_per_pix_detc, 2.0) / v->read256_bytes_block_height_c; + } + if (v->source_scan[k] == dcn_bw_hor) { + v->maximum_swath_height_y = v->read256_bytes_block_height_y; + v->maximum_swath_height_c = v->read256_bytes_block_height_c; + } + else { + v->maximum_swath_height_y = v->read256_bytes_block_width_y; + v->maximum_swath_height_c = v->read256_bytes_block_width_c; + } + if ((v->source_pixel_format[k] == dcn_bw_rgb_sub_64 || v->source_pixel_format[k] == dcn_bw_rgb_sub_32 || v->source_pixel_format[k] == dcn_bw_rgb_sub_16)) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear || (v->source_pixel_format[k] == dcn_bw_rgb_sub_64 && (v->source_surface_mode[k] == dcn_bw_sw_4_kb_s || v->source_surface_mode[k] == dcn_bw_sw_4_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_var_s || v->source_surface_mode[k] == dcn_bw_sw_var_s_x) && v->source_scan[k] == dcn_bw_hor)) { + v->minimum_swath_height_y = v->maximum_swath_height_y; + } + else { + v->minimum_swath_height_y = v->maximum_swath_height_y / 2.0; + } + v->minimum_swath_height_c = v->maximum_swath_height_c; + } + else { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->minimum_swath_height_y = v->maximum_swath_height_y; + v->minimum_swath_height_c = v->maximum_swath_height_c; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8 && v->source_scan[k] == dcn_bw_hor) { + v->minimum_swath_height_y = v->maximum_swath_height_y / 2.0; + if (v->bug_forcing_luma_and_chroma_request_to_same_size_fixed == dcn_bw_yes) { + v->minimum_swath_height_c = v->maximum_swath_height_c; + } + else { + v->minimum_swath_height_c = v->maximum_swath_height_c / 2.0; + } + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_10 && v->source_scan[k] == dcn_bw_hor) { + v->minimum_swath_height_c = v->maximum_swath_height_c / 2.0; + if (v->bug_forcing_luma_and_chroma_request_to_same_size_fixed == dcn_bw_yes) { + v->minimum_swath_height_y = v->maximum_swath_height_y; + } + else { + v->minimum_swath_height_y = v->maximum_swath_height_y / 2.0; + } + } + else { + v->minimum_swath_height_y = v->maximum_swath_height_y; + v->minimum_swath_height_c = v->maximum_swath_height_c; + } + } + if (v->source_scan[k] == dcn_bw_hor) { + v->swath_width = v->viewport_width[k] / v->dpp_per_plane[k]; + } + else { + v->swath_width = v->viewport_height[k] / v->dpp_per_plane[k]; + } + v->swath_width_granularity_y = 256.0 /dcn_bw_ceil2(v->byte_per_pix_dety, 1.0) / v->maximum_swath_height_y; + v->rounded_up_max_swath_size_bytes_y = (dcn_bw_ceil2(v->swath_width - 1.0, v->swath_width_granularity_y) + v->swath_width_granularity_y) * v->byte_per_pix_dety * v->maximum_swath_height_y; + if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_10) { + v->rounded_up_max_swath_size_bytes_y =dcn_bw_ceil2(v->rounded_up_max_swath_size_bytes_y, 256.0) + 256; + } + if (v->maximum_swath_height_c > 0.0) { + v->swath_width_granularity_c = 256.0 /dcn_bw_ceil2(v->byte_per_pix_detc, 2.0) / v->maximum_swath_height_c; + } + v->rounded_up_max_swath_size_bytes_c = (dcn_bw_ceil2(v->swath_width / 2.0 - 1.0, v->swath_width_granularity_c) + v->swath_width_granularity_c) * v->byte_per_pix_detc * v->maximum_swath_height_c; + if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_10) { + v->rounded_up_max_swath_size_bytes_c =dcn_bw_ceil2(v->rounded_up_max_swath_size_bytes_c, 256.0) + 256; + } + if (v->rounded_up_max_swath_size_bytes_y + v->rounded_up_max_swath_size_bytes_c <= v->det_buffer_size_in_kbyte * 1024.0 / 2.0) { + v->swath_height_y[k] = v->maximum_swath_height_y; + v->swath_height_c[k] = v->maximum_swath_height_c; + } + else { + v->swath_height_y[k] = v->minimum_swath_height_y; + v->swath_height_c[k] = v->minimum_swath_height_c; + } + if (v->swath_height_c[k] == 0.0) { + v->det_buffer_size_y[k] = v->det_buffer_size_in_kbyte * 1024.0; + v->det_buffer_size_c[k] = 0.0; + } + else if (v->swath_height_y[k] <= v->swath_height_c[k]) { + v->det_buffer_size_y[k] = v->det_buffer_size_in_kbyte * 1024.0 / 2.0; + v->det_buffer_size_c[k] = v->det_buffer_size_in_kbyte * 1024.0 / 2.0; + } + else { + v->det_buffer_size_y[k] = v->det_buffer_size_in_kbyte * 1024.0 * 2.0 / 3.0; + v->det_buffer_size_c[k] = v->det_buffer_size_in_kbyte * 1024.0 / 3.0; + } + } +} +void dispclkdppclkdcfclk_deep_sleep_prefetch_parameters_watermarks_and_performance_calculation(struct dcn_bw_internal_vars *v) +{ + int k; + /*dispclk and dppclk calculation*/ + + v->dispclk_with_ramping = 0.0; + v->dispclk_without_ramping = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->h_ratio[k] > 1.0) { + v->pscl_throughput[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput * v->h_ratio[k] /dcn_bw_ceil2(v->htaps[k] / 6.0, 1.0)); + } + else { + v->pscl_throughput[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput); + } + v->dppclk_using_single_dpp_luma = v->pixel_clock[k] *dcn_bw_max3(v->vtaps[k] / 6.0 *dcn_bw_min2(1.0, v->h_ratio[k]), v->h_ratio[k] * v->v_ratio[k] / v->pscl_throughput[k], 1.0); + if ((v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10)) { + v->pscl_throughput_chroma[k] = 0.0; + v->dppclk_using_single_dpp = v->dppclk_using_single_dpp_luma; + } + else { + if (v->h_ratio[k] > 1.0) { + v->pscl_throughput_chroma[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput * v->h_ratio[k] / 2.0 /dcn_bw_ceil2(v->hta_pschroma[k] / 6.0, 1.0)); + } + else { + v->pscl_throughput_chroma[k] =dcn_bw_min2(v->max_dchub_topscl_throughput, v->max_pscl_tolb_throughput); + } + v->dppclk_using_single_dpp_chroma = v->pixel_clock[k] *dcn_bw_max3(v->vta_pschroma[k] / 6.0 *dcn_bw_min2(1.0, v->h_ratio[k] / 2.0), v->h_ratio[k] * v->v_ratio[k] / 4.0 / v->pscl_throughput_chroma[k], 1.0); + v->dppclk_using_single_dpp =dcn_bw_max2(v->dppclk_using_single_dpp_luma, v->dppclk_using_single_dpp_chroma); + } + if (v->odm_capable == dcn_bw_yes) { + v->dispclk_with_ramping =dcn_bw_max2(v->dispclk_with_ramping,dcn_bw_max2(v->dppclk_using_single_dpp / v->dpp_per_plane[k] * v->dispclk_dppclk_ratio, v->pixel_clock[k] / v->dpp_per_plane[k]) * (1.0 + v->downspreading / 100.0) * (1.0 + v->dispclk_ramping_margin / 100.0)); + v->dispclk_without_ramping =dcn_bw_max2(v->dispclk_without_ramping,dcn_bw_max2(v->dppclk_using_single_dpp / v->dpp_per_plane[k] * v->dispclk_dppclk_ratio, v->pixel_clock[k] / v->dpp_per_plane[k]) * (1.0 + v->downspreading / 100.0)); + } + else { + v->dispclk_with_ramping =dcn_bw_max2(v->dispclk_with_ramping,dcn_bw_max2(v->dppclk_using_single_dpp / v->dpp_per_plane[k] * v->dispclk_dppclk_ratio, v->pixel_clock[k]) * (1.0 + v->downspreading / 100.0) * (1.0 + v->dispclk_ramping_margin / 100.0)); + v->dispclk_without_ramping =dcn_bw_max2(v->dispclk_without_ramping,dcn_bw_max2(v->dppclk_using_single_dpp / v->dpp_per_plane[k] * v->dispclk_dppclk_ratio, v->pixel_clock[k]) * (1.0 + v->downspreading / 100.0)); + } + } + if (v->dispclk_without_ramping > v->max_dispclk[number_of_states]) { + v->dispclk = v->dispclk_without_ramping; + } + else if (v->dispclk_with_ramping > v->max_dispclk[number_of_states]) { + v->dispclk = v->max_dispclk[number_of_states]; + } + else { + v->dispclk = v->dispclk_with_ramping; + } + v->dppclk = v->dispclk / v->dispclk_dppclk_ratio; + /*urgent watermark*/ + + v->return_bandwidth_to_dcn =dcn_bw_min2(v->return_bus_width * v->dcfclk, v->fabric_and_dram_bandwidth * 1000.0 * v->percent_of_ideal_drambw_received_after_urg_latency / 100.0); + v->dcc_enabled_any_plane = dcn_bw_no; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->dcc_enable[k] == dcn_bw_yes) { + v->dcc_enabled_any_plane = dcn_bw_yes; + } + } + v->return_bw = v->return_bandwidth_to_dcn; + if (v->dcc_enabled_any_plane == dcn_bw_yes && v->return_bandwidth_to_dcn > v->dcfclk * v->return_bus_width / 4.0) { + v->return_bw =dcn_bw_min2(v->return_bw, v->return_bandwidth_to_dcn * 4.0 * (1.0 - v->urgent_latency / ((v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 / (v->return_bandwidth_to_dcn - v->dcfclk * v->return_bus_width / 4.0) + v->urgent_latency))); + } + v->critical_compression = 2.0 * v->return_bus_width * v->dcfclk * v->urgent_latency / (v->return_bandwidth_to_dcn * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0); + if (v->dcc_enabled_any_plane == dcn_bw_yes && v->critical_compression > 1.0 && v->critical_compression < 4.0) { + v->return_bw =dcn_bw_min2(v->return_bw, dcn_bw_pow(4.0 * v->return_bandwidth_to_dcn * (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 * v->return_bus_width * v->dcfclk * v->urgent_latency / (v->return_bandwidth_to_dcn * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0), 2)); + } + v->return_bandwidth_to_dcn =dcn_bw_min2(v->return_bus_width * v->dcfclk, v->fabric_and_dram_bandwidth * 1000.0); + if (v->dcc_enabled_any_plane == dcn_bw_yes && v->return_bandwidth_to_dcn > v->dcfclk * v->return_bus_width / 4.0) { + v->return_bw =dcn_bw_min2(v->return_bw, v->return_bandwidth_to_dcn * 4.0 * (1.0 - v->urgent_latency / ((v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 / (v->return_bandwidth_to_dcn - v->dcfclk * v->return_bus_width / 4.0) + v->urgent_latency))); + } + v->critical_compression = 2.0 * v->return_bus_width * v->dcfclk * v->urgent_latency / (v->return_bandwidth_to_dcn * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0); + if (v->dcc_enabled_any_plane == dcn_bw_yes && v->critical_compression > 1.0 && v->critical_compression < 4.0) { + v->return_bw =dcn_bw_min2(v->return_bw, dcn_bw_pow(4.0 * v->return_bandwidth_to_dcn * (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0 * v->return_bus_width * v->dcfclk * v->urgent_latency / (v->return_bandwidth_to_dcn * v->urgent_latency + (v->rob_buffer_size_in_kbyte - v->pixel_chunk_size_in_kbyte) * 1024.0), 2)); + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->source_scan[k] == dcn_bw_hor) { + v->swath_width_y[k] = v->viewport_width[k] / v->dpp_per_plane[k]; + } + else { + v->swath_width_y[k] = v->viewport_height[k] / v->dpp_per_plane[k]; + } + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->source_pixel_format[k] == dcn_bw_rgb_sub_64) { + v->byte_per_pixel_dety[k] = 8.0; + v->byte_per_pixel_detc[k] = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_32) { + v->byte_per_pixel_dety[k] = 4.0; + v->byte_per_pixel_detc[k] = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_16) { + v->byte_per_pixel_dety[k] = 2.0; + v->byte_per_pixel_detc[k] = 0.0; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8) { + v->byte_per_pixel_dety[k] = 1.0; + v->byte_per_pixel_detc[k] = 2.0; + } + else { + v->byte_per_pixel_dety[k] = 4.0f / 3.0f; + v->byte_per_pixel_detc[k] = 8.0f / 3.0f; + } + } + v->total_data_read_bandwidth = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->read_bandwidth_plane_luma[k] = v->swath_width_y[k] * v->dpp_per_plane[k] *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / (v->htotal[k] / v->pixel_clock[k]) * v->v_ratio[k]; + v->read_bandwidth_plane_chroma[k] = v->swath_width_y[k] / 2.0 * v->dpp_per_plane[k] *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / (v->htotal[k] / v->pixel_clock[k]) * v->v_ratio[k] / 2.0; + v->total_data_read_bandwidth = v->total_data_read_bandwidth + v->read_bandwidth_plane_luma[k] + v->read_bandwidth_plane_chroma[k]; + } + v->total_active_dpp = 0.0; + v->total_dcc_active_dpp = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->total_active_dpp = v->total_active_dpp + v->dpp_per_plane[k]; + if (v->dcc_enable[k] == dcn_bw_yes) { + v->total_dcc_active_dpp = v->total_dcc_active_dpp + v->dpp_per_plane[k]; + } + } + v->urgent_round_trip_and_out_of_order_latency = (v->round_trip_ping_latency_cycles + 32.0) / v->dcfclk + v->urgent_out_of_order_return_per_channel * v->number_of_channels / v->return_bw; + v->last_pixel_of_line_extra_watermark = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->v_ratio[k] <= 1.0) { + v->display_pipe_line_delivery_time_luma[k] = v->swath_width_y[k] * v->dpp_per_plane[k] / v->h_ratio[k] / v->pixel_clock[k]; + } + else { + v->display_pipe_line_delivery_time_luma[k] = v->swath_width_y[k] / v->pscl_throughput[k] / v->dppclk; + } + v->data_fabric_line_delivery_time_luma = v->swath_width_y[k] * v->swath_height_y[k] *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / (v->return_bw * v->read_bandwidth_plane_luma[k] / v->dpp_per_plane[k] / v->total_data_read_bandwidth); + v->last_pixel_of_line_extra_watermark =dcn_bw_max2(v->last_pixel_of_line_extra_watermark, v->data_fabric_line_delivery_time_luma - v->display_pipe_line_delivery_time_luma[k]); + if (v->byte_per_pixel_detc[k] == 0.0) { + v->display_pipe_line_delivery_time_chroma[k] = 0.0; + } + else { + if (v->v_ratio[k] / 2.0 <= 1.0) { + v->display_pipe_line_delivery_time_chroma[k] = v->swath_width_y[k] / 2.0 * v->dpp_per_plane[k] / (v->h_ratio[k] / 2.0) / v->pixel_clock[k]; + } + else { + v->display_pipe_line_delivery_time_chroma[k] = v->swath_width_y[k] / 2.0 / v->pscl_throughput_chroma[k] / v->dppclk; + } + v->data_fabric_line_delivery_time_chroma = v->swath_width_y[k] / 2.0 * v->swath_height_c[k] *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / (v->return_bw * v->read_bandwidth_plane_chroma[k] / v->dpp_per_plane[k] / v->total_data_read_bandwidth); + v->last_pixel_of_line_extra_watermark =dcn_bw_max2(v->last_pixel_of_line_extra_watermark, v->data_fabric_line_delivery_time_chroma - v->display_pipe_line_delivery_time_chroma[k]); + } + } + v->urgent_extra_latency = v->urgent_round_trip_and_out_of_order_latency + (v->total_active_dpp * v->pixel_chunk_size_in_kbyte + v->total_dcc_active_dpp * v->meta_chunk_size) * 1024.0 / v->return_bw; + if (v->pte_enable == dcn_bw_yes) { + v->urgent_extra_latency = v->urgent_extra_latency + v->total_active_dpp * v->pte_chunk_size * 1024.0 / v->return_bw; + } + v->urgent_watermark = v->urgent_latency + v->last_pixel_of_line_extra_watermark + v->urgent_extra_latency; + v->ptemeta_urgent_watermark = v->urgent_watermark + 2.0 * v->urgent_latency; + /*nb p-state/dram clock change watermark*/ + + v->dram_clock_change_watermark = v->dram_clock_change_latency + v->urgent_watermark; + v->total_active_writeback = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->output[k] == dcn_bw_writeback) { + v->total_active_writeback = v->total_active_writeback + 1.0; + } + } + if (v->total_active_writeback <= 1.0) { + v->writeback_dram_clock_change_watermark = v->dram_clock_change_latency + v->write_back_latency; + } + else { + v->writeback_dram_clock_change_watermark = v->dram_clock_change_latency + v->write_back_latency + v->writeback_chunk_size * 1024.0 / 32.0 / v->socclk; + } + /*stutter efficiency*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->lines_in_dety[k] = v->det_buffer_size_y[k] / v->byte_per_pixel_dety[k] / v->swath_width_y[k]; + v->lines_in_dety_rounded_down_to_swath[k] =dcn_bw_floor2(v->lines_in_dety[k], v->swath_height_y[k]); + v->full_det_buffering_time_y[k] = v->lines_in_dety_rounded_down_to_swath[k] * (v->htotal[k] / v->pixel_clock[k]) / v->v_ratio[k]; + if (v->byte_per_pixel_detc[k] > 0.0) { + v->lines_in_detc[k] = v->det_buffer_size_c[k] / v->byte_per_pixel_detc[k] / (v->swath_width_y[k] / 2.0); + v->lines_in_detc_rounded_down_to_swath[k] =dcn_bw_floor2(v->lines_in_detc[k], v->swath_height_c[k]); + v->full_det_buffering_time_c[k] = v->lines_in_detc_rounded_down_to_swath[k] * (v->htotal[k] / v->pixel_clock[k]) / (v->v_ratio[k] / 2.0); + } + else { + v->lines_in_detc[k] = 0.0; + v->lines_in_detc_rounded_down_to_swath[k] = 0.0; + v->full_det_buffering_time_c[k] = 999999.0; + } + } + v->min_full_det_buffering_time = 999999.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->full_det_buffering_time_y[k] < v->min_full_det_buffering_time) { + v->min_full_det_buffering_time = v->full_det_buffering_time_y[k]; + v->frame_time_for_min_full_det_buffering_time = v->vtotal[k] * v->htotal[k] / v->pixel_clock[k]; + } + if (v->full_det_buffering_time_c[k] < v->min_full_det_buffering_time) { + v->min_full_det_buffering_time = v->full_det_buffering_time_c[k]; + v->frame_time_for_min_full_det_buffering_time = v->vtotal[k] * v->htotal[k] / v->pixel_clock[k]; + } + } + v->average_read_bandwidth_gbyte_per_second = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->dcc_enable[k] == dcn_bw_yes) { + v->average_read_bandwidth_gbyte_per_second = v->average_read_bandwidth_gbyte_per_second + v->read_bandwidth_plane_luma[k] / v->dcc_rate[k] / 1000.0 + v->read_bandwidth_plane_chroma[k] / v->dcc_rate[k] / 1000.0; + } + else { + v->average_read_bandwidth_gbyte_per_second = v->average_read_bandwidth_gbyte_per_second + v->read_bandwidth_plane_luma[k] / 1000.0 + v->read_bandwidth_plane_chroma[k] / 1000.0; + } + if (v->dcc_enable[k] == dcn_bw_yes) { + v->average_read_bandwidth_gbyte_per_second = v->average_read_bandwidth_gbyte_per_second + v->read_bandwidth_plane_luma[k] / 1000.0 / 256.0 + v->read_bandwidth_plane_chroma[k] / 1000.0 / 256.0; + } + if (v->pte_enable == dcn_bw_yes) { + v->average_read_bandwidth_gbyte_per_second = v->average_read_bandwidth_gbyte_per_second + v->read_bandwidth_plane_luma[k] / 1000.0 / 512.0 + v->read_bandwidth_plane_chroma[k] / 1000.0 / 512.0; + } + } + v->part_of_burst_that_fits_in_rob =dcn_bw_min2(v->min_full_det_buffering_time * v->total_data_read_bandwidth, v->rob_buffer_size_in_kbyte * 1024.0 * v->total_data_read_bandwidth / (v->average_read_bandwidth_gbyte_per_second * 1000.0)); + v->stutter_burst_time = v->part_of_burst_that_fits_in_rob * (v->average_read_bandwidth_gbyte_per_second * 1000.0) / v->total_data_read_bandwidth / v->return_bw + (v->min_full_det_buffering_time * v->total_data_read_bandwidth - v->part_of_burst_that_fits_in_rob) / (v->dcfclk * 64.0); + if (v->total_active_writeback == 0.0) { + v->stutter_efficiency_not_including_vblank = (1.0 - (v->sr_exit_time + v->stutter_burst_time) / v->min_full_det_buffering_time) * 100.0; + } + else { + v->stutter_efficiency_not_including_vblank = 0.0; + } + v->smallest_vblank = 999999.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->synchronized_vblank == dcn_bw_yes || v->number_of_active_planes == 1) { + v->v_blank_time = (v->vtotal[k] - v->vactive[k]) * v->htotal[k] / v->pixel_clock[k]; + } + else { + v->v_blank_time = 0.0; + } + v->smallest_vblank =dcn_bw_min2(v->smallest_vblank, v->v_blank_time); + } + v->stutter_efficiency = (v->stutter_efficiency_not_including_vblank / 100.0 * (v->frame_time_for_min_full_det_buffering_time - v->smallest_vblank) + v->smallest_vblank) / v->frame_time_for_min_full_det_buffering_time * 100.0; + /*dcfclk deep sleep*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->byte_per_pixel_detc[k] > 0.0) { + v->dcfclk_deep_sleep_per_plane[k] =dcn_bw_max2(1.1 * v->swath_width_y[k] *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / 32.0 / v->display_pipe_line_delivery_time_luma[k], 1.1 * v->swath_width_y[k] / 2.0 *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / 32.0 / v->display_pipe_line_delivery_time_chroma[k]); + } + else { + v->dcfclk_deep_sleep_per_plane[k] = 1.1 * v->swath_width_y[k] *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / 64.0 / v->display_pipe_line_delivery_time_luma[k]; + } + v->dcfclk_deep_sleep_per_plane[k] =dcn_bw_max2(v->dcfclk_deep_sleep_per_plane[k], v->pixel_clock[k] / 16.0); + } + v->dcf_clk_deep_sleep = 8.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->dcf_clk_deep_sleep =dcn_bw_max2(v->dcf_clk_deep_sleep, v->dcfclk_deep_sleep_per_plane[k]); + } + /*stutter watermark*/ + + v->stutter_exit_watermark = v->sr_exit_time + v->last_pixel_of_line_extra_watermark + v->urgent_extra_latency + 10.0 / v->dcf_clk_deep_sleep; + v->stutter_enter_plus_exit_watermark = v->sr_enter_plus_exit_time + v->last_pixel_of_line_extra_watermark + v->urgent_extra_latency; + /*urgent latency supported*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->effective_det_plus_lb_lines_luma =dcn_bw_floor2(v->lines_in_dety[k] +dcn_bw_min2(v->lines_in_dety[k] * v->dppclk * v->byte_per_pixel_dety[k] * v->pscl_throughput[k] / (v->return_bw / v->dpp_per_plane[k]), v->effective_lb_latency_hiding_source_lines_luma), v->swath_height_y[k]); + v->urgent_latency_support_us_luma = v->effective_det_plus_lb_lines_luma * (v->htotal[k] / v->pixel_clock[k]) / v->v_ratio[k] - v->effective_det_plus_lb_lines_luma * v->swath_width_y[k] * v->byte_per_pixel_dety[k] / (v->return_bw / v->dpp_per_plane[k]); + if (v->byte_per_pixel_detc[k] > 0.0) { + v->effective_det_plus_lb_lines_chroma =dcn_bw_floor2(v->lines_in_detc[k] +dcn_bw_min2(v->lines_in_detc[k] * v->dppclk * v->byte_per_pixel_detc[k] * v->pscl_throughput_chroma[k] / (v->return_bw / v->dpp_per_plane[k]), v->effective_lb_latency_hiding_source_lines_chroma), v->swath_height_c[k]); + v->urgent_latency_support_us_chroma = v->effective_det_plus_lb_lines_chroma * (v->htotal[k] / v->pixel_clock[k]) / (v->v_ratio[k] / 2.0) - v->effective_det_plus_lb_lines_chroma * (v->swath_width_y[k] / 2.0) * v->byte_per_pixel_detc[k] / (v->return_bw / v->dpp_per_plane[k]); + v->urgent_latency_support_us[k] =dcn_bw_min2(v->urgent_latency_support_us_luma, v->urgent_latency_support_us_chroma); + } + else { + v->urgent_latency_support_us[k] = v->urgent_latency_support_us_luma; + } + } + v->min_urgent_latency_support_us = 999999.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->min_urgent_latency_support_us =dcn_bw_min2(v->min_urgent_latency_support_us, v->urgent_latency_support_us[k]); + } + /*non-urgent latency tolerance*/ + + v->non_urgent_latency_tolerance = v->min_urgent_latency_support_us - v->urgent_watermark; + /*prefetch*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if ((v->source_pixel_format[k] == dcn_bw_rgb_sub_64 || v->source_pixel_format[k] == dcn_bw_rgb_sub_32 || v->source_pixel_format[k] == dcn_bw_rgb_sub_16)) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->block_height256_bytes_y = 1.0; + } + else if (v->source_pixel_format[k] == dcn_bw_rgb_sub_64) { + v->block_height256_bytes_y = 4.0; + } + else { + v->block_height256_bytes_y = 8.0; + } + v->block_height256_bytes_c = 0.0; + } + else { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->block_height256_bytes_y = 1.0; + v->block_height256_bytes_c = 1.0; + } + else if (v->source_pixel_format[k] == dcn_bw_yuv420_sub_8) { + v->block_height256_bytes_y = 16.0; + v->block_height256_bytes_c = 8.0; + } + else { + v->block_height256_bytes_y = 8.0; + v->block_height256_bytes_c = 8.0; + } + } + if (v->dcc_enable[k] == dcn_bw_yes) { + v->meta_request_width_y = 64.0 * 256.0 /dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / (8.0 * v->block_height256_bytes_y); + v->meta_surf_width_y =dcn_bw_ceil2(v->swath_width_y[k] - 1.0, v->meta_request_width_y) + v->meta_request_width_y; + v->meta_surf_height_y =dcn_bw_ceil2(v->viewport_height[k] - 1.0, 8.0 * v->block_height256_bytes_y) + 8.0 * v->block_height256_bytes_y; + if (v->pte_enable == dcn_bw_yes) { + v->meta_pte_bytes_frame_y = (dcn_bw_ceil2((v->meta_surf_width_y * v->meta_surf_height_y *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / 256.0 - 4096.0) / 8.0 / 4096.0, 1.0) + 1) * 64.0; + } + else { + v->meta_pte_bytes_frame_y = 0.0; + } + if (v->source_scan[k] == dcn_bw_hor) { + v->meta_row_byte_y = v->meta_surf_width_y * 8.0 * v->block_height256_bytes_y *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / 256.0; + } + else { + v->meta_row_byte_y = v->meta_surf_height_y * v->meta_request_width_y *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / 256.0; + } + } + else { + v->meta_pte_bytes_frame_y = 0.0; + v->meta_row_byte_y = 0.0; + } + if (v->pte_enable == dcn_bw_yes) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->macro_tile_size_byte_y = 256.0; + v->macro_tile_height_y = 1.0; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_4_kb_s || v->source_surface_mode[k] == dcn_bw_sw_4_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d_x) { + v->macro_tile_size_byte_y = 4096.0; + v->macro_tile_height_y = 4.0 * v->block_height256_bytes_y; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_64_kb_s || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_x) { + v->macro_tile_size_byte_y = 64.0 * 1024; + v->macro_tile_height_y = 16.0 * v->block_height256_bytes_y; + } + else { + v->macro_tile_size_byte_y = 256.0 * 1024; + v->macro_tile_height_y = 32.0 * v->block_height256_bytes_y; + } + if (v->macro_tile_size_byte_y <= 65536.0) { + v->pixel_pte_req_height_y = v->macro_tile_height_y; + } + else { + v->pixel_pte_req_height_y = 16.0 * v->block_height256_bytes_y; + } + v->pixel_pte_req_width_y = 4096.0 /dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) / v->pixel_pte_req_height_y * 8; + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->pixel_pte_bytes_per_row_y = 64.0 * (dcn_bw_ceil2((v->swath_width_y[k] *dcn_bw_min2(128.0, dcn_bw_pow(2.0,dcn_bw_floor2(dcn_bw_log(v->pte_buffer_size_in_requests * v->pixel_pte_req_width_y / v->swath_width_y[k], 2.0), 1.0))) - 1.0) / v->pixel_pte_req_width_y, 1.0) + 1); + } + else if (v->source_scan[k] == dcn_bw_hor) { + v->pixel_pte_bytes_per_row_y = 64.0 * (dcn_bw_ceil2((v->swath_width_y[k] - 1.0) / v->pixel_pte_req_width_y, 1.0) + 1); + } + else { + v->pixel_pte_bytes_per_row_y = 64.0 * (dcn_bw_ceil2((v->viewport_height[k] - 1.0) / v->pixel_pte_req_height_y, 1.0) + 1); + } + } + else { + v->pixel_pte_bytes_per_row_y = 0.0; + } + if ((v->source_pixel_format[k] != dcn_bw_rgb_sub_64 && v->source_pixel_format[k] != dcn_bw_rgb_sub_32 && v->source_pixel_format[k] != dcn_bw_rgb_sub_16)) { + if (v->dcc_enable[k] == dcn_bw_yes) { + v->meta_request_width_c = 64.0 * 256.0 /dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / (8.0 * v->block_height256_bytes_c); + v->meta_surf_width_c =dcn_bw_ceil2(v->swath_width_y[k] / 2.0 - 1.0, v->meta_request_width_c) + v->meta_request_width_c; + v->meta_surf_height_c =dcn_bw_ceil2(v->viewport_height[k] / 2.0 - 1.0, 8.0 * v->block_height256_bytes_c) + 8.0 * v->block_height256_bytes_c; + if (v->pte_enable == dcn_bw_yes) { + v->meta_pte_bytes_frame_c = (dcn_bw_ceil2((v->meta_surf_width_c * v->meta_surf_height_c *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / 256.0 - 4096.0) / 8.0 / 4096.0, 1.0) + 1) * 64.0; + } + else { + v->meta_pte_bytes_frame_c = 0.0; + } + if (v->source_scan[k] == dcn_bw_hor) { + v->meta_row_byte_c = v->meta_surf_width_c * 8.0 * v->block_height256_bytes_c *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / 256.0; + } + else { + v->meta_row_byte_c = v->meta_surf_height_c * v->meta_request_width_c *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / 256.0; + } + } + else { + v->meta_pte_bytes_frame_c = 0.0; + v->meta_row_byte_c = 0.0; + } + if (v->pte_enable == dcn_bw_yes) { + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->macro_tile_size_bytes_c = 256.0; + v->macro_tile_height_c = 1.0; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_4_kb_s || v->source_surface_mode[k] == dcn_bw_sw_4_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d || v->source_surface_mode[k] == dcn_bw_sw_4_kb_d_x) { + v->macro_tile_size_bytes_c = 4096.0; + v->macro_tile_height_c = 4.0 * v->block_height256_bytes_c; + } + else if (v->source_surface_mode[k] == dcn_bw_sw_64_kb_s || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_s_x || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_t || v->source_surface_mode[k] == dcn_bw_sw_64_kb_d_x) { + v->macro_tile_size_bytes_c = 64.0 * 1024; + v->macro_tile_height_c = 16.0 * v->block_height256_bytes_c; + } + else { + v->macro_tile_size_bytes_c = 256.0 * 1024; + v->macro_tile_height_c = 32.0 * v->block_height256_bytes_c; + } + if (v->macro_tile_size_bytes_c <= 65536.0) { + v->pixel_pte_req_height_c = v->macro_tile_height_c; + } + else { + v->pixel_pte_req_height_c = 16.0 * v->block_height256_bytes_c; + } + v->pixel_pte_req_width_c = 4096.0 /dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / v->pixel_pte_req_height_c * 8; + if (v->source_surface_mode[k] == dcn_bw_sw_linear) { + v->pixel_pte_bytes_per_row_c = 64.0 * (dcn_bw_ceil2((v->swath_width_y[k] / 2.0 * dcn_bw_min2(128.0, dcn_bw_pow(2.0,dcn_bw_floor2(dcn_bw_log(v->pte_buffer_size_in_requests * v->pixel_pte_req_width_c / (v->swath_width_y[k] / 2.0), 2.0), 1.0))) - 1.0) / v->pixel_pte_req_width_c, 1.0) + 1); + } + else if (v->source_scan[k] == dcn_bw_hor) { + v->pixel_pte_bytes_per_row_c = 64.0 * (dcn_bw_ceil2((v->swath_width_y[k] / 2.0 - 1.0) / v->pixel_pte_req_width_c, 1.0) + 1); + } + else { + v->pixel_pte_bytes_per_row_c = 64.0 * (dcn_bw_ceil2((v->viewport_height[k] / 2.0 - 1.0) / v->pixel_pte_req_height_c, 1.0) + 1); + } + } + else { + v->pixel_pte_bytes_per_row_c = 0.0; + } + } + else { + v->pixel_pte_bytes_per_row_c = 0.0; + v->meta_pte_bytes_frame_c = 0.0; + v->meta_row_byte_c = 0.0; + } + v->pixel_pte_bytes_per_row[k] = v->pixel_pte_bytes_per_row_y + v->pixel_pte_bytes_per_row_c; + v->meta_pte_bytes_frame[k] = v->meta_pte_bytes_frame_y + v->meta_pte_bytes_frame_c; + v->meta_row_byte[k] = v->meta_row_byte_y + v->meta_row_byte_c; + v->v_init_pre_fill_y[k] =dcn_bw_floor2((v->v_ratio[k] + v->vtaps[k] + 1.0 + v->interlace_output[k] * 0.5 * v->v_ratio[k]) / 2.0, 1.0); + v->max_num_swath_y[k] =dcn_bw_ceil2((v->v_init_pre_fill_y[k] - 1.0) / v->swath_height_y[k], 1.0) + 1; + if (v->v_init_pre_fill_y[k] > 1.0) { + v->max_partial_swath_y =dcn_bw_mod((v->v_init_pre_fill_y[k] - 2.0), v->swath_height_y[k]); + } + else { + v->max_partial_swath_y =dcn_bw_mod((v->v_init_pre_fill_y[k] + v->swath_height_y[k] - 2.0), v->swath_height_y[k]); + } + v->max_partial_swath_y =dcn_bw_max2(1.0, v->max_partial_swath_y); + v->prefetch_source_lines_y[k] = v->max_num_swath_y[k] * v->swath_height_y[k] + v->max_partial_swath_y; + if ((v->source_pixel_format[k] != dcn_bw_rgb_sub_64 && v->source_pixel_format[k] != dcn_bw_rgb_sub_32 && v->source_pixel_format[k] != dcn_bw_rgb_sub_16)) { + v->v_init_pre_fill_c[k] =dcn_bw_floor2((v->v_ratio[k] / 2.0 + v->vtaps[k] + 1.0 + v->interlace_output[k] * 0.5 * v->v_ratio[k] / 2.0) / 2.0, 1.0); + v->max_num_swath_c[k] =dcn_bw_ceil2((v->v_init_pre_fill_c[k] - 1.0) / v->swath_height_c[k], 1.0) + 1; + if (v->v_init_pre_fill_c[k] > 1.0) { + v->max_partial_swath_c =dcn_bw_mod((v->v_init_pre_fill_c[k] - 2.0), v->swath_height_c[k]); + } + else { + v->max_partial_swath_c =dcn_bw_mod((v->v_init_pre_fill_c[k] + v->swath_height_c[k] - 2.0), v->swath_height_c[k]); + } + v->max_partial_swath_c =dcn_bw_max2(1.0, v->max_partial_swath_c); + } + else { + v->max_num_swath_c[k] = 0.0; + v->max_partial_swath_c = 0.0; + } + v->prefetch_source_lines_c[k] = v->max_num_swath_c[k] * v->swath_height_c[k] + v->max_partial_swath_c; + } + v->t_calc = 24.0 / v->dcf_clk_deep_sleep; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one == dcn_bw_yes) { + v->max_vstartup_lines[k] = v->vtotal[k] - v->vactive[k] - 1.0; + } + else { + v->max_vstartup_lines[k] = v->v_sync_plus_back_porch[k] - 1.0; + } + } + v->next_prefetch_mode = 0.0; + do { + v->v_startup_lines = 13.0; + do { + v->planes_with_room_to_increase_vstartup_prefetch_bw_less_than_active_bw = dcn_bw_yes; + v->planes_with_room_to_increase_vstartup_vratio_prefetch_more_than4 = dcn_bw_no; + v->planes_with_room_to_increase_vstartup_destination_line_times_for_prefetch_less_than2 = dcn_bw_no; + v->v_ratio_prefetch_more_than4 = dcn_bw_no; + v->destination_line_times_for_prefetch_less_than2 = dcn_bw_no; + v->prefetch_mode = v->next_prefetch_mode; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->dstx_after_scaler = 90.0 * v->pixel_clock[k] / v->dppclk + 42.0 * v->pixel_clock[k] / v->dispclk; + if (v->dpp_per_plane[k] > 1.0) { + v->dstx_after_scaler = v->dstx_after_scaler + v->scaler_rec_out_width[k] / 2.0; + } + if (v->output_format[k] == dcn_bw_420) { + v->dsty_after_scaler = 1.0; + } + else { + v->dsty_after_scaler = 0.0; + } + v->v_update_offset_pix =dcn_bw_ceil2(v->htotal[k] / 4.0, 1.0); + v->total_repeater_delay_time = v->max_inter_dcn_tile_repeaters * (2.0 / v->dppclk + 3.0 / v->dispclk); + v->v_update_width_pix = (14.0 / v->dcf_clk_deep_sleep + 12.0 / v->dppclk + v->total_repeater_delay_time) * v->pixel_clock[k]; + v->v_ready_offset_pix =dcn_bw_max2(150.0 / v->dppclk, v->total_repeater_delay_time + 20.0 / v->dcf_clk_deep_sleep + 10.0 / v->dppclk) * v->pixel_clock[k]; + v->t_setup = (v->v_update_offset_pix + v->v_update_width_pix + v->v_ready_offset_pix) / v->pixel_clock[k]; + v->v_startup[k] =dcn_bw_min2(v->v_startup_lines, v->max_vstartup_lines[k]); + if (v->prefetch_mode == 0.0) { + v->t_wait =dcn_bw_max3(v->dram_clock_change_latency + v->urgent_latency, v->sr_enter_plus_exit_time, v->urgent_latency); + } + else if (v->prefetch_mode == 1.0) { + v->t_wait =dcn_bw_max2(v->sr_enter_plus_exit_time, v->urgent_latency); + } + else { + v->t_wait = v->urgent_latency; + } + v->destination_lines_for_prefetch[k] =dcn_bw_floor2(4.0 * (v->v_startup[k] - v->t_wait / (v->htotal[k] / v->pixel_clock[k]) - (v->t_calc + v->t_setup) / (v->htotal[k] / v->pixel_clock[k]) - (v->dsty_after_scaler + v->dstx_after_scaler / v->htotal[k]) + 0.125), 1.0) / 4; + if (v->destination_lines_for_prefetch[k] > 0.0) { + v->prefetch_bandwidth[k] = (v->meta_pte_bytes_frame[k] + 2.0 * v->meta_row_byte[k] + 2.0 * v->pixel_pte_bytes_per_row[k] + v->prefetch_source_lines_y[k] * v->swath_width_y[k] *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) + v->prefetch_source_lines_c[k] * v->swath_width_y[k] / 2.0 *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0)) / (v->destination_lines_for_prefetch[k] * v->htotal[k] / v->pixel_clock[k]); + } + else { + v->prefetch_bandwidth[k] = 999999.0; + } + } + v->bandwidth_available_for_immediate_flip = v->return_bw; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->bandwidth_available_for_immediate_flip = v->bandwidth_available_for_immediate_flip -dcn_bw_max2(v->read_bandwidth_plane_luma[k] + v->read_bandwidth_plane_chroma[k], v->prefetch_bandwidth[k]); + } + v->tot_immediate_flip_bytes = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->immediate_flip_supported == dcn_bw_yes && (v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10)) { + v->tot_immediate_flip_bytes = v->tot_immediate_flip_bytes + v->meta_pte_bytes_frame[k] + v->meta_row_byte[k] + v->pixel_pte_bytes_per_row[k]; + } + } + v->max_rd_bandwidth = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->pte_enable == dcn_bw_yes && v->dcc_enable[k] == dcn_bw_yes) { + if (v->immediate_flip_supported == dcn_bw_yes && (v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10)) { + v->time_for_fetching_meta_pte =dcn_bw_max5(v->meta_pte_bytes_frame[k] / v->prefetch_bandwidth[k], v->meta_pte_bytes_frame[k] * v->tot_immediate_flip_bytes / (v->bandwidth_available_for_immediate_flip * (v->meta_pte_bytes_frame[k] + v->meta_row_byte[k] + v->pixel_pte_bytes_per_row[k])), v->urgent_extra_latency, v->urgent_latency, v->htotal[k] / v->pixel_clock[k] / 4.0); + } + else { + v->time_for_fetching_meta_pte =dcn_bw_max3(v->meta_pte_bytes_frame[k] / v->prefetch_bandwidth[k], v->urgent_extra_latency, v->htotal[k] / v->pixel_clock[k] / 4.0); + } + } + else { + v->time_for_fetching_meta_pte = v->htotal[k] / v->pixel_clock[k] / 4.0; + } + v->destination_lines_to_request_vm_inv_blank[k] =dcn_bw_floor2(4.0 * (v->time_for_fetching_meta_pte / (v->htotal[k] / v->pixel_clock[k]) + 0.125), 1.0) / 4; + if ((v->pte_enable == dcn_bw_yes || v->dcc_enable[k] == dcn_bw_yes)) { + if (v->immediate_flip_supported == dcn_bw_yes && (v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10)) { + v->time_for_fetching_row_in_vblank =dcn_bw_max5((v->meta_row_byte[k] + v->pixel_pte_bytes_per_row[k]) / v->prefetch_bandwidth[k], (v->meta_row_byte[k] + v->pixel_pte_bytes_per_row[k]) * v->tot_immediate_flip_bytes / (v->bandwidth_available_for_immediate_flip * (v->meta_pte_bytes_frame[k] + v->meta_row_byte[k] + v->pixel_pte_bytes_per_row[k])), v->urgent_extra_latency, 2.0 * v->urgent_latency, v->htotal[k] / v->pixel_clock[k] - v->time_for_fetching_meta_pte); + } + else { + v->time_for_fetching_row_in_vblank =dcn_bw_max3((v->meta_row_byte[k] + v->pixel_pte_bytes_per_row[k]) / v->prefetch_bandwidth[k], v->urgent_extra_latency, v->htotal[k] / v->pixel_clock[k] - v->time_for_fetching_meta_pte); + } + } + else { + v->time_for_fetching_row_in_vblank =dcn_bw_max2(v->urgent_extra_latency - v->time_for_fetching_meta_pte, v->htotal[k] / v->pixel_clock[k] - v->time_for_fetching_meta_pte); + } + v->destination_lines_to_request_row_in_vblank[k] =dcn_bw_floor2(4.0 * (v->time_for_fetching_row_in_vblank / (v->htotal[k] / v->pixel_clock[k]) + 0.125), 1.0) / 4; + v->lines_to_request_prefetch_pixel_data = v->destination_lines_for_prefetch[k] - v->destination_lines_to_request_vm_inv_blank[k] - v->destination_lines_to_request_row_in_vblank[k]; + if (v->lines_to_request_prefetch_pixel_data > 0.0) { + v->v_ratio_prefetch_y[k] = v->prefetch_source_lines_y[k] / v->lines_to_request_prefetch_pixel_data; + if ((v->swath_height_y[k] > 4.0)) { + if (v->lines_to_request_prefetch_pixel_data > (v->v_init_pre_fill_y[k] - 3.0) / 2.0) { + v->v_ratio_prefetch_y[k] =dcn_bw_max2(v->v_ratio_prefetch_y[k], v->max_num_swath_y[k] * v->swath_height_y[k] / (v->lines_to_request_prefetch_pixel_data - (v->v_init_pre_fill_y[k] - 3.0) / 2.0)); + } + else { + v->v_ratio_prefetch_y[k] = 999999.0; + } + } + } + else { + v->v_ratio_prefetch_y[k] = 999999.0; + } + v->v_ratio_prefetch_y[k] =dcn_bw_max2(v->v_ratio_prefetch_y[k], 1.0); + if (v->lines_to_request_prefetch_pixel_data > 0.0) { + v->v_ratio_prefetch_c[k] = v->prefetch_source_lines_c[k] / v->lines_to_request_prefetch_pixel_data; + if ((v->swath_height_c[k] > 4.0)) { + if (v->lines_to_request_prefetch_pixel_data > (v->v_init_pre_fill_c[k] - 3.0) / 2.0) { + v->v_ratio_prefetch_c[k] =dcn_bw_max2(v->v_ratio_prefetch_c[k], v->max_num_swath_c[k] * v->swath_height_c[k] / (v->lines_to_request_prefetch_pixel_data - (v->v_init_pre_fill_c[k] - 3.0) / 2.0)); + } + else { + v->v_ratio_prefetch_c[k] = 999999.0; + } + } + } + else { + v->v_ratio_prefetch_c[k] = 999999.0; + } + v->v_ratio_prefetch_c[k] =dcn_bw_max2(v->v_ratio_prefetch_c[k], 1.0); + if (v->lines_to_request_prefetch_pixel_data > 0.0) { + v->required_prefetch_pix_data_bw = v->dpp_per_plane[k] * (v->prefetch_source_lines_y[k] / v->lines_to_request_prefetch_pixel_data *dcn_bw_ceil2(v->byte_per_pixel_dety[k], 1.0) + v->prefetch_source_lines_c[k] / v->lines_to_request_prefetch_pixel_data *dcn_bw_ceil2(v->byte_per_pixel_detc[k], 2.0) / 2.0) * v->swath_width_y[k] / (v->htotal[k] / v->pixel_clock[k]); + } + else { + v->required_prefetch_pix_data_bw = 999999.0; + } + v->max_rd_bandwidth = v->max_rd_bandwidth +dcn_bw_max2(v->read_bandwidth_plane_luma[k] + v->read_bandwidth_plane_chroma[k], v->required_prefetch_pix_data_bw); + if (v->immediate_flip_supported == dcn_bw_yes && (v->source_pixel_format[k] != dcn_bw_yuv420_sub_8 && v->source_pixel_format[k] != dcn_bw_yuv420_sub_10)) { + v->max_rd_bandwidth = v->max_rd_bandwidth +dcn_bw_max2(v->meta_pte_bytes_frame[k] / (v->destination_lines_to_request_vm_inv_blank[k] * v->htotal[k] / v->pixel_clock[k]), (v->meta_row_byte[k] + v->pixel_pte_bytes_per_row[k]) / (v->destination_lines_to_request_row_in_vblank[k] * v->htotal[k] / v->pixel_clock[k])); + } + if (v->v_ratio_prefetch_y[k] > 4.0 || v->v_ratio_prefetch_c[k] > 4.0) { + v->v_ratio_prefetch_more_than4 = dcn_bw_yes; + } + if (v->destination_lines_for_prefetch[k] < 2.0) { + v->destination_line_times_for_prefetch_less_than2 = dcn_bw_yes; + } + if (v->max_vstartup_lines[k] > v->v_startup_lines) { + if (v->required_prefetch_pix_data_bw > (v->read_bandwidth_plane_luma[k] + v->read_bandwidth_plane_chroma[k])) { + v->planes_with_room_to_increase_vstartup_prefetch_bw_less_than_active_bw = dcn_bw_no; + } + if (v->v_ratio_prefetch_y[k] > 4.0 || v->v_ratio_prefetch_c[k] > 4.0) { + v->planes_with_room_to_increase_vstartup_vratio_prefetch_more_than4 = dcn_bw_yes; + } + if (v->destination_lines_for_prefetch[k] < 2.0) { + v->planes_with_room_to_increase_vstartup_destination_line_times_for_prefetch_less_than2 = dcn_bw_yes; + } + } + } + if (v->max_rd_bandwidth <= v->return_bw && v->v_ratio_prefetch_more_than4 == dcn_bw_no && v->destination_line_times_for_prefetch_less_than2 == dcn_bw_no) { + v->prefetch_mode_supported = dcn_bw_yes; + } + else { + v->prefetch_mode_supported = dcn_bw_no; + } + v->v_startup_lines = v->v_startup_lines + 1.0; + } while (!(v->prefetch_mode_supported == dcn_bw_yes || (v->planes_with_room_to_increase_vstartup_prefetch_bw_less_than_active_bw == dcn_bw_yes && v->planes_with_room_to_increase_vstartup_vratio_prefetch_more_than4 == dcn_bw_no && v->planes_with_room_to_increase_vstartup_destination_line_times_for_prefetch_less_than2 == dcn_bw_no))); + v->next_prefetch_mode = v->next_prefetch_mode + 1.0; + } while (!(v->prefetch_mode_supported == dcn_bw_yes || v->prefetch_mode == 2.0)); + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->v_ratio_prefetch_y[k] <= 1.0) { + v->display_pipe_line_delivery_time_luma_prefetch[k] = v->swath_width_y[k] * v->dpp_per_plane[k] / v->h_ratio[k] / v->pixel_clock[k]; + } + else { + v->display_pipe_line_delivery_time_luma_prefetch[k] = v->swath_width_y[k] / v->pscl_throughput[k] / v->dppclk; + } + if (v->byte_per_pixel_detc[k] == 0.0) { + v->display_pipe_line_delivery_time_chroma_prefetch[k] = 0.0; + } + else { + if (v->v_ratio_prefetch_c[k] <= 1.0) { + v->display_pipe_line_delivery_time_chroma_prefetch[k] = v->swath_width_y[k] * v->dpp_per_plane[k] / v->h_ratio[k] / v->pixel_clock[k]; + } + else { + v->display_pipe_line_delivery_time_chroma_prefetch[k] = v->swath_width_y[k] / v->pscl_throughput[k] / v->dppclk; + } + } + } + /*min ttuv_blank*/ + + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->prefetch_mode == 0.0) { + v->allow_dram_clock_change_during_vblank[k] = dcn_bw_yes; + v->allow_dram_self_refresh_during_vblank[k] = dcn_bw_yes; + v->min_ttuv_blank[k] = v->t_calc +dcn_bw_max3(v->dram_clock_change_watermark, v->stutter_enter_plus_exit_watermark, v->urgent_watermark); + } + else if (v->prefetch_mode == 1.0) { + v->allow_dram_clock_change_during_vblank[k] = dcn_bw_no; + v->allow_dram_self_refresh_during_vblank[k] = dcn_bw_yes; + v->min_ttuv_blank[k] = v->t_calc +dcn_bw_max2(v->stutter_enter_plus_exit_watermark, v->urgent_watermark); + } + else { + v->allow_dram_clock_change_during_vblank[k] = dcn_bw_no; + v->allow_dram_self_refresh_during_vblank[k] = dcn_bw_no; + v->min_ttuv_blank[k] = v->t_calc + v->urgent_watermark; + } + } + /*nb p-state/dram clock change support*/ + + v->active_dp_ps = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->active_dp_ps = v->active_dp_ps + v->dpp_per_plane[k]; + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + v->lb_latency_hiding_source_lines_y =dcn_bw_min2(v->max_line_buffer_lines,dcn_bw_floor2(v->line_buffer_size / v->lb_bit_per_pixel[k] / (v->swath_width_y[k] /dcn_bw_max2(v->h_ratio[k], 1.0)), 1.0)) - (v->vtaps[k] - 1.0); + v->lb_latency_hiding_source_lines_c =dcn_bw_min2(v->max_line_buffer_lines,dcn_bw_floor2(v->line_buffer_size / v->lb_bit_per_pixel[k] / (v->swath_width_y[k] / 2.0 /dcn_bw_max2(v->h_ratio[k] / 2.0, 1.0)), 1.0)) - (v->vta_pschroma[k] - 1.0); + v->effective_lb_latency_hiding_y = v->lb_latency_hiding_source_lines_y / v->v_ratio[k] * (v->htotal[k] / v->pixel_clock[k]); + v->effective_lb_latency_hiding_c = v->lb_latency_hiding_source_lines_c / (v->v_ratio[k] / 2.0) * (v->htotal[k] / v->pixel_clock[k]); + if (v->swath_width_y[k] > 2.0 * v->dpp_output_buffer_pixels) { + v->dpp_output_buffer_lines_y = v->dpp_output_buffer_pixels / v->swath_width_y[k]; + } + else if (v->swath_width_y[k] > v->dpp_output_buffer_pixels) { + v->dpp_output_buffer_lines_y = 0.5; + } + else { + v->dpp_output_buffer_lines_y = 1.0; + } + if (v->swath_width_y[k] / 2.0 > 2.0 * v->dpp_output_buffer_pixels) { + v->dpp_output_buffer_lines_c = v->dpp_output_buffer_pixels / (v->swath_width_y[k] / 2.0); + } + else if (v->swath_width_y[k] / 2.0 > v->dpp_output_buffer_pixels) { + v->dpp_output_buffer_lines_c = 0.5; + } + else { + v->dpp_output_buffer_lines_c = 1.0; + } + v->dppopp_buffering_y = (v->htotal[k] / v->pixel_clock[k]) * (v->dpp_output_buffer_lines_y + v->opp_output_buffer_lines); + v->max_det_buffering_time_y = v->full_det_buffering_time_y[k] + (v->lines_in_dety[k] - v->lines_in_dety_rounded_down_to_swath[k]) / v->swath_height_y[k] * (v->htotal[k] / v->pixel_clock[k]); + v->active_dram_clock_change_latency_margin_y = v->dppopp_buffering_y + v->effective_lb_latency_hiding_y + v->max_det_buffering_time_y - v->dram_clock_change_watermark; + if (v->active_dp_ps > 1.0) { + v->active_dram_clock_change_latency_margin_y = v->active_dram_clock_change_latency_margin_y - (1.0 - 1.0 / (v->active_dp_ps - 1.0)) * v->swath_height_y[k] * (v->htotal[k] / v->pixel_clock[k]); + } + if (v->byte_per_pixel_detc[k] > 0.0) { + v->dppopp_buffering_c = (v->htotal[k] / v->pixel_clock[k]) * (v->dpp_output_buffer_lines_c + v->opp_output_buffer_lines); + v->max_det_buffering_time_c = v->full_det_buffering_time_c[k] + (v->lines_in_detc[k] - v->lines_in_detc_rounded_down_to_swath[k]) / v->swath_height_c[k] * (v->htotal[k] / v->pixel_clock[k]); + v->active_dram_clock_change_latency_margin_c = v->dppopp_buffering_c + v->effective_lb_latency_hiding_c + v->max_det_buffering_time_c - v->dram_clock_change_watermark; + if (v->active_dp_ps > 1.0) { + v->active_dram_clock_change_latency_margin_c = v->active_dram_clock_change_latency_margin_c - (1.0 - 1.0 / (v->active_dp_ps - 1.0)) * v->swath_height_c[k] * (v->htotal[k] / v->pixel_clock[k]); + } + v->active_dram_clock_change_latency_margin[k] =dcn_bw_min2(v->active_dram_clock_change_latency_margin_y, v->active_dram_clock_change_latency_margin_c); + } + else { + v->active_dram_clock_change_latency_margin[k] = v->active_dram_clock_change_latency_margin_y; + } + if (v->output_format[k] == dcn_bw_444) { + v->writeback_dram_clock_change_latency_margin = (v->writeback_luma_buffer_size + v->writeback_chroma_buffer_size) * 1024.0 / (v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k]) * 4.0) - v->writeback_dram_clock_change_watermark; + } + else { + v->writeback_dram_clock_change_latency_margin =dcn_bw_min2(v->writeback_luma_buffer_size, 2.0 * v->writeback_chroma_buffer_size) * 1024.0 / (v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k])) - v->writeback_dram_clock_change_watermark; + } + if (v->output[k] == dcn_bw_writeback) { + v->active_dram_clock_change_latency_margin[k] =dcn_bw_min2(v->active_dram_clock_change_latency_margin[k], v->writeback_dram_clock_change_latency_margin); + } + } + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->allow_dram_clock_change_during_vblank[k] == dcn_bw_yes) { + v->v_blank_dram_clock_change_latency_margin[k] = (v->vtotal[k] - v->scaler_recout_height[k]) * (v->htotal[k] / v->pixel_clock[k]) -dcn_bw_max2(v->dram_clock_change_watermark, v->writeback_dram_clock_change_watermark); + } + else { + v->v_blank_dram_clock_change_latency_margin[k] = 0.0; + } + } + v->min_active_dram_clock_change_margin = 999999.0; + v->v_blank_of_min_active_dram_clock_change_margin = 999999.0; + v->second_min_active_dram_clock_change_margin = 999999.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->active_dram_clock_change_latency_margin[k] < v->min_active_dram_clock_change_margin) { + v->second_min_active_dram_clock_change_margin = v->min_active_dram_clock_change_margin; + v->min_active_dram_clock_change_margin = v->active_dram_clock_change_latency_margin[k]; + v->v_blank_of_min_active_dram_clock_change_margin = v->v_blank_dram_clock_change_latency_margin[k]; + } + else if (v->active_dram_clock_change_latency_margin[k] < v->second_min_active_dram_clock_change_margin) { + v->second_min_active_dram_clock_change_margin = v->active_dram_clock_change_latency_margin[k]; + } + } + v->min_vblank_dram_clock_change_margin = 999999.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->min_vblank_dram_clock_change_margin > v->v_blank_dram_clock_change_latency_margin[k]) { + v->min_vblank_dram_clock_change_margin = v->v_blank_dram_clock_change_latency_margin[k]; + } + } + if (v->synchronized_vblank == dcn_bw_yes || v->number_of_active_planes == 1) { + v->dram_clock_change_margin =dcn_bw_max2(v->min_active_dram_clock_change_margin, v->min_vblank_dram_clock_change_margin); + } + else if (v->v_blank_of_min_active_dram_clock_change_margin > v->min_active_dram_clock_change_margin) { + v->dram_clock_change_margin =dcn_bw_min2(v->second_min_active_dram_clock_change_margin, v->v_blank_of_min_active_dram_clock_change_margin); + } + else { + v->dram_clock_change_margin = v->min_active_dram_clock_change_margin; + } + if (v->min_active_dram_clock_change_margin > 0.0) { + v->dram_clock_change_support = dcn_bw_supported_in_v_active; + } + else if (v->dram_clock_change_margin > 0.0) { + v->dram_clock_change_support = dcn_bw_supported_in_v_blank; + } + else { + v->dram_clock_change_support = dcn_bw_not_supported; + } + /*maximum bandwidth used*/ + + v->wr_bandwidth = 0.0; + for (k = 0; k <= v->number_of_active_planes - 1; k++) { + if (v->output[k] == dcn_bw_writeback && v->output_format[k] == dcn_bw_444) { + v->wr_bandwidth = v->wr_bandwidth + v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k]) * 4.0; + } + else if (v->output[k] == dcn_bw_writeback) { + v->wr_bandwidth = v->wr_bandwidth + v->scaler_rec_out_width[k] / (v->htotal[k] / v->pixel_clock[k]) * 1.5; + } + } + v->max_used_bw = v->max_rd_bandwidth + v->wr_bandwidth; +} diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_auto.h b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_auto.h new file mode 100644 index 000000000000..03f06f682ead --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_auto.h @@ -0,0 +1,37 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _DCN_CALC_AUTO_H_ +#define _DCN_CALC_AUTO_H_ + +#include "dcn_calcs.h" + +void scaler_settings_calculation(struct dcn_bw_internal_vars *v); +void mode_support_and_system_configuration(struct dcn_bw_internal_vars *v); +void display_pipe_configuration(struct dcn_bw_internal_vars *v); +void dispclkdppclkdcfclk_deep_sleep_prefetch_parameters_watermarks_and_performance_calculation( + struct dcn_bw_internal_vars *v); + +#endif /* _DCN_CALC_AUTO_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.c b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.c new file mode 100644 index 000000000000..b6abe0f3bb15 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.c @@ -0,0 +1,120 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dcn_calc_math.h" + +float dcn_bw_mod(const float arg1, const float arg2) +{ + if (arg1 != arg1) + return arg2; + if (arg2 != arg2) + return arg1; + return arg1 - arg1 * ((int) (arg1 / arg2)); +} + +float dcn_bw_min2(const float arg1, const float arg2) +{ + if (arg1 != arg1) + return arg2; + if (arg2 != arg2) + return arg1; + return arg1 < arg2 ? arg1 : arg2; +} + +unsigned int dcn_bw_max(const unsigned int arg1, const unsigned int arg2) +{ + if (arg1 != arg1) + return arg2; + if (arg2 != arg2) + return arg1; + return arg1 > arg2 ? arg1 : arg2; +} +float dcn_bw_max2(const float arg1, const float arg2) +{ + if (arg1 != arg1) + return arg2; + if (arg2 != arg2) + return arg1; + return arg1 > arg2 ? arg1 : arg2; +} + +float dcn_bw_floor2(const float arg, const float significance) +{ + if (significance == 0) + return 0; + return ((int) (arg / significance)) * significance; +} + +float dcn_bw_ceil2(const float arg, const float significance) +{ + float flr = dcn_bw_floor2(arg, significance); + if (significance == 0) + return 0; + return flr + 0.00001 >= arg ? arg : flr + significance; +} + +float dcn_bw_max3(float v1, float v2, float v3) +{ + return v3 > dcn_bw_max2(v1, v2) ? v3 : dcn_bw_max2(v1, v2); +} + +float dcn_bw_max5(float v1, float v2, float v3, float v4, float v5) +{ + return dcn_bw_max3(v1, v2, v3) > dcn_bw_max2(v4, v5) ? dcn_bw_max3(v1, v2, v3) : dcn_bw_max2(v4, v5); +} + +float dcn_bw_pow(float a, float exp) +{ + float temp; + /*ASSERT(exp == (int)exp);*/ + if ((int)exp == 0) + return 1; + temp = dcn_bw_pow(a, (int)(exp / 2)); + if (((int)exp % 2) == 0) { + return temp * temp; + } else { + if ((int)exp > 0) + return a * temp * temp; + else + return (temp * temp) / a; + } +} + +float dcn_bw_log(float a, float b) +{ + int * const exp_ptr = (int *)(&a); + int x = *exp_ptr; + const int log_2 = ((x >> 23) & 255) - 128; + x &= ~(255 << 23); + x += 127 << 23; + *exp_ptr = x; + + a = ((-1.0f / 3) * a + 2) * a - 2.0f / 3; + + if (b > 2.00001 || b < 1.99999) + return (a + log_2) / dcn_bw_log(b, 2); + else + return (a + log_2); +} diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.h b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.h new file mode 100644 index 000000000000..f46ab0e24ca1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.h @@ -0,0 +1,40 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _DCN_CALC_MATH_H_ +#define _DCN_CALC_MATH_H_ + +float dcn_bw_mod(const float arg1, const float arg2); +float dcn_bw_min2(const float arg1, const float arg2); +unsigned int dcn_bw_max(const unsigned int arg1, const unsigned int arg2); +float dcn_bw_max2(const float arg1, const float arg2); +float dcn_bw_floor2(const float arg, const float significance); +float dcn_bw_ceil2(const float arg, const float significance); +float dcn_bw_max3(float v1, float v2, float v3); +float dcn_bw_max5(float v1, float v2, float v3, float v4, float v5); +float dcn_bw_pow(float a, float exp); +float dcn_bw_log(float a, float b); + +#endif /* _DCN_CALC_MATH_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c new file mode 100644 index 000000000000..3dce35e66b09 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c @@ -0,0 +1,1626 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dcn_calcs.h" +#include "dcn_calc_auto.h" +#include "dc.h" +#include "dal_asic_id.h" + +#include "resource.h" +#include "dcn10/dcn10_resource.h" +#include "dcn_calc_math.h" + +/* Defaults from spreadsheet rev#247 */ +const struct dcn_soc_bounding_box dcn10_soc_defaults = { + /* latencies */ + .sr_exit_time = 17, /*us*/ + .sr_enter_plus_exit_time = 19, /*us*/ + .urgent_latency = 4, /*us*/ + .dram_clock_change_latency = 17, /*us*/ + .write_back_latency = 12, /*us*/ + .percent_of_ideal_drambw_received_after_urg_latency = 80, /*%*/ + + /* below default clocks derived from STA target base on + * slow-slow corner + 10% margin with voltages aligned to FCLK. + * + * Use these value if fused value doesn't make sense as earlier + * part don't have correct value fused */ + /* default DCF CLK DPM on RV*/ + .dcfclkv_max0p9 = 655, /* MHz, = 3600/5.5 */ + .dcfclkv_nom0p8 = 626, /* MHz, = 3600/5.75 */ + .dcfclkv_mid0p72 = 600, /* MHz, = 3600/6, bypass */ + .dcfclkv_min0p65 = 300, /* MHz, = 3600/12, bypass */ + + /* default DISP CLK voltage state on RV */ + .max_dispclk_vmax0p9 = 1108, /* MHz, = 3600/3.25 */ + .max_dispclk_vnom0p8 = 1029, /* MHz, = 3600/3.5 */ + .max_dispclk_vmid0p72 = 960, /* MHz, = 3600/3.75 */ + .max_dispclk_vmin0p65 = 626, /* MHz, = 3600/5.75 */ + + /* default DPP CLK voltage state on RV */ + .max_dppclk_vmax0p9 = 720, /* MHz, = 3600/5 */ + .max_dppclk_vnom0p8 = 686, /* MHz, = 3600/5.25 */ + .max_dppclk_vmid0p72 = 626, /* MHz, = 3600/5.75 */ + .max_dppclk_vmin0p65 = 400, /* MHz, = 3600/9 */ + + /* default PHY CLK voltage state on RV */ + .phyclkv_max0p9 = 900, /*MHz*/ + .phyclkv_nom0p8 = 847, /*MHz*/ + .phyclkv_mid0p72 = 800, /*MHz*/ + .phyclkv_min0p65 = 600, /*MHz*/ + + /* BW depend on FCLK, MCLK, # of channels */ + /* dual channel BW */ + .fabric_and_dram_bandwidth_vmax0p9 = 38.4f, /*GB/s*/ + .fabric_and_dram_bandwidth_vnom0p8 = 34.133f, /*GB/s*/ + .fabric_and_dram_bandwidth_vmid0p72 = 29.866f, /*GB/s*/ + .fabric_and_dram_bandwidth_vmin0p65 = 12.8f, /*GB/s*/ + /* single channel BW + .fabric_and_dram_bandwidth_vmax0p9 = 19.2f, + .fabric_and_dram_bandwidth_vnom0p8 = 17.066f, + .fabric_and_dram_bandwidth_vmid0p72 = 14.933f, + .fabric_and_dram_bandwidth_vmin0p65 = 12.8f, + */ + + .number_of_channels = 2, + + .socclk = 208, /*MHz*/ + .downspreading = 0.5f, /*%*/ + .round_trip_ping_latency_cycles = 128, /*DCFCLK Cycles*/ + .urgent_out_of_order_return_per_channel = 256, /*bytes*/ + .vmm_page_size = 4096, /*bytes*/ + .return_bus_width = 64, /*bytes*/ + .max_request_size = 256, /*bytes*/ + + /* Depends on user class (client vs embedded, workstation, etc) */ + .percent_disp_bw_limit = 0.3f /*%*/ +}; + +const struct dcn_ip_params dcn10_ip_defaults = { + .rob_buffer_size_in_kbyte = 64, + .det_buffer_size_in_kbyte = 164, + .dpp_output_buffer_pixels = 2560, + .opp_output_buffer_lines = 1, + .pixel_chunk_size_in_kbyte = 8, + .pte_enable = dcn_bw_yes, + .pte_chunk_size = 2, /*kbytes*/ + .meta_chunk_size = 2, /*kbytes*/ + .writeback_chunk_size = 2, /*kbytes*/ + .odm_capability = dcn_bw_no, + .dsc_capability = dcn_bw_no, + .line_buffer_size = 589824, /*bit*/ + .max_line_buffer_lines = 12, + .is_line_buffer_bpp_fixed = dcn_bw_no, + .line_buffer_fixed_bpp = dcn_bw_na, + .writeback_luma_buffer_size = 12, /*kbytes*/ + .writeback_chroma_buffer_size = 8, /*kbytes*/ + .max_num_dpp = 4, + .max_num_writeback = 2, + .max_dchub_topscl_throughput = 4, /*pixels/dppclk*/ + .max_pscl_tolb_throughput = 2, /*pixels/dppclk*/ + .max_lb_tovscl_throughput = 4, /*pixels/dppclk*/ + .max_vscl_tohscl_throughput = 4, /*pixels/dppclk*/ + .max_hscl_ratio = 4, + .max_vscl_ratio = 4, + .max_hscl_taps = 8, + .max_vscl_taps = 8, + .pte_buffer_size_in_requests = 42, + .dispclk_ramping_margin = 1, /*%*/ + .under_scan_factor = 1.11f, + .max_inter_dcn_tile_repeaters = 8, + .can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one = dcn_bw_no, + .bug_forcing_luma_and_chroma_request_to_same_size_fixed = dcn_bw_no, + .dcfclk_cstate_latency = 10 /*TODO clone of something else? sr_enter_plus_exit_time?*/ +}; + +static enum dcn_bw_defs tl_sw_mode_to_bw_defs(enum swizzle_mode_values sw_mode) +{ + switch (sw_mode) { + case DC_SW_LINEAR: + return dcn_bw_sw_linear; + case DC_SW_4KB_S: + return dcn_bw_sw_4_kb_s; + case DC_SW_4KB_D: + return dcn_bw_sw_4_kb_d; + case DC_SW_64KB_S: + return dcn_bw_sw_64_kb_s; + case DC_SW_64KB_D: + return dcn_bw_sw_64_kb_d; + case DC_SW_VAR_S: + return dcn_bw_sw_var_s; + case DC_SW_VAR_D: + return dcn_bw_sw_var_d; + case DC_SW_64KB_S_T: + return dcn_bw_sw_64_kb_s_t; + case DC_SW_64KB_D_T: + return dcn_bw_sw_64_kb_d_t; + case DC_SW_4KB_S_X: + return dcn_bw_sw_4_kb_s_x; + case DC_SW_4KB_D_X: + return dcn_bw_sw_4_kb_d_x; + case DC_SW_64KB_S_X: + return dcn_bw_sw_64_kb_s_x; + case DC_SW_64KB_D_X: + return dcn_bw_sw_64_kb_d_x; + case DC_SW_VAR_S_X: + return dcn_bw_sw_var_s_x; + case DC_SW_VAR_D_X: + return dcn_bw_sw_var_d_x; + case DC_SW_256B_S: + case DC_SW_256_D: + case DC_SW_256_R: + case DC_SW_4KB_R: + case DC_SW_64KB_R: + case DC_SW_VAR_R: + case DC_SW_4KB_R_X: + case DC_SW_64KB_R_X: + case DC_SW_VAR_R_X: + default: + BREAK_TO_DEBUGGER(); /*not in formula*/ + return dcn_bw_sw_4_kb_s; + } +} + +static int tl_lb_bpp_to_int(enum lb_pixel_depth depth) +{ + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + return 18; + case LB_PIXEL_DEPTH_24BPP: + return 24; + case LB_PIXEL_DEPTH_30BPP: + return 30; + case LB_PIXEL_DEPTH_36BPP: + return 36; + default: + return 30; + } +} + +static enum dcn_bw_defs tl_pixel_format_to_bw_defs(enum surface_pixel_format format) +{ + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + return dcn_bw_rgb_sub_16; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + return dcn_bw_rgb_sub_32; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + return dcn_bw_rgb_sub_64; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + return dcn_bw_yuv420_sub_8; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + return dcn_bw_yuv420_sub_10; + default: + return dcn_bw_rgb_sub_32; + } +} + +static void pipe_ctx_to_e2e_pipe_params ( + const struct pipe_ctx *pipe, + struct _vcs_dpi_display_pipe_params_st *input) +{ + input->src.is_hsplit = false; + if (pipe->top_pipe != NULL && pipe->top_pipe->plane_state == pipe->plane_state) + input->src.is_hsplit = true; + else if (pipe->bottom_pipe != NULL && pipe->bottom_pipe->plane_state == pipe->plane_state) + input->src.is_hsplit = true; + + input->src.dcc = pipe->plane_state->dcc.enable; + input->src.dcc_rate = 1; + input->src.meta_pitch = pipe->plane_state->dcc.grph.meta_pitch; + input->src.source_scan = dm_horz; + input->src.sw_mode = pipe->plane_state->tiling_info.gfx9.swizzle; + + input->src.viewport_width = pipe->plane_res.scl_data.viewport.width; + input->src.viewport_height = pipe->plane_res.scl_data.viewport.height; + input->src.data_pitch = pipe->plane_res.scl_data.viewport.width; + input->src.data_pitch_c = pipe->plane_res.scl_data.viewport.width; + input->src.cur0_src_width = 128; /* TODO: Cursor calcs, not curently stored */ + input->src.cur0_bpp = 32; + + switch (pipe->plane_state->tiling_info.gfx9.swizzle) { + /* for 4/8/16 high tiles */ + case DC_SW_LINEAR: + input->src.is_display_sw = 1; + input->src.macro_tile_size = dm_4k_tile; + break; + case DC_SW_4KB_S: + case DC_SW_4KB_S_X: + input->src.is_display_sw = 0; + input->src.macro_tile_size = dm_4k_tile; + break; + case DC_SW_64KB_S: + case DC_SW_64KB_S_X: + case DC_SW_64KB_S_T: + input->src.is_display_sw = 0; + input->src.macro_tile_size = dm_64k_tile; + break; + case DC_SW_VAR_S: + case DC_SW_VAR_S_X: + input->src.is_display_sw = 0; + input->src.macro_tile_size = dm_256k_tile; + break; + + /* For 64bpp 2 high tiles */ + case DC_SW_4KB_D: + case DC_SW_4KB_D_X: + input->src.is_display_sw = 1; + input->src.macro_tile_size = dm_4k_tile; + break; + case DC_SW_64KB_D: + case DC_SW_64KB_D_X: + case DC_SW_64KB_D_T: + input->src.is_display_sw = 1; + input->src.macro_tile_size = dm_64k_tile; + break; + case DC_SW_VAR_D: + case DC_SW_VAR_D_X: + input->src.is_display_sw = 1; + input->src.macro_tile_size = dm_256k_tile; + break; + + /* Unsupported swizzle modes for dcn */ + case DC_SW_256B_S: + default: + ASSERT(0); /* Not supported */ + break; + } + + switch (pipe->plane_state->rotation) { + case ROTATION_ANGLE_0: + case ROTATION_ANGLE_180: + input->src.source_scan = dm_horz; + break; + case ROTATION_ANGLE_90: + case ROTATION_ANGLE_270: + input->src.source_scan = dm_vert; + break; + default: + ASSERT(0); /* Not supported */ + break; + } + + /* TODO: Fix pixel format mappings */ + switch (pipe->plane_state->format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + input->src.source_format = dm_420_8; + input->src.viewport_width_c = input->src.viewport_width / 2; + input->src.viewport_height_c = input->src.viewport_height / 2; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + input->src.source_format = dm_420_10; + input->src.viewport_width_c = input->src.viewport_width / 2; + input->src.viewport_height_c = input->src.viewport_height / 2; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + input->src.source_format = dm_444_64; + input->src.viewport_width_c = input->src.viewport_width; + input->src.viewport_height_c = input->src.viewport_height; + break; + default: + input->src.source_format = dm_444_32; + input->src.viewport_width_c = input->src.viewport_width; + input->src.viewport_height_c = input->src.viewport_height; + break; + } + + input->scale_taps.htaps = pipe->plane_res.scl_data.taps.h_taps; + input->scale_ratio_depth.hscl_ratio = pipe->plane_res.scl_data.ratios.horz.value/4294967296.0; + input->scale_ratio_depth.vscl_ratio = pipe->plane_res.scl_data.ratios.vert.value/4294967296.0; + input->scale_ratio_depth.vinit = pipe->plane_res.scl_data.inits.v.value/4294967296.0; + if (input->scale_ratio_depth.vinit < 1.0) + input->scale_ratio_depth.vinit = 1; + input->scale_taps.vtaps = pipe->plane_res.scl_data.taps.v_taps; + input->scale_taps.vtaps_c = pipe->plane_res.scl_data.taps.v_taps_c; + input->scale_taps.htaps_c = pipe->plane_res.scl_data.taps.h_taps_c; + input->scale_ratio_depth.hscl_ratio_c = pipe->plane_res.scl_data.ratios.horz_c.value/4294967296.0; + input->scale_ratio_depth.vscl_ratio_c = pipe->plane_res.scl_data.ratios.vert_c.value/4294967296.0; + input->scale_ratio_depth.vinit_c = pipe->plane_res.scl_data.inits.v_c.value/4294967296.0; + if (input->scale_ratio_depth.vinit_c < 1.0) + input->scale_ratio_depth.vinit_c = 1; + switch (pipe->plane_res.scl_data.lb_params.depth) { + case LB_PIXEL_DEPTH_30BPP: + input->scale_ratio_depth.lb_depth = 30; break; + case LB_PIXEL_DEPTH_36BPP: + input->scale_ratio_depth.lb_depth = 36; break; + default: + input->scale_ratio_depth.lb_depth = 24; break; + } + + + input->dest.vactive = pipe->stream->timing.v_addressable + pipe->stream->timing.v_border_top + + pipe->stream->timing.v_border_bottom; + + input->dest.recout_width = pipe->plane_res.scl_data.recout.width; + input->dest.recout_height = pipe->plane_res.scl_data.recout.height; + + input->dest.full_recout_width = pipe->plane_res.scl_data.recout.width; + input->dest.full_recout_height = pipe->plane_res.scl_data.recout.height; + + input->dest.htotal = pipe->stream->timing.h_total; + input->dest.hblank_start = input->dest.htotal - pipe->stream->timing.h_front_porch; + input->dest.hblank_end = input->dest.hblank_start + - pipe->stream->timing.h_addressable + - pipe->stream->timing.h_border_left + - pipe->stream->timing.h_border_right; + + input->dest.vtotal = pipe->stream->timing.v_total; + input->dest.vblank_start = input->dest.vtotal - pipe->stream->timing.v_front_porch; + input->dest.vblank_end = input->dest.vblank_start + - pipe->stream->timing.v_addressable + - pipe->stream->timing.v_border_bottom + - pipe->stream->timing.v_border_top; + input->dest.pixel_rate_mhz = pipe->stream->timing.pix_clk_khz/1000.0; + input->dest.vstartup_start = pipe->pipe_dlg_param.vstartup_start; + input->dest.vupdate_offset = pipe->pipe_dlg_param.vupdate_offset; + input->dest.vupdate_offset = pipe->pipe_dlg_param.vupdate_offset; + input->dest.vupdate_width = pipe->pipe_dlg_param.vupdate_width; + +} + +static void dcn_bw_calc_rq_dlg_ttu( + const struct dc *dc, + const struct dcn_bw_internal_vars *v, + struct pipe_ctx *pipe, + int in_idx) +{ + struct display_mode_lib *dml = (struct display_mode_lib *)(&dc->dml); + struct _vcs_dpi_display_dlg_regs_st *dlg_regs = &pipe->dlg_regs; + struct _vcs_dpi_display_ttu_regs_st *ttu_regs = &pipe->ttu_regs; + struct _vcs_dpi_display_rq_regs_st *rq_regs = &pipe->rq_regs; + struct _vcs_dpi_display_rq_params_st rq_param = {0}; + struct _vcs_dpi_display_dlg_sys_params_st dlg_sys_param = {0}; + struct _vcs_dpi_display_e2e_pipe_params_st input = { { { 0 } } }; + float total_active_bw = 0; + float total_prefetch_bw = 0; + int total_flip_bytes = 0; + int i; + + for (i = 0; i < number_of_planes; i++) { + total_active_bw += v->read_bandwidth[i]; + total_prefetch_bw += v->prefetch_bandwidth[i]; + total_flip_bytes += v->total_immediate_flip_bytes[i]; + } + dlg_sys_param.total_flip_bw = v->return_bw - dcn_bw_max2(total_active_bw, total_prefetch_bw); + if (dlg_sys_param.total_flip_bw < 0.0) + dlg_sys_param.total_flip_bw = 0; + + dlg_sys_param.t_mclk_wm_us = v->dram_clock_change_watermark; + dlg_sys_param.t_sr_wm_us = v->stutter_enter_plus_exit_watermark; + dlg_sys_param.t_urg_wm_us = v->urgent_watermark; + dlg_sys_param.t_extra_us = v->urgent_extra_latency; + dlg_sys_param.deepsleep_dcfclk_mhz = v->dcf_clk_deep_sleep; + dlg_sys_param.total_flip_bytes = total_flip_bytes; + + pipe_ctx_to_e2e_pipe_params(pipe, &input.pipe); + input.clks_cfg.dcfclk_mhz = v->dcfclk; + input.clks_cfg.dispclk_mhz = v->dispclk; + input.clks_cfg.dppclk_mhz = v->dppclk; + input.clks_cfg.refclk_mhz = dc->res_pool->ref_clock_inKhz/1000; + input.clks_cfg.socclk_mhz = v->socclk; + input.clks_cfg.voltage = v->voltage_level; +// dc->dml.logger = pool->base.logger; + input.dout.output_format = (v->output_format[in_idx] == dcn_bw_420) ? dm_420 : dm_444; + input.dout.output_type = (v->output[in_idx] == dcn_bw_hdmi) ? dm_hdmi : dm_dp; + //input[in_idx].dout.output_standard; + switch (v->output_deep_color[in_idx]) { + case dcn_bw_encoder_12bpc: + input.dout.output_bpc = dm_out_12; + break; + case dcn_bw_encoder_10bpc: + input.dout.output_bpc = dm_out_10; + break; + case dcn_bw_encoder_8bpc: + default: + input.dout.output_bpc = dm_out_8; + break; + } + + /*todo: soc->sr_enter_plus_exit_time??*/ + dlg_sys_param.t_srx_delay_us = dc->dcn_ip->dcfclk_cstate_latency / v->dcf_clk_deep_sleep; + + dml1_rq_dlg_get_rq_params(dml, &rq_param, input.pipe.src); + dml1_extract_rq_regs(dml, rq_regs, rq_param); + dml1_rq_dlg_get_dlg_params( + dml, + dlg_regs, + ttu_regs, + rq_param.dlg, + dlg_sys_param, + input, + true, + true, + v->pte_enable == dcn_bw_yes, + pipe->plane_state->flip_immediate); +} + +static void split_stream_across_pipes( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct pipe_ctx *primary_pipe, + struct pipe_ctx *secondary_pipe) +{ + int pipe_idx = secondary_pipe->pipe_idx; + + if (!primary_pipe->plane_state) + return; + + *secondary_pipe = *primary_pipe; + + secondary_pipe->pipe_idx = pipe_idx; + secondary_pipe->plane_res.mi = pool->mis[secondary_pipe->pipe_idx]; + secondary_pipe->plane_res.hubp = pool->hubps[secondary_pipe->pipe_idx]; + secondary_pipe->plane_res.ipp = pool->ipps[secondary_pipe->pipe_idx]; + secondary_pipe->plane_res.xfm = pool->transforms[secondary_pipe->pipe_idx]; + secondary_pipe->plane_res.dpp = pool->dpps[secondary_pipe->pipe_idx]; + if (primary_pipe->bottom_pipe) { + ASSERT(primary_pipe->bottom_pipe != secondary_pipe); + secondary_pipe->bottom_pipe = primary_pipe->bottom_pipe; + secondary_pipe->bottom_pipe->top_pipe = secondary_pipe; + } + primary_pipe->bottom_pipe = secondary_pipe; + secondary_pipe->top_pipe = primary_pipe; + + resource_build_scaling_params(primary_pipe); + resource_build_scaling_params(secondary_pipe); +} + +static void calc_wm_sets_and_perf_params( + struct dc_state *context, + struct dcn_bw_internal_vars *v) +{ + /* Calculate set A last to keep internal var state consistent for required config */ + if (v->voltage_level < 2) { + v->fabric_and_dram_bandwidth_per_state[1] = v->fabric_and_dram_bandwidth_vnom0p8; + v->fabric_and_dram_bandwidth_per_state[0] = v->fabric_and_dram_bandwidth_vnom0p8; + v->fabric_and_dram_bandwidth = v->fabric_and_dram_bandwidth_vnom0p8; + dispclkdppclkdcfclk_deep_sleep_prefetch_parameters_watermarks_and_performance_calculation(v); + + context->bw.dcn.watermarks.b.cstate_pstate.cstate_exit_ns = + v->stutter_exit_watermark * 1000; + context->bw.dcn.watermarks.b.cstate_pstate.cstate_enter_plus_exit_ns = + v->stutter_enter_plus_exit_watermark * 1000; + context->bw.dcn.watermarks.b.cstate_pstate.pstate_change_ns = + v->dram_clock_change_watermark * 1000; + context->bw.dcn.watermarks.b.pte_meta_urgent_ns = v->ptemeta_urgent_watermark * 1000; + context->bw.dcn.watermarks.b.urgent_ns = v->urgent_watermark * 1000; + + v->dcfclk_per_state[1] = v->dcfclkv_nom0p8; + v->dcfclk_per_state[0] = v->dcfclkv_nom0p8; + v->dcfclk = v->dcfclkv_nom0p8; + dispclkdppclkdcfclk_deep_sleep_prefetch_parameters_watermarks_and_performance_calculation(v); + + context->bw.dcn.watermarks.c.cstate_pstate.cstate_exit_ns = + v->stutter_exit_watermark * 1000; + context->bw.dcn.watermarks.c.cstate_pstate.cstate_enter_plus_exit_ns = + v->stutter_enter_plus_exit_watermark * 1000; + context->bw.dcn.watermarks.c.cstate_pstate.pstate_change_ns = + v->dram_clock_change_watermark * 1000; + context->bw.dcn.watermarks.c.pte_meta_urgent_ns = v->ptemeta_urgent_watermark * 1000; + context->bw.dcn.watermarks.c.urgent_ns = v->urgent_watermark * 1000; + } + + if (v->voltage_level < 3) { + v->fabric_and_dram_bandwidth_per_state[2] = v->fabric_and_dram_bandwidth_vmax0p9; + v->fabric_and_dram_bandwidth_per_state[1] = v->fabric_and_dram_bandwidth_vmax0p9; + v->fabric_and_dram_bandwidth_per_state[0] = v->fabric_and_dram_bandwidth_vmax0p9; + v->fabric_and_dram_bandwidth = v->fabric_and_dram_bandwidth_vmax0p9; + v->dcfclk_per_state[2] = v->dcfclkv_max0p9; + v->dcfclk_per_state[1] = v->dcfclkv_max0p9; + v->dcfclk_per_state[0] = v->dcfclkv_max0p9; + v->dcfclk = v->dcfclkv_max0p9; + dispclkdppclkdcfclk_deep_sleep_prefetch_parameters_watermarks_and_performance_calculation(v); + + context->bw.dcn.watermarks.d.cstate_pstate.cstate_exit_ns = + v->stutter_exit_watermark * 1000; + context->bw.dcn.watermarks.d.cstate_pstate.cstate_enter_plus_exit_ns = + v->stutter_enter_plus_exit_watermark * 1000; + context->bw.dcn.watermarks.d.cstate_pstate.pstate_change_ns = + v->dram_clock_change_watermark * 1000; + context->bw.dcn.watermarks.d.pte_meta_urgent_ns = v->ptemeta_urgent_watermark * 1000; + context->bw.dcn.watermarks.d.urgent_ns = v->urgent_watermark * 1000; + } + + v->fabric_and_dram_bandwidth_per_state[2] = v->fabric_and_dram_bandwidth_vnom0p8; + v->fabric_and_dram_bandwidth_per_state[1] = v->fabric_and_dram_bandwidth_vmid0p72; + v->fabric_and_dram_bandwidth_per_state[0] = v->fabric_and_dram_bandwidth_vmin0p65; + v->fabric_and_dram_bandwidth = v->fabric_and_dram_bandwidth_per_state[v->voltage_level]; + v->dcfclk_per_state[2] = v->dcfclkv_nom0p8; + v->dcfclk_per_state[1] = v->dcfclkv_mid0p72; + v->dcfclk_per_state[0] = v->dcfclkv_min0p65; + v->dcfclk = v->dcfclk_per_state[v->voltage_level]; + dispclkdppclkdcfclk_deep_sleep_prefetch_parameters_watermarks_and_performance_calculation(v); + + context->bw.dcn.watermarks.a.cstate_pstate.cstate_exit_ns = + v->stutter_exit_watermark * 1000; + context->bw.dcn.watermarks.a.cstate_pstate.cstate_enter_plus_exit_ns = + v->stutter_enter_plus_exit_watermark * 1000; + context->bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = + v->dram_clock_change_watermark * 1000; + context->bw.dcn.watermarks.a.pte_meta_urgent_ns = v->ptemeta_urgent_watermark * 1000; + context->bw.dcn.watermarks.a.urgent_ns = v->urgent_watermark * 1000; + if (v->voltage_level >= 2) { + context->bw.dcn.watermarks.b = context->bw.dcn.watermarks.a; + context->bw.dcn.watermarks.c = context->bw.dcn.watermarks.a; + } + if (v->voltage_level >= 3) + context->bw.dcn.watermarks.d = context->bw.dcn.watermarks.a; +} + +static bool dcn_bw_apply_registry_override(struct dc *dc) +{ + bool updated = false; + + kernel_fpu_begin(); + if ((int)(dc->dcn_soc->sr_exit_time * 1000) != dc->debug.sr_exit_time_ns + && dc->debug.sr_exit_time_ns) { + updated = true; + dc->dcn_soc->sr_exit_time = dc->debug.sr_exit_time_ns / 1000.0; + } + + if ((int)(dc->dcn_soc->sr_enter_plus_exit_time * 1000) + != dc->debug.sr_enter_plus_exit_time_ns + && dc->debug.sr_enter_plus_exit_time_ns) { + updated = true; + dc->dcn_soc->sr_enter_plus_exit_time = + dc->debug.sr_enter_plus_exit_time_ns / 1000.0; + } + + if ((int)(dc->dcn_soc->urgent_latency * 1000) != dc->debug.urgent_latency_ns + && dc->debug.urgent_latency_ns) { + updated = true; + dc->dcn_soc->urgent_latency = dc->debug.urgent_latency_ns / 1000.0; + } + + if ((int)(dc->dcn_soc->percent_of_ideal_drambw_received_after_urg_latency * 1000) + != dc->debug.percent_of_ideal_drambw + && dc->debug.percent_of_ideal_drambw) { + updated = true; + dc->dcn_soc->percent_of_ideal_drambw_received_after_urg_latency = + dc->debug.percent_of_ideal_drambw; + } + + if ((int)(dc->dcn_soc->dram_clock_change_latency * 1000) + != dc->debug.dram_clock_change_latency_ns + && dc->debug.dram_clock_change_latency_ns) { + updated = true; + dc->dcn_soc->dram_clock_change_latency = + dc->debug.dram_clock_change_latency_ns / 1000.0; + } + kernel_fpu_end(); + + return updated; +} + +void hack_disable_optional_pipe_split(struct dcn_bw_internal_vars *v) +{ + /* + * disable optional pipe split by lower dispclk bounding box + * at DPM0 + */ + v->max_dispclk[0] = v->max_dppclk_vmin0p65; +} + +void hack_force_pipe_split(struct dcn_bw_internal_vars *v, + unsigned int pixel_rate_khz) +{ + float pixel_rate_mhz = pixel_rate_khz / 1000; + + /* + * force enabling pipe split by lower dpp clock for DPM0 to just + * below the specify pixel_rate, so bw calc would split pipe. + */ + if (pixel_rate_mhz < v->max_dppclk[0]) + v->max_dppclk[0] = pixel_rate_mhz; +} + +void hack_bounding_box(struct dcn_bw_internal_vars *v, + struct dc_debug *dbg, + struct dc_state *context) +{ + if (dbg->pipe_split_policy == MPC_SPLIT_AVOID) { + hack_disable_optional_pipe_split(v); + } + + if (dbg->pipe_split_policy == MPC_SPLIT_AVOID_MULT_DISP && + context->stream_count >= 2) { + hack_disable_optional_pipe_split(v); + } + + if (context->stream_count == 1 && + dbg->force_single_disp_pipe_split) { + struct dc_stream_state *stream0 = context->streams[0]; + + hack_force_pipe_split(v, stream0->timing.pix_clk_khz); + } +} + +bool dcn_validate_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + const struct resource_pool *pool = dc->res_pool; + struct dcn_bw_internal_vars *v = &context->dcn_bw_vars; + int i, input_idx; + int vesa_sync_start, asic_blank_end, asic_blank_start; + bool bw_limit_pass; + float bw_limit; + + PERFORMANCE_TRACE_START(); + if (dcn_bw_apply_registry_override(dc)) + dcn_bw_sync_calcs_and_dml(dc); + + memset(v, 0, sizeof(*v)); + kernel_fpu_begin(); + v->sr_exit_time = dc->dcn_soc->sr_exit_time; + v->sr_enter_plus_exit_time = dc->dcn_soc->sr_enter_plus_exit_time; + v->urgent_latency = dc->dcn_soc->urgent_latency; + v->write_back_latency = dc->dcn_soc->write_back_latency; + v->percent_of_ideal_drambw_received_after_urg_latency = + dc->dcn_soc->percent_of_ideal_drambw_received_after_urg_latency; + + v->dcfclkv_min0p65 = dc->dcn_soc->dcfclkv_min0p65; + v->dcfclkv_mid0p72 = dc->dcn_soc->dcfclkv_mid0p72; + v->dcfclkv_nom0p8 = dc->dcn_soc->dcfclkv_nom0p8; + v->dcfclkv_max0p9 = dc->dcn_soc->dcfclkv_max0p9; + + v->max_dispclk_vmin0p65 = dc->dcn_soc->max_dispclk_vmin0p65; + v->max_dispclk_vmid0p72 = dc->dcn_soc->max_dispclk_vmid0p72; + v->max_dispclk_vnom0p8 = dc->dcn_soc->max_dispclk_vnom0p8; + v->max_dispclk_vmax0p9 = dc->dcn_soc->max_dispclk_vmax0p9; + + v->max_dppclk_vmin0p65 = dc->dcn_soc->max_dppclk_vmin0p65; + v->max_dppclk_vmid0p72 = dc->dcn_soc->max_dppclk_vmid0p72; + v->max_dppclk_vnom0p8 = dc->dcn_soc->max_dppclk_vnom0p8; + v->max_dppclk_vmax0p9 = dc->dcn_soc->max_dppclk_vmax0p9; + + v->socclk = dc->dcn_soc->socclk; + + v->fabric_and_dram_bandwidth_vmin0p65 = dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65; + v->fabric_and_dram_bandwidth_vmid0p72 = dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72; + v->fabric_and_dram_bandwidth_vnom0p8 = dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8; + v->fabric_and_dram_bandwidth_vmax0p9 = dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9; + + v->phyclkv_min0p65 = dc->dcn_soc->phyclkv_min0p65; + v->phyclkv_mid0p72 = dc->dcn_soc->phyclkv_mid0p72; + v->phyclkv_nom0p8 = dc->dcn_soc->phyclkv_nom0p8; + v->phyclkv_max0p9 = dc->dcn_soc->phyclkv_max0p9; + + v->downspreading = dc->dcn_soc->downspreading; + v->round_trip_ping_latency_cycles = dc->dcn_soc->round_trip_ping_latency_cycles; + v->urgent_out_of_order_return_per_channel = dc->dcn_soc->urgent_out_of_order_return_per_channel; + v->number_of_channels = dc->dcn_soc->number_of_channels; + v->vmm_page_size = dc->dcn_soc->vmm_page_size; + v->dram_clock_change_latency = dc->dcn_soc->dram_clock_change_latency; + v->return_bus_width = dc->dcn_soc->return_bus_width; + + v->rob_buffer_size_in_kbyte = dc->dcn_ip->rob_buffer_size_in_kbyte; + v->det_buffer_size_in_kbyte = dc->dcn_ip->det_buffer_size_in_kbyte; + v->dpp_output_buffer_pixels = dc->dcn_ip->dpp_output_buffer_pixels; + v->opp_output_buffer_lines = dc->dcn_ip->opp_output_buffer_lines; + v->pixel_chunk_size_in_kbyte = dc->dcn_ip->pixel_chunk_size_in_kbyte; + v->pte_enable = dc->dcn_ip->pte_enable; + v->pte_chunk_size = dc->dcn_ip->pte_chunk_size; + v->meta_chunk_size = dc->dcn_ip->meta_chunk_size; + v->writeback_chunk_size = dc->dcn_ip->writeback_chunk_size; + v->odm_capability = dc->dcn_ip->odm_capability; + v->dsc_capability = dc->dcn_ip->dsc_capability; + v->line_buffer_size = dc->dcn_ip->line_buffer_size; + v->is_line_buffer_bpp_fixed = dc->dcn_ip->is_line_buffer_bpp_fixed; + v->line_buffer_fixed_bpp = dc->dcn_ip->line_buffer_fixed_bpp; + v->max_line_buffer_lines = dc->dcn_ip->max_line_buffer_lines; + v->writeback_luma_buffer_size = dc->dcn_ip->writeback_luma_buffer_size; + v->writeback_chroma_buffer_size = dc->dcn_ip->writeback_chroma_buffer_size; + v->max_num_dpp = dc->dcn_ip->max_num_dpp; + v->max_num_writeback = dc->dcn_ip->max_num_writeback; + v->max_dchub_topscl_throughput = dc->dcn_ip->max_dchub_topscl_throughput; + v->max_pscl_tolb_throughput = dc->dcn_ip->max_pscl_tolb_throughput; + v->max_lb_tovscl_throughput = dc->dcn_ip->max_lb_tovscl_throughput; + v->max_vscl_tohscl_throughput = dc->dcn_ip->max_vscl_tohscl_throughput; + v->max_hscl_ratio = dc->dcn_ip->max_hscl_ratio; + v->max_vscl_ratio = dc->dcn_ip->max_vscl_ratio; + v->max_hscl_taps = dc->dcn_ip->max_hscl_taps; + v->max_vscl_taps = dc->dcn_ip->max_vscl_taps; + v->under_scan_factor = dc->dcn_ip->under_scan_factor; + v->pte_buffer_size_in_requests = dc->dcn_ip->pte_buffer_size_in_requests; + v->dispclk_ramping_margin = dc->dcn_ip->dispclk_ramping_margin; + v->max_inter_dcn_tile_repeaters = dc->dcn_ip->max_inter_dcn_tile_repeaters; + v->can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one = + dc->dcn_ip->can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one; + v->bug_forcing_luma_and_chroma_request_to_same_size_fixed = + dc->dcn_ip->bug_forcing_luma_and_chroma_request_to_same_size_fixed; + + v->voltage[5] = dcn_bw_no_support; + v->voltage[4] = dcn_bw_v_max0p9; + v->voltage[3] = dcn_bw_v_max0p9; + v->voltage[2] = dcn_bw_v_nom0p8; + v->voltage[1] = dcn_bw_v_mid0p72; + v->voltage[0] = dcn_bw_v_min0p65; + v->fabric_and_dram_bandwidth_per_state[5] = v->fabric_and_dram_bandwidth_vmax0p9; + v->fabric_and_dram_bandwidth_per_state[4] = v->fabric_and_dram_bandwidth_vmax0p9; + v->fabric_and_dram_bandwidth_per_state[3] = v->fabric_and_dram_bandwidth_vmax0p9; + v->fabric_and_dram_bandwidth_per_state[2] = v->fabric_and_dram_bandwidth_vnom0p8; + v->fabric_and_dram_bandwidth_per_state[1] = v->fabric_and_dram_bandwidth_vmid0p72; + v->fabric_and_dram_bandwidth_per_state[0] = v->fabric_and_dram_bandwidth_vmin0p65; + v->dcfclk_per_state[5] = v->dcfclkv_max0p9; + v->dcfclk_per_state[4] = v->dcfclkv_max0p9; + v->dcfclk_per_state[3] = v->dcfclkv_max0p9; + v->dcfclk_per_state[2] = v->dcfclkv_nom0p8; + v->dcfclk_per_state[1] = v->dcfclkv_mid0p72; + v->dcfclk_per_state[0] = v->dcfclkv_min0p65; + v->max_dispclk[5] = v->max_dispclk_vmax0p9; + v->max_dispclk[4] = v->max_dispclk_vmax0p9; + v->max_dispclk[3] = v->max_dispclk_vmax0p9; + v->max_dispclk[2] = v->max_dispclk_vnom0p8; + v->max_dispclk[1] = v->max_dispclk_vmid0p72; + v->max_dispclk[0] = v->max_dispclk_vmin0p65; + v->max_dppclk[5] = v->max_dppclk_vmax0p9; + v->max_dppclk[4] = v->max_dppclk_vmax0p9; + v->max_dppclk[3] = v->max_dppclk_vmax0p9; + v->max_dppclk[2] = v->max_dppclk_vnom0p8; + v->max_dppclk[1] = v->max_dppclk_vmid0p72; + v->max_dppclk[0] = v->max_dppclk_vmin0p65; + v->phyclk_per_state[5] = v->phyclkv_max0p9; + v->phyclk_per_state[4] = v->phyclkv_max0p9; + v->phyclk_per_state[3] = v->phyclkv_max0p9; + v->phyclk_per_state[2] = v->phyclkv_nom0p8; + v->phyclk_per_state[1] = v->phyclkv_mid0p72; + v->phyclk_per_state[0] = v->phyclkv_min0p65; + + hack_bounding_box(v, &dc->debug, context); + + if (v->voltage_override == dcn_bw_v_max0p9) { + v->voltage_override_level = number_of_states - 1; + } else if (v->voltage_override == dcn_bw_v_nom0p8) { + v->voltage_override_level = number_of_states - 2; + } else if (v->voltage_override == dcn_bw_v_mid0p72) { + v->voltage_override_level = number_of_states - 3; + } else { + v->voltage_override_level = 0; + } + v->synchronized_vblank = dcn_bw_no; + v->ta_pscalculation = dcn_bw_override; + v->allow_different_hratio_vratio = dcn_bw_yes; + + + for (i = 0, input_idx = 0; i < pool->pipe_count; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + + if (!pipe->stream) + continue; + /* skip all but first of split pipes */ + if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) + continue; + + v->underscan_output[input_idx] = false; /* taken care of in recout already*/ + v->interlace_output[input_idx] = false; + + v->htotal[input_idx] = pipe->stream->timing.h_total; + v->vtotal[input_idx] = pipe->stream->timing.v_total; + v->vactive[input_idx] = pipe->stream->timing.v_addressable + + pipe->stream->timing.v_border_top + pipe->stream->timing.v_border_bottom; + v->v_sync_plus_back_porch[input_idx] = pipe->stream->timing.v_total + - v->vactive[input_idx] + - pipe->stream->timing.v_front_porch; + v->pixel_clock[input_idx] = pipe->stream->timing.pix_clk_khz / 1000.0f; + + if (!pipe->plane_state) { + v->dcc_enable[input_idx] = dcn_bw_yes; + v->source_pixel_format[input_idx] = dcn_bw_rgb_sub_32; + v->source_surface_mode[input_idx] = dcn_bw_sw_4_kb_s; + v->lb_bit_per_pixel[input_idx] = 30; + v->viewport_width[input_idx] = pipe->stream->timing.h_addressable; + v->viewport_height[input_idx] = pipe->stream->timing.v_addressable; + v->scaler_rec_out_width[input_idx] = pipe->stream->timing.h_addressable; + v->scaler_recout_height[input_idx] = pipe->stream->timing.v_addressable; + v->override_hta_ps[input_idx] = 1; + v->override_vta_ps[input_idx] = 1; + v->override_hta_pschroma[input_idx] = 1; + v->override_vta_pschroma[input_idx] = 1; + v->source_scan[input_idx] = dcn_bw_hor; + + } else { + v->viewport_height[input_idx] = pipe->plane_res.scl_data.viewport.height; + v->viewport_width[input_idx] = pipe->plane_res.scl_data.viewport.width; + v->scaler_rec_out_width[input_idx] = pipe->plane_res.scl_data.recout.width; + v->scaler_recout_height[input_idx] = pipe->plane_res.scl_data.recout.height; + if (pipe->bottom_pipe && pipe->bottom_pipe->plane_state == pipe->plane_state) { + if (pipe->plane_state->rotation % 2 == 0) { + int viewport_end = pipe->plane_res.scl_data.viewport.width + + pipe->plane_res.scl_data.viewport.x; + int viewport_b_end = pipe->bottom_pipe->plane_res.scl_data.viewport.width + + pipe->bottom_pipe->plane_res.scl_data.viewport.x; + + if (viewport_end > viewport_b_end) + v->viewport_width[input_idx] = viewport_end + - pipe->bottom_pipe->plane_res.scl_data.viewport.x; + else + v->viewport_width[input_idx] = viewport_b_end + - pipe->plane_res.scl_data.viewport.x; + } else { + int viewport_end = pipe->plane_res.scl_data.viewport.height + + pipe->plane_res.scl_data.viewport.y; + int viewport_b_end = pipe->bottom_pipe->plane_res.scl_data.viewport.height + + pipe->bottom_pipe->plane_res.scl_data.viewport.y; + + if (viewport_end > viewport_b_end) + v->viewport_height[input_idx] = viewport_end + - pipe->bottom_pipe->plane_res.scl_data.viewport.y; + else + v->viewport_height[input_idx] = viewport_b_end + - pipe->plane_res.scl_data.viewport.y; + } + v->scaler_rec_out_width[input_idx] = pipe->plane_res.scl_data.recout.width + + pipe->bottom_pipe->plane_res.scl_data.recout.width; + } + + v->dcc_enable[input_idx] = pipe->plane_state->dcc.enable ? dcn_bw_yes : dcn_bw_no; + v->source_pixel_format[input_idx] = tl_pixel_format_to_bw_defs( + pipe->plane_state->format); + v->source_surface_mode[input_idx] = tl_sw_mode_to_bw_defs( + pipe->plane_state->tiling_info.gfx9.swizzle); + v->lb_bit_per_pixel[input_idx] = tl_lb_bpp_to_int(pipe->plane_res.scl_data.lb_params.depth); + v->override_hta_ps[input_idx] = pipe->plane_res.scl_data.taps.h_taps; + v->override_vta_ps[input_idx] = pipe->plane_res.scl_data.taps.v_taps; + v->override_hta_pschroma[input_idx] = pipe->plane_res.scl_data.taps.h_taps_c; + v->override_vta_pschroma[input_idx] = pipe->plane_res.scl_data.taps.v_taps_c; + v->source_scan[input_idx] = (pipe->plane_state->rotation % 2) ? dcn_bw_vert : dcn_bw_hor; + } + if (v->is_line_buffer_bpp_fixed == dcn_bw_yes) + v->lb_bit_per_pixel[input_idx] = v->line_buffer_fixed_bpp; + v->dcc_rate[input_idx] = 1; /*TODO: Worst case? does this change?*/ + v->output_format[input_idx] = pipe->stream->timing.pixel_encoding == + PIXEL_ENCODING_YCBCR420 ? dcn_bw_420 : dcn_bw_444; + v->output[input_idx] = pipe->stream->sink->sink_signal == + SIGNAL_TYPE_HDMI_TYPE_A ? dcn_bw_hdmi : dcn_bw_dp; + v->output_deep_color[input_idx] = dcn_bw_encoder_8bpc; + if (v->output[input_idx] == dcn_bw_hdmi) { + switch (pipe->stream->timing.display_color_depth) { + case COLOR_DEPTH_101010: + v->output_deep_color[input_idx] = dcn_bw_encoder_10bpc; + break; + case COLOR_DEPTH_121212: + v->output_deep_color[input_idx] = dcn_bw_encoder_12bpc; + break; + case COLOR_DEPTH_161616: + v->output_deep_color[input_idx] = dcn_bw_encoder_16bpc; + break; + default: + break; + } + } + + input_idx++; + } + v->number_of_active_planes = input_idx; + + scaler_settings_calculation(v); + mode_support_and_system_configuration(v); + + if (v->voltage_level == 0 && + (dc->debug.sr_exit_time_dpm0_ns + || dc->debug.sr_enter_plus_exit_time_dpm0_ns)) { + + if (dc->debug.sr_enter_plus_exit_time_dpm0_ns) + v->sr_enter_plus_exit_time = + dc->debug.sr_enter_plus_exit_time_dpm0_ns / 1000.0f; + if (dc->debug.sr_exit_time_dpm0_ns) + v->sr_exit_time = dc->debug.sr_exit_time_dpm0_ns / 1000.0f; + dc->dml.soc.sr_enter_plus_exit_time_us = v->sr_enter_plus_exit_time; + dc->dml.soc.sr_exit_time_us = v->sr_exit_time; + mode_support_and_system_configuration(v); + } + + if (v->voltage_level != 5) { + float bw_consumed = v->total_bandwidth_consumed_gbyte_per_second; + if (bw_consumed < v->fabric_and_dram_bandwidth_vmin0p65) + bw_consumed = v->fabric_and_dram_bandwidth_vmin0p65; + else if (bw_consumed < v->fabric_and_dram_bandwidth_vmid0p72) + bw_consumed = v->fabric_and_dram_bandwidth_vmid0p72; + else if (bw_consumed < v->fabric_and_dram_bandwidth_vnom0p8) + bw_consumed = v->fabric_and_dram_bandwidth_vnom0p8; + else + bw_consumed = v->fabric_and_dram_bandwidth_vmax0p9; + + if (bw_consumed < v->fabric_and_dram_bandwidth) + if (dc->debug.voltage_align_fclk) + bw_consumed = v->fabric_and_dram_bandwidth; + + display_pipe_configuration(v); + calc_wm_sets_and_perf_params(context, v); + context->bw.dcn.calc_clk.fclk_khz = (int)(bw_consumed * 1000000 / + (ddr4_dram_factor_single_Channel * v->number_of_channels)); + if (bw_consumed == v->fabric_and_dram_bandwidth_vmin0p65) { + context->bw.dcn.calc_clk.fclk_khz = (int)(bw_consumed * 1000000 / 32); + } + + context->bw.dcn.calc_clk.dram_ccm_us = (int)(v->dram_clock_change_margin); + context->bw.dcn.calc_clk.min_active_dram_ccm_us = (int)(v->min_active_dram_clock_change_margin); + context->bw.dcn.calc_clk.dcfclk_deep_sleep_khz = (int)(v->dcf_clk_deep_sleep * 1000); + context->bw.dcn.calc_clk.dcfclk_khz = (int)(v->dcfclk * 1000); + + context->bw.dcn.calc_clk.dispclk_khz = (int)(v->dispclk * 1000); + if (dc->debug.max_disp_clk == true) + context->bw.dcn.calc_clk.dispclk_khz = (int)(dc->dcn_soc->max_dispclk_vmax0p9 * 1000); + + if (context->bw.dcn.calc_clk.dispclk_khz < + dc->debug.min_disp_clk_khz) { + context->bw.dcn.calc_clk.dispclk_khz = + dc->debug.min_disp_clk_khz; + } + + context->bw.dcn.calc_clk.dppclk_div = (int)(v->dispclk_dppclk_ratio) == 2; + + for (i = 0, input_idx = 0; i < pool->pipe_count; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + + /* skip inactive pipe */ + if (!pipe->stream) + continue; + /* skip all but first of split pipes */ + if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) + continue; + + pipe->pipe_dlg_param.vupdate_width = v->v_update_width[input_idx]; + pipe->pipe_dlg_param.vupdate_offset = v->v_update_offset[input_idx]; + pipe->pipe_dlg_param.vready_offset = v->v_ready_offset[input_idx]; + pipe->pipe_dlg_param.vstartup_start = v->v_startup[input_idx]; + + pipe->pipe_dlg_param.htotal = pipe->stream->timing.h_total; + pipe->pipe_dlg_param.vtotal = pipe->stream->timing.v_total; + vesa_sync_start = pipe->stream->timing.v_addressable + + pipe->stream->timing.v_border_bottom + + pipe->stream->timing.v_front_porch; + + asic_blank_end = (pipe->stream->timing.v_total - + vesa_sync_start - + pipe->stream->timing.v_border_top) + * (pipe->stream->timing.flags.INTERLACE ? 1 : 0); + + asic_blank_start = asic_blank_end + + (pipe->stream->timing.v_border_top + + pipe->stream->timing.v_addressable + + pipe->stream->timing.v_border_bottom) + * (pipe->stream->timing.flags.INTERLACE ? 1 : 0); + + pipe->pipe_dlg_param.vblank_start = asic_blank_start; + pipe->pipe_dlg_param.vblank_end = asic_blank_end; + + if (pipe->plane_state) { + struct pipe_ctx *hsplit_pipe = pipe->bottom_pipe; + + if (v->dpp_per_plane[input_idx] == 2 || + ((pipe->stream->view_format == + VIEW_3D_FORMAT_SIDE_BY_SIDE || + pipe->stream->view_format == + VIEW_3D_FORMAT_TOP_AND_BOTTOM) && + (pipe->stream->timing.timing_3d_format == + TIMING_3D_FORMAT_TOP_AND_BOTTOM || + pipe->stream->timing.timing_3d_format == + TIMING_3D_FORMAT_SIDE_BY_SIDE))) { + if (hsplit_pipe && hsplit_pipe->plane_state == pipe->plane_state) { + /* update previously split pipe */ + hsplit_pipe->pipe_dlg_param.vupdate_width = v->v_update_width[input_idx]; + hsplit_pipe->pipe_dlg_param.vupdate_offset = v->v_update_offset[input_idx]; + hsplit_pipe->pipe_dlg_param.vready_offset = v->v_ready_offset[input_idx]; + hsplit_pipe->pipe_dlg_param.vstartup_start = v->v_startup[input_idx]; + + hsplit_pipe->pipe_dlg_param.htotal = pipe->stream->timing.h_total; + hsplit_pipe->pipe_dlg_param.vtotal = pipe->stream->timing.v_total; + hsplit_pipe->pipe_dlg_param.vblank_start = pipe->pipe_dlg_param.vblank_start; + hsplit_pipe->pipe_dlg_param.vblank_end = pipe->pipe_dlg_param.vblank_end; + } else { + /* pipe not split previously needs split */ + hsplit_pipe = find_idle_secondary_pipe(&context->res_ctx, pool); + ASSERT(hsplit_pipe); + split_stream_across_pipes( + &context->res_ctx, pool, + pipe, hsplit_pipe); + } + + dcn_bw_calc_rq_dlg_ttu(dc, v, hsplit_pipe, input_idx); + } else if (hsplit_pipe && hsplit_pipe->plane_state == pipe->plane_state) { + /* merge previously split pipe */ + pipe->bottom_pipe = hsplit_pipe->bottom_pipe; + if (hsplit_pipe->bottom_pipe) + hsplit_pipe->bottom_pipe->top_pipe = pipe; + hsplit_pipe->plane_state = NULL; + hsplit_pipe->stream = NULL; + hsplit_pipe->top_pipe = NULL; + hsplit_pipe->bottom_pipe = NULL; + resource_build_scaling_params(pipe); + } + /* for now important to do this after pipe split for building e2e params */ + dcn_bw_calc_rq_dlg_ttu(dc, v, pipe, input_idx); + } + + input_idx++; + } + } + + if (v->voltage_level == 0) { + + dc->dml.soc.sr_enter_plus_exit_time_us = + dc->dcn_soc->sr_enter_plus_exit_time; + dc->dml.soc.sr_exit_time_us = dc->dcn_soc->sr_exit_time; + } + + /* + * BW limit is set to prevent display from impacting other system functions + */ + + bw_limit = dc->dcn_soc->percent_disp_bw_limit * v->fabric_and_dram_bandwidth_vmax0p9; + bw_limit_pass = (v->total_data_read_bandwidth / 1000.0) < bw_limit; + + kernel_fpu_end(); + + PERFORMANCE_TRACE_END(); + + if (bw_limit_pass && v->voltage_level != 5) + return true; + else + return false; +} + +static unsigned int dcn_find_normalized_clock_vdd_Level( + const struct dc *dc, + enum dm_pp_clock_type clocks_type, + int clocks_in_khz) +{ + int vdd_level = dcn_bw_v_min0p65; + + if (clocks_in_khz == 0)/*todo some clock not in the considerations*/ + return vdd_level; + + switch (clocks_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + if (clocks_in_khz > dc->dcn_soc->max_dispclk_vmax0p9*1000) { + vdd_level = dcn_bw_v_max0p91; + BREAK_TO_DEBUGGER(); + } else if (clocks_in_khz > dc->dcn_soc->max_dispclk_vnom0p8*1000) { + vdd_level = dcn_bw_v_max0p9; + } else if (clocks_in_khz > dc->dcn_soc->max_dispclk_vmid0p72*1000) { + vdd_level = dcn_bw_v_nom0p8; + } else if (clocks_in_khz > dc->dcn_soc->max_dispclk_vmin0p65*1000) { + vdd_level = dcn_bw_v_mid0p72; + } else + vdd_level = dcn_bw_v_min0p65; + break; + case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: + if (clocks_in_khz > dc->dcn_soc->phyclkv_max0p9*1000) { + vdd_level = dcn_bw_v_max0p91; + BREAK_TO_DEBUGGER(); + } else if (clocks_in_khz > dc->dcn_soc->phyclkv_nom0p8*1000) { + vdd_level = dcn_bw_v_max0p9; + } else if (clocks_in_khz > dc->dcn_soc->phyclkv_mid0p72*1000) { + vdd_level = dcn_bw_v_nom0p8; + } else if (clocks_in_khz > dc->dcn_soc->phyclkv_min0p65*1000) { + vdd_level = dcn_bw_v_mid0p72; + } else + vdd_level = dcn_bw_v_min0p65; + break; + + case DM_PP_CLOCK_TYPE_DPPCLK: + if (clocks_in_khz > dc->dcn_soc->max_dppclk_vmax0p9*1000) { + vdd_level = dcn_bw_v_max0p91; + BREAK_TO_DEBUGGER(); + } else if (clocks_in_khz > dc->dcn_soc->max_dppclk_vnom0p8*1000) { + vdd_level = dcn_bw_v_max0p9; + } else if (clocks_in_khz > dc->dcn_soc->max_dppclk_vmid0p72*1000) { + vdd_level = dcn_bw_v_nom0p8; + } else if (clocks_in_khz > dc->dcn_soc->max_dppclk_vmin0p65*1000) { + vdd_level = dcn_bw_v_mid0p72; + } else + vdd_level = dcn_bw_v_min0p65; + break; + + case DM_PP_CLOCK_TYPE_MEMORY_CLK: + { + unsigned factor = (ddr4_dram_factor_single_Channel * dc->dcn_soc->number_of_channels); + + if (clocks_in_khz > dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9*1000000/factor) { + vdd_level = dcn_bw_v_max0p91; + BREAK_TO_DEBUGGER(); + } else if (clocks_in_khz > dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8*1000000/factor) { + vdd_level = dcn_bw_v_max0p9; + } else if (clocks_in_khz > dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72*1000000/factor) { + vdd_level = dcn_bw_v_nom0p8; + } else if (clocks_in_khz > dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65*1000000/factor) { + vdd_level = dcn_bw_v_mid0p72; + } else + vdd_level = dcn_bw_v_min0p65; + } + break; + + case DM_PP_CLOCK_TYPE_DCFCLK: + if (clocks_in_khz > dc->dcn_soc->dcfclkv_max0p9*1000) { + vdd_level = dcn_bw_v_max0p91; + BREAK_TO_DEBUGGER(); + } else if (clocks_in_khz > dc->dcn_soc->dcfclkv_nom0p8*1000) { + vdd_level = dcn_bw_v_max0p9; + } else if (clocks_in_khz > dc->dcn_soc->dcfclkv_mid0p72*1000) { + vdd_level = dcn_bw_v_nom0p8; + } else if (clocks_in_khz > dc->dcn_soc->dcfclkv_min0p65*1000) { + vdd_level = dcn_bw_v_mid0p72; + } else + vdd_level = dcn_bw_v_min0p65; + break; + + default: + break; + } + return vdd_level; +} + +unsigned int dcn_find_dcfclk_suits_all( + const struct dc *dc, + struct clocks_value *clocks) +{ + unsigned vdd_level, vdd_level_temp; + unsigned dcf_clk; + + /*find a common supported voltage level*/ + vdd_level = dcn_find_normalized_clock_vdd_Level( + dc, DM_PP_CLOCK_TYPE_DISPLAY_CLK, clocks->dispclk_in_khz); + vdd_level_temp = dcn_find_normalized_clock_vdd_Level( + dc, DM_PP_CLOCK_TYPE_DISPLAYPHYCLK, clocks->phyclk_in_khz); + + vdd_level = dcn_bw_max(vdd_level, vdd_level_temp); + vdd_level_temp = dcn_find_normalized_clock_vdd_Level( + dc, DM_PP_CLOCK_TYPE_DPPCLK, clocks->dppclk_in_khz); + vdd_level = dcn_bw_max(vdd_level, vdd_level_temp); + + vdd_level_temp = dcn_find_normalized_clock_vdd_Level( + dc, DM_PP_CLOCK_TYPE_MEMORY_CLK, clocks->dcfclock_in_khz); + vdd_level = dcn_bw_max(vdd_level, vdd_level_temp); + vdd_level_temp = dcn_find_normalized_clock_vdd_Level( + dc, DM_PP_CLOCK_TYPE_DCFCLK, clocks->dcfclock_in_khz); + + /*find that level conresponding dcfclk*/ + vdd_level = dcn_bw_max(vdd_level, vdd_level_temp); + if (vdd_level == dcn_bw_v_max0p91) { + BREAK_TO_DEBUGGER(); + dcf_clk = dc->dcn_soc->dcfclkv_max0p9*1000; + } else if (vdd_level == dcn_bw_v_max0p9) + dcf_clk = dc->dcn_soc->dcfclkv_max0p9*1000; + else if (vdd_level == dcn_bw_v_nom0p8) + dcf_clk = dc->dcn_soc->dcfclkv_nom0p8*1000; + else if (vdd_level == dcn_bw_v_mid0p72) + dcf_clk = dc->dcn_soc->dcfclkv_mid0p72*1000; + else + dcf_clk = dc->dcn_soc->dcfclkv_min0p65*1000; + + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "\tdcf_clk for voltage = %d\n", dcf_clk); + return dcf_clk; +} + +void dcn_bw_update_from_pplib(struct dc *dc) +{ + struct dc_context *ctx = dc->ctx; + struct dm_pp_clock_levels_with_voltage clks = {0}; + + kernel_fpu_begin(); + + /* TODO: This is not the proper way to obtain fabric_and_dram_bandwidth, should be min(fclk, memclk) */ + + if (dm_pp_get_clock_levels_by_type_with_voltage( + ctx, DM_PP_CLOCK_TYPE_FCLK, &clks) && + clks.num_levels != 0) { + ASSERT(clks.num_levels >= 3); + dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65 = 32 * (clks.data[0].clocks_in_khz / 1000.0) / 1000.0; + if (clks.num_levels > 2) { + dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72 = dc->dcn_soc->number_of_channels * + (clks.data[clks.num_levels - 3].clocks_in_khz / 1000.0) * ddr4_dram_factor_single_Channel / 1000.0; + } else { + dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72 = dc->dcn_soc->number_of_channels * + (clks.data[clks.num_levels - 2].clocks_in_khz / 1000.0) * ddr4_dram_factor_single_Channel / 1000.0; + } + dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8 = dc->dcn_soc->number_of_channels * + (clks.data[clks.num_levels - 2].clocks_in_khz / 1000.0) * ddr4_dram_factor_single_Channel / 1000.0; + dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 = dc->dcn_soc->number_of_channels * + (clks.data[clks.num_levels - 1].clocks_in_khz / 1000.0) * ddr4_dram_factor_single_Channel / 1000.0; + } else + BREAK_TO_DEBUGGER(); + if (dm_pp_get_clock_levels_by_type_with_voltage( + ctx, DM_PP_CLOCK_TYPE_DCFCLK, &clks) && + clks.num_levels >= 3) { + dc->dcn_soc->dcfclkv_min0p65 = clks.data[0].clocks_in_khz / 1000.0; + dc->dcn_soc->dcfclkv_mid0p72 = clks.data[clks.num_levels - 3].clocks_in_khz / 1000.0; + dc->dcn_soc->dcfclkv_nom0p8 = clks.data[clks.num_levels - 2].clocks_in_khz / 1000.0; + dc->dcn_soc->dcfclkv_max0p9 = clks.data[clks.num_levels - 1].clocks_in_khz / 1000.0; + } else + BREAK_TO_DEBUGGER(); + + kernel_fpu_end(); +} + +void dcn_bw_notify_pplib_of_wm_ranges(struct dc *dc) +{ + struct pp_smu_funcs_rv *pp = dc->res_pool->pp_smu; + struct pp_smu_wm_range_sets ranges = {0}; + int max_fclk_khz, nom_fclk_khz, mid_fclk_khz, min_fclk_khz; + int max_dcfclk_khz, min_dcfclk_khz; + int socclk_khz; + const int overdrive = 5000000; /* 5 GHz to cover Overdrive */ + unsigned factor = (ddr4_dram_factor_single_Channel * dc->dcn_soc->number_of_channels); + + if (!pp->set_wm_ranges) + return; + + kernel_fpu_begin(); + max_fclk_khz = dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 * 1000000 / factor; + nom_fclk_khz = dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8 * 1000000 / factor; + mid_fclk_khz = dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72 * 1000000 / factor; + min_fclk_khz = dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65 * 1000000 / 32; + max_dcfclk_khz = dc->dcn_soc->dcfclkv_max0p9 * 1000; + min_dcfclk_khz = dc->dcn_soc->dcfclkv_min0p65 * 1000; + socclk_khz = dc->dcn_soc->socclk * 1000; + kernel_fpu_end(); + + /* Now notify PPLib/SMU about which Watermarks sets they should select + * depending on DPM state they are in. And update BW MGR GFX Engine and + * Memory clock member variables for Watermarks calculations for each + * Watermark Set + */ + /* SOCCLK does not affect anytihng but writeback for DCN so for now we dont + * care what the value is, hence min to overdrive level + */ + ranges.num_reader_wm_sets = WM_COUNT; + ranges.num_writer_wm_sets = WM_COUNT; + ranges.reader_wm_sets[0].wm_inst = WM_A; + ranges.reader_wm_sets[0].min_drain_clk_khz = min_dcfclk_khz; + ranges.reader_wm_sets[0].max_drain_clk_khz = max_dcfclk_khz; + ranges.reader_wm_sets[0].min_fill_clk_khz = min_fclk_khz; + ranges.reader_wm_sets[0].max_fill_clk_khz = min_fclk_khz; + ranges.writer_wm_sets[0].wm_inst = WM_A; + ranges.writer_wm_sets[0].min_fill_clk_khz = socclk_khz; + ranges.writer_wm_sets[0].max_fill_clk_khz = overdrive; + ranges.writer_wm_sets[0].min_drain_clk_khz = min_fclk_khz; + ranges.writer_wm_sets[0].max_drain_clk_khz = min_fclk_khz; + + ranges.reader_wm_sets[1].wm_inst = WM_B; + ranges.reader_wm_sets[1].min_drain_clk_khz = min_fclk_khz; + ranges.reader_wm_sets[1].max_drain_clk_khz = max_dcfclk_khz; + ranges.reader_wm_sets[1].min_fill_clk_khz = mid_fclk_khz; + ranges.reader_wm_sets[1].max_fill_clk_khz = mid_fclk_khz; + ranges.writer_wm_sets[1].wm_inst = WM_B; + ranges.writer_wm_sets[1].min_fill_clk_khz = socclk_khz; + ranges.writer_wm_sets[1].max_fill_clk_khz = overdrive; + ranges.writer_wm_sets[1].min_drain_clk_khz = mid_fclk_khz; + ranges.writer_wm_sets[1].max_drain_clk_khz = mid_fclk_khz; + + + ranges.reader_wm_sets[2].wm_inst = WM_C; + ranges.reader_wm_sets[2].min_drain_clk_khz = min_fclk_khz; + ranges.reader_wm_sets[2].max_drain_clk_khz = max_dcfclk_khz; + ranges.reader_wm_sets[2].min_fill_clk_khz = nom_fclk_khz; + ranges.reader_wm_sets[2].max_fill_clk_khz = nom_fclk_khz; + ranges.writer_wm_sets[2].wm_inst = WM_C; + ranges.writer_wm_sets[2].min_fill_clk_khz = socclk_khz; + ranges.writer_wm_sets[2].max_fill_clk_khz = overdrive; + ranges.writer_wm_sets[2].min_drain_clk_khz = nom_fclk_khz; + ranges.writer_wm_sets[2].max_drain_clk_khz = nom_fclk_khz; + + ranges.reader_wm_sets[3].wm_inst = WM_D; + ranges.reader_wm_sets[3].min_drain_clk_khz = min_fclk_khz; + ranges.reader_wm_sets[3].max_drain_clk_khz = max_dcfclk_khz; + ranges.reader_wm_sets[3].min_fill_clk_khz = max_fclk_khz; + ranges.reader_wm_sets[3].max_fill_clk_khz = max_fclk_khz; + ranges.writer_wm_sets[3].wm_inst = WM_D; + ranges.writer_wm_sets[3].min_fill_clk_khz = socclk_khz; + ranges.writer_wm_sets[3].max_fill_clk_khz = overdrive; + ranges.writer_wm_sets[3].min_drain_clk_khz = max_fclk_khz; + ranges.writer_wm_sets[3].max_drain_clk_khz = max_fclk_khz; + + if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) { + ranges.reader_wm_sets[0].wm_inst = WM_A; + ranges.reader_wm_sets[0].min_drain_clk_khz = 300000; + ranges.reader_wm_sets[0].max_drain_clk_khz = 654000; + ranges.reader_wm_sets[0].min_fill_clk_khz = 800000; + ranges.reader_wm_sets[0].max_fill_clk_khz = 800000; + ranges.writer_wm_sets[0].wm_inst = WM_A; + ranges.writer_wm_sets[0].min_fill_clk_khz = 200000; + ranges.writer_wm_sets[0].max_fill_clk_khz = 757000; + ranges.writer_wm_sets[0].min_drain_clk_khz = 800000; + ranges.writer_wm_sets[0].max_drain_clk_khz = 800000; + + ranges.reader_wm_sets[1].wm_inst = WM_B; + ranges.reader_wm_sets[1].min_drain_clk_khz = 300000; + ranges.reader_wm_sets[1].max_drain_clk_khz = 654000; + ranges.reader_wm_sets[1].min_fill_clk_khz = 933000; + ranges.reader_wm_sets[1].max_fill_clk_khz = 933000; + ranges.writer_wm_sets[1].wm_inst = WM_B; + ranges.writer_wm_sets[1].min_fill_clk_khz = 200000; + ranges.writer_wm_sets[1].max_fill_clk_khz = 757000; + ranges.writer_wm_sets[1].min_drain_clk_khz = 933000; + ranges.writer_wm_sets[1].max_drain_clk_khz = 933000; + + + ranges.reader_wm_sets[2].wm_inst = WM_C; + ranges.reader_wm_sets[2].min_drain_clk_khz = 300000; + ranges.reader_wm_sets[2].max_drain_clk_khz = 654000; + ranges.reader_wm_sets[2].min_fill_clk_khz = 1067000; + ranges.reader_wm_sets[2].max_fill_clk_khz = 1067000; + ranges.writer_wm_sets[2].wm_inst = WM_C; + ranges.writer_wm_sets[2].min_fill_clk_khz = 200000; + ranges.writer_wm_sets[2].max_fill_clk_khz = 757000; + ranges.writer_wm_sets[2].min_drain_clk_khz = 1067000; + ranges.writer_wm_sets[2].max_drain_clk_khz = 1067000; + + ranges.reader_wm_sets[3].wm_inst = WM_D; + ranges.reader_wm_sets[3].min_drain_clk_khz = 300000; + ranges.reader_wm_sets[3].max_drain_clk_khz = 654000; + ranges.reader_wm_sets[3].min_fill_clk_khz = 1200000; + ranges.reader_wm_sets[3].max_fill_clk_khz = 1200000; + ranges.writer_wm_sets[3].wm_inst = WM_D; + ranges.writer_wm_sets[3].min_fill_clk_khz = 200000; + ranges.writer_wm_sets[3].max_fill_clk_khz = 757000; + ranges.writer_wm_sets[3].min_drain_clk_khz = 1200000; + ranges.writer_wm_sets[3].max_drain_clk_khz = 1200000; + } + + /* Notify PP Lib/SMU which Watermarks to use for which clock ranges */ + pp->set_wm_ranges(&pp->pp_smu, &ranges); +} + +void dcn_bw_sync_calcs_and_dml(struct dc *dc) +{ + kernel_fpu_begin(); + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "sr_exit_time: %d ns\n" + "sr_enter_plus_exit_time: %d ns\n" + "urgent_latency: %d ns\n" + "write_back_latency: %d ns\n" + "percent_of_ideal_drambw_received_after_urg_latency: %d %\n" + "max_request_size: %d bytes\n" + "dcfclkv_max0p9: %d kHz\n" + "dcfclkv_nom0p8: %d kHz\n" + "dcfclkv_mid0p72: %d kHz\n" + "dcfclkv_min0p65: %d kHz\n" + "max_dispclk_vmax0p9: %d kHz\n" + "max_dispclk_vnom0p8: %d kHz\n" + "max_dispclk_vmid0p72: %d kHz\n" + "max_dispclk_vmin0p65: %d kHz\n" + "max_dppclk_vmax0p9: %d kHz\n" + "max_dppclk_vnom0p8: %d kHz\n" + "max_dppclk_vmid0p72: %d kHz\n" + "max_dppclk_vmin0p65: %d kHz\n" + "socclk: %d kHz\n" + "fabric_and_dram_bandwidth_vmax0p9: %d MB/s\n" + "fabric_and_dram_bandwidth_vnom0p8: %d MB/s\n" + "fabric_and_dram_bandwidth_vmid0p72: %d MB/s\n" + "fabric_and_dram_bandwidth_vmin0p65: %d MB/s\n" + "phyclkv_max0p9: %d kHz\n" + "phyclkv_nom0p8: %d kHz\n" + "phyclkv_mid0p72: %d kHz\n" + "phyclkv_min0p65: %d kHz\n" + "downspreading: %d %\n" + "round_trip_ping_latency_cycles: %d DCFCLK Cycles\n" + "urgent_out_of_order_return_per_channel: %d Bytes\n" + "number_of_channels: %d\n" + "vmm_page_size: %d Bytes\n" + "dram_clock_change_latency: %d ns\n" + "return_bus_width: %d Bytes\n", + dc->dcn_soc->sr_exit_time * 1000, + dc->dcn_soc->sr_enter_plus_exit_time * 1000, + dc->dcn_soc->urgent_latency * 1000, + dc->dcn_soc->write_back_latency * 1000, + dc->dcn_soc->percent_of_ideal_drambw_received_after_urg_latency, + dc->dcn_soc->max_request_size, + dc->dcn_soc->dcfclkv_max0p9 * 1000, + dc->dcn_soc->dcfclkv_nom0p8 * 1000, + dc->dcn_soc->dcfclkv_mid0p72 * 1000, + dc->dcn_soc->dcfclkv_min0p65 * 1000, + dc->dcn_soc->max_dispclk_vmax0p9 * 1000, + dc->dcn_soc->max_dispclk_vnom0p8 * 1000, + dc->dcn_soc->max_dispclk_vmid0p72 * 1000, + dc->dcn_soc->max_dispclk_vmin0p65 * 1000, + dc->dcn_soc->max_dppclk_vmax0p9 * 1000, + dc->dcn_soc->max_dppclk_vnom0p8 * 1000, + dc->dcn_soc->max_dppclk_vmid0p72 * 1000, + dc->dcn_soc->max_dppclk_vmin0p65 * 1000, + dc->dcn_soc->socclk * 1000, + dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 * 1000, + dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8 * 1000, + dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72 * 1000, + dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65 * 1000, + dc->dcn_soc->phyclkv_max0p9 * 1000, + dc->dcn_soc->phyclkv_nom0p8 * 1000, + dc->dcn_soc->phyclkv_mid0p72 * 1000, + dc->dcn_soc->phyclkv_min0p65 * 1000, + dc->dcn_soc->downspreading * 100, + dc->dcn_soc->round_trip_ping_latency_cycles, + dc->dcn_soc->urgent_out_of_order_return_per_channel, + dc->dcn_soc->number_of_channels, + dc->dcn_soc->vmm_page_size, + dc->dcn_soc->dram_clock_change_latency * 1000, + dc->dcn_soc->return_bus_width); + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "rob_buffer_size_in_kbyte: %d\n" + "det_buffer_size_in_kbyte: %d\n" + "dpp_output_buffer_pixels: %d\n" + "opp_output_buffer_lines: %d\n" + "pixel_chunk_size_in_kbyte: %d\n" + "pte_enable: %d\n" + "pte_chunk_size: %d kbytes\n" + "meta_chunk_size: %d kbytes\n" + "writeback_chunk_size: %d kbytes\n" + "odm_capability: %d\n" + "dsc_capability: %d\n" + "line_buffer_size: %d bits\n" + "max_line_buffer_lines: %d\n" + "is_line_buffer_bpp_fixed: %d\n" + "line_buffer_fixed_bpp: %d\n" + "writeback_luma_buffer_size: %d kbytes\n" + "writeback_chroma_buffer_size: %d kbytes\n" + "max_num_dpp: %d\n" + "max_num_writeback: %d\n" + "max_dchub_topscl_throughput: %d pixels/dppclk\n" + "max_pscl_tolb_throughput: %d pixels/dppclk\n" + "max_lb_tovscl_throughput: %d pixels/dppclk\n" + "max_vscl_tohscl_throughput: %d pixels/dppclk\n" + "max_hscl_ratio: %d\n" + "max_vscl_ratio: %d\n" + "max_hscl_taps: %d\n" + "max_vscl_taps: %d\n" + "pte_buffer_size_in_requests: %d\n" + "dispclk_ramping_margin: %d %\n" + "under_scan_factor: %d %\n" + "max_inter_dcn_tile_repeaters: %d\n" + "can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one: %d\n" + "bug_forcing_luma_and_chroma_request_to_same_size_fixed: %d\n" + "dcfclk_cstate_latency: %d\n", + dc->dcn_ip->rob_buffer_size_in_kbyte, + dc->dcn_ip->det_buffer_size_in_kbyte, + dc->dcn_ip->dpp_output_buffer_pixels, + dc->dcn_ip->opp_output_buffer_lines, + dc->dcn_ip->pixel_chunk_size_in_kbyte, + dc->dcn_ip->pte_enable, + dc->dcn_ip->pte_chunk_size, + dc->dcn_ip->meta_chunk_size, + dc->dcn_ip->writeback_chunk_size, + dc->dcn_ip->odm_capability, + dc->dcn_ip->dsc_capability, + dc->dcn_ip->line_buffer_size, + dc->dcn_ip->max_line_buffer_lines, + dc->dcn_ip->is_line_buffer_bpp_fixed, + dc->dcn_ip->line_buffer_fixed_bpp, + dc->dcn_ip->writeback_luma_buffer_size, + dc->dcn_ip->writeback_chroma_buffer_size, + dc->dcn_ip->max_num_dpp, + dc->dcn_ip->max_num_writeback, + dc->dcn_ip->max_dchub_topscl_throughput, + dc->dcn_ip->max_pscl_tolb_throughput, + dc->dcn_ip->max_lb_tovscl_throughput, + dc->dcn_ip->max_vscl_tohscl_throughput, + dc->dcn_ip->max_hscl_ratio, + dc->dcn_ip->max_vscl_ratio, + dc->dcn_ip->max_hscl_taps, + dc->dcn_ip->max_vscl_taps, + dc->dcn_ip->pte_buffer_size_in_requests, + dc->dcn_ip->dispclk_ramping_margin, + dc->dcn_ip->under_scan_factor * 100, + dc->dcn_ip->max_inter_dcn_tile_repeaters, + dc->dcn_ip->can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one, + dc->dcn_ip->bug_forcing_luma_and_chroma_request_to_same_size_fixed, + dc->dcn_ip->dcfclk_cstate_latency); + dc->dml.soc.vmin.socclk_mhz = dc->dcn_soc->socclk; + dc->dml.soc.vmid.socclk_mhz = dc->dcn_soc->socclk; + dc->dml.soc.vnom.socclk_mhz = dc->dcn_soc->socclk; + dc->dml.soc.vmax.socclk_mhz = dc->dcn_soc->socclk; + + dc->dml.soc.vmin.dcfclk_mhz = dc->dcn_soc->dcfclkv_min0p65; + dc->dml.soc.vmid.dcfclk_mhz = dc->dcn_soc->dcfclkv_mid0p72; + dc->dml.soc.vnom.dcfclk_mhz = dc->dcn_soc->dcfclkv_nom0p8; + dc->dml.soc.vmax.dcfclk_mhz = dc->dcn_soc->dcfclkv_max0p9; + + dc->dml.soc.vmin.dispclk_mhz = dc->dcn_soc->max_dispclk_vmin0p65; + dc->dml.soc.vmid.dispclk_mhz = dc->dcn_soc->max_dispclk_vmid0p72; + dc->dml.soc.vnom.dispclk_mhz = dc->dcn_soc->max_dispclk_vnom0p8; + dc->dml.soc.vmax.dispclk_mhz = dc->dcn_soc->max_dispclk_vmax0p9; + + dc->dml.soc.vmin.dppclk_mhz = dc->dcn_soc->max_dppclk_vmin0p65; + dc->dml.soc.vmid.dppclk_mhz = dc->dcn_soc->max_dppclk_vmid0p72; + dc->dml.soc.vnom.dppclk_mhz = dc->dcn_soc->max_dppclk_vnom0p8; + dc->dml.soc.vmax.dppclk_mhz = dc->dcn_soc->max_dppclk_vmax0p9; + + dc->dml.soc.vmin.phyclk_mhz = dc->dcn_soc->phyclkv_min0p65; + dc->dml.soc.vmid.phyclk_mhz = dc->dcn_soc->phyclkv_mid0p72; + dc->dml.soc.vnom.phyclk_mhz = dc->dcn_soc->phyclkv_nom0p8; + dc->dml.soc.vmax.phyclk_mhz = dc->dcn_soc->phyclkv_max0p9; + + dc->dml.soc.vmin.dram_bw_per_chan_gbps = dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65; + dc->dml.soc.vmid.dram_bw_per_chan_gbps = dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72; + dc->dml.soc.vnom.dram_bw_per_chan_gbps = dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8; + dc->dml.soc.vmax.dram_bw_per_chan_gbps = dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9; + + dc->dml.soc.sr_exit_time_us = dc->dcn_soc->sr_exit_time; + dc->dml.soc.sr_enter_plus_exit_time_us = dc->dcn_soc->sr_enter_plus_exit_time; + dc->dml.soc.urgent_latency_us = dc->dcn_soc->urgent_latency; + dc->dml.soc.writeback_latency_us = dc->dcn_soc->write_back_latency; + dc->dml.soc.ideal_dram_bw_after_urgent_percent = + dc->dcn_soc->percent_of_ideal_drambw_received_after_urg_latency; + dc->dml.soc.max_request_size_bytes = dc->dcn_soc->max_request_size; + dc->dml.soc.downspread_percent = dc->dcn_soc->downspreading; + dc->dml.soc.round_trip_ping_latency_dcfclk_cycles = + dc->dcn_soc->round_trip_ping_latency_cycles; + dc->dml.soc.urgent_out_of_order_return_per_channel_bytes = + dc->dcn_soc->urgent_out_of_order_return_per_channel; + dc->dml.soc.num_chans = dc->dcn_soc->number_of_channels; + dc->dml.soc.vmm_page_size_bytes = dc->dcn_soc->vmm_page_size; + dc->dml.soc.dram_clock_change_latency_us = dc->dcn_soc->dram_clock_change_latency; + dc->dml.soc.return_bus_width_bytes = dc->dcn_soc->return_bus_width; + + dc->dml.ip.rob_buffer_size_kbytes = dc->dcn_ip->rob_buffer_size_in_kbyte; + dc->dml.ip.det_buffer_size_kbytes = dc->dcn_ip->det_buffer_size_in_kbyte; + dc->dml.ip.dpp_output_buffer_pixels = dc->dcn_ip->dpp_output_buffer_pixels; + dc->dml.ip.opp_output_buffer_lines = dc->dcn_ip->opp_output_buffer_lines; + dc->dml.ip.pixel_chunk_size_kbytes = dc->dcn_ip->pixel_chunk_size_in_kbyte; + dc->dml.ip.pte_enable = dc->dcn_ip->pte_enable == dcn_bw_yes; + dc->dml.ip.pte_chunk_size_kbytes = dc->dcn_ip->pte_chunk_size; + dc->dml.ip.meta_chunk_size_kbytes = dc->dcn_ip->meta_chunk_size; + dc->dml.ip.writeback_chunk_size_kbytes = dc->dcn_ip->writeback_chunk_size; + dc->dml.ip.line_buffer_size_bits = dc->dcn_ip->line_buffer_size; + dc->dml.ip.max_line_buffer_lines = dc->dcn_ip->max_line_buffer_lines; + dc->dml.ip.IsLineBufferBppFixed = dc->dcn_ip->is_line_buffer_bpp_fixed == dcn_bw_yes; + dc->dml.ip.LineBufferFixedBpp = dc->dcn_ip->line_buffer_fixed_bpp; + dc->dml.ip.writeback_luma_buffer_size_kbytes = dc->dcn_ip->writeback_luma_buffer_size; + dc->dml.ip.writeback_chroma_buffer_size_kbytes = dc->dcn_ip->writeback_chroma_buffer_size; + dc->dml.ip.max_num_dpp = dc->dcn_ip->max_num_dpp; + dc->dml.ip.max_num_wb = dc->dcn_ip->max_num_writeback; + dc->dml.ip.max_dchub_pscl_bw_pix_per_clk = dc->dcn_ip->max_dchub_topscl_throughput; + dc->dml.ip.max_pscl_lb_bw_pix_per_clk = dc->dcn_ip->max_pscl_tolb_throughput; + dc->dml.ip.max_lb_vscl_bw_pix_per_clk = dc->dcn_ip->max_lb_tovscl_throughput; + dc->dml.ip.max_vscl_hscl_bw_pix_per_clk = dc->dcn_ip->max_vscl_tohscl_throughput; + dc->dml.ip.max_hscl_ratio = dc->dcn_ip->max_hscl_ratio; + dc->dml.ip.max_vscl_ratio = dc->dcn_ip->max_vscl_ratio; + dc->dml.ip.max_hscl_taps = dc->dcn_ip->max_hscl_taps; + dc->dml.ip.max_vscl_taps = dc->dcn_ip->max_vscl_taps; + /*pte_buffer_size_in_requests missing in dml*/ + dc->dml.ip.dispclk_ramp_margin_percent = dc->dcn_ip->dispclk_ramping_margin; + dc->dml.ip.underscan_factor = dc->dcn_ip->under_scan_factor; + dc->dml.ip.max_inter_dcn_tile_repeaters = dc->dcn_ip->max_inter_dcn_tile_repeaters; + dc->dml.ip.can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one = + dc->dcn_ip->can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one == dcn_bw_yes; + dc->dml.ip.bug_forcing_LC_req_same_size_fixed = + dc->dcn_ip->bug_forcing_luma_and_chroma_request_to_same_size_fixed == dcn_bw_yes; + dc->dml.ip.dcfclk_cstate_latency = dc->dcn_ip->dcfclk_cstate_latency; + kernel_fpu_end(); +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c new file mode 100644 index 000000000000..fe63f5894d43 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -0,0 +1,1677 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + */ + +#include "dm_services.h" + +#include "dc.h" + +#include "core_status.h" +#include "core_types.h" +#include "hw_sequencer.h" + +#include "resource.h" + +#include "clock_source.h" +#include "dc_bios_types.h" + +#include "bios_parser_interface.h" +#include "include/irq_service_interface.h" +#include "transform.h" +#include "dpp.h" +#include "timing_generator.h" +#include "virtual/virtual_link_encoder.h" + +#include "link_hwss.h" +#include "link_encoder.h" + +#include "dc_link_ddc.h" +#include "dm_helpers.h" +#include "mem_input.h" +#include "hubp.h" + + +/******************************************************************************* + * Private functions + ******************************************************************************/ +static void destroy_links(struct dc *dc) +{ + uint32_t i; + + for (i = 0; i < dc->link_count; i++) { + if (NULL != dc->links[i]) + link_destroy(&dc->links[i]); + } +} + +static bool create_links( + struct dc *dc, + uint32_t num_virtual_links) +{ + int i; + int connectors_num; + struct dc_bios *bios = dc->ctx->dc_bios; + + dc->link_count = 0; + + connectors_num = bios->funcs->get_connectors_number(bios); + + if (connectors_num > ENUM_ID_COUNT) { + dm_error( + "DC: Number of connectors %d exceeds maximum of %d!\n", + connectors_num, + ENUM_ID_COUNT); + return false; + } + + if (connectors_num == 0 && num_virtual_links == 0) { + dm_error("DC: Number of connectors is zero!\n"); + } + + dm_output_to_console( + "DC: %s: connectors_num: physical:%d, virtual:%d\n", + __func__, + connectors_num, + num_virtual_links); + + for (i = 0; i < connectors_num; i++) { + struct link_init_data link_init_params = {0}; + struct dc_link *link; + + link_init_params.ctx = dc->ctx; + /* next BIOS object table connector */ + link_init_params.connector_index = i; + link_init_params.link_index = dc->link_count; + link_init_params.dc = dc; + link = link_create(&link_init_params); + + if (link) { + dc->links[dc->link_count] = link; + link->dc = dc; + ++dc->link_count; + } + } + + for (i = 0; i < num_virtual_links; i++) { + struct dc_link *link = kzalloc(sizeof(*link), GFP_KERNEL); + struct encoder_init_data enc_init = {0}; + + if (link == NULL) { + BREAK_TO_DEBUGGER(); + goto failed_alloc; + } + + link->ctx = dc->ctx; + link->dc = dc; + link->connector_signal = SIGNAL_TYPE_VIRTUAL; + link->link_id.type = OBJECT_TYPE_CONNECTOR; + link->link_id.id = CONNECTOR_ID_VIRTUAL; + link->link_id.enum_id = ENUM_ID_1; + link->link_enc = kzalloc(sizeof(*link->link_enc), GFP_KERNEL); + + enc_init.ctx = dc->ctx; + enc_init.channel = CHANNEL_ID_UNKNOWN; + enc_init.hpd_source = HPD_SOURCEID_UNKNOWN; + enc_init.transmitter = TRANSMITTER_UNKNOWN; + enc_init.connector = link->link_id; + enc_init.encoder.type = OBJECT_TYPE_ENCODER; + enc_init.encoder.id = ENCODER_ID_INTERNAL_VIRTUAL; + enc_init.encoder.enum_id = ENUM_ID_1; + virtual_link_encoder_construct(link->link_enc, &enc_init); + + link->link_index = dc->link_count; + dc->links[dc->link_count] = link; + dc->link_count++; + } + + return true; + +failed_alloc: + return false; +} + +static bool stream_adjust_vmin_vmax(struct dc *dc, + struct dc_stream_state **streams, int num_streams, + int vmin, int vmax) +{ + /* TODO: Support multiple streams */ + struct dc_stream_state *stream = streams[0]; + int i = 0; + bool ret = false; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i]; + + if (pipe->stream == stream && pipe->stream_res.stream_enc) { + dc->hwss.set_drr(&pipe, 1, vmin, vmax); + + /* build and update the info frame */ + resource_build_info_frame(pipe); + dc->hwss.update_info_frame(pipe); + + ret = true; + } + } + return ret; +} + +static bool stream_get_crtc_position(struct dc *dc, + struct dc_stream_state **streams, int num_streams, + unsigned int *v_pos, unsigned int *nom_v_pos) +{ + /* TODO: Support multiple streams */ + struct dc_stream_state *stream = streams[0]; + int i = 0; + bool ret = false; + struct crtc_position position; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe = + &dc->current_state->res_ctx.pipe_ctx[i]; + + if (pipe->stream == stream && pipe->stream_res.stream_enc) { + dc->hwss.get_position(&pipe, 1, &position); + + *v_pos = position.vertical_count; + *nom_v_pos = position.nominal_vcount; + ret = true; + } + } + return ret; +} + +static bool set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream) +{ + int i = 0; + bool ret = false; + struct pipe_ctx *pipes; + + for (i = 0; i < MAX_PIPES; i++) { + if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) { + pipes = &dc->current_state->res_ctx.pipe_ctx[i]; + dc->hwss.program_gamut_remap(pipes); + ret = true; + } + } + + return ret; +} + +static bool program_csc_matrix(struct dc *dc, struct dc_stream_state *stream) +{ + int i = 0; + bool ret = false; + struct pipe_ctx *pipes; + + for (i = 0; i < MAX_PIPES; i++) { + if (dc->current_state->res_ctx.pipe_ctx[i].stream + == stream) { + + pipes = &dc->current_state->res_ctx.pipe_ctx[i]; + dc->hwss.program_csc_matrix(pipes, + stream->output_color_space, + stream->csc_color_matrix.matrix); + ret = true; + } + } + + return ret; +} + +static void set_static_screen_events(struct dc *dc, + struct dc_stream_state **streams, + int num_streams, + const struct dc_static_screen_events *events) +{ + int i = 0; + int j = 0; + struct pipe_ctx *pipes_affected[MAX_PIPES]; + int num_pipes_affected = 0; + + for (i = 0; i < num_streams; i++) { + struct dc_stream_state *stream = streams[i]; + + for (j = 0; j < MAX_PIPES; j++) { + if (dc->current_state->res_ctx.pipe_ctx[j].stream + == stream) { + pipes_affected[num_pipes_affected++] = + &dc->current_state->res_ctx.pipe_ctx[j]; + } + } + } + + dc->hwss.set_static_screen_control(pipes_affected, num_pipes_affected, events); +} + +static void set_drive_settings(struct dc *dc, + struct link_training_settings *lt_settings, + const struct dc_link *link) +{ + + int i; + + for (i = 0; i < dc->link_count; i++) { + if (dc->links[i] == link) + break; + } + + if (i >= dc->link_count) + ASSERT_CRITICAL(false); + + dc_link_dp_set_drive_settings(dc->links[i], lt_settings); +} + +static void perform_link_training(struct dc *dc, + struct dc_link_settings *link_setting, + bool skip_video_pattern) +{ + int i; + + for (i = 0; i < dc->link_count; i++) + dc_link_dp_perform_link_training( + dc->links[i], + link_setting, + skip_video_pattern); +} + +static void set_preferred_link_settings(struct dc *dc, + struct dc_link_settings *link_setting, + struct dc_link *link) +{ + link->preferred_link_setting = *link_setting; + dp_retrain_link_dp_test(link, link_setting, false); +} + +static void enable_hpd(const struct dc_link *link) +{ + dc_link_dp_enable_hpd(link); +} + +static void disable_hpd(const struct dc_link *link) +{ + dc_link_dp_disable_hpd(link); +} + + +static void set_test_pattern( + struct dc_link *link, + enum dp_test_pattern test_pattern, + const struct link_training_settings *p_link_settings, + const unsigned char *p_custom_pattern, + unsigned int cust_pattern_size) +{ + if (link != NULL) + dc_link_dp_set_test_pattern( + link, + test_pattern, + p_link_settings, + p_custom_pattern, + cust_pattern_size); +} + +static void set_dither_option(struct dc_stream_state *stream, + enum dc_dither_option option) +{ + struct bit_depth_reduction_params params; + struct dc_link *link = stream->status.link; + struct pipe_ctx *pipes = NULL; + int i; + + for (i = 0; i < MAX_PIPES; i++) { + if (link->dc->current_state->res_ctx.pipe_ctx[i].stream == + stream) { + pipes = &link->dc->current_state->res_ctx.pipe_ctx[i]; + break; + } + } + + memset(¶ms, 0, sizeof(params)); + if (!pipes) + return; + if (option > DITHER_OPTION_MAX) + return; + + stream->dither_option = option; + + resource_build_bit_depth_reduction_params(stream, + ¶ms); + stream->bit_depth_params = params; + pipes->stream_res.opp->funcs-> + opp_program_bit_depth_reduction(pipes->stream_res.opp, ¶ms); +} + +void set_dpms( + struct dc *dc, + struct dc_stream_state *stream, + bool dpms_off) +{ + struct pipe_ctx *pipe_ctx = NULL; + int i; + + for (i = 0; i < MAX_PIPES; i++) { + if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) { + pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + break; + } + } + + if (!pipe_ctx) { + ASSERT(0); + return; + } + + if (stream->dpms_off != dpms_off) { + stream->dpms_off = dpms_off; + if (dpms_off) + core_link_disable_stream(pipe_ctx, + KEEP_ACQUIRED_RESOURCE); + else + core_link_enable_stream(dc->current_state, pipe_ctx); + } +} + +static void allocate_dc_stream_funcs(struct dc *dc) +{ + if (dc->hwss.set_drr != NULL) { + dc->stream_funcs.adjust_vmin_vmax = + stream_adjust_vmin_vmax; + } + + dc->stream_funcs.set_static_screen_events = + set_static_screen_events; + + dc->stream_funcs.get_crtc_position = + stream_get_crtc_position; + + dc->stream_funcs.set_gamut_remap = + set_gamut_remap; + + dc->stream_funcs.program_csc_matrix = + program_csc_matrix; + + dc->stream_funcs.set_dither_option = + set_dither_option; + + dc->stream_funcs.set_dpms = + set_dpms; + + dc->link_funcs.set_drive_settings = + set_drive_settings; + + dc->link_funcs.perform_link_training = + perform_link_training; + + dc->link_funcs.set_preferred_link_settings = + set_preferred_link_settings; + + dc->link_funcs.enable_hpd = + enable_hpd; + + dc->link_funcs.disable_hpd = + disable_hpd; + + dc->link_funcs.set_test_pattern = + set_test_pattern; +} + +static void destruct(struct dc *dc) +{ + dc_release_state(dc->current_state); + dc->current_state = NULL; + + destroy_links(dc); + + dc_destroy_resource_pool(dc); + + if (dc->ctx->gpio_service) + dal_gpio_service_destroy(&dc->ctx->gpio_service); + + if (dc->ctx->i2caux) + dal_i2caux_destroy(&dc->ctx->i2caux); + + if (dc->ctx->created_bios) + dal_bios_parser_destroy(&dc->ctx->dc_bios); + + if (dc->ctx->logger) + dal_logger_destroy(&dc->ctx->logger); + + kfree(dc->ctx); + dc->ctx = NULL; + + kfree(dc->bw_vbios); + dc->bw_vbios = NULL; + + kfree(dc->bw_dceip); + dc->bw_dceip = NULL; + +#ifdef CONFIG_DRM_AMD_DC_DCN1_0 + kfree(dc->dcn_soc); + dc->dcn_soc = NULL; + + kfree(dc->dcn_ip); + dc->dcn_ip = NULL; + +#endif +} + +static bool construct(struct dc *dc, + const struct dc_init_data *init_params) +{ + struct dal_logger *logger; + struct dc_context *dc_ctx = kzalloc(sizeof(*dc_ctx), GFP_KERNEL); + struct bw_calcs_dceip *dc_dceip = kzalloc(sizeof(*dc_dceip), + GFP_KERNEL); + struct bw_calcs_vbios *dc_vbios = kzalloc(sizeof(*dc_vbios), + GFP_KERNEL); +#ifdef CONFIG_DRM_AMD_DC_DCN1_0 + struct dcn_soc_bounding_box *dcn_soc = kzalloc(sizeof(*dcn_soc), + GFP_KERNEL); + struct dcn_ip_params *dcn_ip = kzalloc(sizeof(*dcn_ip), GFP_KERNEL); +#endif + + enum dce_version dc_version = DCE_VERSION_UNKNOWN; + + if (!dc_dceip) { + dm_error("%s: failed to create dceip\n", __func__); + goto fail; + } + + dc->bw_dceip = dc_dceip; + + if (!dc_vbios) { + dm_error("%s: failed to create vbios\n", __func__); + goto fail; + } + + dc->bw_vbios = dc_vbios; +#ifdef CONFIG_DRM_AMD_DC_DCN1_0 + if (!dcn_soc) { + dm_error("%s: failed to create dcn_soc\n", __func__); + goto fail; + } + + dc->dcn_soc = dcn_soc; + + if (!dcn_ip) { + dm_error("%s: failed to create dcn_ip\n", __func__); + goto fail; + } + + dc->dcn_ip = dcn_ip; +#endif + + if (!dc_ctx) { + dm_error("%s: failed to create ctx\n", __func__); + goto fail; + } + + dc->current_state = dc_create_state(); + + if (!dc->current_state) { + dm_error("%s: failed to create validate ctx\n", __func__); + goto fail; + } + + dc_ctx->cgs_device = init_params->cgs_device; + dc_ctx->driver_context = init_params->driver; + dc_ctx->dc = dc; + dc_ctx->asic_id = init_params->asic_id; + + /* Create logger */ + logger = dal_logger_create(dc_ctx, init_params->log_mask); + + if (!logger) { + /* can *not* call logger. call base driver 'print error' */ + dm_error("%s: failed to create Logger!\n", __func__); + goto fail; + } + dc_ctx->logger = logger; + dc->ctx = dc_ctx; + dc->ctx->dce_environment = init_params->dce_environment; + + dc_version = resource_parse_asic_id(init_params->asic_id); + dc->ctx->dce_version = dc_version; +#if defined(CONFIG_DRM_AMD_DC_FBC) + dc->ctx->fbc_gpu_addr = init_params->fbc_gpu_addr; +#endif + /* Resource should construct all asic specific resources. + * This should be the only place where we need to parse the asic id + */ + if (init_params->vbios_override) + dc_ctx->dc_bios = init_params->vbios_override; + else { + /* Create BIOS parser */ + struct bp_init_data bp_init_data; + + bp_init_data.ctx = dc_ctx; + bp_init_data.bios = init_params->asic_id.atombios_base_address; + + dc_ctx->dc_bios = dal_bios_parser_create( + &bp_init_data, dc_version); + + if (!dc_ctx->dc_bios) { + ASSERT_CRITICAL(false); + goto fail; + } + + dc_ctx->created_bios = true; + } + + /* Create I2C AUX */ + dc_ctx->i2caux = dal_i2caux_create(dc_ctx); + + if (!dc_ctx->i2caux) { + ASSERT_CRITICAL(false); + goto fail; + } + + /* Create GPIO service */ + dc_ctx->gpio_service = dal_gpio_service_create( + dc_version, + dc_ctx->dce_environment, + dc_ctx); + + if (!dc_ctx->gpio_service) { + ASSERT_CRITICAL(false); + goto fail; + } + + dc->res_pool = dc_create_resource_pool( + dc, + init_params->num_virtual_links, + dc_version, + init_params->asic_id); + if (!dc->res_pool) + goto fail; + + dc_resource_state_construct(dc, dc->current_state); + + if (!create_links(dc, init_params->num_virtual_links)) + goto fail; + + allocate_dc_stream_funcs(dc); + + return true; + +fail: + + destruct(dc); + return false; +} + +static void disable_dangling_plane(struct dc *dc, struct dc_state *context) +{ + int i, j; + struct dc_state *dangling_context = dc_create_state(); + struct dc_state *current_ctx; + + if (dangling_context == NULL) + return; + + dc_resource_state_copy_construct(dc->current_state, dangling_context); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct dc_stream_state *old_stream = + dc->current_state->res_ctx.pipe_ctx[i].stream; + bool should_disable = true; + + for (j = 0; j < context->stream_count; j++) { + if (old_stream == context->streams[j]) { + should_disable = false; + break; + } + } + if (should_disable && old_stream) { + dc_rem_all_planes_for_stream(dc, old_stream, dangling_context); + dc->hwss.apply_ctx_for_surface(dc, old_stream, 0, dangling_context); + } + } + + current_ctx = dc->current_state; + dc->current_state = dangling_context; + dc_release_state(current_ctx); +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ + +struct dc *dc_create(const struct dc_init_data *init_params) + { + struct dc *dc = kzalloc(sizeof(*dc), GFP_KERNEL); + unsigned int full_pipe_count; + + if (NULL == dc) + goto alloc_fail; + + if (false == construct(dc, init_params)) + goto construct_fail; + + /*TODO: separate HW and SW initialization*/ + dc->hwss.init_hw(dc); + + full_pipe_count = dc->res_pool->pipe_count; + if (dc->res_pool->underlay_pipe_index != NO_UNDERLAY_PIPE) + full_pipe_count--; + dc->caps.max_streams = min( + full_pipe_count, + dc->res_pool->stream_enc_count); + + dc->caps.max_links = dc->link_count; + dc->caps.max_audios = dc->res_pool->audio_count; + + dc->config = init_params->flags; + + dm_logger_write(dc->ctx->logger, LOG_DC, + "Display Core initialized\n"); + + + /* TODO: missing feature to be enabled */ + dc->debug.disable_dfs_bypass = true; + + return dc; + +construct_fail: + kfree(dc); + +alloc_fail: + return NULL; +} + +void dc_destroy(struct dc **dc) +{ + destruct(*dc); + kfree(*dc); + *dc = NULL; +} + +static void program_timing_sync( + struct dc *dc, + struct dc_state *ctx) +{ + int i, j; + int group_index = 0; + int pipe_count = dc->res_pool->pipe_count; + struct pipe_ctx *unsynced_pipes[MAX_PIPES] = { NULL }; + + for (i = 0; i < pipe_count; i++) { + if (!ctx->res_ctx.pipe_ctx[i].stream || ctx->res_ctx.pipe_ctx[i].top_pipe) + continue; + + unsynced_pipes[i] = &ctx->res_ctx.pipe_ctx[i]; + } + + for (i = 0; i < pipe_count; i++) { + int group_size = 1; + struct pipe_ctx *pipe_set[MAX_PIPES]; + + if (!unsynced_pipes[i]) + continue; + + pipe_set[0] = unsynced_pipes[i]; + unsynced_pipes[i] = NULL; + + /* Add tg to the set, search rest of the tg's for ones with + * same timing, add all tgs with same timing to the group + */ + for (j = i + 1; j < pipe_count; j++) { + if (!unsynced_pipes[j]) + continue; + + if (resource_are_streams_timing_synchronizable( + unsynced_pipes[j]->stream, + pipe_set[0]->stream)) { + pipe_set[group_size] = unsynced_pipes[j]; + unsynced_pipes[j] = NULL; + group_size++; + } + } + + /* set first unblanked pipe as master */ + for (j = 0; j < group_size; j++) { + struct pipe_ctx *temp; + + if (!pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) { + if (j == 0) + break; + + temp = pipe_set[0]; + pipe_set[0] = pipe_set[j]; + pipe_set[j] = temp; + break; + } + } + + /* remove any other unblanked pipes as they have already been synced */ + for (j = j + 1; j < group_size; j++) { + if (!pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) { + group_size--; + pipe_set[j] = pipe_set[group_size]; + j--; + } + } + + if (group_size > 1) { + dc->hwss.enable_timing_synchronization( + dc, group_index, group_size, pipe_set); + group_index++; + } + } +} + +static bool context_changed( + struct dc *dc, + struct dc_state *context) +{ + uint8_t i; + + if (context->stream_count != dc->current_state->stream_count) + return true; + + for (i = 0; i < dc->current_state->stream_count; i++) { + if (dc->current_state->streams[i] != context->streams[i]) + return true; + } + + return false; +} + +bool dc_enable_stereo( + struct dc *dc, + struct dc_state *context, + struct dc_stream_state *streams[], + uint8_t stream_count) +{ + bool ret = true; + int i, j; + struct pipe_ctx *pipe; + + for (i = 0; i < MAX_PIPES; i++) { + if (context != NULL) + pipe = &context->res_ctx.pipe_ctx[i]; + else + pipe = &dc->current_state->res_ctx.pipe_ctx[i]; + for (j = 0 ; pipe && j < stream_count; j++) { + if (streams[j] && streams[j] == pipe->stream && + dc->hwss.setup_stereo) + dc->hwss.setup_stereo(pipe, dc); + } + } + + return ret; +} + + +/* + * Applies given context to HW and copy it into current context. + * It's up to the user to release the src context afterwards. + */ +static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *context) +{ + struct dc_bios *dcb = dc->ctx->dc_bios; + enum dc_status result = DC_ERROR_UNEXPECTED; + struct pipe_ctx *pipe; + int i, j, k, l; + struct dc_stream_state *dc_streams[MAX_STREAMS] = {0}; + + disable_dangling_plane(dc, context); + + for (i = 0; i < context->stream_count; i++) + dc_streams[i] = context->streams[i]; + + if (!dcb->funcs->is_accelerated_mode(dcb)) + dc->hwss.enable_accelerated_mode(dc); + + for (i = 0; i < context->stream_count; i++) { + const struct dc_sink *sink = context->streams[i]->sink; + + dc->hwss.apply_ctx_for_surface( + dc, context->streams[i], + context->stream_status[i].plane_count, + context); + + /* + * enable stereo + * TODO rework dc_enable_stereo call to work with validation sets? + */ + for (k = 0; k < MAX_PIPES; k++) { + pipe = &context->res_ctx.pipe_ctx[k]; + + for (l = 0 ; pipe && l < context->stream_count; l++) { + if (context->streams[l] && + context->streams[l] == pipe->stream && + dc->hwss.setup_stereo) + dc->hwss.setup_stereo(pipe, dc); + } + } + + CONN_MSG_MODE(sink->link, "{%dx%d, %dx%d@%dKhz}", + context->streams[i]->timing.h_addressable, + context->streams[i]->timing.v_addressable, + context->streams[i]->timing.h_total, + context->streams[i]->timing.v_total, + context->streams[i]->timing.pix_clk_khz); + } + + dc->hwss.ready_shared_resources(dc, context); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + pipe = &context->res_ctx.pipe_ctx[i]; + dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe); + } + result = dc->hwss.apply_ctx_to_hw(dc, context); + + program_timing_sync(dc, context); + + dc_enable_stereo(dc, context, dc_streams, context->stream_count); + + for (i = 0; i < context->stream_count; i++) { + for (j = 0; j < MAX_PIPES; j++) { + pipe = &context->res_ctx.pipe_ctx[j]; + + if (!pipe->top_pipe && pipe->stream == context->streams[i]) + dc->hwss.pipe_control_lock(dc, pipe, false); + } + } + + dc_release_state(dc->current_state); + + dc->current_state = context; + + dc_retain_state(dc->current_state); + + dc->hwss.optimize_shared_resources(dc); + + return result; +} + +bool dc_commit_state(struct dc *dc, struct dc_state *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + int i; + + if (false == context_changed(dc, context)) + return DC_OK; + + dm_logger_write(dc->ctx->logger, LOG_DC, "%s: %d streams\n", + __func__, context->stream_count); + + for (i = 0; i < context->stream_count; i++) { + struct dc_stream_state *stream = context->streams[i]; + + dc_stream_log(stream, + dc->ctx->logger, + LOG_DC); + } + + result = dc_commit_state_no_check(dc, context); + + return (result == DC_OK); +} + + +bool dc_post_update_surfaces_to_stream(struct dc *dc) +{ + int i; + struct dc_state *context = dc->current_state; + + post_surface_trace(dc); + + for (i = 0; i < dc->res_pool->pipe_count; i++) + if (context->res_ctx.pipe_ctx[i].stream == NULL + || context->res_ctx.pipe_ctx[i].plane_state == NULL) + dc->hwss.power_down_front_end(dc, i); + + /* 3rd param should be true, temp w/a for RV*/ +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + dc->hwss.set_bandwidth(dc, context, dc->ctx->dce_version < DCN_VERSION_1_0); +#else + dc->hwss.set_bandwidth(dc, context, true); +#endif + return true; +} + +/* + * TODO this whole function needs to go + * + * dc_surface_update is needlessly complex. See if we can just replace this + * with a dc_plane_state and follow the atomic model a bit more closely here. + */ +bool dc_commit_planes_to_stream( + struct dc *dc, + struct dc_plane_state **plane_states, + uint8_t new_plane_count, + struct dc_stream_state *dc_stream, + struct dc_state *state) +{ + /* no need to dynamically allocate this. it's pretty small */ + struct dc_surface_update updates[MAX_SURFACES]; + struct dc_flip_addrs *flip_addr; + struct dc_plane_info *plane_info; + struct dc_scaling_info *scaling_info; + int i; + struct dc_stream_update *stream_update = + kzalloc(sizeof(struct dc_stream_update), GFP_KERNEL); + + if (!stream_update) { + BREAK_TO_DEBUGGER(); + return false; + } + + flip_addr = kcalloc(MAX_SURFACES, sizeof(struct dc_flip_addrs), + GFP_KERNEL); + plane_info = kcalloc(MAX_SURFACES, sizeof(struct dc_plane_info), + GFP_KERNEL); + scaling_info = kcalloc(MAX_SURFACES, sizeof(struct dc_scaling_info), + GFP_KERNEL); + + if (!flip_addr || !plane_info || !scaling_info) { + kfree(flip_addr); + kfree(plane_info); + kfree(scaling_info); + kfree(stream_update); + return false; + } + + memset(updates, 0, sizeof(updates)); + + stream_update->src = dc_stream->src; + stream_update->dst = dc_stream->dst; + stream_update->out_transfer_func = dc_stream->out_transfer_func; + + for (i = 0; i < new_plane_count; i++) { + updates[i].surface = plane_states[i]; + updates[i].gamma = + (struct dc_gamma *)plane_states[i]->gamma_correction; + updates[i].in_transfer_func = plane_states[i]->in_transfer_func; + flip_addr[i].address = plane_states[i]->address; + flip_addr[i].flip_immediate = plane_states[i]->flip_immediate; + plane_info[i].color_space = plane_states[i]->color_space; + plane_info[i].format = plane_states[i]->format; + plane_info[i].plane_size = plane_states[i]->plane_size; + plane_info[i].rotation = plane_states[i]->rotation; + plane_info[i].horizontal_mirror = plane_states[i]->horizontal_mirror; + plane_info[i].stereo_format = plane_states[i]->stereo_format; + plane_info[i].tiling_info = plane_states[i]->tiling_info; + plane_info[i].visible = plane_states[i]->visible; + plane_info[i].per_pixel_alpha = plane_states[i]->per_pixel_alpha; + plane_info[i].dcc = plane_states[i]->dcc; + scaling_info[i].scaling_quality = plane_states[i]->scaling_quality; + scaling_info[i].src_rect = plane_states[i]->src_rect; + scaling_info[i].dst_rect = plane_states[i]->dst_rect; + scaling_info[i].clip_rect = plane_states[i]->clip_rect; + + updates[i].flip_addr = &flip_addr[i]; + updates[i].plane_info = &plane_info[i]; + updates[i].scaling_info = &scaling_info[i]; + } + + dc_commit_updates_for_stream( + dc, + updates, + new_plane_count, + dc_stream, stream_update, plane_states, state); + + kfree(flip_addr); + kfree(plane_info); + kfree(scaling_info); + kfree(stream_update); + return true; +} + +struct dc_state *dc_create_state(void) +{ + struct dc_state *context = kzalloc(sizeof(struct dc_state), + GFP_KERNEL); + + if (!context) + return NULL; + + kref_init(&context->refcount); + return context; +} + +void dc_retain_state(struct dc_state *context) +{ + kref_get(&context->refcount); +} + +static void dc_state_free(struct kref *kref) +{ + struct dc_state *context = container_of(kref, struct dc_state, refcount); + dc_resource_state_destruct(context); + kfree(context); +} + +void dc_release_state(struct dc_state *context) +{ + kref_put(&context->refcount, dc_state_free); +} + +static bool is_surface_in_context( + const struct dc_state *context, + const struct dc_plane_state *plane_state) +{ + int j; + + for (j = 0; j < MAX_PIPES; j++) { + const struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + if (plane_state == pipe_ctx->plane_state) { + return true; + } + } + + return false; +} + +static unsigned int pixel_format_to_bpp(enum surface_pixel_format format) +{ + switch (format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + return 12; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + return 16; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + return 32; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + return 64; + default: + ASSERT_CRITICAL(false); + return -1; + } +} + +static enum surface_update_type get_plane_info_update_type( + const struct dc_surface_update *u, + int surface_index) +{ + struct dc_plane_info temp_plane_info; + memset(&temp_plane_info, 0, sizeof(temp_plane_info)); + + if (!u->plane_info) + return UPDATE_TYPE_FAST; + + temp_plane_info = *u->plane_info; + + /* Copy all parameters that will cause a full update + * from current surface, the rest of the parameters + * from provided plane configuration. + * Perform memory compare and special validation + * for those that can cause fast/medium updates + */ + + /* Full update parameters */ + temp_plane_info.color_space = u->surface->color_space; + temp_plane_info.dcc = u->surface->dcc; + temp_plane_info.horizontal_mirror = u->surface->horizontal_mirror; + temp_plane_info.plane_size = u->surface->plane_size; + temp_plane_info.rotation = u->surface->rotation; + temp_plane_info.stereo_format = u->surface->stereo_format; + + if (surface_index == 0) + temp_plane_info.visible = u->plane_info->visible; + else + temp_plane_info.visible = u->surface->visible; + + if (memcmp(u->plane_info, &temp_plane_info, + sizeof(struct dc_plane_info)) != 0) + return UPDATE_TYPE_FULL; + + if (pixel_format_to_bpp(u->plane_info->format) != + pixel_format_to_bpp(u->surface->format)) { + /* different bytes per element will require full bandwidth + * and DML calculation + */ + return UPDATE_TYPE_FULL; + } + + if (memcmp(&u->plane_info->tiling_info, &u->surface->tiling_info, + sizeof(union dc_tiling_info)) != 0) { + /* todo: below are HW dependent, we should add a hook to + * DCE/N resource and validated there. + */ + if (u->plane_info->tiling_info.gfx9.swizzle != DC_SW_LINEAR) { + /* swizzled mode requires RQ to be setup properly, + * thus need to run DML to calculate RQ settings + */ + return UPDATE_TYPE_FULL; + } + } + + return UPDATE_TYPE_MED; +} + +static enum surface_update_type get_scaling_info_update_type( + const struct dc_surface_update *u) +{ + if (!u->scaling_info) + return UPDATE_TYPE_FAST; + + if (u->scaling_info->src_rect.width != u->surface->src_rect.width + || u->scaling_info->src_rect.height != u->surface->src_rect.height + || u->scaling_info->clip_rect.width != u->surface->clip_rect.width + || u->scaling_info->clip_rect.height != u->surface->clip_rect.height + || u->scaling_info->dst_rect.width != u->surface->dst_rect.width + || u->scaling_info->dst_rect.height != u->surface->dst_rect.height) + return UPDATE_TYPE_FULL; + + if (u->scaling_info->src_rect.x != u->surface->src_rect.x + || u->scaling_info->src_rect.y != u->surface->src_rect.y + || u->scaling_info->clip_rect.x != u->surface->clip_rect.x + || u->scaling_info->clip_rect.y != u->surface->clip_rect.y + || u->scaling_info->dst_rect.x != u->surface->dst_rect.x + || u->scaling_info->dst_rect.y != u->surface->dst_rect.y) + return UPDATE_TYPE_MED; + + return UPDATE_TYPE_FAST; +} + +static enum surface_update_type det_surface_update( + const struct dc *dc, + const struct dc_surface_update *u, + int surface_index) +{ + const struct dc_state *context = dc->current_state; + enum surface_update_type type = UPDATE_TYPE_FAST; + enum surface_update_type overall_type = UPDATE_TYPE_FAST; + + if (!is_surface_in_context(context, u->surface)) + return UPDATE_TYPE_FULL; + + type = get_plane_info_update_type(u, surface_index); + if (overall_type < type) + overall_type = type; + + type = get_scaling_info_update_type(u); + if (overall_type < type) + overall_type = type; + + if (u->in_transfer_func || + u->hdr_static_metadata) { + if (overall_type < UPDATE_TYPE_MED) + overall_type = UPDATE_TYPE_MED; + } + + return overall_type; +} + +enum surface_update_type dc_check_update_surfaces_for_stream( + struct dc *dc, + struct dc_surface_update *updates, + int surface_count, + struct dc_stream_update *stream_update, + const struct dc_stream_status *stream_status) +{ + int i; + enum surface_update_type overall_type = UPDATE_TYPE_FAST; + + if (stream_status == NULL || stream_status->plane_count != surface_count) + return UPDATE_TYPE_FULL; + + if (stream_update) + return UPDATE_TYPE_FULL; + + for (i = 0 ; i < surface_count; i++) { + enum surface_update_type type = + det_surface_update(dc, &updates[i], i); + + if (type == UPDATE_TYPE_FULL) + return type; + + if (overall_type < type) + overall_type = type; + } + + return overall_type; +} + +static struct dc_stream_status *stream_get_status( + struct dc_state *ctx, + struct dc_stream_state *stream) +{ + uint8_t i; + + for (i = 0; i < ctx->stream_count; i++) { + if (stream == ctx->streams[i]) { + return &ctx->stream_status[i]; + } + } + + return NULL; +} + +static const enum surface_update_type update_surface_trace_level = UPDATE_TYPE_FULL; + + +static void commit_planes_for_stream(struct dc *dc, + struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update, + enum surface_update_type update_type, + struct dc_state *context) +{ + int i, j; + + if (update_type == UPDATE_TYPE_FULL) { + dc->hwss.set_bandwidth(dc, context, false); + context_clock_trace(dc, context); + } + + if (update_type > UPDATE_TYPE_FAST) { + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx); + } + } + + if (surface_count == 0) { + /* + * In case of turning off screen, no need to program front end a second time. + * just return after program front end. + */ + dc->hwss.apply_ctx_for_surface(dc, stream, surface_count, context); + return; + } + + /* Lock pipes for provided surfaces, or all active if full update*/ + for (i = 0; i < surface_count; i++) { + struct dc_plane_state *plane_state = srf_updates[i].surface; + + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + if (update_type != UPDATE_TYPE_FULL && pipe_ctx->plane_state != plane_state) + continue; + if (!pipe_ctx->plane_state || pipe_ctx->top_pipe) + continue; + + dc->hwss.pipe_control_lock( + dc, + pipe_ctx, + true); + } + if (update_type == UPDATE_TYPE_FULL) + break; + } + + /* Full fe update*/ + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + if (update_type != UPDATE_TYPE_FULL || !pipe_ctx->plane_state) + continue; + + if (!pipe_ctx->top_pipe && pipe_ctx->stream) { + struct dc_stream_status *stream_status = stream_get_status(context, pipe_ctx->stream); + + dc->hwss.apply_ctx_for_surface( + dc, pipe_ctx->stream, stream_status->plane_count, context); + } + } + + if (update_type > UPDATE_TYPE_FAST) + context_timing_trace(dc, &context->res_ctx); + + /* Perform requested Updates */ + for (i = 0; i < surface_count; i++) { + struct dc_plane_state *plane_state = srf_updates[i].surface; + + if (update_type == UPDATE_TYPE_MED) + dc->hwss.apply_ctx_for_surface( + dc, stream, surface_count, context); + + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + if (pipe_ctx->plane_state != plane_state) + continue; + + if (srf_updates[i].flip_addr) + dc->hwss.update_plane_addr(dc, pipe_ctx); + + if (update_type == UPDATE_TYPE_FAST) + continue; + + /* work around to program degamma regs for split pipe after set mode. */ + if (srf_updates[i].in_transfer_func || (pipe_ctx->top_pipe && + pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state)) + dc->hwss.set_input_transfer_func( + pipe_ctx, pipe_ctx->plane_state); + + if (stream_update != NULL && + stream_update->out_transfer_func != NULL) { + dc->hwss.set_output_transfer_func( + pipe_ctx, pipe_ctx->stream); + } + + if (srf_updates[i].hdr_static_metadata) { + resource_build_info_frame(pipe_ctx); + dc->hwss.update_info_frame(pipe_ctx); + } + } + } + + /* Unlock pipes */ + for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + for (j = 0; j < surface_count; j++) { + if (update_type != UPDATE_TYPE_FULL && + srf_updates[j].surface != pipe_ctx->plane_state) + continue; + if (!pipe_ctx->plane_state || pipe_ctx->top_pipe) + continue; + + dc->hwss.pipe_control_lock( + dc, + pipe_ctx, + false); + + break; + } + } +} + +void dc_commit_updates_for_stream(struct dc *dc, + struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update, + struct dc_plane_state **plane_states, + struct dc_state *state) +{ + const struct dc_stream_status *stream_status; + enum surface_update_type update_type; + struct dc_state *context; + struct dc_context *dc_ctx = dc->ctx; + int i, j; + + stream_status = dc_stream_get_status(stream); + context = dc->current_state; + + update_type = dc_check_update_surfaces_for_stream( + dc, srf_updates, surface_count, stream_update, stream_status); + + if (update_type >= update_surface_trace_level) + update_surface_trace(dc, srf_updates, surface_count); + + + if (update_type >= UPDATE_TYPE_FULL) { + + /* initialize scratch memory for building context */ + context = dc_create_state(); + if (context == NULL) { + DC_ERROR("Failed to allocate new validate context!\n"); + return; + } + + dc_resource_state_copy_construct(state, context); + } + + + for (i = 0; i < surface_count; i++) { + struct dc_plane_state *surface = srf_updates[i].surface; + + /* TODO: On flip we don't build the state, so it still has the + * old address. Which is why we are updating the address here + */ + if (srf_updates[i].flip_addr) { + surface->address = srf_updates[i].flip_addr->address; + surface->flip_immediate = srf_updates[i].flip_addr->flip_immediate; + + } + + if (update_type >= UPDATE_TYPE_MED) { + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + if (pipe_ctx->plane_state != surface) + continue; + + resource_build_scaling_params(pipe_ctx); + } + } + } + + commit_planes_for_stream( + dc, + srf_updates, + surface_count, + stream, + stream_update, + update_type, + context); + + if (update_type >= UPDATE_TYPE_FULL) + dc_post_update_surfaces_to_stream(dc); + + if (dc->current_state != context) { + + struct dc_state *old = dc->current_state; + + dc->current_state = context; + dc_release_state(old); + + } + + return; + +} + +uint8_t dc_get_current_stream_count(struct dc *dc) +{ + return dc->current_state->stream_count; +} + +struct dc_stream_state *dc_get_stream_at_index(struct dc *dc, uint8_t i) +{ + if (i < dc->current_state->stream_count) + return dc->current_state->streams[i]; + return NULL; +} + +enum dc_irq_source dc_interrupt_to_irq_source( + struct dc *dc, + uint32_t src_id, + uint32_t ext_id) +{ + return dal_irq_service_to_irq_source(dc->res_pool->irqs, src_id, ext_id); +} + +void dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable) +{ + + if (dc == NULL) + return; + + dal_irq_service_set(dc->res_pool->irqs, src, enable); +} + +void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src) +{ + dal_irq_service_ack(dc->res_pool->irqs, src); +} + +void dc_set_power_state( + struct dc *dc, + enum dc_acpi_cm_power_state power_state) +{ + struct kref refcount; + + switch (power_state) { + case DC_ACPI_CM_POWER_STATE_D0: + dc_resource_state_construct(dc, dc->current_state); + + dc->hwss.init_hw(dc); + break; + default: + + dc->hwss.power_down(dc); + + /* Zero out the current context so that on resume we start with + * clean state, and dc hw programming optimizations will not + * cause any trouble. + */ + + /* Preserve refcount */ + refcount = dc->current_state->refcount; + dc_resource_state_destruct(dc->current_state); + memset(dc->current_state, 0, + sizeof(*dc->current_state)); + + dc->current_state->refcount = refcount; + + break; + } + +} + +void dc_resume(struct dc *dc) +{ + + uint32_t i; + + for (i = 0; i < dc->link_count; i++) + core_link_resume(dc->links[i]); +} + +bool dc_submit_i2c( + struct dc *dc, + uint32_t link_index, + struct i2c_command *cmd) +{ + + struct dc_link *link = dc->links[link_index]; + struct ddc_service *ddc = link->ddc; + + return dal_i2caux_submit_i2c_command( + ddc->ctx->i2caux, + ddc->ddc_pin, + cmd); +} + +static bool link_add_remote_sink_helper(struct dc_link *dc_link, struct dc_sink *sink) +{ + if (dc_link->sink_count >= MAX_SINKS_PER_LINK) { + BREAK_TO_DEBUGGER(); + return false; + } + + dc_sink_retain(sink); + + dc_link->remote_sinks[dc_link->sink_count] = sink; + dc_link->sink_count++; + + return true; +} + +struct dc_sink *dc_link_add_remote_sink( + struct dc_link *link, + const uint8_t *edid, + int len, + struct dc_sink_init_data *init_data) +{ + struct dc_sink *dc_sink; + enum dc_edid_status edid_status; + + if (len > MAX_EDID_BUFFER_SIZE) { + dm_error("Max EDID buffer size breached!\n"); + return NULL; + } + + if (!init_data) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (!init_data->link) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dc_sink = dc_sink_create(init_data); + + if (!dc_sink) + return NULL; + + memmove(dc_sink->dc_edid.raw_edid, edid, len); + dc_sink->dc_edid.length = len; + + if (!link_add_remote_sink_helper( + link, + dc_sink)) + goto fail_add_sink; + + edid_status = dm_helpers_parse_edid_caps( + link->ctx, + &dc_sink->dc_edid, + &dc_sink->edid_caps); + + if (edid_status != EDID_OK) + goto fail; + + return dc_sink; +fail: + dc_link_remove_remote_sink(link, dc_sink); +fail_add_sink: + dc_sink_release(dc_sink); + return NULL; +} + +void dc_link_remove_remote_sink(struct dc_link *link, struct dc_sink *sink) +{ + int i; + + if (!link->sink_count) { + BREAK_TO_DEBUGGER(); + return; + } + + for (i = 0; i < link->sink_count; i++) { + if (link->remote_sinks[i] == sink) { + dc_sink_release(sink); + link->remote_sinks[i] = NULL; + + /* shrink array to remove empty place */ + while (i < link->sink_count - 1) { + link->remote_sinks[i] = link->remote_sinks[i+1]; + i++; + } + link->remote_sinks[i] = NULL; + link->sink_count--; + return; + } + } +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c new file mode 100644 index 000000000000..6acee5426e4b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c @@ -0,0 +1,359 @@ +/* + * dc_debug.c + * + * Created on: Nov 3, 2016 + * Author: yonsun + */ + +#include "dm_services.h" + +#include "dc.h" + +#include "core_status.h" +#include "core_types.h" +#include "hw_sequencer.h" + +#include "resource.h" + +#define SURFACE_TRACE(...) do {\ + if (dc->debug.surface_trace) \ + dm_logger_write(logger, \ + LOG_IF_TRACE, \ + ##__VA_ARGS__); \ +} while (0) + +#define TIMING_TRACE(...) do {\ + if (dc->debug.timing_trace) \ + dm_logger_write(logger, \ + LOG_SYNC, \ + ##__VA_ARGS__); \ +} while (0) + +#define CLOCK_TRACE(...) do {\ + if (dc->debug.clock_trace) \ + dm_logger_write(logger, \ + LOG_BANDWIDTH_CALCS, \ + ##__VA_ARGS__); \ +} while (0) + +void pre_surface_trace( + struct dc *dc, + const struct dc_plane_state *const *plane_states, + int surface_count) +{ + int i; + struct dc *core_dc = dc; + struct dal_logger *logger = core_dc->ctx->logger; + + for (i = 0; i < surface_count; i++) { + const struct dc_plane_state *plane_state = plane_states[i]; + + SURFACE_TRACE("Planes %d:\n", i); + + SURFACE_TRACE( + "plane_state->visible = %d;\n" + "plane_state->flip_immediate = %d;\n" + "plane_state->address.type = %d;\n" + "plane_state->address.grph.addr.quad_part = 0x%X;\n" + "plane_state->address.grph.meta_addr.quad_part = 0x%X;\n" + "plane_state->scaling_quality.h_taps = %d;\n" + "plane_state->scaling_quality.v_taps = %d;\n" + "plane_state->scaling_quality.h_taps_c = %d;\n" + "plane_state->scaling_quality.v_taps_c = %d;\n", + plane_state->visible, + plane_state->flip_immediate, + plane_state->address.type, + plane_state->address.grph.addr.quad_part, + plane_state->address.grph.meta_addr.quad_part, + plane_state->scaling_quality.h_taps, + plane_state->scaling_quality.v_taps, + plane_state->scaling_quality.h_taps_c, + plane_state->scaling_quality.v_taps_c); + + SURFACE_TRACE( + "plane_state->src_rect.x = %d;\n" + "plane_state->src_rect.y = %d;\n" + "plane_state->src_rect.width = %d;\n" + "plane_state->src_rect.height = %d;\n" + "plane_state->dst_rect.x = %d;\n" + "plane_state->dst_rect.y = %d;\n" + "plane_state->dst_rect.width = %d;\n" + "plane_state->dst_rect.height = %d;\n" + "plane_state->clip_rect.x = %d;\n" + "plane_state->clip_rect.y = %d;\n" + "plane_state->clip_rect.width = %d;\n" + "plane_state->clip_rect.height = %d;\n", + plane_state->src_rect.x, + plane_state->src_rect.y, + plane_state->src_rect.width, + plane_state->src_rect.height, + plane_state->dst_rect.x, + plane_state->dst_rect.y, + plane_state->dst_rect.width, + plane_state->dst_rect.height, + plane_state->clip_rect.x, + plane_state->clip_rect.y, + plane_state->clip_rect.width, + plane_state->clip_rect.height); + + SURFACE_TRACE( + "plane_state->plane_size.grph.surface_size.x = %d;\n" + "plane_state->plane_size.grph.surface_size.y = %d;\n" + "plane_state->plane_size.grph.surface_size.width = %d;\n" + "plane_state->plane_size.grph.surface_size.height = %d;\n" + "plane_state->plane_size.grph.surface_pitch = %d;\n", + plane_state->plane_size.grph.surface_size.x, + plane_state->plane_size.grph.surface_size.y, + plane_state->plane_size.grph.surface_size.width, + plane_state->plane_size.grph.surface_size.height, + plane_state->plane_size.grph.surface_pitch); + + + SURFACE_TRACE( + "plane_state->tiling_info.gfx8.num_banks = %d;\n" + "plane_state->tiling_info.gfx8.bank_width = %d;\n" + "plane_state->tiling_info.gfx8.bank_width_c = %d;\n" + "plane_state->tiling_info.gfx8.bank_height = %d;\n" + "plane_state->tiling_info.gfx8.bank_height_c = %d;\n" + "plane_state->tiling_info.gfx8.tile_aspect = %d;\n" + "plane_state->tiling_info.gfx8.tile_aspect_c = %d;\n" + "plane_state->tiling_info.gfx8.tile_split = %d;\n" + "plane_state->tiling_info.gfx8.tile_split_c = %d;\n" + "plane_state->tiling_info.gfx8.tile_mode = %d;\n" + "plane_state->tiling_info.gfx8.tile_mode_c = %d;\n", + plane_state->tiling_info.gfx8.num_banks, + plane_state->tiling_info.gfx8.bank_width, + plane_state->tiling_info.gfx8.bank_width_c, + plane_state->tiling_info.gfx8.bank_height, + plane_state->tiling_info.gfx8.bank_height_c, + plane_state->tiling_info.gfx8.tile_aspect, + plane_state->tiling_info.gfx8.tile_aspect_c, + plane_state->tiling_info.gfx8.tile_split, + plane_state->tiling_info.gfx8.tile_split_c, + plane_state->tiling_info.gfx8.tile_mode, + plane_state->tiling_info.gfx8.tile_mode_c); + + SURFACE_TRACE( + "plane_state->tiling_info.gfx8.pipe_config = %d;\n" + "plane_state->tiling_info.gfx8.array_mode = %d;\n" + "plane_state->color_space = %d;\n" + "plane_state->dcc.enable = %d;\n" + "plane_state->format = %d;\n" + "plane_state->rotation = %d;\n" + "plane_state->stereo_format = %d;\n", + plane_state->tiling_info.gfx8.pipe_config, + plane_state->tiling_info.gfx8.array_mode, + plane_state->color_space, + plane_state->dcc.enable, + plane_state->format, + plane_state->rotation, + plane_state->stereo_format); + + SURFACE_TRACE("plane_state->tiling_info.gfx9.swizzle = %d;\n", + plane_state->tiling_info.gfx9.swizzle); + + SURFACE_TRACE("\n"); + } + SURFACE_TRACE("\n"); +} + +void update_surface_trace( + struct dc *dc, + const struct dc_surface_update *updates, + int surface_count) +{ + int i; + struct dc *core_dc = dc; + struct dal_logger *logger = core_dc->ctx->logger; + + for (i = 0; i < surface_count; i++) { + const struct dc_surface_update *update = &updates[i]; + + SURFACE_TRACE("Update %d\n", i); + if (update->flip_addr) { + SURFACE_TRACE("flip_addr->address.type = %d;\n" + "flip_addr->address.grph.addr.quad_part = 0x%X;\n" + "flip_addr->address.grph.meta_addr.quad_part = 0x%X;\n" + "flip_addr->flip_immediate = %d;\n", + update->flip_addr->address.type, + update->flip_addr->address.grph.addr.quad_part, + update->flip_addr->address.grph.meta_addr.quad_part, + update->flip_addr->flip_immediate); + } + + if (update->plane_info) { + SURFACE_TRACE( + "plane_info->color_space = %d;\n" + "plane_info->format = %d;\n" + "plane_info->plane_size.grph.surface_pitch = %d;\n" + "plane_info->plane_size.grph.surface_size.height = %d;\n" + "plane_info->plane_size.grph.surface_size.width = %d;\n" + "plane_info->plane_size.grph.surface_size.x = %d;\n" + "plane_info->plane_size.grph.surface_size.y = %d;\n" + "plane_info->rotation = %d;\n", + update->plane_info->color_space, + update->plane_info->format, + update->plane_info->plane_size.grph.surface_pitch, + update->plane_info->plane_size.grph.surface_size.height, + update->plane_info->plane_size.grph.surface_size.width, + update->plane_info->plane_size.grph.surface_size.x, + update->plane_info->plane_size.grph.surface_size.y, + update->plane_info->rotation, + update->plane_info->stereo_format); + + SURFACE_TRACE( + "plane_info->tiling_info.gfx8.num_banks = %d;\n" + "plane_info->tiling_info.gfx8.bank_width = %d;\n" + "plane_info->tiling_info.gfx8.bank_width_c = %d;\n" + "plane_info->tiling_info.gfx8.bank_height = %d;\n" + "plane_info->tiling_info.gfx8.bank_height_c = %d;\n" + "plane_info->tiling_info.gfx8.tile_aspect = %d;\n" + "plane_info->tiling_info.gfx8.tile_aspect_c = %d;\n" + "plane_info->tiling_info.gfx8.tile_split = %d;\n" + "plane_info->tiling_info.gfx8.tile_split_c = %d;\n" + "plane_info->tiling_info.gfx8.tile_mode = %d;\n" + "plane_info->tiling_info.gfx8.tile_mode_c = %d;\n", + update->plane_info->tiling_info.gfx8.num_banks, + update->plane_info->tiling_info.gfx8.bank_width, + update->plane_info->tiling_info.gfx8.bank_width_c, + update->plane_info->tiling_info.gfx8.bank_height, + update->plane_info->tiling_info.gfx8.bank_height_c, + update->plane_info->tiling_info.gfx8.tile_aspect, + update->plane_info->tiling_info.gfx8.tile_aspect_c, + update->plane_info->tiling_info.gfx8.tile_split, + update->plane_info->tiling_info.gfx8.tile_split_c, + update->plane_info->tiling_info.gfx8.tile_mode, + update->plane_info->tiling_info.gfx8.tile_mode_c); + + SURFACE_TRACE( + "plane_info->tiling_info.gfx8.pipe_config = %d;\n" + "plane_info->tiling_info.gfx8.array_mode = %d;\n" + "plane_info->visible = %d;\n" + "plane_info->per_pixel_alpha = %d;\n", + update->plane_info->tiling_info.gfx8.pipe_config, + update->plane_info->tiling_info.gfx8.array_mode, + update->plane_info->visible, + update->plane_info->per_pixel_alpha); + + SURFACE_TRACE("surface->tiling_info.gfx9.swizzle = %d;\n", + update->plane_info->tiling_info.gfx9.swizzle); + } + + if (update->scaling_info) { + SURFACE_TRACE( + "scaling_info->src_rect.x = %d;\n" + "scaling_info->src_rect.y = %d;\n" + "scaling_info->src_rect.width = %d;\n" + "scaling_info->src_rect.height = %d;\n" + "scaling_info->dst_rect.x = %d;\n" + "scaling_info->dst_rect.y = %d;\n" + "scaling_info->dst_rect.width = %d;\n" + "scaling_info->dst_rect.height = %d;\n" + "scaling_info->clip_rect.x = %d;\n" + "scaling_info->clip_rect.y = %d;\n" + "scaling_info->clip_rect.width = %d;\n" + "scaling_info->clip_rect.height = %d;\n" + "scaling_info->scaling_quality.h_taps = %d;\n" + "scaling_info->scaling_quality.v_taps = %d;\n" + "scaling_info->scaling_quality.h_taps_c = %d;\n" + "scaling_info->scaling_quality.v_taps_c = %d;\n", + update->scaling_info->src_rect.x, + update->scaling_info->src_rect.y, + update->scaling_info->src_rect.width, + update->scaling_info->src_rect.height, + update->scaling_info->dst_rect.x, + update->scaling_info->dst_rect.y, + update->scaling_info->dst_rect.width, + update->scaling_info->dst_rect.height, + update->scaling_info->clip_rect.x, + update->scaling_info->clip_rect.y, + update->scaling_info->clip_rect.width, + update->scaling_info->clip_rect.height, + update->scaling_info->scaling_quality.h_taps, + update->scaling_info->scaling_quality.v_taps, + update->scaling_info->scaling_quality.h_taps_c, + update->scaling_info->scaling_quality.v_taps_c); + } + SURFACE_TRACE("\n"); + } + SURFACE_TRACE("\n"); +} + +void post_surface_trace(struct dc *dc) +{ + struct dc *core_dc = dc; + struct dal_logger *logger = core_dc->ctx->logger; + + SURFACE_TRACE("post surface process.\n"); + +} + +void context_timing_trace( + struct dc *dc, + struct resource_context *res_ctx) +{ + int i; + struct dc *core_dc = dc; + struct dal_logger *logger = core_dc->ctx->logger; + int h_pos[MAX_PIPES], v_pos[MAX_PIPES]; + struct crtc_position position; + unsigned int underlay_idx = core_dc->res_pool->underlay_pipe_index; + + + for (i = 0; i < core_dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + /* get_position() returns CRTC vertical/horizontal counter + * hence not applicable for underlay pipe + */ + if (pipe_ctx->stream == NULL + || pipe_ctx->pipe_idx == underlay_idx) + continue; + + pipe_ctx->stream_res.tg->funcs->get_position(pipe_ctx->stream_res.tg, &position); + h_pos[i] = position.horizontal_count; + v_pos[i] = position.vertical_count; + } + for (i = 0; i < core_dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + TIMING_TRACE("OTG_%d H_tot:%d V_tot:%d H_pos:%d V_pos:%d\n", + pipe_ctx->stream_res.tg->inst, + pipe_ctx->stream->timing.h_total, + pipe_ctx->stream->timing.v_total, + h_pos[i], v_pos[i]); + } +} + +void context_clock_trace( + struct dc *dc, + struct dc_state *context) +{ +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + struct dc *core_dc = dc; + struct dal_logger *logger = core_dc->ctx->logger; + + CLOCK_TRACE("Current: dispclk_khz:%d dppclk_div:%d dcfclk_khz:%d\n" + "dcfclk_deep_sleep_khz:%d fclk_khz:%d\n" + "dram_ccm_us:%d min_active_dram_ccm_us:%d\n", + context->bw.dcn.calc_clk.dispclk_khz, + context->bw.dcn.calc_clk.dppclk_div, + context->bw.dcn.calc_clk.dcfclk_khz, + context->bw.dcn.calc_clk.dcfclk_deep_sleep_khz, + context->bw.dcn.calc_clk.fclk_khz, + context->bw.dcn.calc_clk.dram_ccm_us, + context->bw.dcn.calc_clk.min_active_dram_ccm_us); + CLOCK_TRACE("Calculated: dispclk_khz:%d dppclk_div:%d dcfclk_khz:%d\n" + "dcfclk_deep_sleep_khz:%d fclk_khz:%d\n" + "dram_ccm_us:%d min_active_dram_ccm_us:%d\n", + context->bw.dcn.calc_clk.dispclk_khz, + context->bw.dcn.calc_clk.dppclk_div, + context->bw.dcn.calc_clk.dcfclk_khz, + context->bw.dcn.calc_clk.dcfclk_deep_sleep_khz, + context->bw.dcn.calc_clk.fclk_khz, + context->bw.dcn.calc_clk.dram_ccm_us, + context->bw.dcn.calc_clk.min_active_dram_ccm_us); +#endif +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c new file mode 100644 index 000000000000..71993d5983bf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -0,0 +1,101 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "core_types.h" +#include "timing_generator.h" +#include "hw_sequencer.h" + +/* used as index in array of black_color_format */ +enum black_color_format { + BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, + BLACK_COLOR_FORMAT_RGB_LIMITED, + BLACK_COLOR_FORMAT_YUV_TV, + BLACK_COLOR_FORMAT_YUV_CV, + BLACK_COLOR_FORMAT_YUV_SUPER_AA, + BLACK_COLOR_FORMAT_DEBUG, +}; + +static const struct tg_color black_color_format[] = { + /* BlackColorFormat_RGB_FullRange */ + {0, 0, 0}, + /* BlackColorFormat_RGB_Limited */ + {0x40, 0x40, 0x40}, + /* BlackColorFormat_YUV_TV */ + {0x200, 0x40, 0x200}, + /* BlackColorFormat_YUV_CV */ + {0x1f4, 0x40, 0x1f4}, + /* BlackColorFormat_YUV_SuperAA */ + {0x1a2, 0x20, 0x1a2}, + /* visual confirm debug */ + {0xff, 0xff, 0}, +}; + +void color_space_to_black_color( + const struct dc *dc, + enum dc_color_space colorspace, + struct tg_color *black_color) +{ + switch (colorspace) { + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR601_LIMITED: + case COLOR_SPACE_YCBCR709_LIMITED: + *black_color = black_color_format[BLACK_COLOR_FORMAT_YUV_CV]; + break; + + case COLOR_SPACE_SRGB_LIMITED: + *black_color = + black_color_format[BLACK_COLOR_FORMAT_RGB_LIMITED]; + break; + + default: + /* fefault is sRGB black (full range). */ + *black_color = + black_color_format[BLACK_COLOR_FORMAT_RGB_FULLRANGE]; + /* default is sRGB black 0. */ + break; + } +} + +bool hwss_wait_for_blank_complete( + struct timing_generator *tg) +{ + int counter; + + for (counter = 0; counter < 100; counter++) { + if (tg->funcs->is_blanked(tg)) + break; + + msleep(1); + } + + if (counter == 100) { + dm_error("DC: failed to blank crtc!\n"); + return false; + } + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c new file mode 100644 index 000000000000..0602610489d7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -0,0 +1,2367 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "atom.h" +#include "dm_helpers.h" +#include "dc.h" +#include "grph_object_id.h" +#include "gpio_service_interface.h" +#include "core_status.h" +#include "dc_link_dp.h" +#include "dc_link_ddc.h" +#include "link_hwss.h" + +#include "link_encoder.h" +#include "hw_sequencer.h" +#include "resource.h" +#include "abm.h" +#include "fixed31_32.h" +#include "dpcd_defs.h" +#include "dmcu.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_enum.h" +#include "dce/dce_11_0_sh_mask.h" + +#define LINK_INFO(...) \ + dm_logger_write(dc_ctx->logger, LOG_HW_HOTPLUG, \ + __VA_ARGS__) + +/******************************************************************************* + * Private structures + ******************************************************************************/ + +enum { + LINK_RATE_REF_FREQ_IN_MHZ = 27, + PEAK_FACTOR_X1000 = 1006 +}; + +/******************************************************************************* + * Private functions + ******************************************************************************/ +static void destruct(struct dc_link *link) +{ + int i; + + if (link->ddc) + dal_ddc_service_destroy(&link->ddc); + + if(link->link_enc) + link->link_enc->funcs->destroy(&link->link_enc); + + if (link->local_sink) + dc_sink_release(link->local_sink); + + for (i = 0; i < link->sink_count; ++i) + dc_sink_release(link->remote_sinks[i]); +} + +struct gpio *get_hpd_gpio(struct dc_bios *dcb, + struct graphics_object_id link_id, + struct gpio_service *gpio_service) +{ + enum bp_result bp_result; + struct graphics_object_hpd_info hpd_info; + struct gpio_pin_info pin_info; + + if (dcb->funcs->get_hpd_info(dcb, link_id, &hpd_info) != BP_RESULT_OK) + return NULL; + + bp_result = dcb->funcs->get_gpio_pin_info(dcb, + hpd_info.hpd_int_gpio_uid, &pin_info); + + if (bp_result != BP_RESULT_OK) { + ASSERT(bp_result == BP_RESULT_NORECORD); + return NULL; + } + + return dal_gpio_service_create_irq( + gpio_service, + pin_info.offset, + pin_info.mask); +} + +/* + * Function: program_hpd_filter + * + * @brief + * Programs HPD filter on associated HPD line + * + * @param [in] delay_on_connect_in_ms: Connect filter timeout + * @param [in] delay_on_disconnect_in_ms: Disconnect filter timeout + * + * @return + * true on success, false otherwise + */ +static bool program_hpd_filter( + const struct dc_link *link) +{ + bool result = false; + + struct gpio *hpd; + + int delay_on_connect_in_ms = 0; + int delay_on_disconnect_in_ms = 0; + + /* Verify feature is supported */ + switch (link->connector_signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + case SIGNAL_TYPE_HDMI_TYPE_A: + /* Program hpd filter */ + delay_on_connect_in_ms = 500; + delay_on_disconnect_in_ms = 100; + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + /* Program hpd filter to allow DP signal to settle */ + /* 500: not able to detect MST <-> SST switch as HPD is low for + * only 100ms on DELL U2413 + * 0: some passive dongle still show aux mode instead of i2c + * 20-50:not enough to hide bouncing HPD with passive dongle. + * also see intermittent i2c read issues. + */ + delay_on_connect_in_ms = 80; + delay_on_disconnect_in_ms = 0; + break; + case SIGNAL_TYPE_LVDS: + case SIGNAL_TYPE_EDP: + default: + /* Don't program hpd filter */ + return false; + } + + /* Obtain HPD handle */ + hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); + + if (!hpd) + return result; + + /* Setup HPD filtering */ + if (dal_gpio_open(hpd, GPIO_MODE_INTERRUPT) == GPIO_RESULT_OK) { + struct gpio_hpd_config config; + + config.delay_on_connect = delay_on_connect_in_ms; + config.delay_on_disconnect = delay_on_disconnect_in_ms; + + dal_irq_setup_hpd_filter(hpd, &config); + + dal_gpio_close(hpd); + + result = true; + } else { + ASSERT_CRITICAL(false); + } + + /* Release HPD handle */ + dal_gpio_destroy_irq(&hpd); + + return result; +} + +static bool detect_sink(struct dc_link *link, enum dc_connection_type *type) +{ + uint32_t is_hpd_high = 0; + struct gpio *hpd_pin; + + /* todo: may need to lock gpio access */ + hpd_pin = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); + if (hpd_pin == NULL) + goto hpd_gpio_failure; + + dal_gpio_open(hpd_pin, GPIO_MODE_INTERRUPT); + dal_gpio_get_value(hpd_pin, &is_hpd_high); + dal_gpio_close(hpd_pin); + dal_gpio_destroy_irq(&hpd_pin); + + if (is_hpd_high) { + *type = dc_connection_single; + /* TODO: need to do the actual detection */ + } else { + *type = dc_connection_none; + } + + return true; + +hpd_gpio_failure: + return false; +} + +static enum ddc_transaction_type get_ddc_transaction_type( + enum signal_type sink_signal) +{ + enum ddc_transaction_type transaction_type = DDC_TRANSACTION_TYPE_NONE; + + switch (sink_signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_LVDS: + case SIGNAL_TYPE_RGB: + transaction_type = DDC_TRANSACTION_TYPE_I2C; + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; + break; + + case SIGNAL_TYPE_DISPLAY_PORT_MST: + /* MST does not use I2COverAux, but there is the + * SPECIAL use case for "immediate dwnstrm device + * access" (EPR#370830). */ + transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; + break; + + default: + break; + } + + return transaction_type; +} + +static enum signal_type get_basic_signal_type( + struct graphics_object_id encoder, + struct graphics_object_id downstream) +{ + if (downstream.type == OBJECT_TYPE_CONNECTOR) { + switch (downstream.id) { + case CONNECTOR_ID_SINGLE_LINK_DVII: + switch (encoder.id) { + case ENCODER_ID_INTERNAL_DAC1: + case ENCODER_ID_INTERNAL_KLDSCP_DAC1: + case ENCODER_ID_INTERNAL_DAC2: + case ENCODER_ID_INTERNAL_KLDSCP_DAC2: + return SIGNAL_TYPE_RGB; + default: + return SIGNAL_TYPE_DVI_SINGLE_LINK; + } + break; + case CONNECTOR_ID_DUAL_LINK_DVII: + { + switch (encoder.id) { + case ENCODER_ID_INTERNAL_DAC1: + case ENCODER_ID_INTERNAL_KLDSCP_DAC1: + case ENCODER_ID_INTERNAL_DAC2: + case ENCODER_ID_INTERNAL_KLDSCP_DAC2: + return SIGNAL_TYPE_RGB; + default: + return SIGNAL_TYPE_DVI_DUAL_LINK; + } + } + break; + case CONNECTOR_ID_SINGLE_LINK_DVID: + return SIGNAL_TYPE_DVI_SINGLE_LINK; + case CONNECTOR_ID_DUAL_LINK_DVID: + return SIGNAL_TYPE_DVI_DUAL_LINK; + case CONNECTOR_ID_VGA: + return SIGNAL_TYPE_RGB; + case CONNECTOR_ID_HDMI_TYPE_A: + return SIGNAL_TYPE_HDMI_TYPE_A; + case CONNECTOR_ID_LVDS: + return SIGNAL_TYPE_LVDS; + case CONNECTOR_ID_DISPLAY_PORT: + return SIGNAL_TYPE_DISPLAY_PORT; + case CONNECTOR_ID_EDP: + return SIGNAL_TYPE_EDP; + default: + return SIGNAL_TYPE_NONE; + } + } else if (downstream.type == OBJECT_TYPE_ENCODER) { + switch (downstream.id) { + case ENCODER_ID_EXTERNAL_NUTMEG: + case ENCODER_ID_EXTERNAL_TRAVIS: + return SIGNAL_TYPE_DISPLAY_PORT; + default: + return SIGNAL_TYPE_NONE; + } + } + + return SIGNAL_TYPE_NONE; +} + +/* + * @brief + * Check whether there is a dongle on DP connector + */ +static bool is_dp_sink_present(struct dc_link *link) +{ + enum gpio_result gpio_result; + uint32_t clock_pin = 0; + + struct ddc *ddc; + + enum connector_id connector_id = + dal_graphics_object_id_get_connector_id(link->link_id); + + bool present = + ((connector_id == CONNECTOR_ID_DISPLAY_PORT) || + (connector_id == CONNECTOR_ID_EDP)); + + ddc = dal_ddc_service_get_ddc_pin(link->ddc); + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return present; + } + + /* Open GPIO and set it to I2C mode */ + /* Note: this GpioMode_Input will be converted + * to GpioConfigType_I2cAuxDualMode in GPIO component, + * which indicates we need additional delay */ + + if (GPIO_RESULT_OK != dal_ddc_open( + ddc, GPIO_MODE_INPUT, GPIO_DDC_CONFIG_TYPE_MODE_I2C)) { + dal_gpio_destroy_ddc(&ddc); + + return present; + } + + /* Read GPIO: DP sink is present if both clock and data pins are zero */ + /* [anaumov] in DAL2, there was no check for GPIO failure */ + + gpio_result = dal_gpio_get_value(ddc->pin_clock, &clock_pin); + ASSERT(gpio_result == GPIO_RESULT_OK); + + present = (gpio_result == GPIO_RESULT_OK) && !clock_pin; + + dal_ddc_close(ddc); + + return present; +} + +/* + * @brief + * Detect output sink type + */ +static enum signal_type link_detect_sink( + struct dc_link *link, + enum dc_detect_reason reason) +{ + enum signal_type result = get_basic_signal_type( + link->link_enc->id, link->link_id); + + /* Internal digital encoder will detect only dongles + * that require digital signal */ + + /* Detection mechanism is different + * for different native connectors. + * LVDS connector supports only LVDS signal; + * PCIE is a bus slot, the actual connector needs to be detected first; + * eDP connector supports only eDP signal; + * HDMI should check straps for audio */ + + /* PCIE detects the actual connector on add-on board */ + + if (link->link_id.id == CONNECTOR_ID_PCIE) { + /* ZAZTODO implement PCIE add-on card detection */ + } + + switch (link->link_id.id) { + case CONNECTOR_ID_HDMI_TYPE_A: { + /* check audio support: + * if native HDMI is not supported, switch to DVI */ + struct audio_support *aud_support = &link->dc->res_pool->audio_support; + + if (!aud_support->hdmi_audio_native) + if (link->link_id.id == CONNECTOR_ID_HDMI_TYPE_A) + result = SIGNAL_TYPE_DVI_SINGLE_LINK; + } + break; + case CONNECTOR_ID_DISPLAY_PORT: { + /* DP HPD short pulse. Passive DP dongle will not + * have short pulse + */ + if (reason != DETECT_REASON_HPDRX) { + /* Check whether DP signal detected: if not - + * we assume signal is DVI; it could be corrected + * to HDMI after dongle detection + */ + if (!is_dp_sink_present(link)) + result = SIGNAL_TYPE_DVI_SINGLE_LINK; + } + } + break; + default: + break; + } + + return result; +} + +static enum signal_type decide_signal_from_strap_and_dongle_type( + enum display_dongle_type dongle_type, + struct audio_support *audio_support) +{ + enum signal_type signal = SIGNAL_TYPE_NONE; + + switch (dongle_type) { + case DISPLAY_DONGLE_DP_HDMI_DONGLE: + if (audio_support->hdmi_audio_on_dongle) + signal = SIGNAL_TYPE_HDMI_TYPE_A; + else + signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + break; + case DISPLAY_DONGLE_DP_DVI_DONGLE: + signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + break; + case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: + if (audio_support->hdmi_audio_native) + signal = SIGNAL_TYPE_HDMI_TYPE_A; + else + signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + break; + default: + signal = SIGNAL_TYPE_NONE; + break; + } + + return signal; +} + +static enum signal_type dp_passive_dongle_detection( + struct ddc_service *ddc, + struct display_sink_capability *sink_cap, + struct audio_support *audio_support) +{ + dal_ddc_service_i2c_query_dp_dual_mode_adaptor( + ddc, sink_cap); + return decide_signal_from_strap_and_dongle_type( + sink_cap->dongle_type, + audio_support); +} + +static void link_disconnect_sink(struct dc_link *link) +{ + if (link->local_sink) { + dc_sink_release(link->local_sink); + link->local_sink = NULL; + } + + link->dpcd_sink_count = 0; +} + +static void detect_dp( + struct dc_link *link, + struct display_sink_capability *sink_caps, + bool *converter_disable_audio, + struct audio_support *audio_support, + enum dc_detect_reason reason) +{ + bool boot = false; + sink_caps->signal = link_detect_sink(link, reason); + sink_caps->transaction_type = + get_ddc_transaction_type(sink_caps->signal); + + if (sink_caps->transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { + sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; + detect_dp_sink_caps(link); + + /* DP active dongles */ + if (is_dp_active_dongle(link)) { + link->type = dc_connection_active_dongle; + if (!link->dpcd_caps.sink_count.bits.SINK_COUNT) { + /* + * active dongle unplug processing for short irq + */ + link_disconnect_sink(link); + return; + } + + if (link->dpcd_caps.dongle_type != + DISPLAY_DONGLE_DP_HDMI_CONVERTER) { + *converter_disable_audio = true; + } + } + if (is_mst_supported(link)) { + sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + link->type = dc_connection_mst_branch; + + /* + * This call will initiate MST topology discovery. Which + * will detect MST ports and add new DRM connector DRM + * framework. Then read EDID via remote i2c over aux. In + * the end, will notify DRM detect result and save EDID + * into DRM framework. + * + * .detect is called by .fill_modes. + * .fill_modes is called by user mode ioctl + * DRM_IOCTL_MODE_GETCONNECTOR. + * + * .get_modes is called by .fill_modes. + * + * call .get_modes, AMDGPU DM implementation will create + * new dc_sink and add to dc_link. For long HPD plug + * in/out, MST has its own handle. + * + * Therefore, just after dc_create, link->sink is not + * created for MST until user mode app calls + * DRM_IOCTL_MODE_GETCONNECTOR. + * + * Need check ->sink usages in case ->sink = NULL + * TODO: s3 resume check + */ + if (reason == DETECT_REASON_BOOT) + boot = true; + + if (!dm_helpers_dp_mst_start_top_mgr( + link->ctx, + link, boot)) { + /* MST not supported */ + link->type = dc_connection_single; + sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; + } + } + } else { + /* DP passive dongles */ + sink_caps->signal = dp_passive_dongle_detection(link->ddc, + sink_caps, + audio_support); + } +} + +bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) +{ + struct dc_sink_init_data sink_init_data = { 0 }; + struct display_sink_capability sink_caps = { 0 }; + uint8_t i; + bool converter_disable_audio = false; + struct audio_support *aud_support = &link->dc->res_pool->audio_support; + enum dc_edid_status edid_status; + struct dc_context *dc_ctx = link->ctx; + struct dc_sink *sink = NULL; + enum dc_connection_type new_connection_type = dc_connection_none; + + if (link->connector_signal == SIGNAL_TYPE_VIRTUAL) + return false; + + if (false == detect_sink(link, &new_connection_type)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (link->connector_signal == SIGNAL_TYPE_EDP && + link->local_sink) + return true; + + link_disconnect_sink(link); + + if (new_connection_type != dc_connection_none) { + link->type = new_connection_type; + + /* From Disconnected-to-Connected. */ + switch (link->connector_signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: { + sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; + if (aud_support->hdmi_audio_native) + sink_caps.signal = SIGNAL_TYPE_HDMI_TYPE_A; + else + sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + break; + } + + case SIGNAL_TYPE_DVI_SINGLE_LINK: { + sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; + sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + break; + } + + case SIGNAL_TYPE_DVI_DUAL_LINK: { + sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; + sink_caps.signal = SIGNAL_TYPE_DVI_DUAL_LINK; + break; + } + + case SIGNAL_TYPE_EDP: { + detect_edp_sink_caps(link); + sink_caps.transaction_type = + DDC_TRANSACTION_TYPE_I2C_OVER_AUX; + sink_caps.signal = SIGNAL_TYPE_EDP; + break; + } + + case SIGNAL_TYPE_DISPLAY_PORT: { + detect_dp( + link, + &sink_caps, + &converter_disable_audio, + aud_support, reason); + + /* Active dongle downstream unplug */ + if (link->type == dc_connection_active_dongle + && link->dpcd_caps.sink_count. + bits.SINK_COUNT == 0) + return true; + + if (link->type == dc_connection_mst_branch) { + LINK_INFO("link=%d, mst branch is now Connected\n", + link->link_index); + /* Need to setup mst link_cap struct here + * otherwise dc_link_detect() will leave mst link_cap + * empty which leads to allocate_mst_payload() has "0" + * pbn_per_slot value leading to exception on dal_fixed31_32_div() + */ + link->verified_link_cap = link->reported_link_cap; + return false; + } + + break; + } + + default: + DC_ERROR("Invalid connector type! signal:%d\n", + link->connector_signal); + return false; + } /* switch() */ + + if (link->dpcd_caps.sink_count.bits.SINK_COUNT) + link->dpcd_sink_count = link->dpcd_caps.sink_count. + bits.SINK_COUNT; + else + link->dpcd_sink_count = 1; + + dal_ddc_service_set_transaction_type( + link->ddc, + sink_caps.transaction_type); + + link->aux_mode = dal_ddc_service_is_in_aux_transaction_mode( + link->ddc); + + sink_init_data.link = link; + sink_init_data.sink_signal = sink_caps.signal; + + sink = dc_sink_create(&sink_init_data); + if (!sink) { + DC_ERROR("Failed to create sink!\n"); + return false; + } + + sink->dongle_max_pix_clk = sink_caps.max_hdmi_pixel_clock; + sink->converter_disable_audio = converter_disable_audio; + + link->local_sink = sink; + + edid_status = dm_helpers_read_local_edid( + link->ctx, + link, + sink); + + switch (edid_status) { + case EDID_BAD_CHECKSUM: + dm_logger_write(link->ctx->logger, LOG_ERROR, + "EDID checksum invalid.\n"); + break; + case EDID_NO_RESPONSE: + dm_logger_write(link->ctx->logger, LOG_ERROR, + "No EDID read.\n"); + return false; + + default: + break; + } + + if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && + sink_caps.transaction_type == + DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { + /* + * TODO debug why Dell 2413 doesn't like + * two link trainings + */ + + /* deal with non-mst cases */ + dp_hbr_verify_link_cap(link, &link->reported_link_cap); + } + + /* HDMI-DVI Dongle */ + if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && + !sink->edid_caps.edid_hdmi) + sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + + /* Connectivity log: detection */ + for (i = 0; i < sink->dc_edid.length / EDID_BLOCK_SIZE; i++) { + CONN_DATA_DETECT(link, + &sink->dc_edid.raw_edid[i * EDID_BLOCK_SIZE], + EDID_BLOCK_SIZE, + "%s: [Block %d] ", sink->edid_caps.display_name, i); + } + + dm_logger_write(link->ctx->logger, LOG_DETECTION_EDID_PARSER, + "%s: " + "manufacturer_id = %X, " + "product_id = %X, " + "serial_number = %X, " + "manufacture_week = %d, " + "manufacture_year = %d, " + "display_name = %s, " + "speaker_flag = %d, " + "audio_mode_count = %d\n", + __func__, + sink->edid_caps.manufacturer_id, + sink->edid_caps.product_id, + sink->edid_caps.serial_number, + sink->edid_caps.manufacture_week, + sink->edid_caps.manufacture_year, + sink->edid_caps.display_name, + sink->edid_caps.speaker_flags, + sink->edid_caps.audio_mode_count); + + for (i = 0; i < sink->edid_caps.audio_mode_count; i++) { + dm_logger_write(link->ctx->logger, LOG_DETECTION_EDID_PARSER, + "%s: mode number = %d, " + "format_code = %d, " + "channel_count = %d, " + "sample_rate = %d, " + "sample_size = %d\n", + __func__, + i, + sink->edid_caps.audio_modes[i].format_code, + sink->edid_caps.audio_modes[i].channel_count, + sink->edid_caps.audio_modes[i].sample_rate, + sink->edid_caps.audio_modes[i].sample_size); + } + + } else { + /* From Connected-to-Disconnected. */ + if (link->type == dc_connection_mst_branch) { + LINK_INFO("link=%d, mst branch is now Disconnected\n", + link->link_index); + + dm_helpers_dp_mst_stop_top_mgr(link->ctx, link); + + link->mst_stream_alloc_table.stream_count = 0; + memset(link->mst_stream_alloc_table.stream_allocations, 0, sizeof(link->mst_stream_alloc_table.stream_allocations)); + } + + link->type = dc_connection_none; + sink_caps.signal = SIGNAL_TYPE_NONE; + } + + LINK_INFO("link=%d, dc_sink_in=%p is now %s\n", + link->link_index, sink, + (sink_caps.signal == SIGNAL_TYPE_NONE ? + "Disconnected":"Connected")); + + return true; +} + +static enum hpd_source_id get_hpd_line( + struct dc_link *link) +{ + struct gpio *hpd; + enum hpd_source_id hpd_id = HPD_SOURCEID_UNKNOWN; + + hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); + + if (hpd) { + switch (dal_irq_get_source(hpd)) { + case DC_IRQ_SOURCE_HPD1: + hpd_id = HPD_SOURCEID1; + break; + case DC_IRQ_SOURCE_HPD2: + hpd_id = HPD_SOURCEID2; + break; + case DC_IRQ_SOURCE_HPD3: + hpd_id = HPD_SOURCEID3; + break; + case DC_IRQ_SOURCE_HPD4: + hpd_id = HPD_SOURCEID4; + break; + case DC_IRQ_SOURCE_HPD5: + hpd_id = HPD_SOURCEID5; + break; + case DC_IRQ_SOURCE_HPD6: + hpd_id = HPD_SOURCEID6; + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + dal_gpio_destroy_irq(&hpd); + } + + return hpd_id; +} + +static enum channel_id get_ddc_line(struct dc_link *link) +{ + struct ddc *ddc; + enum channel_id channel = CHANNEL_ID_UNKNOWN; + + ddc = dal_ddc_service_get_ddc_pin(link->ddc); + + if (ddc) { + switch (dal_ddc_get_line(ddc)) { + case GPIO_DDC_LINE_DDC1: + channel = CHANNEL_ID_DDC1; + break; + case GPIO_DDC_LINE_DDC2: + channel = CHANNEL_ID_DDC2; + break; + case GPIO_DDC_LINE_DDC3: + channel = CHANNEL_ID_DDC3; + break; + case GPIO_DDC_LINE_DDC4: + channel = CHANNEL_ID_DDC4; + break; + case GPIO_DDC_LINE_DDC5: + channel = CHANNEL_ID_DDC5; + break; + case GPIO_DDC_LINE_DDC6: + channel = CHANNEL_ID_DDC6; + break; + case GPIO_DDC_LINE_DDC_VGA: + channel = CHANNEL_ID_DDC_VGA; + break; + case GPIO_DDC_LINE_I2C_PAD: + channel = CHANNEL_ID_I2C_PAD; + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + } + + return channel; +} + +static enum transmitter translate_encoder_to_transmitter( + struct graphics_object_id encoder) +{ + switch (encoder.id) { + case ENCODER_ID_INTERNAL_UNIPHY: + switch (encoder.enum_id) { + case ENUM_ID_1: + return TRANSMITTER_UNIPHY_A; + case ENUM_ID_2: + return TRANSMITTER_UNIPHY_B; + default: + return TRANSMITTER_UNKNOWN; + } + break; + case ENCODER_ID_INTERNAL_UNIPHY1: + switch (encoder.enum_id) { + case ENUM_ID_1: + return TRANSMITTER_UNIPHY_C; + case ENUM_ID_2: + return TRANSMITTER_UNIPHY_D; + default: + return TRANSMITTER_UNKNOWN; + } + break; + case ENCODER_ID_INTERNAL_UNIPHY2: + switch (encoder.enum_id) { + case ENUM_ID_1: + return TRANSMITTER_UNIPHY_E; + case ENUM_ID_2: + return TRANSMITTER_UNIPHY_F; + default: + return TRANSMITTER_UNKNOWN; + } + break; + case ENCODER_ID_INTERNAL_UNIPHY3: + switch (encoder.enum_id) { + case ENUM_ID_1: + return TRANSMITTER_UNIPHY_G; + default: + return TRANSMITTER_UNKNOWN; + } + break; + case ENCODER_ID_EXTERNAL_NUTMEG: + switch (encoder.enum_id) { + case ENUM_ID_1: + return TRANSMITTER_NUTMEG_CRT; + default: + return TRANSMITTER_UNKNOWN; + } + break; + case ENCODER_ID_EXTERNAL_TRAVIS: + switch (encoder.enum_id) { + case ENUM_ID_1: + return TRANSMITTER_TRAVIS_CRT; + case ENUM_ID_2: + return TRANSMITTER_TRAVIS_LCD; + default: + return TRANSMITTER_UNKNOWN; + } + break; + default: + return TRANSMITTER_UNKNOWN; + } +} + +static bool construct( + struct dc_link *link, + const struct link_init_data *init_params) +{ + uint8_t i; + struct gpio *hpd_gpio = NULL; + struct ddc_service_init_data ddc_service_init_data = { { 0 } }; + struct dc_context *dc_ctx = init_params->ctx; + struct encoder_init_data enc_init_data = { 0 }; + struct integrated_info info = {{{ 0 }}}; + struct dc_bios *bios = init_params->dc->ctx->dc_bios; + const struct dc_vbios_funcs *bp_funcs = bios->funcs; + + link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; + link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; + + link->link_status.dpcd_caps = &link->dpcd_caps; + + link->dc = init_params->dc; + link->ctx = dc_ctx; + link->link_index = init_params->link_index; + + link->link_id = bios->funcs->get_connector_id(bios, init_params->connector_index); + + if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { + dm_error("%s: Invalid Connector ObjectID from Adapter Service for connector index:%d!\n", + __func__, init_params->connector_index); + goto create_fail; + } + + hpd_gpio = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); + + if (hpd_gpio != NULL) + link->irq_source_hpd = dal_irq_get_source(hpd_gpio); + + switch (link->link_id.id) { + case CONNECTOR_ID_HDMI_TYPE_A: + link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; + + break; + case CONNECTOR_ID_SINGLE_LINK_DVID: + case CONNECTOR_ID_SINGLE_LINK_DVII: + link->connector_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + break; + case CONNECTOR_ID_DUAL_LINK_DVID: + case CONNECTOR_ID_DUAL_LINK_DVII: + link->connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK; + break; + case CONNECTOR_ID_DISPLAY_PORT: + link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; + + if (hpd_gpio != NULL) + link->irq_source_hpd_rx = + dal_irq_get_rx_source(hpd_gpio); + + break; + case CONNECTOR_ID_EDP: + link->connector_signal = SIGNAL_TYPE_EDP; + + if (hpd_gpio != NULL) { + link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; + link->irq_source_hpd_rx = + dal_irq_get_rx_source(hpd_gpio); + } + break; + default: + dm_logger_write(dc_ctx->logger, LOG_WARNING, + "Unsupported Connector type:%d!\n", link->link_id.id); + goto create_fail; + } + + if (hpd_gpio != NULL) { + dal_gpio_destroy_irq(&hpd_gpio); + hpd_gpio = NULL; + } + + /* TODO: #DAL3 Implement id to str function.*/ + LINK_INFO("Connector[%d] description:" + "signal %d\n", + init_params->connector_index, + link->connector_signal); + + ddc_service_init_data.ctx = link->ctx; + ddc_service_init_data.id = link->link_id; + ddc_service_init_data.link = link; + link->ddc = dal_ddc_service_create(&ddc_service_init_data); + + if (link->ddc == NULL) { + DC_ERROR("Failed to create ddc_service!\n"); + goto ddc_create_fail; + } + + link->ddc_hw_inst = + dal_ddc_get_line( + dal_ddc_service_get_ddc_pin(link->ddc)); + + enc_init_data.ctx = dc_ctx; + bp_funcs->get_src_obj(dc_ctx->dc_bios, link->link_id, 0, &enc_init_data.encoder); + enc_init_data.connector = link->link_id; + enc_init_data.channel = get_ddc_line(link); + enc_init_data.hpd_source = get_hpd_line(link); + + link->hpd_src = enc_init_data.hpd_source; + + enc_init_data.transmitter = + translate_encoder_to_transmitter(enc_init_data.encoder); + link->link_enc = link->dc->res_pool->funcs->link_enc_create( + &enc_init_data); + + if( link->link_enc == NULL) { + DC_ERROR("Failed to create link encoder!\n"); + goto link_enc_create_fail; + } + + link->link_enc_hw_inst = link->link_enc->transmitter; + + for (i = 0; i < 4; i++) { + if (BP_RESULT_OK != + bp_funcs->get_device_tag(dc_ctx->dc_bios, link->link_id, i, &link->device_tag)) { + DC_ERROR("Failed to find device tag!\n"); + goto device_tag_fail; + } + + /* Look for device tag that matches connector signal, + * CRT for rgb, LCD for other supported signal tyes + */ + if (!bp_funcs->is_device_id_supported(dc_ctx->dc_bios, link->device_tag.dev_id)) + continue; + if (link->device_tag.dev_id.device_type == DEVICE_TYPE_CRT + && link->connector_signal != SIGNAL_TYPE_RGB) + continue; + if (link->device_tag.dev_id.device_type == DEVICE_TYPE_LCD + && link->connector_signal == SIGNAL_TYPE_RGB) + continue; + break; + } + + if (bios->integrated_info) + info = *bios->integrated_info; + + /* Look for channel mapping corresponding to connector and device tag */ + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; i++) { + struct external_display_path *path = + &info.ext_disp_conn_info.path[i]; + if (path->device_connector_id.enum_id == link->link_id.enum_id + && path->device_connector_id.id == link->link_id.id + && path->device_connector_id.type == link->link_id.type) { + + if (link->device_tag.acpi_device != 0 + && path->device_acpi_enum == link->device_tag.acpi_device) { + link->ddi_channel_mapping = path->channel_mapping; + link->chip_caps = path->caps; + } else if (path->device_tag == + link->device_tag.dev_id.raw_device_tag) { + link->ddi_channel_mapping = path->channel_mapping; + link->chip_caps = path->caps; + } + break; + } + } + + /* + * TODO check if GPIO programmed correctly + * + * If GPIO isn't programmed correctly HPD might not rise or drain + * fast enough, leading to bounces. + */ + program_hpd_filter(link); + + return true; +device_tag_fail: + link->link_enc->funcs->destroy(&link->link_enc); +link_enc_create_fail: + dal_ddc_service_destroy(&link->ddc); +ddc_create_fail: +create_fail: + + if (hpd_gpio != NULL) { + dal_gpio_destroy_irq(&hpd_gpio); + } + + return false; +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ +struct dc_link *link_create(const struct link_init_data *init_params) +{ + struct dc_link *link = + kzalloc(sizeof(*link), GFP_KERNEL); + + if (NULL == link) + goto alloc_fail; + + if (false == construct(link, init_params)) + goto construct_fail; + + return link; + +construct_fail: + kfree(link); + +alloc_fail: + return NULL; +} + +void link_destroy(struct dc_link **link) +{ + destruct(*link); + kfree(*link); + *link = NULL; +} + +static void dpcd_configure_panel_mode( + struct dc_link *link, + enum dp_panel_mode panel_mode) +{ + union dpcd_edp_config edp_config_set; + bool panel_mode_edp = false; + + memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config)); + + if (DP_PANEL_MODE_DEFAULT != panel_mode) { + + switch (panel_mode) { + case DP_PANEL_MODE_EDP: + case DP_PANEL_MODE_SPECIAL: + panel_mode_edp = true; + break; + + default: + break; + } + + /*set edp panel mode in receiver*/ + core_link_read_dpcd( + link, + DP_EDP_CONFIGURATION_SET, + &edp_config_set.raw, + sizeof(edp_config_set.raw)); + + if (edp_config_set.bits.PANEL_MODE_EDP + != panel_mode_edp) { + enum ddc_result result = DDC_RESULT_UNKNOWN; + + edp_config_set.bits.PANEL_MODE_EDP = + panel_mode_edp; + result = core_link_write_dpcd( + link, + DP_EDP_CONFIGURATION_SET, + &edp_config_set.raw, + sizeof(edp_config_set.raw)); + + ASSERT(result == DDC_RESULT_SUCESSFULL); + } + } + dm_logger_write(link->ctx->logger, LOG_DETECTION_DP_CAPS, + "Link: %d eDP panel mode supported: %d " + "eDP panel mode enabled: %d \n", + link->link_index, + link->dpcd_caps.panel_mode_edp, + panel_mode_edp); +} + +static void enable_stream_features(struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->sink->link; + union down_spread_ctrl downspread; + + core_link_read_dpcd(link, DP_DOWNSPREAD_CTRL, + &downspread.raw, sizeof(downspread)); + + downspread.bits.IGNORE_MSA_TIMING_PARAM = + (stream->ignore_msa_timing_param) ? 1 : 0; + + core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, + &downspread.raw, sizeof(downspread)); +} + +static enum dc_status enable_link_dp( + struct dc_state *state, + struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + enum dc_status status; + bool skip_video_pattern; + struct dc_link *link = stream->sink->link; + struct dc_link_settings link_settings = {0}; + enum dp_panel_mode panel_mode; + enum dc_link_rate max_link_rate = LINK_RATE_HIGH2; + + /* get link settings for video mode timing */ + decide_link_settings(stream, &link_settings); + + /* raise clock state for HBR3 if required. Confirmed with HW DCE/DPCS + * logic for HBR3 still needs Nominal (0.8V) on VDDC rail + */ + if (link->link_enc->features.flags.bits.IS_HBR3_CAPABLE) + max_link_rate = LINK_RATE_HIGH3; + + if (link_settings.link_rate == max_link_rate) { + if (state->dis_clk->funcs->set_min_clocks_state) { + if (state->dis_clk->cur_min_clks_state < DM_PP_CLOCKS_STATE_NOMINAL) + state->dis_clk->funcs->set_min_clocks_state( + state->dis_clk, DM_PP_CLOCKS_STATE_NOMINAL); + } else { + uint32_t dp_phyclk_in_khz; + const struct clocks_value clocks_value = + state->dis_clk->cur_clocks_value; + + /* 27mhz = 27000000hz= 27000khz */ + dp_phyclk_in_khz = link_settings.link_rate * 27000; + + if (((clocks_value.max_non_dp_phyclk_in_khz != 0) && + (dp_phyclk_in_khz > clocks_value.max_non_dp_phyclk_in_khz)) || + (dp_phyclk_in_khz > clocks_value.max_dp_phyclk_in_khz)) { + state->dis_clk->funcs->apply_clock_voltage_request( + state->dis_clk, + DM_PP_CLOCK_TYPE_DISPLAYPHYCLK, + dp_phyclk_in_khz, + false, + true); + } + } + } + + dp_enable_link_phy( + link, + pipe_ctx->stream->signal, + pipe_ctx->clock_source->id, + &link_settings); + + panel_mode = dp_get_panel_mode(link); + dpcd_configure_panel_mode(link, panel_mode); + + skip_video_pattern = true; + + if (link_settings.link_rate == LINK_RATE_LOW) + skip_video_pattern = false; + + if (perform_link_training_with_retries( + link, + &link_settings, + skip_video_pattern, + LINK_TRAINING_ATTEMPTS)) { + link->cur_link_settings = link_settings; + status = DC_OK; + } + else + status = DC_FAIL_DP_LINK_TRAINING; + + enable_stream_features(pipe_ctx); + + return status; +} + +static enum dc_status enable_link_dp_mst( + struct dc_state *state, + struct pipe_ctx *pipe_ctx) +{ + struct dc_link *link = pipe_ctx->stream->sink->link; + + /* sink signal type after MST branch is MST. Multiple MST sinks + * share one link. Link DP PHY is enable or training only once. + */ + if (link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) + return DC_OK; + + /* set the sink to MST mode before enabling the link */ + dp_enable_mst_on_sink(link, true); + + return enable_link_dp(state, pipe_ctx); +} + +static bool get_ext_hdmi_settings(struct pipe_ctx *pipe_ctx, + enum engine_id eng_id, + struct ext_hdmi_settings *settings) +{ + bool result = false; + int i = 0; + struct integrated_info *integrated_info = + pipe_ctx->stream->ctx->dc_bios->integrated_info; + + if (integrated_info == NULL) + return false; + + /* + * Get retimer settings from sbios for passing SI eye test for DCE11 + * The setting values are varied based on board revision and port id + * Therefore the setting values of each ports is passed by sbios. + */ + + // Check if current bios contains ext Hdmi settings + if (integrated_info->gpu_cap_info & 0x20) { + switch (eng_id) { + case ENGINE_ID_DIGA: + settings->slv_addr = integrated_info->dp0_ext_hdmi_slv_addr; + settings->reg_num = integrated_info->dp0_ext_hdmi_6g_reg_num; + settings->reg_num_6g = integrated_info->dp0_ext_hdmi_6g_reg_num; + memmove(settings->reg_settings, + integrated_info->dp0_ext_hdmi_reg_settings, + sizeof(integrated_info->dp0_ext_hdmi_reg_settings)); + memmove(settings->reg_settings_6g, + integrated_info->dp0_ext_hdmi_6g_reg_settings, + sizeof(integrated_info->dp0_ext_hdmi_6g_reg_settings)); + result = true; + break; + case ENGINE_ID_DIGB: + settings->slv_addr = integrated_info->dp1_ext_hdmi_slv_addr; + settings->reg_num = integrated_info->dp1_ext_hdmi_6g_reg_num; + settings->reg_num_6g = integrated_info->dp1_ext_hdmi_6g_reg_num; + memmove(settings->reg_settings, + integrated_info->dp1_ext_hdmi_reg_settings, + sizeof(integrated_info->dp1_ext_hdmi_reg_settings)); + memmove(settings->reg_settings_6g, + integrated_info->dp1_ext_hdmi_6g_reg_settings, + sizeof(integrated_info->dp1_ext_hdmi_6g_reg_settings)); + result = true; + break; + case ENGINE_ID_DIGC: + settings->slv_addr = integrated_info->dp2_ext_hdmi_slv_addr; + settings->reg_num = integrated_info->dp2_ext_hdmi_6g_reg_num; + settings->reg_num_6g = integrated_info->dp2_ext_hdmi_6g_reg_num; + memmove(settings->reg_settings, + integrated_info->dp2_ext_hdmi_reg_settings, + sizeof(integrated_info->dp2_ext_hdmi_reg_settings)); + memmove(settings->reg_settings_6g, + integrated_info->dp2_ext_hdmi_6g_reg_settings, + sizeof(integrated_info->dp2_ext_hdmi_6g_reg_settings)); + result = true; + break; + case ENGINE_ID_DIGD: + settings->slv_addr = integrated_info->dp3_ext_hdmi_slv_addr; + settings->reg_num = integrated_info->dp3_ext_hdmi_6g_reg_num; + settings->reg_num_6g = integrated_info->dp3_ext_hdmi_6g_reg_num; + memmove(settings->reg_settings, + integrated_info->dp3_ext_hdmi_reg_settings, + sizeof(integrated_info->dp3_ext_hdmi_reg_settings)); + memmove(settings->reg_settings_6g, + integrated_info->dp3_ext_hdmi_6g_reg_settings, + sizeof(integrated_info->dp3_ext_hdmi_6g_reg_settings)); + result = true; + break; + default: + break; + } + + if (result == true) { + // Validate settings from bios integrated info table + if (settings->slv_addr == 0) + return false; + if (settings->reg_num > 9) + return false; + if (settings->reg_num_6g > 3) + return false; + + for (i = 0; i < settings->reg_num; i++) { + if (settings->reg_settings[i].i2c_reg_index > 0x20) + return false; + } + + for (i = 0; i < settings->reg_num_6g; i++) { + if (settings->reg_settings_6g[i].i2c_reg_index > 0x20) + return false; + } + } + } + + return result; +} + +static bool i2c_write(struct pipe_ctx *pipe_ctx, + uint8_t address, uint8_t *buffer, uint32_t length) +{ + struct i2c_command cmd = {0}; + struct i2c_payload payload = {0}; + + memset(&payload, 0, sizeof(payload)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.number_of_payloads = 1; + cmd.engine = I2C_COMMAND_ENGINE_DEFAULT; + cmd.speed = pipe_ctx->stream->ctx->dc->caps.i2c_speed_in_khz; + + payload.address = address; + payload.data = buffer; + payload.length = length; + payload.write = true; + cmd.payloads = &payload; + + if (dc_submit_i2c(pipe_ctx->stream->ctx->dc, + pipe_ctx->stream->sink->link->link_index, &cmd)) + return true; + + return false; +} + +static void write_i2c_retimer_setting( + struct pipe_ctx *pipe_ctx, + bool is_vga_mode, + bool is_over_340mhz, + struct ext_hdmi_settings *settings) +{ + uint8_t slave_address = (settings->slv_addr >> 1); + uint8_t buffer[2]; + const uint8_t apply_rx_tx_change = 0x4; + uint8_t offset = 0xA; + uint8_t value = 0; + int i = 0; + bool i2c_success = false; + + memset(&buffer, 0, sizeof(buffer)); + + /* Start Ext-Hdmi programming*/ + + for (i = 0; i < settings->reg_num; i++) { + /* Apply 3G settings */ + if (settings->reg_settings[i].i2c_reg_index <= 0x20) { + + buffer[0] = settings->reg_settings[i].i2c_reg_index; + buffer[1] = settings->reg_settings[i].i2c_reg_val; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A + * needs to be set to 1 on every 0xA-0xC write. + */ + if (settings->reg_settings[i].i2c_reg_index == 0xA || + settings->reg_settings[i].i2c_reg_index == 0xB || + settings->reg_settings[i].i2c_reg_index == 0xC) { + + /* Query current value from offset 0xA */ + if (settings->reg_settings[i].i2c_reg_index == 0xA) + value = settings->reg_settings[i].i2c_reg_val; + else { + i2c_success = + dal_ddc_service_query_ddc_data( + pipe_ctx->stream->sink->link->ddc, + slave_address, &offset, 1, &value, 1); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + } + + buffer[0] = offset; + /* Set APPLY_RX_TX_CHANGE bit to 1 */ + buffer[1] = value | apply_rx_tx_change; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + } + } + } + + /* Apply 3G settings */ + if (is_over_340mhz) { + for (i = 0; i < settings->reg_num_6g; i++) { + /* Apply 3G settings */ + if (settings->reg_settings[i].i2c_reg_index <= 0x20) { + + buffer[0] = settings->reg_settings_6g[i].i2c_reg_index; + buffer[1] = settings->reg_settings_6g[i].i2c_reg_val; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A + * needs to be set to 1 on every 0xA-0xC write. + */ + if (settings->reg_settings_6g[i].i2c_reg_index == 0xA || + settings->reg_settings_6g[i].i2c_reg_index == 0xB || + settings->reg_settings_6g[i].i2c_reg_index == 0xC) { + + /* Query current value from offset 0xA */ + if (settings->reg_settings_6g[i].i2c_reg_index == 0xA) + value = settings->reg_settings_6g[i].i2c_reg_val; + else { + i2c_success = + dal_ddc_service_query_ddc_data( + pipe_ctx->stream->sink->link->ddc, + slave_address, &offset, 1, &value, 1); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + } + + buffer[0] = offset; + /* Set APPLY_RX_TX_CHANGE bit to 1 */ + buffer[1] = value | apply_rx_tx_change; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + } + } + } + } + + if (is_vga_mode) { + /* Program additional settings if using 640x480 resolution */ + + /* Write offset 0xFF to 0x01 */ + buffer[0] = 0xff; + buffer[1] = 0x01; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0x00 to 0x23 */ + buffer[0] = 0x00; + buffer[1] = 0x23; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0xff to 0x00 */ + buffer[0] = 0xff; + buffer[1] = 0x00; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + } +} + +static void write_i2c_default_retimer_setting( + struct pipe_ctx *pipe_ctx, + bool is_vga_mode, + bool is_over_340mhz) +{ + uint8_t slave_address = (0xBA >> 1); + uint8_t buffer[2]; + bool i2c_success = false; + + memset(&buffer, 0, sizeof(buffer)); + + /* Program Slave Address for tuning single integrity */ + /* Write offset 0x0A to 0x13 */ + buffer[0] = 0x0A; + buffer[1] = 0x13; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0x0A to 0x17 */ + buffer[0] = 0x0A; + buffer[1] = 0x17; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0x0B to 0xDA or 0xD8 */ + buffer[0] = 0x0B; + buffer[1] = is_over_340mhz ? 0xDA : 0xD8; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0x0A to 0x17 */ + buffer[0] = 0x0A; + buffer[1] = 0x17; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0x0C to 0x1D or 0x91 */ + buffer[0] = 0x0C; + buffer[1] = is_over_340mhz ? 0x1D : 0x91; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0x0A to 0x17 */ + buffer[0] = 0x0A; + buffer[1] = 0x17; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + + if (is_vga_mode) { + /* Program additional settings if using 640x480 resolution */ + + /* Write offset 0xFF to 0x01 */ + buffer[0] = 0xff; + buffer[1] = 0x01; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0x00 to 0x23 */ + buffer[0] = 0x00; + buffer[1] = 0x23; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + + /* Write offset 0xff to 0x00 */ + buffer[0] = 0xff; + buffer[1] = 0x00; + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); + } +} + +static void write_i2c_redriver_setting( + struct pipe_ctx *pipe_ctx, + bool is_over_340mhz) +{ + uint8_t slave_address = (0xF0 >> 1); + uint8_t buffer[16]; + bool i2c_success = false; + + memset(&buffer, 0, sizeof(buffer)); + + // Program Slave Address for tuning single integrity + buffer[3] = 0x4E; + buffer[4] = 0x4E; + buffer[5] = 0x4E; + buffer[6] = is_over_340mhz ? 0x4E : 0x4A; + + i2c_success = i2c_write(pipe_ctx, slave_address, + buffer, sizeof(buffer)); + + if (!i2c_success) + /* Write failure */ + ASSERT(i2c_success); +} + +static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->sink->link; + enum dc_color_depth display_color_depth; + enum engine_id eng_id; + struct ext_hdmi_settings settings = {0}; + bool is_over_340mhz = false; + bool is_vga_mode = (stream->timing.h_addressable == 640) + && (stream->timing.v_addressable == 480); + + if (stream->phy_pix_clk > 340000) + is_over_340mhz = true; + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { + unsigned short masked_chip_caps = pipe_ctx->stream->sink->link->chip_caps & + EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; + if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { + /* DP159, Retimer settings */ + eng_id = pipe_ctx->stream_res.stream_enc->id; + + if (get_ext_hdmi_settings(pipe_ctx, eng_id, &settings)) { + write_i2c_retimer_setting(pipe_ctx, + is_vga_mode, is_over_340mhz, &settings); + } else { + write_i2c_default_retimer_setting(pipe_ctx, + is_vga_mode, is_over_340mhz); + } + } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { + /* PI3EQX1204, Redriver settings */ + write_i2c_redriver_setting(pipe_ctx, is_over_340mhz); + } + } + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + dal_ddc_service_write_scdc_data( + stream->sink->link->ddc, + stream->phy_pix_clk, + stream->timing.flags.LTE_340MCSC_SCRAMBLE); + + memset(&stream->sink->link->cur_link_settings, 0, + sizeof(struct dc_link_settings)); + + display_color_depth = stream->timing.display_color_depth; + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) + display_color_depth = COLOR_DEPTH_888; + + link->link_enc->funcs->enable_tmds_output( + link->link_enc, + pipe_ctx->clock_source->id, + display_color_depth, + pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_TYPE_A, + pipe_ctx->stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK, + stream->phy_pix_clk); + + if (pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) + dal_ddc_service_read_scdc_data(link->ddc); +} + +/****************************enable_link***********************************/ +static enum dc_status enable_link( + struct dc_state *state, + struct pipe_ctx *pipe_ctx) +{ + enum dc_status status = DC_ERROR_UNEXPECTED; + switch (pipe_ctx->stream->signal) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + status = enable_link_dp(state, pipe_ctx); + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + status = enable_link_dp_mst(state, pipe_ctx); + msleep(200); + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + case SIGNAL_TYPE_HDMI_TYPE_A: + enable_link_hdmi(pipe_ctx); + status = DC_OK; + break; + case SIGNAL_TYPE_VIRTUAL: + status = DC_OK; + break; + default: + break; + } + + if (pipe_ctx->stream_res.audio && status == DC_OK) { + /* notify audio driver for audio modes of monitor */ + pipe_ctx->stream_res.audio->funcs->az_enable(pipe_ctx->stream_res.audio); + + /* un-mute audio */ + /* TODO: audio should be per stream rather than per link */ + pipe_ctx->stream_res.stream_enc->funcs->audio_mute_control( + pipe_ctx->stream_res.stream_enc, false); + } + + return status; +} + +static void disable_link(struct dc_link *link, enum signal_type signal) +{ + /* + * TODO: implement call for dp_set_hw_test_pattern + * it is needed for compliance testing + */ + + /* here we need to specify that encoder output settings + * need to be calculated as for the set mode, + * it will lead to querying dynamic link capabilities + * which should be done before enable output */ + + if (dc_is_dp_signal(signal)) { + /* SST DP, eDP */ + if (dc_is_dp_sst_signal(signal)) + dp_disable_link_phy(link, signal); + else + dp_disable_link_phy_mst(link, signal); + } else + link->link_enc->funcs->disable_output(link->link_enc, signal, link); +} + +enum dc_status dc_link_validate_mode_timing( + const struct dc_stream_state *stream, + struct dc_link *link, + const struct dc_crtc_timing *timing) +{ + uint32_t max_pix_clk = stream->sink->dongle_max_pix_clk; + + /* A hack to avoid failing any modes for EDID override feature on + * topology change such as lower quality cable for DP or different dongle + */ + if (link->remote_sinks[0]) + return DC_OK; + + if (0 != max_pix_clk && timing->pix_clk_khz > max_pix_clk) + return DC_EXCEED_DONGLE_MAX_CLK; + + switch (stream->signal) { + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + if (!dp_validate_mode_timing( + link, + timing)) + return DC_NO_DP_LINK_BANDWIDTH; + break; + + default: + break; + } + + return DC_OK; +} + + +bool dc_link_set_backlight_level(const struct dc_link *link, uint32_t level, + uint32_t frame_ramp, const struct dc_stream_state *stream) +{ + struct dc *core_dc = link->ctx->dc; + struct abm *abm = core_dc->res_pool->abm; + unsigned int controller_id = 0; + int i; + + if ((abm == NULL) || (abm->funcs->set_backlight_level == NULL)) + return false; + + dm_logger_write(link->ctx->logger, LOG_BACKLIGHT, + "New Backlight level: %d (0x%X)\n", level, level); + + if (dc_is_embedded_signal(link->connector_signal)) { + if (stream != NULL) { + for (i = 0; i < MAX_PIPES; i++) { + if (core_dc->current_state->res_ctx. + pipe_ctx[i].stream + == stream) + /* DMCU -1 for all controller id values, + * therefore +1 here + */ + controller_id = + core_dc->current_state-> + res_ctx.pipe_ctx[i].stream_res.tg->inst + + 1; + } + } + abm->funcs->set_backlight_level( + abm, + level, + frame_ramp, + controller_id); + } + + return true; +} + +bool dc_link_set_psr_enable(const struct dc_link *link, bool enable, bool wait) +{ + struct dc *core_dc = link->ctx->dc; + struct dmcu *dmcu = core_dc->res_pool->dmcu; + + if (dmcu != NULL && link->psr_enabled) + dmcu->funcs->set_psr_enable(dmcu, enable, wait); + + return true; +} + +bool dc_link_get_psr_state(const struct dc_link *link, uint32_t *psr_state) +{ + struct dc *core_dc = link->ctx->dc; + struct dmcu *dmcu = core_dc->res_pool->dmcu; + + if (dmcu != NULL && link->psr_enabled) + dmcu->funcs->get_psr_state(dmcu, psr_state); + + return true; +} + +bool dc_link_setup_psr(struct dc_link *link, + const struct dc_stream_state *stream, struct psr_config *psr_config, + struct psr_context *psr_context) +{ + struct dc *core_dc = link->ctx->dc; + struct dmcu *dmcu = core_dc->res_pool->dmcu; + int i; + + psr_context->controllerId = CONTROLLER_ID_UNDEFINED; + + if (link != NULL && + dmcu != NULL) { + /* updateSinkPsrDpcdConfig*/ + union dpcd_psr_configuration psr_configuration; + + memset(&psr_configuration, 0, sizeof(psr_configuration)); + + psr_configuration.bits.ENABLE = 1; + psr_configuration.bits.CRC_VERIFICATION = 1; + psr_configuration.bits.FRAME_CAPTURE_INDICATION = + psr_config->psr_frame_capture_indication_req; + + /* Check for PSR v2*/ + if (psr_config->psr_version == 0x2) { + /* For PSR v2 selective update. + * Indicates whether sink should start capturing + * immediately following active scan line, + * or starting with the 2nd active scan line. + */ + psr_configuration.bits.LINE_CAPTURE_INDICATION = 0; + /*For PSR v2, determines whether Sink should generate + * IRQ_HPD when CRC mismatch is detected. + */ + psr_configuration.bits.IRQ_HPD_WITH_CRC_ERROR = 1; + } + + dm_helpers_dp_write_dpcd( + link->ctx, + link, + 368, + &psr_configuration.raw, + sizeof(psr_configuration.raw)); + + psr_context->channel = link->ddc->ddc_pin->hw_info.ddc_channel; + psr_context->transmitterId = link->link_enc->transmitter; + psr_context->engineId = link->link_enc->preferred_engine; + + for (i = 0; i < MAX_PIPES; i++) { + if (core_dc->current_state->res_ctx.pipe_ctx[i].stream + == stream) { + /* dmcu -1 for all controller id values, + * therefore +1 here + */ + psr_context->controllerId = + core_dc->current_state->res_ctx. + pipe_ctx[i].stream_res.tg->inst + 1; + break; + } + } + + /* Hardcoded for now. Can be Pcie or Uniphy (or Unknown)*/ + psr_context->phyType = PHY_TYPE_UNIPHY; + /*PhyId is associated with the transmitter id*/ + psr_context->smuPhyId = link->link_enc->transmitter; + + psr_context->crtcTimingVerticalTotal = stream->timing.v_total; + psr_context->vsyncRateHz = div64_u64(div64_u64((stream-> + timing.pix_clk_khz * 1000), + stream->timing.v_total), + stream->timing.h_total); + + psr_context->psrSupportedDisplayConfig = true; + psr_context->psrExitLinkTrainingRequired = + psr_config->psr_exit_link_training_required; + psr_context->sdpTransmitLineNumDeadline = + psr_config->psr_sdp_transmit_line_num_deadline; + psr_context->psrFrameCaptureIndicationReq = + psr_config->psr_frame_capture_indication_req; + + psr_context->skipPsrWaitForPllLock = 0; /* only = 1 in KV */ + + psr_context->numberOfControllers = + link->dc->res_pool->res_cap->num_timing_generator; + + psr_context->rfb_update_auto_en = true; + + /* 2 frames before enter PSR. */ + psr_context->timehyst_frames = 2; + /* half a frame + * (units in 100 lines, i.e. a value of 1 represents 100 lines) + */ + psr_context->hyst_lines = stream->timing.v_total / 2 / 100; + psr_context->aux_repeats = 10; + + psr_context->psr_level.u32all = 0; + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + /*skip power down the single pipe since it blocks the cstate*/ + if (ASIC_REV_IS_RAVEN(link->ctx->asic_id.hw_internal_rev)) + psr_context->psr_level.bits.SKIP_CRTC_DISABLE = true; +#endif + + /* SMU will perform additional powerdown sequence. + * For unsupported ASICs, set psr_level flag to skip PSR + * static screen notification to SMU. + * (Always set for DAL2, did not check ASIC) + */ + psr_context->psr_level.bits.SKIP_SMU_NOTIFICATION = 1; + + /* Complete PSR entry before aborting to prevent intermittent + * freezes on certain eDPs + */ + psr_context->psr_level.bits.DISABLE_PSR_ENTRY_ABORT = 1; + + /* Controls additional delay after remote frame capture before + * continuing power down, default = 0 + */ + psr_context->frame_delay = 0; + + link->psr_enabled = true; + dmcu->funcs->setup_psr(dmcu, link, psr_context); + return true; + } else + return false; + +} + +const struct dc_link_status *dc_link_get_status(const struct dc_link *link) +{ + return &link->link_status; +} + +void core_link_resume(struct dc_link *link) +{ + if (link->connector_signal != SIGNAL_TYPE_VIRTUAL) + program_hpd_filter(link); +} + +static struct fixed31_32 get_pbn_per_slot(struct dc_stream_state *stream) +{ + struct dc_link_settings *link_settings = + &stream->sink->link->cur_link_settings; + uint32_t link_rate_in_mbps = + link_settings->link_rate * LINK_RATE_REF_FREQ_IN_MHZ; + struct fixed31_32 mbps = dal_fixed31_32_from_int( + link_rate_in_mbps * link_settings->lane_count); + + return dal_fixed31_32_div_int(mbps, 54); +} + +static int get_color_depth(enum dc_color_depth color_depth) +{ + switch (color_depth) { + case COLOR_DEPTH_666: return 6; + case COLOR_DEPTH_888: return 8; + case COLOR_DEPTH_101010: return 10; + case COLOR_DEPTH_121212: return 12; + case COLOR_DEPTH_141414: return 14; + case COLOR_DEPTH_161616: return 16; + default: return 0; + } +} + +static struct fixed31_32 get_pbn_from_timing(struct pipe_ctx *pipe_ctx) +{ + uint32_t bpc; + uint64_t kbps; + struct fixed31_32 peak_kbps; + uint32_t numerator; + uint32_t denominator; + + bpc = get_color_depth(pipe_ctx->stream_res.pix_clk_params.color_depth); + kbps = pipe_ctx->stream_res.pix_clk_params.requested_pix_clk * bpc * 3; + + /* + * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 + * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on + * common multiplier to render an integer PBN for all link rate/lane + * counts combinations + * calculate + * peak_kbps *= (1006/1000) + * peak_kbps *= (64/54) + * peak_kbps *= 8 convert to bytes + */ + + numerator = 64 * PEAK_FACTOR_X1000; + denominator = 54 * 8 * 1000 * 1000; + kbps *= numerator; + peak_kbps = dal_fixed31_32_from_fraction(kbps, denominator); + + return peak_kbps; +} + +static void update_mst_stream_alloc_table( + struct dc_link *link, + struct stream_encoder *stream_enc, + const struct dp_mst_stream_allocation_table *proposed_table) +{ + struct link_mst_stream_allocation work_table[MAX_CONTROLLER_NUM] = { + { 0 } }; + struct link_mst_stream_allocation *dc_alloc; + + int i; + int j; + + /* if DRM proposed_table has more than one new payload */ + ASSERT(proposed_table->stream_count - + link->mst_stream_alloc_table.stream_count < 2); + + /* copy proposed_table to link, add stream encoder */ + for (i = 0; i < proposed_table->stream_count; i++) { + + for (j = 0; j < link->mst_stream_alloc_table.stream_count; j++) { + dc_alloc = + &link->mst_stream_alloc_table.stream_allocations[j]; + + if (dc_alloc->vcp_id == + proposed_table->stream_allocations[i].vcp_id) { + + work_table[i] = *dc_alloc; + break; /* exit j loop */ + } + } + + /* new vcp_id */ + if (j == link->mst_stream_alloc_table.stream_count) { + work_table[i].vcp_id = + proposed_table->stream_allocations[i].vcp_id; + work_table[i].slot_count = + proposed_table->stream_allocations[i].slot_count; + work_table[i].stream_enc = stream_enc; + } + } + + /* update link->mst_stream_alloc_table with work_table */ + link->mst_stream_alloc_table.stream_count = + proposed_table->stream_count; + for (i = 0; i < MAX_CONTROLLER_NUM; i++) + link->mst_stream_alloc_table.stream_allocations[i] = + work_table[i]; +} + +/* convert link_mst_stream_alloc_table to dm dp_mst_stream_alloc_table + * because stream_encoder is not exposed to dm + */ +static enum dc_status allocate_mst_payload(struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->sink->link; + struct link_encoder *link_encoder = link->link_enc; + struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc; + struct dp_mst_stream_allocation_table proposed_table = {0}; + struct fixed31_32 avg_time_slots_per_mtp; + struct fixed31_32 pbn; + struct fixed31_32 pbn_per_slot; + uint8_t i; + + /* enable_link_dp_mst already check link->enabled_stream_count + * and stream is in link->stream[]. This is called during set mode, + * stream_enc is available. + */ + + /* get calculate VC payload for stream: stream_alloc */ + if (dm_helpers_dp_mst_write_payload_allocation_table( + stream->ctx, + stream, + &proposed_table, + true)) { + update_mst_stream_alloc_table( + link, pipe_ctx->stream_res.stream_enc, &proposed_table); + } + else + dm_logger_write(link->ctx->logger, LOG_WARNING, + "Failed to update" + "MST allocation table for" + "pipe idx:%d\n", + pipe_ctx->pipe_idx); + + dm_logger_write(link->ctx->logger, LOG_MST, + "%s " + "stream_count: %d: \n ", + __func__, + link->mst_stream_alloc_table.stream_count); + + for (i = 0; i < MAX_CONTROLLER_NUM; i++) { + dm_logger_write(link->ctx->logger, LOG_MST, + "stream_enc[%d]: 0x%x " + "stream[%d].vcp_id: %d " + "stream[%d].slot_count: %d\n", + i, + link->mst_stream_alloc_table.stream_allocations[i].stream_enc, + i, + link->mst_stream_alloc_table.stream_allocations[i].vcp_id, + i, + link->mst_stream_alloc_table.stream_allocations[i].slot_count); + } + + ASSERT(proposed_table.stream_count > 0); + + /* program DP source TX for payload */ + link_encoder->funcs->update_mst_stream_allocation_table( + link_encoder, + &link->mst_stream_alloc_table); + + /* send down message */ + dm_helpers_dp_mst_poll_for_allocation_change_trigger( + stream->ctx, + stream); + + dm_helpers_dp_mst_send_payload_allocation( + stream->ctx, + stream, + true); + + /* slot X.Y for only current stream */ + pbn_per_slot = get_pbn_per_slot(stream); + pbn = get_pbn_from_timing(pipe_ctx); + avg_time_slots_per_mtp = dal_fixed31_32_div(pbn, pbn_per_slot); + + stream_encoder->funcs->set_mst_bandwidth( + stream_encoder, + avg_time_slots_per_mtp); + + return DC_OK; + +} + +static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->sink->link; + struct link_encoder *link_encoder = link->link_enc; + struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc; + struct dp_mst_stream_allocation_table proposed_table = {0}; + struct fixed31_32 avg_time_slots_per_mtp = dal_fixed31_32_from_int(0); + uint8_t i; + bool mst_mode = (link->type == dc_connection_mst_branch); + + /* deallocate_mst_payload is called before disable link. When mode or + * disable/enable monitor, new stream is created which is not in link + * stream[] yet. For this, payload is not allocated yet, so de-alloc + * should not done. For new mode set, map_resources will get engine + * for new stream, so stream_enc->id should be validated until here. + */ + + /* slot X.Y */ + stream_encoder->funcs->set_mst_bandwidth( + stream_encoder, + avg_time_slots_per_mtp); + + /* TODO: which component is responsible for remove payload table? */ + if (mst_mode) { + if (dm_helpers_dp_mst_write_payload_allocation_table( + stream->ctx, + stream, + &proposed_table, + false)) { + + update_mst_stream_alloc_table( + link, pipe_ctx->stream_res.stream_enc, &proposed_table); + } + else { + dm_logger_write(link->ctx->logger, LOG_WARNING, + "Failed to update" + "MST allocation table for" + "pipe idx:%d\n", + pipe_ctx->pipe_idx); + } + } + + dm_logger_write(link->ctx->logger, LOG_MST, + "%s" + "stream_count: %d: ", + __func__, + link->mst_stream_alloc_table.stream_count); + + for (i = 0; i < MAX_CONTROLLER_NUM; i++) { + dm_logger_write(link->ctx->logger, LOG_MST, + "stream_enc[%d]: 0x%x " + "stream[%d].vcp_id: %d " + "stream[%d].slot_count: %d\n", + i, + link->mst_stream_alloc_table.stream_allocations[i].stream_enc, + i, + link->mst_stream_alloc_table.stream_allocations[i].vcp_id, + i, + link->mst_stream_alloc_table.stream_allocations[i].slot_count); + } + + link_encoder->funcs->update_mst_stream_allocation_table( + link_encoder, + &link->mst_stream_alloc_table); + + if (mst_mode) { + dm_helpers_dp_mst_poll_for_allocation_change_trigger( + stream->ctx, + stream); + + dm_helpers_dp_mst_send_payload_allocation( + stream->ctx, + stream, + false); + } + + return DC_OK; +} + +void core_link_enable_stream( + struct dc_state *state, + struct pipe_ctx *pipe_ctx) +{ + struct dc *core_dc = pipe_ctx->stream->ctx->dc; + + enum dc_status status = enable_link(state, pipe_ctx); + + if (status != DC_OK) { + dm_logger_write(pipe_ctx->stream->ctx->logger, + LOG_WARNING, "enabling link %u failed: %d\n", + pipe_ctx->stream->sink->link->link_index, + status); + + /* Abort stream enable *unless* the failure was due to + * DP link training - some DP monitors will recover and + * show the stream anyway. But MST displays can't proceed + * without link training. + */ + if (status != DC_FAIL_DP_LINK_TRAINING || + pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + BREAK_TO_DEBUGGER(); + return; + } + } + + /* turn off otg test pattern if enable */ + pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, + CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, + COLOR_DEPTH_UNDEFINED); + + core_dc->hwss.enable_stream(pipe_ctx); + + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + allocate_mst_payload(pipe_ctx); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + core_dc->hwss.unblank_stream(pipe_ctx, + &pipe_ctx->stream->sink->link->cur_link_settings); +} + +void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option) +{ + struct dc *core_dc = pipe_ctx->stream->ctx->dc; + + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + deallocate_mst_payload(pipe_ctx); + + core_dc->hwss.disable_stream(pipe_ctx, option); + + disable_link(pipe_ctx->stream->sink->link, pipe_ctx->stream->signal); +} + +void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) +{ + struct dc *core_dc = pipe_ctx->stream->ctx->dc; + + if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A) + return; + + core_dc->hwss.set_avmute(pipe_ctx, enable); +} + diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c new file mode 100644 index 000000000000..d5294798b0a5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c @@ -0,0 +1,775 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dm_helpers.h" +#include "gpio_service_interface.h" +#include "include/ddc_service_types.h" +#include "include/grph_object_id.h" +#include "include/dpcd_defs.h" +#include "include/logger_interface.h" +#include "include/vector.h" +#include "core_types.h" +#include "dc_link_ddc.h" + +#define AUX_POWER_UP_WA_DELAY 500 +#define I2C_OVER_AUX_DEFER_WA_DELAY 70 + +/* CV smart dongle slave address for retrieving supported HDTV modes*/ +#define CV_SMART_DONGLE_ADDRESS 0x20 +/* DVI-HDMI dongle slave address for retrieving dongle signature*/ +#define DVI_HDMI_DONGLE_ADDRESS 0x68 +static const int8_t dvi_hdmi_dongle_signature_str[] = "6140063500G"; +struct dvi_hdmi_dongle_signature_data { + int8_t vendor[3];/* "AMD" */ + uint8_t version[2]; + uint8_t size; + int8_t id[11];/* "6140063500G"*/ +}; +/* DP-HDMI dongle slave address for retrieving dongle signature*/ +#define DP_HDMI_DONGLE_ADDRESS 0x40 +static const uint8_t dp_hdmi_dongle_signature_str[] = "DP-HDMI ADAPTOR"; +#define DP_HDMI_DONGLE_SIGNATURE_EOT 0x04 + +struct dp_hdmi_dongle_signature_data { + int8_t id[15];/* "DP-HDMI ADAPTOR"*/ + uint8_t eot;/* end of transmition '\x4' */ +}; + +/* SCDC Address defines (HDMI 2.0)*/ +#define HDMI_SCDC_WRITE_UPDATE_0_ARRAY 3 +#define HDMI_SCDC_ADDRESS 0x54 +#define HDMI_SCDC_SINK_VERSION 0x01 +#define HDMI_SCDC_SOURCE_VERSION 0x02 +#define HDMI_SCDC_UPDATE_0 0x10 +#define HDMI_SCDC_TMDS_CONFIG 0x20 +#define HDMI_SCDC_SCRAMBLER_STATUS 0x21 +#define HDMI_SCDC_CONFIG_0 0x30 +#define HDMI_SCDC_STATUS_FLAGS 0x40 +#define HDMI_SCDC_ERR_DETECT 0x50 +#define HDMI_SCDC_TEST_CONFIG 0xC0 + +union hdmi_scdc_update_read_data { + uint8_t byte[2]; + struct { + uint8_t STATUS_UPDATE:1; + uint8_t CED_UPDATE:1; + uint8_t RR_TEST:1; + uint8_t RESERVED:5; + uint8_t RESERVED2:8; + } fields; +}; + +union hdmi_scdc_status_flags_data { + uint8_t byte[2]; + struct { + uint8_t CLOCK_DETECTED:1; + uint8_t CH0_LOCKED:1; + uint8_t CH1_LOCKED:1; + uint8_t CH2_LOCKED:1; + uint8_t RESERVED:4; + uint8_t RESERVED2:8; + } fields; +}; + +union hdmi_scdc_ced_data { + uint8_t byte[7]; + struct { + uint8_t CH0_8LOW:8; + uint8_t CH0_7HIGH:7; + uint8_t CH0_VALID:1; + uint8_t CH1_8LOW:8; + uint8_t CH1_7HIGH:7; + uint8_t CH1_VALID:1; + uint8_t CH2_8LOW:8; + uint8_t CH2_7HIGH:7; + uint8_t CH2_VALID:1; + uint8_t CHECKSUM:8; + } fields; +}; + +union hdmi_scdc_test_config_Data { + uint8_t byte; + struct { + uint8_t TEST_READ_REQUEST_DELAY:7; + uint8_t TEST_READ_REQUEST: 1; + } fields; +}; + +struct i2c_payloads { + struct vector payloads; +}; + +struct aux_payloads { + struct vector payloads; +}; + +static struct i2c_payloads *dal_ddc_i2c_payloads_create(struct dc_context *ctx, uint32_t count) +{ + struct i2c_payloads *payloads; + + payloads = kzalloc(sizeof(struct i2c_payloads), GFP_KERNEL); + + if (!payloads) + return NULL; + + if (dal_vector_construct( + &payloads->payloads, ctx, count, sizeof(struct i2c_payload))) + return payloads; + + kfree(payloads); + return NULL; + +} + +static struct i2c_payload *dal_ddc_i2c_payloads_get(struct i2c_payloads *p) +{ + return (struct i2c_payload *)p->payloads.container; +} + +static uint32_t dal_ddc_i2c_payloads_get_count(struct i2c_payloads *p) +{ + return p->payloads.count; +} + +static void dal_ddc_i2c_payloads_destroy(struct i2c_payloads **p) +{ + if (!p || !*p) + return; + dal_vector_destruct(&(*p)->payloads); + kfree(*p); + *p = NULL; + +} + +static struct aux_payloads *dal_ddc_aux_payloads_create(struct dc_context *ctx, uint32_t count) +{ + struct aux_payloads *payloads; + + payloads = kzalloc(sizeof(struct aux_payloads), GFP_KERNEL); + + if (!payloads) + return NULL; + + if (dal_vector_construct( + &payloads->payloads, ctx, count, sizeof(struct aux_payload))) + return payloads; + + kfree(payloads); + return NULL; +} + +static struct aux_payload *dal_ddc_aux_payloads_get(struct aux_payloads *p) +{ + return (struct aux_payload *)p->payloads.container; +} + +static uint32_t dal_ddc_aux_payloads_get_count(struct aux_payloads *p) +{ + return p->payloads.count; +} + +static void dal_ddc_aux_payloads_destroy(struct aux_payloads **p) +{ + if (!p || !*p) + return; + + dal_vector_destruct(&(*p)->payloads); + kfree(*p); + *p = NULL; +} + +#define DDC_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +void dal_ddc_i2c_payloads_add( + struct i2c_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write) +{ + uint32_t payload_size = EDID_SEGMENT_SIZE; + uint32_t pos; + + for (pos = 0; pos < len; pos += payload_size) { + struct i2c_payload payload = { + .write = write, + .address = address, + .length = DDC_MIN(payload_size, len - pos), + .data = data + pos }; + dal_vector_append(&payloads->payloads, &payload); + } + +} + +void dal_ddc_aux_payloads_add( + struct aux_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write) +{ + uint32_t payload_size = DEFAULT_AUX_MAX_DATA_SIZE; + uint32_t pos; + + for (pos = 0; pos < len; pos += payload_size) { + struct aux_payload payload = { + .i2c_over_aux = true, + .write = write, + .address = address, + .length = DDC_MIN(payload_size, len - pos), + .data = data + pos }; + dal_vector_append(&payloads->payloads, &payload); + } +} + +static void construct( + struct ddc_service *ddc_service, + struct ddc_service_init_data *init_data) +{ + enum connector_id connector_id = + dal_graphics_object_id_get_connector_id(init_data->id); + + struct gpio_service *gpio_service = init_data->ctx->gpio_service; + struct graphics_object_i2c_info i2c_info; + struct gpio_ddc_hw_info hw_info; + struct dc_bios *dcb = init_data->ctx->dc_bios; + + ddc_service->link = init_data->link; + ddc_service->ctx = init_data->ctx; + + if (BP_RESULT_OK != dcb->funcs->get_i2c_info(dcb, init_data->id, &i2c_info)) { + ddc_service->ddc_pin = NULL; + } else { + hw_info.ddc_channel = i2c_info.i2c_line; + hw_info.hw_supported = i2c_info.i2c_hw_assist; + + ddc_service->ddc_pin = dal_gpio_create_ddc( + gpio_service, + i2c_info.gpio_info.clk_a_register_index, + 1 << i2c_info.gpio_info.clk_a_shift, + &hw_info); + } + + ddc_service->flags.EDID_QUERY_DONE_ONCE = false; + ddc_service->flags.FORCE_READ_REPEATED_START = false; + ddc_service->flags.EDID_STRESS_READ = false; + + ddc_service->flags.IS_INTERNAL_DISPLAY = + connector_id == CONNECTOR_ID_EDP || + connector_id == CONNECTOR_ID_LVDS; + + ddc_service->wa.raw = 0; +} + +struct ddc_service *dal_ddc_service_create( + struct ddc_service_init_data *init_data) +{ + struct ddc_service *ddc_service; + + ddc_service = kzalloc(sizeof(struct ddc_service), GFP_KERNEL); + + if (!ddc_service) + return NULL; + + construct(ddc_service, init_data); + return ddc_service; +} + +static void destruct(struct ddc_service *ddc) +{ + if (ddc->ddc_pin) + dal_gpio_destroy_ddc(&ddc->ddc_pin); +} + +void dal_ddc_service_destroy(struct ddc_service **ddc) +{ + if (!ddc || !*ddc) { + BREAK_TO_DEBUGGER(); + return; + } + destruct(*ddc); + kfree(*ddc); + *ddc = NULL; +} + +enum ddc_service_type dal_ddc_service_get_type(struct ddc_service *ddc) +{ + return DDC_SERVICE_TYPE_CONNECTOR; +} + +void dal_ddc_service_set_transaction_type( + struct ddc_service *ddc, + enum ddc_transaction_type type) +{ + ddc->transaction_type = type; +} + +bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc) +{ + switch (ddc->transaction_type) { + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX: + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER: + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER: + return true; + default: + break; + } + return false; +} + +void ddc_service_set_dongle_type(struct ddc_service *ddc, + enum display_dongle_type dongle_type) +{ + ddc->dongle_type = dongle_type; +} + +static uint32_t defer_delay_converter_wa( + struct ddc_service *ddc, + uint32_t defer_delay) +{ + struct dc_link *link = ddc->link; + + if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_4 && + !memcmp(link->dpcd_caps.branch_dev_name, + DP_DVI_CONVERTER_ID_4, + sizeof(link->dpcd_caps.branch_dev_name))) + return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY ? + defer_delay : I2C_OVER_AUX_DEFER_WA_DELAY; + + return defer_delay; +} + +#define DP_TRANSLATOR_DELAY 5 + +uint32_t get_defer_delay(struct ddc_service *ddc) +{ + uint32_t defer_delay = 0; + + switch (ddc->transaction_type) { + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX: + if ((DISPLAY_DONGLE_DP_VGA_CONVERTER == ddc->dongle_type) || + (DISPLAY_DONGLE_DP_DVI_CONVERTER == ddc->dongle_type) || + (DISPLAY_DONGLE_DP_HDMI_CONVERTER == + ddc->dongle_type)) { + + defer_delay = DP_TRANSLATOR_DELAY; + + defer_delay = + defer_delay_converter_wa(ddc, defer_delay); + + } else /*sink has a delay different from an Active Converter*/ + defer_delay = 0; + break; + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER: + defer_delay = DP_TRANSLATOR_DELAY; + break; + default: + break; + } + return defer_delay; +} + +static bool i2c_read( + struct ddc_service *ddc, + uint32_t address, + uint8_t *buffer, + uint32_t len) +{ + uint8_t offs_data = 0; + struct i2c_payload payloads[2] = { + { + .write = true, + .address = address, + .length = 1, + .data = &offs_data }, + { + .write = false, + .address = address, + .length = len, + .data = buffer } }; + + struct i2c_command command = { + .payloads = payloads, + .number_of_payloads = 2, + .engine = DDC_I2C_COMMAND_ENGINE, + .speed = ddc->ctx->dc->caps.i2c_speed_in_khz }; + + return dm_helpers_submit_i2c( + ddc->ctx, + ddc->link, + &command); +} + +void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( + struct ddc_service *ddc, + struct display_sink_capability *sink_cap) +{ + uint8_t i; + bool is_valid_hdmi_signature; + enum display_dongle_type *dongle = &sink_cap->dongle_type; + uint8_t type2_dongle_buf[DP_ADAPTOR_TYPE2_SIZE]; + bool is_type2_dongle = false; + struct dp_hdmi_dongle_signature_data *dongle_signature; + + /* Assume we have no valid DP passive dongle connected */ + *dongle = DISPLAY_DONGLE_NONE; + sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK; + + /* Read DP-HDMI dongle I2c (no response interpreted as DP-DVI dongle)*/ + if (!i2c_read( + ddc, + DP_HDMI_DONGLE_ADDRESS, + type2_dongle_buf, + sizeof(type2_dongle_buf))) { + *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; + sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK; + + CONN_DATA_DETECT(ddc->link, type2_dongle_buf, sizeof(type2_dongle_buf), + "DP-DVI passive dongle %dMhz: ", + DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000); + return; + } + + /* Check if Type 2 dongle.*/ + if (type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_ID] == DP_ADAPTOR_TYPE2_ID) + is_type2_dongle = true; + + dongle_signature = + (struct dp_hdmi_dongle_signature_data *)type2_dongle_buf; + + is_valid_hdmi_signature = true; + + /* Check EOT */ + if (dongle_signature->eot != DP_HDMI_DONGLE_SIGNATURE_EOT) { + is_valid_hdmi_signature = false; + } + + /* Check signature */ + for (i = 0; i < sizeof(dongle_signature->id); ++i) { + /* If its not the right signature, + * skip mismatch in subversion byte.*/ + if (dongle_signature->id[i] != + dp_hdmi_dongle_signature_str[i] && i != 3) { + + if (is_type2_dongle) { + is_valid_hdmi_signature = false; + break; + } + + } + } + + if (is_type2_dongle) { + uint32_t max_tmds_clk = + type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK]; + + max_tmds_clk = max_tmds_clk * 2 + max_tmds_clk / 2; + + if (0 == max_tmds_clk || + max_tmds_clk < DP_ADAPTOR_TYPE2_MIN_TMDS_CLK || + max_tmds_clk > DP_ADAPTOR_TYPE2_MAX_TMDS_CLK) { + *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; + + CONN_DATA_DETECT(ddc->link, type2_dongle_buf, + sizeof(type2_dongle_buf), + "DP-DVI passive dongle %dMhz: ", + DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000); + } else { + if (is_valid_hdmi_signature == true) { + *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; + + CONN_DATA_DETECT(ddc->link, type2_dongle_buf, + sizeof(type2_dongle_buf), + "Type 2 DP-HDMI passive dongle %dMhz: ", + max_tmds_clk); + } else { + *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; + + CONN_DATA_DETECT(ddc->link, type2_dongle_buf, + sizeof(type2_dongle_buf), + "Type 2 DP-HDMI passive dongle (no signature) %dMhz: ", + max_tmds_clk); + + } + + /* Multiply by 1000 to convert to kHz. */ + sink_cap->max_hdmi_pixel_clock = + max_tmds_clk * 1000; + } + + } else { + if (is_valid_hdmi_signature == true) { + *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; + + CONN_DATA_DETECT(ddc->link, type2_dongle_buf, + sizeof(type2_dongle_buf), + "Type 1 DP-HDMI passive dongle %dMhz: ", + sink_cap->max_hdmi_pixel_clock / 1000); + } else { + *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; + + CONN_DATA_DETECT(ddc->link, type2_dongle_buf, + sizeof(type2_dongle_buf), + "Type 1 DP-HDMI passive dongle (no signature) %dMhz: ", + sink_cap->max_hdmi_pixel_clock / 1000); + } + } + + return; +} + +enum { + DP_SINK_CAP_SIZE = + DP_EDP_CONFIGURATION_CAP - DP_DPCD_REV + 1 +}; + +bool dal_ddc_service_query_ddc_data( + struct ddc_service *ddc, + uint32_t address, + uint8_t *write_buf, + uint32_t write_size, + uint8_t *read_buf, + uint32_t read_size) +{ + bool ret; + uint32_t payload_size = + dal_ddc_service_is_in_aux_transaction_mode(ddc) ? + DEFAULT_AUX_MAX_DATA_SIZE : EDID_SEGMENT_SIZE; + + uint32_t write_payloads = + (write_size + payload_size - 1) / payload_size; + + uint32_t read_payloads = + (read_size + payload_size - 1) / payload_size; + + uint32_t payloads_num = write_payloads + read_payloads; + + if (write_size > EDID_SEGMENT_SIZE || read_size > EDID_SEGMENT_SIZE) + return false; + + /*TODO: len of payload data for i2c and aux is uint8!!!!, + * but we want to read 256 over i2c!!!!*/ + if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) { + + struct aux_payloads *payloads = + dal_ddc_aux_payloads_create(ddc->ctx, payloads_num); + + struct aux_command command = { + .payloads = dal_ddc_aux_payloads_get(payloads), + .number_of_payloads = 0, + .defer_delay = get_defer_delay(ddc), + .max_defer_write_retry = 0 }; + + dal_ddc_aux_payloads_add( + payloads, address, write_size, write_buf, true); + + dal_ddc_aux_payloads_add( + payloads, address, read_size, read_buf, false); + + command.number_of_payloads = + dal_ddc_aux_payloads_get_count(payloads); + + ret = dal_i2caux_submit_aux_command( + ddc->ctx->i2caux, + ddc->ddc_pin, + &command); + + dal_ddc_aux_payloads_destroy(&payloads); + + } else { + struct i2c_payloads *payloads = + dal_ddc_i2c_payloads_create(ddc->ctx, payloads_num); + + struct i2c_command command = { + .payloads = dal_ddc_i2c_payloads_get(payloads), + .number_of_payloads = 0, + .engine = DDC_I2C_COMMAND_ENGINE, + .speed = ddc->ctx->dc->caps.i2c_speed_in_khz }; + + dal_ddc_i2c_payloads_add( + payloads, address, write_size, write_buf, true); + + dal_ddc_i2c_payloads_add( + payloads, address, read_size, read_buf, false); + + command.number_of_payloads = + dal_ddc_i2c_payloads_get_count(payloads); + + ret = dm_helpers_submit_i2c( + ddc->ctx, + ddc->link, + &command); + + dal_ddc_i2c_payloads_destroy(&payloads); + } + + return ret; +} + +enum ddc_result dal_ddc_service_read_dpcd_data( + struct ddc_service *ddc, + bool i2c, + enum i2c_mot_mode mot, + uint32_t address, + uint8_t *data, + uint32_t len) +{ + struct aux_payload read_payload = { + .i2c_over_aux = i2c, + .write = false, + .address = address, + .length = len, + .data = data, + }; + struct aux_command command = { + .payloads = &read_payload, + .number_of_payloads = 1, + .defer_delay = 0, + .max_defer_write_retry = 0, + .mot = mot + }; + + if (len > DEFAULT_AUX_MAX_DATA_SIZE) { + BREAK_TO_DEBUGGER(); + return DDC_RESULT_FAILED_INVALID_OPERATION; + } + + if (dal_i2caux_submit_aux_command( + ddc->ctx->i2caux, + ddc->ddc_pin, + &command)) + return DDC_RESULT_SUCESSFULL; + + return DDC_RESULT_FAILED_OPERATION; +} + +enum ddc_result dal_ddc_service_write_dpcd_data( + struct ddc_service *ddc, + bool i2c, + enum i2c_mot_mode mot, + uint32_t address, + const uint8_t *data, + uint32_t len) +{ + struct aux_payload write_payload = { + .i2c_over_aux = i2c, + .write = true, + .address = address, + .length = len, + .data = (uint8_t *)data, + }; + struct aux_command command = { + .payloads = &write_payload, + .number_of_payloads = 1, + .defer_delay = 0, + .max_defer_write_retry = 0, + .mot = mot + }; + + if (len > DEFAULT_AUX_MAX_DATA_SIZE) { + BREAK_TO_DEBUGGER(); + return DDC_RESULT_FAILED_INVALID_OPERATION; + } + + if (dal_i2caux_submit_aux_command( + ddc->ctx->i2caux, + ddc->ddc_pin, + &command)) + return DDC_RESULT_SUCESSFULL; + + return DDC_RESULT_FAILED_OPERATION; +} + +/*test only function*/ +void dal_ddc_service_set_ddc_pin( + struct ddc_service *ddc_service, + struct ddc *ddc) +{ + ddc_service->ddc_pin = ddc; +} + +struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service) +{ + return ddc_service->ddc_pin; +} + +void dal_ddc_service_write_scdc_data(struct ddc_service *ddc_service, + uint32_t pix_clk, + bool lte_340_scramble) +{ + bool over_340_mhz = pix_clk > 340000 ? 1 : 0; + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_SINK_VERSION; + uint8_t sink_version = 0; + uint8_t write_buffer[2] = {0}; + /*Lower than 340 Scramble bit from SCDC caps*/ + + dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset, + sizeof(offset), &sink_version, sizeof(sink_version)); + if (sink_version == 1) { + /*Source Version = 1*/ + write_buffer[0] = HDMI_SCDC_SOURCE_VERSION; + write_buffer[1] = 1; + dal_ddc_service_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + /*Read Request from SCDC caps*/ + } + write_buffer[0] = HDMI_SCDC_TMDS_CONFIG; + + if (over_340_mhz) { + write_buffer[1] = 3; + } else if (lte_340_scramble) { + write_buffer[1] = 1; + } else { + write_buffer[1] = 0; + } + dal_ddc_service_query_ddc_data(ddc_service, slave_address, write_buffer, + sizeof(write_buffer), NULL, 0); +} + +void dal_ddc_service_read_scdc_data(struct ddc_service *ddc_service) +{ + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_TMDS_CONFIG; + uint8_t tmds_config = 0; + + dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset, + sizeof(offset), &tmds_config, sizeof(tmds_config)); + if (tmds_config & 0x1) { + union hdmi_scdc_status_flags_data status_data = { {0} }; + uint8_t scramble_status = 0; + + offset = HDMI_SCDC_SCRAMBLER_STATUS; + dal_ddc_service_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), &scramble_status, + sizeof(scramble_status)); + offset = HDMI_SCDC_STATUS_FLAGS; + dal_ddc_service_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), status_data.byte, + sizeof(status_data.byte)); + } +} + diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c new file mode 100644 index 000000000000..ced42484dcfc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -0,0 +1,2587 @@ +/* Copyright 2015 Advanced Micro Devices, Inc. */ +#include "dm_services.h" +#include "dc.h" +#include "dc_link_dp.h" +#include "dm_helpers.h" + +#include "inc/core_types.h" +#include "link_hwss.h" +#include "dc_link_ddc.h" +#include "core_status.h" +#include "dpcd_defs.h" + +#include "resource.h" + +/* maximum pre emphasis level allowed for each voltage swing level*/ +static const enum dc_pre_emphasis voltage_swing_to_pre_emphasis[] = { + PRE_EMPHASIS_LEVEL3, + PRE_EMPHASIS_LEVEL2, + PRE_EMPHASIS_LEVEL1, + PRE_EMPHASIS_DISABLED }; + +enum { + POST_LT_ADJ_REQ_LIMIT = 6, + POST_LT_ADJ_REQ_TIMEOUT = 200 +}; + +enum { + LINK_TRAINING_MAX_RETRY_COUNT = 5, + /* to avoid infinite loop where-in the receiver + * switches between different VS + */ + LINK_TRAINING_MAX_CR_RETRY = 100 +}; + +static bool decide_fallback_link_setting( + struct dc_link_settings initial_link_settings, + struct dc_link_settings *current_link_setting, + enum link_training_result training_result); +static struct dc_link_settings get_common_supported_link_settings ( + struct dc_link_settings link_setting_a, + struct dc_link_settings link_setting_b); + +static void wait_for_training_aux_rd_interval( + struct dc_link *link, + uint32_t default_wait_in_micro_secs) +{ + union training_aux_rd_interval training_rd_interval; + + /* overwrite the delay if rev > 1.1*/ + if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) { + /* DP 1.2 or later - retrieve delay through + * "DPCD_ADDR_TRAINING_AUX_RD_INTERVAL" register */ + core_link_read_dpcd( + link, + DP_TRAINING_AUX_RD_INTERVAL, + (uint8_t *)&training_rd_interval, + sizeof(training_rd_interval)); + + if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) + default_wait_in_micro_secs = + training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; + } + + udelay(default_wait_in_micro_secs); + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s:\n wait = %d\n", + __func__, + default_wait_in_micro_secs); +} + +static void dpcd_set_training_pattern( + struct dc_link *link, + union dpcd_training_pattern dpcd_pattern) +{ + core_link_write_dpcd( + link, + DP_TRAINING_PATTERN_SET, + &dpcd_pattern.raw, + 1); + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s\n %x pattern = %x\n", + __func__, + DP_TRAINING_PATTERN_SET, + dpcd_pattern.v1_4.TRAINING_PATTERN_SET); +} + +static void dpcd_set_link_settings( + struct dc_link *link, + const struct link_training_settings *lt_settings) +{ + uint8_t rate = (uint8_t) + (lt_settings->link_settings.link_rate); + + union down_spread_ctrl downspread = {{0}}; + union lane_count_set lane_count_set = {{0}}; + uint8_t link_set_buffer[2]; + + downspread.raw = (uint8_t) + (lt_settings->link_settings.link_spread); + + lane_count_set.bits.LANE_COUNT_SET = + lt_settings->link_settings.lane_count; + + lane_count_set.bits.ENHANCED_FRAMING = 1; + + lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = + link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED; + + link_set_buffer[0] = rate; + link_set_buffer[1] = lane_count_set.raw; + + core_link_write_dpcd(link, DP_LINK_BW_SET, + link_set_buffer, 2); + core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, + &downspread.raw, sizeof(downspread)); + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n", + __func__, + DP_LINK_BW_SET, + lt_settings->link_settings.link_rate, + DP_LANE_COUNT_SET, + lt_settings->link_settings.lane_count, + DP_DOWNSPREAD_CTRL, + lt_settings->link_settings.link_spread); + +} + +static enum dpcd_training_patterns + hw_training_pattern_to_dpcd_training_pattern( + struct dc_link *link, + enum hw_dp_training_pattern pattern) +{ + enum dpcd_training_patterns dpcd_tr_pattern = + DPCD_TRAINING_PATTERN_VIDEOIDLE; + + switch (pattern) { + case HW_DP_TRAINING_PATTERN_1: + dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1; + break; + case HW_DP_TRAINING_PATTERN_2: + dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2; + break; + case HW_DP_TRAINING_PATTERN_3: + dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3; + break; + case HW_DP_TRAINING_PATTERN_4: + dpcd_tr_pattern = DPCD_TRAINING_PATTERN_4; + break; + default: + ASSERT(0); + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s: Invalid HW Training pattern: %d\n", + __func__, pattern); + break; + } + + return dpcd_tr_pattern; + +} + +static void dpcd_set_lt_pattern_and_lane_settings( + struct dc_link *link, + const struct link_training_settings *lt_settings, + enum hw_dp_training_pattern pattern) +{ + union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}}; + const uint32_t dpcd_base_lt_offset = + DP_TRAINING_PATTERN_SET; + uint8_t dpcd_lt_buffer[5] = {0}; + union dpcd_training_pattern dpcd_pattern = {{0}}; + uint32_t lane; + uint32_t size_in_bytes; + bool edp_workaround = false; /* TODO link_prop.INTERNAL */ + + /***************************************************************** + * DpcdAddress_TrainingPatternSet + *****************************************************************/ + dpcd_pattern.v1_4.TRAINING_PATTERN_SET = + hw_training_pattern_to_dpcd_training_pattern(link, pattern); + + dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - dpcd_base_lt_offset] + = dpcd_pattern.raw; + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s\n %x pattern = %x\n", + __func__, + DP_TRAINING_PATTERN_SET, + dpcd_pattern.v1_4.TRAINING_PATTERN_SET); + + /***************************************************************** + * DpcdAddress_Lane0Set -> DpcdAddress_Lane3Set + *****************************************************************/ + for (lane = 0; lane < + (uint32_t)(lt_settings->link_settings.lane_count); lane++) { + + dpcd_lane[lane].bits.VOLTAGE_SWING_SET = + (uint8_t)(lt_settings->lane_settings[lane].VOLTAGE_SWING); + dpcd_lane[lane].bits.PRE_EMPHASIS_SET = + (uint8_t)(lt_settings->lane_settings[lane].PRE_EMPHASIS); + + dpcd_lane[lane].bits.MAX_SWING_REACHED = + (lt_settings->lane_settings[lane].VOLTAGE_SWING == + VOLTAGE_SWING_MAX_LEVEL ? 1 : 0); + dpcd_lane[lane].bits.MAX_PRE_EMPHASIS_REACHED = + (lt_settings->lane_settings[lane].PRE_EMPHASIS == + PRE_EMPHASIS_MAX_LEVEL ? 1 : 0); + } + + /* concatinate everything into one buffer*/ + + size_in_bytes = lt_settings->link_settings.lane_count * sizeof(dpcd_lane[0]); + + // 0x00103 - 0x00102 + memmove( + &dpcd_lt_buffer[DP_TRAINING_LANE0_SET - dpcd_base_lt_offset], + dpcd_lane, + size_in_bytes); + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s:\n %x VS set = %x PE set = %x \ + max VS Reached = %x max PE Reached = %x\n", + __func__, + DP_TRAINING_LANE0_SET, + dpcd_lane[0].bits.VOLTAGE_SWING_SET, + dpcd_lane[0].bits.PRE_EMPHASIS_SET, + dpcd_lane[0].bits.MAX_SWING_REACHED, + dpcd_lane[0].bits.MAX_PRE_EMPHASIS_REACHED); + + if (edp_workaround) { + /* for eDP write in 2 parts because the 5-byte burst is + * causing issues on some eDP panels (EPR#366724) + */ + core_link_write_dpcd( + link, + DP_TRAINING_PATTERN_SET, + &dpcd_pattern.raw, + sizeof(dpcd_pattern.raw) ); + + core_link_write_dpcd( + link, + DP_TRAINING_LANE0_SET, + (uint8_t *)(dpcd_lane), + size_in_bytes); + + } else + /* write it all in (1 + number-of-lanes)-byte burst*/ + core_link_write_dpcd( + link, + dpcd_base_lt_offset, + dpcd_lt_buffer, + size_in_bytes + sizeof(dpcd_pattern.raw) ); + + link->cur_lane_setting = lt_settings->lane_settings[0]; +} + +static bool is_cr_done(enum dc_lane_count ln_count, + union lane_status *dpcd_lane_status) +{ + bool done = true; + uint32_t lane; + /*LANEx_CR_DONE bits All 1's?*/ + for (lane = 0; lane < (uint32_t)(ln_count); lane++) { + if (!dpcd_lane_status[lane].bits.CR_DONE_0) + done = false; + } + return done; + +} + +static bool is_ch_eq_done(enum dc_lane_count ln_count, + union lane_status *dpcd_lane_status, + union lane_align_status_updated *lane_status_updated) +{ + bool done = true; + uint32_t lane; + if (!lane_status_updated->bits.INTERLANE_ALIGN_DONE) + done = false; + else { + for (lane = 0; lane < (uint32_t)(ln_count); lane++) { + if (!dpcd_lane_status[lane].bits.SYMBOL_LOCKED_0 || + !dpcd_lane_status[lane].bits.CHANNEL_EQ_DONE_0) + done = false; + } + } + return done; + +} + +static void update_drive_settings( + struct link_training_settings *dest, + struct link_training_settings src) +{ + uint32_t lane; + for (lane = 0; lane < src.link_settings.lane_count; lane++) { + dest->lane_settings[lane].VOLTAGE_SWING = + src.lane_settings[lane].VOLTAGE_SWING; + dest->lane_settings[lane].PRE_EMPHASIS = + src.lane_settings[lane].PRE_EMPHASIS; + dest->lane_settings[lane].POST_CURSOR2 = + src.lane_settings[lane].POST_CURSOR2; + } +} + +static uint8_t get_nibble_at_index(const uint8_t *buf, + uint32_t index) +{ + uint8_t nibble; + nibble = buf[index / 2]; + + if (index % 2) + nibble >>= 4; + else + nibble &= 0x0F; + + return nibble; +} + +static enum dc_pre_emphasis get_max_pre_emphasis_for_voltage_swing( + enum dc_voltage_swing voltage) +{ + enum dc_pre_emphasis pre_emphasis; + pre_emphasis = PRE_EMPHASIS_MAX_LEVEL; + + if (voltage <= VOLTAGE_SWING_MAX_LEVEL) + pre_emphasis = voltage_swing_to_pre_emphasis[voltage]; + + return pre_emphasis; + +} + +static void find_max_drive_settings( + const struct link_training_settings *link_training_setting, + struct link_training_settings *max_lt_setting) +{ + uint32_t lane; + struct dc_lane_settings max_requested; + + max_requested.VOLTAGE_SWING = + link_training_setting-> + lane_settings[0].VOLTAGE_SWING; + max_requested.PRE_EMPHASIS = + link_training_setting-> + lane_settings[0].PRE_EMPHASIS; + /*max_requested.postCursor2 = + * link_training_setting->laneSettings[0].postCursor2;*/ + + /* Determine what the maximum of the requested settings are*/ + for (lane = 1; lane < link_training_setting->link_settings.lane_count; + lane++) { + if (link_training_setting->lane_settings[lane].VOLTAGE_SWING > + max_requested.VOLTAGE_SWING) + + max_requested.VOLTAGE_SWING = + link_training_setting-> + lane_settings[lane].VOLTAGE_SWING; + + if (link_training_setting->lane_settings[lane].PRE_EMPHASIS > + max_requested.PRE_EMPHASIS) + max_requested.PRE_EMPHASIS = + link_training_setting-> + lane_settings[lane].PRE_EMPHASIS; + + /* + if (link_training_setting->laneSettings[lane].postCursor2 > + max_requested.postCursor2) + { + max_requested.postCursor2 = + link_training_setting->laneSettings[lane].postCursor2; + } + */ + } + + /* make sure the requested settings are + * not higher than maximum settings*/ + if (max_requested.VOLTAGE_SWING > VOLTAGE_SWING_MAX_LEVEL) + max_requested.VOLTAGE_SWING = VOLTAGE_SWING_MAX_LEVEL; + + if (max_requested.PRE_EMPHASIS > PRE_EMPHASIS_MAX_LEVEL) + max_requested.PRE_EMPHASIS = PRE_EMPHASIS_MAX_LEVEL; + /* + if (max_requested.postCursor2 > PostCursor2_MaxLevel) + max_requested.postCursor2 = PostCursor2_MaxLevel; + */ + + /* make sure the pre-emphasis matches the voltage swing*/ + if (max_requested.PRE_EMPHASIS > + get_max_pre_emphasis_for_voltage_swing( + max_requested.VOLTAGE_SWING)) + max_requested.PRE_EMPHASIS = + get_max_pre_emphasis_for_voltage_swing( + max_requested.VOLTAGE_SWING); + + /* + * Post Cursor2 levels are completely independent from + * pre-emphasis (Post Cursor1) levels. But Post Cursor2 levels + * can only be applied to each allowable combination of voltage + * swing and pre-emphasis levels */ + /* if ( max_requested.postCursor2 > + * getMaxPostCursor2ForVoltageSwing(max_requested.voltageSwing)) + * max_requested.postCursor2 = + * getMaxPostCursor2ForVoltageSwing(max_requested.voltageSwing); + */ + + max_lt_setting->link_settings.link_rate = + link_training_setting->link_settings.link_rate; + max_lt_setting->link_settings.lane_count = + link_training_setting->link_settings.lane_count; + max_lt_setting->link_settings.link_spread = + link_training_setting->link_settings.link_spread; + + for (lane = 0; lane < + link_training_setting->link_settings.lane_count; + lane++) { + max_lt_setting->lane_settings[lane].VOLTAGE_SWING = + max_requested.VOLTAGE_SWING; + max_lt_setting->lane_settings[lane].PRE_EMPHASIS = + max_requested.PRE_EMPHASIS; + /*max_lt_setting->laneSettings[lane].postCursor2 = + * max_requested.postCursor2; + */ + } + +} + +static void get_lane_status_and_drive_settings( + struct dc_link *link, + const struct link_training_settings *link_training_setting, + union lane_status *ln_status, + union lane_align_status_updated *ln_status_updated, + struct link_training_settings *req_settings) +{ + uint8_t dpcd_buf[6] = {0}; + union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {{{0}}}; + struct link_training_settings request_settings = {{0}}; + uint32_t lane; + + memset(req_settings, '\0', sizeof(struct link_training_settings)); + + core_link_read_dpcd( + link, + DP_LANE0_1_STATUS, + (uint8_t *)(dpcd_buf), + sizeof(dpcd_buf)); + + for (lane = 0; lane < + (uint32_t)(link_training_setting->link_settings.lane_count); + lane++) { + + ln_status[lane].raw = + get_nibble_at_index(&dpcd_buf[0], lane); + dpcd_lane_adjust[lane].raw = + get_nibble_at_index(&dpcd_buf[4], lane); + } + + ln_status_updated->raw = dpcd_buf[2]; + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s:\n%x Lane01Status = %x\n %x Lane23Status = %x\n ", + __func__, + DP_LANE0_1_STATUS, dpcd_buf[0], + DP_LANE2_3_STATUS, dpcd_buf[1]); + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s:\n %x Lane01AdjustRequest = %x\n %x Lane23AdjustRequest = %x\n", + __func__, + DP_ADJUST_REQUEST_LANE0_1, + dpcd_buf[4], + DP_ADJUST_REQUEST_LANE2_3, + dpcd_buf[5]); + + /*copy to req_settings*/ + request_settings.link_settings.lane_count = + link_training_setting->link_settings.lane_count; + request_settings.link_settings.link_rate = + link_training_setting->link_settings.link_rate; + request_settings.link_settings.link_spread = + link_training_setting->link_settings.link_spread; + + for (lane = 0; lane < + (uint32_t)(link_training_setting->link_settings.lane_count); + lane++) { + + request_settings.lane_settings[lane].VOLTAGE_SWING = + (enum dc_voltage_swing)(dpcd_lane_adjust[lane].bits. + VOLTAGE_SWING_LANE); + request_settings.lane_settings[lane].PRE_EMPHASIS = + (enum dc_pre_emphasis)(dpcd_lane_adjust[lane].bits. + PRE_EMPHASIS_LANE); + } + + /*Note: for postcursor2, read adjusted + * postcursor2 settings from*/ + /*DpcdAddress_AdjustRequestPostCursor2 = + *0x020C (not implemented yet)*/ + + /* we find the maximum of the requested settings across all lanes*/ + /* and set this maximum for all lanes*/ + find_max_drive_settings(&request_settings, req_settings); + + /* if post cursor 2 is needed in the future, + * read DpcdAddress_AdjustRequestPostCursor2 = 0x020C + */ + +} + +static void dpcd_set_lane_settings( + struct dc_link *link, + const struct link_training_settings *link_training_setting) +{ + union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {{{0}}}; + uint32_t lane; + + for (lane = 0; lane < + (uint32_t)(link_training_setting-> + link_settings.lane_count); + lane++) { + dpcd_lane[lane].bits.VOLTAGE_SWING_SET = + (uint8_t)(link_training_setting-> + lane_settings[lane].VOLTAGE_SWING); + dpcd_lane[lane].bits.PRE_EMPHASIS_SET = + (uint8_t)(link_training_setting-> + lane_settings[lane].PRE_EMPHASIS); + dpcd_lane[lane].bits.MAX_SWING_REACHED = + (link_training_setting-> + lane_settings[lane].VOLTAGE_SWING == + VOLTAGE_SWING_MAX_LEVEL ? 1 : 0); + dpcd_lane[lane].bits.MAX_PRE_EMPHASIS_REACHED = + (link_training_setting-> + lane_settings[lane].PRE_EMPHASIS == + PRE_EMPHASIS_MAX_LEVEL ? 1 : 0); + } + + core_link_write_dpcd(link, + DP_TRAINING_LANE0_SET, + (uint8_t *)(dpcd_lane), + link_training_setting->link_settings.lane_count); + + /* + if (LTSettings.link.rate == LinkRate_High2) + { + DpcdTrainingLaneSet2 dpcd_lane2[lane_count_DPMax] = {0}; + for ( uint32_t lane = 0; + lane < lane_count_DPMax; lane++) + { + dpcd_lane2[lane].bits.post_cursor2_set = + static_cast( + LTSettings.laneSettings[lane].postCursor2); + dpcd_lane2[lane].bits.max_post_cursor2_reached = 0; + } + m_pDpcdAccessSrv->WriteDpcdData( + DpcdAddress_Lane0Set2, + reinterpret_cast(dpcd_lane2), + LTSettings.link.lanes); + } + */ + + dm_logger_write(link->ctx->logger, LOG_HW_LINK_TRAINING, + "%s\n %x VS set = %x PE set = %x \ + max VS Reached = %x max PE Reached = %x\n", + __func__, + DP_TRAINING_LANE0_SET, + dpcd_lane[0].bits.VOLTAGE_SWING_SET, + dpcd_lane[0].bits.PRE_EMPHASIS_SET, + dpcd_lane[0].bits.MAX_SWING_REACHED, + dpcd_lane[0].bits.MAX_PRE_EMPHASIS_REACHED); + + link->cur_lane_setting = link_training_setting->lane_settings[0]; + +} + +static bool is_max_vs_reached( + const struct link_training_settings *lt_settings) +{ + uint32_t lane; + for (lane = 0; lane < + (uint32_t)(lt_settings->link_settings.lane_count); + lane++) { + if (lt_settings->lane_settings[lane].VOLTAGE_SWING + == VOLTAGE_SWING_MAX_LEVEL) + return true; + } + return false; + +} + +void dc_link_dp_set_drive_settings( + struct dc_link *link, + struct link_training_settings *lt_settings) +{ + /* program ASIC PHY settings*/ + dp_set_hw_lane_settings(link, lt_settings); + + /* Notify DP sink the PHY settings from source */ + dpcd_set_lane_settings(link, lt_settings); +} + +static bool perform_post_lt_adj_req_sequence( + struct dc_link *link, + struct link_training_settings *lt_settings) +{ + enum dc_lane_count lane_count = + lt_settings->link_settings.lane_count; + + uint32_t adj_req_count; + uint32_t adj_req_timer; + bool req_drv_setting_changed; + uint32_t lane; + + req_drv_setting_changed = false; + for (adj_req_count = 0; adj_req_count < POST_LT_ADJ_REQ_LIMIT; + adj_req_count++) { + + req_drv_setting_changed = false; + + for (adj_req_timer = 0; + adj_req_timer < POST_LT_ADJ_REQ_TIMEOUT; + adj_req_timer++) { + + struct link_training_settings req_settings; + union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; + union lane_align_status_updated + dpcd_lane_status_updated; + + get_lane_status_and_drive_settings( + link, + lt_settings, + dpcd_lane_status, + &dpcd_lane_status_updated, + &req_settings); + + if (dpcd_lane_status_updated.bits. + POST_LT_ADJ_REQ_IN_PROGRESS == 0) + return true; + + if (!is_cr_done(lane_count, dpcd_lane_status)) + return false; + + if (!is_ch_eq_done( + lane_count, + dpcd_lane_status, + &dpcd_lane_status_updated)) + return false; + + for (lane = 0; lane < (uint32_t)(lane_count); lane++) { + + if (lt_settings-> + lane_settings[lane].VOLTAGE_SWING != + req_settings.lane_settings[lane]. + VOLTAGE_SWING || + lt_settings->lane_settings[lane].PRE_EMPHASIS != + req_settings.lane_settings[lane].PRE_EMPHASIS) { + + req_drv_setting_changed = true; + break; + } + } + + if (req_drv_setting_changed) { + update_drive_settings( + lt_settings,req_settings); + + dc_link_dp_set_drive_settings(link, + lt_settings); + break; + } + + msleep(1); + } + + if (!req_drv_setting_changed) { + dm_logger_write(link->ctx->logger, LOG_WARNING, + "%s: Post Link Training Adjust Request Timed out\n", + __func__); + + ASSERT(0); + return true; + } + } + dm_logger_write(link->ctx->logger, LOG_WARNING, + "%s: Post Link Training Adjust Request limit reached\n", + __func__); + + ASSERT(0); + return true; + +} + +static enum hw_dp_training_pattern get_supported_tp(struct dc_link *link) +{ + enum hw_dp_training_pattern highest_tp = HW_DP_TRAINING_PATTERN_2; + struct encoder_feature_support *features = &link->link_enc->features; + struct dpcd_caps *dpcd_caps = &link->dpcd_caps; + + if (features->flags.bits.IS_TPS3_CAPABLE) + highest_tp = HW_DP_TRAINING_PATTERN_3; + + if (features->flags.bits.IS_TPS4_CAPABLE) + highest_tp = HW_DP_TRAINING_PATTERN_4; + + if (dpcd_caps->max_down_spread.bits.TPS4_SUPPORTED && + highest_tp >= HW_DP_TRAINING_PATTERN_4) + return HW_DP_TRAINING_PATTERN_4; + + if (dpcd_caps->max_ln_count.bits.TPS3_SUPPORTED && + highest_tp >= HW_DP_TRAINING_PATTERN_3) + return HW_DP_TRAINING_PATTERN_3; + + return HW_DP_TRAINING_PATTERN_2; +} + +static enum link_training_result perform_channel_equalization_sequence( + struct dc_link *link, + struct link_training_settings *lt_settings) +{ + struct link_training_settings req_settings; + enum hw_dp_training_pattern hw_tr_pattern; + uint32_t retries_ch_eq; + enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; + union lane_align_status_updated dpcd_lane_status_updated = {{0}}; + union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {{{0}}};; + + hw_tr_pattern = get_supported_tp(link); + + dp_set_hw_training_pattern(link, hw_tr_pattern); + + for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; + retries_ch_eq++) { + + dp_set_hw_lane_settings(link, lt_settings); + + /* 2. update DPCD*/ + if (!retries_ch_eq) + /* EPR #361076 - write as a 5-byte burst, + * but only for the 1-st iteration*/ + dpcd_set_lt_pattern_and_lane_settings( + link, + lt_settings, + hw_tr_pattern); + else + dpcd_set_lane_settings(link, lt_settings); + + /* 3. wait for receiver to lock-on*/ + wait_for_training_aux_rd_interval(link, 400); + + /* 4. Read lane status and requested + * drive settings as set by the sink*/ + + get_lane_status_and_drive_settings( + link, + lt_settings, + dpcd_lane_status, + &dpcd_lane_status_updated, + &req_settings); + + /* 5. check CR done*/ + if (!is_cr_done(lane_count, dpcd_lane_status)) + return LINK_TRAINING_EQ_FAIL_CR; + + /* 6. check CHEQ done*/ + if (is_ch_eq_done(lane_count, + dpcd_lane_status, + &dpcd_lane_status_updated)) + return LINK_TRAINING_SUCCESS; + + /* 7. update VS/PE/PC2 in lt_settings*/ + update_drive_settings(lt_settings, req_settings); + } + + return LINK_TRAINING_EQ_FAIL_EQ; + +} + +static bool perform_clock_recovery_sequence( + struct dc_link *link, + struct link_training_settings *lt_settings) +{ + uint32_t retries_cr; + uint32_t retry_count; + uint32_t lane; + struct link_training_settings req_settings; + enum dc_lane_count lane_count = + lt_settings->link_settings.lane_count; + enum hw_dp_training_pattern hw_tr_pattern = HW_DP_TRAINING_PATTERN_1; + union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; + union lane_align_status_updated dpcd_lane_status_updated; + + retries_cr = 0; + retry_count = 0; + /* initial drive setting (VS/PE/PC2)*/ + for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { + lt_settings->lane_settings[lane].VOLTAGE_SWING = + VOLTAGE_SWING_LEVEL0; + lt_settings->lane_settings[lane].PRE_EMPHASIS = + PRE_EMPHASIS_DISABLED; + lt_settings->lane_settings[lane].POST_CURSOR2 = + POST_CURSOR2_DISABLED; + } + + dp_set_hw_training_pattern(link, hw_tr_pattern); + + /* najeeb - The synaptics MST hub can put the LT in + * infinite loop by switching the VS + */ + /* between level 0 and level 1 continuously, here + * we try for CR lock for LinkTrainingMaxCRRetry count*/ + while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && + (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { + + memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status)); + memset(&dpcd_lane_status_updated, '\0', + sizeof(dpcd_lane_status_updated)); + + /* 1. call HWSS to set lane settings*/ + dp_set_hw_lane_settings( + link, + lt_settings); + + /* 2. update DPCD of the receiver*/ + if (!retries_cr) + /* EPR #361076 - write as a 5-byte burst, + * but only for the 1-st iteration.*/ + dpcd_set_lt_pattern_and_lane_settings( + link, + lt_settings, + hw_tr_pattern); + else + dpcd_set_lane_settings( + link, + lt_settings); + + /* 3. wait receiver to lock-on*/ + wait_for_training_aux_rd_interval( + link, + 100); + + /* 4. Read lane status and requested drive + * settings as set by the sink + */ + get_lane_status_and_drive_settings( + link, + lt_settings, + dpcd_lane_status, + &dpcd_lane_status_updated, + &req_settings); + + /* 5. check CR done*/ + if (is_cr_done(lane_count, dpcd_lane_status)) + return true; + + /* 6. max VS reached*/ + if (is_max_vs_reached(lt_settings)) + return false; + + /* 7. same voltage*/ + /* Note: VS same for all lanes, + * so comparing first lane is sufficient*/ + if (lt_settings->lane_settings[0].VOLTAGE_SWING == + req_settings.lane_settings[0].VOLTAGE_SWING) + retries_cr++; + else + retries_cr = 0; + + /* 8. update VS/PE/PC2 in lt_settings*/ + update_drive_settings(lt_settings, req_settings); + + retry_count++; + } + + if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) { + ASSERT(0); + dm_logger_write(link->ctx->logger, LOG_ERROR, + "%s: Link Training Error, could not \ + get CR after %d tries. \ + Possibly voltage swing issue", __func__, + LINK_TRAINING_MAX_CR_RETRY); + + } + + return false; +} + +static inline bool perform_link_training_int( + struct dc_link *link, + struct link_training_settings *lt_settings, + bool status) +{ + union lane_count_set lane_count_set = { {0} }; + union dpcd_training_pattern dpcd_pattern = { {0} }; + + /* 3. set training not in progress*/ + dpcd_pattern.v1_4.TRAINING_PATTERN_SET = DPCD_TRAINING_PATTERN_VIDEOIDLE; + dpcd_set_training_pattern(link, dpcd_pattern); + + /* 4. mainlink output idle pattern*/ + dp_set_hw_test_pattern(link, DP_TEST_PATTERN_VIDEO_MODE, NULL, 0); + + /* + * 5. post training adjust if required + * If the upstream DPTX and downstream DPRX both support TPS4, + * TPS4 must be used instead of POST_LT_ADJ_REQ. + */ + if (link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED != 1 || + get_supported_tp(link) == HW_DP_TRAINING_PATTERN_4) + return status; + + if (status && + perform_post_lt_adj_req_sequence(link, lt_settings) == false) + status = false; + + lane_count_set.bits.LANE_COUNT_SET = lt_settings->link_settings.lane_count; + lane_count_set.bits.ENHANCED_FRAMING = 1; + lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; + + core_link_write_dpcd( + link, + DP_LANE_COUNT_SET, + &lane_count_set.raw, + sizeof(lane_count_set)); + + return status; +} + +enum link_training_result dc_link_dp_perform_link_training( + struct dc_link *link, + const struct dc_link_settings *link_setting, + bool skip_video_pattern) +{ + enum link_training_result status = LINK_TRAINING_SUCCESS; + + char *link_rate = "Unknown"; + struct link_training_settings lt_settings; + + memset(<_settings, '\0', sizeof(lt_settings)); + + lt_settings.link_settings.link_rate = link_setting->link_rate; + lt_settings.link_settings.lane_count = link_setting->lane_count; + + /*@todo[vdevulap] move SS to LS, should not be handled by displaypath*/ + + /* TODO hard coded to SS for now + * lt_settings.link_settings.link_spread = + * dal_display_path_is_ss_supported( + * path_mode->display_path) ? + * LINK_SPREAD_05_DOWNSPREAD_30KHZ : + * LINK_SPREAD_DISABLED; + */ + lt_settings.link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ; + + /* 1. set link rate, lane count and spread*/ + dpcd_set_link_settings(link, <_settings); + + /* 2. perform link training (set link training done + * to false is done as well)*/ + if (!perform_clock_recovery_sequence(link, <_settings)) { + status = LINK_TRAINING_CR_FAIL; + } else { + status = perform_channel_equalization_sequence(link, + <_settings); + } + + if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { + if (!perform_link_training_int(link, + <_settings, + status == LINK_TRAINING_SUCCESS)) { + /* the next link training setting in this case + * would be the same as CR failure case. + */ + status = LINK_TRAINING_CR_FAIL; + } + } + + /* 6. print status message*/ + switch (lt_settings.link_settings.link_rate) { + + case LINK_RATE_LOW: + link_rate = "RBR"; + break; + case LINK_RATE_HIGH: + link_rate = "HBR"; + break; + case LINK_RATE_HIGH2: + link_rate = "HBR2"; + break; + case LINK_RATE_RBR2: + link_rate = "RBR2"; + break; + case LINK_RATE_HIGH3: + link_rate = "HBR3"; + break; + default: + break; + } + + /* Connectivity log: link training */ + CONN_MSG_LT(link, "%sx%d %s VS=%d, PE=%d", + link_rate, + lt_settings.link_settings.lane_count, + (status == LINK_TRAINING_SUCCESS) ? "pass" : + ((status == LINK_TRAINING_CR_FAIL) ? "CR failed" : + "EQ failed"), + lt_settings.lane_settings[0].VOLTAGE_SWING, + lt_settings.lane_settings[0].PRE_EMPHASIS); + + return status; +} + + +bool perform_link_training_with_retries( + struct dc_link *link, + const struct dc_link_settings *link_setting, + bool skip_video_pattern, + int attempts) +{ + uint8_t j; + uint8_t delay_between_attempts = LINK_TRAINING_RETRY_DELAY; + + for (j = 0; j < attempts; ++j) { + + if (dc_link_dp_perform_link_training( + link, + link_setting, + skip_video_pattern) == LINK_TRAINING_SUCCESS) + return true; + + msleep(delay_between_attempts); + delay_between_attempts += LINK_TRAINING_RETRY_DELAY; + } + + return false; +} + +static struct dc_link_settings get_max_link_cap(struct dc_link *link) +{ + /* Set Default link settings */ + struct dc_link_settings max_link_cap = {LANE_COUNT_FOUR, LINK_RATE_HIGH, + LINK_SPREAD_05_DOWNSPREAD_30KHZ}; + + /* Higher link settings based on feature supported */ + if (link->link_enc->features.flags.bits.IS_HBR2_CAPABLE) + max_link_cap.link_rate = LINK_RATE_HIGH2; + + if (link->link_enc->features.flags.bits.IS_HBR3_CAPABLE) + max_link_cap.link_rate = LINK_RATE_HIGH3; + + /* Lower link settings based on sink's link cap */ + if (link->reported_link_cap.lane_count < max_link_cap.lane_count) + max_link_cap.lane_count = + link->reported_link_cap.lane_count; + if (link->reported_link_cap.link_rate < max_link_cap.link_rate) + max_link_cap.link_rate = + link->reported_link_cap.link_rate; + if (link->reported_link_cap.link_spread < + max_link_cap.link_spread) + max_link_cap.link_spread = + link->reported_link_cap.link_spread; + return max_link_cap; +} + +bool dp_hbr_verify_link_cap( + struct dc_link *link, + struct dc_link_settings *known_limit_link_setting) +{ + struct dc_link_settings max_link_cap = {0}; + struct dc_link_settings cur_link_setting = {0}; + struct dc_link_settings *cur = &cur_link_setting; + struct dc_link_settings initial_link_settings = {0}; + bool success; + bool skip_link_training; + bool skip_video_pattern; + struct clock_source *dp_cs; + enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL; + enum link_training_result status; + + success = false; + skip_link_training = false; + + max_link_cap = get_max_link_cap(link); + + /* TODO implement override and monitor patch later */ + + /* try to train the link from high to low to + * find the physical link capability + */ + /* disable PHY done possible by BIOS, will be done by driver itself */ + dp_disable_link_phy(link, link->connector_signal); + + dp_cs = link->dc->res_pool->dp_clock_source; + + if (dp_cs) + dp_cs_id = dp_cs->id; + else { + /* + * dp clock source is not initialized for some reason. + * Should not happen, CLOCK_SOURCE_ID_EXTERNAL will be used + */ + ASSERT(dp_cs); + } + + /* link training starts with the maximum common settings + * supported by both sink and ASIC. + */ + initial_link_settings = get_common_supported_link_settings( + *known_limit_link_setting, + max_link_cap); + cur_link_setting = initial_link_settings; + do { + skip_video_pattern = true; + + if (cur->link_rate == LINK_RATE_LOW) + skip_video_pattern = false; + + dp_enable_link_phy( + link, + link->connector_signal, + dp_cs_id, + cur); + + if (skip_link_training) + success = true; + else { + status = dc_link_dp_perform_link_training( + link, + cur, + skip_video_pattern); + if (status == LINK_TRAINING_SUCCESS) + success = true; + } + + if (success) + link->verified_link_cap = *cur; + + /* always disable the link before trying another + * setting or before returning we'll enable it later + * based on the actual mode we're driving + */ + dp_disable_link_phy(link, link->connector_signal); + } while (!success && decide_fallback_link_setting( + initial_link_settings, cur, status)); + + /* Link Training failed for all Link Settings + * (Lane Count is still unknown) + */ + if (!success) { + /* If all LT fails for all settings, + * set verified = failed safe (1 lane low) + */ + link->verified_link_cap.lane_count = LANE_COUNT_ONE; + link->verified_link_cap.link_rate = LINK_RATE_LOW; + + link->verified_link_cap.link_spread = + LINK_SPREAD_DISABLED; + } + + + return success; +} + +static struct dc_link_settings get_common_supported_link_settings ( + struct dc_link_settings link_setting_a, + struct dc_link_settings link_setting_b) +{ + struct dc_link_settings link_settings = {0}; + + link_settings.lane_count = + (link_setting_a.lane_count <= + link_setting_b.lane_count) ? + link_setting_a.lane_count : + link_setting_b.lane_count; + link_settings.link_rate = + (link_setting_a.link_rate <= + link_setting_b.link_rate) ? + link_setting_a.link_rate : + link_setting_b.link_rate; + link_settings.link_spread = LINK_SPREAD_DISABLED; + + /* in DP compliance test, DPR-120 may have + * a random value in its MAX_LINK_BW dpcd field. + * We map it to the maximum supported link rate that + * is smaller than MAX_LINK_BW in this case. + */ + if (link_settings.link_rate > LINK_RATE_HIGH3) { + link_settings.link_rate = LINK_RATE_HIGH3; + } else if (link_settings.link_rate < LINK_RATE_HIGH3 + && link_settings.link_rate > LINK_RATE_HIGH2) { + link_settings.link_rate = LINK_RATE_HIGH2; + } else if (link_settings.link_rate < LINK_RATE_HIGH2 + && link_settings.link_rate > LINK_RATE_HIGH) { + link_settings.link_rate = LINK_RATE_HIGH; + } else if (link_settings.link_rate < LINK_RATE_HIGH + && link_settings.link_rate > LINK_RATE_LOW) { + link_settings.link_rate = LINK_RATE_LOW; + } else if (link_settings.link_rate < LINK_RATE_LOW) { + link_settings.link_rate = LINK_RATE_UNKNOWN; + } + + return link_settings; +} + +static inline bool reached_minimum_lane_count(enum dc_lane_count lane_count) +{ + return lane_count <= LANE_COUNT_ONE; +} + +static inline bool reached_minimum_link_rate(enum dc_link_rate link_rate) +{ + return link_rate <= LINK_RATE_LOW; +} + +static enum dc_lane_count reduce_lane_count(enum dc_lane_count lane_count) +{ + switch (lane_count) { + case LANE_COUNT_FOUR: + return LANE_COUNT_TWO; + case LANE_COUNT_TWO: + return LANE_COUNT_ONE; + case LANE_COUNT_ONE: + return LANE_COUNT_UNKNOWN; + default: + return LANE_COUNT_UNKNOWN; + } +} + +static enum dc_link_rate reduce_link_rate(enum dc_link_rate link_rate) +{ + switch (link_rate) { + case LINK_RATE_HIGH3: + return LINK_RATE_HIGH2; + case LINK_RATE_HIGH2: + return LINK_RATE_HIGH; + case LINK_RATE_HIGH: + return LINK_RATE_LOW; + case LINK_RATE_LOW: + return LINK_RATE_UNKNOWN; + default: + return LINK_RATE_UNKNOWN; + } +} + +static enum dc_lane_count increase_lane_count(enum dc_lane_count lane_count) +{ + switch (lane_count) { + case LANE_COUNT_ONE: + return LANE_COUNT_TWO; + case LANE_COUNT_TWO: + return LANE_COUNT_FOUR; + default: + return LANE_COUNT_UNKNOWN; + } +} + +static enum dc_link_rate increase_link_rate(enum dc_link_rate link_rate) +{ + switch (link_rate) { + case LINK_RATE_LOW: + return LINK_RATE_HIGH; + case LINK_RATE_HIGH: + return LINK_RATE_HIGH2; + case LINK_RATE_HIGH2: + return LINK_RATE_HIGH3; + default: + return LINK_RATE_UNKNOWN; + } +} + +/* + * function: set link rate and lane count fallback based + * on current link setting and last link training result + * return value: + * true - link setting could be set + * false - has reached minimum setting + * and no further fallback could be done + */ +static bool decide_fallback_link_setting( + struct dc_link_settings initial_link_settings, + struct dc_link_settings *current_link_setting, + enum link_training_result training_result) +{ + if (!current_link_setting) + return false; + + switch (training_result) { + case LINK_TRAINING_CR_FAIL: + { + if (!reached_minimum_link_rate + (current_link_setting->link_rate)) { + current_link_setting->link_rate = + reduce_link_rate( + current_link_setting->link_rate); + } else if (!reached_minimum_lane_count + (current_link_setting->lane_count)) { + current_link_setting->link_rate = + initial_link_settings.link_rate; + current_link_setting->lane_count = + reduce_lane_count( + current_link_setting->lane_count); + } else { + return false; + } + break; + } + case LINK_TRAINING_EQ_FAIL_EQ: + { + if (!reached_minimum_lane_count + (current_link_setting->lane_count)) { + current_link_setting->lane_count = + reduce_lane_count( + current_link_setting->lane_count); + } else if (!reached_minimum_link_rate + (current_link_setting->link_rate)) { + current_link_setting->link_rate = + reduce_link_rate( + current_link_setting->link_rate); + } else { + return false; + } + break; + } + case LINK_TRAINING_EQ_FAIL_CR: + { + if (!reached_minimum_link_rate + (current_link_setting->link_rate)) { + current_link_setting->link_rate = + reduce_link_rate( + current_link_setting->link_rate); + } else { + return false; + } + break; + } + default: + return false; + } + return true; +} + +static uint32_t bandwidth_in_kbps_from_timing( + const struct dc_crtc_timing *timing) +{ + uint32_t bits_per_channel = 0; + uint32_t kbps; + switch (timing->display_color_depth) { + + case COLOR_DEPTH_666: + bits_per_channel = 6; + break; + case COLOR_DEPTH_888: + bits_per_channel = 8; + break; + case COLOR_DEPTH_101010: + bits_per_channel = 10; + break; + case COLOR_DEPTH_121212: + bits_per_channel = 12; + break; + case COLOR_DEPTH_141414: + bits_per_channel = 14; + break; + case COLOR_DEPTH_161616: + bits_per_channel = 16; + break; + default: + break; + } + ASSERT(bits_per_channel != 0); + + kbps = timing->pix_clk_khz; + kbps *= bits_per_channel; + + if (timing->flags.Y_ONLY != 1) + /*Only YOnly make reduce bandwidth by 1/3 compares to RGB*/ + kbps *= 3; + + return kbps; + +} + +static uint32_t bandwidth_in_kbps_from_link_settings( + const struct dc_link_settings *link_setting) +{ + uint32_t link_rate_in_kbps = link_setting->link_rate * + LINK_RATE_REF_FREQ_IN_KHZ; + + uint32_t lane_count = link_setting->lane_count; + uint32_t kbps = link_rate_in_kbps; + kbps *= lane_count; + kbps *= 8; /* 8 bits per byte*/ + + return kbps; + +} + +bool dp_validate_mode_timing( + struct dc_link *link, + const struct dc_crtc_timing *timing) +{ + uint32_t req_bw; + uint32_t max_bw; + + const struct dc_link_settings *link_setting; + + /*always DP fail safe mode*/ + if (timing->pix_clk_khz == (uint32_t)25175 && + timing->h_addressable == (uint32_t)640 && + timing->v_addressable == (uint32_t)480) + return true; + + /* We always use verified link settings */ + link_setting = &link->verified_link_cap; + + /* TODO: DYNAMIC_VALIDATION needs to be implemented */ + /*if (flags.DYNAMIC_VALIDATION == 1 && + link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN) + link_setting = &link->verified_link_cap; + */ + + req_bw = bandwidth_in_kbps_from_timing(timing); + max_bw = bandwidth_in_kbps_from_link_settings(link_setting); + + if (req_bw <= max_bw) { + /* remember the biggest mode here, during + * initial link training (to get + * verified_link_cap), LS sends event about + * cannot train at reported cap to upper + * layer and upper layer will re-enumerate modes. + * this is not necessary if the lower + * verified_link_cap is enough to drive + * all the modes */ + + /* TODO: DYNAMIC_VALIDATION needs to be implemented */ + /* if (flags.DYNAMIC_VALIDATION == 1) + dpsst->max_req_bw_for_verified_linkcap = dal_max( + dpsst->max_req_bw_for_verified_linkcap, req_bw); */ + return true; + } else + return false; +} + +void decide_link_settings(struct dc_stream_state *stream, + struct dc_link_settings *link_setting) +{ + + struct dc_link_settings initial_link_setting = { + LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED}; + struct dc_link_settings current_link_setting = + initial_link_setting; + struct dc_link *link; + uint32_t req_bw; + uint32_t link_bw; + + req_bw = bandwidth_in_kbps_from_timing(&stream->timing); + + link = stream->sink->link; + + /* if preferred is specified through AMDDP, use it, if it's enough + * to drive the mode + */ + if (link->preferred_link_setting.lane_count != + LANE_COUNT_UNKNOWN && + link->preferred_link_setting.link_rate != + LINK_RATE_UNKNOWN) { + *link_setting = link->preferred_link_setting; + return; + } + + /* MST doesn't perform link training for now + * TODO: add MST specific link training routine + */ + if (is_mst_supported(link)) { + *link_setting = link->verified_link_cap; + return; + } + + /* search for the minimum link setting that: + * 1. is supported according to the link training result + * 2. could support the b/w requested by the timing + */ + while (current_link_setting.link_rate <= + link->verified_link_cap.link_rate) { + link_bw = bandwidth_in_kbps_from_link_settings( + ¤t_link_setting); + if (req_bw <= link_bw) { + *link_setting = current_link_setting; + return; + } + + if (current_link_setting.lane_count < + link->verified_link_cap.lane_count) { + current_link_setting.lane_count = + increase_lane_count( + current_link_setting.lane_count); + } else { + current_link_setting.link_rate = + increase_link_rate( + current_link_setting.link_rate); + current_link_setting.lane_count = + initial_link_setting.lane_count; + } + } + + BREAK_TO_DEBUGGER(); + ASSERT(link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN); + + *link_setting = link->verified_link_cap; +} + +/*************************Short Pulse IRQ***************************/ + +static bool hpd_rx_irq_check_link_loss_status( + struct dc_link *link, + union hpd_irq_data *hpd_irq_dpcd_data) +{ + uint8_t irq_reg_rx_power_state; + enum dc_status dpcd_result = DC_ERROR_UNEXPECTED; + union lane_status lane_status; + uint32_t lane; + bool sink_status_changed; + bool return_code; + + sink_status_changed = false; + return_code = false; + + if (link->cur_link_settings.lane_count == 0) + return return_code; + /*1. Check that we can handle interrupt: Not in FS DOS, + * Not in "Display Timeout" state, Link is trained. + */ + + dpcd_result = core_link_read_dpcd(link, + DP_SET_POWER, + &irq_reg_rx_power_state, + sizeof(irq_reg_rx_power_state)); + + if (dpcd_result != DC_OK) { + irq_reg_rx_power_state = DP_SET_POWER_D0; + dm_logger_write(link->ctx->logger, LOG_HW_HPD_IRQ, + "%s: DPCD read failed to obtain power state.\n", + __func__); + } + + if (irq_reg_rx_power_state == DP_SET_POWER_D0) { + + /*2. Check that Link Status changed, before re-training.*/ + + /*parse lane status*/ + for (lane = 0; + lane < link->cur_link_settings.lane_count; + lane++) { + + /* check status of lanes 0,1 + * changed DpcdAddress_Lane01Status (0x202)*/ + lane_status.raw = get_nibble_at_index( + &hpd_irq_dpcd_data->bytes.lane01_status.raw, + lane); + + if (!lane_status.bits.CHANNEL_EQ_DONE_0 || + !lane_status.bits.CR_DONE_0 || + !lane_status.bits.SYMBOL_LOCKED_0) { + /* if one of the channel equalization, clock + * recovery or symbol lock is dropped + * consider it as (link has been + * dropped) dp sink status has changed*/ + sink_status_changed = true; + break; + } + + } + + /* Check interlane align.*/ + if (sink_status_changed || + !hpd_irq_dpcd_data->bytes.lane_status_updated.bits. + INTERLANE_ALIGN_DONE) { + + dm_logger_write(link->ctx->logger, LOG_HW_HPD_IRQ, + "%s: Link Status changed.\n", + __func__); + + return_code = true; + } + } + + return return_code; +} + +static enum dc_status read_hpd_rx_irq_data( + struct dc_link *link, + union hpd_irq_data *irq_data) +{ + /* The HW reads 16 bytes from 200h on HPD, + * but if we get an AUX_DEFER, the HW cannot retry + * and this causes the CTS tests 4.3.2.1 - 3.2.4 to + * fail, so we now explicitly read 6 bytes which is + * the req from the above mentioned test cases. + */ + return core_link_read_dpcd( + link, + DP_SINK_COUNT, + irq_data->raw, + sizeof(union hpd_irq_data)); +} + +static bool allow_hpd_rx_irq(const struct dc_link *link) +{ + /* + * Don't handle RX IRQ unless one of following is met: + * 1) The link is established (cur_link_settings != unknown) + * 2) We kicked off MST detection + * 3) We know we're dealing with an active dongle + */ + + if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) || + (link->type == dc_connection_mst_branch) || + is_dp_active_dongle(link)) + return true; + + return false; +} + +static bool handle_hpd_irq_psr_sink(const struct dc_link *link) +{ + union dpcd_psr_configuration psr_configuration; + + if (!link->psr_enabled) + return false; + + dm_helpers_dp_read_dpcd( + link->ctx, + link, + 368,/*DpcdAddress_PSR_Enable_Cfg*/ + &psr_configuration.raw, + sizeof(psr_configuration.raw)); + + + if (psr_configuration.bits.ENABLE) { + unsigned char dpcdbuf[3] = {0}; + union psr_error_status psr_error_status; + union psr_sink_psr_status psr_sink_psr_status; + + dm_helpers_dp_read_dpcd( + link->ctx, + link, + 0x2006, /*DpcdAddress_PSR_Error_Status*/ + (unsigned char *) dpcdbuf, + sizeof(dpcdbuf)); + + /*DPCD 2006h ERROR STATUS*/ + psr_error_status.raw = dpcdbuf[0]; + /*DPCD 2008h SINK PANEL SELF REFRESH STATUS*/ + psr_sink_psr_status.raw = dpcdbuf[2]; + + if (psr_error_status.bits.LINK_CRC_ERROR || + psr_error_status.bits.RFB_STORAGE_ERROR) { + /* Acknowledge and clear error bits */ + dm_helpers_dp_write_dpcd( + link->ctx, + link, + 8198,/*DpcdAddress_PSR_Error_Status*/ + &psr_error_status.raw, + sizeof(psr_error_status.raw)); + + /* PSR error, disable and re-enable PSR */ + dc_link_set_psr_enable(link, false, true); + dc_link_set_psr_enable(link, true, true); + + return true; + } else if (psr_sink_psr_status.bits.SINK_SELF_REFRESH_STATUS == + PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB){ + /* No error is detect, PSR is active. + * We should return with IRQ_HPD handled without + * checking for loss of sync since PSR would have + * powered down main link. + */ + return true; + } + } + return false; +} + +static void dp_test_send_link_training(struct dc_link *link) +{ + struct dc_link_settings link_settings = {0}; + + core_link_read_dpcd( + link, + DP_TEST_LANE_COUNT, + (unsigned char *)(&link_settings.lane_count), + 1); + core_link_read_dpcd( + link, + DP_TEST_LINK_RATE, + (unsigned char *)(&link_settings.link_rate), + 1); + + /* Set preferred link settings */ + link->verified_link_cap.lane_count = link_settings.lane_count; + link->verified_link_cap.link_rate = link_settings.link_rate; + + dp_retrain_link_dp_test(link, &link_settings, false); +} + +/* TODO hbr2 compliance eye output is unstable + * (toggling on and off) with debugger break + * This caueses intermittent PHY automation failure + * Need to look into the root cause */ +static uint8_t force_tps4_for_cp2520 = 1; + +static void dp_test_send_phy_test_pattern(struct dc_link *link) +{ + union phy_test_pattern dpcd_test_pattern; + union lane_adjust dpcd_lane_adjustment[2]; + unsigned char dpcd_post_cursor_2_adjustment = 0; + unsigned char test_80_bit_pattern[ + (DP_TEST_80BIT_CUSTOM_PATTERN_79_72 - + DP_TEST_80BIT_CUSTOM_PATTERN_7_0)+1] = {0}; + enum dp_test_pattern test_pattern; + struct dc_link_training_settings link_settings; + union lane_adjust dpcd_lane_adjust; + unsigned int lane; + struct link_training_settings link_training_settings; + int i = 0; + + dpcd_test_pattern.raw = 0; + memset(dpcd_lane_adjustment, 0, sizeof(dpcd_lane_adjustment)); + memset(&link_settings, 0, sizeof(link_settings)); + + /* get phy test pattern and pattern parameters from DP receiver */ + core_link_read_dpcd( + link, + DP_TEST_PHY_PATTERN, + &dpcd_test_pattern.raw, + sizeof(dpcd_test_pattern)); + core_link_read_dpcd( + link, + DP_ADJUST_REQUEST_LANE0_1, + &dpcd_lane_adjustment[0].raw, + sizeof(dpcd_lane_adjustment)); + + /*get post cursor 2 parameters + * For DP 1.1a or eariler, this DPCD register's value is 0 + * For DP 1.2 or later: + * Bits 1:0 = POST_CURSOR2_LANE0; Bits 3:2 = POST_CURSOR2_LANE1 + * Bits 5:4 = POST_CURSOR2_LANE2; Bits 7:6 = POST_CURSOR2_LANE3 + */ + core_link_read_dpcd( + link, + DP_ADJUST_REQUEST_POST_CURSOR2, + &dpcd_post_cursor_2_adjustment, + sizeof(dpcd_post_cursor_2_adjustment)); + + /* translate request */ + switch (dpcd_test_pattern.bits.PATTERN) { + case PHY_TEST_PATTERN_D10_2: + test_pattern = DP_TEST_PATTERN_D102; + break; + case PHY_TEST_PATTERN_SYMBOL_ERROR: + test_pattern = DP_TEST_PATTERN_SYMBOL_ERROR; + break; + case PHY_TEST_PATTERN_PRBS7: + test_pattern = DP_TEST_PATTERN_PRBS7; + break; + case PHY_TEST_PATTERN_80BIT_CUSTOM: + test_pattern = DP_TEST_PATTERN_80BIT_CUSTOM; + break; + case PHY_TEST_PATTERN_CP2520_1: + /* CP2520 pattern is unstable, temporarily use TPS4 instead */ + test_pattern = (force_tps4_for_cp2520 == 1) ? + DP_TEST_PATTERN_TRAINING_PATTERN4 : + DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE; + break; + case PHY_TEST_PATTERN_CP2520_2: + /* CP2520 pattern is unstable, temporarily use TPS4 instead */ + test_pattern = (force_tps4_for_cp2520 == 1) ? + DP_TEST_PATTERN_TRAINING_PATTERN4 : + DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE; + break; + case PHY_TEST_PATTERN_CP2520_3: + test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN4; + break; + default: + test_pattern = DP_TEST_PATTERN_VIDEO_MODE; + break; + } + + if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM) + core_link_read_dpcd( + link, + DP_TEST_80BIT_CUSTOM_PATTERN_7_0, + test_80_bit_pattern, + sizeof(test_80_bit_pattern)); + + /* prepare link training settings */ + link_settings.link = link->cur_link_settings; + + for (lane = 0; lane < + (unsigned int)(link->cur_link_settings.lane_count); + lane++) { + dpcd_lane_adjust.raw = + get_nibble_at_index(&dpcd_lane_adjustment[0].raw, lane); + link_settings.lane_settings[lane].VOLTAGE_SWING = + (enum dc_voltage_swing) + (dpcd_lane_adjust.bits.VOLTAGE_SWING_LANE); + link_settings.lane_settings[lane].PRE_EMPHASIS = + (enum dc_pre_emphasis) + (dpcd_lane_adjust.bits.PRE_EMPHASIS_LANE); + link_settings.lane_settings[lane].POST_CURSOR2 = + (enum dc_post_cursor2) + ((dpcd_post_cursor_2_adjustment >> (lane * 2)) & 0x03); + } + + for (i = 0; i < 4; i++) + link_training_settings.lane_settings[i] = + link_settings.lane_settings[i]; + link_training_settings.link_settings = link_settings.link; + link_training_settings.allow_invalid_msa_timing_param = false; + /*Usage: Measure DP physical lane signal + * by DP SI test equipment automatically. + * PHY test pattern request is generated by equipment via HPD interrupt. + * HPD needs to be active all the time. HPD should be active + * all the time. Do not touch it. + * forward request to DS + */ + dc_link_dp_set_test_pattern( + link, + test_pattern, + &link_training_settings, + test_80_bit_pattern, + (DP_TEST_80BIT_CUSTOM_PATTERN_79_72 - + DP_TEST_80BIT_CUSTOM_PATTERN_7_0)+1); +} + +static void dp_test_send_link_test_pattern(struct dc_link *link) +{ + union link_test_pattern dpcd_test_pattern; + union test_misc dpcd_test_params; + enum dp_test_pattern test_pattern; + + memset(&dpcd_test_pattern, 0, sizeof(dpcd_test_pattern)); + memset(&dpcd_test_params, 0, sizeof(dpcd_test_params)); + + /* get link test pattern and pattern parameters */ + core_link_read_dpcd( + link, + DP_TEST_PATTERN, + &dpcd_test_pattern.raw, + sizeof(dpcd_test_pattern)); + core_link_read_dpcd( + link, + DP_TEST_MISC0, + &dpcd_test_params.raw, + sizeof(dpcd_test_params)); + + switch (dpcd_test_pattern.bits.PATTERN) { + case LINK_TEST_PATTERN_COLOR_RAMP: + test_pattern = DP_TEST_PATTERN_COLOR_RAMP; + break; + case LINK_TEST_PATTERN_VERTICAL_BARS: + test_pattern = DP_TEST_PATTERN_VERTICAL_BARS; + break; /* black and white */ + case LINK_TEST_PATTERN_COLOR_SQUARES: + test_pattern = (dpcd_test_params.bits.DYN_RANGE == + TEST_DYN_RANGE_VESA ? + DP_TEST_PATTERN_COLOR_SQUARES : + DP_TEST_PATTERN_COLOR_SQUARES_CEA); + break; + default: + test_pattern = DP_TEST_PATTERN_VIDEO_MODE; + break; + } + + dc_link_dp_set_test_pattern( + link, + test_pattern, + NULL, + NULL, + 0); +} + +static void handle_automated_test(struct dc_link *link) +{ + union test_request test_request; + union test_response test_response; + + memset(&test_request, 0, sizeof(test_request)); + memset(&test_response, 0, sizeof(test_response)); + + core_link_read_dpcd( + link, + DP_TEST_REQUEST, + &test_request.raw, + sizeof(union test_request)); + if (test_request.bits.LINK_TRAINING) { + /* ACK first to let DP RX test box monitor LT sequence */ + test_response.bits.ACK = 1; + core_link_write_dpcd( + link, + DP_TEST_RESPONSE, + &test_response.raw, + sizeof(test_response)); + dp_test_send_link_training(link); + /* no acknowledge request is needed again */ + test_response.bits.ACK = 0; + } + if (test_request.bits.LINK_TEST_PATTRN) { + dp_test_send_link_test_pattern(link); + test_response.bits.ACK = 1; + } + if (test_request.bits.PHY_TEST_PATTERN) { + dp_test_send_phy_test_pattern(link); + test_response.bits.ACK = 1; + } + if (!test_request.raw) + /* no requests, revert all test signals + * TODO: revert all test signals + */ + test_response.bits.ACK = 1; + /* send request acknowledgment */ + if (test_response.bits.ACK) + core_link_write_dpcd( + link, + DP_TEST_RESPONSE, + &test_response.raw, + sizeof(test_response)); +} + +bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd_irq_dpcd_data) +{ + union hpd_irq_data hpd_irq_dpcd_data = {{{{0}}}}; + union device_service_irq device_service_clear = { { 0 } }; + enum dc_status result = DDC_RESULT_UNKNOWN; + bool status = false; + /* For use cases related to down stream connection status change, + * PSR and device auto test, refer to function handle_sst_hpd_irq + * in DAL2.1*/ + + dm_logger_write(link->ctx->logger, LOG_HW_HPD_IRQ, + "%s: Got short pulse HPD on link %d\n", + __func__, link->link_index); + + + /* All the "handle_hpd_irq_xxx()" methods + * should be called only after + * dal_dpsst_ls_read_hpd_irq_data + * Order of calls is important too + */ + result = read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data); + if (out_hpd_irq_dpcd_data) + *out_hpd_irq_dpcd_data = hpd_irq_dpcd_data; + + if (result != DC_OK) { + dm_logger_write(link->ctx->logger, LOG_HW_HPD_IRQ, + "%s: DPCD read failed to obtain irq data\n", + __func__); + return false; + } + + if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) { + device_service_clear.bits.AUTOMATED_TEST = 1; + core_link_write_dpcd( + link, + DP_DEVICE_SERVICE_IRQ_VECTOR, + &device_service_clear.raw, + sizeof(device_service_clear.raw)); + device_service_clear.raw = 0; + handle_automated_test(link); + return false; + } + + if (!allow_hpd_rx_irq(link)) { + dm_logger_write(link->ctx->logger, LOG_HW_HPD_IRQ, + "%s: skipping HPD handling on %d\n", + __func__, link->link_index); + return false; + } + + if (handle_hpd_irq_psr_sink(link)) + /* PSR-related error was detected and handled */ + return true; + + /* If PSR-related error handled, Main link may be off, + * so do not handle as a normal sink status change interrupt. + */ + + if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) + return true; + + /* check if we have MST msg and return since we poll for it */ + if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) + return false; + + /* For now we only handle 'Downstream port status' case. + * If we got sink count changed it means + * Downstream port status changed, + * then DM should call DC to do the detection. */ + if (hpd_rx_irq_check_link_loss_status( + link, + &hpd_irq_dpcd_data)) { + /* Connectivity log: link loss */ + CONN_DATA_LINK_LOSS(link, + hpd_irq_dpcd_data.raw, + sizeof(hpd_irq_dpcd_data), + "Status: "); + + perform_link_training_with_retries(link, + &link->cur_link_settings, + true, LINK_TRAINING_ATTEMPTS); + + status = false; + } + + if (link->type == dc_connection_active_dongle && + hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT + != link->dpcd_sink_count) + status = true; + + /* reasons for HPD RX: + * 1. Link Loss - ie Re-train the Link + * 2. MST sideband message + * 3. Automated Test - ie. Internal Commit + * 4. CP (copy protection) - (not interesting for DM???) + * 5. DRR + * 6. Downstream Port status changed + * -ie. Detect - this the only one + * which is interesting for DM because + * it must call dc_link_detect. + */ + return status; +} + +/*query dpcd for version and mst cap addresses*/ +bool is_mst_supported(struct dc_link *link) +{ + bool mst = false; + enum dc_status st = DC_OK; + union dpcd_rev rev; + union mstm_cap cap; + + rev.raw = 0; + cap.raw = 0; + + st = core_link_read_dpcd(link, DP_DPCD_REV, &rev.raw, + sizeof(rev)); + + if (st == DC_OK && rev.raw >= DPCD_REV_12) { + + st = core_link_read_dpcd(link, DP_MSTM_CAP, + &cap.raw, sizeof(cap)); + if (st == DC_OK && cap.bits.MST_CAP == 1) + mst = true; + } + return mst; + +} + +bool is_dp_active_dongle(const struct dc_link *link) +{ + enum display_dongle_type dongle_type = link->dpcd_caps.dongle_type; + + return (dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER) || + (dongle_type == DISPLAY_DONGLE_DP_DVI_CONVERTER) || + (dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER); +} + +static void get_active_converter_info( + uint8_t data, struct dc_link *link) +{ + union dp_downstream_port_present ds_port = { .byte = data }; + + /* decode converter info*/ + if (!ds_port.fields.PORT_PRESENT) { + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE; + ddc_service_set_dongle_type(link->ddc, + link->dpcd_caps.dongle_type); + return; + } + + switch (ds_port.fields.PORT_TYPE) { + case DOWNSTREAM_VGA: + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_VGA_CONVERTER; + break; + case DOWNSTREAM_DVI_HDMI: + /* At this point we don't know is it DVI or HDMI, + * assume DVI.*/ + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_DVI_CONVERTER; + break; + default: + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE; + break; + } + + if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_11) { + uint8_t det_caps[4]; + union dwnstream_port_caps_byte0 *port_caps = + (union dwnstream_port_caps_byte0 *)det_caps; + core_link_read_dpcd(link, DP_DOWNSTREAM_PORT_0, + det_caps, sizeof(det_caps)); + + switch (port_caps->bits.DWN_STRM_PORTX_TYPE) { + case DOWN_STREAM_DETAILED_VGA: + link->dpcd_caps.dongle_type = + DISPLAY_DONGLE_DP_VGA_CONVERTER; + break; + case DOWN_STREAM_DETAILED_DVI: + link->dpcd_caps.dongle_type = + DISPLAY_DONGLE_DP_DVI_CONVERTER; + break; + case DOWN_STREAM_DETAILED_HDMI: + link->dpcd_caps.dongle_type = + DISPLAY_DONGLE_DP_HDMI_CONVERTER; + + link->dpcd_caps.dongle_caps.dongle_type = link->dpcd_caps.dongle_type; + if (ds_port.fields.DETAILED_CAPS) { + + union dwnstream_port_caps_byte3_hdmi + hdmi_caps = {.raw = det_caps[3] }; + union dwnstream_port_caps_byte1 + hdmi_color_caps = {.raw = det_caps[2] }; + link->dpcd_caps.dongle_caps.dp_hdmi_max_pixel_clk = + det_caps[1] * 25000; + + link->dpcd_caps.dongle_caps.is_dp_hdmi_s3d_converter = + hdmi_caps.bits.FRAME_SEQ_TO_FRAME_PACK; + link->dpcd_caps.dongle_caps.is_dp_hdmi_ycbcr422_pass_through = + hdmi_caps.bits.YCrCr422_PASS_THROUGH; + link->dpcd_caps.dongle_caps.is_dp_hdmi_ycbcr420_pass_through = + hdmi_caps.bits.YCrCr420_PASS_THROUGH; + link->dpcd_caps.dongle_caps.is_dp_hdmi_ycbcr422_converter = + hdmi_caps.bits.YCrCr422_CONVERSION; + link->dpcd_caps.dongle_caps.is_dp_hdmi_ycbcr420_converter = + hdmi_caps.bits.YCrCr420_CONVERSION; + + link->dpcd_caps.dongle_caps.dp_hdmi_max_bpc = + hdmi_color_caps.bits.MAX_BITS_PER_COLOR_COMPONENT; + + link->dpcd_caps.dongle_caps.extendedCapValid = true; + } + + break; + } + } + + ddc_service_set_dongle_type(link->ddc, link->dpcd_caps.dongle_type); + + { + struct dp_device_vendor_id dp_id; + + /* read IEEE branch device id */ + core_link_read_dpcd( + link, + DP_BRANCH_OUI, + (uint8_t *)&dp_id, + sizeof(dp_id)); + + link->dpcd_caps.branch_dev_id = + (dp_id.ieee_oui[0] << 16) + + (dp_id.ieee_oui[1] << 8) + + dp_id.ieee_oui[2]; + + memmove( + link->dpcd_caps.branch_dev_name, + dp_id.ieee_device_id, + sizeof(dp_id.ieee_device_id)); + } + + { + struct dp_sink_hw_fw_revision dp_hw_fw_revision; + + core_link_read_dpcd( + link, + DP_BRANCH_REVISION_START, + (uint8_t *)&dp_hw_fw_revision, + sizeof(dp_hw_fw_revision)); + + link->dpcd_caps.branch_hw_revision = + dp_hw_fw_revision.ieee_hw_rev; + } +} + +static void dp_wa_power_up_0010FA(struct dc_link *link, uint8_t *dpcd_data, + int length) +{ + int retry = 0; + union dp_downstream_port_present ds_port = { 0 }; + + if (!link->dpcd_caps.dpcd_rev.raw) { + do { + dp_receiver_power_ctrl(link, true); + core_link_read_dpcd(link, DP_DPCD_REV, + dpcd_data, length); + link->dpcd_caps.dpcd_rev.raw = dpcd_data[ + DP_DPCD_REV - + DP_DPCD_REV]; + } while (retry++ < 4 && !link->dpcd_caps.dpcd_rev.raw); + } + + ds_port.byte = dpcd_data[DP_DOWNSTREAMPORT_PRESENT - + DP_DPCD_REV]; + + if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER) { + switch (link->dpcd_caps.branch_dev_id) { + /* Some active dongles (DP-VGA, DP-DLDVI converters) power down + * all internal circuits including AUX communication preventing + * reading DPCD table and EDID (spec violation). + * Encoder will skip DP RX power down on disable_output to + * keep receiver powered all the time.*/ + case DP_BRANCH_DEVICE_ID_1: + case DP_BRANCH_DEVICE_ID_4: + link->wa_flags.dp_keep_receiver_powered = true; + break; + + /* TODO: May need work around for other dongles. */ + default: + link->wa_flags.dp_keep_receiver_powered = false; + break; + } + } else + link->wa_flags.dp_keep_receiver_powered = false; +} + +static void retrieve_link_cap(struct dc_link *link) +{ + uint8_t dpcd_data[DP_TRAINING_AUX_RD_INTERVAL - DP_DPCD_REV + 1]; + + union down_stream_port_count down_strm_port_count; + union edp_configuration_cap edp_config_cap; + union dp_downstream_port_present ds_port = { 0 }; + + memset(dpcd_data, '\0', sizeof(dpcd_data)); + memset(&down_strm_port_count, + '\0', sizeof(union down_stream_port_count)); + memset(&edp_config_cap, '\0', + sizeof(union edp_configuration_cap)); + + core_link_read_dpcd( + link, + DP_DPCD_REV, + dpcd_data, + sizeof(dpcd_data)); + + { + union training_aux_rd_interval aux_rd_interval; + + aux_rd_interval.raw = + dpcd_data[DP_TRAINING_AUX_RD_INTERVAL]; + + if (aux_rd_interval.bits.EXT_RECIEVER_CAP_FIELD_PRESENT == 1) { + core_link_read_dpcd( + link, + DP_DP13_DPCD_REV, + dpcd_data, + sizeof(dpcd_data)); + } + } + + link->dpcd_caps.dpcd_rev.raw = + dpcd_data[DP_DPCD_REV - DP_DPCD_REV]; + + ds_port.byte = dpcd_data[DP_DOWNSTREAMPORT_PRESENT - + DP_DPCD_REV]; + + get_active_converter_info(ds_port.byte, link); + + dp_wa_power_up_0010FA(link, dpcd_data, sizeof(dpcd_data)); + + link->dpcd_caps.allow_invalid_MSA_timing_param = + down_strm_port_count.bits.IGNORE_MSA_TIMING_PARAM; + + link->dpcd_caps.max_ln_count.raw = dpcd_data[ + DP_MAX_LANE_COUNT - DP_DPCD_REV]; + + link->dpcd_caps.max_down_spread.raw = dpcd_data[ + DP_MAX_DOWNSPREAD - DP_DPCD_REV]; + + link->reported_link_cap.lane_count = + link->dpcd_caps.max_ln_count.bits.MAX_LANE_COUNT; + link->reported_link_cap.link_rate = dpcd_data[ + DP_MAX_LINK_RATE - DP_DPCD_REV]; + link->reported_link_cap.link_spread = + link->dpcd_caps.max_down_spread.bits.MAX_DOWN_SPREAD ? + LINK_SPREAD_05_DOWNSPREAD_30KHZ : LINK_SPREAD_DISABLED; + + edp_config_cap.raw = dpcd_data[ + DP_EDP_CONFIGURATION_CAP - DP_DPCD_REV]; + link->dpcd_caps.panel_mode_edp = + edp_config_cap.bits.ALT_SCRAMBLER_RESET; + link->dpcd_caps.dpcd_display_control_capable = + edp_config_cap.bits.DPCD_DISPLAY_CONTROL_CAPABLE; + + link->test_pattern_enabled = false; + link->compliance_test_state.raw = 0; + + /* read sink count */ + core_link_read_dpcd(link, + DP_SINK_COUNT, + &link->dpcd_caps.sink_count.raw, + sizeof(link->dpcd_caps.sink_count.raw)); + + /* Connectivity log: detection */ + CONN_DATA_DETECT(link, dpcd_data, sizeof(dpcd_data), "Rx Caps: "); +} + +void detect_dp_sink_caps(struct dc_link *link) +{ + retrieve_link_cap(link); + + /* dc init_hw has power encoder using default + * signal for connector. For native DP, no + * need to power up encoder again. If not native + * DP, hw_init may need check signal or power up + * encoder here. + */ + /* TODO save sink caps in link->sink */ +} + +void detect_edp_sink_caps(struct dc_link *link) +{ + retrieve_link_cap(link); + link->verified_link_cap = link->reported_link_cap; +} + +void dc_link_dp_enable_hpd(const struct dc_link *link) +{ + struct link_encoder *encoder = link->link_enc; + + if (encoder != NULL && encoder->funcs->enable_hpd != NULL) + encoder->funcs->enable_hpd(encoder); +} + +void dc_link_dp_disable_hpd(const struct dc_link *link) +{ + struct link_encoder *encoder = link->link_enc; + + if (encoder != NULL && encoder->funcs->enable_hpd != NULL) + encoder->funcs->disable_hpd(encoder); +} + +static bool is_dp_phy_pattern(enum dp_test_pattern test_pattern) +{ + if ((DP_TEST_PATTERN_PHY_PATTERN_BEGIN <= test_pattern && + test_pattern <= DP_TEST_PATTERN_PHY_PATTERN_END) || + test_pattern == DP_TEST_PATTERN_VIDEO_MODE) + return true; + else + return false; +} + +static void set_crtc_test_pattern(struct dc_link *link, + struct pipe_ctx *pipe_ctx, + enum dp_test_pattern test_pattern) +{ + enum controller_dp_test_pattern controller_test_pattern; + enum dc_color_depth color_depth = pipe_ctx-> + stream->timing.display_color_depth; + struct bit_depth_reduction_params params; + + memset(¶ms, 0, sizeof(params)); + + switch (test_pattern) { + case DP_TEST_PATTERN_COLOR_SQUARES: + controller_test_pattern = + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES; + break; + case DP_TEST_PATTERN_COLOR_SQUARES_CEA: + controller_test_pattern = + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA; + break; + case DP_TEST_PATTERN_VERTICAL_BARS: + controller_test_pattern = + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS; + break; + case DP_TEST_PATTERN_HORIZONTAL_BARS: + controller_test_pattern = + CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS; + break; + case DP_TEST_PATTERN_COLOR_RAMP: + controller_test_pattern = + CONTROLLER_DP_TEST_PATTERN_COLORRAMP; + break; + default: + controller_test_pattern = + CONTROLLER_DP_TEST_PATTERN_VIDEOMODE; + break; + } + + switch (test_pattern) { + case DP_TEST_PATTERN_COLOR_SQUARES: + case DP_TEST_PATTERN_COLOR_SQUARES_CEA: + case DP_TEST_PATTERN_VERTICAL_BARS: + case DP_TEST_PATTERN_HORIZONTAL_BARS: + case DP_TEST_PATTERN_COLOR_RAMP: + { + /* disable bit depth reduction */ + pipe_ctx->stream->bit_depth_params = params; + pipe_ctx->stream_res.opp->funcs-> + opp_program_bit_depth_reduction(pipe_ctx->stream_res.opp, ¶ms); + + pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, + controller_test_pattern, color_depth); + } + break; + case DP_TEST_PATTERN_VIDEO_MODE: + { + /* restore bitdepth reduction */ + resource_build_bit_depth_reduction_params(pipe_ctx->stream, + ¶ms); + pipe_ctx->stream->bit_depth_params = params; + pipe_ctx->stream_res.opp->funcs-> + opp_program_bit_depth_reduction(pipe_ctx->stream_res.opp, ¶ms); + + pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, + CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, + color_depth); + } + break; + + default: + break; + } +} + +bool dc_link_dp_set_test_pattern( + struct dc_link *link, + enum dp_test_pattern test_pattern, + const struct link_training_settings *p_link_settings, + const unsigned char *p_custom_pattern, + unsigned int cust_pattern_size) +{ + struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx; + struct pipe_ctx *pipe_ctx = &pipes[0]; + unsigned int lane; + unsigned int i; + unsigned char link_qual_pattern[LANE_COUNT_DP_MAX] = {0}; + union dpcd_training_pattern training_pattern; + enum dpcd_phy_test_patterns pattern; + + memset(&training_pattern, 0, sizeof(training_pattern)); + + for (i = 0; i < MAX_PIPES; i++) { + if (pipes[i].stream->sink->link == link) { + pipe_ctx = &pipes[i]; + break; + } + } + + /* Reset CRTC Test Pattern if it is currently running and request + * is VideoMode Reset DP Phy Test Pattern if it is currently running + * and request is VideoMode + */ + if (link->test_pattern_enabled && test_pattern == + DP_TEST_PATTERN_VIDEO_MODE) { + /* Set CRTC Test Pattern */ + set_crtc_test_pattern(link, pipe_ctx, test_pattern); + dp_set_hw_test_pattern(link, test_pattern, + (uint8_t *)p_custom_pattern, + (uint32_t)cust_pattern_size); + + /* Unblank Stream */ + link->dc->hwss.unblank_stream( + pipe_ctx, + &link->verified_link_cap); + /* TODO:m_pHwss->MuteAudioEndpoint + * (pPathMode->pDisplayPath, false); + */ + + /* Reset Test Pattern state */ + link->test_pattern_enabled = false; + + return true; + } + + /* Check for PHY Test Patterns */ + if (is_dp_phy_pattern(test_pattern)) { + /* Set DPCD Lane Settings before running test pattern */ + if (p_link_settings != NULL) { + dp_set_hw_lane_settings(link, p_link_settings); + dpcd_set_lane_settings(link, p_link_settings); + } + + /* Blank stream if running test pattern */ + if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) { + /*TODO: + * m_pHwss-> + * MuteAudioEndpoint(pPathMode->pDisplayPath, true); + */ + /* Blank stream */ + pipes->stream_res.stream_enc->funcs->dp_blank(pipe_ctx->stream_res.stream_enc); + } + + dp_set_hw_test_pattern(link, test_pattern, + (uint8_t *)p_custom_pattern, + (uint32_t)cust_pattern_size); + + if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) { + /* Set Test Pattern state */ + link->test_pattern_enabled = true; + if (p_link_settings != NULL) + dpcd_set_link_settings(link, + p_link_settings); + } + + switch (test_pattern) { + case DP_TEST_PATTERN_VIDEO_MODE: + pattern = PHY_TEST_PATTERN_NONE; + break; + case DP_TEST_PATTERN_D102: + pattern = PHY_TEST_PATTERN_D10_2; + break; + case DP_TEST_PATTERN_SYMBOL_ERROR: + pattern = PHY_TEST_PATTERN_SYMBOL_ERROR; + break; + case DP_TEST_PATTERN_PRBS7: + pattern = PHY_TEST_PATTERN_PRBS7; + break; + case DP_TEST_PATTERN_80BIT_CUSTOM: + pattern = PHY_TEST_PATTERN_80BIT_CUSTOM; + break; + case DP_TEST_PATTERN_CP2520_1: + pattern = PHY_TEST_PATTERN_CP2520_1; + break; + case DP_TEST_PATTERN_CP2520_2: + pattern = PHY_TEST_PATTERN_CP2520_2; + break; + case DP_TEST_PATTERN_CP2520_3: + pattern = PHY_TEST_PATTERN_CP2520_3; + break; + default: + return false; + } + + if (test_pattern == DP_TEST_PATTERN_VIDEO_MODE + /*TODO:&& !pPathMode->pDisplayPath->IsTargetPoweredOn()*/) + return false; + + if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) { + /* tell receiver that we are sending qualification + * pattern DP 1.2 or later - DP receiver's link quality + * pattern is set using DPCD LINK_QUAL_LANEx_SET + * register (0x10B~0x10E)\ + */ + for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) + link_qual_pattern[lane] = + (unsigned char)(pattern); + + core_link_write_dpcd(link, + DP_LINK_QUAL_LANE0_SET, + link_qual_pattern, + sizeof(link_qual_pattern)); + } else if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_10 || + link->dpcd_caps.dpcd_rev.raw == 0) { + /* tell receiver that we are sending qualification + * pattern DP 1.1a or earlier - DP receiver's link + * quality pattern is set using + * DPCD TRAINING_PATTERN_SET -> LINK_QUAL_PATTERN_SET + * register (0x102). We will use v_1.3 when we are + * setting test pattern for DP 1.1. + */ + core_link_read_dpcd(link, DP_TRAINING_PATTERN_SET, + &training_pattern.raw, + sizeof(training_pattern)); + training_pattern.v1_3.LINK_QUAL_PATTERN_SET = pattern; + core_link_write_dpcd(link, DP_TRAINING_PATTERN_SET, + &training_pattern.raw, + sizeof(training_pattern)); + } + } else { + /* CRTC Patterns */ + set_crtc_test_pattern(link, pipe_ctx, test_pattern); + /* Set Test Pattern state */ + link->test_pattern_enabled = true; + } + + return true; +} + +void dp_enable_mst_on_sink(struct dc_link *link, bool enable) +{ + unsigned char mstmCntl; + + core_link_read_dpcd(link, DP_MSTM_CTRL, &mstmCntl, 1); + if (enable) + mstmCntl |= DP_MST_EN; + else + mstmCntl &= (~DP_MST_EN); + + core_link_write_dpcd(link, DP_MSTM_CTRL, &mstmCntl, 1); +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c new file mode 100644 index 000000000000..9a33b471270a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c @@ -0,0 +1,331 @@ +/* Copyright 2015 Advanced Micro Devices, Inc. */ + + +#include "dm_services.h" +#include "dc.h" +#include "inc/core_types.h" +#include "include/ddc_service_types.h" +#include "include/i2caux_interface.h" +#include "link_hwss.h" +#include "hw_sequencer.h" +#include "dc_link_dp.h" +#include "dc_link_ddc.h" +#include "dm_helpers.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dpcd_defs.h" + +enum dc_status core_link_read_dpcd( + struct dc_link *link, + uint32_t address, + uint8_t *data, + uint32_t size) +{ + if (!dm_helpers_dp_read_dpcd(link->ctx, + link, + address, data, size)) + return DC_ERROR_UNEXPECTED; + + return DC_OK; +} + +enum dc_status core_link_write_dpcd( + struct dc_link *link, + uint32_t address, + const uint8_t *data, + uint32_t size) +{ + if (!dm_helpers_dp_write_dpcd(link->ctx, + link, + address, data, size)) + return DC_ERROR_UNEXPECTED; + + return DC_OK; +} + +void dp_receiver_power_ctrl(struct dc_link *link, bool on) +{ + uint8_t state; + + state = on ? DP_POWER_STATE_D0 : DP_POWER_STATE_D3; + + core_link_write_dpcd(link, DP_SET_POWER, &state, + sizeof(state)); +} + +void dp_enable_link_phy( + struct dc_link *link, + enum signal_type signal, + enum clock_source_id clock_source, + const struct dc_link_settings *link_settings) +{ + struct link_encoder *link_enc = link->link_enc; + + struct pipe_ctx *pipes = + link->dc->current_state->res_ctx.pipe_ctx; + struct clock_source *dp_cs = + link->dc->res_pool->dp_clock_source; + unsigned int i; + /* If the current pixel clock source is not DTO(happens after + * switching from HDMI passive dongle to DP on the same connector), + * switch the pixel clock source to DTO. + */ + for (i = 0; i < MAX_PIPES; i++) { + if (pipes[i].stream != NULL && + pipes[i].stream->sink != NULL && + pipes[i].stream->sink->link == link) { + if (pipes[i].clock_source != NULL && + pipes[i].clock_source->id != CLOCK_SOURCE_ID_DP_DTO) { + pipes[i].clock_source = dp_cs; + pipes[i].stream_res.pix_clk_params.requested_pix_clk = + pipes[i].stream->timing.pix_clk_khz; + pipes[i].clock_source->funcs->program_pix_clk( + pipes[i].clock_source, + &pipes[i].stream_res.pix_clk_params, + &pipes[i].pll_settings); + } + } + } + + if (dc_is_dp_sst_signal(signal)) { + if (signal == SIGNAL_TYPE_EDP) { + link->dc->hwss.edp_power_control(link->link_enc, true); + link_enc->funcs->enable_dp_output( + link_enc, + link_settings, + clock_source); + link->dc->hwss.edp_backlight_control(link, true); + } else + link_enc->funcs->enable_dp_output( + link_enc, + link_settings, + clock_source); + } else { + link_enc->funcs->enable_dp_mst_output( + link_enc, + link_settings, + clock_source); + } + + dp_receiver_power_ctrl(link, true); +} + +static bool edp_receiver_ready_T9(struct dc_link *link) +{ + unsigned int tries = 0; + unsigned char sinkstatus = 0; + unsigned char edpRev = 0; + enum dc_status result = DC_OK; + result = core_link_read_dpcd(link, DP_EDP_DPCD_REV, &edpRev, sizeof(edpRev)); + if (edpRev < DP_EDP_12) + return true; + /* start from eDP version 1.2, SINK_STAUS indicate the sink is ready.*/ + do { + sinkstatus = 1; + result = core_link_read_dpcd(link, DP_SINK_STATUS, &sinkstatus, sizeof(sinkstatus)); + if (sinkstatus == 0) + break; + if (result != DC_OK) + break; + udelay(100); //MAx T9 + } while (++tries < 50); + return result; +} + +void dp_disable_link_phy(struct dc_link *link, enum signal_type signal) +{ + if (!link->wa_flags.dp_keep_receiver_powered) + dp_receiver_power_ctrl(link, false); + + if (signal == SIGNAL_TYPE_EDP) { + link->dc->hwss.edp_backlight_control(link, false); + edp_receiver_ready_T9(link); + link->link_enc->funcs->disable_output(link->link_enc, signal, link); + link->dc->hwss.edp_power_control(link->link_enc, false); + } else + link->link_enc->funcs->disable_output(link->link_enc, signal, link); + + /* Clear current link setting.*/ + memset(&link->cur_link_settings, 0, + sizeof(link->cur_link_settings)); +} + +void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal) +{ + /* MST disable link only when no stream use the link */ + if (link->mst_stream_alloc_table.stream_count > 0) + return; + + dp_disable_link_phy(link, signal); + + /* set the sink to SST mode after disabling the link */ + dp_enable_mst_on_sink(link, false); +} + +bool dp_set_hw_training_pattern( + struct dc_link *link, + enum hw_dp_training_pattern pattern) +{ + enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; + + switch (pattern) { + case HW_DP_TRAINING_PATTERN_1: + test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN1; + break; + case HW_DP_TRAINING_PATTERN_2: + test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN2; + break; + case HW_DP_TRAINING_PATTERN_3: + test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN3; + break; + case HW_DP_TRAINING_PATTERN_4: + test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN4; + break; + default: + break; + } + + dp_set_hw_test_pattern(link, test_pattern, NULL, 0); + + return true; +} + +void dp_set_hw_lane_settings( + struct dc_link *link, + const struct link_training_settings *link_settings) +{ + struct link_encoder *encoder = link->link_enc; + + /* call Encoder to set lane settings */ + encoder->funcs->dp_set_lane_settings(encoder, link_settings); +} + +enum dp_panel_mode dp_get_panel_mode(struct dc_link *link) +{ + /* We need to explicitly check that connector + * is not DP. Some Travis_VGA get reported + * by video bios as DP. + */ + if (link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT) { + + switch (link->dpcd_caps.branch_dev_id) { + case DP_BRANCH_DEVICE_ID_2: + if (strncmp( + link->dpcd_caps.branch_dev_name, + DP_VGA_LVDS_CONVERTER_ID_2, + sizeof( + link->dpcd_caps. + branch_dev_name)) == 0) { + return DP_PANEL_MODE_SPECIAL; + } + break; + case DP_BRANCH_DEVICE_ID_3: + if (strncmp(link->dpcd_caps.branch_dev_name, + DP_VGA_LVDS_CONVERTER_ID_3, + sizeof( + link->dpcd_caps. + branch_dev_name)) == 0) { + return DP_PANEL_MODE_SPECIAL; + } + break; + default: + break; + } + } + + if (link->dpcd_caps.panel_mode_edp) { + return DP_PANEL_MODE_EDP; + } + + return DP_PANEL_MODE_DEFAULT; +} + +void dp_set_hw_test_pattern( + struct dc_link *link, + enum dp_test_pattern test_pattern, + uint8_t *custom_pattern, + uint32_t custom_pattern_size) +{ + struct encoder_set_dp_phy_pattern_param pattern_param = {0}; + struct link_encoder *encoder = link->link_enc; + + pattern_param.dp_phy_pattern = test_pattern; + pattern_param.custom_pattern = custom_pattern; + pattern_param.custom_pattern_size = custom_pattern_size; + pattern_param.dp_panel_mode = dp_get_panel_mode(link); + + encoder->funcs->dp_set_phy_pattern(encoder, &pattern_param); +} + +void dp_retrain_link_dp_test(struct dc_link *link, + struct dc_link_settings *link_setting, + bool skip_video_pattern) +{ + struct pipe_ctx *pipes = + &link->dc->current_state->res_ctx.pipe_ctx[0]; + unsigned int i; + + for (i = 0; i < MAX_PIPES; i++) { + if (pipes[i].stream != NULL && + pipes[i].stream->sink != NULL && + pipes[i].stream->sink->link != NULL && + pipes[i].stream_res.stream_enc != NULL && + pipes[i].stream->sink->link == link) { + udelay(100); + + pipes[i].stream_res.stream_enc->funcs->dp_blank( + pipes[i].stream_res.stream_enc); + + /* disable any test pattern that might be active */ + dp_set_hw_test_pattern(link, + DP_TEST_PATTERN_VIDEO_MODE, NULL, 0); + + dp_receiver_power_ctrl(link, false); + + link->dc->hwss.disable_stream(&pipes[i], KEEP_ACQUIRED_RESOURCE); + + link->link_enc->funcs->disable_output( + link->link_enc, + SIGNAL_TYPE_DISPLAY_PORT, + link); + + /* Clear current link setting. */ + memset(&link->cur_link_settings, 0, + sizeof(link->cur_link_settings)); + + link->link_enc->funcs->enable_dp_output( + link->link_enc, + link_setting, + pipes[i].clock_source->id); + + dp_receiver_power_ctrl(link, true); + + perform_link_training_with_retries( + link, + link_setting, + skip_video_pattern, + LINK_TRAINING_ATTEMPTS); + + link->cur_link_settings = *link_setting; + + link->dc->hwss.enable_stream(&pipes[i]); + + link->dc->hwss.unblank_stream(&pipes[i], + link_setting); + + if (pipes[i].stream_res.audio) { + /* notify audio driver for + * audio modes of monitor */ + pipes[i].stream_res.audio->funcs->az_enable( + pipes[i].stream_res.audio); + + /* un-mute audio */ + /* TODO: audio should be per stream rather than + * per link */ + pipes[i].stream_res.stream_enc->funcs-> + audio_mute_control( + pipes[i].stream_res.stream_enc, false); + } + } + } +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c new file mode 100644 index 000000000000..d1cdf9f8853d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -0,0 +1,2795 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "link_encoder.h" +#include "stream_encoder.h" +#include "opp.h" +#include "timing_generator.h" +#include "transform.h" +#include "dpp.h" +#include "core_types.h" +#include "set_mode_types.h" +#include "virtual/virtual_stream_encoder.h" + +#include "dce80/dce80_resource.h" +#include "dce100/dce100_resource.h" +#include "dce110/dce110_resource.h" +#include "dce112/dce112_resource.h" +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "dcn10/dcn10_resource.h" +#endif +#include "dce120/dce120_resource.h" + +enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) +{ + enum dce_version dc_version = DCE_VERSION_UNKNOWN; + switch (asic_id.chip_family) { + + case FAMILY_CI: + dc_version = DCE_VERSION_8_0; + break; + case FAMILY_KV: + if (ASIC_REV_IS_KALINDI(asic_id.hw_internal_rev) || + ASIC_REV_IS_BHAVANI(asic_id.hw_internal_rev) || + ASIC_REV_IS_GODAVARI(asic_id.hw_internal_rev)) + dc_version = DCE_VERSION_8_3; + else + dc_version = DCE_VERSION_8_1; + break; + case FAMILY_CZ: + dc_version = DCE_VERSION_11_0; + break; + + case FAMILY_VI: + if (ASIC_REV_IS_TONGA_P(asic_id.hw_internal_rev) || + ASIC_REV_IS_FIJI_P(asic_id.hw_internal_rev)) { + dc_version = DCE_VERSION_10_0; + break; + } + if (ASIC_REV_IS_POLARIS10_P(asic_id.hw_internal_rev) || + ASIC_REV_IS_POLARIS11_M(asic_id.hw_internal_rev) || + ASIC_REV_IS_POLARIS12_V(asic_id.hw_internal_rev)) { + dc_version = DCE_VERSION_11_2; + } + break; + case FAMILY_AI: + dc_version = DCE_VERSION_12_0; + break; +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case FAMILY_RV: + dc_version = DCN_VERSION_1_0; + break; +#endif + default: + dc_version = DCE_VERSION_UNKNOWN; + break; + } + return dc_version; +} + +struct resource_pool *dc_create_resource_pool( + struct dc *dc, + int num_virtual_links, + enum dce_version dc_version, + struct hw_asic_id asic_id) +{ + struct resource_pool *res_pool = NULL; + + switch (dc_version) { + case DCE_VERSION_8_0: + res_pool = dce80_create_resource_pool( + num_virtual_links, dc); + break; + case DCE_VERSION_8_1: + res_pool = dce81_create_resource_pool( + num_virtual_links, dc); + break; + case DCE_VERSION_8_3: + res_pool = dce83_create_resource_pool( + num_virtual_links, dc); + break; + case DCE_VERSION_10_0: + res_pool = dce100_create_resource_pool( + num_virtual_links, dc); + break; + case DCE_VERSION_11_0: + res_pool = dce110_create_resource_pool( + num_virtual_links, dc, asic_id); + break; + case DCE_VERSION_11_2: + res_pool = dce112_create_resource_pool( + num_virtual_links, dc); + break; + case DCE_VERSION_12_0: + res_pool = dce120_create_resource_pool( + num_virtual_links, dc); + break; + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case DCN_VERSION_1_0: + res_pool = dcn10_create_resource_pool( + num_virtual_links, dc); + break; +#endif + + + default: + break; + } + if (res_pool != NULL) { + struct dc_firmware_info fw_info = { { 0 } }; + + if (dc->ctx->dc_bios->funcs->get_firmware_info( + dc->ctx->dc_bios, &fw_info) == BP_RESULT_OK) { + res_pool->ref_clock_inKhz = fw_info.pll_info.crystal_frequency; + } else + ASSERT_CRITICAL(false); + } + + return res_pool; +} + +void dc_destroy_resource_pool(struct dc *dc) +{ + if (dc) { + if (dc->res_pool) + dc->res_pool->funcs->destroy(&dc->res_pool); + + kfree(dc->hwseq); + } +} + +static void update_num_audio( + const struct resource_straps *straps, + unsigned int *num_audio, + struct audio_support *aud_support) +{ + aud_support->dp_audio = true; + aud_support->hdmi_audio_native = false; + aud_support->hdmi_audio_on_dongle = false; + + if (straps->hdmi_disable == 0) { + if (straps->dc_pinstraps_audio & 0x2) { + aud_support->hdmi_audio_on_dongle = true; + aud_support->hdmi_audio_native = true; + } + } + + switch (straps->audio_stream_number) { + case 0: /* multi streams supported */ + break; + case 1: /* multi streams not supported */ + *num_audio = 1; + break; + default: + DC_ERR("DC: unexpected audio fuse!\n"); + } +} + +bool resource_construct( + unsigned int num_virtual_links, + struct dc *dc, + struct resource_pool *pool, + const struct resource_create_funcs *create_funcs) +{ + struct dc_context *ctx = dc->ctx; + const struct resource_caps *caps = pool->res_cap; + int i; + unsigned int num_audio = caps->num_audio; + struct resource_straps straps = {0}; + + if (create_funcs->read_dce_straps) + create_funcs->read_dce_straps(dc->ctx, &straps); + + pool->audio_count = 0; + if (create_funcs->create_audio) { + /* find the total number of streams available via the + * AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT + * registers (one for each pin) starting from pin 1 + * up to the max number of audio pins. + * We stop on the first pin where + * PORT_CONNECTIVITY == 1 (as instructed by HW team). + */ + update_num_audio(&straps, &num_audio, &pool->audio_support); + for (i = 0; i < pool->pipe_count && i < num_audio; i++) { + struct audio *aud = create_funcs->create_audio(ctx, i); + + if (aud == NULL) { + DC_ERR("DC: failed to create audio!\n"); + return false; + } + + if (!aud->funcs->endpoint_valid(aud)) { + aud->funcs->destroy(&aud); + break; + } + + pool->audios[i] = aud; + pool->audio_count++; + } + } + + pool->stream_enc_count = 0; + if (create_funcs->create_stream_encoder) { + for (i = 0; i < caps->num_stream_encoder; i++) { + pool->stream_enc[i] = create_funcs->create_stream_encoder(i, ctx); + if (pool->stream_enc[i] == NULL) + DC_ERR("DC: failed to create stream_encoder!\n"); + pool->stream_enc_count++; + } + } + dc->caps.dynamic_audio = false; + if (pool->audio_count < pool->stream_enc_count) { + dc->caps.dynamic_audio = true; + } + for (i = 0; i < num_virtual_links; i++) { + pool->stream_enc[pool->stream_enc_count] = + virtual_stream_encoder_create( + ctx, ctx->dc_bios); + if (pool->stream_enc[pool->stream_enc_count] == NULL) { + DC_ERR("DC: failed to create stream_encoder!\n"); + return false; + } + pool->stream_enc_count++; + } + + dc->hwseq = create_funcs->create_hwseq(ctx); + + return true; +} + + +void resource_unreference_clock_source( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct clock_source *clock_source) +{ + int i; + + for (i = 0; i < pool->clk_src_count; i++) { + if (pool->clock_sources[i] != clock_source) + continue; + + res_ctx->clock_source_ref_count[i]--; + + break; + } + + if (pool->dp_clock_source == clock_source) + res_ctx->dp_clock_source_ref_count--; +} + +void resource_reference_clock_source( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct clock_source *clock_source) +{ + int i; + for (i = 0; i < pool->clk_src_count; i++) { + if (pool->clock_sources[i] != clock_source) + continue; + + res_ctx->clock_source_ref_count[i]++; + break; + } + + if (pool->dp_clock_source == clock_source) + res_ctx->dp_clock_source_ref_count++; +} + +bool resource_are_streams_timing_synchronizable( + struct dc_stream_state *stream1, + struct dc_stream_state *stream2) +{ + if (stream1->timing.h_total != stream2->timing.h_total) + return false; + + if (stream1->timing.v_total != stream2->timing.v_total) + return false; + + if (stream1->timing.h_addressable + != stream2->timing.h_addressable) + return false; + + if (stream1->timing.v_addressable + != stream2->timing.v_addressable) + return false; + + if (stream1->timing.pix_clk_khz + != stream2->timing.pix_clk_khz) + return false; + + if (stream1->phy_pix_clk != stream2->phy_pix_clk + && (!dc_is_dp_signal(stream1->signal) + || !dc_is_dp_signal(stream2->signal))) + return false; + + return true; +} + +static bool is_sharable_clk_src( + const struct pipe_ctx *pipe_with_clk_src, + const struct pipe_ctx *pipe) +{ + if (pipe_with_clk_src->clock_source == NULL) + return false; + + if (pipe_with_clk_src->stream->signal == SIGNAL_TYPE_VIRTUAL) + return false; + + if (dc_is_dp_signal(pipe_with_clk_src->stream->signal)) + return false; + + if (dc_is_hdmi_signal(pipe_with_clk_src->stream->signal) + && dc_is_dvi_signal(pipe->stream->signal)) + return false; + + if (dc_is_hdmi_signal(pipe->stream->signal) + && dc_is_dvi_signal(pipe_with_clk_src->stream->signal)) + return false; + + if (!resource_are_streams_timing_synchronizable( + pipe_with_clk_src->stream, pipe->stream)) + return false; + + return true; +} + +struct clock_source *resource_find_used_clk_src_for_sharing( + struct resource_context *res_ctx, + struct pipe_ctx *pipe_ctx) +{ + int i; + + for (i = 0; i < MAX_PIPES; i++) { + if (is_sharable_clk_src(&res_ctx->pipe_ctx[i], pipe_ctx)) + return res_ctx->pipe_ctx[i].clock_source; + } + + return NULL; +} + +static enum pixel_format convert_pixel_format_to_dalsurface( + enum surface_pixel_format surface_pixel_format) +{ + enum pixel_format dal_pixel_format = PIXEL_FORMAT_UNKNOWN; + + switch (surface_pixel_format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + dal_pixel_format = PIXEL_FORMAT_INDEX8; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + dal_pixel_format = PIXEL_FORMAT_RGB565; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + dal_pixel_format = PIXEL_FORMAT_RGB565; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + dal_pixel_format = PIXEL_FORMAT_ARGB8888; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + dal_pixel_format = PIXEL_FORMAT_ARGB8888; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + dal_pixel_format = PIXEL_FORMAT_ARGB2101010; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + dal_pixel_format = PIXEL_FORMAT_ARGB2101010; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + dal_pixel_format = PIXEL_FORMAT_ARGB2101010_XRBIAS; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + dal_pixel_format = PIXEL_FORMAT_FP16; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + dal_pixel_format = PIXEL_FORMAT_420BPP8; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + dal_pixel_format = PIXEL_FORMAT_420BPP10; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + default: + dal_pixel_format = PIXEL_FORMAT_UNKNOWN; + break; + } + return dal_pixel_format; +} + +static void rect_swap_helper(struct rect *rect) +{ + uint32_t temp = 0; + + temp = rect->height; + rect->height = rect->width; + rect->width = temp; + + temp = rect->x; + rect->x = rect->y; + rect->y = temp; +} + +static void calculate_viewport(struct pipe_ctx *pipe_ctx) +{ + const struct dc_plane_state *plane_state = pipe_ctx->plane_state; + const struct dc_stream_state *stream = pipe_ctx->stream; + struct scaler_data *data = &pipe_ctx->plane_res.scl_data; + struct rect surf_src = plane_state->src_rect; + struct rect clip = { 0 }; + int vpc_div = (data->format == PIXEL_FORMAT_420BPP8 + || data->format == PIXEL_FORMAT_420BPP10) ? 2 : 1; + bool pri_split = pipe_ctx->bottom_pipe && + pipe_ctx->bottom_pipe->plane_state == pipe_ctx->plane_state; + bool sec_split = pipe_ctx->top_pipe && + pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state; + + if (stream->view_format == VIEW_3D_FORMAT_SIDE_BY_SIDE || + stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) { + pri_split = false; + sec_split = false; + } + + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 || + pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) + rect_swap_helper(&surf_src); + + /* The actual clip is an intersection between stream + * source and surface clip + */ + clip.x = stream->src.x > plane_state->clip_rect.x ? + stream->src.x : plane_state->clip_rect.x; + + clip.width = stream->src.x + stream->src.width < + plane_state->clip_rect.x + plane_state->clip_rect.width ? + stream->src.x + stream->src.width - clip.x : + plane_state->clip_rect.x + plane_state->clip_rect.width - clip.x ; + + clip.y = stream->src.y > plane_state->clip_rect.y ? + stream->src.y : plane_state->clip_rect.y; + + clip.height = stream->src.y + stream->src.height < + plane_state->clip_rect.y + plane_state->clip_rect.height ? + stream->src.y + stream->src.height - clip.y : + plane_state->clip_rect.y + plane_state->clip_rect.height - clip.y ; + + /* offset = surf_src.ofs + (clip.ofs - surface->dst_rect.ofs) * scl_ratio + * num_pixels = clip.num_pix * scl_ratio + */ + data->viewport.x = surf_src.x + (clip.x - plane_state->dst_rect.x) * + surf_src.width / plane_state->dst_rect.width; + data->viewport.width = clip.width * + surf_src.width / plane_state->dst_rect.width; + + data->viewport.y = surf_src.y + (clip.y - plane_state->dst_rect.y) * + surf_src.height / plane_state->dst_rect.height; + data->viewport.height = clip.height * + surf_src.height / plane_state->dst_rect.height; + + /* Round down, compensate in init */ + data->viewport_c.x = data->viewport.x / vpc_div; + data->viewport_c.y = data->viewport.y / vpc_div; + data->inits.h_c = (data->viewport.x % vpc_div) != 0 ? + dal_fixed31_32_half : dal_fixed31_32_zero; + data->inits.v_c = (data->viewport.y % vpc_div) != 0 ? + dal_fixed31_32_half : dal_fixed31_32_zero; + /* Round up, assume original video size always even dimensions */ + data->viewport_c.width = (data->viewport.width + vpc_div - 1) / vpc_div; + data->viewport_c.height = (data->viewport.height + vpc_div - 1) / vpc_div; + + /* Handle hsplit */ + if (pri_split || sec_split) { + /* HMirror XOR Secondary_pipe XOR Rotation_180 */ + bool right_view = (sec_split != plane_state->horizontal_mirror) != + (plane_state->rotation == ROTATION_ANGLE_180); + + if (plane_state->rotation == ROTATION_ANGLE_90 + || plane_state->rotation == ROTATION_ANGLE_270) + /* Secondary_pipe XOR Rotation_270 */ + right_view = (plane_state->rotation == ROTATION_ANGLE_270) != sec_split; + + if (right_view) { + data->viewport.width /= 2; + data->viewport_c.width /= 2; + data->viewport.x += data->viewport.width; + data->viewport_c.x += data->viewport_c.width; + /* Ceil offset pipe */ + data->viewport.width += data->viewport.width % 2; + data->viewport_c.width += data->viewport_c.width % 2; + } else { + data->viewport.width /= 2; + data->viewport_c.width /= 2; + } + } + + if (plane_state->rotation == ROTATION_ANGLE_90 || + plane_state->rotation == ROTATION_ANGLE_270) { + rect_swap_helper(&data->viewport_c); + rect_swap_helper(&data->viewport); + } +} + +static void calculate_recout(struct pipe_ctx *pipe_ctx, struct view *recout_skip) +{ + const struct dc_plane_state *plane_state = pipe_ctx->plane_state; + const struct dc_stream_state *stream = pipe_ctx->stream; + struct rect surf_src = plane_state->src_rect; + struct rect surf_clip = plane_state->clip_rect; + int recout_full_x, recout_full_y; + + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 || + pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) + rect_swap_helper(&surf_src); + + pipe_ctx->plane_res.scl_data.recout.x = stream->dst.x; + if (stream->src.x < surf_clip.x) + pipe_ctx->plane_res.scl_data.recout.x += (surf_clip.x + - stream->src.x) * stream->dst.width + / stream->src.width; + + pipe_ctx->plane_res.scl_data.recout.width = surf_clip.width * + stream->dst.width / stream->src.width; + if (pipe_ctx->plane_res.scl_data.recout.width + pipe_ctx->plane_res.scl_data.recout.x > + stream->dst.x + stream->dst.width) + pipe_ctx->plane_res.scl_data.recout.width = + stream->dst.x + stream->dst.width + - pipe_ctx->plane_res.scl_data.recout.x; + + pipe_ctx->plane_res.scl_data.recout.y = stream->dst.y; + if (stream->src.y < surf_clip.y) + pipe_ctx->plane_res.scl_data.recout.y += (surf_clip.y + - stream->src.y) * stream->dst.height + / stream->src.height; + + pipe_ctx->plane_res.scl_data.recout.height = surf_clip.height * + stream->dst.height / stream->src.height; + if (pipe_ctx->plane_res.scl_data.recout.height + pipe_ctx->plane_res.scl_data.recout.y > + stream->dst.y + stream->dst.height) + pipe_ctx->plane_res.scl_data.recout.height = + stream->dst.y + stream->dst.height + - pipe_ctx->plane_res.scl_data.recout.y; + + /* Handle h & vsplit */ + if (pipe_ctx->top_pipe && pipe_ctx->top_pipe->plane_state == + pipe_ctx->plane_state) { + if (stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) { + pipe_ctx->plane_res.scl_data.recout.height /= 2; + pipe_ctx->plane_res.scl_data.recout.y += pipe_ctx->plane_res.scl_data.recout.height; + /* Floor primary pipe, ceil 2ndary pipe */ + pipe_ctx->plane_res.scl_data.recout.height += pipe_ctx->plane_res.scl_data.recout.height % 2; + } else { + pipe_ctx->plane_res.scl_data.recout.width /= 2; + pipe_ctx->plane_res.scl_data.recout.x += pipe_ctx->plane_res.scl_data.recout.width; + pipe_ctx->plane_res.scl_data.recout.width += pipe_ctx->plane_res.scl_data.recout.width % 2; + } + } else if (pipe_ctx->bottom_pipe && + pipe_ctx->bottom_pipe->plane_state == pipe_ctx->plane_state) { + if (stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) + pipe_ctx->plane_res.scl_data.recout.height /= 2; + else + pipe_ctx->plane_res.scl_data.recout.width /= 2; + } + + /* Unclipped recout offset = stream dst offset + ((surf dst offset - stream surf_src offset) + * * 1/ stream scaling ratio) - (surf surf_src offset * 1/ full scl + * ratio) + */ + recout_full_x = stream->dst.x + (plane_state->dst_rect.x - stream->src.x) + * stream->dst.width / stream->src.width - + surf_src.x * plane_state->dst_rect.width / surf_src.width + * stream->dst.width / stream->src.width; + recout_full_y = stream->dst.y + (plane_state->dst_rect.y - stream->src.y) + * stream->dst.height / stream->src.height - + surf_src.y * plane_state->dst_rect.height / surf_src.height + * stream->dst.height / stream->src.height; + + recout_skip->width = pipe_ctx->plane_res.scl_data.recout.x - recout_full_x; + recout_skip->height = pipe_ctx->plane_res.scl_data.recout.y - recout_full_y; +} + +static void calculate_scaling_ratios(struct pipe_ctx *pipe_ctx) +{ + const struct dc_plane_state *plane_state = pipe_ctx->plane_state; + const struct dc_stream_state *stream = pipe_ctx->stream; + struct rect surf_src = plane_state->src_rect; + const int in_w = stream->src.width; + const int in_h = stream->src.height; + const int out_w = stream->dst.width; + const int out_h = stream->dst.height; + + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 || + pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) + rect_swap_helper(&surf_src); + + pipe_ctx->plane_res.scl_data.ratios.horz = dal_fixed31_32_from_fraction( + surf_src.width, + plane_state->dst_rect.width); + pipe_ctx->plane_res.scl_data.ratios.vert = dal_fixed31_32_from_fraction( + surf_src.height, + plane_state->dst_rect.height); + + if (stream->view_format == VIEW_3D_FORMAT_SIDE_BY_SIDE) + pipe_ctx->plane_res.scl_data.ratios.horz.value *= 2; + else if (stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) + pipe_ctx->plane_res.scl_data.ratios.vert.value *= 2; + + pipe_ctx->plane_res.scl_data.ratios.vert.value = div64_s64( + pipe_ctx->plane_res.scl_data.ratios.vert.value * in_h, out_h); + pipe_ctx->plane_res.scl_data.ratios.horz.value = div64_s64( + pipe_ctx->plane_res.scl_data.ratios.horz.value * in_w, out_w); + + pipe_ctx->plane_res.scl_data.ratios.horz_c = pipe_ctx->plane_res.scl_data.ratios.horz; + pipe_ctx->plane_res.scl_data.ratios.vert_c = pipe_ctx->plane_res.scl_data.ratios.vert; + + if (pipe_ctx->plane_res.scl_data.format == PIXEL_FORMAT_420BPP8 + || pipe_ctx->plane_res.scl_data.format == PIXEL_FORMAT_420BPP10) { + pipe_ctx->plane_res.scl_data.ratios.horz_c.value /= 2; + pipe_ctx->plane_res.scl_data.ratios.vert_c.value /= 2; + } +} + +static void calculate_inits_and_adj_vp(struct pipe_ctx *pipe_ctx, struct view *recout_skip) +{ + struct scaler_data *data = &pipe_ctx->plane_res.scl_data; + struct rect src = pipe_ctx->plane_state->src_rect; + int vpc_div = (data->format == PIXEL_FORMAT_420BPP8 + || data->format == PIXEL_FORMAT_420BPP10) ? 2 : 1; + + + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 || + pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) { + rect_swap_helper(&src); + rect_swap_helper(&data->viewport_c); + rect_swap_helper(&data->viewport); + } + + /* + * Init calculated according to formula: + * init = (scaling_ratio + number_of_taps + 1) / 2 + * init_bot = init + scaling_ratio + * init_c = init + truncated_vp_c_offset(from calculate viewport) + */ + data->inits.h = dal_fixed31_32_div_int( + dal_fixed31_32_add_int(data->ratios.horz, data->taps.h_taps + 1), 2); + + data->inits.h_c = dal_fixed31_32_add(data->inits.h_c, dal_fixed31_32_div_int( + dal_fixed31_32_add_int(data->ratios.horz_c, data->taps.h_taps_c + 1), 2)); + + data->inits.v = dal_fixed31_32_div_int( + dal_fixed31_32_add_int(data->ratios.vert, data->taps.v_taps + 1), 2); + + data->inits.v_c = dal_fixed31_32_add(data->inits.v_c, dal_fixed31_32_div_int( + dal_fixed31_32_add_int(data->ratios.vert_c, data->taps.v_taps_c + 1), 2)); + + + /* Adjust for viewport end clip-off */ + if ((data->viewport.x + data->viewport.width) < (src.x + src.width)) { + int vp_clip = src.x + src.width - data->viewport.width - data->viewport.x; + int int_part = dal_fixed31_32_floor( + dal_fixed31_32_sub(data->inits.h, data->ratios.horz)); + + int_part = int_part > 0 ? int_part : 0; + data->viewport.width += int_part < vp_clip ? int_part : vp_clip; + } + if ((data->viewport.y + data->viewport.height) < (src.y + src.height)) { + int vp_clip = src.y + src.height - data->viewport.height - data->viewport.y; + int int_part = dal_fixed31_32_floor( + dal_fixed31_32_sub(data->inits.v, data->ratios.vert)); + + int_part = int_part > 0 ? int_part : 0; + data->viewport.height += int_part < vp_clip ? int_part : vp_clip; + } + if ((data->viewport_c.x + data->viewport_c.width) < (src.x + src.width) / vpc_div) { + int vp_clip = (src.x + src.width) / vpc_div - + data->viewport_c.width - data->viewport_c.x; + int int_part = dal_fixed31_32_floor( + dal_fixed31_32_sub(data->inits.h_c, data->ratios.horz_c)); + + int_part = int_part > 0 ? int_part : 0; + data->viewport_c.width += int_part < vp_clip ? int_part : vp_clip; + } + if ((data->viewport_c.y + data->viewport_c.height) < (src.y + src.height) / vpc_div) { + int vp_clip = (src.y + src.height) / vpc_div - + data->viewport_c.height - data->viewport_c.y; + int int_part = dal_fixed31_32_floor( + dal_fixed31_32_sub(data->inits.v_c, data->ratios.vert_c)); + + int_part = int_part > 0 ? int_part : 0; + data->viewport_c.height += int_part < vp_clip ? int_part : vp_clip; + } + + /* Adjust for non-0 viewport offset */ + if (data->viewport.x) { + int int_part; + + data->inits.h = dal_fixed31_32_add(data->inits.h, dal_fixed31_32_mul_int( + data->ratios.horz, recout_skip->width)); + int_part = dal_fixed31_32_floor(data->inits.h) - data->viewport.x; + if (int_part < data->taps.h_taps) { + int int_adj = data->viewport.x >= (data->taps.h_taps - int_part) ? + (data->taps.h_taps - int_part) : data->viewport.x; + data->viewport.x -= int_adj; + data->viewport.width += int_adj; + int_part += int_adj; + } else if (int_part > data->taps.h_taps) { + data->viewport.x += int_part - data->taps.h_taps; + data->viewport.width -= int_part - data->taps.h_taps; + int_part = data->taps.h_taps; + } + data->inits.h.value &= 0xffffffff; + data->inits.h = dal_fixed31_32_add_int(data->inits.h, int_part); + } + + if (data->viewport_c.x) { + int int_part; + + data->inits.h_c = dal_fixed31_32_add(data->inits.h_c, dal_fixed31_32_mul_int( + data->ratios.horz_c, recout_skip->width)); + int_part = dal_fixed31_32_floor(data->inits.h_c) - data->viewport_c.x; + if (int_part < data->taps.h_taps_c) { + int int_adj = data->viewport_c.x >= (data->taps.h_taps_c - int_part) ? + (data->taps.h_taps_c - int_part) : data->viewport_c.x; + data->viewport_c.x -= int_adj; + data->viewport_c.width += int_adj; + int_part += int_adj; + } else if (int_part > data->taps.h_taps_c) { + data->viewport_c.x += int_part - data->taps.h_taps_c; + data->viewport_c.width -= int_part - data->taps.h_taps_c; + int_part = data->taps.h_taps_c; + } + data->inits.h_c.value &= 0xffffffff; + data->inits.h_c = dal_fixed31_32_add_int(data->inits.h_c, int_part); + } + + if (data->viewport.y) { + int int_part; + + data->inits.v = dal_fixed31_32_add(data->inits.v, dal_fixed31_32_mul_int( + data->ratios.vert, recout_skip->height)); + int_part = dal_fixed31_32_floor(data->inits.v) - data->viewport.y; + if (int_part < data->taps.v_taps) { + int int_adj = data->viewport.y >= (data->taps.v_taps - int_part) ? + (data->taps.v_taps - int_part) : data->viewport.y; + data->viewport.y -= int_adj; + data->viewport.height += int_adj; + int_part += int_adj; + } else if (int_part > data->taps.v_taps) { + data->viewport.y += int_part - data->taps.v_taps; + data->viewport.height -= int_part - data->taps.v_taps; + int_part = data->taps.v_taps; + } + data->inits.v.value &= 0xffffffff; + data->inits.v = dal_fixed31_32_add_int(data->inits.v, int_part); + } + + if (data->viewport_c.y) { + int int_part; + + data->inits.v_c = dal_fixed31_32_add(data->inits.v_c, dal_fixed31_32_mul_int( + data->ratios.vert_c, recout_skip->height)); + int_part = dal_fixed31_32_floor(data->inits.v_c) - data->viewport_c.y; + if (int_part < data->taps.v_taps_c) { + int int_adj = data->viewport_c.y >= (data->taps.v_taps_c - int_part) ? + (data->taps.v_taps_c - int_part) : data->viewport_c.y; + data->viewport_c.y -= int_adj; + data->viewport_c.height += int_adj; + int_part += int_adj; + } else if (int_part > data->taps.v_taps_c) { + data->viewport_c.y += int_part - data->taps.v_taps_c; + data->viewport_c.height -= int_part - data->taps.v_taps_c; + int_part = data->taps.v_taps_c; + } + data->inits.v_c.value &= 0xffffffff; + data->inits.v_c = dal_fixed31_32_add_int(data->inits.v_c, int_part); + } + + /* Interlaced inits based on final vert inits */ + data->inits.v_bot = dal_fixed31_32_add(data->inits.v, data->ratios.vert); + data->inits.v_c_bot = dal_fixed31_32_add(data->inits.v_c, data->ratios.vert_c); + + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 || + pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) { + rect_swap_helper(&data->viewport_c); + rect_swap_helper(&data->viewport); + } +} + +bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) +{ + const struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct dc_crtc_timing *timing = &pipe_ctx->stream->timing; + struct view recout_skip = { 0 }; + bool res = false; + + /* Important: scaling ratio calculation requires pixel format, + * lb depth calculation requires recout and taps require scaling ratios. + * Inits require viewport, taps, ratios and recout of split pipe + */ + pipe_ctx->plane_res.scl_data.format = convert_pixel_format_to_dalsurface( + pipe_ctx->plane_state->format); + + calculate_scaling_ratios(pipe_ctx); + + calculate_viewport(pipe_ctx); + + if (pipe_ctx->plane_res.scl_data.viewport.height < 16 || pipe_ctx->plane_res.scl_data.viewport.width < 16) + return false; + + calculate_recout(pipe_ctx, &recout_skip); + + /** + * Setting line buffer pixel depth to 24bpp yields banding + * on certain displays, such as the Sharp 4k + */ + pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP; + + pipe_ctx->plane_res.scl_data.recout.x += timing->h_border_left; + pipe_ctx->plane_res.scl_data.recout.y += timing->v_border_top; + + pipe_ctx->plane_res.scl_data.h_active = timing->h_addressable + timing->h_border_left + timing->h_border_right; + pipe_ctx->plane_res.scl_data.v_active = timing->v_addressable + timing->v_border_top + timing->v_border_bottom; + + /* Taps calculations */ + if (pipe_ctx->plane_res.xfm != NULL) + res = pipe_ctx->plane_res.xfm->funcs->transform_get_optimal_number_of_taps( + pipe_ctx->plane_res.xfm, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality); + + if (pipe_ctx->plane_res.dpp != NULL) + res = pipe_ctx->plane_res.dpp->funcs->dpp_get_optimal_number_of_taps( + pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality); + + if (!res) { + /* Try 24 bpp linebuffer */ + pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_24BPP; + + res = pipe_ctx->plane_res.xfm->funcs->transform_get_optimal_number_of_taps( + pipe_ctx->plane_res.xfm, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality); + + res = pipe_ctx->plane_res.dpp->funcs->dpp_get_optimal_number_of_taps( + pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality); + } + + if (res) + /* May need to re-check lb size after this in some obscure scenario */ + calculate_inits_and_adj_vp(pipe_ctx, &recout_skip); + + dm_logger_write(pipe_ctx->stream->ctx->logger, LOG_SCALER, + "%s: Viewport:\nheight:%d width:%d x:%d " + "y:%d\n dst_rect:\nheight:%d width:%d x:%d " + "y:%d\n", + __func__, + pipe_ctx->plane_res.scl_data.viewport.height, + pipe_ctx->plane_res.scl_data.viewport.width, + pipe_ctx->plane_res.scl_data.viewport.x, + pipe_ctx->plane_res.scl_data.viewport.y, + plane_state->dst_rect.height, + plane_state->dst_rect.width, + plane_state->dst_rect.x, + plane_state->dst_rect.y); + + return res; +} + + +enum dc_status resource_build_scaling_params_for_context( + const struct dc *dc, + struct dc_state *context) +{ + int i; + + for (i = 0; i < MAX_PIPES; i++) { + if (context->res_ctx.pipe_ctx[i].plane_state != NULL && + context->res_ctx.pipe_ctx[i].stream != NULL) + if (!resource_build_scaling_params(&context->res_ctx.pipe_ctx[i])) + return DC_FAIL_SCALING; + } + + return DC_OK; +} + +struct pipe_ctx *find_idle_secondary_pipe( + struct resource_context *res_ctx, + const struct resource_pool *pool) +{ + int i; + struct pipe_ctx *secondary_pipe = NULL; + + /* + * search backwards for the second pipe to keep pipe + * assignment more consistent + */ + + for (i = pool->pipe_count - 1; i >= 0; i--) { + if (res_ctx->pipe_ctx[i].stream == NULL) { + secondary_pipe = &res_ctx->pipe_ctx[i]; + secondary_pipe->pipe_idx = i; + break; + } + } + + + return secondary_pipe; +} + +struct pipe_ctx *resource_get_head_pipe_for_stream( + struct resource_context *res_ctx, + struct dc_stream_state *stream) +{ + int i; + for (i = 0; i < MAX_PIPES; i++) { + if (res_ctx->pipe_ctx[i].stream == stream && + !res_ctx->pipe_ctx[i].top_pipe) { + return &res_ctx->pipe_ctx[i]; + break; + } + } + return NULL; +} + +static struct pipe_ctx *resource_get_tail_pipe_for_stream( + struct resource_context *res_ctx, + struct dc_stream_state *stream) +{ + struct pipe_ctx *head_pipe, *tail_pipe; + head_pipe = resource_get_head_pipe_for_stream(res_ctx, stream); + + if (!head_pipe) + return NULL; + + tail_pipe = head_pipe->bottom_pipe; + + while (tail_pipe) { + head_pipe = tail_pipe; + tail_pipe = tail_pipe->bottom_pipe; + } + + return head_pipe; +} + +/* + * A free_pipe for a stream is defined here as a pipe + * that has no surface attached yet + */ +static struct pipe_ctx *acquire_free_pipe_for_stream( + struct dc_state *context, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + int i; + struct resource_context *res_ctx = &context->res_ctx; + + struct pipe_ctx *head_pipe = NULL; + + /* Find head pipe, which has the back end set up*/ + + head_pipe = resource_get_head_pipe_for_stream(res_ctx, stream); + + if (!head_pipe) + ASSERT(0); + + if (!head_pipe->plane_state) + return head_pipe; + + /* Re-use pipe already acquired for this stream if available*/ + for (i = pool->pipe_count - 1; i >= 0; i--) { + if (res_ctx->pipe_ctx[i].stream == stream && + !res_ctx->pipe_ctx[i].plane_state) { + return &res_ctx->pipe_ctx[i]; + } + } + + /* + * At this point we have no re-useable pipe for this stream and we need + * to acquire an idle one to satisfy the request + */ + + if (!pool->funcs->acquire_idle_pipe_for_layer) + return NULL; + + return pool->funcs->acquire_idle_pipe_for_layer(context, pool, stream); + +} + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +static int acquire_first_split_pipe( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + int i; + + for (i = 0; i < pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + + if (pipe_ctx->top_pipe && + pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state) { + pipe_ctx->top_pipe->bottom_pipe = pipe_ctx->bottom_pipe; + if (pipe_ctx->bottom_pipe) + pipe_ctx->bottom_pipe->top_pipe = pipe_ctx->top_pipe; + + memset(pipe_ctx, 0, sizeof(*pipe_ctx)); + pipe_ctx->stream_res.tg = pool->timing_generators[i]; + pipe_ctx->plane_res.hubp = pool->hubps[i]; + pipe_ctx->plane_res.ipp = pool->ipps[i]; + pipe_ctx->plane_res.dpp = pool->dpps[i]; + pipe_ctx->stream_res.opp = pool->opps[i]; + pipe_ctx->pipe_idx = i; + + pipe_ctx->stream = stream; + return i; + } + } + return -1; +} +#endif + +bool dc_add_plane_to_context( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_plane_state *plane_state, + struct dc_state *context) +{ + int i; + struct resource_pool *pool = dc->res_pool; + struct pipe_ctx *head_pipe, *tail_pipe, *free_pipe; + struct dc_stream_status *stream_status = NULL; + + for (i = 0; i < context->stream_count; i++) + if (context->streams[i] == stream) { + stream_status = &context->stream_status[i]; + break; + } + if (stream_status == NULL) { + dm_error("Existing stream not found; failed to attach surface!\n"); + return false; + } + + + if (stream_status->plane_count == MAX_SURFACE_NUM) { + dm_error("Surface: can not attach plane_state %p! Maximum is: %d\n", + plane_state, MAX_SURFACE_NUM); + return false; + } + + head_pipe = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!head_pipe) { + dm_error("Head pipe not found for stream_state %p !\n", stream); + return false; + } + + free_pipe = acquire_free_pipe_for_stream(context, pool, stream); + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (!free_pipe) { + int pipe_idx = acquire_first_split_pipe(&context->res_ctx, pool, stream); + if (pipe_idx >= 0) + free_pipe = &context->res_ctx.pipe_ctx[pipe_idx]; + } +#endif + if (!free_pipe) + return false; + + /* retain new surfaces */ + dc_plane_state_retain(plane_state); + free_pipe->plane_state = plane_state; + + if (head_pipe != free_pipe) { + + tail_pipe = resource_get_tail_pipe_for_stream(&context->res_ctx, stream); + ASSERT(tail_pipe); + + free_pipe->stream_res.tg = tail_pipe->stream_res.tg; + free_pipe->stream_res.opp = tail_pipe->stream_res.opp; + free_pipe->stream_res.stream_enc = tail_pipe->stream_res.stream_enc; + free_pipe->stream_res.audio = tail_pipe->stream_res.audio; + free_pipe->clock_source = tail_pipe->clock_source; + free_pipe->top_pipe = tail_pipe; + tail_pipe->bottom_pipe = free_pipe; + } + + /* assign new surfaces*/ + stream_status->plane_states[stream_status->plane_count] = plane_state; + + stream_status->plane_count++; + + return true; +} + +bool dc_remove_plane_from_context( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_plane_state *plane_state, + struct dc_state *context) +{ + int i; + struct dc_stream_status *stream_status = NULL; + struct resource_pool *pool = dc->res_pool; + + for (i = 0; i < context->stream_count; i++) + if (context->streams[i] == stream) { + stream_status = &context->stream_status[i]; + break; + } + + if (stream_status == NULL) { + dm_error("Existing stream not found; failed to remove plane.\n"); + return false; + } + + /* release pipe for plane*/ + for (i = pool->pipe_count - 1; i >= 0; i--) { + struct pipe_ctx *pipe_ctx; + + if (context->res_ctx.pipe_ctx[i].plane_state == plane_state) { + pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->top_pipe) + pipe_ctx->top_pipe->bottom_pipe = pipe_ctx->bottom_pipe; + + /* Second condition is to avoid setting NULL to top pipe + * of tail pipe making it look like head pipe in subsequent + * deletes + */ + if (pipe_ctx->bottom_pipe && pipe_ctx->top_pipe) + pipe_ctx->bottom_pipe->top_pipe = pipe_ctx->top_pipe; + + /* + * For head pipe detach surfaces from pipe for tail + * pipe just zero it out + */ + if (!pipe_ctx->top_pipe) { + pipe_ctx->plane_state = NULL; + pipe_ctx->bottom_pipe = NULL; + } else { + memset(pipe_ctx, 0, sizeof(*pipe_ctx)); + } + } + } + + + for (i = 0; i < stream_status->plane_count; i++) { + if (stream_status->plane_states[i] == plane_state) { + + dc_plane_state_release(stream_status->plane_states[i]); + break; + } + } + + if (i == stream_status->plane_count) { + dm_error("Existing plane_state not found; failed to detach it!\n"); + return false; + } + + stream_status->plane_count--; + + /* Start at the plane we've just released, and move all the planes one index forward to "trim" the array */ + for (; i < stream_status->plane_count; i++) + stream_status->plane_states[i] = stream_status->plane_states[i + 1]; + + stream_status->plane_states[stream_status->plane_count] = NULL; + + return true; +} + +bool dc_rem_all_planes_for_stream( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_state *context) +{ + int i, old_plane_count; + struct dc_stream_status *stream_status = NULL; + struct dc_plane_state *del_planes[MAX_SURFACE_NUM] = { 0 }; + + for (i = 0; i < context->stream_count; i++) + if (context->streams[i] == stream) { + stream_status = &context->stream_status[i]; + break; + } + + if (stream_status == NULL) { + dm_error("Existing stream %p not found!\n", stream); + return false; + } + + old_plane_count = stream_status->plane_count; + + for (i = 0; i < old_plane_count; i++) + del_planes[i] = stream_status->plane_states[i]; + + for (i = 0; i < old_plane_count; i++) + if (!dc_remove_plane_from_context(dc, stream, del_planes[i], context)) + return false; + + return true; +} + +static bool add_all_planes_for_stream( + const struct dc *dc, + struct dc_stream_state *stream, + const struct dc_validation_set set[], + int set_count, + struct dc_state *context) +{ + int i, j; + + for (i = 0; i < set_count; i++) + if (set[i].stream == stream) + break; + + if (i == set_count) { + dm_error("Stream %p not found in set!\n", stream); + return false; + } + + for (j = 0; j < set[i].plane_count; j++) + if (!dc_add_plane_to_context(dc, stream, set[i].plane_states[j], context)) + return false; + + return true; +} + +bool dc_add_all_planes_for_stream( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_plane_state * const *plane_states, + int plane_count, + struct dc_state *context) +{ + struct dc_validation_set set; + int i; + + set.stream = stream; + set.plane_count = plane_count; + + for (i = 0; i < plane_count; i++) + set.plane_states[i] = plane_states[i]; + + return add_all_planes_for_stream(dc, stream, &set, 1, context); +} + + + +static bool is_timing_changed(struct dc_stream_state *cur_stream, + struct dc_stream_state *new_stream) +{ + if (cur_stream == NULL) + return true; + + /* If sink pointer changed, it means this is a hotplug, we should do + * full hw setting. + */ + if (cur_stream->sink != new_stream->sink) + return true; + + /* If output color space is changed, need to reprogram info frames */ + if (cur_stream->output_color_space != new_stream->output_color_space) + return true; + + return memcmp( + &cur_stream->timing, + &new_stream->timing, + sizeof(struct dc_crtc_timing)) != 0; +} + +static bool are_stream_backends_same( + struct dc_stream_state *stream_a, struct dc_stream_state *stream_b) +{ + if (stream_a == stream_b) + return true; + + if (stream_a == NULL || stream_b == NULL) + return false; + + if (is_timing_changed(stream_a, stream_b)) + return false; + + return true; +} + +bool dc_is_stream_unchanged( + struct dc_stream_state *old_stream, struct dc_stream_state *stream) +{ + + if (!are_stream_backends_same(old_stream, stream)) + return false; + + return true; +} + +bool dc_is_stream_scaling_unchanged( + struct dc_stream_state *old_stream, struct dc_stream_state *stream) +{ + if (old_stream == stream) + return true; + + if (old_stream == NULL || stream == NULL) + return false; + + if (memcmp(&old_stream->src, + &stream->src, + sizeof(struct rect)) != 0) + return false; + + if (memcmp(&old_stream->dst, + &stream->dst, + sizeof(struct rect)) != 0) + return false; + + return true; +} + +/* Maximum TMDS single link pixel clock 165MHz */ +#define TMDS_MAX_PIXEL_CLOCK_IN_KHZ 165000 + +static void update_stream_engine_usage( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct stream_encoder *stream_enc, + bool acquired) +{ + int i; + + for (i = 0; i < pool->stream_enc_count; i++) { + if (pool->stream_enc[i] == stream_enc) + res_ctx->is_stream_enc_acquired[i] = acquired; + } +} + +/* TODO: release audio object */ +void update_audio_usage( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct audio *audio, + bool acquired) +{ + int i; + for (i = 0; i < pool->audio_count; i++) { + if (pool->audios[i] == audio) + res_ctx->is_audio_acquired[i] = acquired; + } +} + +static int acquire_first_free_pipe( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + int i; + + for (i = 0; i < pool->pipe_count; i++) { + if (!res_ctx->pipe_ctx[i].stream) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + + pipe_ctx->stream_res.tg = pool->timing_generators[i]; + pipe_ctx->plane_res.mi = pool->mis[i]; + pipe_ctx->plane_res.hubp = pool->hubps[i]; + pipe_ctx->plane_res.ipp = pool->ipps[i]; + pipe_ctx->plane_res.xfm = pool->transforms[i]; + pipe_ctx->plane_res.dpp = pool->dpps[i]; + pipe_ctx->stream_res.opp = pool->opps[i]; + pipe_ctx->pipe_idx = i; + + + pipe_ctx->stream = stream; + return i; + } + } + return -1; +} + +static struct stream_encoder *find_first_free_match_stream_enc_for_link( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + int i; + int j = -1; + struct dc_link *link = stream->sink->link; + + for (i = 0; i < pool->stream_enc_count; i++) { + if (!res_ctx->is_stream_enc_acquired[i] && + pool->stream_enc[i]) { + /* Store first available for MST second display + * in daisy chain use case */ + j = i; + if (pool->stream_enc[i]->id == + link->link_enc->preferred_engine) + return pool->stream_enc[i]; + } + } + + /* + * below can happen in cases when stream encoder is acquired: + * 1) for second MST display in chain, so preferred engine already + * acquired; + * 2) for another link, which preferred engine already acquired by any + * MST configuration. + * + * If signal is of DP type and preferred engine not found, return last available + * + * TODO - This is just a patch up and a generic solution is + * required for non DP connectors. + */ + + if (j >= 0 && dc_is_dp_signal(stream->signal)) + return pool->stream_enc[j]; + + return NULL; +} + +static struct audio *find_first_free_audio( + struct resource_context *res_ctx, + const struct resource_pool *pool) +{ + int i; + for (i = 0; i < pool->audio_count; i++) { + if ((res_ctx->is_audio_acquired[i] == false) && (res_ctx->is_stream_enc_acquired[i] == true)) { + return pool->audios[i]; + } + } + /*not found the matching one, first come first serve*/ + for (i = 0; i < pool->audio_count; i++) { + if (res_ctx->is_audio_acquired[i] == false) { + return pool->audios[i]; + } + } + return 0; +} + +bool resource_is_stream_unchanged( + struct dc_state *old_context, struct dc_stream_state *stream) +{ + int i; + + for (i = 0; i < old_context->stream_count; i++) { + struct dc_stream_state *old_stream = old_context->streams[i]; + + if (are_stream_backends_same(old_stream, stream)) + return true; + } + + return false; +} + +enum dc_status dc_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *stream) +{ + struct dc_context *dc_ctx = dc->ctx; + enum dc_status res; + + if (new_ctx->stream_count >= dc->res_pool->pipe_count) { + DC_ERROR("Max streams reached, can add stream %p !\n", stream); + return DC_ERROR_UNEXPECTED; + } + + new_ctx->streams[new_ctx->stream_count] = stream; + dc_stream_retain(stream); + new_ctx->stream_count++; + + res = dc->res_pool->funcs->add_stream_to_ctx(dc, new_ctx, stream); + if (res != DC_OK) + DC_ERROR("Adding stream %p to context failed with err %d!\n", stream, res); + + return res; +} + +enum dc_status dc_remove_stream_from_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *stream) +{ + int i; + struct dc_context *dc_ctx = dc->ctx; + struct pipe_ctx *del_pipe = NULL; + + /* Release primary pipe */ + for (i = 0; i < MAX_PIPES; i++) { + if (new_ctx->res_ctx.pipe_ctx[i].stream == stream && + !new_ctx->res_ctx.pipe_ctx[i].top_pipe) { + del_pipe = &new_ctx->res_ctx.pipe_ctx[i]; + + ASSERT(del_pipe->stream_res.stream_enc); + update_stream_engine_usage( + &new_ctx->res_ctx, + dc->res_pool, + del_pipe->stream_res.stream_enc, + false); + + if (del_pipe->stream_res.audio) + update_audio_usage( + &new_ctx->res_ctx, + dc->res_pool, + del_pipe->stream_res.audio, + false); + + resource_unreference_clock_source(&new_ctx->res_ctx, + dc->res_pool, + del_pipe->clock_source); + + memset(del_pipe, 0, sizeof(*del_pipe)); + + break; + } + } + + if (!del_pipe) { + DC_ERROR("Pipe not found for stream %p !\n", stream); + return DC_ERROR_UNEXPECTED; + } + + for (i = 0; i < new_ctx->stream_count; i++) + if (new_ctx->streams[i] == stream) + break; + + if (new_ctx->streams[i] != stream) { + DC_ERROR("Context doesn't have stream %p !\n", stream); + return DC_ERROR_UNEXPECTED; + } + + dc_stream_release(new_ctx->streams[i]); + new_ctx->stream_count--; + + /* Trim back arrays */ + for (; i < new_ctx->stream_count; i++) { + new_ctx->streams[i] = new_ctx->streams[i + 1]; + new_ctx->stream_status[i] = new_ctx->stream_status[i + 1]; + } + + new_ctx->streams[new_ctx->stream_count] = NULL; + memset( + &new_ctx->stream_status[new_ctx->stream_count], + 0, + sizeof(new_ctx->stream_status[0])); + + return DC_OK; +} + +static void copy_pipe_ctx( + const struct pipe_ctx *from_pipe_ctx, struct pipe_ctx *to_pipe_ctx) +{ + struct dc_plane_state *plane_state = to_pipe_ctx->plane_state; + struct dc_stream_state *stream = to_pipe_ctx->stream; + + *to_pipe_ctx = *from_pipe_ctx; + to_pipe_ctx->stream = stream; + if (plane_state != NULL) + to_pipe_ctx->plane_state = plane_state; +} + +static struct dc_stream_state *find_pll_sharable_stream( + struct dc_stream_state *stream_needs_pll, + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + struct dc_stream_state *stream_has_pll = context->streams[i]; + + /* We are looking for non dp, non virtual stream */ + if (resource_are_streams_timing_synchronizable( + stream_needs_pll, stream_has_pll) + && !dc_is_dp_signal(stream_has_pll->signal) + && stream_has_pll->sink->link->connector_signal + != SIGNAL_TYPE_VIRTUAL) + return stream_has_pll; + + } + + return NULL; +} + +static int get_norm_pix_clk(const struct dc_crtc_timing *timing) +{ + uint32_t pix_clk = timing->pix_clk_khz; + uint32_t normalized_pix_clk = pix_clk; + + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) + pix_clk /= 2; + if (timing->pixel_encoding != PIXEL_ENCODING_YCBCR422) { + switch (timing->display_color_depth) { + case COLOR_DEPTH_888: + normalized_pix_clk = pix_clk; + break; + case COLOR_DEPTH_101010: + normalized_pix_clk = (pix_clk * 30) / 24; + break; + case COLOR_DEPTH_121212: + normalized_pix_clk = (pix_clk * 36) / 24; + break; + case COLOR_DEPTH_161616: + normalized_pix_clk = (pix_clk * 48) / 24; + break; + default: + ASSERT(0); + break; + } + } + return normalized_pix_clk; +} + +static void calculate_phy_pix_clks(struct dc_stream_state *stream) +{ + /* update actual pixel clock on all streams */ + if (dc_is_hdmi_signal(stream->signal)) + stream->phy_pix_clk = get_norm_pix_clk( + &stream->timing); + else + stream->phy_pix_clk = + stream->timing.pix_clk_khz; +} + +enum dc_status resource_map_pool_resources( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + const struct resource_pool *pool = dc->res_pool; + int i; + struct dc_context *dc_ctx = dc->ctx; + struct pipe_ctx *pipe_ctx = NULL; + int pipe_idx = -1; + + /* TODO Check if this is needed */ + /*if (!resource_is_stream_unchanged(old_context, stream)) { + if (stream != NULL && old_context->streams[i] != NULL) { + stream->bit_depth_params = + old_context->streams[i]->bit_depth_params; + stream->clamping = old_context->streams[i]->clamping; + continue; + } + } + */ + + /* acquire new resources */ + pipe_idx = acquire_first_free_pipe(&context->res_ctx, pool, stream); + +#ifdef CONFIG_DRM_AMD_DC_DCN1_0 + if (pipe_idx < 0) + pipe_idx = acquire_first_split_pipe(&context->res_ctx, pool, stream); +#endif + + if (pipe_idx < 0) + return DC_NO_CONTROLLER_RESOURCE; + + pipe_ctx = &context->res_ctx.pipe_ctx[pipe_idx]; + + pipe_ctx->stream_res.stream_enc = + find_first_free_match_stream_enc_for_link( + &context->res_ctx, pool, stream); + + if (!pipe_ctx->stream_res.stream_enc) + return DC_NO_STREAM_ENG_RESOURCE; + + update_stream_engine_usage( + &context->res_ctx, pool, + pipe_ctx->stream_res.stream_enc, + true); + + /* TODO: Add check if ASIC support and EDID audio */ + if (!stream->sink->converter_disable_audio && + dc_is_audio_capable_signal(pipe_ctx->stream->signal) && + stream->audio_info.mode_count) { + pipe_ctx->stream_res.audio = find_first_free_audio( + &context->res_ctx, pool); + + /* + * Audio assigned in order first come first get. + * There are asics which has number of audio + * resources less then number of pipes + */ + if (pipe_ctx->stream_res.audio) + update_audio_usage(&context->res_ctx, pool, + pipe_ctx->stream_res.audio, true); + } + + for (i = 0; i < context->stream_count; i++) + if (context->streams[i] == stream) { + context->stream_status[i].primary_otg_inst = pipe_ctx->stream_res.tg->inst; + context->stream_status[i].stream_enc_inst = pipe_ctx->stream_res.stream_enc->id; + return DC_OK; + } + + DC_ERROR("Stream %p not found in new ctx!\n", stream); + return DC_ERROR_UNEXPECTED; +} + +/* first stream in the context is used to populate the rest */ +void validate_guaranteed_copy_streams( + struct dc_state *context, + int max_streams) +{ + int i; + + for (i = 1; i < max_streams; i++) { + context->streams[i] = context->streams[0]; + + copy_pipe_ctx(&context->res_ctx.pipe_ctx[0], + &context->res_ctx.pipe_ctx[i]); + context->res_ctx.pipe_ctx[i].stream = + context->res_ctx.pipe_ctx[0].stream; + + dc_stream_retain(context->streams[i]); + context->stream_count++; + } +} + +void dc_resource_state_copy_construct_current( + const struct dc *dc, + struct dc_state *dst_ctx) +{ + dc_resource_state_copy_construct(dc->current_state, dst_ctx); +} + + +void dc_resource_state_construct( + const struct dc *dc, + struct dc_state *dst_ctx) +{ + dst_ctx->dis_clk = dc->res_pool->display_clock; +} + +enum dc_status dc_validate_global_state( + struct dc *dc, + struct dc_state *new_ctx) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + int i, j; + + if (dc->res_pool->funcs->validate_global) { + result = dc->res_pool->funcs->validate_global(dc, new_ctx); + if (result != DC_OK) + return result; + } + + for (i = 0; new_ctx && i < new_ctx->stream_count; i++) { + struct dc_stream_state *stream = new_ctx->streams[i]; + + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &new_ctx->res_ctx.pipe_ctx[j]; + + if (pipe_ctx->stream != stream) + continue; + + /* Switch to dp clock source only if there is + * no non dp stream that shares the same timing + * with the dp stream. + */ + if (dc_is_dp_signal(pipe_ctx->stream->signal) && + !find_pll_sharable_stream(stream, new_ctx)) { + + resource_unreference_clock_source( + &new_ctx->res_ctx, + dc->res_pool, + pipe_ctx->clock_source); + + pipe_ctx->clock_source = dc->res_pool->dp_clock_source; + resource_reference_clock_source( + &new_ctx->res_ctx, + dc->res_pool, + pipe_ctx->clock_source); + } + } + } + + result = resource_build_scaling_params_for_context(dc, new_ctx); + + if (result == DC_OK) + if (!dc->res_pool->funcs->validate_bandwidth(dc, new_ctx)) + result = DC_FAIL_BANDWIDTH_VALIDATE; + + return result; +} + +static void patch_gamut_packet_checksum( + struct encoder_info_packet *gamut_packet) +{ + /* For gamut we recalc checksum */ + if (gamut_packet->valid) { + uint8_t chk_sum = 0; + uint8_t *ptr; + uint8_t i; + + /*start of the Gamut data. */ + ptr = &gamut_packet->sb[3]; + + for (i = 0; i <= gamut_packet->sb[1]; i++) + chk_sum += ptr[i]; + + gamut_packet->sb[2] = (uint8_t) (0x100 - chk_sum); + } +} + +static void set_avi_info_frame( + struct encoder_info_packet *info_packet, + struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + struct info_frame info_frame = { {0} }; + uint32_t pixel_encoding = 0; + enum scanning_type scan_type = SCANNING_TYPE_NODATA; + enum dc_aspect_ratio aspect = ASPECT_RATIO_NO_DATA; + bool itc = false; + uint8_t itc_value = 0; + uint8_t cn0_cn1 = 0; + unsigned int cn0_cn1_value = 0; + uint8_t *check_sum = NULL; + uint8_t byte_index = 0; + union hdmi_info_packet *hdmi_info = &info_frame.avi_info_packet.info_packet_hdmi; + union display_content_support support = {0}; + unsigned int vic = pipe_ctx->stream->timing.vic; + enum dc_timing_3d_format format; + + color_space = pipe_ctx->stream->output_color_space; + if (color_space == COLOR_SPACE_UNKNOWN) + color_space = (stream->timing.pixel_encoding == PIXEL_ENCODING_RGB) ? + COLOR_SPACE_SRGB:COLOR_SPACE_YCBCR709; + + /* Initialize header */ + hdmi_info->bits.header.info_frame_type = HDMI_INFOFRAME_TYPE_AVI; + /* InfoFrameVersion_3 is defined by CEA861F (Section 6.4), but shall + * not be used in HDMI 2.0 (Section 10.1) */ + hdmi_info->bits.header.version = 2; + hdmi_info->bits.header.length = HDMI_AVI_INFOFRAME_SIZE; + + /* + * IDO-defined (Y2,Y1,Y0 = 1,1,1) shall not be used by devices built + * according to HDMI 2.0 spec (Section 10.1) + */ + + switch (stream->timing.pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + pixel_encoding = 1; + break; + + case PIXEL_ENCODING_YCBCR444: + pixel_encoding = 2; + break; + case PIXEL_ENCODING_YCBCR420: + pixel_encoding = 3; + break; + + case PIXEL_ENCODING_RGB: + default: + pixel_encoding = 0; + } + + /* Y0_Y1_Y2 : The pixel encoding */ + /* H14b AVI InfoFrame has extension on Y-field from 2 bits to 3 bits */ + hdmi_info->bits.Y0_Y1_Y2 = pixel_encoding; + + /* A0 = 1 Active Format Information valid */ + hdmi_info->bits.A0 = ACTIVE_FORMAT_VALID; + + /* B0, B1 = 3; Bar info data is valid */ + hdmi_info->bits.B0_B1 = BAR_INFO_BOTH_VALID; + + hdmi_info->bits.SC0_SC1 = PICTURE_SCALING_UNIFORM; + + /* S0, S1 : Underscan / Overscan */ + /* TODO: un-hardcode scan type */ + scan_type = SCANNING_TYPE_UNDERSCAN; + hdmi_info->bits.S0_S1 = scan_type; + + /* C0, C1 : Colorimetry */ + if (color_space == COLOR_SPACE_YCBCR709 || + color_space == COLOR_SPACE_YCBCR709_LIMITED) + hdmi_info->bits.C0_C1 = COLORIMETRY_ITU709; + else if (color_space == COLOR_SPACE_YCBCR601 || + color_space == COLOR_SPACE_YCBCR601_LIMITED) + hdmi_info->bits.C0_C1 = COLORIMETRY_ITU601; + else { + hdmi_info->bits.C0_C1 = COLORIMETRY_NO_DATA; + } + if (color_space == COLOR_SPACE_2020_RGB_FULLRANGE || + color_space == COLOR_SPACE_2020_RGB_LIMITEDRANGE || + color_space == COLOR_SPACE_2020_YCBCR) { + hdmi_info->bits.EC0_EC2 = COLORIMETRYEX_BT2020RGBYCBCR; + hdmi_info->bits.C0_C1 = COLORIMETRY_EXTENDED; + } else if (color_space == COLOR_SPACE_ADOBERGB) { + hdmi_info->bits.EC0_EC2 = COLORIMETRYEX_ADOBERGB; + hdmi_info->bits.C0_C1 = COLORIMETRY_EXTENDED; + } + + /* TODO: un-hardcode aspect ratio */ + aspect = stream->timing.aspect_ratio; + + switch (aspect) { + case ASPECT_RATIO_4_3: + case ASPECT_RATIO_16_9: + hdmi_info->bits.M0_M1 = aspect; + break; + + case ASPECT_RATIO_NO_DATA: + case ASPECT_RATIO_64_27: + case ASPECT_RATIO_256_135: + default: + hdmi_info->bits.M0_M1 = 0; + } + + /* Active Format Aspect ratio - same as Picture Aspect Ratio. */ + hdmi_info->bits.R0_R3 = ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE; + + /* TODO: un-hardcode cn0_cn1 and itc */ + + cn0_cn1 = 0; + cn0_cn1_value = 0; + + itc = true; + itc_value = 1; + + support = stream->sink->edid_caps.content_support; + + if (itc) { + if (!support.bits.valid_content_type) { + cn0_cn1_value = 0; + } else { + if (cn0_cn1 == DISPLAY_CONTENT_TYPE_GRAPHICS) { + if (support.bits.graphics_content == 1) { + cn0_cn1_value = 0; + } + } else if (cn0_cn1 == DISPLAY_CONTENT_TYPE_PHOTO) { + if (support.bits.photo_content == 1) { + cn0_cn1_value = 1; + } else { + cn0_cn1_value = 0; + itc_value = 0; + } + } else if (cn0_cn1 == DISPLAY_CONTENT_TYPE_CINEMA) { + if (support.bits.cinema_content == 1) { + cn0_cn1_value = 2; + } else { + cn0_cn1_value = 0; + itc_value = 0; + } + } else if (cn0_cn1 == DISPLAY_CONTENT_TYPE_GAME) { + if (support.bits.game_content == 1) { + cn0_cn1_value = 3; + } else { + cn0_cn1_value = 0; + itc_value = 0; + } + } + } + hdmi_info->bits.CN0_CN1 = cn0_cn1_value; + hdmi_info->bits.ITC = itc_value; + } + + /* TODO : We should handle YCC quantization */ + /* but we do not have matrix calculation */ + if (stream->sink->edid_caps.qs_bit == 1 && + stream->sink->edid_caps.qy_bit == 1) { + if (color_space == COLOR_SPACE_SRGB || + color_space == COLOR_SPACE_2020_RGB_FULLRANGE) { + hdmi_info->bits.Q0_Q1 = RGB_QUANTIZATION_FULL_RANGE; + hdmi_info->bits.YQ0_YQ1 = YYC_QUANTIZATION_FULL_RANGE; + } else if (color_space == COLOR_SPACE_SRGB_LIMITED || + color_space == COLOR_SPACE_2020_RGB_LIMITEDRANGE) { + hdmi_info->bits.Q0_Q1 = RGB_QUANTIZATION_LIMITED_RANGE; + hdmi_info->bits.YQ0_YQ1 = YYC_QUANTIZATION_LIMITED_RANGE; + } else { + hdmi_info->bits.Q0_Q1 = RGB_QUANTIZATION_DEFAULT_RANGE; + hdmi_info->bits.YQ0_YQ1 = YYC_QUANTIZATION_LIMITED_RANGE; + } + } else { + hdmi_info->bits.Q0_Q1 = RGB_QUANTIZATION_DEFAULT_RANGE; + hdmi_info->bits.YQ0_YQ1 = YYC_QUANTIZATION_LIMITED_RANGE; + } + + ///VIC + format = stream->timing.timing_3d_format; + /*todo, add 3DStereo support*/ + if (format != TIMING_3D_FORMAT_NONE) { + // Based on HDMI specs hdmi vic needs to be converted to cea vic when 3D is enabled + switch (pipe_ctx->stream->timing.hdmi_vic) { + case 1: + vic = 95; + break; + case 2: + vic = 94; + break; + case 3: + vic = 93; + break; + case 4: + vic = 98; + break; + default: + break; + } + } + hdmi_info->bits.VIC0_VIC7 = vic; + + /* pixel repetition + * PR0 - PR3 start from 0 whereas pHwPathMode->mode.timing.flags.pixel + * repetition start from 1 */ + hdmi_info->bits.PR0_PR3 = 0; + + /* Bar Info + * barTop: Line Number of End of Top Bar. + * barBottom: Line Number of Start of Bottom Bar. + * barLeft: Pixel Number of End of Left Bar. + * barRight: Pixel Number of Start of Right Bar. */ + hdmi_info->bits.bar_top = stream->timing.v_border_top; + hdmi_info->bits.bar_bottom = (stream->timing.v_total + - stream->timing.v_border_bottom + 1); + hdmi_info->bits.bar_left = stream->timing.h_border_left; + hdmi_info->bits.bar_right = (stream->timing.h_total + - stream->timing.h_border_right + 1); + + /* check_sum - Calculate AFMT_AVI_INFO0 ~ AFMT_AVI_INFO3 */ + check_sum = &info_frame.avi_info_packet.info_packet_hdmi.packet_raw_data.sb[0]; + + *check_sum = HDMI_INFOFRAME_TYPE_AVI + HDMI_AVI_INFOFRAME_SIZE + 2; + + for (byte_index = 1; byte_index <= HDMI_AVI_INFOFRAME_SIZE; byte_index++) + *check_sum += hdmi_info->packet_raw_data.sb[byte_index]; + + /* one byte complement */ + *check_sum = (uint8_t) (0x100 - *check_sum); + + /* Store in hw_path_mode */ + info_packet->hb0 = hdmi_info->packet_raw_data.hb0; + info_packet->hb1 = hdmi_info->packet_raw_data.hb1; + info_packet->hb2 = hdmi_info->packet_raw_data.hb2; + + for (byte_index = 0; byte_index < sizeof(info_frame.avi_info_packet. + info_packet_hdmi.packet_raw_data.sb); byte_index++) + info_packet->sb[byte_index] = info_frame.avi_info_packet. + info_packet_hdmi.packet_raw_data.sb[byte_index]; + + info_packet->valid = true; +} + +static void set_vendor_info_packet( + struct encoder_info_packet *info_packet, + struct dc_stream_state *stream) +{ + uint32_t length = 0; + bool hdmi_vic_mode = false; + uint8_t checksum = 0; + uint32_t i = 0; + enum dc_timing_3d_format format; + // Can be different depending on packet content /*todo*/ + // unsigned int length = pPathMode->dolbyVision ? 24 : 5; + + info_packet->valid = false; + + format = stream->timing.timing_3d_format; + if (stream->view_format == VIEW_3D_FORMAT_NONE) + format = TIMING_3D_FORMAT_NONE; + + /* Can be different depending on packet content */ + length = 5; + + if (stream->timing.hdmi_vic != 0 + && stream->timing.h_total >= 3840 + && stream->timing.v_total >= 2160) + hdmi_vic_mode = true; + + /* According to HDMI 1.4a CTS, VSIF should be sent + * for both 3D stereo and HDMI VIC modes. + * For all other modes, there is no VSIF sent. */ + + if (format == TIMING_3D_FORMAT_NONE && !hdmi_vic_mode) + return; + + /* 24bit IEEE Registration identifier (0x000c03). LSB first. */ + info_packet->sb[1] = 0x03; + info_packet->sb[2] = 0x0C; + info_packet->sb[3] = 0x00; + + /*PB4: 5 lower bytes = 0 (reserved). 3 higher bits = HDMI_Video_Format. + * The value for HDMI_Video_Format are: + * 0x0 (0b000) - No additional HDMI video format is presented in this + * packet + * 0x1 (0b001) - Extended resolution format present. 1 byte of HDMI_VIC + * parameter follows + * 0x2 (0b010) - 3D format indication present. 3D_Structure and + * potentially 3D_Ext_Data follows + * 0x3..0x7 (0b011..0b111) - reserved for future use */ + if (format != TIMING_3D_FORMAT_NONE) + info_packet->sb[4] = (2 << 5); + else if (hdmi_vic_mode) + info_packet->sb[4] = (1 << 5); + + /* PB5: If PB4 claims 3D timing (HDMI_Video_Format = 0x2): + * 4 lower bites = 0 (reserved). 4 higher bits = 3D_Structure. + * The value for 3D_Structure are: + * 0x0 - Frame Packing + * 0x1 - Field Alternative + * 0x2 - Line Alternative + * 0x3 - Side-by-Side (full) + * 0x4 - L + depth + * 0x5 - L + depth + graphics + graphics-depth + * 0x6 - Top-and-Bottom + * 0x7 - Reserved for future use + * 0x8 - Side-by-Side (Half) + * 0x9..0xE - Reserved for future use + * 0xF - Not used */ + switch (format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + info_packet->sb[5] = (0x0 << 4); + break; + + case TIMING_3D_FORMAT_SIDE_BY_SIDE: + case TIMING_3D_FORMAT_SBS_SW_PACKED: + info_packet->sb[5] = (0x8 << 4); + length = 6; + break; + + case TIMING_3D_FORMAT_TOP_AND_BOTTOM: + case TIMING_3D_FORMAT_TB_SW_PACKED: + info_packet->sb[5] = (0x6 << 4); + break; + + default: + break; + } + + /*PB5: If PB4 is set to 0x1 (extended resolution format) + * fill PB5 with the correct HDMI VIC code */ + if (hdmi_vic_mode) + info_packet->sb[5] = stream->timing.hdmi_vic; + + /* Header */ + info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR; /* VSIF packet type. */ + info_packet->hb1 = 0x01; /* Version */ + + /* 4 lower bits = Length, 4 higher bits = 0 (reserved) */ + info_packet->hb2 = (uint8_t) (length); + + /* Calculate checksum */ + checksum = 0; + checksum += info_packet->hb0; + checksum += info_packet->hb1; + checksum += info_packet->hb2; + + for (i = 1; i <= length; i++) + checksum += info_packet->sb[i]; + + info_packet->sb[0] = (uint8_t) (0x100 - checksum); + + info_packet->valid = true; +} + +static void set_spd_info_packet( + struct encoder_info_packet *info_packet, + struct dc_stream_state *stream) +{ + /* SPD info packet for FreeSync */ + + unsigned char checksum = 0; + unsigned int idx, payload_size = 0; + + /* Check if Freesync is supported. Return if false. If true, + * set the corresponding bit in the info packet + */ + if (stream->freesync_ctx.supported == false) + return; + + if (dc_is_hdmi_signal(stream->signal)) { + + /* HEADER */ + + /* HB0 = Packet Type = 0x83 (Source Product + * Descriptor InfoFrame) + */ + info_packet->hb0 = HDMI_INFOFRAME_TYPE_SPD; + + /* HB1 = Version = 0x01 */ + info_packet->hb1 = 0x01; + + /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x08] */ + info_packet->hb2 = 0x08; + + payload_size = 0x08; + + } else if (dc_is_dp_signal(stream->signal)) { + + /* HEADER */ + + /* HB0 = Secondary-data Packet ID = 0 - Only non-zero + * when used to associate audio related info packets + */ + info_packet->hb0 = 0x00; + + /* HB1 = Packet Type = 0x83 (Source Product + * Descriptor InfoFrame) + */ + info_packet->hb1 = HDMI_INFOFRAME_TYPE_SPD; + + /* HB2 = [Bits 7:0 = Least significant eight bits - + * For INFOFRAME, the value must be 1Bh] + */ + info_packet->hb2 = 0x1B; + + /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x1] + * [Bits 1:0 = Most significant two bits = 0x00] + */ + info_packet->hb3 = 0x04; + + payload_size = 0x1B; + } + + /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */ + info_packet->sb[1] = 0x1A; + + /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */ + info_packet->sb[2] = 0x00; + + /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */ + info_packet->sb[3] = 0x00; + + /* PB4 = Reserved */ + info_packet->sb[4] = 0x00; + + /* PB5 = Reserved */ + info_packet->sb[5] = 0x00; + + /* PB6 = [Bits 7:3 = Reserved] */ + info_packet->sb[6] = 0x00; + + if (stream->freesync_ctx.supported == true) + /* PB6 = [Bit 0 = FreeSync Supported] */ + info_packet->sb[6] |= 0x01; + + if (stream->freesync_ctx.enabled == true) + /* PB6 = [Bit 1 = FreeSync Enabled] */ + info_packet->sb[6] |= 0x02; + + if (stream->freesync_ctx.active == true) + /* PB6 = [Bit 2 = FreeSync Active] */ + info_packet->sb[6] |= 0x04; + + /* PB7 = FreeSync Minimum refresh rate (Hz) */ + info_packet->sb[7] = (unsigned char) (stream->freesync_ctx. + min_refresh_in_micro_hz / 1000000); + + /* PB8 = FreeSync Maximum refresh rate (Hz) + * + * Note: We do not use the maximum capable refresh rate + * of the panel, because we should never go above the field + * rate of the mode timing set. + */ + info_packet->sb[8] = (unsigned char) (stream->freesync_ctx. + nominal_refresh_in_micro_hz / 1000000); + + /* PB9 - PB27 = Reserved */ + for (idx = 9; idx <= 27; idx++) + info_packet->sb[idx] = 0x00; + + /* Calculate checksum */ + checksum += info_packet->hb0; + checksum += info_packet->hb1; + checksum += info_packet->hb2; + checksum += info_packet->hb3; + + for (idx = 1; idx <= payload_size; idx++) + checksum += info_packet->sb[idx]; + + /* PB0 = Checksum (one byte complement) */ + info_packet->sb[0] = (unsigned char) (0x100 - checksum); + + info_packet->valid = true; +} + +static void set_hdr_static_info_packet( + struct encoder_info_packet *info_packet, + struct dc_plane_state *plane_state, + struct dc_stream_state *stream) +{ + uint16_t i = 0; + enum signal_type signal = stream->signal; + struct dc_hdr_static_metadata hdr_metadata; + uint32_t data; + + if (!plane_state) + return; + + hdr_metadata = plane_state->hdr_static_ctx; + + if (!hdr_metadata.hdr_supported) + return; + + if (dc_is_hdmi_signal(signal)) { + info_packet->valid = true; + + info_packet->hb0 = 0x87; + info_packet->hb1 = 0x01; + info_packet->hb2 = 0x1A; + i = 1; + } else if (dc_is_dp_signal(signal)) { + info_packet->valid = true; + + info_packet->hb0 = 0x00; + info_packet->hb1 = 0x87; + info_packet->hb2 = 0x1D; + info_packet->hb3 = (0x13 << 2); + i = 2; + } + + data = hdr_metadata.is_hdr; + info_packet->sb[i++] = data ? 0x02 : 0x00; + info_packet->sb[i++] = 0x00; + + data = hdr_metadata.chromaticity_green_x / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.chromaticity_green_y / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.chromaticity_blue_x / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.chromaticity_blue_y / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.chromaticity_red_x / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.chromaticity_red_y / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.chromaticity_white_point_x / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.chromaticity_white_point_y / 2; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.max_luminance; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.min_luminance; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.maximum_content_light_level; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + data = hdr_metadata.maximum_frame_average_light_level; + info_packet->sb[i++] = data & 0xFF; + info_packet->sb[i++] = (data & 0xFF00) >> 8; + + if (dc_is_hdmi_signal(signal)) { + uint32_t checksum = 0; + + checksum += info_packet->hb0; + checksum += info_packet->hb1; + checksum += info_packet->hb2; + + for (i = 1; i <= info_packet->hb2; i++) + checksum += info_packet->sb[i]; + + info_packet->sb[0] = 0x100 - checksum; + } else if (dc_is_dp_signal(signal)) { + info_packet->sb[0] = 0x01; + info_packet->sb[1] = 0x1A; + } +} + +static void set_vsc_info_packet( + struct encoder_info_packet *info_packet, + struct dc_stream_state *stream) +{ + unsigned int vscPacketRevision = 0; + unsigned int i; + + if (stream->sink->link->psr_enabled) { + vscPacketRevision = 2; + } + + /* VSC packet not needed based on the features + * supported by this DP display + */ + if (vscPacketRevision == 0) + return; + + if (vscPacketRevision == 0x2) { + /* Secondary-data Packet ID = 0*/ + info_packet->hb0 = 0x00; + /* 07h - Packet Type Value indicating Video + * Stream Configuration packet + */ + info_packet->hb1 = 0x07; + /* 02h = VSC SDP supporting 3D stereo and PSR + * (applies to eDP v1.3 or higher). + */ + info_packet->hb2 = 0x02; + /* 08h = VSC packet supporting 3D stereo + PSR + * (HB2 = 02h). + */ + info_packet->hb3 = 0x08; + + for (i = 0; i < 28; i++) + info_packet->sb[i] = 0; + + info_packet->valid = true; + } + + /*TODO: stereo 3D support and extend pixel encoding colorimetry*/ +} + +void dc_resource_state_destruct(struct dc_state *context) +{ + int i, j; + + for (i = 0; i < context->stream_count; i++) { + for (j = 0; j < context->stream_status[i].plane_count; j++) + dc_plane_state_release( + context->stream_status[i].plane_states[j]); + + context->stream_status[i].plane_count = 0; + dc_stream_release(context->streams[i]); + context->streams[i] = NULL; + } +} + +/* + * Copy src_ctx into dst_ctx and retain all surfaces and streams referenced + * by the src_ctx + */ +void dc_resource_state_copy_construct( + const struct dc_state *src_ctx, + struct dc_state *dst_ctx) +{ + int i, j; + struct kref refcount = dst_ctx->refcount; + + *dst_ctx = *src_ctx; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *cur_pipe = &dst_ctx->res_ctx.pipe_ctx[i]; + + if (cur_pipe->top_pipe) + cur_pipe->top_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->top_pipe->pipe_idx]; + + if (cur_pipe->bottom_pipe) + cur_pipe->bottom_pipe = &dst_ctx->res_ctx.pipe_ctx[cur_pipe->bottom_pipe->pipe_idx]; + + } + + for (i = 0; i < dst_ctx->stream_count; i++) { + dc_stream_retain(dst_ctx->streams[i]); + for (j = 0; j < dst_ctx->stream_status[i].plane_count; j++) + dc_plane_state_retain( + dst_ctx->stream_status[i].plane_states[j]); + } + + /* context refcount should not be overridden */ + dst_ctx->refcount = refcount; + +} + +struct clock_source *dc_resource_find_first_free_pll( + struct resource_context *res_ctx, + const struct resource_pool *pool) +{ + int i; + + for (i = 0; i < pool->clk_src_count; ++i) { + if (res_ctx->clock_source_ref_count[i] == 0) + return pool->clock_sources[i]; + } + + return NULL; +} + +void resource_build_info_frame(struct pipe_ctx *pipe_ctx) +{ + enum signal_type signal = SIGNAL_TYPE_NONE; + struct encoder_info_frame *info = &pipe_ctx->stream_res.encoder_info_frame; + + /* default all packets to invalid */ + info->avi.valid = false; + info->gamut.valid = false; + info->vendor.valid = false; + info->spd.valid = false; + info->hdrsmd.valid = false; + info->vsc.valid = false; + + signal = pipe_ctx->stream->signal; + + /* HDMi and DP have different info packets*/ + if (dc_is_hdmi_signal(signal)) { + set_avi_info_frame(&info->avi, pipe_ctx); + + set_vendor_info_packet(&info->vendor, pipe_ctx->stream); + + set_spd_info_packet(&info->spd, pipe_ctx->stream); + + set_hdr_static_info_packet(&info->hdrsmd, + pipe_ctx->plane_state, pipe_ctx->stream); + + } else if (dc_is_dp_signal(signal)) { + set_vsc_info_packet(&info->vsc, pipe_ctx->stream); + + set_spd_info_packet(&info->spd, pipe_ctx->stream); + + set_hdr_static_info_packet(&info->hdrsmd, + pipe_ctx->plane_state, pipe_ctx->stream); + } + + patch_gamut_packet_checksum(&info->gamut); +} + +enum dc_status resource_map_clock_resources( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + /* acquire new resources */ + const struct resource_pool *pool = dc->res_pool; + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream( + &context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + if (dc_is_dp_signal(pipe_ctx->stream->signal) + || pipe_ctx->stream->signal == SIGNAL_TYPE_VIRTUAL) + pipe_ctx->clock_source = pool->dp_clock_source; + else { + pipe_ctx->clock_source = NULL; + + if (!dc->config.disable_disp_pll_sharing) + pipe_ctx->clock_source = resource_find_used_clk_src_for_sharing( + &context->res_ctx, + pipe_ctx); + + if (pipe_ctx->clock_source == NULL) + pipe_ctx->clock_source = + dc_resource_find_first_free_pll( + &context->res_ctx, + pool); + } + + if (pipe_ctx->clock_source == NULL) + return DC_NO_CLOCK_SOURCE_RESOURCE; + + resource_reference_clock_source( + &context->res_ctx, pool, + pipe_ctx->clock_source); + + return DC_OK; +} + +/* + * Note: We need to disable output if clock sources change, + * since bios does optimization and doesn't apply if changing + * PHY when not already disabled. + */ +bool pipe_need_reprogram( + struct pipe_ctx *pipe_ctx_old, + struct pipe_ctx *pipe_ctx) +{ + if (!pipe_ctx_old->stream) + return false; + + if (pipe_ctx_old->stream->sink != pipe_ctx->stream->sink) + return true; + + if (pipe_ctx_old->stream->signal != pipe_ctx->stream->signal) + return true; + + if (pipe_ctx_old->stream_res.audio != pipe_ctx->stream_res.audio) + return true; + + if (pipe_ctx_old->clock_source != pipe_ctx->clock_source + && pipe_ctx_old->stream != pipe_ctx->stream) + return true; + + if (pipe_ctx_old->stream_res.stream_enc != pipe_ctx->stream_res.stream_enc) + return true; + + if (is_timing_changed(pipe_ctx_old->stream, pipe_ctx->stream)) + return true; + + + return false; +} + +void resource_build_bit_depth_reduction_params(struct dc_stream_state *stream, + struct bit_depth_reduction_params *fmt_bit_depth) +{ + enum dc_dither_option option = stream->dither_option; + enum dc_pixel_encoding pixel_encoding = + stream->timing.pixel_encoding; + + memset(fmt_bit_depth, 0, sizeof(*fmt_bit_depth)); + + if (option == DITHER_OPTION_DEFAULT) { + switch (stream->timing.display_color_depth) { + case COLOR_DEPTH_666: + option = DITHER_OPTION_SPATIAL6; + break; + case COLOR_DEPTH_888: + option = DITHER_OPTION_SPATIAL8; + break; + case COLOR_DEPTH_101010: + option = DITHER_OPTION_SPATIAL10; + break; + default: + option = DITHER_OPTION_DISABLE; + } + } + + if (option == DITHER_OPTION_DISABLE) + return; + + if (option == DITHER_OPTION_TRUN6) { + fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; + fmt_bit_depth->flags.TRUNCATE_DEPTH = 0; + } else if (option == DITHER_OPTION_TRUN8 || + option == DITHER_OPTION_TRUN8_SPATIAL6 || + option == DITHER_OPTION_TRUN8_FM6) { + fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; + fmt_bit_depth->flags.TRUNCATE_DEPTH = 1; + } else if (option == DITHER_OPTION_TRUN10 || + option == DITHER_OPTION_TRUN10_SPATIAL6 || + option == DITHER_OPTION_TRUN10_SPATIAL8 || + option == DITHER_OPTION_TRUN10_FM8 || + option == DITHER_OPTION_TRUN10_FM6 || + option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) { + fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; + fmt_bit_depth->flags.TRUNCATE_DEPTH = 2; + } + + /* special case - Formatter can only reduce by 4 bits at most. + * When reducing from 12 to 6 bits, + * HW recommends we use trunc with round mode + * (if we did nothing, trunc to 10 bits would be used) + * note that any 12->10 bit reduction is ignored prior to DCE8, + * as the input was 10 bits. + */ + if (option == DITHER_OPTION_SPATIAL6_FRAME_RANDOM || + option == DITHER_OPTION_SPATIAL6 || + option == DITHER_OPTION_FM6) { + fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; + fmt_bit_depth->flags.TRUNCATE_DEPTH = 2; + fmt_bit_depth->flags.TRUNCATE_MODE = 1; + } + + /* spatial dither + * note that spatial modes 1-3 are never used + */ + if (option == DITHER_OPTION_SPATIAL6_FRAME_RANDOM || + option == DITHER_OPTION_SPATIAL6 || + option == DITHER_OPTION_TRUN10_SPATIAL6 || + option == DITHER_OPTION_TRUN8_SPATIAL6) { + fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; + fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 0; + fmt_bit_depth->flags.HIGHPASS_RANDOM = 1; + fmt_bit_depth->flags.RGB_RANDOM = + (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; + } else if (option == DITHER_OPTION_SPATIAL8_FRAME_RANDOM || + option == DITHER_OPTION_SPATIAL8 || + option == DITHER_OPTION_SPATIAL8_FM6 || + option == DITHER_OPTION_TRUN10_SPATIAL8 || + option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) { + fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; + fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 1; + fmt_bit_depth->flags.HIGHPASS_RANDOM = 1; + fmt_bit_depth->flags.RGB_RANDOM = + (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; + } else if (option == DITHER_OPTION_SPATIAL10_FRAME_RANDOM || + option == DITHER_OPTION_SPATIAL10 || + option == DITHER_OPTION_SPATIAL10_FM8 || + option == DITHER_OPTION_SPATIAL10_FM6) { + fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; + fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 2; + fmt_bit_depth->flags.HIGHPASS_RANDOM = 1; + fmt_bit_depth->flags.RGB_RANDOM = + (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; + } + + if (option == DITHER_OPTION_SPATIAL6 || + option == DITHER_OPTION_SPATIAL8 || + option == DITHER_OPTION_SPATIAL10) { + fmt_bit_depth->flags.FRAME_RANDOM = 0; + } else { + fmt_bit_depth->flags.FRAME_RANDOM = 1; + } + + ////////////////////// + //// temporal dither + ////////////////////// + if (option == DITHER_OPTION_FM6 || + option == DITHER_OPTION_SPATIAL8_FM6 || + option == DITHER_OPTION_SPATIAL10_FM6 || + option == DITHER_OPTION_TRUN10_FM6 || + option == DITHER_OPTION_TRUN8_FM6 || + option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) { + fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1; + fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 0; + } else if (option == DITHER_OPTION_FM8 || + option == DITHER_OPTION_SPATIAL10_FM8 || + option == DITHER_OPTION_TRUN10_FM8) { + fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1; + fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 1; + } else if (option == DITHER_OPTION_FM10) { + fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1; + fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 2; + } + + fmt_bit_depth->pixel_encoding = pixel_encoding; +} + +enum dc_status dc_validate_stream(struct dc *dc, struct dc_stream_state *stream) +{ + struct dc *core_dc = dc; + struct dc_link *link = stream->sink->link; + struct timing_generator *tg = core_dc->res_pool->timing_generators[0]; + enum dc_status res = DC_OK; + + calculate_phy_pix_clks(stream); + + if (!tg->funcs->validate_timing(tg, &stream->timing)) + res = DC_FAIL_CONTROLLER_VALIDATE; + + if (res == DC_OK) + if (!link->link_enc->funcs->validate_output_with_stream( + link->link_enc, stream)) + res = DC_FAIL_ENC_VALIDATE; + + /* TODO: validate audio ASIC caps, encoder */ + + if (res == DC_OK) + res = dc_link_validate_mode_timing(stream, + link, + &stream->timing); + + return res; +} + +enum dc_status dc_validate_plane(struct dc *dc, const struct dc_plane_state *plane_state) +{ + enum dc_status res = DC_OK; + + /* TODO For now validates pixel format only */ + if (dc->res_pool->funcs->validate_plane) + return dc->res_pool->funcs->validate_plane(plane_state, &dc->caps); + + return res; +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_sink.c b/drivers/gpu/drm/amd/display/dc/core/dc_sink.c new file mode 100644 index 000000000000..25fae38409ab --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_sink.c @@ -0,0 +1,104 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dm_helpers.h" +#include "core_types.h" + +/******************************************************************************* + * Private functions + ******************************************************************************/ + +static void destruct(struct dc_sink *sink) +{ + if (sink->dc_container_id) { + kfree(sink->dc_container_id); + sink->dc_container_id = NULL; + } +} + +static bool construct(struct dc_sink *sink, const struct dc_sink_init_data *init_params) +{ + + struct dc_link *link = init_params->link; + + if (!link) + return false; + + sink->sink_signal = init_params->sink_signal; + sink->link = link; + sink->ctx = link->ctx; + sink->dongle_max_pix_clk = init_params->dongle_max_pix_clk; + sink->converter_disable_audio = init_params->converter_disable_audio; + sink->dc_container_id = NULL; + + return true; +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ + +void dc_sink_retain(struct dc_sink *sink) +{ + kref_get(&sink->refcount); +} + +static void dc_sink_free(struct kref *kref) +{ + struct dc_sink *sink = container_of(kref, struct dc_sink, refcount); + destruct(sink); + kfree(sink); +} + +void dc_sink_release(struct dc_sink *sink) +{ + kref_put(&sink->refcount, dc_sink_free); +} + +struct dc_sink *dc_sink_create(const struct dc_sink_init_data *init_params) +{ + struct dc_sink *sink = kzalloc(sizeof(*sink), GFP_KERNEL); + + if (NULL == sink) + goto alloc_fail; + + if (false == construct(sink, init_params)) + goto construct_fail; + + kref_init(&sink->refcount); + + return sink; + +construct_fail: + kfree(sink); + +alloc_fail: + return NULL; +} + +/******************************************************************************* + * Protected functions - visible only inside of DC (not visible in DM) + ******************************************************************************/ diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c new file mode 100644 index 000000000000..b00a6040a697 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -0,0 +1,398 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dc.h" +#include "core_types.h" +#include "resource.h" +#include "ipp.h" +#include "timing_generator.h" + +/******************************************************************************* + * Private functions + ******************************************************************************/ +#define TMDS_MAX_PIXEL_CLOCK_IN_KHZ_UPMOST 297000 +static void update_stream_signal(struct dc_stream_state *stream) +{ + if (stream->output_signal == SIGNAL_TYPE_NONE) { + struct dc_sink *dc_sink = stream->sink; + + if (dc_sink->sink_signal == SIGNAL_TYPE_NONE) + stream->signal = stream->sink->link->connector_signal; + else + stream->signal = dc_sink->sink_signal; + } else { + stream->signal = stream->output_signal; + } + + if (dc_is_dvi_signal(stream->signal)) { + if (stream->timing.pix_clk_khz > TMDS_MAX_PIXEL_CLOCK_IN_KHZ_UPMOST && + stream->sink->sink_signal != SIGNAL_TYPE_DVI_SINGLE_LINK) + stream->signal = SIGNAL_TYPE_DVI_DUAL_LINK; + else + stream->signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + } +} + +static void construct(struct dc_stream_state *stream, + struct dc_sink *dc_sink_data) +{ + uint32_t i = 0; + + stream->sink = dc_sink_data; + stream->ctx = stream->sink->ctx; + + dc_sink_retain(dc_sink_data); + + /* Copy audio modes */ + /* TODO - Remove this translation */ + for (i = 0; i < (dc_sink_data->edid_caps.audio_mode_count); i++) + { + stream->audio_info.modes[i].channel_count = dc_sink_data->edid_caps.audio_modes[i].channel_count; + stream->audio_info.modes[i].format_code = dc_sink_data->edid_caps.audio_modes[i].format_code; + stream->audio_info.modes[i].sample_rates.all = dc_sink_data->edid_caps.audio_modes[i].sample_rate; + stream->audio_info.modes[i].sample_size = dc_sink_data->edid_caps.audio_modes[i].sample_size; + } + stream->audio_info.mode_count = dc_sink_data->edid_caps.audio_mode_count; + stream->audio_info.audio_latency = dc_sink_data->edid_caps.audio_latency; + stream->audio_info.video_latency = dc_sink_data->edid_caps.video_latency; + memmove( + stream->audio_info.display_name, + dc_sink_data->edid_caps.display_name, + AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); + stream->audio_info.manufacture_id = dc_sink_data->edid_caps.manufacturer_id; + stream->audio_info.product_id = dc_sink_data->edid_caps.product_id; + stream->audio_info.flags.all = dc_sink_data->edid_caps.speaker_flags; + + if (dc_sink_data->dc_container_id != NULL) { + struct dc_container_id *dc_container_id = dc_sink_data->dc_container_id; + + stream->audio_info.port_id[0] = dc_container_id->portId[0]; + stream->audio_info.port_id[1] = dc_container_id->portId[1]; + } else { + /* TODO - WindowDM has implemented, + other DMs need Unhardcode port_id */ + stream->audio_info.port_id[0] = 0x5558859e; + stream->audio_info.port_id[1] = 0xd989449; + } + + /* EDID CAP translation for HDMI 2.0 */ + stream->timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble; + + stream->status.link = stream->sink->link; + + update_stream_signal(stream); +} + +static void destruct(struct dc_stream_state *stream) +{ + dc_sink_release(stream->sink); + if (stream->out_transfer_func != NULL) { + dc_transfer_func_release( + stream->out_transfer_func); + stream->out_transfer_func = NULL; + } +} + +void dc_stream_retain(struct dc_stream_state *stream) +{ + kref_get(&stream->refcount); +} + +static void dc_stream_free(struct kref *kref) +{ + struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount); + + destruct(stream); + kfree(stream); +} + +void dc_stream_release(struct dc_stream_state *stream) +{ + if (stream != NULL) { + kref_put(&stream->refcount, dc_stream_free); + } +} + +struct dc_stream_state *dc_create_stream_for_sink( + struct dc_sink *sink) +{ + struct dc_stream_state *stream; + + if (sink == NULL) + return NULL; + + stream = kzalloc(sizeof(struct dc_stream_state), GFP_KERNEL); + if (stream == NULL) + return NULL; + + construct(stream, sink); + + kref_init(&stream->refcount); + + return stream; +} + +struct dc_stream_status *dc_stream_get_status( + struct dc_stream_state *stream) +{ + uint8_t i; + struct dc *dc = stream->ctx->dc; + + for (i = 0; i < dc->current_state->stream_count; i++) { + if (stream == dc->current_state->streams[i]) + return &dc->current_state->stream_status[i]; + } + + return NULL; +} + +/** + * Update the cursor attributes and set cursor surface address + */ +bool dc_stream_set_cursor_attributes( + struct dc_stream_state *stream, + const struct dc_cursor_attributes *attributes) +{ + int i; + struct dc *core_dc; + struct resource_context *res_ctx; + + if (NULL == stream) { + dm_error("DC: dc_stream is NULL!\n"); + return false; + } + if (NULL == attributes) { + dm_error("DC: attributes is NULL!\n"); + return false; + } + + if (attributes->address.quad_part == 0) { + dm_output_to_console("DC: Cursor address is 0!\n"); + return false; + } + + core_dc = stream->ctx->dc; + res_ctx = &core_dc->current_state->res_ctx; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + + if (pipe_ctx->stream != stream || (!pipe_ctx->plane_res.xfm && !pipe_ctx->plane_res.dpp)) + continue; + if (pipe_ctx->top_pipe && pipe_ctx->plane_state != pipe_ctx->top_pipe->plane_state) + continue; + + + if (pipe_ctx->plane_res.ipp->funcs->ipp_cursor_set_attributes != NULL) + pipe_ctx->plane_res.ipp->funcs->ipp_cursor_set_attributes( + pipe_ctx->plane_res.ipp, attributes); + + if (pipe_ctx->plane_res.hubp != NULL && + pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes != NULL) + pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes( + pipe_ctx->plane_res.hubp, attributes); + + if (pipe_ctx->plane_res.mi != NULL && + pipe_ctx->plane_res.mi->funcs->set_cursor_attributes != NULL) + pipe_ctx->plane_res.mi->funcs->set_cursor_attributes( + pipe_ctx->plane_res.mi, attributes); + + + if (pipe_ctx->plane_res.xfm != NULL && + pipe_ctx->plane_res.xfm->funcs->set_cursor_attributes != NULL) + pipe_ctx->plane_res.xfm->funcs->set_cursor_attributes( + pipe_ctx->plane_res.xfm, attributes); + + if (pipe_ctx->plane_res.dpp != NULL && + pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes != NULL) + pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes( + pipe_ctx->plane_res.dpp, attributes); + } + + stream->cursor_attributes = *attributes; + + return true; +} + +bool dc_stream_set_cursor_position( + struct dc_stream_state *stream, + const struct dc_cursor_position *position) +{ + int i; + struct dc *core_dc; + struct resource_context *res_ctx; + + if (NULL == stream) { + dm_error("DC: dc_stream is NULL!\n"); + return false; + } + + if (NULL == position) { + dm_error("DC: cursor position is NULL!\n"); + return false; + } + + core_dc = stream->ctx->dc; + res_ctx = &core_dc->current_state->res_ctx; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + struct input_pixel_processor *ipp = pipe_ctx->plane_res.ipp; + struct mem_input *mi = pipe_ctx->plane_res.mi; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + struct transform *xfm = pipe_ctx->plane_res.xfm; + struct dpp *dpp = pipe_ctx->plane_res.dpp; + struct dc_cursor_position pos_cpy = *position; + struct dc_cursor_mi_param param = { + .pixel_clk_khz = stream->timing.pix_clk_khz, + .ref_clk_khz = core_dc->res_pool->ref_clock_inKhz, + .viewport_x_start = pipe_ctx->plane_res.scl_data.viewport.x, + .viewport_width = pipe_ctx->plane_res.scl_data.viewport.width, + .h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz + }; + + if (pipe_ctx->stream != stream || + (!pipe_ctx->plane_res.mi && !pipe_ctx->plane_res.hubp) || + !pipe_ctx->plane_state || + (!pipe_ctx->plane_res.xfm && !pipe_ctx->plane_res.dpp)) + continue; + + if (pipe_ctx->plane_state->address.type + == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE) + pos_cpy.enable = false; + + if (pipe_ctx->top_pipe && pipe_ctx->plane_state != pipe_ctx->top_pipe->plane_state) + pos_cpy.enable = false; + + + if (ipp != NULL && ipp->funcs->ipp_cursor_set_position != NULL) + ipp->funcs->ipp_cursor_set_position(ipp, &pos_cpy, ¶m); + + if (mi != NULL && mi->funcs->set_cursor_position != NULL) + mi->funcs->set_cursor_position(mi, &pos_cpy, ¶m); + + if (hubp != NULL && hubp->funcs->set_cursor_position != NULL) + hubp->funcs->set_cursor_position(hubp, &pos_cpy, ¶m); + + if (xfm != NULL && xfm->funcs->set_cursor_position != NULL) + xfm->funcs->set_cursor_position(xfm, &pos_cpy, ¶m, hubp->curs_attr.width); + + if (dpp != NULL && dpp->funcs->set_cursor_position != NULL) + dpp->funcs->set_cursor_position(dpp, &pos_cpy, ¶m, hubp->curs_attr.width); + + } + + return true; +} + +uint32_t dc_stream_get_vblank_counter(const struct dc_stream_state *stream) +{ + uint8_t i; + struct dc *core_dc = stream->ctx->dc; + struct resource_context *res_ctx = + &core_dc->current_state->res_ctx; + + for (i = 0; i < MAX_PIPES; i++) { + struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg; + + if (res_ctx->pipe_ctx[i].stream != stream) + continue; + + return tg->funcs->get_frame_count(tg); + } + + return 0; +} + +bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position) +{ + uint8_t i; + bool ret = false; + struct dc *core_dc = stream->ctx->dc; + struct resource_context *res_ctx = + &core_dc->current_state->res_ctx; + + for (i = 0; i < MAX_PIPES; i++) { + struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg; + + if (res_ctx->pipe_ctx[i].stream != stream) + continue; + + tg->funcs->get_scanoutpos(tg, + v_blank_start, + v_blank_end, + h_position, + v_position); + + ret = true; + break; + } + + return ret; +} + + +void dc_stream_log( + const struct dc_stream_state *stream, + struct dal_logger *dm_logger, + enum dc_log_type log_type) +{ + + dm_logger_write(dm_logger, + log_type, + "core_stream 0x%x: src: %d, %d, %d, %d; dst: %d, %d, %d, %d, colorSpace:%d\n", + stream, + stream->src.x, + stream->src.y, + stream->src.width, + stream->src.height, + stream->dst.x, + stream->dst.y, + stream->dst.width, + stream->dst.height, + stream->output_color_space); + dm_logger_write(dm_logger, + log_type, + "\tpix_clk_khz: %d, h_total: %d, v_total: %d, pixelencoder:%d, displaycolorDepth:%d\n", + stream->timing.pix_clk_khz, + stream->timing.h_total, + stream->timing.v_total, + stream->timing.pixel_encoding, + stream->timing.display_color_depth); + dm_logger_write(dm_logger, + log_type, + "\tsink name: %s, serial: %d\n", + stream->sink->edid_caps.display_name, + stream->sink->edid_caps.serial_number); + dm_logger_write(dm_logger, + log_type, + "\tlink: %d\n", + stream->sink->link->link_index); +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c new file mode 100644 index 000000000000..ade5b8ee9c3c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c @@ -0,0 +1,193 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* DC interface (public) */ +#include "dm_services.h" +#include "dc.h" + +/* DC core (private) */ +#include "core_types.h" +#include "transform.h" +#include "dpp.h" + +/******************************************************************************* + * Private functions + ******************************************************************************/ +static void construct(struct dc_context *ctx, struct dc_plane_state *plane_state) +{ + plane_state->ctx = ctx; +} + +static void destruct(struct dc_plane_state *plane_state) +{ + if (plane_state->gamma_correction != NULL) { + dc_gamma_release(&plane_state->gamma_correction); + } + if (plane_state->in_transfer_func != NULL) { + dc_transfer_func_release( + plane_state->in_transfer_func); + plane_state->in_transfer_func = NULL; + } +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ +void enable_surface_flip_reporting(struct dc_plane_state *plane_state, + uint32_t controller_id) +{ + plane_state->irq_source = controller_id + DC_IRQ_SOURCE_PFLIP1 - 1; + /*register_flip_interrupt(surface);*/ +} + +struct dc_plane_state *dc_create_plane_state(struct dc *dc) +{ + struct dc *core_dc = dc; + + struct dc_plane_state *plane_state = kzalloc(sizeof(*plane_state), + GFP_KERNEL); + + if (NULL == plane_state) + return NULL; + + kref_init(&plane_state->refcount); + construct(core_dc->ctx, plane_state); + + return plane_state; +} + +const struct dc_plane_status *dc_plane_get_status( + const struct dc_plane_state *plane_state) +{ + const struct dc_plane_status *plane_status; + struct dc *core_dc; + int i; + + if (!plane_state || + !plane_state->ctx || + !plane_state->ctx->dc) { + ASSERT(0); + return NULL; /* remove this if above assert never hit */ + } + + plane_status = &plane_state->status; + core_dc = plane_state->ctx->dc; + + if (core_dc->current_state == NULL) + return NULL; + + for (i = 0; i < core_dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = + &core_dc->current_state->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->plane_state != plane_state) + continue; + + core_dc->hwss.update_pending_status(pipe_ctx); + } + + return plane_status; +} + +void dc_plane_state_retain(struct dc_plane_state *plane_state) +{ + kref_get(&plane_state->refcount); +} + +static void dc_plane_state_free(struct kref *kref) +{ + struct dc_plane_state *plane_state = container_of(kref, struct dc_plane_state, refcount); + destruct(plane_state); + kfree(plane_state); +} + +void dc_plane_state_release(struct dc_plane_state *plane_state) +{ + kref_put(&plane_state->refcount, dc_plane_state_free); +} + +void dc_gamma_retain(struct dc_gamma *gamma) +{ + kref_get(&gamma->refcount); +} + +static void dc_gamma_free(struct kref *kref) +{ + struct dc_gamma *gamma = container_of(kref, struct dc_gamma, refcount); + kfree(gamma); +} + +void dc_gamma_release(struct dc_gamma **gamma) +{ + kref_put(&(*gamma)->refcount, dc_gamma_free); + *gamma = NULL; +} + +struct dc_gamma *dc_create_gamma(void) +{ + struct dc_gamma *gamma = kzalloc(sizeof(*gamma), GFP_KERNEL); + + if (gamma == NULL) + goto alloc_fail; + + kref_init(&gamma->refcount); + return gamma; + +alloc_fail: + return NULL; +} + +void dc_transfer_func_retain(struct dc_transfer_func *tf) +{ + kref_get(&tf->refcount); +} + +static void dc_transfer_func_free(struct kref *kref) +{ + struct dc_transfer_func *tf = container_of(kref, struct dc_transfer_func, refcount); + kfree(tf); +} + +void dc_transfer_func_release(struct dc_transfer_func *tf) +{ + kref_put(&tf->refcount, dc_transfer_func_free); +} + +struct dc_transfer_func *dc_create_transfer_func(void) +{ + struct dc_transfer_func *tf = kzalloc(sizeof(*tf), GFP_KERNEL); + + if (tf == NULL) + goto alloc_fail; + + kref_init(&tf->refcount); + + return tf; + +alloc_fail: + return NULL; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h new file mode 100644 index 000000000000..9d8f4a55c74e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -0,0 +1,1103 @@ +/* + * Copyright 2012-14 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DC_INTERFACE_H_ +#define DC_INTERFACE_H_ + +#include "dc_types.h" +#include "grph_object_defs.h" +#include "logger_types.h" +#include "gpio_types.h" +#include "link_service_types.h" +#include "grph_object_ctrl_defs.h" +#include + +#include "inc/hw_sequencer.h" +#include "inc/compressor.h" +#include "dml/display_mode_lib.h" + +#define DC_VER "3.1.07" + +#define MAX_SURFACES 3 +#define MAX_STREAMS 6 +#define MAX_SINKS_PER_LINK 4 + + +/******************************************************************************* + * Display Core Interfaces + ******************************************************************************/ +struct dc_caps { + uint32_t max_streams; + uint32_t max_links; + uint32_t max_audios; + uint32_t max_slave_planes; + uint32_t max_planes; + uint32_t max_downscale_ratio; + uint32_t i2c_speed_in_khz; + unsigned int max_cursor_size; + unsigned int max_video_width; + bool dcc_const_color; + bool dynamic_audio; +}; + +struct dc_dcc_surface_param { + struct dc_size surface_size; + enum surface_pixel_format format; + enum swizzle_mode_values swizzle_mode; + enum dc_scan_direction scan; +}; + +struct dc_dcc_setting { + unsigned int max_compressed_blk_size; + unsigned int max_uncompressed_blk_size; + bool independent_64b_blks; +}; + +struct dc_surface_dcc_cap { + union { + struct { + struct dc_dcc_setting rgb; + } grph; + + struct { + struct dc_dcc_setting luma; + struct dc_dcc_setting chroma; + } video; + }; + + bool capable; + bool const_color_support; +}; + +struct dc_static_screen_events { + bool cursor_update; + bool surface_update; + bool overlay_update; +}; + +/* Forward declaration*/ +struct dc; +struct dc_plane_state; +struct dc_state; + +struct dc_cap_funcs { + bool (*get_dcc_compression_cap)(const struct dc *dc, + const struct dc_dcc_surface_param *input, + struct dc_surface_dcc_cap *output); +}; + +struct dc_stream_state_funcs { + bool (*adjust_vmin_vmax)(struct dc *dc, + struct dc_stream_state **stream, + int num_streams, + int vmin, + int vmax); + bool (*get_crtc_position)(struct dc *dc, + struct dc_stream_state **stream, + int num_streams, + unsigned int *v_pos, + unsigned int *nom_v_pos); + + bool (*set_gamut_remap)(struct dc *dc, + const struct dc_stream_state *stream); + + bool (*program_csc_matrix)(struct dc *dc, + struct dc_stream_state *stream); + + void (*set_static_screen_events)(struct dc *dc, + struct dc_stream_state **stream, + int num_streams, + const struct dc_static_screen_events *events); + + void (*set_dither_option)(struct dc_stream_state *stream, + enum dc_dither_option option); + + void (*set_dpms)(struct dc *dc, + struct dc_stream_state *stream, + bool dpms_off); +}; + +struct link_training_settings; + +struct dc_link_funcs { + void (*set_drive_settings)(struct dc *dc, + struct link_training_settings *lt_settings, + const struct dc_link *link); + void (*perform_link_training)(struct dc *dc, + struct dc_link_settings *link_setting, + bool skip_video_pattern); + void (*set_preferred_link_settings)(struct dc *dc, + struct dc_link_settings *link_setting, + struct dc_link *link); + void (*enable_hpd)(const struct dc_link *link); + void (*disable_hpd)(const struct dc_link *link); + void (*set_test_pattern)( + struct dc_link *link, + enum dp_test_pattern test_pattern, + const struct link_training_settings *p_link_settings, + const unsigned char *p_custom_pattern, + unsigned int cust_pattern_size); +}; + +/* Structure to hold configuration flags set by dm at dc creation. */ +struct dc_config { + bool gpu_vm_support; + bool disable_disp_pll_sharing; +}; + +enum dcc_option { + DCC_ENABLE = 0, + DCC_DISABLE = 1, + DCC_HALF_REQ_DISALBE = 2, +}; + +enum pipe_split_policy { + MPC_SPLIT_DYNAMIC = 0, + MPC_SPLIT_AVOID = 1, + MPC_SPLIT_AVOID_MULT_DISP = 2, +}; + +enum wm_report_mode { + WM_REPORT_DEFAULT = 0, + WM_REPORT_OVERRIDE = 1, +}; + +struct dc_debug { + bool surface_visual_confirm; + bool sanity_checks; + bool max_disp_clk; + bool surface_trace; + bool timing_trace; + bool clock_trace; + bool validation_trace; + + /* stutter efficiency related */ + bool disable_stutter; + bool use_max_lb; + enum dcc_option disable_dcc; + enum pipe_split_policy pipe_split_policy; + bool force_single_disp_pipe_split; + bool voltage_align_fclk; + + bool disable_dfs_bypass; + bool disable_dpp_power_gate; + bool disable_hubp_power_gate; + bool disable_pplib_wm_range; + enum wm_report_mode pplib_wm_report_mode; + unsigned int min_disp_clk_khz; + int sr_exit_time_dpm0_ns; + int sr_enter_plus_exit_time_dpm0_ns; + int sr_exit_time_ns; + int sr_enter_plus_exit_time_ns; + int urgent_latency_ns; + int percent_of_ideal_drambw; + int dram_clock_change_latency_ns; + int always_scale; + bool disable_pplib_clock_request; + bool disable_clock_gate; + bool disable_dmcu; + bool disable_psr; + bool force_abm_enable; + bool disable_hbup_pg; + bool disable_dpp_pg; + bool disable_stereo_support; + bool vsr_support; + bool performance_trace; +}; +struct dc_state; +struct resource_pool; +struct dce_hwseq; +struct dc { + struct dc_caps caps; + struct dc_cap_funcs cap_funcs; + struct dc_stream_state_funcs stream_funcs; + struct dc_link_funcs link_funcs; + struct dc_config config; + struct dc_debug debug; + + struct dc_context *ctx; + + uint8_t link_count; + struct dc_link *links[MAX_PIPES * 2]; + + struct dc_state *current_state; + struct resource_pool *res_pool; + + /* Display Engine Clock levels */ + struct dm_pp_clock_levels sclk_lvls; + + /* Inputs into BW and WM calculations. */ + struct bw_calcs_dceip *bw_dceip; + struct bw_calcs_vbios *bw_vbios; +#ifdef CONFIG_DRM_AMD_DC_DCN1_0 + struct dcn_soc_bounding_box *dcn_soc; + struct dcn_ip_params *dcn_ip; + struct display_mode_lib dml; +#endif + + /* HW functions */ + struct hw_sequencer_funcs hwss; + struct dce_hwseq *hwseq; + + /* temp store of dm_pp_display_configuration + * to compare to see if display config changed + */ + struct dm_pp_display_configuration prev_display_config; + + /* FBC compressor */ +#if defined(CONFIG_DRM_AMD_DC_FBC) + struct compressor *fbc_compressor; +#endif +}; + +enum frame_buffer_mode { + FRAME_BUFFER_MODE_LOCAL_ONLY = 0, + FRAME_BUFFER_MODE_ZFB_ONLY, + FRAME_BUFFER_MODE_MIXED_ZFB_AND_LOCAL, +} ; + +struct dchub_init_data { + int64_t zfb_phys_addr_base; + int64_t zfb_mc_base_addr; + uint64_t zfb_size_in_byte; + enum frame_buffer_mode fb_mode; + bool dchub_initialzied; + bool dchub_info_valid; +}; + +struct dc_init_data { + struct hw_asic_id asic_id; + void *driver; /* ctx */ + struct cgs_device *cgs_device; + + int num_virtual_links; + /* + * If 'vbios_override' not NULL, it will be called instead + * of the real VBIOS. Intended use is Diagnostics on FPGA. + */ + struct dc_bios *vbios_override; + enum dce_environment dce_environment; + + struct dc_config flags; + uint32_t log_mask; +#if defined(CONFIG_DRM_AMD_DC_FBC) + uint64_t fbc_gpu_addr; +#endif +}; + +struct dc *dc_create(const struct dc_init_data *init_params); + +void dc_destroy(struct dc **dc); + +/******************************************************************************* + * Surface Interfaces + ******************************************************************************/ + +enum { + TRANSFER_FUNC_POINTS = 1025 +}; + +// Moved here from color module for linux +enum color_transfer_func { + transfer_func_unknown, + transfer_func_srgb, + transfer_func_bt709, + transfer_func_pq2084, + transfer_func_pq2084_interim, + transfer_func_linear_0_1, + transfer_func_linear_0_125, + transfer_func_dolbyvision, + transfer_func_gamma_22, + transfer_func_gamma_26 +}; + +enum color_color_space { + color_space_unsupported, + color_space_srgb, + color_space_bt601, + color_space_bt709, + color_space_xv_ycc_bt601, + color_space_xv_ycc_bt709, + color_space_xr_rgb, + color_space_bt2020, + color_space_adobe, + color_space_dci_p3, + color_space_sc_rgb_ms_ref, + color_space_display_native, + color_space_app_ctrl, + color_space_dolby_vision, + color_space_custom_coordinates +}; + +struct dc_hdr_static_metadata { + /* display chromaticities and white point in units of 0.00001 */ + unsigned int chromaticity_green_x; + unsigned int chromaticity_green_y; + unsigned int chromaticity_blue_x; + unsigned int chromaticity_blue_y; + unsigned int chromaticity_red_x; + unsigned int chromaticity_red_y; + unsigned int chromaticity_white_point_x; + unsigned int chromaticity_white_point_y; + + uint32_t min_luminance; + uint32_t max_luminance; + uint32_t maximum_content_light_level; + uint32_t maximum_frame_average_light_level; + + bool hdr_supported; + bool is_hdr; +}; + +enum dc_transfer_func_type { + TF_TYPE_PREDEFINED, + TF_TYPE_DISTRIBUTED_POINTS, + TF_TYPE_BYPASS +}; + +struct dc_transfer_func_distributed_points { + struct fixed31_32 red[TRANSFER_FUNC_POINTS]; + struct fixed31_32 green[TRANSFER_FUNC_POINTS]; + struct fixed31_32 blue[TRANSFER_FUNC_POINTS]; + + uint16_t end_exponent; + uint16_t x_point_at_y1_red; + uint16_t x_point_at_y1_green; + uint16_t x_point_at_y1_blue; +}; + +enum dc_transfer_func_predefined { + TRANSFER_FUNCTION_SRGB, + TRANSFER_FUNCTION_BT709, + TRANSFER_FUNCTION_PQ, + TRANSFER_FUNCTION_LINEAR, +}; + +struct dc_transfer_func { + struct kref refcount; + struct dc_transfer_func_distributed_points tf_pts; + enum dc_transfer_func_type type; + enum dc_transfer_func_predefined tf; + struct dc_context *ctx; +}; + +/* + * This structure is filled in by dc_surface_get_status and contains + * the last requested address and the currently active address so the called + * can determine if there are any outstanding flips + */ +struct dc_plane_status { + struct dc_plane_address requested_address; + struct dc_plane_address current_address; + bool is_flip_pending; + bool is_right_eye; +}; + +struct dc_plane_state { + struct dc_plane_address address; + struct scaling_taps scaling_quality; + struct rect src_rect; + struct rect dst_rect; + struct rect clip_rect; + + union plane_size plane_size; + union dc_tiling_info tiling_info; + + struct dc_plane_dcc_param dcc; + struct dc_hdr_static_metadata hdr_static_ctx; + + struct dc_gamma *gamma_correction; + struct dc_transfer_func *in_transfer_func; + + // sourceContentAttribute cache + bool is_source_input_valid; + struct dc_hdr_static_metadata source_input_mastering_info; + enum color_color_space source_input_color_space; + enum color_transfer_func source_input_tf; + + enum dc_color_space color_space; + enum surface_pixel_format format; + enum dc_rotation_angle rotation; + enum plane_stereo_format stereo_format; + + bool per_pixel_alpha; + bool visible; + bool flip_immediate; + bool horizontal_mirror; + + /* private to DC core */ + struct dc_plane_status status; + struct dc_context *ctx; + + /* private to dc_surface.c */ + enum dc_irq_source irq_source; + struct kref refcount; +}; + +struct dc_plane_info { + union plane_size plane_size; + union dc_tiling_info tiling_info; + struct dc_plane_dcc_param dcc; + enum surface_pixel_format format; + enum dc_rotation_angle rotation; + enum plane_stereo_format stereo_format; + enum dc_color_space color_space; /*todo: wrong place, fits in scaling info*/ + bool horizontal_mirror; + bool visible; + bool per_pixel_alpha; +}; + +struct dc_scaling_info { + struct rect src_rect; + struct rect dst_rect; + struct rect clip_rect; + struct scaling_taps scaling_quality; +}; + +struct dc_surface_update { + struct dc_plane_state *surface; + + /* isr safe update parameters. null means no updates */ + struct dc_flip_addrs *flip_addr; + struct dc_plane_info *plane_info; + struct dc_scaling_info *scaling_info; + /* following updates require alloc/sleep/spin that is not isr safe, + * null means no updates + */ + /* gamma TO BE REMOVED */ + struct dc_gamma *gamma; + struct dc_transfer_func *in_transfer_func; + struct dc_hdr_static_metadata *hdr_static_metadata; +}; + +/* + * Create a new surface with default parameters; + */ +struct dc_plane_state *dc_create_plane_state(struct dc *dc); +const struct dc_plane_status *dc_plane_get_status( + const struct dc_plane_state *plane_state); + +void dc_plane_state_retain(struct dc_plane_state *plane_state); +void dc_plane_state_release(struct dc_plane_state *plane_state); + +void dc_gamma_retain(struct dc_gamma *dc_gamma); +void dc_gamma_release(struct dc_gamma **dc_gamma); +struct dc_gamma *dc_create_gamma(void); + +void dc_transfer_func_retain(struct dc_transfer_func *dc_tf); +void dc_transfer_func_release(struct dc_transfer_func *dc_tf); +struct dc_transfer_func *dc_create_transfer_func(void); + +/* + * This structure holds a surface address. There could be multiple addresses + * in cases such as Stereo 3D, Planar YUV, etc. Other per-flip attributes such + * as frame durations and DCC format can also be set. + */ +struct dc_flip_addrs { + struct dc_plane_address address; + bool flip_immediate; + /* TODO: add flip duration for FreeSync */ +}; + +bool dc_post_update_surfaces_to_stream( + struct dc *dc); + +/* Surface update type is used by dc_update_surfaces_and_stream + * The update type is determined at the very beginning of the function based + * on parameters passed in and decides how much programming (or updating) is + * going to be done during the call. + * + * UPDATE_TYPE_FAST is used for really fast updates that do not require much + * logical calculations or hardware register programming. This update MUST be + * ISR safe on windows. Currently fast update will only be used to flip surface + * address. + * + * UPDATE_TYPE_MED is used for slower updates which require significant hw + * re-programming however do not affect bandwidth consumption or clock + * requirements. At present, this is the level at which front end updates + * that do not require us to run bw_calcs happen. These are in/out transfer func + * updates, viewport offset changes, recout size changes and pixel depth changes. + * This update can be done at ISR, but we want to minimize how often this happens. + * + * UPDATE_TYPE_FULL is slow. Really slow. This requires us to recalculate our + * bandwidth and clocks, possibly rearrange some pipes and reprogram anything front + * end related. Any time viewport dimensions, recout dimensions, scaling ratios or + * gamma need to be adjusted or pipe needs to be turned on (or disconnected) we do + * a full update. This cannot be done at ISR level and should be a rare event. + * Unless someone is stress testing mpo enter/exit, playing with colour or adjusting + * underscan we don't expect to see this call at all. + */ + +enum surface_update_type { + UPDATE_TYPE_FAST, /* super fast, safe to execute in isr */ + UPDATE_TYPE_MED, /* ISR safe, most of programming needed, no bw/clk change*/ + UPDATE_TYPE_FULL, /* may need to shuffle resources */ +}; + +/******************************************************************************* + * Stream Interfaces + ******************************************************************************/ + +struct dc_stream_status { + int primary_otg_inst; + int stream_enc_inst; + int plane_count; + struct dc_plane_state *plane_states[MAX_SURFACE_NUM]; + + /* + * link this stream passes through + */ + struct dc_link *link; +}; + +struct dc_stream_state { + struct dc_sink *sink; + struct dc_crtc_timing timing; + + struct rect src; /* composition area */ + struct rect dst; /* stream addressable area */ + + struct audio_info audio_info; + + struct freesync_context freesync_ctx; + + struct dc_transfer_func *out_transfer_func; + struct colorspace_transform gamut_remap_matrix; + struct csc_transform csc_color_matrix; + + enum signal_type output_signal; + + enum dc_color_space output_color_space; + enum dc_dither_option dither_option; + + enum view_3d_format view_format; + + bool ignore_msa_timing_param; + /* TODO: custom INFO packets */ + /* TODO: ABM info (DMCU) */ + /* TODO: PSR info */ + /* TODO: CEA VIC */ + + /* from core_stream struct */ + struct dc_context *ctx; + + /* used by DCP and FMT */ + struct bit_depth_reduction_params bit_depth_params; + struct clamping_and_pixel_encoding_params clamping; + + int phy_pix_clk; + enum signal_type signal; + bool dpms_off; + + struct dc_stream_status status; + + struct dc_cursor_attributes cursor_attributes; + + /* from stream struct */ + struct kref refcount; +}; + +struct dc_stream_update { + struct rect src; + struct rect dst; + struct dc_transfer_func *out_transfer_func; +}; + +bool dc_is_stream_unchanged( + struct dc_stream_state *old_stream, struct dc_stream_state *stream); +bool dc_is_stream_scaling_unchanged( + struct dc_stream_state *old_stream, struct dc_stream_state *stream); + +/* + * Set up surface attributes and associate to a stream + * The surfaces parameter is an absolute set of all surface active for the stream. + * If no surfaces are provided, the stream will be blanked; no memory read. + * Any flip related attribute changes must be done through this interface. + * + * After this call: + * Surfaces attributes are programmed and configured to be composed into stream. + * This does not trigger a flip. No surface address is programmed. + */ + +bool dc_commit_planes_to_stream( + struct dc *dc, + struct dc_plane_state **plane_states, + uint8_t new_plane_count, + struct dc_stream_state *dc_stream, + struct dc_state *state); + +void dc_commit_updates_for_stream(struct dc *dc, + struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update, + struct dc_plane_state **plane_states, + struct dc_state *state); +/* + * Log the current stream state. + */ +void dc_stream_log( + const struct dc_stream_state *stream, + struct dal_logger *dc_logger, + enum dc_log_type log_type); + +uint8_t dc_get_current_stream_count(struct dc *dc); +struct dc_stream_state *dc_get_stream_at_index(struct dc *dc, uint8_t i); + +/* + * Return the current frame counter. + */ +uint32_t dc_stream_get_vblank_counter(const struct dc_stream_state *stream); + +/* TODO: Return parsed values rather than direct register read + * This has a dependency on the caller (amdgpu_get_crtc_scanoutpos) + * being refactored properly to be dce-specific + */ +bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position); + +enum dc_status dc_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *stream); + +enum dc_status dc_remove_stream_from_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *stream); + + +bool dc_add_plane_to_context( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_plane_state *plane_state, + struct dc_state *context); + +bool dc_remove_plane_from_context( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_plane_state *plane_state, + struct dc_state *context); + +bool dc_rem_all_planes_for_stream( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_state *context); + +bool dc_add_all_planes_for_stream( + const struct dc *dc, + struct dc_stream_state *stream, + struct dc_plane_state * const *plane_states, + int plane_count, + struct dc_state *context); + +/* + * Structure to store surface/stream associations for validation + */ +struct dc_validation_set { + struct dc_stream_state *stream; + struct dc_plane_state *plane_states[MAX_SURFACES]; + uint8_t plane_count; +}; + +enum dc_status dc_validate_stream(struct dc *dc, struct dc_stream_state *stream); + +enum dc_status dc_validate_plane(struct dc *dc, const struct dc_plane_state *plane_state); + +enum dc_status dc_validate_global_state( + struct dc *dc, + struct dc_state *new_ctx); + +/* + * This function takes a stream and checks if it is guaranteed to be supported. + * Guaranteed means that MAX_COFUNC similar streams are supported. + * + * After this call: + * No hardware is programmed for call. Only validation is done. + */ + + +void dc_resource_state_construct( + const struct dc *dc, + struct dc_state *dst_ctx); + +void dc_resource_state_copy_construct( + const struct dc_state *src_ctx, + struct dc_state *dst_ctx); + +void dc_resource_state_copy_construct_current( + const struct dc *dc, + struct dc_state *dst_ctx); + +void dc_resource_state_destruct(struct dc_state *context); + +/* + * TODO update to make it about validation sets + * Set up streams and links associated to drive sinks + * The streams parameter is an absolute set of all active streams. + * + * After this call: + * Phy, Encoder, Timing Generator are programmed and enabled. + * New streams are enabled with blank stream; no memory read. + */ +bool dc_commit_state(struct dc *dc, struct dc_state *context); + +/* + * Set up streams and links associated to drive sinks + * The streams parameter is an absolute set of all active streams. + * + * After this call: + * Phy, Encoder, Timing Generator are programmed and enabled. + * New streams are enabled with blank stream; no memory read. + */ +/* + * Enable stereo when commit_streams is not required, + * for example, frame alternate. + */ +bool dc_enable_stereo( + struct dc *dc, + struct dc_state *context, + struct dc_stream_state *streams[], + uint8_t stream_count); + +/** + * Create a new default stream for the requested sink + */ +struct dc_stream_state *dc_create_stream_for_sink(struct dc_sink *dc_sink); + +void dc_stream_retain(struct dc_stream_state *dc_stream); +void dc_stream_release(struct dc_stream_state *dc_stream); + +struct dc_stream_status *dc_stream_get_status( + struct dc_stream_state *dc_stream); + +enum surface_update_type dc_check_update_surfaces_for_stream( + struct dc *dc, + struct dc_surface_update *updates, + int surface_count, + struct dc_stream_update *stream_update, + const struct dc_stream_status *stream_status); + + +struct dc_state *dc_create_state(void); +void dc_retain_state(struct dc_state *context); +void dc_release_state(struct dc_state *context); + +/******************************************************************************* + * Link Interfaces + ******************************************************************************/ + +struct dpcd_caps { + union dpcd_rev dpcd_rev; + union max_lane_count max_ln_count; + union max_down_spread max_down_spread; + + /* dongle type (DP converter, CV smart dongle) */ + enum display_dongle_type dongle_type; + /* Dongle's downstream count. */ + union sink_count sink_count; + /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, + indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ + struct dc_dongle_caps dongle_caps; + + uint32_t sink_dev_id; + uint32_t branch_dev_id; + int8_t branch_dev_name[6]; + int8_t branch_hw_revision; + + bool allow_invalid_MSA_timing_param; + bool panel_mode_edp; + bool dpcd_display_control_capable; +}; + +struct dc_link_status { + struct dpcd_caps *dpcd_caps; +}; + +/* DP MST stream allocation (payload bandwidth number) */ +struct link_mst_stream_allocation { + /* DIG front */ + const struct stream_encoder *stream_enc; + /* associate DRM payload table with DC stream encoder */ + uint8_t vcp_id; + /* number of slots required for the DP stream in transport packet */ + uint8_t slot_count; +}; + +/* DP MST stream allocation table */ +struct link_mst_stream_allocation_table { + /* number of DP video streams */ + int stream_count; + /* array of stream allocations */ + struct link_mst_stream_allocation stream_allocations[MAX_CONTROLLER_NUM]; +}; + +/* + * A link contains one or more sinks and their connected status. + * The currently active signal type (HDMI, DP-SST, DP-MST) is also reported. + */ +struct dc_link { + struct dc_sink *remote_sinks[MAX_SINKS_PER_LINK]; + unsigned int sink_count; + struct dc_sink *local_sink; + unsigned int link_index; + enum dc_connection_type type; + enum signal_type connector_signal; + enum dc_irq_source irq_source_hpd; + enum dc_irq_source irq_source_hpd_rx;/* aka DP Short Pulse */ + /* caps is the same as reported_link_cap. link_traing use + * reported_link_cap. Will clean up. TODO + */ + struct dc_link_settings reported_link_cap; + struct dc_link_settings verified_link_cap; + struct dc_link_settings cur_link_settings; + struct dc_lane_settings cur_lane_setting; + struct dc_link_settings preferred_link_setting; + + uint8_t ddc_hw_inst; + + uint8_t hpd_src; + + uint8_t link_enc_hw_inst; + + bool test_pattern_enabled; + union compliance_test_state compliance_test_state; + + void *priv; + + struct ddc_service *ddc; + + bool aux_mode; + + /* Private to DC core */ + + const struct dc *dc; + + struct dc_context *ctx; + + struct link_encoder *link_enc; + struct graphics_object_id link_id; + union ddi_channel_mapping ddi_channel_mapping; + struct connector_device_tag_info device_tag; + struct dpcd_caps dpcd_caps; + unsigned short chip_caps; + unsigned int dpcd_sink_count; + enum edp_revision edp_revision; + bool psr_enabled; + + /* MST record stream using this link */ + struct link_flags { + bool dp_keep_receiver_powered; + } wa_flags; + struct link_mst_stream_allocation_table mst_stream_alloc_table; + + struct dc_link_status link_status; + +}; + +const struct dc_link_status *dc_link_get_status(const struct dc_link *dc_link); + +/* + * Return an enumerated dc_link. dc_link order is constant and determined at + * boot time. They cannot be created or destroyed. + * Use dc_get_caps() to get number of links. + */ +static inline struct dc_link *dc_get_link_at_index(struct dc *dc, uint32_t link_index) +{ + return dc->links[link_index]; +} + +/* Set backlight level of an embedded panel (eDP, LVDS). */ +bool dc_link_set_backlight_level(const struct dc_link *dc_link, uint32_t level, + uint32_t frame_ramp, const struct dc_stream_state *stream); + +bool dc_link_set_psr_enable(const struct dc_link *dc_link, bool enable, bool wait); + +bool dc_link_get_psr_state(const struct dc_link *dc_link, uint32_t *psr_state); + +bool dc_link_setup_psr(struct dc_link *dc_link, + const struct dc_stream_state *stream, struct psr_config *psr_config, + struct psr_context *psr_context); + +/* Request DC to detect if there is a Panel connected. + * boot - If this call is during initial boot. + * Return false for any type of detection failure or MST detection + * true otherwise. True meaning further action is required (status update + * and OS notification). + */ +enum dc_detect_reason { + DETECT_REASON_BOOT, + DETECT_REASON_HPD, + DETECT_REASON_HPDRX, +}; + +bool dc_link_detect(struct dc_link *dc_link, enum dc_detect_reason reason); + +/* Notify DC about DP RX Interrupt (aka Short Pulse Interrupt). + * Return: + * true - Downstream port status changed. DM should call DC to do the + * detection. + * false - no change in Downstream port status. No further action required + * from DM. */ +bool dc_link_handle_hpd_rx_irq(struct dc_link *dc_link, + union hpd_irq_data *hpd_irq_dpcd_data); + +struct dc_sink_init_data; + +struct dc_sink *dc_link_add_remote_sink( + struct dc_link *dc_link, + const uint8_t *edid, + int len, + struct dc_sink_init_data *init_data); + +void dc_link_remove_remote_sink( + struct dc_link *link, + struct dc_sink *sink); + +/* Used by diagnostics for virtual link at the moment */ + +void dc_link_dp_set_drive_settings( + struct dc_link *link, + struct link_training_settings *lt_settings); + +enum link_training_result dc_link_dp_perform_link_training( + struct dc_link *link, + const struct dc_link_settings *link_setting, + bool skip_video_pattern); + +void dc_link_dp_enable_hpd(const struct dc_link *link); + +void dc_link_dp_disable_hpd(const struct dc_link *link); + +bool dc_link_dp_set_test_pattern( + struct dc_link *link, + enum dp_test_pattern test_pattern, + const struct link_training_settings *p_link_settings, + const unsigned char *p_custom_pattern, + unsigned int cust_pattern_size); + +/******************************************************************************* + * Sink Interfaces - A sink corresponds to a display output device + ******************************************************************************/ + +struct dc_container_id { + // 128bit GUID in binary form + unsigned char guid[16]; + // 8 byte port ID -> ELD.PortID + unsigned int portId[2]; + // 128bit GUID in binary formufacturer name -> ELD.ManufacturerName + unsigned short manufacturerName; + // 2 byte product code -> ELD.ProductCode + unsigned short productCode; +}; + + + +/* + * The sink structure contains EDID and other display device properties + */ +struct dc_sink { + enum signal_type sink_signal; + struct dc_edid dc_edid; /* raw edid */ + struct dc_edid_caps edid_caps; /* parse display caps */ + struct dc_container_id *dc_container_id; + uint32_t dongle_max_pix_clk; + void *priv; + struct stereo_3d_features features_3d[TIMING_3D_FORMAT_MAX]; + bool converter_disable_audio; + + /* private to DC core */ + struct dc_link *link; + struct dc_context *ctx; + + /* private to dc_sink.c */ + struct kref refcount; +}; + +void dc_sink_retain(struct dc_sink *sink); +void dc_sink_release(struct dc_sink *sink); + +struct dc_sink_init_data { + enum signal_type sink_signal; + struct dc_link *link; + uint32_t dongle_max_pix_clk; + bool converter_disable_audio; +}; + +struct dc_sink *dc_sink_create(const struct dc_sink_init_data *init_params); + +/******************************************************************************* + * Cursor interfaces - To manages the cursor within a stream + ******************************************************************************/ +/* TODO: Deprecated once we switch to dc_set_cursor_position */ +bool dc_stream_set_cursor_attributes( + struct dc_stream_state *stream, + const struct dc_cursor_attributes *attributes); + +bool dc_stream_set_cursor_position( + struct dc_stream_state *stream, + const struct dc_cursor_position *position); + +/* Newer interfaces */ +struct dc_cursor { + struct dc_plane_address address; + struct dc_cursor_attributes attributes; +}; + +/******************************************************************************* + * Interrupt interfaces + ******************************************************************************/ +enum dc_irq_source dc_interrupt_to_irq_source( + struct dc *dc, + uint32_t src_id, + uint32_t ext_id); +void dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable); +void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src); +enum dc_irq_source dc_get_hpd_irq_source_at_index( + struct dc *dc, uint32_t link_index); + +/******************************************************************************* + * Power Interfaces + ******************************************************************************/ + +void dc_set_power_state( + struct dc *dc, + enum dc_acpi_cm_power_state power_state); +void dc_resume(struct dc *dc); + +/* + * DPCD access interfaces + */ + +bool dc_submit_i2c( + struct dc *dc, + uint32_t link_index, + struct i2c_command *cmd); + + +#endif /* DC_INTERFACE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_bios_types.h b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h new file mode 100644 index 000000000000..273d80a4ebce --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h @@ -0,0 +1,218 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DC_BIOS_TYPES_H +#define DC_BIOS_TYPES_H + +/****************************************************************************** + * Interface file for VBIOS implementations. + * + * The default implementation is inside DC. + * Display Manager (which instantiates DC) has the option to supply it's own + * (external to DC) implementation of VBIOS, which will be called by DC, using + * this interface. + * (The intended use is Diagnostics, but other uses may appear.) + *****************************************************************************/ + +#include "include/bios_parser_types.h" + +struct dc_vbios_funcs { + uint8_t (*get_connectors_number)(struct dc_bios *bios); + + struct graphics_object_id (*get_encoder_id)( + struct dc_bios *bios, + uint32_t i); + struct graphics_object_id (*get_connector_id)( + struct dc_bios *bios, + uint8_t connector_index); + uint32_t (*get_dst_number)( + struct dc_bios *bios, + struct graphics_object_id id); + + enum bp_result (*get_src_obj)( + struct dc_bios *bios, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *src_object_id); + enum bp_result (*get_dst_obj)( + struct dc_bios *bios, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *dest_object_id); + + enum bp_result (*get_i2c_info)( + struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_i2c_info *info); + + enum bp_result (*get_voltage_ddc_info)( + struct dc_bios *bios, + uint32_t index, + struct graphics_object_i2c_info *info); + enum bp_result (*get_thermal_ddc_info)( + struct dc_bios *bios, + uint32_t i2c_channel_id, + struct graphics_object_i2c_info *info); + enum bp_result (*get_hpd_info)( + struct dc_bios *bios, + struct graphics_object_id id, + struct graphics_object_hpd_info *info); + enum bp_result (*get_device_tag)( + struct dc_bios *bios, + struct graphics_object_id connector_object_id, + uint32_t device_tag_index, + struct connector_device_tag_info *info); + enum bp_result (*get_firmware_info)( + struct dc_bios *bios, + struct dc_firmware_info *info); + enum bp_result (*get_spread_spectrum_info)( + struct dc_bios *bios, + enum as_signal_type signal, + uint32_t index, + struct spread_spectrum_info *ss_info); + uint32_t (*get_ss_entry_number)( + struct dc_bios *bios, + enum as_signal_type signal); + enum bp_result (*get_embedded_panel_info)( + struct dc_bios *bios, + struct embedded_panel_info *info); + enum bp_result (*get_gpio_pin_info)( + struct dc_bios *bios, + uint32_t gpio_id, + struct gpio_pin_info *info); + enum bp_result (*get_encoder_cap_info)( + struct dc_bios *bios, + struct graphics_object_id object_id, + struct bp_encoder_cap_info *info); + + bool (*is_lid_status_changed)( + struct dc_bios *bios); + bool (*is_display_config_changed)( + struct dc_bios *bios); + bool (*is_accelerated_mode)( + struct dc_bios *bios); + void (*get_bios_event_info)( + struct dc_bios *bios, + struct bios_event_info *info); + void (*update_requested_backlight_level)( + struct dc_bios *bios, + uint32_t backlight_8bit); + uint32_t (*get_requested_backlight_level)( + struct dc_bios *bios); + void (*take_backlight_control)( + struct dc_bios *bios, + bool cntl); + + bool (*is_active_display)( + struct dc_bios *bios, + enum signal_type signal, + const struct connector_device_tag_info *device_tag); + enum controller_id (*get_embedded_display_controller_id)( + struct dc_bios *bios); + uint32_t (*get_embedded_display_refresh_rate)( + struct dc_bios *bios); + + void (*set_scratch_critical_state)( + struct dc_bios *bios, + bool state); + bool (*is_device_id_supported)( + struct dc_bios *bios, + struct device_id id); + + /* COMMANDS */ + + enum bp_result (*encoder_control)( + struct dc_bios *bios, + struct bp_encoder_control *cntl); + enum bp_result (*transmitter_control)( + struct dc_bios *bios, + struct bp_transmitter_control *cntl); + enum bp_result (*crt_control)( + struct dc_bios *bios, + enum engine_id engine_id, + bool enable, + uint32_t pixel_clock); + enum bp_result (*enable_crtc)( + struct dc_bios *bios, + enum controller_id id, + bool enable); + enum bp_result (*adjust_pixel_clock)( + struct dc_bios *bios, + struct bp_adjust_pixel_clock_parameters *bp_params); + enum bp_result (*set_pixel_clock)( + struct dc_bios *bios, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*set_dce_clock)( + struct dc_bios *bios, + struct bp_set_dce_clock_parameters *bp_params); + unsigned int (*get_smu_clock_info)( + struct dc_bios *bios); + enum bp_result (*enable_spread_spectrum_on_ppll)( + struct dc_bios *bios, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); + enum bp_result (*program_crtc_timing)( + struct dc_bios *bios, + struct bp_hw_crtc_timing_parameters *bp_params); + + enum bp_result (*crtc_source_select)( + struct dc_bios *bios, + struct bp_crtc_source_select *bp_params); + enum bp_result (*program_display_engine_pll)( + struct dc_bios *bios, + struct bp_pixel_clock_parameters *bp_params); + + enum signal_type (*dac_load_detect)( + struct dc_bios *bios, + struct graphics_object_id encoder, + struct graphics_object_id connector, + enum signal_type display_signal); + + enum bp_result (*enable_disp_power_gating)( + struct dc_bios *bios, + enum controller_id controller_id, + enum bp_pipe_control_action action); + + void (*post_init)(struct dc_bios *bios); + + void (*bios_parser_destroy)(struct dc_bios **dcb); +}; + +struct bios_registers { + uint32_t BIOS_SCRATCH_6; +}; + +struct dc_bios { + const struct dc_vbios_funcs *funcs; + + uint8_t *bios; + uint32_t bios_size; + + uint8_t *bios_local_image; + + struct dc_context *ctx; + const struct bios_registers *regs; + struct integrated_info *integrated_info; +}; + +#endif /* DC_BIOS_TYPES_H */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_ddc_types.h b/drivers/gpu/drm/amd/display/dc/dc_ddc_types.h new file mode 100644 index 000000000000..e1affeb5cc51 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_ddc_types.h @@ -0,0 +1,115 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef DC_DDC_TYPES_H_ +#define DC_DDC_TYPES_H_ + +struct i2c_payload { + bool write; + uint8_t address; + uint32_t length; + uint8_t *data; +}; + +enum i2c_command_engine { + I2C_COMMAND_ENGINE_DEFAULT, + I2C_COMMAND_ENGINE_SW, + I2C_COMMAND_ENGINE_HW +}; + +struct i2c_command { + struct i2c_payload *payloads; + uint8_t number_of_payloads; + + enum i2c_command_engine engine; + + /* expressed in KHz + * zero means "use default value" */ + uint32_t speed; +}; + +struct gpio_ddc_hw_info { + bool hw_supported; + uint32_t ddc_channel; +}; + +struct ddc { + struct gpio *pin_data; + struct gpio *pin_clock; + struct gpio_ddc_hw_info hw_info; + struct dc_context *ctx; +}; + +union ddc_wa { + struct { + uint32_t DP_SKIP_POWER_OFF:1; + uint32_t DP_AUX_POWER_UP_WA_DELAY:1; + } bits; + uint32_t raw; +}; + +struct ddc_flags { + uint8_t EDID_QUERY_DONE_ONCE:1; + uint8_t IS_INTERNAL_DISPLAY:1; + uint8_t FORCE_READ_REPEATED_START:1; + uint8_t EDID_STRESS_READ:1; + +}; + +enum ddc_transaction_type { + DDC_TRANSACTION_TYPE_NONE = 0, + DDC_TRANSACTION_TYPE_I2C, + DDC_TRANSACTION_TYPE_I2C_OVER_AUX, + DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER, + DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER +}; + +enum display_dongle_type { + DISPLAY_DONGLE_NONE = 0, + /* Active converter types*/ + DISPLAY_DONGLE_DP_VGA_CONVERTER, + DISPLAY_DONGLE_DP_DVI_CONVERTER, + DISPLAY_DONGLE_DP_HDMI_CONVERTER, + /* DP-HDMI/DVI passive dongles (Type 1 and Type 2)*/ + DISPLAY_DONGLE_DP_DVI_DONGLE, + DISPLAY_DONGLE_DP_HDMI_DONGLE, + /* Other types of dongle*/ + DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE, +}; + +struct ddc_service { + struct ddc *ddc_pin; + struct ddc_flags flags; + union ddc_wa wa; + enum ddc_transaction_type transaction_type; + enum display_dongle_type dongle_type; + struct dc_context *ctx; + struct dc_link *link; + + uint32_t address; + uint32_t edid_buf_len; + uint8_t edid_buf[MAX_EDID_BUFFER_SIZE]; +}; + +#endif /* DC_DDC_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h new file mode 100644 index 000000000000..77e2de69cca3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h @@ -0,0 +1,467 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DC_DP_TYPES_H +#define DC_DP_TYPES_H + +enum dc_lane_count { + LANE_COUNT_UNKNOWN = 0, + LANE_COUNT_ONE = 1, + LANE_COUNT_TWO = 2, + LANE_COUNT_FOUR = 4, + LANE_COUNT_EIGHT = 8, + LANE_COUNT_DP_MAX = LANE_COUNT_FOUR +}; + +/* This is actually a reference clock (27MHz) multiplier + * 162MBps bandwidth for 1.62GHz like rate, + * 270MBps for 2.70GHz, + * 324MBps for 3.24Ghz, + * 540MBps for 5.40GHz + * 810MBps for 8.10GHz + */ +enum dc_link_rate { + LINK_RATE_UNKNOWN = 0, + LINK_RATE_LOW = 0x06, + LINK_RATE_HIGH = 0x0A, + LINK_RATE_RBR2 = 0x0C, + LINK_RATE_HIGH2 = 0x14, + LINK_RATE_HIGH3 = 0x1E +}; + +enum dc_link_spread { + LINK_SPREAD_DISABLED = 0x00, + /* 0.5 % downspread 30 kHz */ + LINK_SPREAD_05_DOWNSPREAD_30KHZ = 0x10, + /* 0.5 % downspread 33 kHz */ + LINK_SPREAD_05_DOWNSPREAD_33KHZ = 0x11 +}; + +enum dc_voltage_swing { + VOLTAGE_SWING_LEVEL0 = 0, /* direct HW translation! */ + VOLTAGE_SWING_LEVEL1, + VOLTAGE_SWING_LEVEL2, + VOLTAGE_SWING_LEVEL3, + VOLTAGE_SWING_MAX_LEVEL = VOLTAGE_SWING_LEVEL3 +}; + +enum dc_pre_emphasis { + PRE_EMPHASIS_DISABLED = 0, /* direct HW translation! */ + PRE_EMPHASIS_LEVEL1, + PRE_EMPHASIS_LEVEL2, + PRE_EMPHASIS_LEVEL3, + PRE_EMPHASIS_MAX_LEVEL = PRE_EMPHASIS_LEVEL3 +}; +/* Post Cursor 2 is optional for transmitter + * and it applies only to the main link operating at HBR2 + */ +enum dc_post_cursor2 { + POST_CURSOR2_DISABLED = 0, /* direct HW translation! */ + POST_CURSOR2_LEVEL1, + POST_CURSOR2_LEVEL2, + POST_CURSOR2_LEVEL3, + POST_CURSOR2_MAX_LEVEL = POST_CURSOR2_LEVEL3, +}; + +struct dc_link_settings { + enum dc_lane_count lane_count; + enum dc_link_rate link_rate; + enum dc_link_spread link_spread; +}; + +struct dc_lane_settings { + enum dc_voltage_swing VOLTAGE_SWING; + enum dc_pre_emphasis PRE_EMPHASIS; + enum dc_post_cursor2 POST_CURSOR2; +}; + +struct dc_link_training_settings { + struct dc_link_settings link; + struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]; +}; + + +union dpcd_rev { + struct { + uint8_t MINOR:4; + uint8_t MAJOR:4; + } bits; + uint8_t raw; +}; + +union max_lane_count { + struct { + uint8_t MAX_LANE_COUNT:5; + uint8_t POST_LT_ADJ_REQ_SUPPORTED:1; + uint8_t TPS3_SUPPORTED:1; + uint8_t ENHANCED_FRAME_CAP:1; + } bits; + uint8_t raw; +}; + +union max_down_spread { + struct { + uint8_t MAX_DOWN_SPREAD:1; + uint8_t RESERVED:5; + uint8_t NO_AUX_HANDSHAKE_LINK_TRAINING:1; + uint8_t TPS4_SUPPORTED:1; + } bits; + uint8_t raw; +}; + +union mstm_cap { + struct { + uint8_t MST_CAP:1; + uint8_t RESERVED:7; + } bits; + uint8_t raw; +}; + +union lane_count_set { + struct { + uint8_t LANE_COUNT_SET:5; + uint8_t POST_LT_ADJ_REQ_GRANTED:1; + uint8_t RESERVED:1; + uint8_t ENHANCED_FRAMING:1; + } bits; + uint8_t raw; +}; + +union lane_status { + struct { + uint8_t CR_DONE_0:1; + uint8_t CHANNEL_EQ_DONE_0:1; + uint8_t SYMBOL_LOCKED_0:1; + uint8_t RESERVED0:1; + uint8_t CR_DONE_1:1; + uint8_t CHANNEL_EQ_DONE_1:1; + uint8_t SYMBOL_LOCKED_1:1; + uint8_t RESERVED_1:1; + } bits; + uint8_t raw; +}; + +union device_service_irq { + struct { + uint8_t REMOTE_CONTROL_CMD_PENDING:1; + uint8_t AUTOMATED_TEST:1; + uint8_t CP_IRQ:1; + uint8_t MCCS_IRQ:1; + uint8_t DOWN_REP_MSG_RDY:1; + uint8_t UP_REQ_MSG_RDY:1; + uint8_t SINK_SPECIFIC:1; + uint8_t reserved:1; + } bits; + uint8_t raw; +}; + +union sink_count { + struct { + uint8_t SINK_COUNT:6; + uint8_t CPREADY:1; + uint8_t RESERVED:1; + } bits; + uint8_t raw; +}; + +union lane_align_status_updated { + struct { + uint8_t INTERLANE_ALIGN_DONE:1; + uint8_t POST_LT_ADJ_REQ_IN_PROGRESS:1; + uint8_t RESERVED:4; + uint8_t DOWNSTREAM_PORT_STATUS_CHANGED:1; + uint8_t LINK_STATUS_UPDATED:1; + } bits; + uint8_t raw; +}; + +union lane_adjust { + struct { + uint8_t VOLTAGE_SWING_LANE:2; + uint8_t PRE_EMPHASIS_LANE:2; + uint8_t RESERVED:4; + } bits; + uint8_t raw; +}; + +union dpcd_training_pattern { + struct { + uint8_t TRAINING_PATTERN_SET:4; + uint8_t RECOVERED_CLOCK_OUT_EN:1; + uint8_t SCRAMBLING_DISABLE:1; + uint8_t SYMBOL_ERROR_COUNT_SEL:2; + } v1_4; + struct { + uint8_t TRAINING_PATTERN_SET:2; + uint8_t LINK_QUAL_PATTERN_SET:2; + uint8_t RESERVED:4; + } v1_3; + uint8_t raw; +}; + +/* Training Lane is used to configure downstream DP device's voltage swing +and pre-emphasis levels*/ +/* The DPCD addresses are from 0x103 to 0x106*/ +union dpcd_training_lane { + struct { + uint8_t VOLTAGE_SWING_SET:2; + uint8_t MAX_SWING_REACHED:1; + uint8_t PRE_EMPHASIS_SET:2; + uint8_t MAX_PRE_EMPHASIS_REACHED:1; + uint8_t RESERVED:2; + } bits; + uint8_t raw; +}; + +/* TMDS-converter related */ +union dwnstream_port_caps_byte0 { + struct { + uint8_t DWN_STRM_PORTX_TYPE:3; + uint8_t DWN_STRM_PORTX_HPD:1; + uint8_t RESERVERD:4; + } bits; + uint8_t raw; +}; + +/* these are the detailed types stored at DWN_STRM_PORTX_CAP (00080h)*/ +enum dpcd_downstream_port_detailed_type { + DOWN_STREAM_DETAILED_DP = 0, + DOWN_STREAM_DETAILED_VGA, + DOWN_STREAM_DETAILED_DVI, + DOWN_STREAM_DETAILED_HDMI, + DOWN_STREAM_DETAILED_NONDDC,/* has no EDID (TV,CV)*/ + DOWN_STREAM_DETAILED_DP_PLUS_PLUS +}; + +union dwnstream_port_caps_byte1 { + struct { + uint8_t MAX_BITS_PER_COLOR_COMPONENT:2; + uint8_t RESERVED:6; + } bits; + uint8_t raw; +}; + +union dp_downstream_port_present { + uint8_t byte; + struct { + uint8_t PORT_PRESENT:1; + uint8_t PORT_TYPE:2; + uint8_t FMT_CONVERSION:1; + uint8_t DETAILED_CAPS:1; + uint8_t RESERVED:3; + } fields; +}; + +union dwnstream_port_caps_byte3_dvi { + struct { + uint8_t RESERVED1:1; + uint8_t DUAL_LINK:1; + uint8_t HIGH_COLOR_DEPTH:1; + uint8_t RESERVED2:5; + } bits; + uint8_t raw; +}; + +union dwnstream_port_caps_byte3_hdmi { + struct { + uint8_t FRAME_SEQ_TO_FRAME_PACK:1; + uint8_t YCrCr422_PASS_THROUGH:1; + uint8_t YCrCr420_PASS_THROUGH:1; + uint8_t YCrCr422_CONVERSION:1; + uint8_t YCrCr420_CONVERSION:1; + uint8_t RESERVED:3; + } bits; + uint8_t raw; +}; + +/*4-byte structure for detailed capabilities of a down-stream port +(DP-to-TMDS converter).*/ + +union sink_status { + struct { + uint8_t RX_PORT0_STATUS:1; + uint8_t RX_PORT1_STATUS:1; + uint8_t RESERVED:6; + } bits; + uint8_t raw; +}; + +/*6-byte structure corresponding to 6 registers (200h-205h) +read during handling of HPD-IRQ*/ +union hpd_irq_data { + struct { + union sink_count sink_cnt;/* 200h */ + union device_service_irq device_service_irq;/* 201h */ + union lane_status lane01_status;/* 202h */ + union lane_status lane23_status;/* 203h */ + union lane_align_status_updated lane_status_updated;/* 204h */ + union sink_status sink_status; + } bytes; + uint8_t raw[6]; +}; + +union down_stream_port_count { + struct { + uint8_t DOWN_STR_PORT_COUNT:4; + uint8_t RESERVED:2; /*Bits 5:4 = RESERVED. Read all 0s.*/ + /*Bit 6 = MSA_TIMING_PAR_IGNORED + 0 = Sink device requires the MSA timing parameters + 1 = Sink device is capable of rendering incoming video + stream without MSA timing parameters*/ + uint8_t IGNORE_MSA_TIMING_PARAM:1; + /*Bit 7 = OUI Support + 0 = OUI not supported + 1 = OUI supported + (OUI and Device Identification mandatory for DP 1.2)*/ + uint8_t OUI_SUPPORT:1; + } bits; + uint8_t raw; +}; + +union down_spread_ctrl { + struct { + uint8_t RESERVED1:4;/* Bit 3:0 = RESERVED. Read all 0s*/ + /* Bits 4 = SPREAD_AMP. Spreading amplitude + 0 = Main link signal is not downspread + 1 = Main link signal is downspread <= 0.5% + with frequency in the range of 30kHz ~ 33kHz*/ + uint8_t SPREAD_AMP:1; + uint8_t RESERVED2:2;/*Bit 6:5 = RESERVED. Read all 0s*/ + /*Bit 7 = MSA_TIMING_PAR_IGNORE_EN + 0 = Source device will send valid data for the MSA Timing Params + 1 = Source device may send invalid data for these MSA Timing Params*/ + uint8_t IGNORE_MSA_TIMING_PARAM:1; + } bits; + uint8_t raw; +}; + +union dpcd_edp_config { + struct { + uint8_t PANEL_MODE_EDP:1; + uint8_t FRAMING_CHANGE_ENABLE:1; + uint8_t RESERVED:5; + uint8_t PANEL_SELF_TEST_ENABLE:1; + } bits; + uint8_t raw; +}; + +struct dp_device_vendor_id { + uint8_t ieee_oui[3];/*24-bit IEEE OUI*/ + uint8_t ieee_device_id[6];/*usually 6-byte ASCII name*/ +}; + +struct dp_sink_hw_fw_revision { + uint8_t ieee_hw_rev; + uint8_t ieee_fw_rev[2]; +}; + +/*DPCD register of DP receiver capability field bits-*/ +union edp_configuration_cap { + struct { + uint8_t ALT_SCRAMBLER_RESET:1; + uint8_t FRAMING_CHANGE:1; + uint8_t RESERVED:1; + uint8_t DPCD_DISPLAY_CONTROL_CAPABLE:1; + uint8_t RESERVED2:4; + } bits; + uint8_t raw; +}; + +union training_aux_rd_interval { + struct { + uint8_t TRAINIG_AUX_RD_INTERVAL:7; + uint8_t EXT_RECIEVER_CAP_FIELD_PRESENT:1; + } bits; + uint8_t raw; +}; + +/* Automated test structures */ +union test_request { + struct { + uint8_t LINK_TRAINING :1; + uint8_t LINK_TEST_PATTRN :1; + uint8_t EDID_REAT :1; + uint8_t PHY_TEST_PATTERN :1; + uint8_t AUDIO_TEST_PATTERN :1; + uint8_t RESERVED :1; + uint8_t TEST_STEREO_3D :1; + } bits; + uint8_t raw; +}; + +union test_response { + struct { + uint8_t ACK :1; + uint8_t NO_ACK :1; + uint8_t RESERVED :6; + } bits; + uint8_t raw; +}; + +union phy_test_pattern { + struct { + /* DpcdPhyTestPatterns. This field is 2 bits for DP1.1 + * and 3 bits for DP1.2. + */ + uint8_t PATTERN :3; + /* BY speci, bit7:2 is 0 for DP1.1. */ + uint8_t RESERVED :5; + } bits; + uint8_t raw; +}; + +/* States of Compliance Test Specification (CTS DP1.2). */ +union compliance_test_state { + struct { + unsigned char STEREO_3D_RUNNING : 1; + unsigned char RESERVED : 7; + } bits; + unsigned char raw; +}; + +union link_test_pattern { + struct { + /* dpcd_link_test_patterns */ + unsigned char PATTERN :2; + unsigned char RESERVED:6; + } bits; + unsigned char raw; +}; + +union test_misc { + struct dpcd_test_misc_bits { + unsigned char SYNC_CLOCK :1; + /* dpcd_test_color_format */ + unsigned char CLR_FORMAT :2; + /* dpcd_test_dyn_range */ + unsigned char DYN_RANGE :1; + unsigned char YCBCR :1; + /* dpcd_test_bit_depth */ + unsigned char BPC :3; + } bits; + unsigned char raw; +}; + +#endif /* DC_DP_TYPES_H */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c new file mode 100644 index 000000000000..0d84b2a1ccfd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c @@ -0,0 +1,171 @@ +/* + * dc_helper.c + * + * Created on: Aug 30, 2016 + * Author: agrodzov + */ +#include "dm_services.h" +#include + +uint32_t generic_reg_update_ex(const struct dc_context *ctx, + uint32_t addr, uint32_t reg_val, int n, + uint8_t shift1, uint32_t mask1, uint32_t field_value1, + ...) +{ + uint32_t shift, mask, field_value; + int i = 1; + + va_list ap; + va_start(ap, field_value1); + + reg_val = set_reg_field_value_ex(reg_val, field_value1, mask1, shift1); + + while (i < n) { + shift = va_arg(ap, uint32_t); + mask = va_arg(ap, uint32_t); + field_value = va_arg(ap, uint32_t); + + reg_val = set_reg_field_value_ex(reg_val, field_value, mask, shift); + i++; + } + + dm_write_reg(ctx, addr, reg_val); + va_end(ap); + + return reg_val; +} + +uint32_t generic_reg_get(const struct dc_context *ctx, uint32_t addr, + uint8_t shift, uint32_t mask, uint32_t *field_value) +{ + uint32_t reg_val = dm_read_reg(ctx, addr); + *field_value = get_reg_field_value_ex(reg_val, mask, shift); + return reg_val; +} + +uint32_t generic_reg_get2(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2) +{ + uint32_t reg_val = dm_read_reg(ctx, addr); + *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1); + *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2); + return reg_val; +} + +uint32_t generic_reg_get3(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2, + uint8_t shift3, uint32_t mask3, uint32_t *field_value3) +{ + uint32_t reg_val = dm_read_reg(ctx, addr); + *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1); + *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2); + *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3); + return reg_val; +} + +uint32_t generic_reg_get4(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2, + uint8_t shift3, uint32_t mask3, uint32_t *field_value3, + uint8_t shift4, uint32_t mask4, uint32_t *field_value4) +{ + uint32_t reg_val = dm_read_reg(ctx, addr); + *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1); + *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2); + *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3); + *field_value4 = get_reg_field_value_ex(reg_val, mask4, shift4); + return reg_val; +} + +uint32_t generic_reg_get5(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2, + uint8_t shift3, uint32_t mask3, uint32_t *field_value3, + uint8_t shift4, uint32_t mask4, uint32_t *field_value4, + uint8_t shift5, uint32_t mask5, uint32_t *field_value5) +{ + uint32_t reg_val = dm_read_reg(ctx, addr); + *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1); + *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2); + *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3); + *field_value4 = get_reg_field_value_ex(reg_val, mask4, shift4); + *field_value5 = get_reg_field_value_ex(reg_val, mask5, shift5); + return reg_val; +} + +/* note: va version of this is pretty bad idea, since there is a output parameter pass by pointer + * compiler won't be able to check for size match and is prone to stack corruption type of bugs + +uint32_t generic_reg_get(const struct dc_context *ctx, + uint32_t addr, int n, ...) +{ + uint32_t shift, mask; + uint32_t *field_value; + uint32_t reg_val; + int i = 0; + + reg_val = dm_read_reg(ctx, addr); + + va_list ap; + va_start(ap, n); + + while (i < n) { + shift = va_arg(ap, uint32_t); + mask = va_arg(ap, uint32_t); + field_value = va_arg(ap, uint32_t *); + + *field_value = get_reg_field_value_ex(reg_val, mask, shift); + i++; + } + + va_end(ap); + + return reg_val; +} +*/ + +uint32_t generic_reg_wait(const struct dc_context *ctx, + uint32_t addr, uint32_t shift, uint32_t mask, uint32_t condition_value, + unsigned int delay_between_poll_us, unsigned int time_out_num_tries, + const char *func_name, int line) +{ + uint32_t field_value; + uint32_t reg_val; + int i; + + /* something is terribly wrong if time out is > 200ms. (5Hz) */ + ASSERT(delay_between_poll_us * time_out_num_tries <= 200000); + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) { + /* 35 seconds */ + delay_between_poll_us = 35000; + time_out_num_tries = 1000; + } + + for (i = 0; i <= time_out_num_tries; i++) { + if (i) { + if (delay_between_poll_us >= 1000) + msleep(delay_between_poll_us/1000); + else if (delay_between_poll_us > 0) + udelay(delay_between_poll_us); + } + + reg_val = dm_read_reg(ctx, addr); + + field_value = get_reg_field_value_ex(reg_val, mask, shift); + + if (field_value == condition_value) + return reg_val; + } + + dm_error("REG_WAIT timeout %dus * %d tries - %s line:%d\n", + delay_between_poll_us, time_out_num_tries, + func_name, line); + + if (!IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + BREAK_TO_DEBUGGER(); + + return reg_val; +} diff --git a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h new file mode 100644 index 000000000000..1a9f57fb0838 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h @@ -0,0 +1,706 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DC_HW_TYPES_H +#define DC_HW_TYPES_H + +#include "os_types.h" +#include "fixed31_32.h" +#include "signal_types.h" + +/****************************************************************************** + * Data types for Virtual HW Layer of DAL3. + * (see DAL3 design documents for HW Layer definition) + * + * The intended uses are: + * 1. Generation pseudocode sequences for HW programming. + * 2. Implementation of real HW programming by HW Sequencer of DAL3. + * + * Note: do *not* add any types which are *not* used for HW programming - this + * will ensure separation of Logic layer from HW layer. + ******************************************************************************/ + +union large_integer { + struct { + uint32_t low_part; + int32_t high_part; + }; + + struct { + uint32_t low_part; + int32_t high_part; + } u; + + int64_t quad_part; +}; + +#define PHYSICAL_ADDRESS_LOC union large_integer + +enum dc_plane_addr_type { + PLN_ADDR_TYPE_GRAPHICS = 0, + PLN_ADDR_TYPE_GRPH_STEREO, + PLN_ADDR_TYPE_VIDEO_PROGRESSIVE, +}; + +struct dc_plane_address { + enum dc_plane_addr_type type; + bool tmz_surface; + union { + struct{ + PHYSICAL_ADDRESS_LOC addr; + PHYSICAL_ADDRESS_LOC meta_addr; + union large_integer dcc_const_color; + } grph; + + /*stereo*/ + struct { + PHYSICAL_ADDRESS_LOC left_addr; + PHYSICAL_ADDRESS_LOC left_meta_addr; + union large_integer left_dcc_const_color; + + PHYSICAL_ADDRESS_LOC right_addr; + PHYSICAL_ADDRESS_LOC right_meta_addr; + union large_integer right_dcc_const_color; + + } grph_stereo; + + /*video progressive*/ + struct { + PHYSICAL_ADDRESS_LOC luma_addr; + PHYSICAL_ADDRESS_LOC luma_meta_addr; + union large_integer luma_dcc_const_color; + + PHYSICAL_ADDRESS_LOC chroma_addr; + PHYSICAL_ADDRESS_LOC chroma_meta_addr; + union large_integer chroma_dcc_const_color; + } video_progressive; + }; +}; + +struct dc_size { + int width; + int height; +}; + +struct rect { + int x; + int y; + int width; + int height; +}; + +union plane_size { + /* Grph or Video will be selected + * based on format above: + * Use Video structure if + * format >= DalPixelFormat_VideoBegin + * else use Grph structure + */ + struct { + struct rect surface_size; + /* Graphic surface pitch in pixels. + * In LINEAR_GENERAL mode, pitch + * is 32 pixel aligned. + */ + int surface_pitch; + } grph; + + struct { + struct rect luma_size; + /* Graphic surface pitch in pixels. + * In LINEAR_GENERAL mode, pitch is + * 32 pixel aligned. + */ + int luma_pitch; + + struct rect chroma_size; + /* Graphic surface pitch in pixels. + * In LINEAR_GENERAL mode, pitch is + * 32 pixel aligned. + */ + int chroma_pitch; + } video; +}; + +struct dc_plane_dcc_param { + bool enable; + + union { + struct { + int meta_pitch; + bool independent_64b_blks; + } grph; + + struct { + int meta_pitch_l; + bool independent_64b_blks_l; + + int meta_pitch_c; + bool independent_64b_blks_c; + } video; + }; +}; + +/*Displayable pixel format in fb*/ +enum surface_pixel_format { + SURFACE_PIXEL_FORMAT_GRPH_BEGIN = 0, + /*TOBE REMOVED paletta 256 colors*/ + SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS = + SURFACE_PIXEL_FORMAT_GRPH_BEGIN, + /*16 bpp*/ + SURFACE_PIXEL_FORMAT_GRPH_ARGB1555, + /*16 bpp*/ + SURFACE_PIXEL_FORMAT_GRPH_RGB565, + /*32 bpp*/ + SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + /*32 bpp swaped*/ + SURFACE_PIXEL_FORMAT_GRPH_ABGR8888, + + SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010, + /*swaped*/ + SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010, + /*TOBE REMOVED swaped, XR_BIAS has no differance + * for pixel layout than previous and we can + * delete this after discusion*/ + SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS, + /*64 bpp */ + SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616, + /*float*/ + SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F, + /*swaped & float*/ + SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F, + /*grow graphics here if necessary */ + + SURFACE_PIXEL_FORMAT_VIDEO_BEGIN, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr = + SURFACE_PIXEL_FORMAT_VIDEO_BEGIN, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb, + SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr, + SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb, + SURFACE_PIXEL_FORMAT_INVALID + + /*grow 444 video here if necessary */ +}; + + + +/* Pixel format */ +enum pixel_format { + /*graph*/ + PIXEL_FORMAT_UNINITIALIZED, + PIXEL_FORMAT_INDEX8, + PIXEL_FORMAT_RGB565, + PIXEL_FORMAT_ARGB8888, + PIXEL_FORMAT_ARGB2101010, + PIXEL_FORMAT_ARGB2101010_XRBIAS, + PIXEL_FORMAT_FP16, + /*video*/ + PIXEL_FORMAT_420BPP8, + PIXEL_FORMAT_420BPP10, + /*end of pixel format definition*/ + PIXEL_FORMAT_INVALID, + + PIXEL_FORMAT_GRPH_BEGIN = PIXEL_FORMAT_INDEX8, + PIXEL_FORMAT_GRPH_END = PIXEL_FORMAT_FP16, + PIXEL_FORMAT_VIDEO_BEGIN = PIXEL_FORMAT_420BPP8, + PIXEL_FORMAT_VIDEO_END = PIXEL_FORMAT_420BPP10, + PIXEL_FORMAT_UNKNOWN +}; + +enum tile_split_values { + DC_DISPLAY_MICRO_TILING = 0x0, + DC_THIN_MICRO_TILING = 0x1, + DC_DEPTH_MICRO_TILING = 0x2, + DC_ROTATED_MICRO_TILING = 0x3, +}; + +/* TODO: These values come from hardware spec. We need to readdress this + * if they ever change. + */ +enum array_mode_values { + DC_ARRAY_LINEAR_GENERAL = 0, + DC_ARRAY_LINEAR_ALLIGNED, + DC_ARRAY_1D_TILED_THIN1, + DC_ARRAY_1D_TILED_THICK, + DC_ARRAY_2D_TILED_THIN1, + DC_ARRAY_PRT_TILED_THIN1, + DC_ARRAY_PRT_2D_TILED_THIN1, + DC_ARRAY_2D_TILED_THICK, + DC_ARRAY_2D_TILED_X_THICK, + DC_ARRAY_PRT_TILED_THICK, + DC_ARRAY_PRT_2D_TILED_THICK, + DC_ARRAY_PRT_3D_TILED_THIN1, + DC_ARRAY_3D_TILED_THIN1, + DC_ARRAY_3D_TILED_THICK, + DC_ARRAY_3D_TILED_X_THICK, + DC_ARRAY_PRT_3D_TILED_THICK, +}; + +enum tile_mode_values { + DC_ADDR_SURF_MICRO_TILING_DISPLAY = 0x0, + DC_ADDR_SURF_MICRO_TILING_NON_DISPLAY = 0x1, +}; + +enum swizzle_mode_values { + DC_SW_LINEAR = 0, + DC_SW_256B_S = 1, + DC_SW_256_D = 2, + DC_SW_256_R = 3, + DC_SW_4KB_S = 5, + DC_SW_4KB_D = 6, + DC_SW_4KB_R = 7, + DC_SW_64KB_S = 9, + DC_SW_64KB_D = 10, + DC_SW_64KB_R = 11, + DC_SW_VAR_S = 13, + DC_SW_VAR_D = 14, + DC_SW_VAR_R = 15, + DC_SW_64KB_S_T = 17, + DC_SW_64KB_D_T = 18, + DC_SW_4KB_S_X = 21, + DC_SW_4KB_D_X = 22, + DC_SW_4KB_R_X = 23, + DC_SW_64KB_S_X = 25, + DC_SW_64KB_D_X = 26, + DC_SW_64KB_R_X = 27, + DC_SW_VAR_S_X = 29, + DC_SW_VAR_D_X = 30, + DC_SW_VAR_R_X = 31, + DC_SW_MAX +}; + +union dc_tiling_info { + + struct { + /* Specifies the number of memory banks for tiling + * purposes. + * Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: 2,4,8,16 + */ + unsigned int num_banks; + /* Specifies the number of tiles in the x direction + * to be incorporated into the same bank. + * Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: 1,2,4,8 + */ + unsigned int bank_width; + unsigned int bank_width_c; + /* Specifies the number of tiles in the y direction to + * be incorporated into the same bank. + * Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: 1,2,4,8 + */ + unsigned int bank_height; + unsigned int bank_height_c; + /* Specifies the macro tile aspect ratio. Only applies + * to 2D and 3D tiling modes. + */ + unsigned int tile_aspect; + unsigned int tile_aspect_c; + /* Specifies the number of bytes that will be stored + * contiguously for each tile. + * If the tile data requires more storage than this + * amount, it is split into multiple slices. + * This field must not be larger than + * GB_ADDR_CONFIG.DRAM_ROW_SIZE. + * Only applies to 2D and 3D tiling modes. + * For color render targets, TILE_SPLIT >= 256B. + */ + enum tile_split_values tile_split; + enum tile_split_values tile_split_c; + /* Specifies the addressing within a tile. + * 0x0 - DISPLAY_MICRO_TILING + * 0x1 - THIN_MICRO_TILING + * 0x2 - DEPTH_MICRO_TILING + * 0x3 - ROTATED_MICRO_TILING + */ + enum tile_mode_values tile_mode; + enum tile_mode_values tile_mode_c; + /* Specifies the number of pipes and how they are + * interleaved in the surface. + * Refer to memory addressing document for complete + * details and constraints. + */ + unsigned int pipe_config; + /* Specifies the tiling mode of the surface. + * THIN tiles use an 8x8x1 tile size. + * THICK tiles use an 8x8x4 tile size. + * 2D tiling modes rotate banks for successive Z slices + * 3D tiling modes rotate pipes and banks for Z slices + * Refer to memory addressing document for complete + * details and constraints. + */ + enum array_mode_values array_mode; + } gfx8; + + struct { + unsigned int num_pipes; + unsigned int num_banks; + unsigned int pipe_interleave; + unsigned int num_shader_engines; + unsigned int num_rb_per_se; + unsigned int max_compressed_frags; + bool shaderEnable; + + enum swizzle_mode_values swizzle; + bool meta_linear; + bool rb_aligned; + bool pipe_aligned; + } gfx9; +}; + +/* Rotation angle */ +enum dc_rotation_angle { + ROTATION_ANGLE_0 = 0, + ROTATION_ANGLE_90, + ROTATION_ANGLE_180, + ROTATION_ANGLE_270, + ROTATION_ANGLE_COUNT +}; + +enum dc_scan_direction { + SCAN_DIRECTION_UNKNOWN = 0, + SCAN_DIRECTION_HORIZONTAL = 1, /* 0, 180 rotation */ + SCAN_DIRECTION_VERTICAL = 2, /* 90, 270 rotation */ +}; + +struct dc_cursor_position { + uint32_t x; + uint32_t y; + + uint32_t x_hotspot; + uint32_t y_hotspot; + + /* + * This parameter indicates whether HW cursor should be enabled + */ + bool enable; + +}; + +struct dc_cursor_mi_param { + unsigned int pixel_clk_khz; + unsigned int ref_clk_khz; + unsigned int viewport_x_start; + unsigned int viewport_width; + struct fixed31_32 h_scale_ratio; +}; + +/* IPP related types */ + +enum { + GAMMA_RGB_256_ENTRIES = 256, + GAMMA_RGB_FLOAT_1024_ENTRIES = 1024, + GAMMA_MAX_ENTRIES = 1024 +}; + +enum dc_gamma_type { + GAMMA_RGB_256 = 1, + GAMMA_RGB_FLOAT_1024 = 2 +}; + +struct dc_gamma { + struct kref refcount; + enum dc_gamma_type type; + unsigned int num_entries; + + struct dc_gamma_entries { + struct fixed31_32 red[GAMMA_MAX_ENTRIES]; + struct fixed31_32 green[GAMMA_MAX_ENTRIES]; + struct fixed31_32 blue[GAMMA_MAX_ENTRIES]; + } entries; + + /* private to DC core */ + struct dc_context *ctx; +}; + +/* Used by both ipp amd opp functions*/ +/* TODO: to be consolidated with enum color_space */ + +/* + * This enum is for programming CURSOR_MODE register field. What this register + * should be programmed to depends on OS requested cursor shape flags and what + * we stored in the cursor surface. + */ +enum dc_cursor_color_format { + CURSOR_MODE_MONO, + CURSOR_MODE_COLOR_1BIT_AND, + CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA, + CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA +}; + +/* + * This is all the parameters required by DAL in order to update the cursor + * attributes, including the new cursor image surface address, size, hotspot + * location, color format, etc. + */ + +union dc_cursor_attribute_flags { + struct { + uint32_t ENABLE_MAGNIFICATION:1; + uint32_t INVERSE_TRANSPARENT_CLAMPING:1; + uint32_t HORIZONTAL_MIRROR:1; + uint32_t VERTICAL_MIRROR:1; + uint32_t INVERT_PIXEL_DATA:1; + uint32_t ZERO_EXPANSION:1; + uint32_t MIN_MAX_INVERT:1; + uint32_t RESERVED:25; + } bits; + uint32_t value; +}; + +struct dc_cursor_attributes { + PHYSICAL_ADDRESS_LOC address; + uint32_t pitch; + + /* Width and height should correspond to cursor surface width x heigh */ + uint32_t width; + uint32_t height; + + enum dc_cursor_color_format color_format; + + /* In case we support HW Cursor rotation in the future */ + enum dc_rotation_angle rotation_angle; + + union dc_cursor_attribute_flags attribute_flags; +}; + +/* OPP */ + +enum dc_color_space { + COLOR_SPACE_UNKNOWN, + COLOR_SPACE_SRGB, + COLOR_SPACE_SRGB_LIMITED, + COLOR_SPACE_YCBCR601, + COLOR_SPACE_YCBCR709, + COLOR_SPACE_YCBCR601_LIMITED, + COLOR_SPACE_YCBCR709_LIMITED, + COLOR_SPACE_2020_RGB_FULLRANGE, + COLOR_SPACE_2020_RGB_LIMITEDRANGE, + COLOR_SPACE_2020_YCBCR, + COLOR_SPACE_ADOBERGB, +}; + +enum dc_dither_option { + DITHER_OPTION_DEFAULT, + DITHER_OPTION_DISABLE, + DITHER_OPTION_FM6, + DITHER_OPTION_FM8, + DITHER_OPTION_FM10, + DITHER_OPTION_SPATIAL6_FRAME_RANDOM, + DITHER_OPTION_SPATIAL8_FRAME_RANDOM, + DITHER_OPTION_SPATIAL10_FRAME_RANDOM, + DITHER_OPTION_SPATIAL6, + DITHER_OPTION_SPATIAL8, + DITHER_OPTION_SPATIAL10, + DITHER_OPTION_TRUN6, + DITHER_OPTION_TRUN8, + DITHER_OPTION_TRUN10, + DITHER_OPTION_TRUN10_SPATIAL8, + DITHER_OPTION_TRUN10_SPATIAL6, + DITHER_OPTION_TRUN10_FM8, + DITHER_OPTION_TRUN10_FM6, + DITHER_OPTION_TRUN10_SPATIAL8_FM6, + DITHER_OPTION_SPATIAL10_FM8, + DITHER_OPTION_SPATIAL10_FM6, + DITHER_OPTION_TRUN8_SPATIAL6, + DITHER_OPTION_TRUN8_FM6, + DITHER_OPTION_SPATIAL8_FM6, + DITHER_OPTION_MAX = DITHER_OPTION_SPATIAL8_FM6, + DITHER_OPTION_INVALID +}; + +enum dc_quantization_range { + QUANTIZATION_RANGE_UNKNOWN, + QUANTIZATION_RANGE_FULL, + QUANTIZATION_RANGE_LIMITED +}; + +/* XFM */ + +/* used in struct dc_plane_state */ +struct scaling_taps { + uint32_t v_taps; + uint32_t h_taps; + uint32_t v_taps_c; + uint32_t h_taps_c; +}; + +enum dc_timing_standard { + TIMING_STANDARD_UNDEFINED, + TIMING_STANDARD_DMT, + TIMING_STANDARD_GTF, + TIMING_STANDARD_CVT, + TIMING_STANDARD_CVT_RB, + TIMING_STANDARD_CEA770, + TIMING_STANDARD_CEA861, + TIMING_STANDARD_HDMI, + TIMING_STANDARD_TV_NTSC, + TIMING_STANDARD_TV_NTSC_J, + TIMING_STANDARD_TV_PAL, + TIMING_STANDARD_TV_PAL_M, + TIMING_STANDARD_TV_PAL_CN, + TIMING_STANDARD_TV_SECAM, + TIMING_STANDARD_EXPLICIT, + /*!< For explicit timings from EDID, VBIOS, etc.*/ + TIMING_STANDARD_USER_OVERRIDE, + /*!< For mode timing override by user*/ + TIMING_STANDARD_MAX +}; + + + +enum dc_color_depth { + COLOR_DEPTH_UNDEFINED, + COLOR_DEPTH_666, + COLOR_DEPTH_888, + COLOR_DEPTH_101010, + COLOR_DEPTH_121212, + COLOR_DEPTH_141414, + COLOR_DEPTH_161616, + COLOR_DEPTH_COUNT +}; + +enum dc_pixel_encoding { + PIXEL_ENCODING_UNDEFINED, + PIXEL_ENCODING_RGB, + PIXEL_ENCODING_YCBCR422, + PIXEL_ENCODING_YCBCR444, + PIXEL_ENCODING_YCBCR420, + PIXEL_ENCODING_COUNT +}; + +enum dc_aspect_ratio { + ASPECT_RATIO_NO_DATA, + ASPECT_RATIO_4_3, + ASPECT_RATIO_16_9, + ASPECT_RATIO_64_27, + ASPECT_RATIO_256_135, + ASPECT_RATIO_FUTURE +}; + +enum scanning_type { + SCANNING_TYPE_NODATA = 0, + SCANNING_TYPE_OVERSCAN, + SCANNING_TYPE_UNDERSCAN, + SCANNING_TYPE_FUTURE, + SCANNING_TYPE_UNDEFINED +}; + +struct dc_crtc_timing_flags { + uint32_t INTERLACE :1; + uint32_t HSYNC_POSITIVE_POLARITY :1; /* when set to 1, + it is positive polarity --reversed with dal1 or video bios define*/ + uint32_t VSYNC_POSITIVE_POLARITY :1; /* when set to 1, + it is positive polarity --reversed with dal1 or video bios define*/ + + uint32_t HORZ_COUNT_BY_TWO:1; + + uint32_t EXCLUSIVE_3D :1; /* if this bit set, + timing can be driven in 3D format only + and there is no corresponding 2D timing*/ + uint32_t RIGHT_EYE_3D_POLARITY :1; /* 1 - means right eye polarity + (right eye = '1', left eye = '0') */ + uint32_t SUB_SAMPLE_3D :1; /* 1 - means left/right images subsampled + when mixed into 3D image. 0 - means summation (3D timing is doubled)*/ + uint32_t USE_IN_3D_VIEW_ONLY :1; /* Do not use this timing in 2D View, + because corresponding 2D timing also present in the list*/ + uint32_t STEREO_3D_PREFERENCE :1; /* Means this is 2D timing + and we want to match priority of corresponding 3D timing*/ + uint32_t Y_ONLY :1; + + uint32_t YCBCR420 :1; /* TODO: shouldn't need this flag, should be a separate pixel format */ + uint32_t DTD_COUNTER :5; /* values 1 to 16 */ + + uint32_t FORCE_HDR :1; + + /* HDMI 2.0 - Support scrambling for TMDS character + * rates less than or equal to 340Mcsc */ + uint32_t LTE_340MCSC_SCRAMBLE:1; + +}; + +enum dc_timing_3d_format { + TIMING_3D_FORMAT_NONE, + TIMING_3D_FORMAT_FRAME_ALTERNATE, /* No stereosync at all*/ + TIMING_3D_FORMAT_INBAND_FA, /* Inband Frame Alternate (DVI/DP)*/ + TIMING_3D_FORMAT_DP_HDMI_INBAND_FA, /* Inband FA to HDMI Frame Pack*/ + /* for active DP-HDMI dongle*/ + TIMING_3D_FORMAT_SIDEBAND_FA, /* Sideband Frame Alternate (eDP)*/ + TIMING_3D_FORMAT_HW_FRAME_PACKING, + TIMING_3D_FORMAT_SW_FRAME_PACKING, + TIMING_3D_FORMAT_ROW_INTERLEAVE, + TIMING_3D_FORMAT_COLUMN_INTERLEAVE, + TIMING_3D_FORMAT_PIXEL_INTERLEAVE, + TIMING_3D_FORMAT_SIDE_BY_SIDE, + TIMING_3D_FORMAT_TOP_AND_BOTTOM, + TIMING_3D_FORMAT_SBS_SW_PACKED, + /* Side-by-side, packed by application/driver into 2D frame*/ + TIMING_3D_FORMAT_TB_SW_PACKED, + /* Top-and-bottom, packed by application/driver into 2D frame*/ + + TIMING_3D_FORMAT_MAX, +}; + + +struct dc_crtc_timing { + + uint32_t h_total; + uint32_t h_border_left; + uint32_t h_addressable; + uint32_t h_border_right; + uint32_t h_front_porch; + uint32_t h_sync_width; + + uint32_t v_total; + uint32_t v_border_top; + uint32_t v_addressable; + uint32_t v_border_bottom; + uint32_t v_front_porch; + uint32_t v_sync_width; + + uint32_t pix_clk_khz; + + uint32_t vic; + uint32_t hdmi_vic; + enum dc_timing_3d_format timing_3d_format; + enum dc_color_depth display_color_depth; + enum dc_pixel_encoding pixel_encoding; + enum dc_aspect_ratio aspect_ratio; + enum scanning_type scan_type; + + struct dc_crtc_timing_flags flags; +}; + +#define MAX_TG_COLOR_VALUE 0x3FF +struct tg_color { + /* Maximum 10 bits color value */ + uint16_t color_r_cr; + uint16_t color_g_y; + uint16_t color_b_cb; +}; + +#endif /* DC_HW_TYPES_H */ + diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h new file mode 100644 index 000000000000..a8698e399111 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -0,0 +1,652 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef DC_TYPES_H_ +#define DC_TYPES_H_ + +#include "fixed32_32.h" +#include "fixed31_32.h" +#include "irq_types.h" +#include "dc_dp_types.h" +#include "dc_hw_types.h" +#include "dal_types.h" +#include "grph_object_defs.h" + +/* forward declarations */ +struct dc_plane_state; +struct dc_stream_state; +struct dc_link; +struct dc_sink; +struct dal; + +/******************************** + * Environment definitions + ********************************/ +enum dce_environment { + DCE_ENV_PRODUCTION_DRV = 0, + /* Emulation on FPGA, in "Maximus" System. + * This environment enforces that *only* DC registers accessed. + * (access to non-DC registers will hang FPGA) */ + DCE_ENV_FPGA_MAXIMUS, + /* Emulation on real HW or on FPGA. Used by Diagnostics, enforces + * requirements of Diagnostics team. */ + DCE_ENV_DIAG +}; + +/* Note: use these macro definitions instead of direct comparison! */ +#define IS_FPGA_MAXIMUS_DC(dce_environment) \ + (dce_environment == DCE_ENV_FPGA_MAXIMUS) + +#define IS_DIAG_DC(dce_environment) \ + (IS_FPGA_MAXIMUS_DC(dce_environment) || (dce_environment == DCE_ENV_DIAG)) + +struct hw_asic_id { + uint32_t chip_id; + uint32_t chip_family; + uint32_t pci_revision_id; + uint32_t hw_internal_rev; + uint32_t vram_type; + uint32_t vram_width; + uint32_t feature_flags; + uint32_t fake_paths_num; + void *atombios_base_address; +}; + +struct dc_context { + struct dc *dc; + + void *driver_context; /* e.g. amdgpu_device */ + + struct dal_logger *logger; + void *cgs_device; + + enum dce_environment dce_environment; + struct hw_asic_id asic_id; + + /* todo: below should probably move to dc. to facilitate removal + * of AS we will store these here + */ + enum dce_version dce_version; + struct dc_bios *dc_bios; + bool created_bios; + struct gpio_service *gpio_service; + struct i2caux *i2caux; +#if defined(CONFIG_DRM_AMD_DC_FBC) + uint64_t fbc_gpu_addr; +#endif +}; + + +#define MAX_EDID_BUFFER_SIZE 512 +#define EDID_BLOCK_SIZE 128 +#define MAX_SURFACE_NUM 4 +#define NUM_PIXEL_FORMATS 10 + +#include "dc_ddc_types.h" + +enum tiling_mode { + TILING_MODE_INVALID, + TILING_MODE_LINEAR, + TILING_MODE_TILED, + TILING_MODE_COUNT +}; + +enum view_3d_format { + VIEW_3D_FORMAT_NONE = 0, + VIEW_3D_FORMAT_FRAME_SEQUENTIAL, + VIEW_3D_FORMAT_SIDE_BY_SIDE, + VIEW_3D_FORMAT_TOP_AND_BOTTOM, + VIEW_3D_FORMAT_COUNT, + VIEW_3D_FORMAT_FIRST = VIEW_3D_FORMAT_FRAME_SEQUENTIAL +}; + +enum plane_stereo_format { + PLANE_STEREO_FORMAT_NONE = 0, + PLANE_STEREO_FORMAT_SIDE_BY_SIDE = 1, + PLANE_STEREO_FORMAT_TOP_AND_BOTTOM = 2, + PLANE_STEREO_FORMAT_FRAME_ALTERNATE = 3, + PLANE_STEREO_FORMAT_ROW_INTERLEAVED = 5, + PLANE_STEREO_FORMAT_COLUMN_INTERLEAVED = 6, + PLANE_STEREO_FORMAT_CHECKER_BOARD = 7 +}; + +/* TODO: Find way to calculate number of bits + * Please increase if pixel_format enum increases + * num from PIXEL_FORMAT_INDEX8 to PIXEL_FORMAT_444BPP32 + */ + +enum dc_edid_connector_type { + EDID_CONNECTOR_UNKNOWN = 0, + EDID_CONNECTOR_ANALOG = 1, + EDID_CONNECTOR_DIGITAL = 10, + EDID_CONNECTOR_DVI = 11, + EDID_CONNECTOR_HDMIA = 12, + EDID_CONNECTOR_MDDI = 14, + EDID_CONNECTOR_DISPLAYPORT = 15 +}; + +enum dc_edid_status { + EDID_OK, + EDID_BAD_INPUT, + EDID_NO_RESPONSE, + EDID_BAD_CHECKSUM, + EDID_THE_SAME, +}; + +/* audio capability from EDID*/ +struct dc_cea_audio_mode { + uint8_t format_code; /* ucData[0] [6:3]*/ + uint8_t channel_count; /* ucData[0] [2:0]*/ + uint8_t sample_rate; /* ucData[1]*/ + union { + uint8_t sample_size; /* for LPCM*/ + /* for Audio Formats 2-8 (Max bit rate divided by 8 kHz)*/ + uint8_t max_bit_rate; + uint8_t audio_codec_vendor_specific; /* for Audio Formats 9-15*/ + }; +}; + +struct dc_edid { + uint32_t length; + uint8_t raw_edid[MAX_EDID_BUFFER_SIZE]; +}; + +/* When speaker location data block is not available, DEFAULT_SPEAKER_LOCATION + * is used. In this case we assume speaker location are: front left, front + * right and front center. */ +#define DEFAULT_SPEAKER_LOCATION 5 + +#define DC_MAX_AUDIO_DESC_COUNT 16 + +#define AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS 20 + +union display_content_support { + unsigned int raw; + struct { + unsigned int valid_content_type :1; + unsigned int game_content :1; + unsigned int cinema_content :1; + unsigned int photo_content :1; + unsigned int graphics_content :1; + unsigned int reserved :27; + } bits; +}; + +struct dc_edid_caps { + /* sink identification */ + uint16_t manufacturer_id; + uint16_t product_id; + uint32_t serial_number; + uint8_t manufacture_week; + uint8_t manufacture_year; + uint8_t display_name[AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS]; + + /* audio caps */ + uint8_t speaker_flags; + uint32_t audio_mode_count; + struct dc_cea_audio_mode audio_modes[DC_MAX_AUDIO_DESC_COUNT]; + uint32_t audio_latency; + uint32_t video_latency; + + union display_content_support content_support; + + uint8_t qs_bit; + uint8_t qy_bit; + + /*HDMI 2.0 caps*/ + bool lte_340mcsc_scramble; + + bool edid_hdmi; +}; + +struct view { + uint32_t width; + uint32_t height; +}; + +struct dc_mode_flags { + /* note: part of refresh rate flag*/ + uint32_t INTERLACE :1; + /* native display timing*/ + uint32_t NATIVE :1; + /* preferred is the recommended mode, one per display */ + uint32_t PREFERRED :1; + /* true if this mode should use reduced blanking timings + *_not_ related to the Reduced Blanking adjustment*/ + uint32_t REDUCED_BLANKING :1; + /* note: part of refreshrate flag*/ + uint32_t VIDEO_OPTIMIZED_RATE :1; + /* should be reported to upper layers as mode_flags*/ + uint32_t PACKED_PIXEL_FORMAT :1; + /*< preferred view*/ + uint32_t PREFERRED_VIEW :1; + /* this timing should be used only in tiled mode*/ + uint32_t TILED_MODE :1; + uint32_t DSE_MODE :1; + /* Refresh rate divider when Miracast sink is using a + different rate than the output display device + Must be zero for wired displays and non-zero for + Miracast displays*/ + uint32_t MIRACAST_REFRESH_DIVIDER; +}; + + +enum dc_timing_source { + TIMING_SOURCE_UNDEFINED, + + /* explicitly specifed by user, most important*/ + TIMING_SOURCE_USER_FORCED, + TIMING_SOURCE_USER_OVERRIDE, + TIMING_SOURCE_CUSTOM, + TIMING_SOURCE_EXPLICIT, + + /* explicitly specified by the display device, more important*/ + TIMING_SOURCE_EDID_CEA_SVD_3D, + TIMING_SOURCE_EDID_CEA_SVD_PREFERRED, + TIMING_SOURCE_EDID_CEA_SVD_420, + TIMING_SOURCE_EDID_DETAILED, + TIMING_SOURCE_EDID_ESTABLISHED, + TIMING_SOURCE_EDID_STANDARD, + TIMING_SOURCE_EDID_CEA_SVD, + TIMING_SOURCE_EDID_CVT_3BYTE, + TIMING_SOURCE_EDID_4BYTE, + TIMING_SOURCE_VBIOS, + TIMING_SOURCE_CV, + TIMING_SOURCE_TV, + TIMING_SOURCE_HDMI_VIC, + + /* implicitly specified by display device, still safe but less important*/ + TIMING_SOURCE_DEFAULT, + + /* only used for custom base modes */ + TIMING_SOURCE_CUSTOM_BASE, + + /* these timing might not work, least important*/ + TIMING_SOURCE_RANGELIMIT, + TIMING_SOURCE_OS_FORCED, + TIMING_SOURCE_IMPLICIT, + + /* only used by default mode list*/ + TIMING_SOURCE_BASICMODE, + + TIMING_SOURCE_COUNT +}; + + +struct stereo_3d_features { + bool supported ; + bool allTimings ; + bool cloneMode ; + bool scaling ; + bool singleFrameSWPacked; +}; + +enum dc_timing_support_method { + TIMING_SUPPORT_METHOD_UNDEFINED, + TIMING_SUPPORT_METHOD_EXPLICIT, + TIMING_SUPPORT_METHOD_IMPLICIT, + TIMING_SUPPORT_METHOD_NATIVE +}; + +struct dc_mode_info { + uint32_t pixel_width; + uint32_t pixel_height; + uint32_t field_rate; + /* Vertical refresh rate for progressive modes. + * Field rate for interlaced modes.*/ + + enum dc_timing_standard timing_standard; + enum dc_timing_source timing_source; + struct dc_mode_flags flags; +}; + +enum dc_power_state { + DC_POWER_STATE_ON = 1, + DC_POWER_STATE_STANDBY, + DC_POWER_STATE_SUSPEND, + DC_POWER_STATE_OFF +}; + +/* DC PowerStates */ +enum dc_video_power_state { + DC_VIDEO_POWER_UNSPECIFIED = 0, + DC_VIDEO_POWER_ON = 1, + DC_VIDEO_POWER_STANDBY, + DC_VIDEO_POWER_SUSPEND, + DC_VIDEO_POWER_OFF, + DC_VIDEO_POWER_HIBERNATE, + DC_VIDEO_POWER_SHUTDOWN, + DC_VIDEO_POWER_ULPS, /* BACO or Ultra-Light-Power-State */ + DC_VIDEO_POWER_AFTER_RESET, + DC_VIDEO_POWER_MAXIMUM +}; + +enum dc_acpi_cm_power_state { + DC_ACPI_CM_POWER_STATE_D0 = 1, + DC_ACPI_CM_POWER_STATE_D1 = 2, + DC_ACPI_CM_POWER_STATE_D2 = 4, + DC_ACPI_CM_POWER_STATE_D3 = 8 +}; + +enum dc_connection_type { + dc_connection_none, + dc_connection_single, + dc_connection_mst_branch, + dc_connection_active_dongle +}; + +struct dc_csc_adjustments { + struct fixed31_32 contrast; + struct fixed31_32 saturation; + struct fixed31_32 brightness; + struct fixed31_32 hue; +}; + +enum { + MAX_LANES = 2, + MAX_COFUNC_PATH = 6, + LAYER_INDEX_PRIMARY = -1, +}; + +enum dpcd_downstream_port_max_bpc { + DOWN_STREAM_MAX_8BPC = 0, + DOWN_STREAM_MAX_10BPC, + DOWN_STREAM_MAX_12BPC, + DOWN_STREAM_MAX_16BPC +}; +struct dc_dongle_caps { + /* dongle type (DP converter, CV smart dongle) */ + enum display_dongle_type dongle_type; + bool extendedCapValid; + /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, + indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ + bool is_dp_hdmi_s3d_converter; + bool is_dp_hdmi_ycbcr422_pass_through; + bool is_dp_hdmi_ycbcr420_pass_through; + bool is_dp_hdmi_ycbcr422_converter; + bool is_dp_hdmi_ycbcr420_converter; + uint32_t dp_hdmi_max_bpc; + uint32_t dp_hdmi_max_pixel_clk; +}; +/* Scaling format */ +enum scaling_transformation { + SCALING_TRANSFORMATION_UNINITIALIZED, + SCALING_TRANSFORMATION_IDENTITY = 0x0001, + SCALING_TRANSFORMATION_CENTER_TIMING = 0x0002, + SCALING_TRANSFORMATION_FULL_SCREEN_SCALE = 0x0004, + SCALING_TRANSFORMATION_PRESERVE_ASPECT_RATIO_SCALE = 0x0008, + SCALING_TRANSFORMATION_DAL_DECIDE = 0x0010, + SCALING_TRANSFORMATION_INVALID = 0x80000000, + + /* Flag the first and last */ + SCALING_TRANSFORMATION_BEGING = SCALING_TRANSFORMATION_IDENTITY, + SCALING_TRANSFORMATION_END = + SCALING_TRANSFORMATION_PRESERVE_ASPECT_RATIO_SCALE +}; + +enum display_content_type { + DISPLAY_CONTENT_TYPE_NO_DATA = 0, + DISPLAY_CONTENT_TYPE_GRAPHICS = 1, + DISPLAY_CONTENT_TYPE_PHOTO = 2, + DISPLAY_CONTENT_TYPE_CINEMA = 4, + DISPLAY_CONTENT_TYPE_GAME = 8 +}; + +/* audio*/ + +union audio_sample_rates { + struct sample_rates { + uint8_t RATE_32:1; + uint8_t RATE_44_1:1; + uint8_t RATE_48:1; + uint8_t RATE_88_2:1; + uint8_t RATE_96:1; + uint8_t RATE_176_4:1; + uint8_t RATE_192:1; + } rate; + + uint8_t all; +}; + +struct audio_speaker_flags { + uint32_t FL_FR:1; + uint32_t LFE:1; + uint32_t FC:1; + uint32_t RL_RR:1; + uint32_t RC:1; + uint32_t FLC_FRC:1; + uint32_t RLC_RRC:1; + uint32_t SUPPORT_AI:1; +}; + +struct audio_speaker_info { + uint32_t ALLSPEAKERS:7; + uint32_t SUPPORT_AI:1; +}; + + +struct audio_info_flags { + + union { + + struct audio_speaker_flags speaker_flags; + struct audio_speaker_info info; + + uint8_t all; + }; +}; + +enum audio_format_code { + AUDIO_FORMAT_CODE_FIRST = 1, + AUDIO_FORMAT_CODE_LINEARPCM = AUDIO_FORMAT_CODE_FIRST, + + AUDIO_FORMAT_CODE_AC3, + /*Layers 1 & 2 */ + AUDIO_FORMAT_CODE_MPEG1, + /*MPEG1 Layer 3 */ + AUDIO_FORMAT_CODE_MP3, + /*multichannel */ + AUDIO_FORMAT_CODE_MPEG2, + AUDIO_FORMAT_CODE_AAC, + AUDIO_FORMAT_CODE_DTS, + AUDIO_FORMAT_CODE_ATRAC, + AUDIO_FORMAT_CODE_1BITAUDIO, + AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS, + AUDIO_FORMAT_CODE_DTS_HD, + AUDIO_FORMAT_CODE_MAT_MLP, + AUDIO_FORMAT_CODE_DST, + AUDIO_FORMAT_CODE_WMAPRO, + AUDIO_FORMAT_CODE_LAST, + AUDIO_FORMAT_CODE_COUNT = + AUDIO_FORMAT_CODE_LAST - AUDIO_FORMAT_CODE_FIRST +}; + +struct audio_mode { + /* ucData[0] [6:3] */ + enum audio_format_code format_code; + /* ucData[0] [2:0] */ + uint8_t channel_count; + /* ucData[1] */ + union audio_sample_rates sample_rates; + union { + /* for LPCM */ + uint8_t sample_size; + /* for Audio Formats 2-8 (Max bit rate divided by 8 kHz) */ + uint8_t max_bit_rate; + /* for Audio Formats 9-15 */ + uint8_t vendor_specific; + }; +}; + +struct audio_info { + struct audio_info_flags flags; + uint32_t video_latency; + uint32_t audio_latency; + uint32_t display_index; + uint8_t display_name[AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS]; + uint32_t manufacture_id; + uint32_t product_id; + /* PortID used for ContainerID when defined */ + uint32_t port_id[2]; + uint32_t mode_count; + /* this field must be last in this struct */ + struct audio_mode modes[DC_MAX_AUDIO_DESC_COUNT]; +}; + +struct freesync_context { + bool supported; + bool enabled; + bool active; + + unsigned int min_refresh_in_micro_hz; + unsigned int nominal_refresh_in_micro_hz; +}; + +struct psr_config { + unsigned char psr_version; + unsigned int psr_rfb_setup_time; + bool psr_exit_link_training_required; + + bool psr_frame_capture_indication_req; + unsigned int psr_sdp_transmit_line_num_deadline; +}; + +union dmcu_psr_level { + struct { + unsigned int SKIP_CRC:1; + unsigned int SKIP_DP_VID_STREAM_DISABLE:1; + unsigned int SKIP_PHY_POWER_DOWN:1; + unsigned int SKIP_AUX_ACK_CHECK:1; + unsigned int SKIP_CRTC_DISABLE:1; + unsigned int SKIP_AUX_RFB_CAPTURE_CHECK:1; + unsigned int SKIP_SMU_NOTIFICATION:1; + unsigned int SKIP_AUTO_STATE_ADVANCE:1; + unsigned int DISABLE_PSR_ENTRY_ABORT:1; + unsigned int SKIP_SINGLE_OTG_DISABLE:1; + unsigned int RESERVED:22; + } bits; + unsigned int u32all; +}; + +enum physical_phy_id { + PHYLD_0, + PHYLD_1, + PHYLD_2, + PHYLD_3, + PHYLD_4, + PHYLD_5, + PHYLD_6, + PHYLD_7, + PHYLD_8, + PHYLD_9, + PHYLD_COUNT, + PHYLD_UNKNOWN = (-1L) +}; + +enum phy_type { + PHY_TYPE_UNKNOWN = 1, + PHY_TYPE_PCIE_PHY = 2, + PHY_TYPE_UNIPHY = 3, +}; + +struct psr_context { + /* ddc line */ + enum channel_id channel; + /* Transmitter id */ + enum transmitter transmitterId; + /* Engine Id is used for Dig Be source select */ + enum engine_id engineId; + /* Controller Id used for Dig Fe source select */ + enum controller_id controllerId; + /* Pcie or Uniphy */ + enum phy_type phyType; + /* Physical PHY Id used by SMU interpretation */ + enum physical_phy_id smuPhyId; + /* Vertical total pixels from crtc timing. + * This is used for static screen detection. + * ie. If we want to detect half a frame, + * we use this to determine the hyst lines. + */ + unsigned int crtcTimingVerticalTotal; + /* PSR supported from panel capabilities and + * current display configuration + */ + bool psrSupportedDisplayConfig; + /* Whether fast link training is supported by the panel */ + bool psrExitLinkTrainingRequired; + /* If RFB setup time is greater than the total VBLANK time, + * it is not possible for the sink to capture the video frame + * in the same frame the SDP is sent. In this case, + * the frame capture indication bit should be set and an extra + * static frame should be transmitted to the sink. + */ + bool psrFrameCaptureIndicationReq; + /* Set the last possible line SDP may be transmitted without violating + * the RFB setup time or entering the active video frame. + */ + unsigned int sdpTransmitLineNumDeadline; + /* The VSync rate in Hz used to calculate the + * step size for smooth brightness feature + */ + unsigned int vsyncRateHz; + unsigned int skipPsrWaitForPllLock; + unsigned int numberOfControllers; + /* Unused, for future use. To indicate that first changed frame from + * state3 shouldn't result in psr_inactive, but rather to perform + * an automatic single frame rfb_update. + */ + bool rfb_update_auto_en; + /* Number of frame before entering static screen */ + unsigned int timehyst_frames; + /* Partial frames before entering static screen */ + unsigned int hyst_lines; + /* # of repeated AUX transaction attempts to make before + * indicating failure to the driver + */ + unsigned int aux_repeats; + /* Controls hw blocks to power down during PSR active state */ + union dmcu_psr_level psr_level; + /* Controls additional delay after remote frame capture before + * continuing powerd own + */ + unsigned int frame_delay; +}; + +struct colorspace_transform { + struct fixed31_32 matrix[12]; + bool enable_remap; +}; + +struct csc_transform { + uint16_t matrix[12]; + bool enable_adjustment; +}; + +enum i2c_mot_mode { + I2C_MOT_UNDEF, + I2C_MOT_TRUE, + I2C_MOT_FALSE +}; + +#endif /* DC_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/Makefile b/drivers/gpu/drm/amd/display/dc/dce/Makefile new file mode 100644 index 000000000000..8abec0bed379 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for common 'dce' logic +# HW object file under this folder follow similar pattern for HW programming +# - register offset and/or shift + mask stored in the dec_hw struct +# - register programming through common macros that look up register +# offset/shift/mask stored in dce_hw struct + +DCE = dce_audio.o dce_stream_encoder.o dce_link_encoder.o dce_hwseq.o \ +dce_mem_input.o dce_clock_source.o dce_scl_filters.o dce_transform.o \ +dce_clocks.o dce_opp.o dce_dmcu.o dce_abm.o dce_ipp.o + + +AMD_DAL_DCE = $(addprefix $(AMDDALPATH)/dc/dce/,$(DCE)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c new file mode 100644 index 000000000000..0e0336c5af4e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c @@ -0,0 +1,485 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce_abm.h" +#include "dm_services.h" +#include "reg_helper.h" +#include "fixed32_32.h" +#include "dc.h" + +#include "atom.h" + + +#define TO_DCE_ABM(abm)\ + container_of(abm, struct dce_abm, base) + +#define REG(reg) \ + (abm_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + abm_dce->abm_shift->field_name, abm_dce->abm_mask->field_name + +#define CTX \ + abm_dce->base.ctx + +#define MCP_ABM_LEVEL_SET 0x65 +#define MCP_ABM_PIPE_SET 0x66 +#define MCP_BL_SET 0x67 + +#define MCP_DISABLE_ABM_IMMEDIATELY 255 + +struct abm_backlight_registers { + unsigned int BL_PWM_CNTL; + unsigned int BL_PWM_CNTL2; + unsigned int BL_PWM_PERIOD_CNTL; + unsigned int LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV; +}; + +/* registers setting needs to be save and restored used at InitBacklight */ +static struct abm_backlight_registers stored_backlight_registers = {0}; + + +static unsigned int get_current_backlight_16_bit(struct dce_abm *abm_dce) +{ + uint64_t current_backlight; + uint32_t round_result; + uint32_t pwm_period_cntl, bl_period, bl_int_count; + uint32_t bl_pwm_cntl, bl_pwm, fractional_duty_cycle_en; + uint32_t bl_period_mask, bl_pwm_mask; + + pwm_period_cntl = REG_READ(BL_PWM_PERIOD_CNTL); + REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD, &bl_period); + REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD_BITCNT, &bl_int_count); + + bl_pwm_cntl = REG_READ(BL_PWM_CNTL); + REG_GET(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, (uint32_t *)(&bl_pwm)); + REG_GET(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, &fractional_duty_cycle_en); + + if (bl_int_count == 0) + bl_int_count = 16; + + bl_period_mask = (1 << bl_int_count) - 1; + bl_period &= bl_period_mask; + + bl_pwm_mask = bl_period_mask << (16 - bl_int_count); + + if (fractional_duty_cycle_en == 0) + bl_pwm &= bl_pwm_mask; + else + bl_pwm &= 0xFFFF; + + current_backlight = bl_pwm << (1 + bl_int_count); + + if (bl_period == 0) + bl_period = 0xFFFF; + + current_backlight = div_u64(current_backlight, bl_period); + current_backlight = (current_backlight + 1) >> 1; + + current_backlight = (uint64_t)(current_backlight) * bl_period; + + round_result = (uint32_t)(current_backlight & 0xFFFFFFFF); + + round_result = (round_result >> (bl_int_count-1)) & 1; + + current_backlight >>= bl_int_count; + current_backlight += round_result; + + return (uint32_t)(current_backlight); +} + +static void driver_set_backlight_level(struct dce_abm *abm_dce, uint32_t level) +{ + uint32_t backlight_24bit; + uint32_t backlight_17bit; + uint32_t backlight_16bit; + uint32_t masked_pwm_period; + uint8_t rounding_bit; + uint8_t bit_count; + uint64_t active_duty_cycle; + uint32_t pwm_period_bitcnt; + + /* + * 1. Convert 8-bit value to 17 bit U1.16 format + * (1 integer, 16 fractional bits) + */ + + /* 1.1 multiply 8 bit value by 0x10101 to get a 24 bit value, + * effectively multiplying value by 256/255 + * eg. for a level of 0xEF, backlight_24bit = 0xEF * 0x10101 = 0xEFEFEF + */ + backlight_24bit = level * 0x10101; + + /* 1.2 The upper 16 bits of the 24 bit value is the fraction, lower 8 + * used for rounding, take most significant bit of fraction for + * rounding, e.g. for 0xEFEFEF, rounding bit is 1 + */ + rounding_bit = (backlight_24bit >> 7) & 1; + + /* 1.3 Add the upper 16 bits of the 24 bit value with the rounding bit + * resulting in a 17 bit value e.g. 0xEFF0 = (0xEFEFEF >> 8) + 1 + */ + backlight_17bit = (backlight_24bit >> 8) + rounding_bit; + + /* + * 2. Find 16 bit backlight active duty cycle, where 0 <= backlight + * active duty cycle <= backlight period + */ + + /* 2.1 Apply bitmask for backlight period value based on value of BITCNT + */ + REG_GET_2(BL_PWM_PERIOD_CNTL, + BL_PWM_PERIOD_BITCNT, &pwm_period_bitcnt, + BL_PWM_PERIOD, &masked_pwm_period); + + if (pwm_period_bitcnt == 0) + bit_count = 16; + else + bit_count = pwm_period_bitcnt; + + /* e.g. maskedPwmPeriod = 0x24 when bitCount is 6 */ + masked_pwm_period = masked_pwm_period & ((1 << bit_count) - 1); + + /* 2.2 Calculate integer active duty cycle required upper 16 bits + * contain integer component, lower 16 bits contain fractional component + * of active duty cycle e.g. 0x21BDC0 = 0xEFF0 * 0x24 + */ + active_duty_cycle = backlight_17bit * masked_pwm_period; + + /* 2.3 Calculate 16 bit active duty cycle from integer and fractional + * components shift by bitCount then mask 16 bits and add rounding bit + * from MSB of fraction e.g. 0x86F7 = ((0x21BDC0 >> 6) & 0xFFF) + 0 + */ + backlight_16bit = active_duty_cycle >> bit_count; + backlight_16bit &= 0xFFFF; + backlight_16bit += (active_duty_cycle >> (bit_count - 1)) & 0x1; + + /* + * 3. Program register with updated value + */ + + /* 3.1 Lock group 2 backlight registers */ + + REG_UPDATE_2(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN, 1, + BL_PWM_GRP1_REG_LOCK, 1); + + // 3.2 Write new active duty cycle + REG_UPDATE(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, backlight_16bit); + + /* 3.3 Unlock group 2 backlight registers */ + REG_UPDATE(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_LOCK, 0); + + /* 5.4.4 Wait for pending bit to be cleared */ + REG_WAIT(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_UPDATE_PENDING, 0, + 1, 10000); +} + +static void dmcu_set_backlight_level( + struct dce_abm *abm_dce, + uint32_t level, + uint32_t frame_ramp, + uint32_t controller_id) +{ + unsigned int backlight_16_bit = (level * 0x10101) >> 8; + unsigned int backlight_17_bit = backlight_16_bit + + (((backlight_16_bit & 0x80) >> 7) & 1); + uint32_t rampingBoundary = 0xFFFF; + uint32_t s2; + + /* set ramping boundary */ + REG_WRITE(MASTER_COMM_DATA_REG1, rampingBoundary); + + /* setDMCUParam_Pipe */ + REG_UPDATE_2(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, MCP_ABM_PIPE_SET, + MASTER_COMM_CMD_REG_BYTE1, controller_id); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, + 0, 1, 80000); + + /* setDMCUParam_BL */ + REG_UPDATE(BL1_PWM_USER_LEVEL, BL1_PWM_USER_LEVEL, backlight_17_bit); + + /* write ramp */ + if (controller_id == 0) + frame_ramp = 0; + REG_WRITE(MASTER_COMM_DATA_REG1, frame_ramp); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_BL_SET); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* UpdateRequestedBacklightLevel */ + s2 = REG_READ(BIOS_SCRATCH_2); + + s2 &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK; + level &= (ATOM_S2_CURRENT_BL_LEVEL_MASK >> + ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + s2 |= (level << ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + + REG_WRITE(BIOS_SCRATCH_2, s2); +} + +static void dce_abm_init(struct abm *abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + unsigned int backlight = get_current_backlight_16_bit(abm_dce); + + REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x103); + REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x101); + REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x103); + REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x101); + REG_WRITE(BL1_PWM_BL_UPDATE_SAMPLE_RATE, 0x101); + + REG_SET_3(DC_ABM1_HG_MISC_CTRL, 0, + ABM1_HG_NUM_OF_BINS_SEL, 0, + ABM1_HG_VMAX_SEL, 1, + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, 0); + + REG_SET_3(DC_ABM1_IPCSC_COEFF_SEL, 0, + ABM1_IPCSC_COEFF_SEL_R, 2, + ABM1_IPCSC_COEFF_SEL_G, 4, + ABM1_IPCSC_COEFF_SEL_B, 2); + + REG_UPDATE(BL1_PWM_CURRENT_ABM_LEVEL, + BL1_PWM_CURRENT_ABM_LEVEL, backlight); + + REG_UPDATE(BL1_PWM_TARGET_ABM_LEVEL, + BL1_PWM_TARGET_ABM_LEVEL, backlight); + + REG_UPDATE(BL1_PWM_USER_LEVEL, + BL1_PWM_USER_LEVEL, backlight); + + REG_UPDATE_2(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, + ABM1_LS_MIN_PIXEL_VALUE_THRES, 0, + ABM1_LS_MAX_PIXEL_VALUE_THRES, 1000); + + REG_SET_3(DC_ABM1_HGLS_REG_READ_PROGRESS, 0, + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, 1, + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, 1, + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, 1); +} + +static unsigned int dce_abm_get_current_backlight_8_bit(struct abm *abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + unsigned int backlight = REG_READ(BL1_PWM_CURRENT_ABM_LEVEL); + + return (backlight >> 8); +} + +static bool dce_abm_set_level(struct abm *abm, uint32_t level) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + 1, 80000); + + /* setDMCUParam_ABMLevel */ + REG_UPDATE_2(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, MCP_ABM_LEVEL_SET, + MASTER_COMM_CMD_REG_BYTE2, level); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + return true; +} + +static bool dce_abm_immediate_disable(struct abm *abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + 1, 80000); + + /* setDMCUParam_ABMLevel */ + REG_UPDATE_2(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, MCP_ABM_LEVEL_SET, + MASTER_COMM_CMD_REG_BYTE2, MCP_DISABLE_ABM_IMMEDIATELY); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + return true; +} + +static bool dce_abm_init_backlight(struct abm *abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + uint32_t value; + + /* It must not be 0, so we have to restore them + * Bios bug w/a - period resets to zero, + * restoring to cache values which is always correct + */ + REG_GET(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, &value); + if (value == 0 || value == 1) { + if (stored_backlight_registers.BL_PWM_CNTL != 0) { + REG_WRITE(BL_PWM_CNTL, + stored_backlight_registers.BL_PWM_CNTL); + REG_WRITE(BL_PWM_CNTL2, + stored_backlight_registers.BL_PWM_CNTL2); + REG_WRITE(BL_PWM_PERIOD_CNTL, + stored_backlight_registers.BL_PWM_PERIOD_CNTL); + REG_UPDATE(LVTMA_PWRSEQ_REF_DIV, + BL_PWM_REF_DIV, + stored_backlight_registers. + LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); + } else { + /* TODO: Note: This should not really happen since VBIOS + * should have initialized PWM registers on boot. + */ + REG_WRITE(BL_PWM_CNTL, 0xC000FA00); + REG_WRITE(BL_PWM_PERIOD_CNTL, 0x000C0FA0); + } + } else { + stored_backlight_registers.BL_PWM_CNTL = + REG_READ(BL_PWM_CNTL); + stored_backlight_registers.BL_PWM_CNTL2 = + REG_READ(BL_PWM_CNTL2); + stored_backlight_registers.BL_PWM_PERIOD_CNTL = + REG_READ(BL_PWM_PERIOD_CNTL); + + REG_GET(LVTMA_PWRSEQ_REF_DIV, BL_PWM_REF_DIV, + &stored_backlight_registers. + LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); + } + + /* Have driver take backlight control + * TakeBacklightControl(true) + */ + value = REG_READ(BIOS_SCRATCH_2); + value |= ATOM_S2_VRI_BRIGHT_ENABLE; + REG_WRITE(BIOS_SCRATCH_2, value); + + /* Enable the backlight output */ + REG_UPDATE(BL_PWM_CNTL, BL_PWM_EN, 1); + + /* Unlock group 2 backlight registers */ + REG_UPDATE(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_LOCK, 0); + + return true; +} + +static bool is_dmcu_initialized(struct abm *abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + unsigned int dmcu_uc_reset; + + REG_GET(DMCU_STATUS, UC_IN_RESET, &dmcu_uc_reset); + + return !dmcu_uc_reset; +} + +static bool dce_abm_set_backlight_level( + struct abm *abm, + unsigned int backlight_level, + unsigned int frame_ramp, + unsigned int controller_id) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + + dm_logger_write(abm->ctx->logger, LOG_BACKLIGHT, + "New Backlight level: %d (0x%X)\n", + backlight_level, backlight_level); + + /* If DMCU is in reset state, DMCU is uninitialized */ + if (is_dmcu_initialized(abm)) + dmcu_set_backlight_level(abm_dce, + backlight_level, + frame_ramp, + controller_id); + else + driver_set_backlight_level(abm_dce, backlight_level); + + return true; +} + +static const struct abm_funcs dce_funcs = { + .abm_init = dce_abm_init, + .set_abm_level = dce_abm_set_level, + .init_backlight = dce_abm_init_backlight, + .set_backlight_level = dce_abm_set_backlight_level, + .get_current_backlight_8_bit = dce_abm_get_current_backlight_8_bit, + .set_abm_immediate_disable = dce_abm_immediate_disable, + .is_dmcu_initialized = is_dmcu_initialized +}; + +static void dce_abm_construct( + struct dce_abm *abm_dce, + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask) +{ + struct abm *base = &abm_dce->base; + + base->ctx = ctx; + base->funcs = &dce_funcs; + + abm_dce->regs = regs; + abm_dce->abm_shift = abm_shift; + abm_dce->abm_mask = abm_mask; +} + +struct abm *dce_abm_create( + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask) +{ + struct dce_abm *abm_dce = kzalloc(sizeof(*abm_dce), GFP_KERNEL); + + if (abm_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_abm_construct(abm_dce, ctx, regs, abm_shift, abm_mask); + + abm_dce->base.funcs = &dce_funcs; + + return &abm_dce->base; +} + +void dce_abm_destroy(struct abm **abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(*abm); + + kfree(abm_dce); + *abm = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h new file mode 100644 index 000000000000..59e909ec88f2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h @@ -0,0 +1,228 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#ifndef _DCE_ABM_H_ +#define _DCE_ABM_H_ + +#include "abm.h" + +#define ABM_COMMON_REG_LIST_DCE_BASE() \ + SR(BL_PWM_PERIOD_CNTL), \ + SR(BL_PWM_CNTL), \ + SR(BL_PWM_CNTL2), \ + SR(BL_PWM_GRP1_REG_LOCK), \ + SR(LVTMA_PWRSEQ_REF_DIV), \ + SR(MASTER_COMM_CNTL_REG), \ + SR(MASTER_COMM_CMD_REG), \ + SR(MASTER_COMM_DATA_REG1), \ + SR(DMCU_STATUS) + +#define ABM_DCE110_COMMON_REG_LIST() \ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SR(DC_ABM1_HG_SAMPLE_RATE), \ + SR(DC_ABM1_LS_SAMPLE_RATE), \ + SR(BL1_PWM_BL_UPDATE_SAMPLE_RATE), \ + SR(DC_ABM1_HG_MISC_CTRL), \ + SR(DC_ABM1_IPCSC_COEFF_SEL), \ + SR(BL1_PWM_CURRENT_ABM_LEVEL), \ + SR(BL1_PWM_TARGET_ABM_LEVEL), \ + SR(BL1_PWM_USER_LEVEL), \ + SR(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES), \ + SR(DC_ABM1_HGLS_REG_READ_PROGRESS), \ + SR(BIOS_SCRATCH_2) + +#define ABM_DCN10_REG_LIST(id)\ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SRI(DC_ABM1_HG_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_LS_SAMPLE_RATE, ABM, id), \ + SRI(BL1_PWM_BL_UPDATE_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_HG_MISC_CTRL, ABM, id), \ + SRI(DC_ABM1_IPCSC_COEFF_SEL, ABM, id), \ + SRI(BL1_PWM_CURRENT_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_TARGET_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_USER_LEVEL, ABM, id), \ + SRI(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, ABM, id), \ + SRI(DC_ABM1_HGLS_REG_READ_PROGRESS, ABM, id), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define ABM_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define ABM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + ABM_SF(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD, mask_sh), \ + ABM_SF(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD_BITCNT, mask_sh), \ + ABM_SF(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, mask_sh), \ + ABM_SF(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, mask_sh), \ + ABM_SF(BL_PWM_CNTL, BL_PWM_EN, mask_sh), \ + ABM_SF(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN, mask_sh), \ + ABM_SF(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_REG_LOCK, mask_sh), \ + ABM_SF(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_REG_UPDATE_PENDING, mask_sh), \ + ABM_SF(LVTMA_PWRSEQ_REF_DIV, BL_PWM_REF_DIV, mask_sh), \ + ABM_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh), \ + ABM_SF(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, mask_sh), \ + ABM_SF(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE1, mask_sh), \ + ABM_SF(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE2, mask_sh), \ + ABM_SF(DMCU_STATUS, UC_IN_RESET, mask_sh) + +#define ABM_MASK_SH_LIST_DCE110(mask_sh) \ + ABM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + ABM_SF(DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_NUM_OF_BINS_SEL, mask_sh), \ + ABM_SF(DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_VMAX_SEL, mask_sh), \ + ABM_SF(DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, mask_sh), \ + ABM_SF(DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_R, mask_sh), \ + ABM_SF(DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_G, mask_sh), \ + ABM_SF(DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_B, mask_sh), \ + ABM_SF(BL1_PWM_CURRENT_ABM_LEVEL, \ + BL1_PWM_CURRENT_ABM_LEVEL, mask_sh), \ + ABM_SF(BL1_PWM_TARGET_ABM_LEVEL, \ + BL1_PWM_TARGET_ABM_LEVEL, mask_sh), \ + ABM_SF(BL1_PWM_USER_LEVEL, \ + BL1_PWM_USER_LEVEL, mask_sh), \ + ABM_SF(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MIN_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MAX_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, mask_sh) + +#define ABM_MASK_SH_LIST_DCN10(mask_sh) \ + ABM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_NUM_OF_BINS_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_VMAX_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_R, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_G, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_B, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_CURRENT_ABM_LEVEL, \ + BL1_PWM_CURRENT_ABM_LEVEL, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_TARGET_ABM_LEVEL, \ + BL1_PWM_TARGET_ABM_LEVEL, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_USER_LEVEL, \ + BL1_PWM_USER_LEVEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MIN_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MAX_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, mask_sh) + +#define ABM_REG_FIELD_LIST(type) \ + type ABM1_HG_NUM_OF_BINS_SEL; \ + type ABM1_HG_VMAX_SEL; \ + type ABM1_HG_BIN_BITWIDTH_SIZE_SEL; \ + type ABM1_IPCSC_COEFF_SEL_R; \ + type ABM1_IPCSC_COEFF_SEL_G; \ + type ABM1_IPCSC_COEFF_SEL_B; \ + type BL1_PWM_CURRENT_ABM_LEVEL; \ + type BL1_PWM_TARGET_ABM_LEVEL; \ + type BL1_PWM_USER_LEVEL; \ + type ABM1_LS_MIN_PIXEL_VALUE_THRES; \ + type ABM1_LS_MAX_PIXEL_VALUE_THRES; \ + type ABM1_HG_REG_READ_MISSED_FRAME_CLEAR; \ + type ABM1_LS_REG_READ_MISSED_FRAME_CLEAR; \ + type ABM1_BL_REG_READ_MISSED_FRAME_CLEAR; \ + type BL_PWM_PERIOD; \ + type BL_PWM_PERIOD_BITCNT; \ + type BL_ACTIVE_INT_FRAC_CNT; \ + type BL_PWM_FRACTIONAL_EN; \ + type MASTER_COMM_INTERRUPT; \ + type MASTER_COMM_CMD_REG_BYTE0; \ + type MASTER_COMM_CMD_REG_BYTE1; \ + type MASTER_COMM_CMD_REG_BYTE2; \ + type BL_PWM_REF_DIV; \ + type BL_PWM_EN; \ + type UC_IN_RESET; \ + type BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN; \ + type BL_PWM_GRP1_REG_LOCK; \ + type BL_PWM_GRP1_REG_UPDATE_PENDING + +struct dce_abm_shift { + ABM_REG_FIELD_LIST(uint8_t); +}; + +struct dce_abm_mask { + ABM_REG_FIELD_LIST(uint32_t); +}; + +struct dce_abm_registers { + uint32_t BL_PWM_PERIOD_CNTL; + uint32_t BL_PWM_CNTL; + uint32_t BL_PWM_CNTL2; + uint32_t LVTMA_PWRSEQ_REF_DIV; + uint32_t DC_ABM1_HG_SAMPLE_RATE; + uint32_t DC_ABM1_LS_SAMPLE_RATE; + uint32_t BL1_PWM_BL_UPDATE_SAMPLE_RATE; + uint32_t DC_ABM1_HG_MISC_CTRL; + uint32_t DC_ABM1_IPCSC_COEFF_SEL; + uint32_t BL1_PWM_CURRENT_ABM_LEVEL; + uint32_t BL1_PWM_TARGET_ABM_LEVEL; + uint32_t BL1_PWM_USER_LEVEL; + uint32_t DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES; + uint32_t DC_ABM1_HGLS_REG_READ_PROGRESS; + uint32_t MASTER_COMM_CNTL_REG; + uint32_t MASTER_COMM_CMD_REG; + uint32_t MASTER_COMM_DATA_REG1; + uint32_t BIOS_SCRATCH_2; + uint32_t DMCU_STATUS; + uint32_t BL_PWM_GRP1_REG_LOCK; +}; + +struct dce_abm { + struct abm base; + const struct dce_abm_registers *regs; + const struct dce_abm_shift *abm_shift; + const struct dce_abm_mask *abm_mask; +}; + +struct abm *dce_abm_create( + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask); + +void dce_abm_destroy(struct abm **abm); + +#endif /* _DCE_ABM_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c new file mode 100644 index 000000000000..81c40f8864db --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c @@ -0,0 +1,945 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "reg_helper.h" +#include "dce_audio.h" +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define DCE_AUD(audio)\ + container_of(audio, struct dce_audio, base) + +#define CTX \ + aud->base.ctx +#define REG(reg)\ + (aud->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + aud->shifts->field_name, aud->masks->field_name + +#define IX_REG(reg)\ + ix ## reg + +#define AZ_REG_READ(reg_name) \ + read_indirect_azalia_reg(audio, IX_REG(reg_name)) + +#define AZ_REG_WRITE(reg_name, value) \ + write_indirect_azalia_reg(audio, IX_REG(reg_name), value) + +static void write_indirect_azalia_reg(struct audio *audio, + uint32_t reg_index, + uint32_t reg_data) +{ + struct dce_audio *aud = DCE_AUD(audio); + + /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ + REG_SET(AZALIA_F0_CODEC_ENDPOINT_INDEX, 0, + AZALIA_ENDPOINT_REG_INDEX, reg_index); + + /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ + REG_SET(AZALIA_F0_CODEC_ENDPOINT_DATA, 0, + AZALIA_ENDPOINT_REG_DATA, reg_data); + + dm_logger_write(CTX->logger, LOG_HW_AUDIO, + "AUDIO:write_indirect_azalia_reg: index: %u data: %u\n", + reg_index, reg_data); +} + +static uint32_t read_indirect_azalia_reg(struct audio *audio, uint32_t reg_index) +{ + struct dce_audio *aud = DCE_AUD(audio); + + uint32_t value = 0; + + /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ + REG_SET(AZALIA_F0_CODEC_ENDPOINT_INDEX, 0, + AZALIA_ENDPOINT_REG_INDEX, reg_index); + + /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ + value = REG_READ(AZALIA_F0_CODEC_ENDPOINT_DATA); + + dm_logger_write(CTX->logger, LOG_HW_AUDIO, + "AUDIO:read_indirect_azalia_reg: index: %u data: %u\n", + reg_index, value); + + return value; +} + +static bool is_audio_format_supported( + const struct audio_info *audio_info, + enum audio_format_code audio_format_code, + uint32_t *format_index) +{ + uint32_t index; + uint32_t max_channe_index = 0; + bool found = false; + + if (audio_info == NULL) + return found; + + /* pass through whole array */ + for (index = 0; index < audio_info->mode_count; index++) { + if (audio_info->modes[index].format_code == audio_format_code) { + if (found) { + /* format has multiply entries, choose one with + * highst number of channels */ + if (audio_info->modes[index].channel_count > + audio_info->modes[max_channe_index].channel_count) { + max_channe_index = index; + } + } else { + /* format found, save it's index */ + found = true; + max_channe_index = index; + } + } + } + + /* return index */ + if (found && format_index != NULL) + *format_index = max_channe_index; + + return found; +} + +/*For HDMI, calculate if specified sample rates can fit into a given timing */ +static void check_audio_bandwidth_hdmi( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + uint32_t samples; + uint32_t h_blank; + bool limit_freq_to_48_khz = false; + bool limit_freq_to_88_2_khz = false; + bool limit_freq_to_96_khz = false; + bool limit_freq_to_174_4_khz = false; + + /* For two channels supported return whatever sink support,unmodified*/ + if (channel_count > 2) { + + /* Based on HDMI spec 1.3 Table 7.5 */ + if ((crtc_info->requested_pixel_clock <= 27000) && + (crtc_info->v_active <= 576) && + !(crtc_info->interlaced) && + !(crtc_info->pixel_repetition == 2 || + crtc_info->pixel_repetition == 4)) { + limit_freq_to_48_khz = true; + + } else if ((crtc_info->requested_pixel_clock <= 27000) && + (crtc_info->v_active <= 576) && + (crtc_info->interlaced) && + (crtc_info->pixel_repetition == 2)) { + limit_freq_to_88_2_khz = true; + + } else if ((crtc_info->requested_pixel_clock <= 54000) && + (crtc_info->v_active <= 576) && + !(crtc_info->interlaced)) { + limit_freq_to_174_4_khz = true; + } + } + + /* Also do some calculation for the available Audio Bandwidth for the + * 8 ch (i.e. for the Layout 1 => ch > 2) + */ + h_blank = crtc_info->h_total - crtc_info->h_active; + + if (crtc_info->pixel_repetition) + h_blank *= crtc_info->pixel_repetition; + + /*based on HDMI spec 1.3 Table 7.5 */ + h_blank -= 58; + /*for Control Period */ + h_blank -= 16; + + samples = h_blank * 10; + /* Number of Audio Packets (multiplied by 10) per Line (for 8 ch number + * of Audio samples per line multiplied by 10 - Layout 1) + */ + samples /= 32; + samples *= crtc_info->v_active; + /*Number of samples multiplied by 10, per second */ + samples *= crtc_info->refresh_rate; + /*Number of Audio samples per second */ + samples /= 10; + + /* @todo do it after deep color is implemented + * 8xx - deep color bandwidth scaling + * Extra bandwidth is avaliable in deep color b/c link runs faster than + * pixel rate. This has the effect of allowing more tmds characters to + * be transmitted during blank + */ + + switch (crtc_info->color_depth) { + case COLOR_DEPTH_888: + samples *= 4; + break; + case COLOR_DEPTH_101010: + samples *= 5; + break; + case COLOR_DEPTH_121212: + samples *= 6; + break; + default: + samples *= 4; + break; + } + + samples /= 4; + + /*check limitation*/ + if (samples < 88200) + limit_freq_to_48_khz = true; + else if (samples < 96000) + limit_freq_to_88_2_khz = true; + else if (samples < 176400) + limit_freq_to_96_khz = true; + else if (samples < 192000) + limit_freq_to_174_4_khz = true; + + if (sample_rates != NULL) { + /* limit frequencies */ + if (limit_freq_to_174_4_khz) + sample_rates->rate.RATE_192 = 0; + + if (limit_freq_to_96_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + } + if (limit_freq_to_88_2_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + sample_rates->rate.RATE_96 = 0; + } + if (limit_freq_to_48_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + sample_rates->rate.RATE_96 = 0; + sample_rates->rate.RATE_88_2 = 0; + } + } +} + +/*For DP SST, calculate if specified sample rates can fit into a given timing */ +static void check_audio_bandwidth_dpsst( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + /* do nothing */ +} + +/*For DP MST, calculate if specified sample rates can fit into a given timing */ +static void check_audio_bandwidth_dpmst( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + /* do nothing */ +} + +static void check_audio_bandwidth( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + enum signal_type signal, + union audio_sample_rates *sample_rates) +{ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + check_audio_bandwidth_hdmi( + crtc_info, channel_count, sample_rates); + break; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + check_audio_bandwidth_dpsst( + crtc_info, channel_count, sample_rates); + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + check_audio_bandwidth_dpmst( + crtc_info, channel_count, sample_rates); + break; + default: + break; + } +} + +/* expose/not expose HBR capability to Audio driver */ +static void set_high_bit_rate_capable( + struct audio *audio, + bool capable) +{ + uint32_t value = 0; + + /* set high bit rate audio capable*/ + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR); + + set_reg_field_value(value, capable, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, + HBR_CAPABLE); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, value); +} + +/* set video latency in in ms/2+1 */ +static void set_video_latency( + struct audio *audio, + int latency_in_ms) +{ + uint32_t value = 0; + + if ((latency_in_ms < 0) || (latency_in_ms > 255)) + return; + + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); + + set_reg_field_value(value, latency_in_ms, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + VIDEO_LIPSYNC); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + value); +} + +/* set audio latency in in ms/2+1 */ +static void set_audio_latency( + struct audio *audio, + int latency_in_ms) +{ + uint32_t value = 0; + + if (latency_in_ms < 0) + latency_in_ms = 0; + + if (latency_in_ms > 255) + latency_in_ms = 255; + + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); + + set_reg_field_value(value, latency_in_ms, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + AUDIO_LIPSYNC); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + value); +} + +void dce_aud_az_enable(struct audio *audio) +{ + struct dce_audio *aud = DCE_AUD(audio); + uint32_t value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + + dm_logger_write(CTX->logger, LOG_HW_AUDIO, + "\n\t========= AUDIO:dce_aud_az_enable: index: %u data: 0x%x\n", + audio->inst, value); +} + +void dce_aud_az_disable(struct audio *audio) +{ + uint32_t value; + struct dce_audio *aud = DCE_AUD(audio); + + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + dm_logger_write(CTX->logger, LOG_HW_AUDIO, + "\n\t========= AUDIO:dce_aud_az_disable: index: %u data: 0x%x\n", + audio->inst, value); +} + +void dce_aud_az_configure( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_info *audio_info) +{ + struct dce_audio *aud = DCE_AUD(audio); + + uint32_t speakers = audio_info->flags.info.ALLSPEAKERS; + uint32_t value; + uint32_t field = 0; + enum audio_format_code audio_format_code; + uint32_t format_index; + uint32_t index; + bool is_ac3_supported = false; + union audio_sample_rates sample_rate; + uint32_t strlen = 0; + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + + /* Speaker Allocation */ + /* + uint32_t value; + uint32_t field = 0;*/ + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER); + + set_reg_field_value(value, + speakers, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + SPEAKER_ALLOCATION); + + /* LFE_PLAYBACK_LEVEL = LFEPBL + * LFEPBL = 0 : Unknown or refer to other information + * LFEPBL = 1 : 0dB playback + * LFEPBL = 2 : +10dB playback + * LFE_BL = 3 : Reserved + */ + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + LFE_PLAYBACK_LEVEL); + /* todo: according to reg spec LFE_PLAYBACK_LEVEL is read only. + * why are we writing to it? DCE8 does not write this */ + + + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + HDMI_CONNECTION); + + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + DP_CONNECTION); + + field = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + + field &= ~0x1; + + set_reg_field_value(value, + field, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + + /* set audio for output signal */ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + set_reg_field_value(value, + 1, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + HDMI_CONNECTION); + + break; + + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + set_reg_field_value(value, + 1, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + DP_CONNECTION); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, value); + + /* Audio Descriptors */ + /* pass through all formats */ + for (format_index = 0; format_index < AUDIO_FORMAT_CODE_COUNT; + format_index++) { + audio_format_code = + (AUDIO_FORMAT_CODE_FIRST + format_index); + + /* those are unsupported, skip programming */ + if (audio_format_code == AUDIO_FORMAT_CODE_1BITAUDIO || + audio_format_code == AUDIO_FORMAT_CODE_DST) + continue; + + value = 0; + + /* check if supported */ + if (is_audio_format_supported( + audio_info, audio_format_code, &index)) { + const struct audio_mode *audio_mode = + &audio_info->modes[index]; + union audio_sample_rates sample_rates = + audio_mode->sample_rates; + uint8_t byte2 = audio_mode->max_bit_rate; + + /* adjust specific properties */ + switch (audio_format_code) { + case AUDIO_FORMAT_CODE_LINEARPCM: { + check_audio_bandwidth( + crtc_info, + audio_mode->channel_count, + signal, + &sample_rates); + + byte2 = audio_mode->sample_size; + + set_reg_field_value(value, + sample_rates.all, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + SUPPORTED_FREQUENCIES_STEREO); + } + break; + case AUDIO_FORMAT_CODE_AC3: + is_ac3_supported = true; + break; + case AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS: + case AUDIO_FORMAT_CODE_DTS_HD: + case AUDIO_FORMAT_CODE_MAT_MLP: + case AUDIO_FORMAT_CODE_DST: + case AUDIO_FORMAT_CODE_WMAPRO: + byte2 = audio_mode->vendor_specific; + break; + default: + break; + } + + /* fill audio format data */ + set_reg_field_value(value, + audio_mode->channel_count - 1, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + MAX_CHANNELS); + + set_reg_field_value(value, + sample_rates.all, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + SUPPORTED_FREQUENCIES); + + set_reg_field_value(value, + byte2, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + DESCRIPTOR_BYTE_2); + } /* if */ + + AZ_REG_WRITE( + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 + format_index, + value); + } /* for */ + + if (is_ac3_supported) + /* todo: this reg global. why program global register? */ + REG_WRITE(AZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS, + 0x05); + + /* check for 192khz/8-Ch support for HBR requirements */ + sample_rate.all = 0; + sample_rate.rate.RATE_192 = 1; + + check_audio_bandwidth( + crtc_info, + 8, + signal, + &sample_rate); + + set_high_bit_rate_capable(audio, sample_rate.rate.RATE_192); + + /* Audio and Video Lipsync */ + set_video_latency(audio, audio_info->video_latency); + set_audio_latency(audio, audio_info->audio_latency); + + value = 0; + set_reg_field_value(value, audio_info->manufacture_id, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + MANUFACTURER_ID); + + set_reg_field_value(value, audio_info->product_id, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + PRODUCT_ID); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + value); + + value = 0; + + /*get display name string length */ + while (audio_info->display_name[strlen++] != '\0') { + if (strlen >= + MAX_HW_AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS) + break; + } + set_reg_field_value(value, strlen, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, + SINK_DESCRIPTION_LEN); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, + value); + + /* + *write the port ID: + *PORT_ID0 = display index + *PORT_ID1 = 16bit BDF + *(format MSB->LSB: 8bit Bus, 5bit Device, 3bit Function) + */ + + value = 0; + + set_reg_field_value(value, audio_info->port_id[0], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, + PORT_ID0); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, value); + + value = 0; + set_reg_field_value(value, audio_info->port_id[1], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, + PORT_ID1); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, value); + + /*write the 18 char monitor string */ + + value = 0; + set_reg_field_value(value, audio_info->display_name[0], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION0); + + set_reg_field_value(value, audio_info->display_name[1], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION1); + + set_reg_field_value(value, audio_info->display_name[2], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION2); + + set_reg_field_value(value, audio_info->display_name[3], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION3); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[4], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION4); + + set_reg_field_value(value, audio_info->display_name[5], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION5); + + set_reg_field_value(value, audio_info->display_name[6], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION6); + + set_reg_field_value(value, audio_info->display_name[7], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION7); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[8], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION8); + + set_reg_field_value(value, audio_info->display_name[9], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION9); + + set_reg_field_value(value, audio_info->display_name[10], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION10); + + set_reg_field_value(value, audio_info->display_name[11], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION11); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[12], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION12); + + set_reg_field_value(value, audio_info->display_name[13], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION13); + + set_reg_field_value(value, audio_info->display_name[14], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION14); + + set_reg_field_value(value, audio_info->display_name[15], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION15); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[16], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, + DESCRIPTION16); + + set_reg_field_value(value, audio_info->display_name[17], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, + DESCRIPTION17); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, value); +} + +/* +* todo: wall clk related functionality probably belong to clock_src. +*/ + +/* search pixel clock value for Azalia HDMI Audio */ +static void get_azalia_clock_info_hdmi( + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct azalia_clock_info *azalia_clock_info) +{ + /* audio_dto_phase= 24 * 10,000; + * 24MHz in [100Hz] units */ + azalia_clock_info->audio_dto_phase = + 24 * 10000; + + /* audio_dto_module = PCLKFrequency * 10,000; + * [khz] -> [100Hz] */ + azalia_clock_info->audio_dto_module = + actual_pixel_clock_in_khz * 10; +} + +static void get_azalia_clock_info_dp( + uint32_t requested_pixel_clock_in_khz, + const struct audio_pll_info *pll_info, + struct azalia_clock_info *azalia_clock_info) +{ + /* Reported dpDtoSourceClockInkhz value for + * DCE8 already adjusted for SS, do not need any + * adjustment here anymore + */ + + /*audio_dto_phase = 24 * 10,000; + * 24MHz in [100Hz] units */ + azalia_clock_info->audio_dto_phase = 24 * 10000; + + /*audio_dto_module = dpDtoSourceClockInkhz * 10,000; + * [khz] ->[100Hz] */ + azalia_clock_info->audio_dto_module = + pll_info->dp_dto_source_clock_in_khz * 10; +} + +void dce_aud_wall_dto_setup( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info) +{ + struct dce_audio *aud = DCE_AUD(audio); + + struct azalia_clock_info clock_info = { 0 }; + + if (dc_is_hdmi_signal(signal)) { + uint32_t src_sel; + + /*DTO0 Programming goal: + -generate 24MHz, 128*Fs from 24MHz + -use DTO0 when an active HDMI port is connected + (optionally a DP is connected) */ + + /* calculate DTO settings */ + get_azalia_clock_info_hdmi( + crtc_info->requested_pixel_clock, + crtc_info->calculated_pixel_clock, + &clock_info); + + dm_logger_write(audio->ctx->logger, LOG_HW_AUDIO,\ + "\n%s:Input::requested_pixel_clock = %d"\ + "calculated_pixel_clock =%d\n"\ + "audio_dto_module = %d audio_dto_phase =%d \n\n", __func__,\ + crtc_info->requested_pixel_clock,\ + crtc_info->calculated_pixel_clock,\ + clock_info.audio_dto_module,\ + clock_info.audio_dto_phase); + + /* On TN/SI, Program DTO source select and DTO select before + programming DTO modulo and DTO phase. These bits must be + programmed first, otherwise there will be no HDMI audio at boot + up. This is a HW sequence change (different from old ASICs). + Caution when changing this programming sequence. + + HDMI enabled, using DTO0 + program master CRTC for DTO0 */ + src_sel = pll_info->dto_source - DTO_SOURCE_ID0; + REG_UPDATE_2(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO0_SOURCE_SEL, src_sel, + DCCG_AUDIO_DTO_SEL, 0); + + /* module */ + REG_UPDATE(DCCG_AUDIO_DTO0_MODULE, + DCCG_AUDIO_DTO0_MODULE, clock_info.audio_dto_module); + + /* phase */ + REG_UPDATE(DCCG_AUDIO_DTO0_PHASE, + DCCG_AUDIO_DTO0_PHASE, clock_info.audio_dto_phase); + } else { + /*DTO1 Programming goal: + -generate 24MHz, 512*Fs, 128*Fs from 24MHz + -default is to used DTO1, and switch to DTO0 when an audio + master HDMI port is connected + -use as default for DP + + calculate DTO settings */ + get_azalia_clock_info_dp( + crtc_info->requested_pixel_clock, + pll_info, + &clock_info); + + /* Program DTO select before programming DTO modulo and DTO + phase. default to use DTO1 */ + + REG_UPDATE(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO_SEL, 1); + + REG_UPDATE(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO_SEL, 1); + /* DCCG_AUDIO_DTO2_USE_512FBR_DTO, 1) + * Select 512fs for DP TODO: web register definition + * does not match register header file + * DCE11 version it's commented out while DCE8 it's set to 1 + */ + + /* module */ + REG_UPDATE(DCCG_AUDIO_DTO1_MODULE, + DCCG_AUDIO_DTO1_MODULE, clock_info.audio_dto_module); + + /* phase */ + REG_UPDATE(DCCG_AUDIO_DTO1_PHASE, + DCCG_AUDIO_DTO1_PHASE, clock_info.audio_dto_phase); + + REG_UPDATE(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO2_USE_512FBR_DTO, 1); + + } +} + +static bool dce_aud_endpoint_valid(struct audio *audio) +{ + uint32_t value; + uint32_t port_connectivity; + + value = AZ_REG_READ( + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT); + + port_connectivity = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT, + PORT_CONNECTIVITY); + + return !(port_connectivity == 1); +} + +/* initialize HW state */ +void dce_aud_hw_init( + struct audio *audio) +{ + uint32_t value; + struct dce_audio *aud = DCE_AUD(audio); + + /* we only need to program the following registers once, so we only do + it for the inst 0*/ + if (audio->inst != 0) + return; + + /* Suport R5 - 32khz + * Suport R6 - 44.1khz + * Suport R7 - 48khz + */ + /*disable clock gating before write to endpoint register*/ + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + REG_UPDATE(AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, + AUDIO_RATE_CAPABILITIES, 0x70); + + /*Keep alive bit to verify HW block in BU. */ + REG_UPDATE_2(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, + CLKSTOP, 1, + EPSS, 1); +} + +static const struct audio_funcs funcs = { + .endpoint_valid = dce_aud_endpoint_valid, + .hw_init = dce_aud_hw_init, + .wall_dto_setup = dce_aud_wall_dto_setup, + .az_enable = dce_aud_az_enable, + .az_disable = dce_aud_az_disable, + .az_configure = dce_aud_az_configure, + .destroy = dce_aud_destroy, +}; + +void dce_aud_destroy(struct audio **audio) +{ + struct dce_audio *aud = DCE_AUD(*audio); + + kfree(aud); + *audio = NULL; +} + +struct audio *dce_audio_create( + struct dc_context *ctx, + unsigned int inst, + const struct dce_audio_registers *reg, + const struct dce_audio_shift *shifts, + const struct dce_aduio_mask *masks + ) +{ + struct dce_audio *audio = kzalloc(sizeof(*audio), GFP_KERNEL); + + if (audio == NULL) { + ASSERT_CRITICAL(audio); + return NULL; + } + + audio->base.ctx = ctx; + audio->base.inst = inst; + audio->base.funcs = &funcs; + + audio->regs = reg; + audio->shifts = shifts; + audio->masks = masks; + + return &audio->base; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.h b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.h new file mode 100644 index 000000000000..0dc5ff137c7a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.h @@ -0,0 +1,148 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DAL_AUDIO_DCE_110_H__ +#define __DAL_AUDIO_DCE_110_H__ + +#include "audio.h" + +#define AUD_COMMON_REG_LIST(id)\ + SRI(AZALIA_F0_CODEC_ENDPOINT_INDEX, AZF0ENDPOINT, id),\ + SRI(AZALIA_F0_CODEC_ENDPOINT_DATA, AZF0ENDPOINT, id),\ + SR(AZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS),\ + SR(AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES),\ + SR(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES),\ + SR(DCCG_AUDIO_DTO_SOURCE),\ + SR(DCCG_AUDIO_DTO0_MODULE),\ + SR(DCCG_AUDIO_DTO0_PHASE),\ + SR(DCCG_AUDIO_DTO1_MODULE),\ + SR(DCCG_AUDIO_DTO1_PHASE) + + + /* set field name */ +#define SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + + +#define AUD_COMMON_MASK_SH_LIST_BASE(mask_sh)\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO0_SOURCE_SEL, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO_SEL, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO2_USE_512FBR_DTO, mask_sh),\ + SF(DCCG_AUDIO_DTO0_MODULE, DCCG_AUDIO_DTO0_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO0_PHASE, DCCG_AUDIO_DTO0_PHASE, mask_sh),\ + SF(DCCG_AUDIO_DTO1_MODULE, DCCG_AUDIO_DTO1_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO1_PHASE, DCCG_AUDIO_DTO1_PHASE, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, AUDIO_RATE_CAPABILITIES, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, CLKSTOP, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, EPSS, mask_sh) + +#define AUD_COMMON_MASK_SH_LIST(mask_sh)\ + AUD_COMMON_MASK_SH_LIST_BASE(mask_sh),\ + SF(AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh) + + +struct dce_audio_registers { + uint32_t AZALIA_F0_CODEC_ENDPOINT_INDEX; + uint32_t AZALIA_F0_CODEC_ENDPOINT_DATA; + + uint32_t AZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS; + uint32_t AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES; + uint32_t AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES; + + uint32_t DCCG_AUDIO_DTO_SOURCE; + uint32_t DCCG_AUDIO_DTO0_MODULE; + uint32_t DCCG_AUDIO_DTO0_PHASE; + uint32_t DCCG_AUDIO_DTO1_MODULE; + uint32_t DCCG_AUDIO_DTO1_PHASE; + + uint32_t AUDIO_RATE_CAPABILITIES; +}; + +struct dce_audio_shift { + uint8_t AZALIA_ENDPOINT_REG_INDEX; + uint8_t AZALIA_ENDPOINT_REG_DATA; + + uint8_t AUDIO_RATE_CAPABILITIES; + uint8_t CLKSTOP; + uint8_t EPSS; + + uint8_t DCCG_AUDIO_DTO0_SOURCE_SEL; + uint8_t DCCG_AUDIO_DTO_SEL; + uint8_t DCCG_AUDIO_DTO0_MODULE; + uint8_t DCCG_AUDIO_DTO0_PHASE; + uint8_t DCCG_AUDIO_DTO1_MODULE; + uint8_t DCCG_AUDIO_DTO1_PHASE; + uint8_t DCCG_AUDIO_DTO2_USE_512FBR_DTO; +}; + +struct dce_aduio_mask { + uint32_t AZALIA_ENDPOINT_REG_INDEX; + uint32_t AZALIA_ENDPOINT_REG_DATA; + + uint32_t AUDIO_RATE_CAPABILITIES; + uint32_t CLKSTOP; + uint32_t EPSS; + + uint32_t DCCG_AUDIO_DTO0_SOURCE_SEL; + uint32_t DCCG_AUDIO_DTO_SEL; + uint32_t DCCG_AUDIO_DTO0_MODULE; + uint32_t DCCG_AUDIO_DTO0_PHASE; + uint32_t DCCG_AUDIO_DTO1_MODULE; + uint32_t DCCG_AUDIO_DTO1_PHASE; + uint32_t DCCG_AUDIO_DTO2_USE_512FBR_DTO; +}; + +struct dce_audio { + struct audio base; + const struct dce_audio_registers *regs; + const struct dce_audio_shift *shifts; + const struct dce_aduio_mask *masks; +}; + +struct audio *dce_audio_create( + struct dc_context *ctx, + unsigned int inst, + const struct dce_audio_registers *reg, + const struct dce_audio_shift *shifts, + const struct dce_aduio_mask *masks); + +void dce_aud_destroy(struct audio **audio); + +void dce_aud_hw_init(struct audio *audio); + +void dce_aud_az_enable(struct audio *audio); +void dce_aud_az_disable(struct audio *audio); + +void dce_aud_az_configure(struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_info *audio_info); + +void dce_aud_wall_dto_setup(struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info); + +#endif /*__DAL_AUDIO_DCE_110_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c new file mode 100644 index 000000000000..31280d252753 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -0,0 +1,1383 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + + +#include "dc_types.h" +#include "core_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" + +#include "dce_clock_source.h" + +#include "reg_helper.h" + +#define REG(reg)\ + (clk_src->regs->reg) + +#define CTX \ + clk_src->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + clk_src->cs_shift->field_name, clk_src->cs_mask->field_name + +#define FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM 6 +#define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1 +#define MAX_PLL_CALC_ERROR 0xFFFFFFFF + +static const struct spread_spectrum_data *get_ss_data_entry( + struct dce110_clk_src *clk_src, + enum signal_type signal, + uint32_t pix_clk_khz) +{ + + uint32_t entrys_num; + uint32_t i; + struct spread_spectrum_data *ss_parm = NULL; + struct spread_spectrum_data *ret = NULL; + + switch (signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + ss_parm = clk_src->dvi_ss_params; + entrys_num = clk_src->dvi_ss_params_cnt; + break; + + case SIGNAL_TYPE_HDMI_TYPE_A: + ss_parm = clk_src->hdmi_ss_params; + entrys_num = clk_src->hdmi_ss_params_cnt; + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_VIRTUAL: + ss_parm = clk_src->dp_ss_params; + entrys_num = clk_src->dp_ss_params_cnt; + break; + + default: + ss_parm = NULL; + entrys_num = 0; + break; + } + + if (ss_parm == NULL) + return ret; + + for (i = 0; i < entrys_num; ++i, ++ss_parm) { + if (ss_parm->freq_range_khz >= pix_clk_khz) { + ret = ss_parm; + break; + } + } + + return ret; +} + +/** +* Function: calculate_fb_and_fractional_fb_divider +* +* * DESCRIPTION: Calculates feedback and fractional feedback dividers values +* +*PARAMETERS: +* targetPixelClock Desired frequency in 10 KHz +* ref_divider Reference divider (already known) +* postDivider Post Divider (already known) +* feedback_divider_param Pointer where to store +* calculated feedback divider value +* fract_feedback_divider_param Pointer where to store +* calculated fract feedback divider value +* +*RETURNS: +* It fills the locations pointed by feedback_divider_param +* and fract_feedback_divider_param +* It returns - true if feedback divider not 0 +* - false should never happen) +*/ +static bool calculate_fb_and_fractional_fb_divider( + struct calc_pll_clock_source *calc_pll_cs, + uint32_t target_pix_clk_khz, + uint32_t ref_divider, + uint32_t post_divider, + uint32_t *feedback_divider_param, + uint32_t *fract_feedback_divider_param) +{ + uint64_t feedback_divider; + + feedback_divider = + (uint64_t)(target_pix_clk_khz * ref_divider * post_divider); + feedback_divider *= 10; + /* additional factor, since we divide by 10 afterwards */ + feedback_divider *= (uint64_t)(calc_pll_cs->fract_fb_divider_factor); + feedback_divider = div_u64(feedback_divider, calc_pll_cs->ref_freq_khz); + +/*Round to the number of precision + * The following code replace the old code (ullfeedbackDivider + 5)/10 + * for example if the difference between the number + * of fractional feedback decimal point and the fractional FB Divider precision + * is 2 then the equation becomes (ullfeedbackDivider + 5*100) / (10*100))*/ + + feedback_divider += (uint64_t) + (5 * calc_pll_cs->fract_fb_divider_precision_factor); + feedback_divider = + div_u64(feedback_divider, + calc_pll_cs->fract_fb_divider_precision_factor * 10); + feedback_divider *= (uint64_t) + (calc_pll_cs->fract_fb_divider_precision_factor); + + *feedback_divider_param = + div_u64_rem( + feedback_divider, + calc_pll_cs->fract_fb_divider_factor, + fract_feedback_divider_param); + + if (*feedback_divider_param != 0) + return true; + return false; +} + +/** +*calc_fb_divider_checking_tolerance +* +*DESCRIPTION: Calculates Feedback and Fractional Feedback divider values +* for passed Reference and Post divider, checking for tolerance. +*PARAMETERS: +* pll_settings Pointer to structure +* ref_divider Reference divider (already known) +* postDivider Post Divider (already known) +* tolerance Tolerance for Calculated Pixel Clock to be within +* +*RETURNS: +* It fills the PLLSettings structure with PLL Dividers values +* if calculated values are within required tolerance +* It returns - true if eror is within tolerance +* - false if eror is not within tolerance +*/ +static bool calc_fb_divider_checking_tolerance( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings, + uint32_t ref_divider, + uint32_t post_divider, + uint32_t tolerance) +{ + uint32_t feedback_divider; + uint32_t fract_feedback_divider; + uint32_t actual_calculated_clock_khz; + uint32_t abs_err; + uint64_t actual_calc_clk_khz; + + calculate_fb_and_fractional_fb_divider( + calc_pll_cs, + pll_settings->adjusted_pix_clk, + ref_divider, + post_divider, + &feedback_divider, + &fract_feedback_divider); + + /*Actual calculated value*/ + actual_calc_clk_khz = (uint64_t)(feedback_divider * + calc_pll_cs->fract_fb_divider_factor) + + fract_feedback_divider; + actual_calc_clk_khz *= calc_pll_cs->ref_freq_khz; + actual_calc_clk_khz = + div_u64(actual_calc_clk_khz, + ref_divider * post_divider * + calc_pll_cs->fract_fb_divider_factor); + + actual_calculated_clock_khz = (uint32_t)(actual_calc_clk_khz); + + abs_err = (actual_calculated_clock_khz > + pll_settings->adjusted_pix_clk) + ? actual_calculated_clock_khz - + pll_settings->adjusted_pix_clk + : pll_settings->adjusted_pix_clk - + actual_calculated_clock_khz; + + if (abs_err <= tolerance) { + /*found good values*/ + pll_settings->reference_freq = calc_pll_cs->ref_freq_khz; + pll_settings->reference_divider = ref_divider; + pll_settings->feedback_divider = feedback_divider; + pll_settings->fract_feedback_divider = fract_feedback_divider; + pll_settings->pix_clk_post_divider = post_divider; + pll_settings->calculated_pix_clk = + actual_calculated_clock_khz; + pll_settings->vco_freq = + actual_calculated_clock_khz * post_divider; + return true; + } + return false; +} + +static bool calc_pll_dividers_in_range( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings, + uint32_t min_ref_divider, + uint32_t max_ref_divider, + uint32_t min_post_divider, + uint32_t max_post_divider, + uint32_t err_tolerance) +{ + uint32_t ref_divider; + uint32_t post_divider; + uint32_t tolerance; + +/* This is err_tolerance / 10000 = 0.0025 - acceptable error of 0.25% + * This is errorTolerance / 10000 = 0.0001 - acceptable error of 0.01%*/ + tolerance = (pll_settings->adjusted_pix_clk * err_tolerance) / + 10000; + if (tolerance < CALC_PLL_CLK_SRC_ERR_TOLERANCE) + tolerance = CALC_PLL_CLK_SRC_ERR_TOLERANCE; + + for ( + post_divider = max_post_divider; + post_divider >= min_post_divider; + --post_divider) { + for ( + ref_divider = min_ref_divider; + ref_divider <= max_ref_divider; + ++ref_divider) { + if (calc_fb_divider_checking_tolerance( + calc_pll_cs, + pll_settings, + ref_divider, + post_divider, + tolerance)) { + return true; + } + } + } + + return false; +} + +static uint32_t calculate_pixel_clock_pll_dividers( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings) +{ + uint32_t err_tolerance; + uint32_t min_post_divider; + uint32_t max_post_divider; + uint32_t min_ref_divider; + uint32_t max_ref_divider; + + if (pll_settings->adjusted_pix_clk == 0) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "%s Bad requested pixel clock", __func__); + return MAX_PLL_CALC_ERROR; + } + +/* 1) Find Post divider ranges */ + if (pll_settings->pix_clk_post_divider) { + min_post_divider = pll_settings->pix_clk_post_divider; + max_post_divider = pll_settings->pix_clk_post_divider; + } else { + min_post_divider = calc_pll_cs->min_pix_clock_pll_post_divider; + if (min_post_divider * pll_settings->adjusted_pix_clk < + calc_pll_cs->min_vco_khz) { + min_post_divider = calc_pll_cs->min_vco_khz / + pll_settings->adjusted_pix_clk; + if ((min_post_divider * + pll_settings->adjusted_pix_clk) < + calc_pll_cs->min_vco_khz) + min_post_divider++; + } + + max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider; + if (max_post_divider * pll_settings->adjusted_pix_clk + > calc_pll_cs->max_vco_khz) + max_post_divider = calc_pll_cs->max_vco_khz / + pll_settings->adjusted_pix_clk; + } + +/* 2) Find Reference divider ranges + * When SS is enabled, or for Display Port even without SS, + * pll_settings->referenceDivider is not zero. + * So calculate PPLL FB and fractional FB divider + * using the passed reference divider*/ + + if (pll_settings->reference_divider) { + min_ref_divider = pll_settings->reference_divider; + max_ref_divider = pll_settings->reference_divider; + } else { + min_ref_divider = ((calc_pll_cs->ref_freq_khz + / calc_pll_cs->max_pll_input_freq_khz) + > calc_pll_cs->min_pll_ref_divider) + ? calc_pll_cs->ref_freq_khz + / calc_pll_cs->max_pll_input_freq_khz + : calc_pll_cs->min_pll_ref_divider; + + max_ref_divider = ((calc_pll_cs->ref_freq_khz + / calc_pll_cs->min_pll_input_freq_khz) + < calc_pll_cs->max_pll_ref_divider) + ? calc_pll_cs->ref_freq_khz / + calc_pll_cs->min_pll_input_freq_khz + : calc_pll_cs->max_pll_ref_divider; + } + +/* If some parameters are invalid we could have scenario when "min">"max" + * which produced endless loop later. + * We should investigate why we get the wrong parameters. + * But to follow the similar logic when "adjustedPixelClock" is set to be 0 + * it is better to return here than cause system hang/watchdog timeout later. + * ## SVS Wed 15 Jul 2009 */ + + if (min_post_divider > max_post_divider) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "%s Post divider range is invalid", __func__); + return MAX_PLL_CALC_ERROR; + } + + if (min_ref_divider > max_ref_divider) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "%s Reference divider range is invalid", __func__); + return MAX_PLL_CALC_ERROR; + } + +/* 3) Try to find PLL dividers given ranges + * starting with minimal error tolerance. + * Increase error tolerance until PLL dividers found*/ + err_tolerance = MAX_PLL_CALC_ERROR; + + while (!calc_pll_dividers_in_range( + calc_pll_cs, + pll_settings, + min_ref_divider, + max_ref_divider, + min_post_divider, + max_post_divider, + err_tolerance)) + err_tolerance += (err_tolerance > 10) + ? (err_tolerance / 10) + : 1; + + return err_tolerance; +} + +static bool pll_adjust_pix_clk( + struct dce110_clk_src *clk_src, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + uint32_t actual_pix_clk_khz = 0; + uint32_t requested_clk_khz = 0; + struct bp_adjust_pixel_clock_parameters bp_adjust_pixel_clock_params = { + 0 }; + enum bp_result bp_result; + switch (pix_clk_params->signal_type) { + case SIGNAL_TYPE_HDMI_TYPE_A: { + requested_clk_khz = pix_clk_params->requested_pix_clk; + if (pix_clk_params->pixel_encoding != PIXEL_ENCODING_YCBCR422) { + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + requested_clk_khz = (requested_clk_khz * 5) >> 2; + break; /* x1.25*/ + case COLOR_DEPTH_121212: + requested_clk_khz = (requested_clk_khz * 6) >> 2; + break; /* x1.5*/ + case COLOR_DEPTH_161616: + requested_clk_khz = requested_clk_khz * 2; + break; /* x2.0*/ + default: + break; + } + } + actual_pix_clk_khz = requested_clk_khz; + } + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + requested_clk_khz = pix_clk_params->requested_sym_clk; + actual_pix_clk_khz = pix_clk_params->requested_pix_clk; + break; + + default: + requested_clk_khz = pix_clk_params->requested_pix_clk; + actual_pix_clk_khz = pix_clk_params->requested_pix_clk; + break; + } + + bp_adjust_pixel_clock_params.pixel_clock = requested_clk_khz; + bp_adjust_pixel_clock_params. + encoder_object_id = pix_clk_params->encoder_object_id; + bp_adjust_pixel_clock_params.signal_type = pix_clk_params->signal_type; + bp_adjust_pixel_clock_params. + ss_enable = pix_clk_params->flags.ENABLE_SS; + bp_result = clk_src->bios->funcs->adjust_pixel_clock( + clk_src->bios, &bp_adjust_pixel_clock_params); + if (bp_result == BP_RESULT_OK) { + pll_settings->actual_pix_clk = actual_pix_clk_khz; + pll_settings->adjusted_pix_clk = + bp_adjust_pixel_clock_params.adjusted_pixel_clock; + pll_settings->reference_divider = + bp_adjust_pixel_clock_params.reference_divider; + pll_settings->pix_clk_post_divider = + bp_adjust_pixel_clock_params.pixel_clock_post_divider; + + return true; + } + + return false; +} + +/** + * Calculate PLL Dividers for given Clock Value. + * First will call VBIOS Adjust Exec table to check if requested Pixel clock + * will be Adjusted based on usage. + * Then it will calculate PLL Dividers for this Adjusted clock using preferred + * method (Maximum VCO frequency). + * + * \return + * Calculation error in units of 0.01% + */ + +static uint32_t dce110_get_pix_clk_dividers_helper ( + struct dce110_clk_src *clk_src, + struct pll_settings *pll_settings, + struct pixel_clk_params *pix_clk_params) +{ + uint32_t field = 0; + uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; + + /* Check if reference clock is external (not pcie/xtalin) + * HW Dce80 spec: + * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB + * 04 - HSYNCA, 05 - GENLK_CLK, 06 - PCIE_REFCLK, 07 - DVOCLK0 */ + REG_GET(PLL_CNTL, PLL_REF_DIV_SRC, &field); + pll_settings->use_external_clk = (field > 1); + + /* VBIOS by default enables DP SS (spread on IDCLK) for DCE 8.0 always + * (we do not care any more from SI for some older DP Sink which + * does not report SS support, no known issues) */ + if ((pix_clk_params->flags.ENABLE_SS) || + (dc_is_dp_signal(pix_clk_params->signal_type))) { + + const struct spread_spectrum_data *ss_data = get_ss_data_entry( + clk_src, + pix_clk_params->signal_type, + pll_settings->adjusted_pix_clk); + + if (NULL != ss_data) + pll_settings->ss_percentage = ss_data->percentage; + } + + /* Check VBIOS AdjustPixelClock Exec table */ + if (!pll_adjust_pix_clk(clk_src, pix_clk_params, pll_settings)) { + /* Should never happen, ASSERT and fill up values to be able + * to continue. */ + dm_logger_write(clk_src->base.ctx->logger, LOG_ERROR, + "%s: Failed to adjust pixel clock!!", __func__); + pll_settings->actual_pix_clk = + pix_clk_params->requested_pix_clk; + pll_settings->adjusted_pix_clk = + pix_clk_params->requested_pix_clk; + + if (dc_is_dp_signal(pix_clk_params->signal_type)) + pll_settings->adjusted_pix_clk = 100000; + } + + /* Calculate Dividers */ + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) + /*Calculate Dividers by HDMI object, no SS case or SS case */ + pll_calc_error = + calculate_pixel_clock_pll_dividers( + &clk_src->calc_pll_hdmi, + pll_settings); + else + /*Calculate Dividers by default object, no SS case or SS case */ + pll_calc_error = + calculate_pixel_clock_pll_dividers( + &clk_src->calc_pll, + pll_settings); + + return pll_calc_error; +} + +static void dce112_get_pix_clk_dividers_helper ( + struct dce110_clk_src *clk_src, + struct pll_settings *pll_settings, + struct pixel_clk_params *pix_clk_params) +{ + uint32_t actualPixelClockInKHz; + + actualPixelClockInKHz = pix_clk_params->requested_pix_clk; + /* Calculate Dividers */ + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + actualPixelClockInKHz = (actualPixelClockInKHz * 5) >> 2; + break; + case COLOR_DEPTH_121212: + actualPixelClockInKHz = (actualPixelClockInKHz * 6) >> 2; + break; + case COLOR_DEPTH_161616: + actualPixelClockInKHz = actualPixelClockInKHz * 2; + break; + default: + break; + } + } + pll_settings->actual_pix_clk = actualPixelClockInKHz; + pll_settings->adjusted_pix_clk = actualPixelClockInKHz; + pll_settings->calculated_pix_clk = pix_clk_params->requested_pix_clk; +} + +static uint32_t dce110_get_pix_clk_dividers( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); + uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; + + if (pix_clk_params == NULL || pll_settings == NULL + || pix_clk_params->requested_pix_clk == 0) { + dm_logger_write(clk_src->base.ctx->logger, LOG_ERROR, + "%s: Invalid parameters!!\n", __func__); + return pll_calc_error; + } + + memset(pll_settings, 0, sizeof(*pll_settings)); + + if (cs->id == CLOCK_SOURCE_ID_DP_DTO || + cs->id == CLOCK_SOURCE_ID_EXTERNAL) { + pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz; + pll_settings->calculated_pix_clk = clk_src->ext_clk_khz; + pll_settings->actual_pix_clk = + pix_clk_params->requested_pix_clk; + return 0; + } + + switch (cs->ctx->dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + case DCE_VERSION_10_0: + case DCE_VERSION_11_0: + pll_calc_error = + dce110_get_pix_clk_dividers_helper(clk_src, + pll_settings, pix_clk_params); + break; + case DCE_VERSION_11_2: + case DCE_VERSION_12_0: +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case DCN_VERSION_1_0: +#endif + + dce112_get_pix_clk_dividers_helper(clk_src, + pll_settings, pix_clk_params); + break; + default: + break; + } + + return pll_calc_error; +} + +static uint32_t dce110_get_pll_pixel_rate_in_hz( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + struct dc *dc_core = cs->ctx->dc; + struct dc_state *context = dc_core->current_state; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[inst]; + + /* This function need separate to different DCE version, before separate, just use pixel clock */ + return pipe_ctx->stream->phy_pix_clk; + +} + +static uint32_t dce110_get_dp_pixel_rate_from_combo_phy_pll( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + struct dc *dc_core = cs->ctx->dc; + struct dc_state *context = dc_core->current_state; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[inst]; + + /* This function need separate to different DCE version, before separate, just use pixel clock */ + return pipe_ctx->stream->phy_pix_clk; +} + +static uint32_t dce110_get_d_to_pixel_rate_in_hz( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); + int dto_enabled = 0; + struct fixed31_32 pix_rate; + + REG_GET(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, &dto_enabled); + + if (dto_enabled) { + uint32_t phase = 0; + uint32_t modulo = 0; + REG_GET(PHASE[inst], DP_DTO0_PHASE, &phase); + REG_GET(MODULO[inst], DP_DTO0_MODULO, &modulo); + + if (modulo == 0) { + return 0; + } + + pix_rate = dal_fixed31_32_from_int(clk_src->ref_freq_khz); + pix_rate = dal_fixed31_32_mul_int(pix_rate, 1000); + pix_rate = dal_fixed31_32_mul_int(pix_rate, phase); + pix_rate = dal_fixed31_32_div_int(pix_rate, modulo); + + return dal_fixed31_32_round(pix_rate); + } else { + return dce110_get_dp_pixel_rate_from_combo_phy_pll(cs, pix_clk_params, pll_settings); + } +} + +static uint32_t dce110_get_pix_rate_in_hz( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + uint32_t pix_rate = 0; + switch (pix_clk_params->signal_type) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_VIRTUAL: + pix_rate = dce110_get_d_to_pixel_rate_in_hz(cs, pix_clk_params, pll_settings); + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + default: + pix_rate = dce110_get_pll_pixel_rate_in_hz(cs, pix_clk_params, pll_settings); + break; + } + + return pix_rate; +} + +static bool disable_spread_spectrum(struct dce110_clk_src *clk_src) +{ + enum bp_result result; + struct bp_spread_spectrum_parameters bp_ss_params = {0}; + + bp_ss_params.pll_id = clk_src->base.id; + + /*Call ASICControl to process ATOMBIOS Exec table*/ + result = clk_src->bios->funcs->enable_spread_spectrum_on_ppll( + clk_src->bios, + &bp_ss_params, + false); + + return result == BP_RESULT_OK; +} + +static bool calculate_ss( + const struct pll_settings *pll_settings, + const struct spread_spectrum_data *ss_data, + struct delta_sigma_data *ds_data) +{ + struct fixed32_32 fb_div; + struct fixed32_32 ss_amount; + struct fixed32_32 ss_nslip_amount; + struct fixed32_32 ss_ds_frac_amount; + struct fixed32_32 ss_step_size; + struct fixed32_32 modulation_time; + + if (ds_data == NULL) + return false; + if (ss_data == NULL) + return false; + if (ss_data->percentage == 0) + return false; + if (pll_settings == NULL) + return false; + + memset(ds_data, 0, sizeof(struct delta_sigma_data)); + + /* compute SS_AMOUNT_FBDIV & SS_AMOUNT_NFRAC_SLIP & SS_AMOUNT_DSFRAC*/ + /* 6 decimal point support in fractional feedback divider */ + fb_div = dal_fixed32_32_from_fraction( + pll_settings->fract_feedback_divider, 1000000); + fb_div = dal_fixed32_32_add_int(fb_div, pll_settings->feedback_divider); + + ds_data->ds_frac_amount = 0; + /*spreadSpectrumPercentage is in the unit of .01%, + * so have to divided by 100 * 100*/ + ss_amount = dal_fixed32_32_mul( + fb_div, dal_fixed32_32_from_fraction(ss_data->percentage, + 100 * ss_data->percentage_divider)); + ds_data->feedback_amount = dal_fixed32_32_floor(ss_amount); + + ss_nslip_amount = dal_fixed32_32_sub(ss_amount, + dal_fixed32_32_from_int(ds_data->feedback_amount)); + ss_nslip_amount = dal_fixed32_32_mul_int(ss_nslip_amount, 10); + ds_data->nfrac_amount = dal_fixed32_32_floor(ss_nslip_amount); + + ss_ds_frac_amount = dal_fixed32_32_sub(ss_nslip_amount, + dal_fixed32_32_from_int(ds_data->nfrac_amount)); + ss_ds_frac_amount = dal_fixed32_32_mul_int(ss_ds_frac_amount, 65536); + ds_data->ds_frac_amount = dal_fixed32_32_floor(ss_ds_frac_amount); + + /* compute SS_STEP_SIZE_DSFRAC */ + modulation_time = dal_fixed32_32_from_fraction( + pll_settings->reference_freq * 1000, + pll_settings->reference_divider * ss_data->modulation_freq_hz); + + if (ss_data->flags.CENTER_SPREAD) + modulation_time = dal_fixed32_32_div_int(modulation_time, 4); + else + modulation_time = dal_fixed32_32_div_int(modulation_time, 2); + + ss_step_size = dal_fixed32_32_div(ss_amount, modulation_time); + /* SS_STEP_SIZE_DSFRAC_DEC = Int(SS_STEP_SIZE * 2 ^ 16 * 10)*/ + ss_step_size = dal_fixed32_32_mul_int(ss_step_size, 65536 * 10); + ds_data->ds_frac_size = dal_fixed32_32_floor(ss_step_size); + + return true; +} + +static bool enable_spread_spectrum( + struct dce110_clk_src *clk_src, + enum signal_type signal, struct pll_settings *pll_settings) +{ + struct bp_spread_spectrum_parameters bp_params = {0}; + struct delta_sigma_data d_s_data; + const struct spread_spectrum_data *ss_data = NULL; + + ss_data = get_ss_data_entry( + clk_src, + signal, + pll_settings->calculated_pix_clk); + +/* Pixel clock PLL has been programmed to generate desired pixel clock, + * now enable SS on pixel clock */ +/* TODO is it OK to return true not doing anything ??*/ + if (ss_data != NULL && pll_settings->ss_percentage != 0) { + if (calculate_ss(pll_settings, ss_data, &d_s_data)) { + bp_params.ds.feedback_amount = + d_s_data.feedback_amount; + bp_params.ds.nfrac_amount = + d_s_data.nfrac_amount; + bp_params.ds.ds_frac_size = d_s_data.ds_frac_size; + bp_params.ds_frac_amount = + d_s_data.ds_frac_amount; + bp_params.flags.DS_TYPE = 1; + bp_params.pll_id = clk_src->base.id; + bp_params.percentage = ss_data->percentage; + if (ss_data->flags.CENTER_SPREAD) + bp_params.flags.CENTER_SPREAD = 1; + if (ss_data->flags.EXTERNAL_SS) + bp_params.flags.EXTERNAL_SS = 1; + + if (BP_RESULT_OK != + clk_src->bios->funcs-> + enable_spread_spectrum_on_ppll( + clk_src->bios, + &bp_params, + true)) + return false; + } else + return false; + } + return true; +} + +static void dce110_program_pixel_clk_resync( + struct dce110_clk_src *clk_src, + enum signal_type signal_type, + enum dc_color_depth colordepth) +{ + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 0); + /* + 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) + 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) + 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) + 48 bit mode: TMDS clock = 2 x pixel clock (2:1) + */ + if (signal_type != SIGNAL_TYPE_HDMI_TYPE_A) + return; + + switch (colordepth) { + case COLOR_DEPTH_888: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 0); + break; + case COLOR_DEPTH_101010: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 1); + break; + case COLOR_DEPTH_121212: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 2); + break; + case COLOR_DEPTH_161616: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 3); + break; + default: + break; + } +} + +static void dce112_program_pixel_clk_resync( + struct dce110_clk_src *clk_src, + enum signal_type signal_type, + enum dc_color_depth colordepth, + bool enable_ycbcr420) +{ + uint32_t deep_color_cntl = 0; + uint32_t double_rate_enable = 0; + + /* + 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) + 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) + 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) + 48 bit mode: TMDS clock = 2 x pixel clock (2:1) + */ + if (signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { + double_rate_enable = enable_ycbcr420 ? 1 : 0; + + switch (colordepth) { + case COLOR_DEPTH_888: + deep_color_cntl = 0; + break; + case COLOR_DEPTH_101010: + deep_color_cntl = 1; + break; + case COLOR_DEPTH_121212: + deep_color_cntl = 2; + break; + case COLOR_DEPTH_161616: + deep_color_cntl = 3; + break; + default: + break; + } + } + + if (clk_src->cs_mask->PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE) + REG_UPDATE_2(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, deep_color_cntl, + PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, double_rate_enable); + else + REG_UPDATE(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, deep_color_cntl); + +} + +static bool dce110_program_pix_clk( + struct clock_source *clock_source, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + struct bp_pixel_clock_parameters bp_pc_params = {0}; + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (IS_FPGA_MAXIMUS_DC(clock_source->ctx->dce_environment)) { + unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + unsigned dp_dto_ref_kHz = 600000; + /* DPREF clock from FPGA TODO: Does FPGA have this value? */ + unsigned clock_kHz = pll_settings->actual_pix_clk; + + /* For faster simulation, if mode pixe clock less than 290MHz, + * pixel clock can be hard coded to 290Mhz. For 4K mode, pixel clock + * is greater than 500Mhz, need real pixel clock + * clock_kHz = 290000; + */ + /* TODO: un-hardcode when we can set display clock properly*/ + /*clock_kHz = pix_clk_params->requested_pix_clk;*/ + clock_kHz = 290000; + + /* Set DTO values: phase = target clock, modulo = reference clock */ + REG_WRITE(PHASE[inst], clock_kHz); + REG_WRITE(MODULO[inst], dp_dto_ref_kHz); + + /* Enable DTO */ + REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1); + return true; + } +#endif + /* First disable SS + * ATOMBIOS will enable by default SS on PLL for DP, + * do not disable it here + */ + if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL && + !dc_is_dp_signal(pix_clk_params->signal_type) && + clock_source->ctx->dce_version <= DCE_VERSION_11_0) + disable_spread_spectrum(clk_src); + + /*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/ + bp_pc_params.controller_id = pix_clk_params->controller_id; + bp_pc_params.pll_id = clock_source->id; + bp_pc_params.target_pixel_clock = pll_settings->actual_pix_clk; + bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; + bp_pc_params.signal_type = pix_clk_params->signal_type; + + switch (clock_source->ctx->dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + case DCE_VERSION_10_0: + case DCE_VERSION_11_0: + bp_pc_params.reference_divider = pll_settings->reference_divider; + bp_pc_params.feedback_divider = pll_settings->feedback_divider; + bp_pc_params.fractional_feedback_divider = + pll_settings->fract_feedback_divider; + bp_pc_params.pixel_clock_post_divider = + pll_settings->pix_clk_post_divider; + bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC = + pll_settings->use_external_clk; + + if (clk_src->bios->funcs->set_pixel_clock( + clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Enable SS + * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock), + * based on HW display PLL team, SS control settings should be programmed + * during PLL Reset, but they do not have effect + * until SS_EN is asserted.*/ + if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL + && !dc_is_dp_signal(pix_clk_params->signal_type)) { + + if (pix_clk_params->flags.ENABLE_SS) + if (!enable_spread_spectrum(clk_src, + pix_clk_params->signal_type, + pll_settings)) + return false; + + /* Resync deep color DTO */ + dce110_program_pixel_clk_resync(clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth); + } + + break; + case DCE_VERSION_11_2: + case DCE_VERSION_12_0: +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case DCN_VERSION_1_0: +#endif + + if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) { + bp_pc_params.flags.SET_GENLOCK_REF_DIV_SRC = + pll_settings->use_external_clk; + bp_pc_params.flags.SET_XTALIN_REF_SRC = + !pll_settings->use_external_clk; + if (pix_clk_params->flags.SUPPORT_YCBCR420) { + bp_pc_params.flags.SUPPORT_YUV_420 = 1; + } + } + if (clk_src->bios->funcs->set_pixel_clock( + clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Resync deep color DTO */ + if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) + dce112_program_pixel_clk_resync(clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth, + pix_clk_params->flags.SUPPORT_YCBCR420); + break; + default: + break; + } + + return true; +} + +static bool dce110_clock_source_power_down( + struct clock_source *clk_src) +{ + struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src); + enum bp_result bp_result; + struct bp_pixel_clock_parameters bp_pixel_clock_params = {0}; + + if (clk_src->dp_clk_src) + return true; + + /* If Pixel Clock is 0 it means Power Down Pll*/ + bp_pixel_clock_params.controller_id = CONTROLLER_ID_UNDEFINED; + bp_pixel_clock_params.pll_id = clk_src->id; + bp_pixel_clock_params.flags.FORCE_PROGRAMMING_OF_PLL = 1; + + /*Call ASICControl to process ATOMBIOS Exec table*/ + bp_result = dce110_clk_src->bios->funcs->set_pixel_clock( + dce110_clk_src->bios, + &bp_pixel_clock_params); + + return bp_result == BP_RESULT_OK; +} + +/*****************************************/ +/* Constructor */ +/*****************************************/ +static const struct clock_source_funcs dce110_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dce110_program_pix_clk, + .get_pix_clk_dividers = dce110_get_pix_clk_dividers, + .get_pix_rate_in_hz = dce110_get_pix_rate_in_hz +}; + +static void get_ss_info_from_atombios( + struct dce110_clk_src *clk_src, + enum as_signal_type as_signal, + struct spread_spectrum_data *spread_spectrum_data[], + uint32_t *ss_entries_num) +{ + enum bp_result bp_result = BP_RESULT_FAILURE; + struct spread_spectrum_info *ss_info; + struct spread_spectrum_data *ss_data; + struct spread_spectrum_info *ss_info_cur; + struct spread_spectrum_data *ss_data_cur; + uint32_t i; + + if (ss_entries_num == NULL) { + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid entry !!!\n"); + return; + } + if (spread_spectrum_data == NULL) { + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid array pointer!!!\n"); + return; + } + + spread_spectrum_data[0] = NULL; + *ss_entries_num = 0; + + *ss_entries_num = clk_src->bios->funcs->get_ss_entry_number( + clk_src->bios, + as_signal); + + if (*ss_entries_num == 0) + return; + + ss_info = kzalloc(sizeof(struct spread_spectrum_info) * (*ss_entries_num), + GFP_KERNEL); + ss_info_cur = ss_info; + if (ss_info == NULL) + return; + + ss_data = kzalloc(sizeof(struct spread_spectrum_data) * (*ss_entries_num), + GFP_KERNEL); + if (ss_data == NULL) + goto out_free_info; + + for (i = 0, ss_info_cur = ss_info; + i < (*ss_entries_num); + ++i, ++ss_info_cur) { + + bp_result = clk_src->bios->funcs->get_spread_spectrum_info( + clk_src->bios, + as_signal, + i, + ss_info_cur); + + if (bp_result != BP_RESULT_OK) + goto out_free_data; + } + + for (i = 0, ss_info_cur = ss_info, ss_data_cur = ss_data; + i < (*ss_entries_num); + ++i, ++ss_info_cur, ++ss_data_cur) { + + if (ss_info_cur->type.STEP_AND_DELAY_INFO != false) { + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid ATOMBIOS SS Table!!!\n"); + goto out_free_data; + } + + /* for HDMI check SS percentage, + * if it is > 6 (0.06%), the ATOMBIOS table info is invalid*/ + if (as_signal == AS_SIGNAL_TYPE_HDMI + && ss_info_cur->spread_spectrum_percentage > 6){ + /* invalid input, do nothing */ + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid SS percentage "); + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "for HDMI in ATOMBIOS info Table!!!\n"); + continue; + } + if (ss_info_cur->spread_percentage_divider == 1000) { + /* Keep previous precision from ATOMBIOS for these + * in case new precision set by ATOMBIOS for these + * (otherwise all code in DCE specific classes + * for all previous ASICs would need + * to be updated for SS calculations, + * Audio SS compensation and DP DTO SS compensation + * which assumes fixed SS percentage Divider = 100)*/ + ss_info_cur->spread_spectrum_percentage /= 10; + ss_info_cur->spread_percentage_divider = 100; + } + + ss_data_cur->freq_range_khz = ss_info_cur->target_clock_range; + ss_data_cur->percentage = + ss_info_cur->spread_spectrum_percentage; + ss_data_cur->percentage_divider = + ss_info_cur->spread_percentage_divider; + ss_data_cur->modulation_freq_hz = + ss_info_cur->spread_spectrum_range; + + if (ss_info_cur->type.CENTER_MODE) + ss_data_cur->flags.CENTER_SPREAD = 1; + + if (ss_info_cur->type.EXTERNAL) + ss_data_cur->flags.EXTERNAL_SS = 1; + + } + + *spread_spectrum_data = ss_data; + kfree(ss_info); + return; + +out_free_data: + kfree(ss_data); + *ss_entries_num = 0; +out_free_info: + kfree(ss_info); +} + +static void ss_info_from_atombios_create( + struct dce110_clk_src *clk_src) +{ + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_DISPLAY_PORT, + &clk_src->dp_ss_params, + &clk_src->dp_ss_params_cnt); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_HDMI, + &clk_src->hdmi_ss_params, + &clk_src->hdmi_ss_params_cnt); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_DVI, + &clk_src->dvi_ss_params, + &clk_src->dvi_ss_params_cnt); +} + +static bool calc_pll_max_vco_construct( + struct calc_pll_clock_source *calc_pll_cs, + struct calc_pll_clock_source_init_data *init_data) +{ + uint32_t i; + struct dc_firmware_info fw_info = { { 0 } }; + if (calc_pll_cs == NULL || + init_data == NULL || + init_data->bp == NULL) + return false; + + if (init_data->bp->funcs->get_firmware_info( + init_data->bp, + &fw_info) != BP_RESULT_OK) + return false; + + calc_pll_cs->ctx = init_data->ctx; + calc_pll_cs->ref_freq_khz = fw_info.pll_info.crystal_frequency; + calc_pll_cs->min_vco_khz = + fw_info.pll_info.min_output_pxl_clk_pll_frequency; + calc_pll_cs->max_vco_khz = + fw_info.pll_info.max_output_pxl_clk_pll_frequency; + + if (init_data->max_override_input_pxl_clk_pll_freq_khz != 0) + calc_pll_cs->max_pll_input_freq_khz = + init_data->max_override_input_pxl_clk_pll_freq_khz; + else + calc_pll_cs->max_pll_input_freq_khz = + fw_info.pll_info.max_input_pxl_clk_pll_frequency; + + if (init_data->min_override_input_pxl_clk_pll_freq_khz != 0) + calc_pll_cs->min_pll_input_freq_khz = + init_data->min_override_input_pxl_clk_pll_freq_khz; + else + calc_pll_cs->min_pll_input_freq_khz = + fw_info.pll_info.min_input_pxl_clk_pll_frequency; + + calc_pll_cs->min_pix_clock_pll_post_divider = + init_data->min_pix_clk_pll_post_divider; + calc_pll_cs->max_pix_clock_pll_post_divider = + init_data->max_pix_clk_pll_post_divider; + calc_pll_cs->min_pll_ref_divider = + init_data->min_pll_ref_divider; + calc_pll_cs->max_pll_ref_divider = + init_data->max_pll_ref_divider; + + if (init_data->num_fract_fb_divider_decimal_point == 0 || + init_data->num_fract_fb_divider_decimal_point_precision > + init_data->num_fract_fb_divider_decimal_point) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "The dec point num or precision is incorrect!"); + return false; + } + if (init_data->num_fract_fb_divider_decimal_point_precision == 0) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "Incorrect fract feedback divider precision num!"); + return false; + } + + calc_pll_cs->fract_fb_divider_decimal_points_num = + init_data->num_fract_fb_divider_decimal_point; + calc_pll_cs->fract_fb_divider_precision = + init_data->num_fract_fb_divider_decimal_point_precision; + calc_pll_cs->fract_fb_divider_factor = 1; + for (i = 0; i < calc_pll_cs->fract_fb_divider_decimal_points_num; ++i) + calc_pll_cs->fract_fb_divider_factor *= 10; + + calc_pll_cs->fract_fb_divider_precision_factor = 1; + for ( + i = 0; + i < (calc_pll_cs->fract_fb_divider_decimal_points_num - + calc_pll_cs->fract_fb_divider_precision); + ++i) + calc_pll_cs->fract_fb_divider_precision_factor *= 10; + + return true; +} + +bool dce110_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + const struct dce110_clk_src_shift *cs_shift, + const struct dce110_clk_src_mask *cs_mask) +{ + struct dc_firmware_info fw_info = { { 0 } }; + struct calc_pll_clock_source_init_data calc_pll_cs_init_data_hdmi; + struct calc_pll_clock_source_init_data calc_pll_cs_init_data; + + clk_src->base.ctx = ctx; + clk_src->bios = bios; + clk_src->base.id = id; + clk_src->base.funcs = &dce110_clk_src_funcs; + + clk_src->regs = regs; + clk_src->cs_shift = cs_shift; + clk_src->cs_mask = cs_mask; + + if (clk_src->bios->funcs->get_firmware_info( + clk_src->bios, &fw_info) != BP_RESULT_OK) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + clk_src->ext_clk_khz = + fw_info.external_clock_source_frequency_for_dp; + + switch (clk_src->base.ctx->dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + case DCE_VERSION_10_0: + case DCE_VERSION_11_0: + + /* structure normally used with PLL ranges from ATOMBIOS; DS on by default */ + calc_pll_cs_init_data.bp = bios; + calc_pll_cs_init_data.min_pix_clk_pll_post_divider = 1; + calc_pll_cs_init_data.max_pix_clk_pll_post_divider = + clk_src->cs_mask->PLL_POST_DIV_PIXCLK; + calc_pll_cs_init_data.min_pll_ref_divider = 1; + calc_pll_cs_init_data.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; + /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data.min_override_input_pxl_clk_pll_freq_khz = 0; + /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data.max_override_input_pxl_clk_pll_freq_khz = 0; + /*numberOfFractFBDividerDecimalPoints*/ + calc_pll_cs_init_data.num_fract_fb_divider_decimal_point = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + /*number of decimal point to round off for fractional feedback divider value*/ + calc_pll_cs_init_data.num_fract_fb_divider_decimal_point_precision = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + calc_pll_cs_init_data.ctx = ctx; + + /*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */ + calc_pll_cs_init_data_hdmi.bp = bios; + calc_pll_cs_init_data_hdmi.min_pix_clk_pll_post_divider = 1; + calc_pll_cs_init_data_hdmi.max_pix_clk_pll_post_divider = + clk_src->cs_mask->PLL_POST_DIV_PIXCLK; + calc_pll_cs_init_data_hdmi.min_pll_ref_divider = 1; + calc_pll_cs_init_data_hdmi.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; + /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data_hdmi.min_override_input_pxl_clk_pll_freq_khz = 13500; + /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data_hdmi.max_override_input_pxl_clk_pll_freq_khz = 27000; + /*numberOfFractFBDividerDecimalPoints*/ + calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + /*number of decimal point to round off for fractional feedback divider value*/ + calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point_precision = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + calc_pll_cs_init_data_hdmi.ctx = ctx; + + clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; + + if (clk_src->base.id == CLOCK_SOURCE_ID_EXTERNAL) + return true; + + /* PLL only from here on */ + ss_info_from_atombios_create(clk_src); + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll, + &calc_pll_cs_init_data)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + + calc_pll_cs_init_data_hdmi. + min_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz/2; + calc_pll_cs_init_data_hdmi. + max_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz; + + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + break; + default: + break; + } + + return true; + +unexpected_failure: + return false; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h new file mode 100644 index 000000000000..c45e2f76189e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h @@ -0,0 +1,145 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_CLOCK_SOURCE_DCE_H__ +#define __DC_CLOCK_SOURCE_DCE_H__ + +#include "../inc/clock_source.h" + +#define TO_DCE110_CLK_SRC(clk_src)\ + container_of(clk_src, struct dce110_clk_src, base) + +#define CS_COMMON_REG_LIST_DCE_100_110(id) \ + SRI(RESYNC_CNTL, PIXCLK, id), \ + SRI(PLL_CNTL, BPHYC_PLL, id) + +#define CS_COMMON_REG_LIST_DCE_80(id) \ + SRI(RESYNC_CNTL, PIXCLK, id), \ + SRI(PLL_CNTL, DCCG_PLL, id) + +#define CS_COMMON_REG_LIST_DCE_112(id) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, id) + + +#define CS_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + CS_SF(PLL_CNTL, PLL_REF_DIV_SRC, mask_sh),\ + CS_SF(PIXCLK1_RESYNC_CNTL, DCCG_DEEP_COLOR_CNTL1, mask_sh),\ + CS_SF(PLL_POST_DIV, PLL_POST_DIV_PIXCLK, mask_sh),\ + CS_SF(PLL_REF_DIV, PLL_REF_DIV, mask_sh) + +#define CS_COMMON_MASK_SH_LIST_DCE_112(mask_sh)\ + CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_DCCG_DEEP_COLOR_CNTL, mask_sh),\ + CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, mask_sh) + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + +#define CS_COMMON_REG_LIST_DCN1_0(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(PHASE, DP_DTO, 2),\ + SRII(PHASE, DP_DTO, 3),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 2),\ + SRII(MODULO, DP_DTO, 3),\ + SRII(PIXEL_RATE_CNTL, OTG, 0), \ + SRII(PIXEL_RATE_CNTL, OTG, 1), \ + SRII(PIXEL_RATE_CNTL, OTG, 2), \ + SRII(PIXEL_RATE_CNTL, OTG, 3) + +#define CS_COMMON_MASK_SH_LIST_DCN1_0(mask_sh)\ + CS_SF(DP_DTO0_PHASE, DP_DTO0_PHASE, mask_sh),\ + CS_SF(DP_DTO0_MODULO, DP_DTO0_MODULO, mask_sh),\ + CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_DCCG_DEEP_COLOR_CNTL, mask_sh),\ + CS_SF(OTG0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE, mask_sh) + +#endif + +#define CS_REG_FIELD_LIST(type) \ + type PLL_REF_DIV_SRC; \ + type DCCG_DEEP_COLOR_CNTL1; \ + type PHYPLLA_DCCG_DEEP_COLOR_CNTL; \ + type PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE; \ + type PLL_POST_DIV_PIXCLK; \ + type PLL_REF_DIV; \ + type DP_DTO0_PHASE; \ + type DP_DTO0_MODULO; \ + type DP_DTO0_ENABLE; + +struct dce110_clk_src_shift { + CS_REG_FIELD_LIST(uint8_t) +}; + +struct dce110_clk_src_mask{ + CS_REG_FIELD_LIST(uint32_t) +}; + +struct dce110_clk_src_regs { + uint32_t RESYNC_CNTL; + uint32_t PIXCLK_RESYNC_CNTL; + uint32_t PLL_CNTL; + + /* below are for DTO. + * todo: should probably use different struct to not waste space + */ + uint32_t PHASE[MAX_PIPES]; + uint32_t MODULO[MAX_PIPES]; + uint32_t PIXEL_RATE_CNTL[MAX_PIPES]; +}; + +struct dce110_clk_src { + struct clock_source base; + const struct dce110_clk_src_regs *regs; + const struct dce110_clk_src_mask *cs_mask; + const struct dce110_clk_src_shift *cs_shift; + struct dc_bios *bios; + + struct spread_spectrum_data *dp_ss_params; + uint32_t dp_ss_params_cnt; + struct spread_spectrum_data *hdmi_ss_params; + uint32_t hdmi_ss_params_cnt; + struct spread_spectrum_data *dvi_ss_params; + uint32_t dvi_ss_params_cnt; + + uint32_t ext_clk_khz; + uint32_t ref_freq_khz; + + struct calc_pll_clock_source calc_pll; + struct calc_pll_clock_source calc_pll_hdmi; +}; + +bool dce110_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id, + const struct dce110_clk_src_regs *regs, + const struct dce110_clk_src_shift *cs_shift, + const struct dce110_clk_src_mask *cs_mask); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.c new file mode 100644 index 000000000000..9031d22285ea --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.c @@ -0,0 +1,827 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce_clocks.h" +#include "dm_services.h" +#include "reg_helper.h" +#include "fixed32_32.h" +#include "bios_parser_interface.h" +#include "dc.h" +#include "dce_abm.h" +#include "dmcu.h" +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "dcn_calcs.h" +#endif +#include "core_types.h" + + +#define TO_DCE_CLOCKS(clocks)\ + container_of(clocks, struct dce_disp_clk, base) + +#define REG(reg) \ + (clk_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + clk_dce->clk_shift->field_name, clk_dce->clk_mask->field_name + +#define CTX \ + clk_dce->base.ctx + +/* Max clock values for each state indexed by "enum clocks_state": */ +static const struct state_dependent_clocks dce80_max_clks_by_state[] = { +/* ClocksStateInvalid - should not be used */ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/* ClocksStateUltraLow - not expected to be used for DCE 8.0 */ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/* ClocksStateLow */ +{ .display_clk_khz = 352000, .pixel_clk_khz = 330000}, +/* ClocksStateNominal */ +{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 }, +/* ClocksStatePerformance */ +{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } }; + +static const struct state_dependent_clocks dce110_max_clks_by_state[] = { +/*ClocksStateInvalid - should not be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ +{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, +/*ClocksStateLow*/ +{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, +/*ClocksStateNominal*/ +{ .display_clk_khz = 467000, .pixel_clk_khz = 400000 }, +/*ClocksStatePerformance*/ +{ .display_clk_khz = 643000, .pixel_clk_khz = 400000 } }; + +static const struct state_dependent_clocks dce112_max_clks_by_state[] = { +/*ClocksStateInvalid - should not be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ +{ .display_clk_khz = 389189, .pixel_clk_khz = 346672 }, +/*ClocksStateLow*/ +{ .display_clk_khz = 459000, .pixel_clk_khz = 400000 }, +/*ClocksStateNominal*/ +{ .display_clk_khz = 667000, .pixel_clk_khz = 600000 }, +/*ClocksStatePerformance*/ +{ .display_clk_khz = 1132000, .pixel_clk_khz = 600000 } }; + +static const struct state_dependent_clocks dce120_max_clks_by_state[] = { +/*ClocksStateInvalid - should not be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateLow*/ +{ .display_clk_khz = 460000, .pixel_clk_khz = 400000 }, +/*ClocksStateNominal*/ +{ .display_clk_khz = 670000, .pixel_clk_khz = 600000 }, +/*ClocksStatePerformance*/ +{ .display_clk_khz = 1133000, .pixel_clk_khz = 600000 } }; + +/* Starting point for each divider range.*/ +enum dce_divider_range_start { + DIVIDER_RANGE_01_START = 200, /* 2.00*/ + DIVIDER_RANGE_02_START = 1600, /* 16.00*/ + DIVIDER_RANGE_03_START = 3200, /* 32.00*/ + DIVIDER_RANGE_SCALE_FACTOR = 100 /* Results are scaled up by 100.*/ +}; + +/* Ranges for divider identifiers (Divider ID or DID) + mmDENTIST_DISPCLK_CNTL.DENTIST_DISPCLK_WDIVIDER*/ +enum dce_divider_id_register_setting { + DIVIDER_RANGE_01_BASE_DIVIDER_ID = 0X08, + DIVIDER_RANGE_02_BASE_DIVIDER_ID = 0X40, + DIVIDER_RANGE_03_BASE_DIVIDER_ID = 0X60, + DIVIDER_RANGE_MAX_DIVIDER_ID = 0X80 +}; + +/* Step size between each divider within a range. + Incrementing the DENTIST_DISPCLK_WDIVIDER by one + will increment the divider by this much.*/ +enum dce_divider_range_step_size { + DIVIDER_RANGE_01_STEP_SIZE = 25, /* 0.25*/ + DIVIDER_RANGE_02_STEP_SIZE = 50, /* 0.50*/ + DIVIDER_RANGE_03_STEP_SIZE = 100 /* 1.00 */ +}; + +static bool dce_divider_range_construct( + struct dce_divider_range *div_range, + int range_start, + int range_step, + int did_min, + int did_max) +{ + div_range->div_range_start = range_start; + div_range->div_range_step = range_step; + div_range->did_min = did_min; + div_range->did_max = did_max; + + if (div_range->div_range_step == 0) { + div_range->div_range_step = 1; + /*div_range_step cannot be zero*/ + BREAK_TO_DEBUGGER(); + } + /* Calculate this based on the other inputs.*/ + /* See DividerRange.h for explanation of */ + /* the relationship between divider id (DID) and a divider.*/ + /* Number of Divider IDs = (Maximum Divider ID - Minimum Divider ID)*/ + /* Maximum divider identified in this range = + * (Number of Divider IDs)*Step size between dividers + * + The start of this range.*/ + div_range->div_range_end = (did_max - did_min) * range_step + + range_start; + return true; +} + +static int dce_divider_range_calc_divider( + struct dce_divider_range *div_range, + int did) +{ + /* Is this DID within our range?*/ + if ((did < div_range->did_min) || (did >= div_range->did_max)) + return INVALID_DIVIDER; + + return ((did - div_range->did_min) * div_range->div_range_step) + + div_range->div_range_start; + +} + +static int dce_divider_range_get_divider( + struct dce_divider_range *div_range, + int ranges_num, + int did) +{ + int div = INVALID_DIVIDER; + int i; + + for (i = 0; i < ranges_num; i++) { + /* Calculate divider with given divider ID*/ + div = dce_divider_range_calc_divider(&div_range[i], did); + /* Found a valid return divider*/ + if (div != INVALID_DIVIDER) + break; + } + return div; +} + +static int dce_clocks_get_dp_ref_freq(struct display_clock *clk) +{ + struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); + int dprefclk_wdivider; + int dprefclk_src_sel; + int dp_ref_clk_khz = 600000; + int target_div = INVALID_DIVIDER; + + /* ASSERT DP Reference Clock source is from DFS*/ + REG_GET(DPREFCLK_CNTL, DPREFCLK_SRC_SEL, &dprefclk_src_sel); + ASSERT(dprefclk_src_sel == 0); + + /* Read the mmDENTIST_DISPCLK_CNTL to get the currently + * programmed DID DENTIST_DPREFCLK_WDIVIDER*/ + REG_GET(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, &dprefclk_wdivider); + + /* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/ + target_div = dce_divider_range_get_divider( + clk_dce->divider_ranges, + DIVIDER_RANGE_MAX, + dprefclk_wdivider); + + if (target_div != INVALID_DIVIDER) { + /* Calculate the current DFS clock, in kHz.*/ + dp_ref_clk_khz = (DIVIDER_RANGE_SCALE_FACTOR + * clk_dce->dentist_vco_freq_khz) / target_div; + } + + /* SW will adjust DP REF Clock average value for all purposes + * (DP DTO / DP Audio DTO and DP GTC) + if clock is spread for all cases: + -if SS enabled on DP Ref clock and HW de-spreading enabled with SW + calculations for DS_INCR/DS_MODULO (this is planned to be default case) + -if SS enabled on DP Ref clock and HW de-spreading enabled with HW + calculations (not planned to be used, but average clock should still + be valid) + -if SS enabled on DP Ref clock and HW de-spreading disabled + (should not be case with CIK) then SW should program all rates + generated according to average value (case as with previous ASICs) + */ + if (clk_dce->ss_on_dprefclk && clk_dce->dprefclk_ss_divider != 0) { + struct fixed32_32 ss_percentage = dal_fixed32_32_div_int( + dal_fixed32_32_from_fraction( + clk_dce->dprefclk_ss_percentage, + clk_dce->dprefclk_ss_divider), 200); + struct fixed32_32 adj_dp_ref_clk_khz; + + ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one, + ss_percentage); + adj_dp_ref_clk_khz = + dal_fixed32_32_mul_int( + ss_percentage, + dp_ref_clk_khz); + dp_ref_clk_khz = dal_fixed32_32_floor(adj_dp_ref_clk_khz); + } + + return dp_ref_clk_khz; +} + +/* TODO: This is DCN DPREFCLK: it could be program by DENTIST by VBIOS + * or CLK0_CLK11 by SMU. For DCE120, it is wlays 600Mhz. Will re-visit + * clock implementation + */ +static int dce_clocks_get_dp_ref_freq_wrkaround(struct display_clock *clk) +{ + struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); + int dp_ref_clk_khz = 600000; + + if (clk_dce->ss_on_dprefclk && clk_dce->dprefclk_ss_divider != 0) { + struct fixed32_32 ss_percentage = dal_fixed32_32_div_int( + dal_fixed32_32_from_fraction( + clk_dce->dprefclk_ss_percentage, + clk_dce->dprefclk_ss_divider), 200); + struct fixed32_32 adj_dp_ref_clk_khz; + + ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one, + ss_percentage); + adj_dp_ref_clk_khz = + dal_fixed32_32_mul_int( + ss_percentage, + dp_ref_clk_khz); + dp_ref_clk_khz = dal_fixed32_32_floor(adj_dp_ref_clk_khz); + } + + return dp_ref_clk_khz; +} +static enum dm_pp_clocks_state dce_get_required_clocks_state( + struct display_clock *clk, + struct state_dependent_clocks *req_clocks) +{ + struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); + int i; + enum dm_pp_clocks_state low_req_clk; + + /* Iterate from highest supported to lowest valid state, and update + * lowest RequiredState with the lowest state that satisfies + * all required clocks + */ + for (i = clk->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; i--) + if (req_clocks->display_clk_khz > + clk_dce->max_clks_by_state[i].display_clk_khz + || req_clocks->pixel_clk_khz > + clk_dce->max_clks_by_state[i].pixel_clk_khz) + break; + + low_req_clk = i + 1; + if (low_req_clk > clk->max_clks_state) { + dm_logger_write(clk->ctx->logger, LOG_WARNING, + "%s: clocks unsupported", __func__); + low_req_clk = DM_PP_CLOCKS_STATE_INVALID; + } + + return low_req_clk; +} + +static bool dce_clock_set_min_clocks_state( + struct display_clock *clk, + enum dm_pp_clocks_state clocks_state) +{ + struct dm_pp_power_level_change_request level_change_req = { + clocks_state }; + + if (clocks_state > clk->max_clks_state) { + /*Requested state exceeds max supported state.*/ + dm_logger_write(clk->ctx->logger, LOG_WARNING, + "Requested state exceeds max supported state"); + return false; + } else if (clocks_state == clk->cur_min_clks_state) { + /*if we're trying to set the same state, we can just return + * since nothing needs to be done*/ + return true; + } + + /* get max clock state from PPLIB */ + if (dm_pp_apply_power_level_change_request(clk->ctx, &level_change_req)) + clk->cur_min_clks_state = clocks_state; + + return true; +} + +static int dce_set_clock( + struct display_clock *clk, + int requested_clk_khz) +{ + struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); + struct bp_pixel_clock_parameters pxl_clk_params = { 0 }; + struct dc_bios *bp = clk->ctx->dc_bios; + int actual_clock = requested_clk_khz; + + /* Make sure requested clock isn't lower than minimum threshold*/ + if (requested_clk_khz > 0) + requested_clk_khz = max(requested_clk_khz, + clk_dce->dentist_vco_freq_khz / 64); + + /* Prepare to program display clock*/ + pxl_clk_params.target_pixel_clock = requested_clk_khz; + pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; + + bp->funcs->program_display_engine_pll(bp, &pxl_clk_params); + + if (clk_dce->dfs_bypass_enabled) { + + /* Cache the fixed display clock*/ + clk_dce->dfs_bypass_disp_clk = + pxl_clk_params.dfs_bypass_display_clock; + actual_clock = pxl_clk_params.dfs_bypass_display_clock; + } + + /* from power down, we need mark the clock state as ClocksStateNominal + * from HWReset, so when resume we will call pplib voltage regulator.*/ + if (requested_clk_khz == 0) + clk->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; + return actual_clock; +} + +static int dce_psr_set_clock( + struct display_clock *clk, + int requested_clk_khz) +{ + struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); + struct dc_context *ctx = clk_dce->base.ctx; + struct dc *core_dc = ctx->dc; + struct dmcu *dmcu = core_dc->res_pool->dmcu; + int actual_clk_khz = requested_clk_khz; + + actual_clk_khz = dce_set_clock(clk, requested_clk_khz); + + dmcu->funcs->set_psr_wait_loop(dmcu, actual_clk_khz / 1000 / 7); + return actual_clk_khz; +} + +static int dce112_set_clock( + struct display_clock *clk, + int requested_clk_khz) +{ + struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); + struct bp_set_dce_clock_parameters dce_clk_params; + struct dc_bios *bp = clk->ctx->dc_bios; + struct dc *core_dc = clk->ctx->dc; + struct abm *abm = core_dc->res_pool->abm; + struct dmcu *dmcu = core_dc->res_pool->dmcu; + int actual_clock = requested_clk_khz; + /* Prepare to program display clock*/ + memset(&dce_clk_params, 0, sizeof(dce_clk_params)); + + /* Make sure requested clock isn't lower than minimum threshold*/ + if (requested_clk_khz > 0) + requested_clk_khz = max(requested_clk_khz, + clk_dce->dentist_vco_freq_khz / 62); + + dce_clk_params.target_clock_frequency = requested_clk_khz; + dce_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; + dce_clk_params.clock_type = DCECLOCK_TYPE_DISPLAY_CLOCK; + + bp->funcs->set_dce_clock(bp, &dce_clk_params); + actual_clock = dce_clk_params.target_clock_frequency; + + /* from power down, we need mark the clock state as ClocksStateNominal + * from HWReset, so when resume we will call pplib voltage regulator.*/ + if (requested_clk_khz == 0) + clk->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; + + /*Program DP ref Clock*/ + /*VBIOS will determine DPREFCLK frequency, so we don't set it*/ + dce_clk_params.target_clock_frequency = 0; + dce_clk_params.clock_type = DCECLOCK_TYPE_DPREFCLK; + dce_clk_params.flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK = + (dce_clk_params.pll_id == + CLOCK_SOURCE_COMBO_DISPLAY_PLL0); + + bp->funcs->set_dce_clock(bp, &dce_clk_params); + + if (abm->funcs->is_dmcu_initialized(abm) && clk_dce->dfs_bypass_disp_clk != actual_clock) + dmcu->funcs->set_psr_wait_loop(dmcu, + actual_clock / 1000 / 7); + clk_dce->dfs_bypass_disp_clk = actual_clock; + return actual_clock; +} + +static void dce_clock_read_integrated_info(struct dce_disp_clk *clk_dce) +{ + struct dc_debug *debug = &clk_dce->base.ctx->dc->debug; + struct dc_bios *bp = clk_dce->base.ctx->dc_bios; + struct integrated_info info = { { { 0 } } }; + struct dc_firmware_info fw_info = { { 0 } }; + int i; + + if (bp->integrated_info) + info = *bp->integrated_info; + + clk_dce->dentist_vco_freq_khz = info.dentist_vco_freq; + if (clk_dce->dentist_vco_freq_khz == 0) { + bp->funcs->get_firmware_info(bp, &fw_info); + clk_dce->dentist_vco_freq_khz = + fw_info.smu_gpu_pll_output_freq; + if (clk_dce->dentist_vco_freq_khz == 0) + clk_dce->dentist_vco_freq_khz = 3600000; + } + + /*update the maximum display clock for each power state*/ + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID; + + switch (i) { + case 0: + clk_state = DM_PP_CLOCKS_STATE_ULTRA_LOW; + break; + + case 1: + clk_state = DM_PP_CLOCKS_STATE_LOW; + break; + + case 2: + clk_state = DM_PP_CLOCKS_STATE_NOMINAL; + break; + + case 3: + clk_state = DM_PP_CLOCKS_STATE_PERFORMANCE; + break; + + default: + clk_state = DM_PP_CLOCKS_STATE_INVALID; + break; + } + + /*Do not allow bad VBIOS/SBIOS to override with invalid values, + * check for > 100MHz*/ + if (info.disp_clk_voltage[i].max_supported_clk >= 100000) + clk_dce->max_clks_by_state[clk_state].display_clk_khz = + info.disp_clk_voltage[i].max_supported_clk; + } + + if (!debug->disable_dfs_bypass && bp->integrated_info) + if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE) + clk_dce->dfs_bypass_enabled = true; + + clk_dce->use_max_disp_clk = debug->max_disp_clk; +} + +static void dce_clock_read_ss_info(struct dce_disp_clk *clk_dce) +{ + struct dc_bios *bp = clk_dce->base.ctx->dc_bios; + int ss_info_num = bp->funcs->get_ss_entry_number( + bp, AS_SIGNAL_TYPE_GPU_PLL); + + if (ss_info_num) { + struct spread_spectrum_info info = { { 0 } }; + enum bp_result result = bp->funcs->get_spread_spectrum_info( + bp, AS_SIGNAL_TYPE_GPU_PLL, 0, &info); + + /* Based on VBIOS, VBIOS will keep entry for GPU PLL SS + * even if SS not enabled and in that case + * SSInfo.spreadSpectrumPercentage !=0 would be sign + * that SS is enabled + */ + if (result == BP_RESULT_OK && + info.spread_spectrum_percentage != 0) { + clk_dce->ss_on_dprefclk = true; + clk_dce->dprefclk_ss_divider = info.spread_percentage_divider; + + if (info.type.CENTER_MODE == 0) { + /* TODO: Currently for DP Reference clock we + * need only SS percentage for + * downspread */ + clk_dce->dprefclk_ss_percentage = + info.spread_spectrum_percentage; + } + + return; + } + + result = bp->funcs->get_spread_spectrum_info( + bp, AS_SIGNAL_TYPE_DISPLAY_PORT, 0, &info); + + /* Based on VBIOS, VBIOS will keep entry for DPREFCLK SS + * even if SS not enabled and in that case + * SSInfo.spreadSpectrumPercentage !=0 would be sign + * that SS is enabled + */ + if (result == BP_RESULT_OK && + info.spread_spectrum_percentage != 0) { + clk_dce->ss_on_dprefclk = true; + clk_dce->dprefclk_ss_divider = info.spread_percentage_divider; + + if (info.type.CENTER_MODE == 0) { + /* Currently for DP Reference clock we + * need only SS percentage for + * downspread */ + clk_dce->dprefclk_ss_percentage = + info.spread_spectrum_percentage; + } + } + } +} + +static bool dce_apply_clock_voltage_request( + struct display_clock *clk, + enum dm_pp_clock_type clocks_type, + int clocks_in_khz, + bool pre_mode_set, + bool update_dp_phyclk) +{ + bool send_request = false; + struct dm_pp_clock_for_voltage_req clock_voltage_req = {0}; + + switch (clocks_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + case DM_PP_CLOCK_TYPE_PIXELCLK: + case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: + break; + default: + BREAK_TO_DEBUGGER(); + return false; + } + + clock_voltage_req.clk_type = clocks_type; + clock_voltage_req.clocks_in_khz = clocks_in_khz; + + /* to pplib */ + if (pre_mode_set) { + switch (clocks_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + if (clocks_in_khz > clk->cur_clocks_value.dispclk_in_khz) { + clk->cur_clocks_value.dispclk_notify_pplib_done = true; + send_request = true; + } else + clk->cur_clocks_value.dispclk_notify_pplib_done = false; + /* no matter incrase or decrase clock, update current clock value */ + clk->cur_clocks_value.dispclk_in_khz = clocks_in_khz; + break; + case DM_PP_CLOCK_TYPE_PIXELCLK: + if (clocks_in_khz > clk->cur_clocks_value.max_pixelclk_in_khz) { + clk->cur_clocks_value.pixelclk_notify_pplib_done = true; + send_request = true; + } else + clk->cur_clocks_value.pixelclk_notify_pplib_done = false; + /* no matter incrase or decrase clock, update current clock value */ + clk->cur_clocks_value.max_pixelclk_in_khz = clocks_in_khz; + break; + case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: + if (clocks_in_khz > clk->cur_clocks_value.max_non_dp_phyclk_in_khz) { + clk->cur_clocks_value.phyclk_notigy_pplib_done = true; + send_request = true; + } else + clk->cur_clocks_value.phyclk_notigy_pplib_done = false; + /* no matter incrase or decrase clock, update current clock value */ + clk->cur_clocks_value.max_non_dp_phyclk_in_khz = clocks_in_khz; + break; + default: + ASSERT(0); + break; + } + + } else { + switch (clocks_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + if (!clk->cur_clocks_value.dispclk_notify_pplib_done) + send_request = true; + break; + case DM_PP_CLOCK_TYPE_PIXELCLK: + if (!clk->cur_clocks_value.pixelclk_notify_pplib_done) + send_request = true; + break; + case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: + if (!clk->cur_clocks_value.phyclk_notigy_pplib_done) + send_request = true; + break; + default: + ASSERT(0); + break; + } + } + if (send_request) { +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (clk->ctx->dce_version >= DCN_VERSION_1_0) { + struct dc *core_dc = clk->ctx->dc; + /*use dcfclk request voltage*/ + clock_voltage_req.clk_type = DM_PP_CLOCK_TYPE_DCFCLK; + clock_voltage_req.clocks_in_khz = + dcn_find_dcfclk_suits_all(core_dc, &clk->cur_clocks_value); + } +#endif + dm_pp_apply_clock_for_voltage_request( + clk->ctx, &clock_voltage_req); + } + if (update_dp_phyclk && (clocks_in_khz > + clk->cur_clocks_value.max_dp_phyclk_in_khz)) + clk->cur_clocks_value.max_dp_phyclk_in_khz = clocks_in_khz; + + return true; +} + + +static const struct display_clock_funcs dce120_funcs = { + .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq_wrkaround, + .apply_clock_voltage_request = dce_apply_clock_voltage_request, + .set_clock = dce112_set_clock +}; + +static const struct display_clock_funcs dce112_funcs = { + .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq, + .get_required_clocks_state = dce_get_required_clocks_state, + .set_min_clocks_state = dce_clock_set_min_clocks_state, + .set_clock = dce112_set_clock +}; + +static const struct display_clock_funcs dce110_funcs = { + .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq, + .get_required_clocks_state = dce_get_required_clocks_state, + .set_min_clocks_state = dce_clock_set_min_clocks_state, + .set_clock = dce_psr_set_clock +}; + +static const struct display_clock_funcs dce_funcs = { + .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq, + .get_required_clocks_state = dce_get_required_clocks_state, + .set_min_clocks_state = dce_clock_set_min_clocks_state, + .set_clock = dce_set_clock +}; + +static void dce_disp_clk_construct( + struct dce_disp_clk *clk_dce, + struct dc_context *ctx, + const struct dce_disp_clk_registers *regs, + const struct dce_disp_clk_shift *clk_shift, + const struct dce_disp_clk_mask *clk_mask) +{ + struct display_clock *base = &clk_dce->base; + + base->ctx = ctx; + base->funcs = &dce_funcs; + + clk_dce->regs = regs; + clk_dce->clk_shift = clk_shift; + clk_dce->clk_mask = clk_mask; + + clk_dce->dfs_bypass_disp_clk = 0; + + clk_dce->dprefclk_ss_percentage = 0; + clk_dce->dprefclk_ss_divider = 1000; + clk_dce->ss_on_dprefclk = false; + + base->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; + base->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID; + + dce_clock_read_integrated_info(clk_dce); + dce_clock_read_ss_info(clk_dce); + + dce_divider_range_construct( + &clk_dce->divider_ranges[DIVIDER_RANGE_01], + DIVIDER_RANGE_01_START, + DIVIDER_RANGE_01_STEP_SIZE, + DIVIDER_RANGE_01_BASE_DIVIDER_ID, + DIVIDER_RANGE_02_BASE_DIVIDER_ID); + dce_divider_range_construct( + &clk_dce->divider_ranges[DIVIDER_RANGE_02], + DIVIDER_RANGE_02_START, + DIVIDER_RANGE_02_STEP_SIZE, + DIVIDER_RANGE_02_BASE_DIVIDER_ID, + DIVIDER_RANGE_03_BASE_DIVIDER_ID); + dce_divider_range_construct( + &clk_dce->divider_ranges[DIVIDER_RANGE_03], + DIVIDER_RANGE_03_START, + DIVIDER_RANGE_03_STEP_SIZE, + DIVIDER_RANGE_03_BASE_DIVIDER_ID, + DIVIDER_RANGE_MAX_DIVIDER_ID); +} + +struct display_clock *dce_disp_clk_create( + struct dc_context *ctx, + const struct dce_disp_clk_registers *regs, + const struct dce_disp_clk_shift *clk_shift, + const struct dce_disp_clk_mask *clk_mask) +{ + struct dce_disp_clk *clk_dce = kzalloc(sizeof(*clk_dce), GFP_KERNEL); + + if (clk_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_dce->max_clks_by_state, + dce80_max_clks_by_state, + sizeof(dce80_max_clks_by_state)); + + dce_disp_clk_construct( + clk_dce, ctx, regs, clk_shift, clk_mask); + + return &clk_dce->base; +} + +struct display_clock *dce110_disp_clk_create( + struct dc_context *ctx, + const struct dce_disp_clk_registers *regs, + const struct dce_disp_clk_shift *clk_shift, + const struct dce_disp_clk_mask *clk_mask) +{ + struct dce_disp_clk *clk_dce = kzalloc(sizeof(*clk_dce), GFP_KERNEL); + + if (clk_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_dce->max_clks_by_state, + dce110_max_clks_by_state, + sizeof(dce110_max_clks_by_state)); + + dce_disp_clk_construct( + clk_dce, ctx, regs, clk_shift, clk_mask); + + clk_dce->base.funcs = &dce110_funcs; + + return &clk_dce->base; +} + +struct display_clock *dce112_disp_clk_create( + struct dc_context *ctx, + const struct dce_disp_clk_registers *regs, + const struct dce_disp_clk_shift *clk_shift, + const struct dce_disp_clk_mask *clk_mask) +{ + struct dce_disp_clk *clk_dce = kzalloc(sizeof(*clk_dce), GFP_KERNEL); + + if (clk_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_dce->max_clks_by_state, + dce112_max_clks_by_state, + sizeof(dce112_max_clks_by_state)); + + dce_disp_clk_construct( + clk_dce, ctx, regs, clk_shift, clk_mask); + + clk_dce->base.funcs = &dce112_funcs; + + return &clk_dce->base; +} + +struct display_clock *dce120_disp_clk_create(struct dc_context *ctx) +{ + struct dce_disp_clk *clk_dce = kzalloc(sizeof(*clk_dce), GFP_KERNEL); + struct dm_pp_clock_levels_with_voltage clk_level_info = {0}; + + if (clk_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_dce->max_clks_by_state, + dce120_max_clks_by_state, + sizeof(dce120_max_clks_by_state)); + + dce_disp_clk_construct( + clk_dce, ctx, NULL, NULL, NULL); + + clk_dce->base.funcs = &dce120_funcs; + + /* new in dce120 */ + if (!ctx->dc->debug.disable_pplib_clock_request && + dm_pp_get_clock_levels_by_type_with_voltage( + ctx, DM_PP_CLOCK_TYPE_DISPLAY_CLK, &clk_level_info) + && clk_level_info.num_levels) + clk_dce->max_displ_clk_in_khz = + clk_level_info.data[clk_level_info.num_levels - 1].clocks_in_khz; + else + clk_dce->max_displ_clk_in_khz = 1133000; + + return &clk_dce->base; +} + +void dce_disp_clk_destroy(struct display_clock **disp_clk) +{ + struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(*disp_clk); + + kfree(clk_dce); + *disp_clk = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.h b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.h new file mode 100644 index 000000000000..0e717e0dc8f0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.h @@ -0,0 +1,137 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#ifndef _DCE_CLOCKS_H_ +#define _DCE_CLOCKS_H_ + +#include "display_clock.h" + +#define CLK_COMMON_REG_LIST_DCE_BASE() \ + .DPREFCLK_CNTL = mmDPREFCLK_CNTL, \ + .DENTIST_DISPCLK_CNTL = mmDENTIST_DISPCLK_CNTL + +#define CLK_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + CLK_SF(DPREFCLK_CNTL, DPREFCLK_SRC_SEL, mask_sh), \ + CLK_SF(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, mask_sh) + +#define CLK_REG_FIELD_LIST(type) \ + type DPREFCLK_SRC_SEL; \ + type DENTIST_DPREFCLK_WDIVIDER; + +struct dce_disp_clk_shift { + CLK_REG_FIELD_LIST(uint8_t) +}; + +struct dce_disp_clk_mask { + CLK_REG_FIELD_LIST(uint32_t) +}; + +struct dce_disp_clk_registers { + uint32_t DPREFCLK_CNTL; + uint32_t DENTIST_DISPCLK_CNTL; +}; + +/* Array identifiers and count for the divider ranges.*/ +enum dce_divider_range_count { + DIVIDER_RANGE_01 = 0, + DIVIDER_RANGE_02, + DIVIDER_RANGE_03, + DIVIDER_RANGE_MAX /* == 3*/ +}; + +enum dce_divider_error_types { + INVALID_DID = 0, + INVALID_DIVIDER = 1 +}; + +struct dce_divider_range { + int div_range_start; + /* The end of this range of dividers.*/ + int div_range_end; + /* The distance between each divider in this range.*/ + int div_range_step; + /* The divider id for the lowest divider.*/ + int did_min; + /* The divider id for the highest divider.*/ + int did_max; +}; + +struct dce_disp_clk { + struct display_clock base; + const struct dce_disp_clk_registers *regs; + const struct dce_disp_clk_shift *clk_shift; + const struct dce_disp_clk_mask *clk_mask; + + struct state_dependent_clocks max_clks_by_state[DM_PP_CLOCKS_MAX_STATES]; + struct dce_divider_range divider_ranges[DIVIDER_RANGE_MAX]; + + bool use_max_disp_clk; + int dentist_vco_freq_khz; + + /* Cache the status of DFS-bypass feature*/ + bool dfs_bypass_enabled; + /* Cache the display clock returned by VBIOS if DFS-bypass is enabled. + * This is basically "Crystal Frequency In KHz" (XTALIN) frequency */ + int dfs_bypass_disp_clk; + + /* Flag for Enabled SS on DPREFCLK */ + bool ss_on_dprefclk; + /* DPREFCLK SS percentage (if down-spread enabled) */ + int dprefclk_ss_percentage; + /* DPREFCLK SS percentage Divider (100 or 1000) */ + int dprefclk_ss_divider; + + /* max disp_clk from PPLIB for max validation display clock*/ + int max_displ_clk_in_khz; +}; + + +struct display_clock *dce_disp_clk_create( + struct dc_context *ctx, + const struct dce_disp_clk_registers *regs, + const struct dce_disp_clk_shift *clk_shift, + const struct dce_disp_clk_mask *clk_mask); + +struct display_clock *dce110_disp_clk_create( + struct dc_context *ctx, + const struct dce_disp_clk_registers *regs, + const struct dce_disp_clk_shift *clk_shift, + const struct dce_disp_clk_mask *clk_mask); + +struct display_clock *dce112_disp_clk_create( + struct dc_context *ctx, + const struct dce_disp_clk_registers *regs, + const struct dce_disp_clk_shift *clk_shift, + const struct dce_disp_clk_mask *clk_mask); + +struct display_clock *dce120_disp_clk_create(struct dc_context *ctx); + +void dce_disp_clk_destroy(struct display_clock **disp_clk); + +#endif /* _DCE_CLOCKS_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c new file mode 100644 index 000000000000..fd77df573b61 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c @@ -0,0 +1,620 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "core_types.h" +#include "link_encoder.h" +#include "dce_dmcu.h" +#include "dm_services.h" +#include "reg_helper.h" +#include "fixed32_32.h" +#include "dc.h" + +#define TO_DCE_DMCU(dmcu)\ + container_of(dmcu, struct dce_dmcu, base) + +#define REG(reg) \ + (dmcu_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + dmcu_dce->dmcu_shift->field_name, dmcu_dce->dmcu_mask->field_name + +#define CTX \ + dmcu_dce->base.ctx + +/* PSR related commands */ +#define PSR_ENABLE 0x20 +#define PSR_EXIT 0x21 +#define PSR_SET 0x23 +#define PSR_SET_WAITLOOP 0x31 +#define MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK 0x00000001L +unsigned int cached_wait_loop_number = 0; + +bool dce_dmcu_load_iram(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, + unsigned int bytes) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int count = 0; + + /* Enable write access to IRAM */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 1, + IRAM_WR_ADDR_AUTO_INC, 1); + + REG_WAIT(DCI_MEM_PWR_STATUS, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + REG_WRITE(DMCU_IRAM_WR_CTRL, start_offset); + + for (count = 0; count < bytes; count++) + REG_WRITE(DMCU_IRAM_WR_DATA, src[count]); + + /* Disable write access to IRAM to allow dynamic sleep state */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 0, + IRAM_WR_ADDR_AUTO_INC, 0); + + return true; +} + +static void dce_get_dmcu_psr_state(struct dmcu *dmcu, uint32_t *psr_state) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + uint32_t psrStateOffset = 0xf0; + + /* Enable write access to IRAM */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 1); + + REG_WAIT(DCI_MEM_PWR_STATUS, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ + REG_WRITE(DMCU_IRAM_RD_CTRL, psrStateOffset); + + /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ + *psr_state = REG_READ(DMCU_IRAM_RD_DATA); + + /* Disable write access to IRAM after finished using IRAM + * in order to allow dynamic sleep state + */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 0); +} + +static void dce_dmcu_set_psr_enable(struct dmcu *dmcu, bool enable, bool wait) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + unsigned int retryCount; + uint32_t psr_state = 0; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* setDMCUParam_Cmd */ + if (enable) + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + PSR_ENABLE); + else + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + PSR_EXIT); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + if (wait == true) { + for (retryCount = 0; retryCount <= 100; retryCount++) { + dce_get_dmcu_psr_state(dmcu, &psr_state); + if (enable) { + if (psr_state != 0) + break; + } else { + if (psr_state == 0) + break; + } + udelay(10); + } + } +} + +static void dce_dmcu_setup_psr(struct dmcu *dmcu, + struct dc_link *link, + struct psr_context *psr_context) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + union dce_dmcu_psr_config_data_reg1 masterCmdData1; + union dce_dmcu_psr_config_data_reg2 masterCmdData2; + union dce_dmcu_psr_config_data_reg3 masterCmdData3; + + link->link_enc->funcs->psr_program_dp_dphy_fast_training(link->link_enc, + psr_context->psrExitLinkTrainingRequired); + + /* Enable static screen interrupts for PSR supported display */ + /* Disable the interrupt coming from other displays. */ + REG_UPDATE_4(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 0, + STATIC_SCREEN2_INT_TO_UC_EN, 0, + STATIC_SCREEN3_INT_TO_UC_EN, 0, + STATIC_SCREEN4_INT_TO_UC_EN, 0); + + switch (psr_context->controllerId) { + /* Driver uses case 1 for unconfigured */ + case 1: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + case 2: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN2_INT_TO_UC_EN, 1); + break; + case 3: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN3_INT_TO_UC_EN, 1); + break; + case 4: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN4_INT_TO_UC_EN, 1); + break; + case 5: + /* CZ/NL only has 4 CRTC!! + * really valid. + * There is no interrupt enable mask for these instances. + */ + break; + case 6: + /* CZ/NL only has 4 CRTC!! + * These are here because they are defined in HW regspec, + * but not really valid. There is no interrupt enable mask + * for these instances. + */ + break; + default: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + } + + link->link_enc->funcs->psr_program_secondary_packet(link->link_enc, + psr_context->sdpTransmitLineNumDeadline); + + if (psr_context->psr_level.bits.SKIP_SMU_NOTIFICATION) + REG_UPDATE(SMU_INTERRUPT_CONTROL, DC_SMU_INT_ENABLE, 1); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* setDMCUParam_PSRHostConfigData */ + masterCmdData1.u32All = 0; + masterCmdData1.bits.timehyst_frames = psr_context->timehyst_frames; + masterCmdData1.bits.hyst_lines = psr_context->hyst_lines; + masterCmdData1.bits.rfb_update_auto_en = + psr_context->rfb_update_auto_en; + masterCmdData1.bits.dp_port_num = psr_context->transmitterId; + masterCmdData1.bits.dcp_sel = psr_context->controllerId; + masterCmdData1.bits.phy_type = psr_context->phyType; + masterCmdData1.bits.frame_cap_ind = + psr_context->psrFrameCaptureIndicationReq; + masterCmdData1.bits.aux_chan = psr_context->channel; + masterCmdData1.bits.aux_repeat = psr_context->aux_repeats; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), + masterCmdData1.u32All); + + masterCmdData2.u32All = 0; + masterCmdData2.bits.dig_fe = psr_context->engineId; + masterCmdData2.bits.dig_be = psr_context->transmitterId; + masterCmdData2.bits.skip_wait_for_pll_lock = + psr_context->skipPsrWaitForPllLock; + masterCmdData2.bits.frame_delay = psr_context->frame_delay; + masterCmdData2.bits.smu_phy_id = psr_context->smuPhyId; + masterCmdData2.bits.num_of_controllers = + psr_context->numberOfControllers; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG2), + masterCmdData2.u32All); + + masterCmdData3.u32All = 0; + masterCmdData3.bits.psr_level = psr_context->psr_level.u32all; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG3), + masterCmdData3.u32All); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, PSR_SET); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} + +static void dce_psr_wait_loop( + struct dmcu *dmcu, + unsigned int wait_loop_number) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + union dce_dmcu_psr_config_data_wait_loop_reg1 masterCmdData1; + if (cached_wait_loop_number == wait_loop_number) + return; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + masterCmdData1.u32 = 0; + masterCmdData1.bits.wait_loop = wait_loop_number; + cached_wait_loop_number = wait_loop_number; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), masterCmdData1.u32); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, PSR_SET_WAITLOOP); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} + +static void dce_get_psr_wait_loop(unsigned int *psr_wait_loop_number) +{ + *psr_wait_loop_number = cached_wait_loop_number; + return; +} + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +bool dcn10_dmcu_load_iram(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, + unsigned int bytes) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int count = 0; + + REG_UPDATE(DMCU_CTRL, DMCU_ENABLE, 1); + + /* Enable write access to IRAM */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 1, + IRAM_WR_ADDR_AUTO_INC, 1); + + REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + REG_WRITE(DMCU_IRAM_WR_CTRL, start_offset); + + for (count = 0; count < bytes; count++) + REG_WRITE(DMCU_IRAM_WR_DATA, src[count]); + + /* Disable write access to IRAM to allow dynamic sleep state */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 0, + IRAM_WR_ADDR_AUTO_INC, 0); + + return true; +} + +static void dcn10_get_dmcu_psr_state(struct dmcu *dmcu, uint32_t *psr_state) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + uint32_t psrStateOffset = 0xf0; + + /* Enable write access to IRAM */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 1); + + REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ + REG_WRITE(DMCU_IRAM_RD_CTRL, psrStateOffset); + + /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ + *psr_state = REG_READ(DMCU_IRAM_RD_DATA); + + /* Disable write access to IRAM after finished using IRAM + * in order to allow dynamic sleep state + */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 0); +} + +static void dcn10_dmcu_set_psr_enable(struct dmcu *dmcu, bool enable, bool wait) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + unsigned int retryCount; + uint32_t psr_state = 0; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* setDMCUParam_Cmd */ + if (enable) + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + PSR_ENABLE); + else + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + PSR_EXIT); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* Below loops 1000 x 500us = 500 ms. + * Exit PSR may need to wait 1-2 frames to power up. Timeout after at + * least a few frames. Should never hit the max retry assert below. + */ + if (wait == true) { + for (retryCount = 0; retryCount <= 1000; retryCount++) { + dcn10_get_dmcu_psr_state(dmcu, &psr_state); + if (enable) { + if (psr_state != 0) + break; + } else { + if (psr_state == 0) + break; + } + udelay(500); + } + + /* assert if max retry hit */ + ASSERT(retryCount <= 1000); + } +} + +static void dcn10_dmcu_setup_psr(struct dmcu *dmcu, + struct dc_link *link, + struct psr_context *psr_context) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + union dce_dmcu_psr_config_data_reg1 masterCmdData1; + union dce_dmcu_psr_config_data_reg2 masterCmdData2; + union dce_dmcu_psr_config_data_reg3 masterCmdData3; + + link->link_enc->funcs->psr_program_dp_dphy_fast_training(link->link_enc, + psr_context->psrExitLinkTrainingRequired); + + /* Enable static screen interrupts for PSR supported display */ + /* Disable the interrupt coming from other displays. */ + REG_UPDATE_4(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 0, + STATIC_SCREEN2_INT_TO_UC_EN, 0, + STATIC_SCREEN3_INT_TO_UC_EN, 0, + STATIC_SCREEN4_INT_TO_UC_EN, 0); + + switch (psr_context->controllerId) { + /* Driver uses case 1 for unconfigured */ + case 1: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + case 2: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN2_INT_TO_UC_EN, 1); + break; + case 3: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN3_INT_TO_UC_EN, 1); + break; + case 4: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN4_INT_TO_UC_EN, 1); + break; + case 5: + /* CZ/NL only has 4 CRTC!! + * really valid. + * There is no interrupt enable mask for these instances. + */ + break; + case 6: + /* CZ/NL only has 4 CRTC!! + * These are here because they are defined in HW regspec, + * but not really valid. There is no interrupt enable mask + * for these instances. + */ + break; + default: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + } + + link->link_enc->funcs->psr_program_secondary_packet(link->link_enc, + psr_context->sdpTransmitLineNumDeadline); + + if (psr_context->psr_level.bits.SKIP_SMU_NOTIFICATION) + REG_UPDATE(SMU_INTERRUPT_CONTROL, DC_SMU_INT_ENABLE, 1); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* setDMCUParam_PSRHostConfigData */ + masterCmdData1.u32All = 0; + masterCmdData1.bits.timehyst_frames = psr_context->timehyst_frames; + masterCmdData1.bits.hyst_lines = psr_context->hyst_lines; + masterCmdData1.bits.rfb_update_auto_en = + psr_context->rfb_update_auto_en; + masterCmdData1.bits.dp_port_num = psr_context->transmitterId; + masterCmdData1.bits.dcp_sel = psr_context->controllerId; + masterCmdData1.bits.phy_type = psr_context->phyType; + masterCmdData1.bits.frame_cap_ind = + psr_context->psrFrameCaptureIndicationReq; + masterCmdData1.bits.aux_chan = psr_context->channel; + masterCmdData1.bits.aux_repeat = psr_context->aux_repeats; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), + masterCmdData1.u32All); + + masterCmdData2.u32All = 0; + masterCmdData2.bits.dig_fe = psr_context->engineId; + masterCmdData2.bits.dig_be = psr_context->transmitterId; + masterCmdData2.bits.skip_wait_for_pll_lock = + psr_context->skipPsrWaitForPllLock; + masterCmdData2.bits.frame_delay = psr_context->frame_delay; + masterCmdData2.bits.smu_phy_id = psr_context->smuPhyId; + masterCmdData2.bits.num_of_controllers = + psr_context->numberOfControllers; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG2), + masterCmdData2.u32All); + + masterCmdData3.u32All = 0; + masterCmdData3.bits.psr_level = psr_context->psr_level.u32all; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG3), + masterCmdData3.u32All); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, PSR_SET); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} + +static void dcn10_psr_wait_loop( + struct dmcu *dmcu, + unsigned int wait_loop_number) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + union dce_dmcu_psr_config_data_wait_loop_reg1 masterCmdData1; + if (wait_loop_number != 0) { + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + masterCmdData1.u32 = 0; + masterCmdData1.bits.wait_loop = wait_loop_number; + cached_wait_loop_number = wait_loop_number; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), masterCmdData1.u32); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, PSR_SET_WAITLOOP); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + } +} + +static void dcn10_get_psr_wait_loop(unsigned int *psr_wait_loop_number) +{ + *psr_wait_loop_number = cached_wait_loop_number; + return; +} + +#endif + +static const struct dmcu_funcs dce_funcs = { + .load_iram = dce_dmcu_load_iram, + .set_psr_enable = dce_dmcu_set_psr_enable, + .setup_psr = dce_dmcu_setup_psr, + .get_psr_state = dce_get_dmcu_psr_state, + .set_psr_wait_loop = dce_psr_wait_loop, + .get_psr_wait_loop = dce_get_psr_wait_loop +}; + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +static const struct dmcu_funcs dcn10_funcs = { + .load_iram = dcn10_dmcu_load_iram, + .set_psr_enable = dcn10_dmcu_set_psr_enable, + .setup_psr = dcn10_dmcu_setup_psr, + .get_psr_state = dcn10_get_dmcu_psr_state, + .set_psr_wait_loop = dcn10_psr_wait_loop, + .get_psr_wait_loop = dcn10_get_psr_wait_loop +}; +#endif + +static void dce_dmcu_construct( + struct dce_dmcu *dmcu_dce, + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dmcu *base = &dmcu_dce->base; + + base->ctx = ctx; + base->funcs = &dce_funcs; + + dmcu_dce->regs = regs; + dmcu_dce->dmcu_shift = dmcu_shift; + dmcu_dce->dmcu_mask = dmcu_mask; +} + +struct dmcu *dce_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_KERNEL); + + if (dmcu_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_dmcu_construct( + dmcu_dce, ctx, regs, dmcu_shift, dmcu_mask); + + dmcu_dce->base.funcs = &dce_funcs; + + return &dmcu_dce->base; +} + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +struct dmcu *dcn10_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_KERNEL); + + if (dmcu_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_dmcu_construct( + dmcu_dce, ctx, regs, dmcu_shift, dmcu_mask); + + dmcu_dce->base.funcs = &dcn10_funcs; + + return &dmcu_dce->base; +} +#endif + +void dce_dmcu_destroy(struct dmcu **dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(*dmcu); + + kfree(dmcu_dce); + *dmcu = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h new file mode 100644 index 000000000000..b85f53c2f6f8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h @@ -0,0 +1,218 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#ifndef _DCE_DMCU_H_ +#define _DCE_DMCU_H_ + +#include "dmcu.h" + +#define DMCU_COMMON_REG_LIST_DCE_BASE() \ + SR(DMCU_CTRL), \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_WR_CTRL), \ + SR(DMCU_IRAM_WR_DATA), \ + SR(MASTER_COMM_DATA_REG1), \ + SR(MASTER_COMM_DATA_REG2), \ + SR(MASTER_COMM_DATA_REG3), \ + SR(MASTER_COMM_CMD_REG), \ + SR(MASTER_COMM_CNTL_REG), \ + SR(DMCU_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + SR(SMU_INTERRUPT_CONTROL) + +#define DMCU_DCE110_COMMON_REG_LIST() \ + DMCU_COMMON_REG_LIST_DCE_BASE(), \ + SR(DCI_MEM_PWR_STATUS) + +#define DMCU_DCN10_REG_LIST()\ + DMCU_COMMON_REG_LIST_DCE_BASE(), \ + SR(DMU_MEM_PWR_CNTL) + +#define DMCU_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define DMCU_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + DMCU_SF(DMCU_CTRL, \ + DMCU_ENABLE, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_HOST_ACCESS_EN, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_WR_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(MASTER_COMM_CMD_REG, \ + MASTER_COMM_CMD_REG_BYTE0, mask_sh), \ + DMCU_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN1_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN2_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN3_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN4_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(SMU_INTERRUPT_CONTROL, DC_SMU_INT_ENABLE, mask_sh) + +#define DMCU_MASK_SH_LIST_DCE110(mask_sh) \ + DMCU_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + DMCU_SF(DCI_MEM_PWR_STATUS, \ + DMCU_IRAM_MEM_PWR_STATE, mask_sh) + +#define DMCU_MASK_SH_LIST_DCN10(mask_sh) \ + DMCU_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + DMCU_SF(DMU_MEM_PWR_CNTL, \ + DMCU_IRAM_MEM_PWR_STATE, mask_sh) + +#define DMCU_REG_FIELD_LIST(type) \ + type DMCU_IRAM_MEM_PWR_STATE; \ + type IRAM_HOST_ACCESS_EN; \ + type IRAM_WR_ADDR_AUTO_INC; \ + type DMCU_ENABLE; \ + type MASTER_COMM_CMD_REG_BYTE0; \ + type MASTER_COMM_INTERRUPT; \ + type DPHY_RX_FAST_TRAINING_CAPABLE; \ + type DPHY_LOAD_BS_COUNT; \ + type STATIC_SCREEN1_INT_TO_UC_EN; \ + type STATIC_SCREEN2_INT_TO_UC_EN; \ + type STATIC_SCREEN3_INT_TO_UC_EN; \ + type STATIC_SCREEN4_INT_TO_UC_EN; \ + type DP_SEC_GSP0_LINE_NUM; \ + type DP_SEC_GSP0_PRIORITY; \ + type DC_SMU_INT_ENABLE + +struct dce_dmcu_shift { + DMCU_REG_FIELD_LIST(uint8_t); +}; + +struct dce_dmcu_mask { + DMCU_REG_FIELD_LIST(uint32_t); +}; + +struct dce_dmcu_registers { + uint32_t DMCU_CTRL; + uint32_t DMCU_RAM_ACCESS_CTRL; + uint32_t DCI_MEM_PWR_STATUS; + uint32_t DMU_MEM_PWR_CNTL; + uint32_t DMCU_IRAM_WR_CTRL; + uint32_t DMCU_IRAM_WR_DATA; + + uint32_t MASTER_COMM_DATA_REG1; + uint32_t MASTER_COMM_DATA_REG2; + uint32_t MASTER_COMM_DATA_REG3; + uint32_t MASTER_COMM_CMD_REG; + uint32_t MASTER_COMM_CNTL_REG; + uint32_t DMCU_IRAM_RD_CTRL; + uint32_t DMCU_IRAM_RD_DATA; + uint32_t DMCU_INTERRUPT_TO_UC_EN_MASK; + uint32_t SMU_INTERRUPT_CONTROL; +}; + +struct dce_dmcu { + struct dmcu base; + const struct dce_dmcu_registers *regs; + const struct dce_dmcu_shift *dmcu_shift; + const struct dce_dmcu_mask *dmcu_mask; +}; + +/******************************************************************* + * MASTER_COMM_DATA_REG1 Bit position Data + * 7:0 hyst_frames[7:0] + * 14:8 hyst_lines[6:0] + * 15 RFB_UPDATE_AUTO_EN + * 18:16 phy_num[2:0] + * 21:19 dcp_sel[2:0] + * 22 phy_type + * 23 frame_cap_ind + * 26:24 aux_chan[2:0] + * 30:27 aux_repeat[3:0] + * 31:31 reserved[31:31] + ******************************************************************/ +union dce_dmcu_psr_config_data_reg1 { + struct { + unsigned int timehyst_frames:8; /*[7:0]*/ + unsigned int hyst_lines:7; /*[14:8]*/ + unsigned int rfb_update_auto_en:1; /*[15:15]*/ + unsigned int dp_port_num:3; /*[18:16]*/ + unsigned int dcp_sel:3; /*[21:19]*/ + unsigned int phy_type:1; /*[22:22]*/ + unsigned int frame_cap_ind:1; /*[23:23]*/ + unsigned int aux_chan:3; /*[26:24]*/ + unsigned int aux_repeat:4; /*[30:27]*/ + unsigned int reserved:1; /*[31:31]*/ + } bits; + unsigned int u32All; +}; + +/******************************************************************* + * MASTER_COMM_DATA_REG2 + *******************************************************************/ +union dce_dmcu_psr_config_data_reg2 { + struct { + unsigned int dig_fe:3; /*[2:0]*/ + unsigned int dig_be:3; /*[5:3]*/ + unsigned int skip_wait_for_pll_lock:1; /*[6:6]*/ + unsigned int reserved:9; /*[15:7]*/ + unsigned int frame_delay:8; /*[23:16]*/ + unsigned int smu_phy_id:4; /*[27:24]*/ + unsigned int num_of_controllers:4; /*[31:28]*/ + } bits; + unsigned int u32All; +}; + +/******************************************************************* + * MASTER_COMM_DATA_REG3 + *******************************************************************/ +union dce_dmcu_psr_config_data_reg3 { + struct { + unsigned int psr_level:16; /*[15:0]*/ + unsigned int link_rate:4; /*[19:16]*/ + unsigned int reserved:12; /*[31:20]*/ + } bits; + unsigned int u32All; +}; + +union dce_dmcu_psr_config_data_wait_loop_reg1 { + struct { + unsigned int wait_loop:16; /* [15:0] */ + unsigned int reserved:16; /* [31:16] */ + } bits; + unsigned int u32; +}; + +struct dmcu *dce_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask); + +struct dmcu *dcn10_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask); + +void dce_dmcu_destroy(struct dmcu **dmcu); + +#endif /* _DCE_ABM_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c new file mode 100644 index 000000000000..d2e66b1bc0ef --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c @@ -0,0 +1,209 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce_hwseq.h" +#include "reg_helper.h" +#include "hw_sequencer.h" +#include "core_types.h" + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +void dce_enable_fe_clock(struct dce_hwseq *hws, + unsigned int fe_inst, bool enable) +{ + REG_UPDATE(DCFE_CLOCK_CONTROL[fe_inst], + DCFE_CLOCK_ENABLE, enable); +} + +void dce_pipe_control_lock(struct dc *dc, + struct pipe_ctx *pipe, + bool lock) +{ + uint32_t lock_val = lock ? 1 : 0; + uint32_t dcp_grph, scl, blnd, update_lock_mode, val; + struct dce_hwseq *hws = dc->hwseq; + + /* Not lock pipe when blank */ + if (lock && pipe->stream_res.tg->funcs->is_blanked(pipe->stream_res.tg)) + return; + + val = REG_GET_4(BLND_V_UPDATE_LOCK[pipe->pipe_idx], + BLND_DCP_GRPH_V_UPDATE_LOCK, &dcp_grph, + BLND_SCL_V_UPDATE_LOCK, &scl, + BLND_BLND_V_UPDATE_LOCK, &blnd, + BLND_V_UPDATE_LOCK_MODE, &update_lock_mode); + + dcp_grph = lock_val; + scl = lock_val; + blnd = lock_val; + update_lock_mode = lock_val; + + REG_SET_2(BLND_V_UPDATE_LOCK[pipe->pipe_idx], val, + BLND_DCP_GRPH_V_UPDATE_LOCK, dcp_grph, + BLND_SCL_V_UPDATE_LOCK, scl); + + if (hws->masks->BLND_BLND_V_UPDATE_LOCK != 0) + REG_SET_2(BLND_V_UPDATE_LOCK[pipe->pipe_idx], val, + BLND_BLND_V_UPDATE_LOCK, blnd, + BLND_V_UPDATE_LOCK_MODE, update_lock_mode); + + if (hws->wa.blnd_crtc_trigger) { + if (!lock) { + uint32_t value = REG_READ(CRTC_H_BLANK_START_END[pipe->pipe_idx]); + REG_WRITE(CRTC_H_BLANK_START_END[pipe->pipe_idx], value); + } + } +} + +void dce_set_blender_mode(struct dce_hwseq *hws, + unsigned int blnd_inst, + enum blnd_mode mode) +{ + uint32_t feedthrough = 1; + uint32_t blnd_mode = 0; + uint32_t multiplied_mode = 0; + uint32_t alpha_mode = 2; + + switch (mode) { + case BLND_MODE_OTHER_PIPE: + feedthrough = 0; + blnd_mode = 1; + alpha_mode = 0; + break; + case BLND_MODE_BLENDING: + feedthrough = 0; + blnd_mode = 2; + alpha_mode = 0; + multiplied_mode = 1; + break; + case BLND_MODE_CURRENT_PIPE: + default: + if (REG(BLND_CONTROL[blnd_inst]) == REG(BLNDV_CONTROL) || + blnd_inst == 0) + feedthrough = 0; + break; + } + + REG_UPDATE(BLND_CONTROL[blnd_inst], + BLND_MODE, blnd_mode); + + if (hws->masks->BLND_ALPHA_MODE != 0) { + REG_UPDATE_3(BLND_CONTROL[blnd_inst], + BLND_FEEDTHROUGH_EN, feedthrough, + BLND_ALPHA_MODE, alpha_mode, + BLND_MULTIPLIED_MODE, multiplied_mode); + } +} + + +static void dce_disable_sram_shut_down(struct dce_hwseq *hws) +{ + if (REG(DC_MEM_GLOBAL_PWR_REQ_CNTL)) + REG_UPDATE(DC_MEM_GLOBAL_PWR_REQ_CNTL, + DC_MEM_GLOBAL_PWR_REQ_DIS, 1); +} + +static void dce_underlay_clock_enable(struct dce_hwseq *hws) +{ + /* todo: why do we need this at boot? is dce_enable_fe_clock enough? */ + if (REG(DCFEV_CLOCK_CONTROL)) + REG_UPDATE(DCFEV_CLOCK_CONTROL, + DCFEV_CLOCK_ENABLE, 1); +} + +static void enable_hw_base_light_sleep(void) +{ + /* TODO: implement */ +} + +static void disable_sw_manual_control_light_sleep(void) +{ + /* TODO: implement */ +} + +void dce_clock_gating_power_up(struct dce_hwseq *hws, + bool enable) +{ + if (enable) { + enable_hw_base_light_sleep(); + disable_sw_manual_control_light_sleep(); + } else { + dce_disable_sram_shut_down(hws); + dce_underlay_clock_enable(hws); + } +} + +void dce_crtc_switch_to_clk_src(struct dce_hwseq *hws, + struct clock_source *clk_src, + unsigned int tg_inst) +{ + if (clk_src->id == CLOCK_SOURCE_ID_DP_DTO || clk_src->dp_clk_src) { + REG_UPDATE(PIXEL_RATE_CNTL[tg_inst], + DP_DTO0_ENABLE, 1); + + } else if (clk_src->id >= CLOCK_SOURCE_COMBO_PHY_PLL0) { + uint32_t rate_source = clk_src->id - CLOCK_SOURCE_COMBO_PHY_PLL0; + + REG_UPDATE_2(PHYPLL_PIXEL_RATE_CNTL[tg_inst], + PHYPLL_PIXEL_RATE_SOURCE, rate_source, + PIXEL_RATE_PLL_SOURCE, 0); + + REG_UPDATE(PIXEL_RATE_CNTL[tg_inst], + DP_DTO0_ENABLE, 0); + + } else if (clk_src->id <= CLOCK_SOURCE_ID_PLL2) { + uint32_t rate_source = clk_src->id - CLOCK_SOURCE_ID_PLL0; + + REG_UPDATE_2(PIXEL_RATE_CNTL[tg_inst], + PIXEL_RATE_SOURCE, rate_source, + DP_DTO0_ENABLE, 0); + + if (REG(PHYPLL_PIXEL_RATE_CNTL[tg_inst])) + REG_UPDATE(PHYPLL_PIXEL_RATE_CNTL[tg_inst], + PIXEL_RATE_PLL_SOURCE, 1); + } else { + DC_ERR("Unknown clock source. clk_src id: %d, TG_inst: %d", + clk_src->id, tg_inst); + } +} + +/* Only use LUT for 8 bit formats */ +bool dce_use_lut(const struct dc_plane_state *plane_state) +{ + switch (plane_state->format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + return true; + default: + return false; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h new file mode 100644 index 000000000000..52506155e361 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h @@ -0,0 +1,631 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DCE_HWSEQ_H__ +#define __DCE_HWSEQ_H__ + +#include "hw_sequencer.h" + +#define BL_REG_LIST()\ + SR(LVTMA_PWRSEQ_CNTL), \ + SR(LVTMA_PWRSEQ_STATE) + +#define HWSEQ_DCEF_REG_LIST_DCE8() \ + .DCFE_CLOCK_CONTROL[0] = mmCRTC0_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[1] = mmCRTC1_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[2] = mmCRTC2_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[3] = mmCRTC3_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[4] = mmCRTC4_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[5] = mmCRTC5_CRTC_DCFE_CLOCK_CONTROL + +#define HWSEQ_DCEF_REG_LIST() \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 0), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 1), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 2), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 3), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 4), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 5), \ + SR(DC_MEM_GLOBAL_PWR_REQ_CNTL) + +#define HWSEQ_BLND_REG_LIST() \ + SRII(BLND_V_UPDATE_LOCK, BLND, 0), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 1), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 2), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 3), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 4), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 5), \ + SRII(BLND_CONTROL, BLND, 0), \ + SRII(BLND_CONTROL, BLND, 1), \ + SRII(BLND_CONTROL, BLND, 2), \ + SRII(BLND_CONTROL, BLND, 3), \ + SRII(BLND_CONTROL, BLND, 4), \ + SRII(BLND_CONTROL, BLND, 5) + +#define HWSEQ_PIXEL_RATE_REG_LIST(blk) \ + SRII(PIXEL_RATE_CNTL, blk, 0), \ + SRII(PIXEL_RATE_CNTL, blk, 1), \ + SRII(PIXEL_RATE_CNTL, blk, 2), \ + SRII(PIXEL_RATE_CNTL, blk, 3), \ + SRII(PIXEL_RATE_CNTL, blk, 4), \ + SRII(PIXEL_RATE_CNTL, blk, 5) + +#define HWSEQ_PHYPLL_REG_LIST(blk) \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 0), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 1), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 2), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 3), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 4), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 5) + +#define HWSEQ_DCE11_REG_LIST_BASE() \ + SR(DC_MEM_GLOBAL_PWR_REQ_CNTL), \ + SR(DCFEV_CLOCK_CONTROL), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 0), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 1), \ + SRII(CRTC_H_BLANK_START_END, CRTC, 0),\ + SRII(CRTC_H_BLANK_START_END, CRTC, 1),\ + SRII(BLND_V_UPDATE_LOCK, BLND, 0),\ + SRII(BLND_V_UPDATE_LOCK, BLND, 1),\ + SRII(BLND_CONTROL, BLND, 0),\ + SRII(BLND_CONTROL, BLND, 1),\ + SR(BLNDV_CONTROL),\ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC),\ + BL_REG_LIST() + +#define HWSEQ_DCE8_REG_LIST() \ + HWSEQ_DCEF_REG_LIST_DCE8(), \ + HWSEQ_BLND_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC),\ + BL_REG_LIST() + +#define HWSEQ_DCE10_REG_LIST() \ + HWSEQ_DCEF_REG_LIST(), \ + HWSEQ_BLND_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC), \ + BL_REG_LIST() + +#define HWSEQ_ST_REG_LIST() \ + HWSEQ_DCE11_REG_LIST_BASE(), \ + .DCFE_CLOCK_CONTROL[2] = mmDCFEV_CLOCK_CONTROL, \ + .CRTC_H_BLANK_START_END[2] = mmCRTCV_H_BLANK_START_END, \ + .BLND_V_UPDATE_LOCK[2] = mmBLNDV_V_UPDATE_LOCK, \ + .BLND_CONTROL[2] = mmBLNDV_CONTROL + +#define HWSEQ_CZ_REG_LIST() \ + HWSEQ_DCE11_REG_LIST_BASE(), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 2), \ + SRII(CRTC_H_BLANK_START_END, CRTC, 2), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 2), \ + SRII(BLND_CONTROL, BLND, 2), \ + .DCFE_CLOCK_CONTROL[3] = mmDCFEV_CLOCK_CONTROL, \ + .CRTC_H_BLANK_START_END[3] = mmCRTCV_H_BLANK_START_END, \ + .BLND_V_UPDATE_LOCK[3] = mmBLNDV_V_UPDATE_LOCK, \ + .BLND_CONTROL[3] = mmBLNDV_CONTROL + +#define HWSEQ_DCE120_REG_LIST() \ + HWSEQ_DCE10_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC), \ + HWSEQ_PHYPLL_REG_LIST(CRTC), \ + SR(DCHUB_FB_LOCATION),\ + SR(DCHUB_AGP_BASE),\ + SR(DCHUB_AGP_BOT),\ + SR(DCHUB_AGP_TOP), \ + BL_REG_LIST() + +#define HWSEQ_DCE112_REG_LIST() \ + HWSEQ_DCE10_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC), \ + HWSEQ_PHYPLL_REG_LIST(CRTC), \ + BL_REG_LIST() + +#define HWSEQ_DCN_REG_LIST()\ + SRII(OTG_GLOBAL_SYNC_STATUS, OTG, 0), \ + SRII(OTG_GLOBAL_SYNC_STATUS, OTG, 1), \ + SRII(OTG_GLOBAL_SYNC_STATUS, OTG, 2), \ + SRII(OTG_GLOBAL_SYNC_STATUS, OTG, 3), \ + SRII(DCHUBP_CNTL, HUBP, 0), \ + SRII(DCHUBP_CNTL, HUBP, 1), \ + SRII(DCHUBP_CNTL, HUBP, 2), \ + SRII(DCHUBP_CNTL, HUBP, 3), \ + SRII(HUBP_CLK_CNTL, HUBP, 0), \ + SRII(HUBP_CLK_CNTL, HUBP, 1), \ + SRII(HUBP_CLK_CNTL, HUBP, 2), \ + SRII(HUBP_CLK_CNTL, HUBP, 3), \ + SRII(DPP_CONTROL, DPP_TOP, 0), \ + SRII(DPP_CONTROL, DPP_TOP, 1), \ + SRII(DPP_CONTROL, DPP_TOP, 2), \ + SRII(DPP_CONTROL, DPP_TOP, 3), \ + SRII(OPP_PIPE_CONTROL, OPP_PIPE, 0), \ + SRII(OPP_PIPE_CONTROL, OPP_PIPE, 1), \ + SRII(OPP_PIPE_CONTROL, OPP_PIPE, 2), \ + SRII(OPP_PIPE_CONTROL, OPP_PIPE, 3), \ + SR(REFCLK_CNTL), \ + SR(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A),\ + SR(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_A),\ + SR(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A),\ + SR(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B),\ + SR(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_B),\ + SR(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B),\ + SR(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C),\ + SR(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_C),\ + SR(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C),\ + SR(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D),\ + SR(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_D),\ + SR(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D),\ + SR(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL),\ + SR(DCHUBBUB_ARB_DRAM_STATE_CNTL),\ + SR(DCHUBBUB_ARB_SAT_LEVEL),\ + SR(DCHUBBUB_ARB_DF_REQ_OUTSTAND),\ + SR(DCHUBBUB_GLOBAL_TIMER_CNTL), \ + SR(DCHUBBUB_TEST_DEBUG_INDEX), \ + SR(DCHUBBUB_TEST_DEBUG_DATA), \ + SR(DIO_MEM_PWR_CTRL), \ + SR(DCCG_GATE_DISABLE_CNTL), \ + SR(DCCG_GATE_DISABLE_CNTL2), \ + SR(DCFCLK_CNTL),\ + SR(DCFCLK_CNTL), \ + /* todo: get these from GVM instead of reading registers ourselves */\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32),\ + MMHUB_SR(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32),\ + MMHUB_SR(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_LOW_ADDR),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_HIGH_ADDR) + +#define HWSEQ_SR_WATERMARK_REG_LIST()\ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A),\ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A),\ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B),\ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B),\ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C),\ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C),\ + SR(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D),\ + SR(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D) + +#define HWSEQ_DCN1_REG_LIST()\ + HWSEQ_DCN_REG_LIST(), \ + HWSEQ_SR_WATERMARK_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(OTG), \ + HWSEQ_PHYPLL_REG_LIST(OTG), \ + SR(DCHUBBUB_SDPIF_FB_TOP),\ + SR(DCHUBBUB_SDPIF_FB_BASE),\ + SR(DCHUBBUB_SDPIF_FB_OFFSET),\ + SR(DCHUBBUB_SDPIF_AGP_BASE),\ + SR(DCHUBBUB_SDPIF_AGP_BOT),\ + SR(DCHUBBUB_SDPIF_AGP_TOP),\ + SR(DOMAIN0_PG_CONFIG), \ + SR(DOMAIN1_PG_CONFIG), \ + SR(DOMAIN2_PG_CONFIG), \ + SR(DOMAIN3_PG_CONFIG), \ + SR(DOMAIN4_PG_CONFIG), \ + SR(DOMAIN5_PG_CONFIG), \ + SR(DOMAIN6_PG_CONFIG), \ + SR(DOMAIN7_PG_CONFIG), \ + SR(DOMAIN0_PG_STATUS), \ + SR(DOMAIN1_PG_STATUS), \ + SR(DOMAIN2_PG_STATUS), \ + SR(DOMAIN3_PG_STATUS), \ + SR(DOMAIN4_PG_STATUS), \ + SR(DOMAIN5_PG_STATUS), \ + SR(DOMAIN6_PG_STATUS), \ + SR(DOMAIN7_PG_STATUS), \ + SR(D1VGA_CONTROL), \ + SR(D2VGA_CONTROL), \ + SR(D3VGA_CONTROL), \ + SR(D4VGA_CONTROL), \ + SR(DC_IP_REQUEST_CNTL), \ + BL_REG_LIST() + +struct dce_hwseq_registers { + + /* Backlight registers */ + uint32_t LVTMA_PWRSEQ_CNTL; + uint32_t LVTMA_PWRSEQ_STATE; + + uint32_t DCFE_CLOCK_CONTROL[6]; + uint32_t DCFEV_CLOCK_CONTROL; + uint32_t DC_MEM_GLOBAL_PWR_REQ_CNTL; + uint32_t BLND_V_UPDATE_LOCK[6]; + uint32_t BLND_CONTROL[6]; + uint32_t BLNDV_CONTROL; + uint32_t CRTC_H_BLANK_START_END[6]; + uint32_t PIXEL_RATE_CNTL[6]; + uint32_t PHYPLL_PIXEL_RATE_CNTL[6]; + /*DCHUB*/ + uint32_t DCHUB_FB_LOCATION; + uint32_t DCHUB_AGP_BASE; + uint32_t DCHUB_AGP_BOT; + uint32_t DCHUB_AGP_TOP; + + uint32_t OTG_GLOBAL_SYNC_STATUS[4]; + uint32_t DCHUBP_CNTL[4]; + uint32_t HUBP_CLK_CNTL[4]; + uint32_t DPP_CONTROL[4]; + uint32_t OPP_PIPE_CONTROL[4]; + uint32_t REFCLK_CNTL; + uint32_t DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A; + uint32_t DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_A; + uint32_t DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A; + uint32_t DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A; + uint32_t DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A; + uint32_t DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B; + uint32_t DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_B; + uint32_t DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B; + uint32_t DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B; + uint32_t DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B; + uint32_t DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C; + uint32_t DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_C; + uint32_t DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C; + uint32_t DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C; + uint32_t DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C; + uint32_t DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D; + uint32_t DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_D; + uint32_t DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D; + uint32_t DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D; + uint32_t DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D; + uint32_t DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL; + uint32_t DCHUBBUB_ARB_SAT_LEVEL; + uint32_t DCHUBBUB_ARB_DF_REQ_OUTSTAND; + uint32_t DCHUBBUB_GLOBAL_TIMER_CNTL; + uint32_t DCHUBBUB_ARB_DRAM_STATE_CNTL; + uint32_t DCHUBBUB_TEST_DEBUG_INDEX; + uint32_t DCHUBBUB_TEST_DEBUG_DATA; + uint32_t DCHUBBUB_SDPIF_FB_TOP; + uint32_t DCHUBBUB_SDPIF_FB_BASE; + uint32_t DCHUBBUB_SDPIF_FB_OFFSET; + uint32_t DCHUBBUB_SDPIF_AGP_BASE; + uint32_t DCHUBBUB_SDPIF_AGP_BOT; + uint32_t DCHUBBUB_SDPIF_AGP_TOP; + uint32_t DC_IP_REQUEST_CNTL; + uint32_t DOMAIN0_PG_CONFIG; + uint32_t DOMAIN1_PG_CONFIG; + uint32_t DOMAIN2_PG_CONFIG; + uint32_t DOMAIN3_PG_CONFIG; + uint32_t DOMAIN4_PG_CONFIG; + uint32_t DOMAIN5_PG_CONFIG; + uint32_t DOMAIN6_PG_CONFIG; + uint32_t DOMAIN7_PG_CONFIG; + uint32_t DOMAIN0_PG_STATUS; + uint32_t DOMAIN1_PG_STATUS; + uint32_t DOMAIN2_PG_STATUS; + uint32_t DOMAIN3_PG_STATUS; + uint32_t DOMAIN4_PG_STATUS; + uint32_t DOMAIN5_PG_STATUS; + uint32_t DOMAIN6_PG_STATUS; + uint32_t DOMAIN7_PG_STATUS; + uint32_t DIO_MEM_PWR_CTRL; + uint32_t DCCG_GATE_DISABLE_CNTL; + uint32_t DCCG_GATE_DISABLE_CNTL2; + uint32_t DCFCLK_CNTL; + uint32_t MICROSECOND_TIME_BASE_DIV; + uint32_t MILLISECOND_TIME_BASE_DIV; + uint32_t DISPCLK_FREQ_CHANGE_CNTL; + uint32_t RBBMIF_TIMEOUT_DIS; + uint32_t RBBMIF_TIMEOUT_DIS_2; + uint32_t DENTIST_DISPCLK_CNTL; + uint32_t DCHUBBUB_CRC_CTRL; + uint32_t DPP_TOP0_DPP_CRC_CTRL; + uint32_t DPP_TOP0_DPP_CRC_VAL_R_G; + uint32_t DPP_TOP0_DPP_CRC_VAL_B_A; + uint32_t MPC_CRC_CTRL; + uint32_t MPC_CRC_RESULT_GB; + uint32_t MPC_CRC_RESULT_C; + uint32_t MPC_CRC_RESULT_AR; + uint32_t D1VGA_CONTROL; + uint32_t D2VGA_CONTROL; + uint32_t D3VGA_CONTROL; + uint32_t D4VGA_CONTROL; + /* MMHUB registers. read only. temporary hack */ + uint32_t VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32; + uint32_t VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32; + uint32_t VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32; + uint32_t VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32; + uint32_t VM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32; + uint32_t VM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32; + uint32_t VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32; + uint32_t VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32; + uint32_t MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB; + uint32_t MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB; + uint32_t MC_VM_SYSTEM_APERTURE_LOW_ADDR; + uint32_t MC_VM_SYSTEM_APERTURE_HIGH_ADDR; +}; + /* set field name */ +#define HWS_SF(blk_name, reg_name, field_name, post_fix)\ + .field_name = blk_name ## reg_name ## __ ## field_name ## post_fix + +#define HWS_SF1(blk_name, reg_name, field_name, post_fix)\ + .field_name = blk_name ## reg_name ## __ ## blk_name ## field_name ## post_fix + + +#define HWSEQ_DCEF_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF(blk, CLOCK_CONTROL, DCFE_CLOCK_ENABLE, mask_sh),\ + SF(DC_MEM_GLOBAL_PWR_REQ_CNTL, DC_MEM_GLOBAL_PWR_REQ_DIS, mask_sh) + +#define HWSEQ_BLND_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_DCP_GRPH_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_SCL_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_DCP_GRPH_SURF_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_BLND_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_V_UPDATE_LOCK_MODE, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_FEEDTHROUGH_EN, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_ALPHA_MODE, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_MODE, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_MULTIPLIED_MODE, mask_sh) + +#define HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF1(blk, PIXEL_RATE_CNTL, PIXEL_RATE_SOURCE, mask_sh),\ + HWS_SF(blk, PIXEL_RATE_CNTL, DP_DTO0_ENABLE, mask_sh) + +#define HWSEQ_PHYPLL_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF1(blk, PHYPLL_PIXEL_RATE_CNTL, PHYPLL_PIXEL_RATE_SOURCE, mask_sh),\ + HWS_SF1(blk, PHYPLL_PIXEL_RATE_CNTL, PIXEL_RATE_PLL_SOURCE, mask_sh) + +#define HWSEQ_DCE8_MASK_SH_LIST(mask_sh)\ + .DCFE_CLOCK_ENABLE = CRTC_DCFE_CLOCK_CONTROL__CRTC_DCFE_CLOCK_ENABLE ## mask_sh, \ + HWS_SF(BLND_, V_UPDATE_LOCK, BLND_DCP_GRPH_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(BLND_, V_UPDATE_LOCK, BLND_SCL_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(BLND_, V_UPDATE_LOCK, BLND_DCP_GRPH_SURF_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(BLND_, CONTROL, BLND_MODE, mask_sh),\ + HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh),\ + HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_) + +#define HWSEQ_DCE10_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCEF_MASK_SH_LIST(mask_sh, DCFE_),\ + HWSEQ_BLND_MASK_SH_LIST(mask_sh, BLND_),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_), \ + HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh), \ + HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh) + +#define HWSEQ_DCE11_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCE10_MASK_SH_LIST(mask_sh),\ + SF(DCFEV_CLOCK_CONTROL, DCFEV_CLOCK_ENABLE, mask_sh),\ + HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh),\ + HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_) + +#define HWSEQ_DCE112_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCE10_MASK_SH_LIST(mask_sh),\ + HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh),\ + HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh),\ + HWSEQ_PHYPLL_MASK_SH_LIST(mask_sh, CRTC0_) + +#define HWSEQ_GFX9_DCHUB_MASK_SH_LIST(mask_sh)\ + SF(DCHUB_FB_LOCATION, FB_TOP, mask_sh),\ + SF(DCHUB_FB_LOCATION, FB_BASE, mask_sh),\ + SF(DCHUB_AGP_BASE, AGP_BASE, mask_sh),\ + SF(DCHUB_AGP_BOT, AGP_BOT, mask_sh),\ + SF(DCHUB_AGP_TOP, AGP_TOP, mask_sh), \ + HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh), \ + HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh) + +#define HWSEQ_DCE12_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCEF_MASK_SH_LIST(mask_sh, DCFE0_DCFE_),\ + HWSEQ_BLND_MASK_SH_LIST(mask_sh, BLND0_BLND_),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_),\ + HWSEQ_PHYPLL_MASK_SH_LIST(mask_sh, CRTC0_),\ + HWSEQ_GFX9_DCHUB_MASK_SH_LIST(mask_sh), \ + HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh), \ + HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh) + +#define HWSEQ_DCN_MASK_SH_LIST(mask_sh)\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, OTG0_),\ + HWS_SF1(OTG0_, PHYPLL_PIXEL_RATE_CNTL, PHYPLL_PIXEL_RATE_SOURCE, mask_sh), \ + HWS_SF(OTG0_, OTG_GLOBAL_SYNC_STATUS, VUPDATE_NO_LOCK_EVENT_CLEAR, mask_sh), \ + HWS_SF(OTG0_, OTG_GLOBAL_SYNC_STATUS, VUPDATE_NO_LOCK_EVENT_OCCURRED, mask_sh), \ + HWS_SF(HUBP0_, DCHUBP_CNTL, HUBP_VTG_SEL, mask_sh), \ + HWS_SF(HUBP0_, HUBP_CLK_CNTL, HUBP_CLOCK_ENABLE, mask_sh), \ + HWS_SF(DPP_TOP0_, DPP_CONTROL, DPP_CLOCK_ENABLE, mask_sh), \ + HWS_SF(OPP_PIPE0_, OPP_PIPE_CONTROL, OPP_PIPE_CLOCK_EN, mask_sh),\ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, DCHUBBUB_ARB_WATERMARK_CHANGE_DONE_INTERRUPT_DISABLE, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_VALUE, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_VALUE, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_ENABLE, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_SAT_LEVEL, DCHUBBUB_ARB_SAT_LEVEL, mask_sh), \ + HWS_SF(, DCHUBBUB_ARB_DF_REQ_OUTSTAND, DCHUBBUB_ARB_MIN_REQ_OUTSTAND, mask_sh), \ + HWS_SF(, DCFCLK_CNTL, DCFCLK_GATE_DIS, mask_sh) + +#define HWSEQ_DCN1_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF1(OTG0_, PHYPLL_PIXEL_RATE_CNTL, PIXEL_RATE_PLL_SOURCE, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_FB_TOP, SDPIF_FB_TOP, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_FB_BASE, SDPIF_FB_BASE, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_FB_OFFSET, SDPIF_FB_OFFSET, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_AGP_BASE, SDPIF_AGP_BASE, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_AGP_BOT, SDPIF_AGP_BOT, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_AGP_TOP, SDPIF_AGP_TOP, mask_sh), \ + HWS_SF(DPP_TOP0_, DPP_CONTROL, DPPCLK_RATE_CONTROL, mask_sh), \ + /* todo: get these from GVM instead of reading registers ourselves */\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32, PAGE_DIRECTORY_ENTRY_HI32, mask_sh),\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32, PAGE_DIRECTORY_ENTRY_LO32, mask_sh),\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32, LOGICAL_PAGE_NUMBER_HI4, mask_sh),\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32, LOGICAL_PAGE_NUMBER_LO32, mask_sh),\ + HWS_SF(, VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32, PHYSICAL_PAGE_ADDR_HI4, mask_sh),\ + HWS_SF(, VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32, PHYSICAL_PAGE_ADDR_LO32, mask_sh),\ + HWS_SF(, MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, PHYSICAL_PAGE_NUMBER_MSB, mask_sh),\ + HWS_SF(, MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, PHYSICAL_PAGE_NUMBER_LSB, mask_sh),\ + HWS_SF(, MC_VM_SYSTEM_APERTURE_LOW_ADDR, LOGICAL_ADDR, mask_sh),\ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN0_PG_STATUS, DOMAIN0_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN1_PG_STATUS, DOMAIN1_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN2_PG_STATUS, DOMAIN2_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN3_PG_STATUS, DOMAIN3_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN4_PG_STATUS, DOMAIN4_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN5_PG_STATUS, DOMAIN5_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN6_PG_STATUS, DOMAIN6_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN7_PG_STATUS, DOMAIN7_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \ + HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh), \ + HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh) + +#define HWSEQ_REG_FIELD_LIST(type) \ + type DCFE_CLOCK_ENABLE; \ + type DCFEV_CLOCK_ENABLE; \ + type DC_MEM_GLOBAL_PWR_REQ_DIS; \ + type BLND_DCP_GRPH_V_UPDATE_LOCK; \ + type BLND_SCL_V_UPDATE_LOCK; \ + type BLND_DCP_GRPH_SURF_V_UPDATE_LOCK; \ + type BLND_BLND_V_UPDATE_LOCK; \ + type BLND_V_UPDATE_LOCK_MODE; \ + type BLND_FEEDTHROUGH_EN; \ + type BLND_ALPHA_MODE; \ + type BLND_MODE; \ + type BLND_MULTIPLIED_MODE; \ + type DP_DTO0_ENABLE; \ + type PIXEL_RATE_SOURCE; \ + type PHYPLL_PIXEL_RATE_SOURCE; \ + type PIXEL_RATE_PLL_SOURCE; \ + /* todo: get these from GVM instead of reading registers ourselves */\ + type PAGE_DIRECTORY_ENTRY_HI32;\ + type PAGE_DIRECTORY_ENTRY_LO32;\ + type LOGICAL_PAGE_NUMBER_HI4;\ + type LOGICAL_PAGE_NUMBER_LO32;\ + type PHYSICAL_PAGE_ADDR_HI4;\ + type PHYSICAL_PAGE_ADDR_LO32;\ + type PHYSICAL_PAGE_NUMBER_MSB;\ + type PHYSICAL_PAGE_NUMBER_LSB;\ + type LOGICAL_ADDR; \ + type ENABLE_L1_TLB;\ + type SYSTEM_ACCESS_MODE;\ + type LVTMA_BLON;\ + type LVTMA_PWRSEQ_TARGET_STATE_R; + +#define HWSEQ_DCN_REG_FIELD_LIST(type) \ + type VUPDATE_NO_LOCK_EVENT_CLEAR; \ + type VUPDATE_NO_LOCK_EVENT_OCCURRED; \ + type HUBP_VTG_SEL; \ + type HUBP_CLOCK_ENABLE; \ + type DPP_CLOCK_ENABLE; \ + type DPPCLK_RATE_CONTROL; \ + type SDPIF_FB_TOP;\ + type SDPIF_FB_BASE;\ + type SDPIF_FB_OFFSET;\ + type SDPIF_AGP_BASE;\ + type SDPIF_AGP_BOT;\ + type SDPIF_AGP_TOP;\ + type FB_TOP;\ + type FB_BASE;\ + type FB_OFFSET;\ + type AGP_BASE;\ + type AGP_BOT;\ + type AGP_TOP;\ + type DCHUBBUB_GLOBAL_TIMER_ENABLE; \ + type DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST;\ + type DCHUBBUB_ARB_WATERMARK_CHANGE_DONE_INTERRUPT_DISABLE;\ + type DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_VALUE;\ + type DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE;\ + type DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_VALUE;\ + type DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_ENABLE;\ + type DCHUBBUB_ARB_SAT_LEVEL;\ + type DCHUBBUB_ARB_MIN_REQ_OUTSTAND;\ + type OPP_PIPE_CLOCK_EN;\ + type IP_REQUEST_EN; \ + type DOMAIN0_POWER_FORCEON; \ + type DOMAIN0_POWER_GATE; \ + type DOMAIN1_POWER_FORCEON; \ + type DOMAIN1_POWER_GATE; \ + type DOMAIN2_POWER_FORCEON; \ + type DOMAIN2_POWER_GATE; \ + type DOMAIN3_POWER_FORCEON; \ + type DOMAIN3_POWER_GATE; \ + type DOMAIN4_POWER_FORCEON; \ + type DOMAIN4_POWER_GATE; \ + type DOMAIN5_POWER_FORCEON; \ + type DOMAIN5_POWER_GATE; \ + type DOMAIN6_POWER_FORCEON; \ + type DOMAIN6_POWER_GATE; \ + type DOMAIN7_POWER_FORCEON; \ + type DOMAIN7_POWER_GATE; \ + type DOMAIN0_PGFSM_PWR_STATUS; \ + type DOMAIN1_PGFSM_PWR_STATUS; \ + type DOMAIN2_PGFSM_PWR_STATUS; \ + type DOMAIN3_PGFSM_PWR_STATUS; \ + type DOMAIN4_PGFSM_PWR_STATUS; \ + type DOMAIN5_PGFSM_PWR_STATUS; \ + type DOMAIN6_PGFSM_PWR_STATUS; \ + type DOMAIN7_PGFSM_PWR_STATUS; \ + type DCFCLK_GATE_DIS; \ + type DCHUBBUB_GLOBAL_TIMER_REFDIV; \ + type DENTIST_DPPCLK_WDIVIDER; + +struct dce_hwseq_shift { + HWSEQ_REG_FIELD_LIST(uint8_t) + HWSEQ_DCN_REG_FIELD_LIST(uint8_t) +}; + +struct dce_hwseq_mask { + HWSEQ_REG_FIELD_LIST(uint32_t) + HWSEQ_DCN_REG_FIELD_LIST(uint32_t) +}; + + +enum blnd_mode { + BLND_MODE_CURRENT_PIPE = 0,/* Data from current pipe only */ + BLND_MODE_OTHER_PIPE, /* Data from other pipe only */ + BLND_MODE_BLENDING,/* Alpha blending - blend 'current' and 'other' */ +}; + +void dce_enable_fe_clock(struct dce_hwseq *hwss, + unsigned int inst, bool enable); + +void dce_pipe_control_lock(struct dc *dc, + struct pipe_ctx *pipe, + bool lock); + +void dce_set_blender_mode(struct dce_hwseq *hws, + unsigned int blnd_inst, enum blnd_mode mode); + +void dce_clock_gating_power_up(struct dce_hwseq *hws, + bool enable); + +void dce_crtc_switch_to_clk_src(struct dce_hwseq *hws, + struct clock_source *clk_src, + unsigned int tg_inst); + +bool dce_use_lut(const struct dc_plane_state *plane_state); +#endif /*__DCE_HWSEQ_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c new file mode 100644 index 000000000000..d618fdd0cc82 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c @@ -0,0 +1,268 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce_ipp.h" +#include "reg_helper.h" +#include "dm_services.h" + +#define REG(reg) \ + (ipp_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + ipp_dce->ipp_shift->field_name, ipp_dce->ipp_mask->field_name + +#define CTX \ + ipp_dce->base.ctx + + +static void dce_ipp_cursor_set_position( + struct input_pixel_processor *ipp, + const struct dc_cursor_position *position, + const struct dc_cursor_mi_param *param) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + + /* lock cursor registers */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, true); + + /* Flag passed in structure differentiates cursor enable/disable. */ + /* Update if it differs from cached state. */ + REG_UPDATE(CUR_CONTROL, CURSOR_EN, position->enable); + + REG_SET_2(CUR_POSITION, 0, + CURSOR_X_POSITION, position->x, + CURSOR_Y_POSITION, position->y); + + REG_SET_2(CUR_HOT_SPOT, 0, + CURSOR_HOT_SPOT_X, position->x_hotspot, + CURSOR_HOT_SPOT_Y, position->y_hotspot); + + /* unlock cursor registers */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, false); +} + +static void dce_ipp_cursor_set_attributes( + struct input_pixel_processor *ipp, + const struct dc_cursor_attributes *attributes) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + int mode; + + /* Lock cursor registers */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, true); + + /* Program cursor control */ + switch (attributes->color_format) { + case CURSOR_MODE_MONO: + mode = 0; + break; + case CURSOR_MODE_COLOR_1BIT_AND: + mode = 1; + break; + case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: + mode = 2; + break; + case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA: + mode = 3; + break; + default: + BREAK_TO_DEBUGGER(); /* unsupported */ + mode = 0; + } + + REG_UPDATE_3(CUR_CONTROL, + CURSOR_MODE, mode, + CURSOR_2X_MAGNIFY, attributes->attribute_flags.bits.ENABLE_MAGNIFICATION, + CUR_INV_TRANS_CLAMP, attributes->attribute_flags.bits.INVERSE_TRANSPARENT_CLAMPING); + + if (attributes->color_format == CURSOR_MODE_MONO) { + REG_SET_3(CUR_COLOR1, 0, + CUR_COLOR1_BLUE, 0, + CUR_COLOR1_GREEN, 0, + CUR_COLOR1_RED, 0); + + REG_SET_3(CUR_COLOR2, 0, + CUR_COLOR2_BLUE, 0xff, + CUR_COLOR2_GREEN, 0xff, + CUR_COLOR2_RED, 0xff); + } + + /* + * Program cursor size -- NOTE: HW spec specifies that HW register + * stores size as (height - 1, width - 1) + */ + REG_SET_2(CUR_SIZE, 0, + CURSOR_WIDTH, attributes->width-1, + CURSOR_HEIGHT, attributes->height-1); + + /* Program cursor surface address */ + /* SURFACE_ADDRESS_HIGH: Higher order bits (39:32) of hardware cursor + * surface base address in byte. It is 4K byte aligned. + * The correct way to program cursor surface address is to first write + * to CUR_SURFACE_ADDRESS_HIGH, and then write to CUR_SURFACE_ADDRESS + */ + REG_SET(CUR_SURFACE_ADDRESS_HIGH, 0, + CURSOR_SURFACE_ADDRESS_HIGH, attributes->address.high_part); + + REG_SET(CUR_SURFACE_ADDRESS, 0, + CURSOR_SURFACE_ADDRESS, attributes->address.low_part); + + /* Unlock Cursor registers. */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, false); +} + + +static void dce_ipp_program_prescale( + struct input_pixel_processor *ipp, + struct ipp_prescale_params *params) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + + /* set to bypass mode first before change */ + REG_UPDATE(PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS, + 1); + + REG_SET_2(PRESCALE_VALUES_GRPH_R, 0, + GRPH_PRESCALE_SCALE_R, params->scale, + GRPH_PRESCALE_BIAS_R, params->bias); + + REG_SET_2(PRESCALE_VALUES_GRPH_G, 0, + GRPH_PRESCALE_SCALE_G, params->scale, + GRPH_PRESCALE_BIAS_G, params->bias); + + REG_SET_2(PRESCALE_VALUES_GRPH_B, 0, + GRPH_PRESCALE_SCALE_B, params->scale, + GRPH_PRESCALE_BIAS_B, params->bias); + + if (params->mode != IPP_PRESCALE_MODE_BYPASS) { + REG_UPDATE(PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS, 0); + + /* If prescale is in use, then legacy lut should be bypassed */ + REG_UPDATE(INPUT_GAMMA_CONTROL, + GRPH_INPUT_GAMMA_MODE, 1); + } +} + +static void dce_ipp_program_input_lut( + struct input_pixel_processor *ipp, + const struct dc_gamma *gamma) +{ + int i; + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + + /* power on LUT memory */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_SET(DCFE_MEM_PWR_CTRL, 0, DCP_LUT_MEM_PWR_DIS, 1); + + /* enable all */ + REG_SET(DC_LUT_WRITE_EN_MASK, 0, DC_LUT_WRITE_EN_MASK, 0x7); + + /* 256 entry mode */ + REG_UPDATE(DC_LUT_RW_MODE, DC_LUT_RW_MODE, 0); + + /* LUT-256, unsigned, integer, new u0.12 format */ + REG_SET_3(DC_LUT_CONTROL, 0, + DC_LUT_DATA_R_FORMAT, 3, + DC_LUT_DATA_G_FORMAT, 3, + DC_LUT_DATA_B_FORMAT, 3); + + /* start from index 0 */ + REG_SET(DC_LUT_RW_INDEX, 0, + DC_LUT_RW_INDEX, 0); + + for (i = 0; i < gamma->num_entries; i++) { + REG_SET(DC_LUT_SEQ_COLOR, 0, DC_LUT_SEQ_COLOR, + dal_fixed31_32_round( + gamma->entries.red[i])); + REG_SET(DC_LUT_SEQ_COLOR, 0, DC_LUT_SEQ_COLOR, + dal_fixed31_32_round( + gamma->entries.green[i])); + REG_SET(DC_LUT_SEQ_COLOR, 0, DC_LUT_SEQ_COLOR, + dal_fixed31_32_round( + gamma->entries.blue[i])); + } + + /* power off LUT memory */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_SET(DCFE_MEM_PWR_CTRL, 0, DCP_LUT_MEM_PWR_DIS, 0); + + /* bypass prescale, enable legacy LUT */ + REG_UPDATE(PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, 1); + REG_UPDATE(INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, 0); +} + +static void dce_ipp_set_degamma( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + uint32_t degamma_type = (mode == IPP_DEGAMMA_MODE_HW_sRGB) ? 1 : 0; + + ASSERT(mode == IPP_DEGAMMA_MODE_BYPASS || + mode == IPP_DEGAMMA_MODE_HW_sRGB); + + REG_SET_3(DEGAMMA_CONTROL, 0, + GRPH_DEGAMMA_MODE, degamma_type, + CURSOR_DEGAMMA_MODE, degamma_type, + CURSOR2_DEGAMMA_MODE, degamma_type); +} + +static const struct ipp_funcs dce_ipp_funcs = { + .ipp_cursor_set_attributes = dce_ipp_cursor_set_attributes, + .ipp_cursor_set_position = dce_ipp_cursor_set_position, + .ipp_program_prescale = dce_ipp_program_prescale, + .ipp_program_input_lut = dce_ipp_program_input_lut, + .ipp_set_degamma = dce_ipp_set_degamma +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void dce_ipp_construct( + struct dce_ipp *ipp_dce, + struct dc_context *ctx, + int inst, + const struct dce_ipp_registers *regs, + const struct dce_ipp_shift *ipp_shift, + const struct dce_ipp_mask *ipp_mask) +{ + ipp_dce->base.ctx = ctx; + ipp_dce->base.inst = inst; + ipp_dce->base.funcs = &dce_ipp_funcs; + + ipp_dce->regs = regs; + ipp_dce->ipp_shift = ipp_shift; + ipp_dce->ipp_mask = ipp_mask; +} + +void dce_ipp_destroy(struct input_pixel_processor **ipp) +{ + kfree(TO_DCE_IPP(*ipp)); + *ipp = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.h b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.h new file mode 100644 index 000000000000..ca04e97d44c3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.h @@ -0,0 +1,238 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _DCE_IPP_H_ +#define _DCE_IPP_H_ + +#include "ipp.h" + +#define TO_DCE_IPP(ipp)\ + container_of(ipp, struct dce_ipp, base) + +#define IPP_COMMON_REG_LIST_DCE_BASE(id) \ + SRI(CUR_UPDATE, DCP, id), \ + SRI(CUR_CONTROL, DCP, id), \ + SRI(CUR_POSITION, DCP, id), \ + SRI(CUR_HOT_SPOT, DCP, id), \ + SRI(CUR_COLOR1, DCP, id), \ + SRI(CUR_COLOR2, DCP, id), \ + SRI(CUR_SIZE, DCP, id), \ + SRI(CUR_SURFACE_ADDRESS_HIGH, DCP, id), \ + SRI(CUR_SURFACE_ADDRESS, DCP, id), \ + SRI(PRESCALE_GRPH_CONTROL, DCP, id), \ + SRI(PRESCALE_VALUES_GRPH_R, DCP, id), \ + SRI(PRESCALE_VALUES_GRPH_G, DCP, id), \ + SRI(PRESCALE_VALUES_GRPH_B, DCP, id), \ + SRI(INPUT_GAMMA_CONTROL, DCP, id), \ + SRI(DC_LUT_WRITE_EN_MASK, DCP, id), \ + SRI(DC_LUT_RW_MODE, DCP, id), \ + SRI(DC_LUT_CONTROL, DCP, id), \ + SRI(DC_LUT_RW_INDEX, DCP, id), \ + SRI(DC_LUT_SEQ_COLOR, DCP, id), \ + SRI(DEGAMMA_CONTROL, DCP, id) + +#define IPP_DCE100_REG_LIST_DCE_BASE(id) \ + IPP_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, CRTC, id) + +#define IPP_DCE110_REG_LIST_DCE_BASE(id) \ + IPP_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, DCFE, id) + +#define IPP_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + IPP_SF(CUR_UPDATE, CURSOR_UPDATE_LOCK, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_EN, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_MODE, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + IPP_SF(CUR_CONTROL, CUR_INV_TRANS_CLAMP, mask_sh), \ + IPP_SF(CUR_POSITION, CURSOR_X_POSITION, mask_sh), \ + IPP_SF(CUR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + IPP_SF(CUR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + IPP_SF(CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_BLUE, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_GREEN, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_RED, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_BLUE, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_GREEN, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_RED, mask_sh), \ + IPP_SF(CUR_SIZE, CURSOR_WIDTH, mask_sh), \ + IPP_SF(CUR_SIZE, CURSOR_HEIGHT, mask_sh), \ + IPP_SF(CUR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + IPP_SF(CUR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + IPP_SF(PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_SCALE_R, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_BIAS_R, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_SCALE_G, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_BIAS_G, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_SCALE_B, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_BIAS_B, mask_sh), \ + IPP_SF(INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, mask_sh), \ + IPP_SF(DC_LUT_WRITE_EN_MASK, DC_LUT_WRITE_EN_MASK, mask_sh), \ + IPP_SF(DC_LUT_RW_MODE, DC_LUT_RW_MODE, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_R_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_G_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_B_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_RW_INDEX, DC_LUT_RW_INDEX, mask_sh), \ + IPP_SF(DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, GRPH_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, CURSOR_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, CURSOR2_DEGAMMA_MODE, mask_sh) + +#define IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + IPP_SF(DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh) + +#define IPP_DCE120_MASK_SH_LIST_SOC_BASE(mask_sh) \ + IPP_SF(DCP0_CUR_UPDATE, CURSOR_UPDATE_LOCK, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CURSOR_EN, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CURSOR_MODE, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CUR_INV_TRANS_CLAMP, mask_sh), \ + IPP_SF(DCP0_CUR_POSITION, CURSOR_X_POSITION, mask_sh), \ + IPP_SF(DCP0_CUR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + IPP_SF(DCP0_CUR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + IPP_SF(DCP0_CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR1, CUR_COLOR1_BLUE, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR1, CUR_COLOR1_GREEN, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR1, CUR_COLOR1_RED, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR2, CUR_COLOR2_BLUE, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR2, CUR_COLOR2_GREEN, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR2, CUR_COLOR2_RED, mask_sh), \ + IPP_SF(DCP0_CUR_SIZE, CURSOR_WIDTH, mask_sh), \ + IPP_SF(DCP0_CUR_SIZE, CURSOR_HEIGHT, mask_sh), \ + IPP_SF(DCP0_CUR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + IPP_SF(DCP0_CUR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + IPP_SF(DCP0_PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_SCALE_R, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_BIAS_R, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_SCALE_G, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_BIAS_G, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_SCALE_B, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_BIAS_B, mask_sh), \ + IPP_SF(DCP0_INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, mask_sh), \ + IPP_SF(DCFE0_DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh), \ + IPP_SF(DCP0_DC_LUT_WRITE_EN_MASK, DC_LUT_WRITE_EN_MASK, mask_sh), \ + IPP_SF(DCP0_DC_LUT_RW_MODE, DC_LUT_RW_MODE, mask_sh), \ + IPP_SF(DCP0_DC_LUT_CONTROL, DC_LUT_DATA_R_FORMAT, mask_sh), \ + IPP_SF(DCP0_DC_LUT_CONTROL, DC_LUT_DATA_G_FORMAT, mask_sh), \ + IPP_SF(DCP0_DC_LUT_CONTROL, DC_LUT_DATA_B_FORMAT, mask_sh), \ + IPP_SF(DCP0_DC_LUT_RW_INDEX, DC_LUT_RW_INDEX, mask_sh), \ + IPP_SF(DCP0_DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, mask_sh), \ + IPP_SF(DCP0_DEGAMMA_CONTROL, GRPH_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DCP0_DEGAMMA_CONTROL, CURSOR_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DCP0_DEGAMMA_CONTROL, CURSOR2_DEGAMMA_MODE, mask_sh) + +#define IPP_REG_FIELD_LIST(type) \ + type CURSOR_UPDATE_LOCK; \ + type CURSOR_EN; \ + type CURSOR_X_POSITION; \ + type CURSOR_Y_POSITION; \ + type CURSOR_HOT_SPOT_X; \ + type CURSOR_HOT_SPOT_Y; \ + type CURSOR_MODE; \ + type CURSOR_2X_MAGNIFY; \ + type CUR_INV_TRANS_CLAMP; \ + type CUR_COLOR1_BLUE; \ + type CUR_COLOR1_GREEN; \ + type CUR_COLOR1_RED; \ + type CUR_COLOR2_BLUE; \ + type CUR_COLOR2_GREEN; \ + type CUR_COLOR2_RED; \ + type CURSOR_WIDTH; \ + type CURSOR_HEIGHT; \ + type CURSOR_SURFACE_ADDRESS_HIGH; \ + type CURSOR_SURFACE_ADDRESS; \ + type GRPH_PRESCALE_BYPASS; \ + type GRPH_PRESCALE_SCALE_R; \ + type GRPH_PRESCALE_BIAS_R; \ + type GRPH_PRESCALE_SCALE_G; \ + type GRPH_PRESCALE_BIAS_G; \ + type GRPH_PRESCALE_SCALE_B; \ + type GRPH_PRESCALE_BIAS_B; \ + type GRPH_INPUT_GAMMA_MODE; \ + type DCP_LUT_MEM_PWR_DIS; \ + type DC_LUT_WRITE_EN_MASK; \ + type DC_LUT_RW_MODE; \ + type DC_LUT_DATA_R_FORMAT; \ + type DC_LUT_DATA_G_FORMAT; \ + type DC_LUT_DATA_B_FORMAT; \ + type DC_LUT_RW_INDEX; \ + type DC_LUT_SEQ_COLOR; \ + type GRPH_DEGAMMA_MODE; \ + type CURSOR_DEGAMMA_MODE; \ + type CURSOR2_DEGAMMA_MODE + +struct dce_ipp_shift { + IPP_REG_FIELD_LIST(uint8_t); +}; + +struct dce_ipp_mask { + IPP_REG_FIELD_LIST(uint32_t); +}; + +struct dce_ipp_registers { + uint32_t CUR_UPDATE; + uint32_t CUR_CONTROL; + uint32_t CUR_POSITION; + uint32_t CUR_HOT_SPOT; + uint32_t CUR_COLOR1; + uint32_t CUR_COLOR2; + uint32_t CUR_SIZE; + uint32_t CUR_SURFACE_ADDRESS_HIGH; + uint32_t CUR_SURFACE_ADDRESS; + uint32_t PRESCALE_GRPH_CONTROL; + uint32_t PRESCALE_VALUES_GRPH_R; + uint32_t PRESCALE_VALUES_GRPH_G; + uint32_t PRESCALE_VALUES_GRPH_B; + uint32_t INPUT_GAMMA_CONTROL; + uint32_t DCFE_MEM_PWR_CTRL; + uint32_t DC_LUT_WRITE_EN_MASK; + uint32_t DC_LUT_RW_MODE; + uint32_t DC_LUT_CONTROL; + uint32_t DC_LUT_RW_INDEX; + uint32_t DC_LUT_SEQ_COLOR; + uint32_t DEGAMMA_CONTROL; +}; + +struct dce_ipp { + struct input_pixel_processor base; + const struct dce_ipp_registers *regs; + const struct dce_ipp_shift *ipp_shift; + const struct dce_ipp_mask *ipp_mask; +}; + +void dce_ipp_construct(struct dce_ipp *ipp_dce, + struct dc_context *ctx, + int inst, + const struct dce_ipp_registers *regs, + const struct dce_ipp_shift *ipp_shift, + const struct dce_ipp_mask *ipp_mask); + +void dce_ipp_destroy(struct input_pixel_processor **ipp); + +#endif /* _DCE_IPP_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c new file mode 100644 index 000000000000..fe88852b4774 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c @@ -0,0 +1,1379 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "reg_helper.h" + +#include "core_types.h" +#include "link_encoder.h" +#include "dce_link_encoder.h" +#include "stream_encoder.h" +#include "i2caux_interface.h" +#include "dc_bios_types.h" + +#include "gpio_service_interface.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "dce/dce_11_0_enum.h" + +#ifndef DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE__SHIFT +#define DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE__SHIFT 0xa +#endif + +#ifndef DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE_MASK +#define DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE_MASK 0x00000400L +#endif + +#ifndef HPD0_DC_HPD_CONTROL__DC_HPD_EN_MASK +#define HPD0_DC_HPD_CONTROL__DC_HPD_EN_MASK 0x10000000L +#endif + +#ifndef HPD0_DC_HPD_CONTROL__DC_HPD_EN__SHIFT +#define HPD0_DC_HPD_CONTROL__DC_HPD_EN__SHIFT 0x1c +#endif + +#define CTX \ + enc110->base.ctx + +#define REG(reg)\ + (enc110->link_regs->reg) + +#define AUX_REG(reg)\ + (enc110->aux_regs->reg) + +#define HPD_REG(reg)\ + (enc110->hpd_regs->reg) + +#define DEFAULT_AUX_MAX_DATA_SIZE 16 +#define AUX_MAX_DEFER_WRITE_RETRY 20 +/* + * @brief + * Trigger Source Select + * ASIC-dependent, actual values for register programming + */ +#define DCE110_DIG_FE_SOURCE_SELECT_INVALID 0x0 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGA 0x1 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGB 0x2 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGC 0x4 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGD 0x08 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGE 0x10 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGF 0x20 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGG 0x40 + +/* Minimum pixel clock, in KHz. For TMDS signal is 25.00 MHz */ +#define TMDS_MIN_PIXEL_CLOCK 25000 +/* Maximum pixel clock, in KHz. For TMDS signal is 165.00 MHz */ +#define TMDS_MAX_PIXEL_CLOCK 165000 +/* For current ASICs pixel clock - 600MHz */ +#define MAX_ENCODER_CLOCK 600000 + +enum { + DP_MST_UPDATE_MAX_RETRY = 50 +}; + +#define DIG_REG(reg)\ + (reg + enc110->offsets.dig) + +#define DP_REG(reg)\ + (reg + enc110->offsets.dp) + +static const struct link_encoder_funcs dce110_lnk_enc_funcs = { + .validate_output_with_stream = + dce110_link_encoder_validate_output_with_stream, + .hw_init = dce110_link_encoder_hw_init, + .setup = dce110_link_encoder_setup, + .enable_tmds_output = dce110_link_encoder_enable_tmds_output, + .enable_dp_output = dce110_link_encoder_enable_dp_output, + .enable_dp_mst_output = dce110_link_encoder_enable_dp_mst_output, + .disable_output = dce110_link_encoder_disable_output, + .dp_set_lane_settings = dce110_link_encoder_dp_set_lane_settings, + .dp_set_phy_pattern = dce110_link_encoder_dp_set_phy_pattern, + .update_mst_stream_allocation_table = + dce110_link_encoder_update_mst_stream_allocation_table, + .psr_program_dp_dphy_fast_training = + dce110_psr_program_dp_dphy_fast_training, + .psr_program_secondary_packet = dce110_psr_program_secondary_packet, + .connect_dig_be_to_fe = dce110_link_encoder_connect_dig_be_to_fe, + .enable_hpd = dce110_link_encoder_enable_hpd, + .disable_hpd = dce110_link_encoder_disable_hpd, + .destroy = dce110_link_encoder_destroy +}; + +static enum bp_result link_transmitter_control( + struct dce110_link_encoder *enc110, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + struct dc_bios *bp = enc110->base.ctx->dc_bios; + + result = bp->funcs->transmitter_control(bp, cntl); + + return result; +} + +static void enable_phy_bypass_mode( + struct dce110_link_encoder *enc110, + bool enable) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_UPDATE(DP_DPHY_CNTL, DPHY_BYPASS, enable); + +} + +static void disable_prbs_symbols( + struct dce110_link_encoder *enc110, + bool disable) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_UPDATE_4(DP_DPHY_CNTL, + DPHY_ATEST_SEL_LANE0, disable, + DPHY_ATEST_SEL_LANE1, disable, + DPHY_ATEST_SEL_LANE2, disable, + DPHY_ATEST_SEL_LANE3, disable); +} + +static void disable_prbs_mode( + struct dce110_link_encoder *enc110) +{ + REG_UPDATE(DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN, 0); +} + +static void program_pattern_symbols( + struct dce110_link_encoder *enc110, + uint16_t pattern_symbols[8]) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_SET_3(DP_DPHY_SYM0, 0, + DPHY_SYM1, pattern_symbols[0], + DPHY_SYM2, pattern_symbols[1], + DPHY_SYM3, pattern_symbols[2]); + + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_SET_3(DP_DPHY_SYM1, 0, + DPHY_SYM4, pattern_symbols[3], + DPHY_SYM5, pattern_symbols[4], + DPHY_SYM6, pattern_symbols[5]); + + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_SET_2(DP_DPHY_SYM2, 0, + DPHY_SYM7, pattern_symbols[6], + DPHY_SYM8, pattern_symbols[7]); +} + +static void set_dp_phy_pattern_d102( + struct dce110_link_encoder *enc110) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* For 10-bit PRBS or debug symbols + * please use the following sequence: */ + + /* Enable debug symbols on the lanes */ + + disable_prbs_symbols(enc110, true); + + /* Disable PRBS mode */ + disable_prbs_mode(enc110); + + /* Program debug symbols to be output */ + { + uint16_t pattern_symbols[8] = { + 0x2AA, 0x2AA, 0x2AA, 0x2AA, + 0x2AA, 0x2AA, 0x2AA, 0x2AA + }; + + program_pattern_symbols(enc110, pattern_symbols); + } + + /* Enable phy bypass mode to enable the test pattern */ + + enable_phy_bypass_mode(enc110, true); +} + +static void set_link_training_complete( + struct dce110_link_encoder *enc110, + bool complete) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_UPDATE(DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE, complete); + +} + +void dce110_link_encoder_set_dp_phy_pattern_training_pattern( + struct link_encoder *enc, + uint32_t index) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + /* Write Training Pattern */ + + REG_WRITE(DP_DPHY_TRAINING_PATTERN_SEL, index); + + /* Set HW Register Training Complete to false */ + + set_link_training_complete(enc110, false); + + /* Disable PHY Bypass mode to output Training Pattern */ + + enable_phy_bypass_mode(enc110, false); + + /* Disable PRBS mode */ + disable_prbs_mode(enc110); +} + +static void setup_panel_mode( + struct dce110_link_encoder *enc110, + enum dp_panel_mode panel_mode) +{ + uint32_t value; + + ASSERT(REG(DP_DPHY_INTERNAL_CTRL)); + value = REG_READ(DP_DPHY_INTERNAL_CTRL); + + switch (panel_mode) { + case DP_PANEL_MODE_EDP: + value = 0x1; + break; + case DP_PANEL_MODE_SPECIAL: + value = 0x11; + break; + default: + value = 0x0; + break; + } + + REG_WRITE(DP_DPHY_INTERNAL_CTRL, value); +} + +static void set_dp_phy_pattern_symbol_error( + struct dce110_link_encoder *enc110) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* program correct panel mode*/ + setup_panel_mode(enc110, DP_PANEL_MODE_DEFAULT); + + /* A PRBS23 pattern is used for most DP electrical measurements. */ + + /* Enable PRBS symbols on the lanes */ + disable_prbs_symbols(enc110, false); + + /* For PRBS23 Set bit DPHY_PRBS_SEL=1 and Set bit DPHY_PRBS_EN=1 */ + REG_UPDATE_2(DP_DPHY_PRBS_CNTL, + DPHY_PRBS_SEL, 1, + DPHY_PRBS_EN, 1); + + /* Enable phy bypass mode to enable the test pattern */ + enable_phy_bypass_mode(enc110, true); +} + +static void set_dp_phy_pattern_prbs7( + struct dce110_link_encoder *enc110) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* A PRBS7 pattern is used for most DP electrical measurements. */ + + /* Enable PRBS symbols on the lanes */ + disable_prbs_symbols(enc110, false); + + /* For PRBS7 Set bit DPHY_PRBS_SEL=0 and Set bit DPHY_PRBS_EN=1 */ + REG_UPDATE_2(DP_DPHY_PRBS_CNTL, + DPHY_PRBS_SEL, 0, + DPHY_PRBS_EN, 1); + + /* Enable phy bypass mode to enable the test pattern */ + enable_phy_bypass_mode(enc110, true); +} + +static void set_dp_phy_pattern_80bit_custom( + struct dce110_link_encoder *enc110, + const uint8_t *pattern) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* Enable debug symbols on the lanes */ + + disable_prbs_symbols(enc110, true); + + /* Enable PHY bypass mode to enable the test pattern */ + /* TODO is it really needed ? */ + + enable_phy_bypass_mode(enc110, true); + + /* Program 80 bit custom pattern */ + { + uint16_t pattern_symbols[8]; + + pattern_symbols[0] = + ((pattern[1] & 0x03) << 8) | pattern[0]; + pattern_symbols[1] = + ((pattern[2] & 0x0f) << 6) | ((pattern[1] >> 2) & 0x3f); + pattern_symbols[2] = + ((pattern[3] & 0x3f) << 4) | ((pattern[2] >> 4) & 0x0f); + pattern_symbols[3] = + (pattern[4] << 2) | ((pattern[3] >> 6) & 0x03); + pattern_symbols[4] = + ((pattern[6] & 0x03) << 8) | pattern[5]; + pattern_symbols[5] = + ((pattern[7] & 0x0f) << 6) | ((pattern[6] >> 2) & 0x3f); + pattern_symbols[6] = + ((pattern[8] & 0x3f) << 4) | ((pattern[7] >> 4) & 0x0f); + pattern_symbols[7] = + (pattern[9] << 2) | ((pattern[8] >> 6) & 0x03); + + program_pattern_symbols(enc110, pattern_symbols); + } + + /* Enable phy bypass mode to enable the test pattern */ + + enable_phy_bypass_mode(enc110, true); +} + +static void set_dp_phy_pattern_hbr2_compliance_cp2520_2( + struct dce110_link_encoder *enc110, + unsigned int cp2520_pattern) +{ + + /* previously there is a register DP_HBR2_EYE_PATTERN + * that is enabled to get the pattern. + * But it does not work with the latest spec change, + * so we are programming the following registers manually. + * + * The following settings have been confirmed + * by Nick Chorney and Sandra Liu */ + + /* Disable PHY Bypass mode to setup the test pattern */ + + enable_phy_bypass_mode(enc110, false); + + /* Setup DIG encoder in DP SST mode */ + enc110->base.funcs->setup(&enc110->base, SIGNAL_TYPE_DISPLAY_PORT); + + /* ensure normal panel mode. */ + setup_panel_mode(enc110, DP_PANEL_MODE_DEFAULT); + + /* no vbid after BS (SR) + * DP_LINK_FRAMING_CNTL changed history Sandra Liu + * 11000260 / 11000104 / 110000FC */ + REG_UPDATE_3(DP_LINK_FRAMING_CNTL, + DP_IDLE_BS_INTERVAL, 0xFC, + DP_VBID_DISABLE, 1, + DP_VID_ENHANCED_FRAME_MODE, 1); + + /* swap every BS with SR */ + REG_UPDATE(DP_DPHY_SCRAM_CNTL, DPHY_SCRAMBLER_BS_COUNT, 0); + + /* select cp2520 patterns */ + if (REG(DP_DPHY_HBR2_PATTERN_CONTROL)) + REG_UPDATE(DP_DPHY_HBR2_PATTERN_CONTROL, + DP_DPHY_HBR2_PATTERN_CONTROL, cp2520_pattern); + else + /* pre-DCE11 can only generate CP2520 pattern 2 */ + ASSERT(cp2520_pattern == 2); + + /* set link training complete */ + set_link_training_complete(enc110, true); + + /* disable video stream */ + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, 0); + + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); +} + +static void set_dp_phy_pattern_passthrough_mode( + struct dce110_link_encoder *enc110, + enum dp_panel_mode panel_mode) +{ + /* program correct panel mode */ + setup_panel_mode(enc110, panel_mode); + + /* restore LINK_FRAMING_CNTL and DPHY_SCRAMBLER_BS_COUNT + * in case we were doing HBR2 compliance pattern before + */ + REG_UPDATE_3(DP_LINK_FRAMING_CNTL, + DP_IDLE_BS_INTERVAL, 0x2000, + DP_VBID_DISABLE, 0, + DP_VID_ENHANCED_FRAME_MODE, 1); + + REG_UPDATE(DP_DPHY_SCRAM_CNTL, DPHY_SCRAMBLER_BS_COUNT, 0x1FF); + + /* set link training complete */ + set_link_training_complete(enc110, true); + + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* Disable PRBS mode */ + disable_prbs_mode(enc110); +} + +/* return value is bit-vector */ +static uint8_t get_frontend_source( + enum engine_id engine) +{ + switch (engine) { + case ENGINE_ID_DIGA: + return DCE110_DIG_FE_SOURCE_SELECT_DIGA; + case ENGINE_ID_DIGB: + return DCE110_DIG_FE_SOURCE_SELECT_DIGB; + case ENGINE_ID_DIGC: + return DCE110_DIG_FE_SOURCE_SELECT_DIGC; + case ENGINE_ID_DIGD: + return DCE110_DIG_FE_SOURCE_SELECT_DIGD; + case ENGINE_ID_DIGE: + return DCE110_DIG_FE_SOURCE_SELECT_DIGE; + case ENGINE_ID_DIGF: + return DCE110_DIG_FE_SOURCE_SELECT_DIGF; + case ENGINE_ID_DIGG: + return DCE110_DIG_FE_SOURCE_SELECT_DIGG; + default: + ASSERT_CRITICAL(false); + return DCE110_DIG_FE_SOURCE_SELECT_INVALID; + } +} + +static void configure_encoder( + struct dce110_link_encoder *enc110, + const struct dc_link_settings *link_settings) +{ + /* set number of lanes */ + + REG_SET(DP_CONFIG, 0, + DP_UDI_LANES, link_settings->lane_count - LANE_COUNT_ONE); + + /* setup scrambler */ + REG_UPDATE(DP_DPHY_SCRAM_CNTL, DPHY_SCRAMBLER_ADVANCE, 1); +} + +static void aux_initialize( + struct dce110_link_encoder *enc110) +{ + struct dc_context *ctx = enc110->base.ctx; + enum hpd_source_id hpd_source = enc110->base.hpd_source; + uint32_t addr = AUX_REG(AUX_CONTROL); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, hpd_source, AUX_CONTROL, AUX_HPD_SEL); + set_reg_field_value(value, 0, AUX_CONTROL, AUX_LS_READ_EN); + dm_write_reg(ctx, addr, value); + + addr = AUX_REG(AUX_DPHY_RX_CONTROL0); + value = dm_read_reg(ctx, addr); + + /* 1/4 window (the maximum allowed) */ + set_reg_field_value(value, 1, + AUX_DPHY_RX_CONTROL0, AUX_RX_RECEIVE_WINDOW); + dm_write_reg(ctx, addr, value); + +} + +void dce110_psr_program_dp_dphy_fast_training(struct link_encoder *enc, + bool exit_link_training_required) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + if (exit_link_training_required) + REG_UPDATE(DP_DPHY_FAST_TRAINING, + DPHY_RX_FAST_TRAINING_CAPABLE, 1); + else { + REG_UPDATE(DP_DPHY_FAST_TRAINING, + DPHY_RX_FAST_TRAINING_CAPABLE, 0); + /*In DCE 11, we are able to pre-program a Force SR register + * to be able to trigger SR symbol after 5 idle patterns + * transmitted. Upon PSR Exit, DMCU can trigger + * DPHY_LOAD_BS_COUNT_START = 1. Upon writing 1 to + * DPHY_LOAD_BS_COUNT_START and the internal counter + * reaches DPHY_LOAD_BS_COUNT, the next BS symbol will be + * replaced by SR symbol once. + */ + + REG_UPDATE(DP_DPHY_BS_SR_SWAP_CNTL, DPHY_LOAD_BS_COUNT, 0x5); + } +} + +void dce110_psr_program_secondary_packet(struct link_encoder *enc, + unsigned int sdp_transmit_line_num_deadline) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + REG_UPDATE_2(DP_SEC_CNTL1, + DP_SEC_GSP0_LINE_NUM, sdp_transmit_line_num_deadline, + DP_SEC_GSP0_PRIORITY, 1); +} + +static bool is_dig_enabled(const struct dce110_link_encoder *enc110) +{ + uint32_t value; + + REG_GET(DIG_BE_EN_CNTL, DIG_ENABLE, &value); + return value; +} + +static void link_encoder_disable(struct dce110_link_encoder *enc110) +{ + /* reset training pattern */ + REG_SET(DP_DPHY_TRAINING_PATTERN_SEL, 0, + DPHY_TRAINING_PATTERN_SEL, 0); + + /* reset training complete */ + REG_UPDATE(DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE, 0); + + /* reset panel mode */ + setup_panel_mode(enc110, DP_PANEL_MODE_DEFAULT); +} + +static void hpd_initialize( + struct dce110_link_encoder *enc110) +{ + /* Associate HPD with DIG_BE */ + enum hpd_source_id hpd_source = enc110->base.hpd_source; + + REG_UPDATE(DIG_BE_CNTL, DIG_HPD_SELECT, hpd_source); +} + +bool dce110_link_encoder_validate_dvi_output( + const struct dce110_link_encoder *enc110, + enum signal_type connector_signal, + enum signal_type signal, + const struct dc_crtc_timing *crtc_timing) +{ + uint32_t max_pixel_clock = TMDS_MAX_PIXEL_CLOCK; + + if (signal == SIGNAL_TYPE_DVI_DUAL_LINK) + max_pixel_clock *= 2; + + /* This handles the case of HDMI downgrade to DVI we don't want to + * we don't want to cap the pixel clock if the DDI is not DVI. + */ + if (connector_signal != SIGNAL_TYPE_DVI_DUAL_LINK && + connector_signal != SIGNAL_TYPE_DVI_SINGLE_LINK) + max_pixel_clock = enc110->base.features.max_hdmi_pixel_clock; + + /* DVI only support RGB pixel encoding */ + if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB) + return false; + + /*connect DVI via adpater's HDMI connector*/ + if ((connector_signal == SIGNAL_TYPE_DVI_SINGLE_LINK || + connector_signal == SIGNAL_TYPE_HDMI_TYPE_A) && + signal != SIGNAL_TYPE_HDMI_TYPE_A && + crtc_timing->pix_clk_khz > TMDS_MAX_PIXEL_CLOCK) + return false; + if (crtc_timing->pix_clk_khz < TMDS_MIN_PIXEL_CLOCK) + return false; + + if (crtc_timing->pix_clk_khz > max_pixel_clock) + return false; + + /* DVI supports 6/8bpp single-link and 10/16bpp dual-link */ + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_666: + case COLOR_DEPTH_888: + break; + case COLOR_DEPTH_101010: + case COLOR_DEPTH_161616: + if (signal != SIGNAL_TYPE_DVI_DUAL_LINK) + return false; + break; + default: + return false; + } + + return true; +} + +static bool dce110_link_encoder_validate_hdmi_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing, + int adjusted_pix_clk_khz) +{ + enum dc_color_depth max_deep_color = + enc110->base.features.max_hdmi_deep_color; + + if (max_deep_color < crtc_timing->display_color_depth) + return false; + + if (crtc_timing->display_color_depth < COLOR_DEPTH_888) + return false; + if (adjusted_pix_clk_khz < TMDS_MIN_PIXEL_CLOCK) + return false; + + if ((adjusted_pix_clk_khz == 0) || + (adjusted_pix_clk_khz > enc110->base.features.max_hdmi_pixel_clock)) + return false; + + /* DCE11 HW does not support 420 */ + if (!enc110->base.features.ycbcr420_supported && + crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) + return false; + + if (!enc110->base.features.flags.bits.HDMI_6GB_EN && + adjusted_pix_clk_khz >= 300000) + return false; + return true; +} + +bool dce110_link_encoder_validate_dp_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing) +{ + /* default RGB only */ + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) + return true; + + if (enc110->base.features.flags.bits.IS_YCBCR_CAPABLE) + return true; + + /* for DCE 8.x or later DP Y-only feature, + * we need ASIC cap + FeatureSupportDPYonly, not support 666 */ + if (crtc_timing->flags.Y_ONLY && + enc110->base.features.flags.bits.IS_YCBCR_CAPABLE && + crtc_timing->display_color_depth != COLOR_DEPTH_666) + return true; + + return false; +} + +void dce110_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + const struct dce110_link_enc_registers *link_regs, + const struct dce110_link_enc_aux_registers *aux_regs, + const struct dce110_link_enc_hpd_registers *hpd_regs) +{ + struct bp_encoder_cap_info bp_cap_info = {0}; + const struct dc_vbios_funcs *bp_funcs = init_data->ctx->dc_bios->funcs; + + enc110->base.funcs = &dce110_lnk_enc_funcs; + enc110->base.ctx = init_data->ctx; + enc110->base.id = init_data->encoder; + + enc110->base.hpd_source = init_data->hpd_source; + enc110->base.connector = init_data->connector; + + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + + enc110->base.features = *enc_features; + + enc110->base.transmitter = init_data->transmitter; + + /* set the flag to indicate whether driver poll the I2C data pin + * while doing the DP sink detect + */ + +/* if (dal_adapter_service_is_feature_supported(as, + FEATURE_DP_SINK_DETECT_POLL_DATA_PIN)) + enc110->base.features.flags.bits. + DP_SINK_DETECT_POLL_DATA_PIN = true;*/ + + enc110->base.output_signals = + SIGNAL_TYPE_DVI_SINGLE_LINK | + SIGNAL_TYPE_DVI_DUAL_LINK | + SIGNAL_TYPE_LVDS | + SIGNAL_TYPE_DISPLAY_PORT | + SIGNAL_TYPE_DISPLAY_PORT_MST | + SIGNAL_TYPE_EDP | + SIGNAL_TYPE_HDMI_TYPE_A; + + /* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE. + * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY. + * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer + * DIG is per UNIPHY and used by SST DP, eDP, HDMI, DVI and LVDS. + * Prefer DIG assignment is decided by board design. + * For DCE 8.0, there are only max 6 UNIPHYs, we assume board design + * and VBIOS will filter out 7 UNIPHY for DCE 8.0. + * By this, adding DIGG should not hurt DCE 8.0. + * This will let DCE 8.1 share DCE 8.0 as much as possible + */ + + enc110->link_regs = link_regs; + enc110->aux_regs = aux_regs; + enc110->hpd_regs = hpd_regs; + + switch (enc110->base.transmitter) { + case TRANSMITTER_UNIPHY_A: + enc110->base.preferred_engine = ENGINE_ID_DIGA; + break; + case TRANSMITTER_UNIPHY_B: + enc110->base.preferred_engine = ENGINE_ID_DIGB; + break; + case TRANSMITTER_UNIPHY_C: + enc110->base.preferred_engine = ENGINE_ID_DIGC; + break; + case TRANSMITTER_UNIPHY_D: + enc110->base.preferred_engine = ENGINE_ID_DIGD; + break; + case TRANSMITTER_UNIPHY_E: + enc110->base.preferred_engine = ENGINE_ID_DIGE; + break; + case TRANSMITTER_UNIPHY_F: + enc110->base.preferred_engine = ENGINE_ID_DIGF; + break; + case TRANSMITTER_UNIPHY_G: + enc110->base.preferred_engine = ENGINE_ID_DIGG; + break; + default: + ASSERT_CRITICAL(false); + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + } + + /* Override features with DCE-specific values */ + if (BP_RESULT_OK == bp_funcs->get_encoder_cap_info( + enc110->base.ctx->dc_bios, enc110->base.id, + &bp_cap_info)) { + enc110->base.features.flags.bits.IS_HBR2_CAPABLE = + bp_cap_info.DP_HBR2_EN; + enc110->base.features.flags.bits.IS_HBR3_CAPABLE = + bp_cap_info.DP_HBR3_EN; + enc110->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; + } +} + +bool dce110_link_encoder_validate_output_with_stream( + struct link_encoder *enc, + const struct dc_stream_state *stream) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + bool is_valid; + + switch (stream->signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + is_valid = dce110_link_encoder_validate_dvi_output( + enc110, + stream->sink->link->connector_signal, + stream->signal, + &stream->timing); + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + is_valid = dce110_link_encoder_validate_hdmi_output( + enc110, + &stream->timing, + stream->phy_pix_clk); + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + is_valid = dce110_link_encoder_validate_dp_output( + enc110, &stream->timing); + break; + case SIGNAL_TYPE_EDP: + is_valid = + (stream->timing. + pixel_encoding == PIXEL_ENCODING_RGB) ? true : false; + break; + case SIGNAL_TYPE_VIRTUAL: + is_valid = true; + break; + default: + is_valid = false; + break; + } + + return is_valid; +} + +void dce110_link_encoder_hw_init( + struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + cntl.action = TRANSMITTER_CONTROL_INIT; + cntl.engine_id = ENGINE_ID_UNKNOWN; + cntl.transmitter = enc110->base.transmitter; + cntl.connector_obj_id = enc110->base.connector; + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.coherent = false; + cntl.hpd_sel = enc110->base.hpd_source; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + return; + } + + if (enc110->base.connector.id == CONNECTOR_ID_LVDS) { + cntl.action = TRANSMITTER_CONTROL_BACKLIGHT_BRIGHTNESS; + + result = link_transmitter_control(enc110, &cntl); + + ASSERT(result == BP_RESULT_OK); + + } else if (enc110->base.connector.id == CONNECTOR_ID_EDP) { + ctx->dc->hwss.edp_power_control(enc, true); + } + aux_initialize(enc110); + + /* reinitialize HPD. + * hpd_initialize() will pass DIG_FE id to HW context. + * All other routine within HW context will use fe_engine_offset + * as DIG_FE id even caller pass DIG_FE id. + * So this routine must be called first. */ + hpd_initialize(enc110); +} + +void dce110_link_encoder_destroy(struct link_encoder **enc) +{ + kfree(TO_DCE110_LINK_ENC(*enc)); + *enc = NULL; +} + +void dce110_link_encoder_setup( + struct link_encoder *enc, + enum signal_type signal) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + switch (signal) { + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + /* DP SST */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 0); + break; + case SIGNAL_TYPE_LVDS: + /* LVDS */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 1); + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + /* TMDS-DVI */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 2); + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + /* TMDS-HDMI */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 3); + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + /* DP MST */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 5); + break; + default: + ASSERT_CRITICAL(false); + /* invalid mode ! */ + break; + } + +} + +/* TODO: still need depth or just pass in adjusted pixel clock? */ +void dce110_link_encoder_enable_tmds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + bool hdmi, + bool dual_link, + uint32_t pixel_clock) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + if (hdmi) { + cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A; + cntl.lanes_number = 4; + } else if (dual_link) { + cntl.signal = SIGNAL_TYPE_DVI_DUAL_LINK; + cntl.lanes_number = 8; + } else { + cntl.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + cntl.lanes_number = 4; + } + cntl.hpd_sel = enc110->base.hpd_source; + + cntl.pixel_clock = pixel_clock; + cntl.color_depth = color_depth; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +/* enables DP PHY output */ +void dce110_link_encoder_enable_dp_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + /* number_of_lanes is used for pixel clock adjust, + * but it's not passed to asic_control. + * We need to set number of lanes manually. + */ + configure_encoder(enc110, link_settings); + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_DISPLAY_PORT; + cntl.lanes_number = link_settings->lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_rate + * LINK_RATE_REF_FREQ_IN_KHZ; + /* TODO: check if undefined works */ + cntl.color_depth = COLOR_DEPTH_UNDEFINED; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +/* enables DP PHY output in MST mode */ +void dce110_link_encoder_enable_dp_mst_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + /* number_of_lanes is used for pixel clock adjust, + * but it's not passed to asic_control. + * We need to set number of lanes manually. + */ + configure_encoder(enc110, link_settings); + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = ENGINE_ID_UNKNOWN; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + cntl.lanes_number = link_settings->lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_rate + * LINK_RATE_REF_FREQ_IN_KHZ; + /* TODO: check if undefined works */ + cntl.color_depth = COLOR_DEPTH_UNDEFINED; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} +/* + * @brief + * Disable transmitter and its encoder + */ +void dce110_link_encoder_disable_output( + struct link_encoder *enc, + enum signal_type signal, + struct dc_link *link) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + if (!is_dig_enabled(enc110)) { + /* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */ + return; + } + if (enc110->base.connector.id == CONNECTOR_ID_EDP) + ctx->dc->hwss.edp_backlight_control(link, false); + /* Power-down RX and disable GPU PHY should be paired. + * Disabling PHY without powering down RX may cause + * symbol lock loss, on which we will get DP Sink interrupt. */ + + /* There is a case for the DP active dongles + * where we want to disable the PHY but keep RX powered, + * for those we need to ignore DP Sink interrupt + * by checking lane count that has been set + * on the last do_enable_output(). */ + + /* disable transmitter */ + cntl.action = TRANSMITTER_CONTROL_DISABLE; + cntl.transmitter = enc110->base.transmitter; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.signal = signal; + cntl.connector_obj_id = enc110->base.connector; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + return; + } + + /* disable encoder */ + if (dc_is_dp_signal(signal)) + link_encoder_disable(enc110); + + if (enc110->base.connector.id == CONNECTOR_ID_EDP) { + /* power down eDP panel */ + /* TODO: Power control cause regression, we should implement + * it properly, for now just comment it. + * + * link_encoder_edp_wait_for_hpd_ready( + link_enc, + link_enc->connector, + false); + + * link_encoder_edp_power_control( + link_enc, false); */ + } +} + +void dce110_link_encoder_dp_set_lane_settings( + struct link_encoder *enc, + const struct link_training_settings *link_settings) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + union dpcd_training_lane_set training_lane_set = { { 0 } }; + int32_t lane = 0; + struct bp_transmitter_control cntl = { 0 }; + + if (!link_settings) { + BREAK_TO_DEBUGGER(); + return; + } + + cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS; + cntl.transmitter = enc110->base.transmitter; + cntl.connector_obj_id = enc110->base.connector; + cntl.lanes_number = link_settings->link_settings.lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_settings.link_rate * + LINK_RATE_REF_FREQ_IN_KHZ; + + for (lane = 0; lane < link_settings->link_settings.lane_count; lane++) { + /* translate lane settings */ + + training_lane_set.bits.VOLTAGE_SWING_SET = + link_settings->lane_settings[lane].VOLTAGE_SWING; + training_lane_set.bits.PRE_EMPHASIS_SET = + link_settings->lane_settings[lane].PRE_EMPHASIS; + + /* post cursor 2 setting only applies to HBR2 link rate */ + if (link_settings->link_settings.link_rate == LINK_RATE_HIGH2) { + /* this is passed to VBIOS + * to program post cursor 2 level */ + + training_lane_set.bits.POST_CURSOR2_SET = + link_settings->lane_settings[lane].POST_CURSOR2; + } + + cntl.lane_select = lane; + cntl.lane_settings = training_lane_set.raw; + + /* call VBIOS table to set voltage swing and pre-emphasis */ + link_transmitter_control(enc110, &cntl); + } +} + +/* set DP PHY test and training patterns */ +void dce110_link_encoder_dp_set_phy_pattern( + struct link_encoder *enc, + const struct encoder_set_dp_phy_pattern_param *param) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + switch (param->dp_phy_pattern) { + case DP_TEST_PATTERN_TRAINING_PATTERN1: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 0); + break; + case DP_TEST_PATTERN_TRAINING_PATTERN2: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 1); + break; + case DP_TEST_PATTERN_TRAINING_PATTERN3: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 2); + break; + case DP_TEST_PATTERN_TRAINING_PATTERN4: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 3); + break; + case DP_TEST_PATTERN_D102: + set_dp_phy_pattern_d102(enc110); + break; + case DP_TEST_PATTERN_SYMBOL_ERROR: + set_dp_phy_pattern_symbol_error(enc110); + break; + case DP_TEST_PATTERN_PRBS7: + set_dp_phy_pattern_prbs7(enc110); + break; + case DP_TEST_PATTERN_80BIT_CUSTOM: + set_dp_phy_pattern_80bit_custom( + enc110, param->custom_pattern); + break; + case DP_TEST_PATTERN_CP2520_1: + set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 1); + break; + case DP_TEST_PATTERN_CP2520_2: + set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 2); + break; + case DP_TEST_PATTERN_CP2520_3: + set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 3); + break; + case DP_TEST_PATTERN_VIDEO_MODE: { + set_dp_phy_pattern_passthrough_mode( + enc110, param->dp_panel_mode); + break; + } + + default: + /* invalid phy pattern */ + ASSERT_CRITICAL(false); + break; + } +} + +static void fill_stream_allocation_row_info( + const struct link_mst_stream_allocation *stream_allocation, + uint32_t *src, + uint32_t *slots) +{ + const struct stream_encoder *stream_enc = stream_allocation->stream_enc; + + if (stream_enc) { + *src = stream_enc->id; + *slots = stream_allocation->slot_count; + } else { + *src = 0; + *slots = 0; + } +} + +/* programs DP MST VC payload allocation */ +void dce110_link_encoder_update_mst_stream_allocation_table( + struct link_encoder *enc, + const struct link_mst_stream_allocation_table *table) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + uint32_t value0 = 0; + uint32_t value1 = 0; + uint32_t value2 = 0; + uint32_t slots = 0; + uint32_t src = 0; + uint32_t retries = 0; + + /* For CZ, there are only 3 pipes. So Virtual channel is up 3.*/ + + /* --- Set MSE Stream Attribute - + * Setup VC Payload Table on Tx Side, + * Issue allocation change trigger + * to commit payload on both tx and rx side */ + + /* we should clean-up table each time */ + + if (table->stream_count >= 1) { + fill_stream_allocation_row_info( + &table->stream_allocations[0], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT0, + DP_MSE_SAT_SRC0, src, + DP_MSE_SAT_SLOT_COUNT0, slots); + + if (table->stream_count >= 2) { + fill_stream_allocation_row_info( + &table->stream_allocations[1], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT0, + DP_MSE_SAT_SRC1, src, + DP_MSE_SAT_SLOT_COUNT1, slots); + + if (table->stream_count >= 3) { + fill_stream_allocation_row_info( + &table->stream_allocations[2], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT1, + DP_MSE_SAT_SRC2, src, + DP_MSE_SAT_SLOT_COUNT2, slots); + + if (table->stream_count >= 4) { + fill_stream_allocation_row_info( + &table->stream_allocations[3], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT1, + DP_MSE_SAT_SRC3, src, + DP_MSE_SAT_SLOT_COUNT3, slots); + + /* --- wait for transaction finish */ + + /* send allocation change trigger (ACT) ? + * this step first sends the ACT, + * then double buffers the SAT into the hardware + * making the new allocation active on the DP MST mode link */ + + + /* DP_MSE_SAT_UPDATE: + * 0 - No Action + * 1 - Update SAT with trigger + * 2 - Update SAT without trigger */ + + REG_UPDATE(DP_MSE_SAT_UPDATE, + DP_MSE_SAT_UPDATE, 1); + + /* wait for update to complete + * (i.e. DP_MSE_SAT_UPDATE field is reset to 0) + * then wait for the transmission + * of at least 16 MTP headers on immediate local link. + * i.e. DP_MSE_16_MTP_KEEPOUT field (read only) is reset to 0 + * a value of 1 indicates that DP MST mode + * is in the 16 MTP keepout region after a VC has been added. + * MST stream bandwidth (VC rate) can be configured + * after this bit is cleared */ + + do { + udelay(10); + + value0 = REG_READ(DP_MSE_SAT_UPDATE); + + REG_GET(DP_MSE_SAT_UPDATE, + DP_MSE_SAT_UPDATE, &value1); + + REG_GET(DP_MSE_SAT_UPDATE, + DP_MSE_16_MTP_KEEPOUT, &value2); + + /* bit field DP_MSE_SAT_UPDATE is set to 1 already */ + if (!value1 && !value2) + break; + ++retries; + } while (retries < DP_MST_UPDATE_MAX_RETRY); +} + +void dce110_link_encoder_connect_dig_be_to_fe( + struct link_encoder *enc, + enum engine_id engine, + bool connect) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + uint32_t field; + + if (engine != ENGINE_ID_UNKNOWN) { + + REG_GET(DIG_BE_CNTL, DIG_FE_SOURCE_SELECT, &field); + + if (connect) + field |= get_frontend_source(engine); + else + field &= ~get_frontend_source(engine); + + REG_UPDATE(DIG_BE_CNTL, DIG_FE_SOURCE_SELECT, field); + } +} + +void dce110_link_encoder_enable_hpd(struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = HPD_REG(DC_HPD_CONTROL); + uint32_t hpd_enable = 0; + uint32_t value = dm_read_reg(ctx, addr); + + get_reg_field_value(hpd_enable, DC_HPD_CONTROL, DC_HPD_EN); + + if (hpd_enable == 0) + set_reg_field_value(value, 1, DC_HPD_CONTROL, DC_HPD_EN); +} + +void dce110_link_encoder_disable_hpd(struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = HPD_REG(DC_HPD_CONTROL); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, 0, DC_HPD_CONTROL, DC_HPD_EN); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h new file mode 100644 index 000000000000..494067dedd03 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h @@ -0,0 +1,268 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_LINK_ENCODER__DCE110_H__ +#define __DC_LINK_ENCODER__DCE110_H__ + +#include "link_encoder.h" + +#define TO_DCE110_LINK_ENC(link_encoder)\ + container_of(link_encoder, struct dce110_link_encoder, base) + +/* Not found regs in dce120 spec + * BIOS_SCRATCH_2 + * DP_DPHY_INTERNAL_CTRL + */ + +#define AUX_REG_LIST(id)\ + SRI(AUX_CONTROL, DP_AUX, id), \ + SRI(AUX_DPHY_RX_CONTROL0, DP_AUX, id) + +#define HPD_REG_LIST(id)\ + SRI(DC_HPD_CONTROL, HPD, id) + +#define LE_COMMON_REG_LIST_BASE(id) \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + SRI(DIG_BE_CNTL, DIG, id), \ + SRI(DIG_BE_EN_CNTL, DIG, id), \ + SRI(DP_CONFIG, DP, id), \ + SRI(DP_DPHY_CNTL, DP, id), \ + SRI(DP_DPHY_PRBS_CNTL, DP, id), \ + SRI(DP_DPHY_SCRAM_CNTL, DP, id),\ + SRI(DP_DPHY_SYM0, DP, id), \ + SRI(DP_DPHY_SYM1, DP, id), \ + SRI(DP_DPHY_SYM2, DP, id), \ + SRI(DP_DPHY_TRAINING_PATTERN_SEL, DP, id), \ + SRI(DP_LINK_CNTL, DP, id), \ + SRI(DP_LINK_FRAMING_CNTL, DP, id), \ + SRI(DP_MSE_SAT0, DP, id), \ + SRI(DP_MSE_SAT1, DP, id), \ + SRI(DP_MSE_SAT2, DP, id), \ + SRI(DP_MSE_SAT_UPDATE, DP, id), \ + SRI(DP_SEC_CNTL, DP, id), \ + SRI(DP_VID_STREAM_CNTL, DP, id), \ + SRI(DP_DPHY_FAST_TRAINING, DP, id), \ + SRI(DP_SEC_CNTL1, DP, id) + +#define LE_COMMON_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SR(DCI_MEM_PWR_STATUS) + +#define LE_DCE80_REG_LIST(id)\ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + LE_COMMON_REG_LIST_BASE(id) + +#define LE_DCE100_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SR(DCI_MEM_PWR_STATUS) + +#define LE_DCE110_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SRI(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id), \ + SR(DCI_MEM_PWR_STATUS) + +#define LE_DCE120_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id), \ + SR(DCI_MEM_PWR_STATUS) + +#define LE_DCN10_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SRI(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id) + +struct dce110_link_enc_aux_registers { + uint32_t AUX_CONTROL; + uint32_t AUX_DPHY_RX_CONTROL0; +}; + +struct dce110_link_enc_hpd_registers { + uint32_t DC_HPD_CONTROL; +}; + +struct dce110_link_enc_registers { + /* DMCU registers */ + uint32_t MASTER_COMM_DATA_REG1; + uint32_t MASTER_COMM_DATA_REG2; + uint32_t MASTER_COMM_DATA_REG3; + uint32_t MASTER_COMM_CMD_REG; + uint32_t MASTER_COMM_CNTL_REG; + uint32_t DMCU_RAM_ACCESS_CTRL; + uint32_t DCI_MEM_PWR_STATUS; + uint32_t DMU_MEM_PWR_CNTL; + uint32_t DMCU_IRAM_RD_CTRL; + uint32_t DMCU_IRAM_RD_DATA; + uint32_t DMCU_INTERRUPT_TO_UC_EN_MASK; + + /* Common DP registers */ + uint32_t DIG_BE_CNTL; + uint32_t DIG_BE_EN_CNTL; + uint32_t DP_CONFIG; + uint32_t DP_DPHY_CNTL; + uint32_t DP_DPHY_INTERNAL_CTRL; + uint32_t DP_DPHY_PRBS_CNTL; + uint32_t DP_DPHY_SCRAM_CNTL; + uint32_t DP_DPHY_SYM0; + uint32_t DP_DPHY_SYM1; + uint32_t DP_DPHY_SYM2; + uint32_t DP_DPHY_TRAINING_PATTERN_SEL; + uint32_t DP_LINK_CNTL; + uint32_t DP_LINK_FRAMING_CNTL; + uint32_t DP_MSE_SAT0; + uint32_t DP_MSE_SAT1; + uint32_t DP_MSE_SAT2; + uint32_t DP_MSE_SAT_UPDATE; + uint32_t DP_SEC_CNTL; + uint32_t DP_VID_STREAM_CNTL; + uint32_t DP_DPHY_FAST_TRAINING; + uint32_t DP_DPHY_BS_SR_SWAP_CNTL; + uint32_t DP_DPHY_HBR2_PATTERN_CONTROL; + uint32_t DP_SEC_CNTL1; +}; + +struct dce110_link_encoder { + struct link_encoder base; + const struct dce110_link_enc_registers *link_regs; + const struct dce110_link_enc_aux_registers *aux_regs; + const struct dce110_link_enc_hpd_registers *hpd_regs; +}; + + +void dce110_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + const struct dce110_link_enc_registers *link_regs, + const struct dce110_link_enc_aux_registers *aux_regs, + const struct dce110_link_enc_hpd_registers *hpd_regs); + +bool dce110_link_encoder_validate_dvi_output( + const struct dce110_link_encoder *enc110, + enum signal_type connector_signal, + enum signal_type signal, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_rgb_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_dp_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_wireless_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_output_with_stream( + struct link_encoder *enc, + const struct dc_stream_state *stream); + +/****************** HW programming ************************/ + +/* initialize HW */ /* why do we initialze aux in here? */ +void dce110_link_encoder_hw_init(struct link_encoder *enc); + +void dce110_link_encoder_destroy(struct link_encoder **enc); + +/* program DIG_MODE in DIG_BE */ +/* TODO can this be combined with enable_output? */ +void dce110_link_encoder_setup( + struct link_encoder *enc, + enum signal_type signal); + +/* enables TMDS PHY output */ +/* TODO: still need depth or just pass in adjusted pixel clock? */ +void dce110_link_encoder_enable_tmds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + bool hdmi, + bool dual_link, + uint32_t pixel_clock); + +/* enables DP PHY output */ +void dce110_link_encoder_enable_dp_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source); + +/* enables DP PHY output in MST mode */ +void dce110_link_encoder_enable_dp_mst_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source); + +/* disable PHY output */ +void dce110_link_encoder_disable_output( + struct link_encoder *link_enc, + enum signal_type signal, + struct dc_link *link); + +/* set DP lane settings */ +void dce110_link_encoder_dp_set_lane_settings( + struct link_encoder *enc, + const struct link_training_settings *link_settings); + +void dce110_link_encoder_dp_set_phy_pattern( + struct link_encoder *enc, + const struct encoder_set_dp_phy_pattern_param *param); + +/* programs DP MST VC payload allocation */ +void dce110_link_encoder_update_mst_stream_allocation_table( + struct link_encoder *enc, + const struct link_mst_stream_allocation_table *table); + +void dce110_link_encoder_connect_dig_be_to_fe( + struct link_encoder *enc, + enum engine_id engine, + bool connect); + +void dce110_link_encoder_set_dp_phy_pattern_training_pattern( + struct link_encoder *enc, + uint32_t index); + +void dce110_link_encoder_enable_hpd(struct link_encoder *enc); + +void dce110_link_encoder_disable_hpd(struct link_encoder *enc); + +void dce110_psr_program_dp_dphy_fast_training(struct link_encoder *enc, + bool exit_link_training_required); + +void dce110_psr_program_secondary_packet(struct link_encoder *enc, + unsigned int sdp_transmit_line_num_deadline); + +#endif /* __DC_LINK_ENCODER__DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c new file mode 100644 index 000000000000..0790f25c7b3b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c @@ -0,0 +1,700 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce_mem_input.h" +#include "reg_helper.h" +#include "basics/conversion.h" + +#define CTX \ + dce_mi->base.ctx +#define REG(reg)\ + dce_mi->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + dce_mi->shifts->field_name, dce_mi->masks->field_name + +struct pte_setting { + unsigned int bpp; + unsigned int page_width; + unsigned int page_height; + unsigned char min_pte_before_flip_horiz_scan; + unsigned char min_pte_before_flip_vert_scan; + unsigned char pte_req_per_chunk; + unsigned char param_6; + unsigned char param_7; + unsigned char param_8; +}; + +enum mi_bits_per_pixel { + mi_bpp_8 = 0, + mi_bpp_16, + mi_bpp_32, + mi_bpp_64, + mi_bpp_count, +}; + +enum mi_tiling_format { + mi_tiling_linear = 0, + mi_tiling_1D, + mi_tiling_2D, + mi_tiling_count, +}; + +static const struct pte_setting pte_settings[mi_tiling_count][mi_bpp_count] = { + [mi_tiling_linear] = { + { 8, 4096, 1, 8, 0, 1, 0, 0, 0}, + { 16, 2048, 1, 8, 0, 1, 0, 0, 0}, + { 32, 1024, 1, 8, 0, 1, 0, 0, 0}, + { 64, 512, 1, 8, 0, 1, 0, 0, 0}, /* new for 64bpp from HW */ + }, + [mi_tiling_1D] = { + { 8, 512, 8, 1, 0, 1, 0, 0, 0}, /* 0 for invalid */ + { 16, 256, 8, 2, 0, 1, 0, 0, 0}, + { 32, 128, 8, 4, 0, 1, 0, 0, 0}, + { 64, 64, 8, 4, 0, 1, 0, 0, 0}, /* fake */ + }, + [mi_tiling_2D] = { + { 8, 64, 64, 8, 8, 1, 4, 0, 0}, + { 16, 64, 32, 8, 16, 1, 8, 0, 0}, + { 32, 32, 32, 16, 16, 1, 8, 0, 0}, + { 64, 8, 32, 16, 16, 1, 8, 0, 0}, /* fake */ + }, +}; + +static enum mi_bits_per_pixel get_mi_bpp( + enum surface_pixel_format format) +{ + if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616) + return mi_bpp_64; + else if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB8888) + return mi_bpp_32; + else if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB1555) + return mi_bpp_16; + else + return mi_bpp_8; +} + +static enum mi_tiling_format get_mi_tiling( + union dc_tiling_info *tiling_info) +{ + switch (tiling_info->gfx8.array_mode) { + case DC_ARRAY_1D_TILED_THIN1: + case DC_ARRAY_1D_TILED_THICK: + case DC_ARRAY_PRT_TILED_THIN1: + return mi_tiling_1D; + case DC_ARRAY_2D_TILED_THIN1: + case DC_ARRAY_2D_TILED_THICK: + case DC_ARRAY_2D_TILED_X_THICK: + case DC_ARRAY_PRT_2D_TILED_THIN1: + case DC_ARRAY_PRT_2D_TILED_THICK: + return mi_tiling_2D; + case DC_ARRAY_LINEAR_GENERAL: + case DC_ARRAY_LINEAR_ALLIGNED: + return mi_tiling_linear; + default: + return mi_tiling_2D; + } +} + +static bool is_vert_scan(enum dc_rotation_angle rotation) +{ + switch (rotation) { + case ROTATION_ANGLE_90: + case ROTATION_ANGLE_270: + return true; + default: + return false; + } +} + +static void dce_mi_program_pte_vm( + struct mem_input *mi, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + enum mi_bits_per_pixel mi_bpp = get_mi_bpp(format); + enum mi_tiling_format mi_tiling = get_mi_tiling(tiling_info); + const struct pte_setting *pte = &pte_settings[mi_tiling][mi_bpp]; + + unsigned int page_width = log_2(pte->page_width); + unsigned int page_height = log_2(pte->page_height); + unsigned int min_pte_before_flip = is_vert_scan(rotation) ? + pte->min_pte_before_flip_vert_scan : + pte->min_pte_before_flip_horiz_scan; + + REG_UPDATE(GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, + GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, 0xff); + + REG_UPDATE_3(DVMM_PTE_CONTROL, + DVMM_PAGE_WIDTH, page_width, + DVMM_PAGE_HEIGHT, page_height, + DVMM_MIN_PTE_BEFORE_FLIP, min_pte_before_flip); + + REG_UPDATE_2(DVMM_PTE_ARB_CONTROL, + DVMM_PTE_REQ_PER_CHUNK, pte->pte_req_per_chunk, + DVMM_MAX_PTE_REQ_OUTSTANDING, 0xff); +} + +static void program_urgency_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t urgency_low_wm, + uint32_t urgency_high_wm) +{ + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK, wm_select); + + REG_SET_2(DPG_PIPE_URGENCY_CONTROL, 0, + URGENCY_LOW_WATERMARK, urgency_low_wm, + URGENCY_HIGH_WATERMARK, urgency_high_wm); +} + +static void program_nbp_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t nbp_wm) +{ + if (REG(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL)) { + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK, wm_select); + + REG_UPDATE_3(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE, 1, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST, 1, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, 1); + + REG_UPDATE(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK, nbp_wm); + } + + if (REG(DPG_PIPE_LOW_POWER_CONTROL)) { + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + PSTATE_CHANGE_WATERMARK_MASK, wm_select); + + REG_UPDATE_3(DPG_PIPE_LOW_POWER_CONTROL, + PSTATE_CHANGE_ENABLE, 1, + PSTATE_CHANGE_URGENT_DURING_REQUEST, 1, + PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, 1); + + REG_UPDATE(DPG_PIPE_LOW_POWER_CONTROL, + PSTATE_CHANGE_WATERMARK, nbp_wm); + } +} + +static void program_stutter_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t stutter_mark) +{ + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, wm_select); + + if (REG(DPG_PIPE_STUTTER_CONTROL2)) + REG_UPDATE(DPG_PIPE_STUTTER_CONTROL2, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark); + else + REG_UPDATE(DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark); +} + +static void dce_mi_program_display_marks( + struct mem_input *mi, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; + + program_urgency_watermark(dce_mi, 2, /* set a */ + urgent.a_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_mi, 1, /* set d */ + urgent.d_mark, total_dest_line_time_ns); + + REG_UPDATE_2(DPG_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE, stutter_en, + STUTTER_IGNORE_FBC, 1); + program_nbp_watermark(dce_mi, 2, nbp.a_mark); /* set a */ + program_nbp_watermark(dce_mi, 1, nbp.d_mark); /* set d */ + + program_stutter_watermark(dce_mi, 2, stutter.a_mark); /* set a */ + program_stutter_watermark(dce_mi, 1, stutter.d_mark); /* set d */ +} + +static void dce120_mi_program_display_marks(struct mem_input *mi, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; + + program_urgency_watermark(dce_mi, 0, /* set a */ + urgent.a_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_mi, 1, /* set b */ + urgent.b_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_mi, 2, /* set c */ + urgent.c_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_mi, 3, /* set d */ + urgent.d_mark, total_dest_line_time_ns); + + REG_UPDATE_2(DPG_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE, stutter_en, + STUTTER_IGNORE_FBC, 1); + program_nbp_watermark(dce_mi, 0, nbp.a_mark); /* set a */ + program_nbp_watermark(dce_mi, 1, nbp.b_mark); /* set b */ + program_nbp_watermark(dce_mi, 2, nbp.c_mark); /* set c */ + program_nbp_watermark(dce_mi, 3, nbp.d_mark); /* set d */ + + program_stutter_watermark(dce_mi, 0, stutter.a_mark); /* set a */ + program_stutter_watermark(dce_mi, 1, stutter.b_mark); /* set b */ + program_stutter_watermark(dce_mi, 2, stutter.c_mark); /* set c */ + program_stutter_watermark(dce_mi, 3, stutter.d_mark); /* set d */ +} + +static void program_tiling( + struct dce_mem_input *dce_mi, const union dc_tiling_info *info) +{ + if (dce_mi->masks->GRPH_SW_MODE) { /* GFX9 */ + REG_UPDATE_6(GRPH_CONTROL, + GRPH_SW_MODE, info->gfx9.swizzle, + GRPH_NUM_BANKS, log_2(info->gfx9.num_banks), + GRPH_NUM_SHADER_ENGINES, log_2(info->gfx9.num_shader_engines), + GRPH_NUM_PIPES, log_2(info->gfx9.num_pipes), + GRPH_COLOR_EXPANSION_MODE, 1, + GRPH_SE_ENABLE, info->gfx9.shaderEnable); + /* TODO: DCP0_GRPH_CONTROL__GRPH_SE_ENABLE where to get info + GRPH_SE_ENABLE, 1, + GRPH_Z, 0); + */ + } + + if (dce_mi->masks->GRPH_ARRAY_MODE) { /* GFX8 */ + REG_UPDATE_9(GRPH_CONTROL, + GRPH_NUM_BANKS, info->gfx8.num_banks, + GRPH_BANK_WIDTH, info->gfx8.bank_width, + GRPH_BANK_HEIGHT, info->gfx8.bank_height, + GRPH_MACRO_TILE_ASPECT, info->gfx8.tile_aspect, + GRPH_TILE_SPLIT, info->gfx8.tile_split, + GRPH_MICRO_TILE_MODE, info->gfx8.tile_mode, + GRPH_PIPE_CONFIG, info->gfx8.pipe_config, + GRPH_ARRAY_MODE, info->gfx8.array_mode, + GRPH_COLOR_EXPANSION_MODE, 1); + /* 01 - DCP_GRPH_COLOR_EXPANSION_MODE_ZEXP: zero expansion for YCbCr */ + /* + GRPH_Z, 0); + */ + } +} + + +static void program_size_and_rotation( + struct dce_mem_input *dce_mi, + enum dc_rotation_angle rotation, + const union plane_size *plane_size) +{ + const struct rect *in_rect = &plane_size->grph.surface_size; + struct rect hw_rect = plane_size->grph.surface_size; + const uint32_t rotation_angles[ROTATION_ANGLE_COUNT] = { + [ROTATION_ANGLE_0] = 0, + [ROTATION_ANGLE_90] = 1, + [ROTATION_ANGLE_180] = 2, + [ROTATION_ANGLE_270] = 3, + }; + + if (rotation == ROTATION_ANGLE_90 || rotation == ROTATION_ANGLE_270) { + hw_rect.x = in_rect->y; + hw_rect.y = in_rect->x; + + hw_rect.height = in_rect->width; + hw_rect.width = in_rect->height; + } + + REG_SET(GRPH_X_START, 0, + GRPH_X_START, hw_rect.x); + + REG_SET(GRPH_Y_START, 0, + GRPH_Y_START, hw_rect.y); + + REG_SET(GRPH_X_END, 0, + GRPH_X_END, hw_rect.width); + + REG_SET(GRPH_Y_END, 0, + GRPH_Y_END, hw_rect.height); + + REG_SET(GRPH_PITCH, 0, + GRPH_PITCH, plane_size->grph.surface_pitch); + + REG_SET(HW_ROTATION, 0, + GRPH_ROTATION_ANGLE, rotation_angles[rotation]); +} + +static void program_grph_pixel_format( + struct dce_mem_input *dce_mi, + enum surface_pixel_format format) +{ + uint32_t red_xbar = 0, blue_xbar = 0; /* no swap */ + uint32_t grph_depth = 0, grph_format = 0; + uint32_t sign = 0, floating = 0; + + if (format == SURFACE_PIXEL_FORMAT_GRPH_ABGR8888 || + /*todo: doesn't look like we handle BGRA here, + * should problem swap endian*/ + format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010 || + format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS || + format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) { + /* ABGR formats */ + red_xbar = 2; + blue_xbar = 2; + } + + REG_SET_2(GRPH_SWAP_CNTL, 0, + GRPH_RED_CROSSBAR, red_xbar, + GRPH_BLUE_CROSSBAR, blue_xbar); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + grph_depth = 0; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + grph_depth = 1; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + grph_depth = 1; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + grph_depth = 2; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + grph_depth = 2; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + sign = 1; + floating = 1; + /* no break */ + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: /* shouldn't this get float too? */ + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + grph_depth = 3; + grph_format = 0; + break; + default: + DC_ERR("unsupported grph pixel format"); + break; + } + + REG_UPDATE_2(GRPH_CONTROL, + GRPH_DEPTH, grph_depth, + GRPH_FORMAT, grph_format); + + REG_UPDATE_4(PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_SELECT, floating, + GRPH_PRESCALE_R_SIGN, sign, + GRPH_PRESCALE_G_SIGN, sign, + GRPH_PRESCALE_B_SIGN, sign); +} + +static void dce_mi_program_surface_config( + struct mem_input *mi, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + REG_UPDATE(GRPH_ENABLE, GRPH_ENABLE, 1); + + program_tiling(dce_mi, tiling_info); + program_size_and_rotation(dce_mi, rotation, plane_size); + + if (format >= SURFACE_PIXEL_FORMAT_GRPH_BEGIN && + format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + program_grph_pixel_format(dce_mi, format); +} + +static uint32_t get_dmif_switch_time_us( + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz) +{ + uint32_t frame_time; + uint32_t pixels_per_second; + uint32_t pixels_per_frame; + uint32_t refresh_rate; + const uint32_t us_in_sec = 1000000; + const uint32_t min_single_frame_time_us = 30000; + /*return double of frame time*/ + const uint32_t single_frame_time_multiplier = 2; + + if (!h_total || v_total || !pix_clk_khz) + return single_frame_time_multiplier * min_single_frame_time_us; + + /*TODO: should we use pixel format normalized pixel clock here?*/ + pixels_per_second = pix_clk_khz * 1000; + pixels_per_frame = h_total * v_total; + + if (!pixels_per_second || !pixels_per_frame) { + /* avoid division by zero */ + ASSERT(pixels_per_frame); + ASSERT(pixels_per_second); + return single_frame_time_multiplier * min_single_frame_time_us; + } + + refresh_rate = pixels_per_second / pixels_per_frame; + + if (!refresh_rate) { + /* avoid division by zero*/ + ASSERT(refresh_rate); + return single_frame_time_multiplier * min_single_frame_time_us; + } + + frame_time = us_in_sec / refresh_rate; + + if (frame_time < min_single_frame_time_us) + frame_time = min_single_frame_time_us; + + frame_time *= single_frame_time_multiplier; + + return frame_time; +} + +static void dce_mi_allocate_dmif( + struct mem_input *mi, + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz, + uint32_t total_stream_num) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + const uint32_t retry_delay = 10; + uint32_t retry_count = get_dmif_switch_time_us( + h_total, + v_total, + pix_clk_khz) / retry_delay; + + uint32_t pix_dur; + uint32_t buffers_allocated; + uint32_t dmif_buffer_control; + + dmif_buffer_control = REG_GET(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATED, &buffers_allocated); + + if (buffers_allocated == 2) + return; + + REG_SET(DMIF_BUFFER_CONTROL, dmif_buffer_control, + DMIF_BUFFERS_ALLOCATED, 2); + + REG_WAIT(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATION_COMPLETED, 1, + retry_delay, retry_count); + + if (pix_clk_khz != 0) { + pix_dur = 1000000000ULL / pix_clk_khz; + + REG_UPDATE(DPG_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION, pix_dur); + } + + if (dce_mi->wa.single_head_rdreq_dmif_limit) { + uint32_t eanble = (total_stream_num > 1) ? 0 : + dce_mi->wa.single_head_rdreq_dmif_limit; + + REG_UPDATE(MC_HUB_RDREQ_DMIF_LIMIT, + ENABLE, eanble); + } +} + +static void dce_mi_free_dmif( + struct mem_input *mi, + uint32_t total_stream_num) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + uint32_t buffers_allocated; + uint32_t dmif_buffer_control; + + dmif_buffer_control = REG_GET(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATED, &buffers_allocated); + + if (buffers_allocated == 0) + return; + + REG_SET(DMIF_BUFFER_CONTROL, dmif_buffer_control, + DMIF_BUFFERS_ALLOCATED, 0); + + REG_WAIT(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATION_COMPLETED, 1, + 10, 3500); + + if (dce_mi->wa.single_head_rdreq_dmif_limit) { + uint32_t eanble = (total_stream_num > 1) ? 0 : + dce_mi->wa.single_head_rdreq_dmif_limit; + + REG_UPDATE(MC_HUB_RDREQ_DMIF_LIMIT, + ENABLE, eanble); + } +} + + +static void program_sec_addr( + struct dce_mem_input *dce_mi, + PHYSICAL_ADDRESS_LOC address) +{ + /*high register MUST be programmed first*/ + REG_SET(GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0, + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + address.high_part); + + REG_SET_2(GRPH_SECONDARY_SURFACE_ADDRESS, 0, + GRPH_SECONDARY_SURFACE_ADDRESS, address.low_part >> 8, + GRPH_SECONDARY_DFQ_ENABLE, 0); +} + +static void program_pri_addr( + struct dce_mem_input *dce_mi, + PHYSICAL_ADDRESS_LOC address) +{ + /*high register MUST be programmed first*/ + REG_SET(GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + address.high_part); + + REG_SET(GRPH_PRIMARY_SURFACE_ADDRESS, 0, + GRPH_PRIMARY_SURFACE_ADDRESS, + address.low_part >> 8); +} + + +static bool dce_mi_is_flip_pending(struct mem_input *mem_input) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mem_input); + uint32_t update_pending; + + REG_GET(GRPH_UPDATE, GRPH_SURFACE_UPDATE_PENDING, &update_pending); + if (update_pending) + return true; + + mem_input->current_address = mem_input->request_address; + return false; +} + +static bool dce_mi_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mem_input); + + REG_UPDATE(GRPH_UPDATE, GRPH_UPDATE_LOCK, 1); + + REG_UPDATE( + GRPH_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_H_RETRACE_EN, flip_immediate ? 1 : 0); + + switch (address->type) { + case PLN_ADDR_TYPE_GRAPHICS: + if (address->grph.addr.quad_part == 0) + break; + program_pri_addr(dce_mi, address->grph.addr); + break; + case PLN_ADDR_TYPE_GRPH_STEREO: + if (address->grph_stereo.left_addr.quad_part == 0 || + address->grph_stereo.right_addr.quad_part == 0) + break; + program_pri_addr(dce_mi, address->grph_stereo.left_addr); + program_sec_addr(dce_mi, address->grph_stereo.right_addr); + break; + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + break; + } + + mem_input->request_address = *address; + + if (flip_immediate) + mem_input->current_address = *address; + + REG_UPDATE(GRPH_UPDATE, GRPH_UPDATE_LOCK, 0); + + return true; +} + +static struct mem_input_funcs dce_mi_funcs = { + .mem_input_program_display_marks = dce_mi_program_display_marks, + .allocate_mem_input = dce_mi_allocate_dmif, + .free_mem_input = dce_mi_free_dmif, + .mem_input_program_surface_flip_and_addr = + dce_mi_program_surface_flip_and_addr, + .mem_input_program_pte_vm = dce_mi_program_pte_vm, + .mem_input_program_surface_config = + dce_mi_program_surface_config, + .mem_input_is_flip_pending = dce_mi_is_flip_pending +}; + + +void dce_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask) +{ + dce_mi->base.ctx = ctx; + + dce_mi->base.inst = inst; + dce_mi->base.funcs = &dce_mi_funcs; + + dce_mi->regs = regs; + dce_mi->shifts = mi_shift; + dce_mi->masks = mi_mask; +} + +void dce112_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask) +{ + dce_mem_input_construct(dce_mi, ctx, inst, regs, mi_shift, mi_mask); + dce_mi->base.funcs->mem_input_program_display_marks = dce120_mi_program_display_marks; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h new file mode 100644 index 000000000000..05d39c0cbe87 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h @@ -0,0 +1,347 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DCE_MEM_INPUT_H__ +#define __DCE_MEM_INPUT_H__ + +#include "dc_hw_types.h" +#include "mem_input.h" + +#define TO_DCE_MEM_INPUT(mem_input)\ + container_of(mem_input, struct dce_mem_input, base) + +#define MI_DCE_BASE_REG_LIST(id)\ + SRI(GRPH_ENABLE, DCP, id),\ + SRI(GRPH_CONTROL, DCP, id),\ + SRI(GRPH_X_START, DCP, id),\ + SRI(GRPH_Y_START, DCP, id),\ + SRI(GRPH_X_END, DCP, id),\ + SRI(GRPH_Y_END, DCP, id),\ + SRI(GRPH_PITCH, DCP, id),\ + SRI(HW_ROTATION, DCP, id),\ + SRI(GRPH_SWAP_CNTL, DCP, id),\ + SRI(PRESCALE_GRPH_CONTROL, DCP, id),\ + SRI(GRPH_UPDATE, DCP, id),\ + SRI(GRPH_FLIP_CONTROL, DCP, id),\ + SRI(GRPH_PRIMARY_SURFACE_ADDRESS, DCP, id),\ + SRI(GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, DCP, id),\ + SRI(GRPH_SECONDARY_SURFACE_ADDRESS, DCP, id),\ + SRI(GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, DCP, id),\ + SRI(DPG_PIPE_ARBITRATION_CONTROL1, DMIF_PG, id),\ + SRI(DPG_WATERMARK_MASK_CONTROL, DMIF_PG, id),\ + SRI(DPG_PIPE_URGENCY_CONTROL, DMIF_PG, id),\ + SRI(DPG_PIPE_STUTTER_CONTROL, DMIF_PG, id),\ + SRI(DMIF_BUFFER_CONTROL, PIPE, id) + +#define MI_DCE_PTE_REG_LIST(id)\ + SRI(DVMM_PTE_CONTROL, DCP, id),\ + SRI(DVMM_PTE_ARB_CONTROL, DCP, id) + +#define MI_DCE8_REG_LIST(id)\ + MI_DCE_BASE_REG_LIST(id),\ + SRI(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, DMIF_PG, id) + +#define MI_DCE11_2_REG_LIST(id)\ + MI_DCE8_REG_LIST(id),\ + SRI(GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, DCP, id) + +#define MI_DCE11_REG_LIST(id)\ + MI_DCE11_2_REG_LIST(id),\ + MI_DCE_PTE_REG_LIST(id) + +#define MI_DCE12_REG_LIST(id)\ + MI_DCE_BASE_REG_LIST(id),\ + MI_DCE_PTE_REG_LIST(id),\ + SRI(GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, DCP, id),\ + SRI(DPG_PIPE_STUTTER_CONTROL2, DMIF_PG, id),\ + SRI(DPG_PIPE_LOW_POWER_CONTROL, DMIF_PG, id),\ + SR(DCHUB_FB_LOCATION),\ + SR(DCHUB_AGP_BASE),\ + SR(DCHUB_AGP_BOT),\ + SR(DCHUB_AGP_TOP) + +struct dce_mem_input_registers { + /* DCP */ + uint32_t GRPH_ENABLE; + uint32_t GRPH_CONTROL; + uint32_t GRPH_X_START; + uint32_t GRPH_Y_START; + uint32_t GRPH_X_END; + uint32_t GRPH_Y_END; + uint32_t GRPH_PITCH; + uint32_t HW_ROTATION; + uint32_t GRPH_SWAP_CNTL; + uint32_t PRESCALE_GRPH_CONTROL; + uint32_t GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT; + uint32_t DVMM_PTE_CONTROL; + uint32_t DVMM_PTE_ARB_CONTROL; + uint32_t GRPH_UPDATE; + uint32_t GRPH_FLIP_CONTROL; + uint32_t GRPH_PRIMARY_SURFACE_ADDRESS; + uint32_t GRPH_PRIMARY_SURFACE_ADDRESS_HIGH; + uint32_t GRPH_SECONDARY_SURFACE_ADDRESS; + uint32_t GRPH_SECONDARY_SURFACE_ADDRESS_HIGH; + /* DMIF_PG */ + uint32_t DPG_PIPE_ARBITRATION_CONTROL1; + uint32_t DPG_WATERMARK_MASK_CONTROL; + uint32_t DPG_PIPE_URGENCY_CONTROL; + uint32_t DPG_PIPE_NB_PSTATE_CHANGE_CONTROL; + uint32_t DPG_PIPE_LOW_POWER_CONTROL; + uint32_t DPG_PIPE_STUTTER_CONTROL; + uint32_t DPG_PIPE_STUTTER_CONTROL2; + /* DCI */ + uint32_t DMIF_BUFFER_CONTROL; + /* MC_HUB */ + uint32_t MC_HUB_RDREQ_DMIF_LIMIT; + /*DCHUB*/ + uint32_t DCHUB_FB_LOCATION; + uint32_t DCHUB_AGP_BASE; + uint32_t DCHUB_AGP_BOT; + uint32_t DCHUB_AGP_TOP; +}; + +/* Set_Filed_for_Block */ +#define SFB(blk_name, reg_name, field_name, post_fix)\ + .field_name = blk_name ## reg_name ## __ ## field_name ## post_fix + +#define MI_GFX8_TILE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_BANKS, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_BANK_WIDTH, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_BANK_HEIGHT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_TILE_SPLIT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_MICRO_TILE_MODE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_PIPE_CONFIG, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_ARRAY_MODE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE, mask_sh) + +#define MI_DCP_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_ENABLE, GRPH_ENABLE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_DEPTH, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_FORMAT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_BANKS, mask_sh),\ + SFB(blk, GRPH_X_START, GRPH_X_START, mask_sh),\ + SFB(blk, GRPH_Y_START, GRPH_Y_START, mask_sh),\ + SFB(blk, GRPH_X_END, GRPH_X_END, mask_sh),\ + SFB(blk, GRPH_Y_END, GRPH_Y_END, mask_sh),\ + SFB(blk, GRPH_PITCH, GRPH_PITCH, mask_sh),\ + SFB(blk, HW_ROTATION, GRPH_ROTATION_ANGLE, mask_sh),\ + SFB(blk, GRPH_SWAP_CNTL, GRPH_RED_CROSSBAR, mask_sh),\ + SFB(blk, GRPH_SWAP_CNTL, GRPH_BLUE_CROSSBAR, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_SELECT, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_R_SIGN, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_G_SIGN, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_B_SIGN, mask_sh),\ + SFB(blk, GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + SFB(blk, GRPH_SECONDARY_SURFACE_ADDRESS, GRPH_SECONDARY_SURFACE_ADDRESS, mask_sh),\ + SFB(blk, GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + SFB(blk, GRPH_PRIMARY_SURFACE_ADDRESS, GRPH_PRIMARY_SURFACE_ADDRESS, mask_sh),\ + SFB(blk, GRPH_UPDATE, GRPH_SURFACE_UPDATE_PENDING, mask_sh),\ + SFB(blk, GRPH_UPDATE, GRPH_UPDATE_LOCK, mask_sh),\ + SFB(blk, GRPH_FLIP_CONTROL, GRPH_SURFACE_UPDATE_H_RETRACE_EN, mask_sh) + +#define MI_DCP_DCE11_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, mask_sh) + +#define MI_DCP_PTE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, DVMM_PTE_CONTROL, DVMM_PAGE_WIDTH, mask_sh),\ + SFB(blk, DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT, mask_sh),\ + SFB(blk, DVMM_PTE_CONTROL, DVMM_MIN_PTE_BEFORE_FLIP, mask_sh),\ + SFB(blk, DVMM_PTE_ARB_CONTROL, DVMM_PTE_REQ_PER_CHUNK, mask_sh),\ + SFB(blk, DVMM_PTE_ARB_CONTROL, DVMM_MAX_PTE_REQ_OUTSTANDING, mask_sh) + +#define MI_DMIF_PG_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, DPG_PIPE_ARBITRATION_CONTROL1, PIXEL_DURATION, mask_sh),\ + SFB(blk, DPG_WATERMARK_MASK_CONTROL, URGENCY_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_WATERMARK_MASK_CONTROL, STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_URGENCY_CONTROL, URGENCY_LOW_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_URGENCY_CONTROL, URGENCY_HIGH_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_ENABLE, mask_sh),\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_IGNORE_FBC, mask_sh),\ + SF(PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED, mask_sh),\ + SF(PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATION_COMPLETED, mask_sh) + +#define MI_DMIF_PG_MASK_SH_DCE(mask_sh, blk)\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_EXIT_SELF_REFRESH_WATERMARK, mask_sh),\ + SFB(blk, DPG_WATERMARK_MASK_CONTROL, NB_PSTATE_CHANGE_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_ENABLE, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_URGENT_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_WATERMARK, mask_sh) + +#define MI_DCE8_MASK_SH_LIST(mask_sh)\ + MI_DCP_MASK_SH_LIST(mask_sh, ),\ + MI_DMIF_PG_MASK_SH_LIST(mask_sh, ),\ + MI_DMIF_PG_MASK_SH_DCE(mask_sh, ),\ + MI_GFX8_TILE_MASK_SH_LIST(mask_sh, ) + +#define MI_DCE11_2_MASK_SH_LIST(mask_sh)\ + MI_DCE8_MASK_SH_LIST(mask_sh),\ + MI_DCP_DCE11_MASK_SH_LIST(mask_sh, ) + +#define MI_DCE11_MASK_SH_LIST(mask_sh)\ + MI_DCE11_2_MASK_SH_LIST(mask_sh),\ + MI_DCP_PTE_MASK_SH_LIST(mask_sh, ) + +#define MI_GFX9_TILE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_CONTROL, GRPH_SW_MODE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_SE_ENABLE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_SHADER_ENGINES, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_PIPES, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE, mask_sh) + +#define MI_DCE12_DMIF_PG_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL2, STUTTER_EXIT_SELF_REFRESH_WATERMARK, mask_sh),\ + SFB(blk, DPG_WATERMARK_MASK_CONTROL, PSTATE_CHANGE_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_ENABLE, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_URGENT_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_WATERMARK, mask_sh) + +#define MI_GFX9_DCHUB_MASK_SH_LIST(mask_sh)\ + SF(DCHUB_FB_LOCATION, FB_TOP, mask_sh),\ + SF(DCHUB_FB_LOCATION, FB_BASE, mask_sh),\ + SF(DCHUB_AGP_BASE, AGP_BASE, mask_sh),\ + SF(DCHUB_AGP_BOT, AGP_BOT, mask_sh),\ + SF(DCHUB_AGP_TOP, AGP_TOP, mask_sh) + +#define MI_DCE12_MASK_SH_LIST(mask_sh)\ + MI_DCP_MASK_SH_LIST(mask_sh, DCP0_),\ + SF(DCP0_GRPH_SECONDARY_SURFACE_ADDRESS, GRPH_SECONDARY_DFQ_ENABLE, mask_sh),\ + MI_DCP_DCE11_MASK_SH_LIST(mask_sh, DCP0_),\ + MI_DCP_PTE_MASK_SH_LIST(mask_sh, DCP0_),\ + MI_DMIF_PG_MASK_SH_LIST(mask_sh, DMIF_PG0_),\ + MI_DCE12_DMIF_PG_MASK_SH_LIST(mask_sh, DMIF_PG0_),\ + MI_GFX9_TILE_MASK_SH_LIST(mask_sh, DCP0_),\ + MI_GFX9_DCHUB_MASK_SH_LIST(mask_sh) + +#define MI_REG_FIELD_LIST(type) \ + type GRPH_ENABLE; \ + type GRPH_X_START; \ + type GRPH_Y_START; \ + type GRPH_X_END; \ + type GRPH_Y_END; \ + type GRPH_PITCH; \ + type GRPH_ROTATION_ANGLE; \ + type GRPH_RED_CROSSBAR; \ + type GRPH_BLUE_CROSSBAR; \ + type GRPH_PRESCALE_SELECT; \ + type GRPH_PRESCALE_R_SIGN; \ + type GRPH_PRESCALE_G_SIGN; \ + type GRPH_PRESCALE_B_SIGN; \ + type GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT; \ + type DVMM_PAGE_WIDTH; \ + type DVMM_PAGE_HEIGHT; \ + type DVMM_MIN_PTE_BEFORE_FLIP; \ + type DVMM_PTE_REQ_PER_CHUNK; \ + type DVMM_MAX_PTE_REQ_OUTSTANDING; \ + type GRPH_DEPTH; \ + type GRPH_FORMAT; \ + type GRPH_NUM_BANKS; \ + type GRPH_BANK_WIDTH;\ + type GRPH_BANK_HEIGHT;\ + type GRPH_MACRO_TILE_ASPECT;\ + type GRPH_TILE_SPLIT;\ + type GRPH_MICRO_TILE_MODE;\ + type GRPH_PIPE_CONFIG;\ + type GRPH_ARRAY_MODE;\ + type GRPH_COLOR_EXPANSION_MODE;\ + type GRPH_SW_MODE; \ + type GRPH_SE_ENABLE; \ + type GRPH_NUM_SHADER_ENGINES; \ + type GRPH_NUM_PIPES; \ + type GRPH_SECONDARY_SURFACE_ADDRESS_HIGH; \ + type GRPH_SECONDARY_SURFACE_ADDRESS; \ + type GRPH_SECONDARY_DFQ_ENABLE; \ + type GRPH_PRIMARY_SURFACE_ADDRESS_HIGH; \ + type GRPH_PRIMARY_SURFACE_ADDRESS; \ + type GRPH_SURFACE_UPDATE_PENDING; \ + type GRPH_SURFACE_UPDATE_H_RETRACE_EN; \ + type GRPH_UPDATE_LOCK; \ + type PIXEL_DURATION; \ + type URGENCY_WATERMARK_MASK; \ + type PSTATE_CHANGE_WATERMARK_MASK; \ + type NB_PSTATE_CHANGE_WATERMARK_MASK; \ + type STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK; \ + type URGENCY_LOW_WATERMARK; \ + type URGENCY_HIGH_WATERMARK; \ + type NB_PSTATE_CHANGE_ENABLE; \ + type NB_PSTATE_CHANGE_URGENT_DURING_REQUEST; \ + type NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST; \ + type NB_PSTATE_CHANGE_WATERMARK; \ + type PSTATE_CHANGE_ENABLE; \ + type PSTATE_CHANGE_URGENT_DURING_REQUEST; \ + type PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST; \ + type PSTATE_CHANGE_WATERMARK; \ + type STUTTER_ENABLE; \ + type STUTTER_IGNORE_FBC; \ + type STUTTER_EXIT_SELF_REFRESH_WATERMARK; \ + type DMIF_BUFFERS_ALLOCATED; \ + type DMIF_BUFFERS_ALLOCATION_COMPLETED; \ + type ENABLE; /* MC_HUB_RDREQ_DMIF_LIMIT */\ + type FB_BASE; \ + type FB_TOP; \ + type AGP_BASE; \ + type AGP_TOP; \ + type AGP_BOT; \ + +struct dce_mem_input_shift { + MI_REG_FIELD_LIST(uint8_t) +}; + +struct dce_mem_input_mask { + MI_REG_FIELD_LIST(uint32_t) +}; + +struct dce_mem_input_wa { + uint8_t single_head_rdreq_dmif_limit; +}; + +struct dce_mem_input { + struct mem_input base; + + const struct dce_mem_input_registers *regs; + const struct dce_mem_input_shift *shifts; + const struct dce_mem_input_mask *masks; + + struct dce_mem_input_wa wa; +}; + +void dce_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask); + +void dce112_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask); + +#endif /*__DCE_MEM_INPUT_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c new file mode 100644 index 000000000000..3931412ab6d3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c @@ -0,0 +1,567 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "basics/conversion.h" + +#include "dce_opp.h" + +#include "reg_helper.h" + +#define REG(reg)\ + (opp110->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + opp110->opp_shift->field_name, opp110->opp_mask->field_name + +#define CTX \ + opp110->base.ctx + +enum { + MAX_PWL_ENTRY = 128, + MAX_REGIONS_NUMBER = 16 +}; + +enum { + MAX_LUT_ENTRY = 256, + MAX_NUMBER_OF_ENTRIES = 256 +}; + + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + + + + + + + + + + + + + + + + + + + + + + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ + + + +/** + * set_truncation + * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp + * 2) enable truncation + * 3) HW remove 12bit FMT support for DCE11 power saving reason. + */ +static void set_truncation( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + /*Disable truncation*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 0, + FMT_TRUNCATE_DEPTH, 0, + FMT_TRUNCATE_MODE, 0); + + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + /* 8bpc trunc on YCbCr422*/ + if (params->flags.TRUNCATE_DEPTH == 1) + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, 1, + FMT_TRUNCATE_MODE, 0); + else if (params->flags.TRUNCATE_DEPTH == 2) + /* 10bpc trunc on YCbCr422*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, 2, + FMT_TRUNCATE_MODE, 0); + return; + } + /* on other format-to do */ + if (params->flags.TRUNCATE_ENABLED == 0 || + params->flags.TRUNCATE_DEPTH == 2) + return; + /*Set truncation depth and Enable truncation*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, + params->flags.TRUNCATE_MODE, + FMT_TRUNCATE_MODE, + params->flags.TRUNCATE_DEPTH); +} + + +/** + * set_spatial_dither + * 1) set spatial dithering mode: pattern of seed + * 2) set spatical dithering depth: 0 for 18bpp or 1 for 24bpp + * 3) set random seed + * 4) set random mode + * lfsr is reset every frame or not reset + * RGB dithering method + * 0: RGB data are all dithered with x^28+x^3+1 + * 1: R data is dithered with x^28+x^3+1 + * G data is dithered with x^28+X^9+1 + * B data is dithered with x^28+x^13+1 + * enable high pass filter or not + * 5) enable spatical dithering + */ +static void set_spatial_dither( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + /*Disable spatial (random) dithering*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_EN, 0, + FMT_SPATIAL_DITHER_DEPTH, 0, + FMT_SPATIAL_DITHER_MODE, 0); + + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_HIGHPASS_RANDOM_ENABLE, 0, + FMT_FRAME_RANDOM_ENABLE, 0, + FMT_RGB_RANDOM_ENABLE, 0); + + REG_UPDATE(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN, 0); + + /* no 10bpc on DCE11*/ + if (params->flags.SPATIAL_DITHER_ENABLED == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 2) + return; + + /* only use FRAME_COUNTER_MAX if frameRandom == 1*/ + + if (opp110->opp_mask->FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX && + opp110->opp_mask->FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP) { + if (params->flags.FRAME_RANDOM == 1) { + if (params->flags.SPATIAL_DITHER_DEPTH == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 1) { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 15, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 2); + } else if (params->flags.SPATIAL_DITHER_DEPTH == 2) { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 3, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 1); + } else + return; + } else { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 0, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 0); + } + } + /* Set seed for random values for + * spatial dithering for R,G,B channels + */ + REG_UPDATE(FMT_DITHER_RAND_R_SEED, + FMT_RAND_R_SEED, params->r_seed_value); + + REG_UPDATE(FMT_DITHER_RAND_G_SEED, + FMT_RAND_G_SEED, params->g_seed_value); + + REG_UPDATE(FMT_DITHER_RAND_B_SEED, + FMT_RAND_B_SEED, params->b_seed_value); + + /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero + * offset for the R/Cr channel, lower 4LSB + * is forced to zeros. Typically set to 0 + * RGB and 0x80000 YCbCr. + */ + /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero + * offset for the G/Y channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB + * and 0x80000 YCbCr. + */ + /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero + * offset for the B/Cb channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB and + * 0x80000 YCbCr. + */ + + /* Disable High pass filter + * Reset only at startup + * Set RGB data dithered with x^28+x^3+1 + */ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_HIGHPASS_RANDOM_ENABLE, params->flags.HIGHPASS_RANDOM, + FMT_FRAME_RANDOM_ENABLE, params->flags.FRAME_RANDOM, + FMT_RGB_RANDOM_ENABLE, params->flags.RGB_RANDOM); + + /* Set spatial dithering bit depth + * Set spatial dithering mode + * (default is Seed patterrn AAAA...) + * Enable spatial dithering + */ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_DEPTH, params->flags.SPATIAL_DITHER_DEPTH, + FMT_SPATIAL_DITHER_MODE, params->flags.SPATIAL_DITHER_MODE, + FMT_SPATIAL_DITHER_EN, 1); +} + +/** + * SetTemporalDither (Frame Modulation) + * 1) set temporal dither depth + * 2) select pattern: from hard-coded pattern or programmable pattern + * 3) select optimized strips for BGR or RGB LCD sub-pixel + * 4) set s matrix + * 5) set t matrix + * 6) set grey level for 0.25, 0.5, 0.75 + * 7) enable temporal dithering + */ + +static void set_temporal_dither( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + /*Disable temporal (frame modulation) dithering first*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN, 0, + FMT_TEMPORAL_DITHER_RESET, 0, + FMT_TEMPORAL_DITHER_OFFSET, 0); + + REG_UPDATE_2(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH, 0, + FMT_TEMPORAL_LEVEL, 0); + + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL, 0, + FMT_50FRC_SEL, 0, + FMT_75FRC_SEL, 0); + + /* no 10bpc dither on DCE11*/ + if (params->flags.FRAME_MODULATION_ENABLED == 0 || + params->flags.FRAME_MODULATION_DEPTH == 2) + return; + + /* Set temporal dithering depth*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH, params->flags.FRAME_MODULATION_DEPTH, + FMT_TEMPORAL_DITHER_RESET, 0, + FMT_TEMPORAL_DITHER_OFFSET, 0); + + /*Select legacy pattern based on FRC and Temporal level*/ + if (REG(FMT_TEMPORAL_DITHER_PATTERN_CONTROL)) { + REG_WRITE(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, 0); + /*Set s matrix*/ + REG_WRITE(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, 0); + /*Set t matrix*/ + REG_WRITE(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, 0); + } + + /*Select patterns for 0.25, 0.5 and 0.75 grey level*/ + REG_UPDATE(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL, params->flags.TEMPORAL_LEVEL); + + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL, params->flags.FRC25, + FMT_50FRC_SEL, params->flags.FRC50, + FMT_75FRC_SEL, params->flags.FRC75); + + /*Enable bit reduction by temporal (frame modulation) dithering*/ + REG_UPDATE(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN, 1); +} + +/** + * Set Clamping + * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) + * 1 for 8 bpc + * 2 for 10 bpc + * 3 for 12 bpc + * 7 for programable + * 2) Enable clamp if Limited range requested + */ +void dce110_opp_set_clamping( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 0, + FMT_CLAMP_COLOR_FORMAT, 0); + + switch (params->clamping_level) { + case CLAMPING_FULL_RANGE: + break; + case CLAMPING_LIMITED_RANGE_8BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 1); + break; + case CLAMPING_LIMITED_RANGE_10BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 2); + break; + case CLAMPING_LIMITED_RANGE_12BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 3); + break; + case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: + /*Set clamp control*/ + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 7); + + /*set the defaults*/ + REG_SET_2(FMT_CLAMP_COMPONENT_R, 0, + FMT_CLAMP_LOWER_R, 0x10, + FMT_CLAMP_UPPER_R, 0xFEF); + + REG_SET_2(FMT_CLAMP_COMPONENT_G, 0, + FMT_CLAMP_LOWER_G, 0x10, + FMT_CLAMP_UPPER_G, 0xFEF); + + REG_SET_2(FMT_CLAMP_COMPONENT_B, 0, + FMT_CLAMP_LOWER_B, 0x10, + FMT_CLAMP_UPPER_B, 0xFEF); + break; + default: + break; + } +} + +/** + * set_pixel_encoding + * + * Set Pixel Encoding + * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly + * 1: YCbCr 4:2:2 + */ +static void set_pixel_encoding( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + if (opp110->opp_mask->FMT_CBCR_BIT_REDUCTION_BYPASS) + REG_UPDATE_3(FMT_CONTROL, + FMT_PIXEL_ENCODING, 0, + FMT_SUBSAMPLING_MODE, 0, + FMT_CBCR_BIT_REDUCTION_BYPASS, 0); + else + REG_UPDATE_2(FMT_CONTROL, + FMT_PIXEL_ENCODING, 0, + FMT_SUBSAMPLING_MODE, 0); + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(FMT_CONTROL, + FMT_PIXEL_ENCODING, 1, + FMT_SUBSAMPLING_ORDER, 0); + } + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR420) { + REG_UPDATE_3(FMT_CONTROL, + FMT_PIXEL_ENCODING, 2, + FMT_SUBSAMPLING_MODE, 2, + FMT_CBCR_BIT_REDUCTION_BYPASS, 1); + } + +} + +void dce110_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + set_truncation(opp110, params); + set_spatial_dither(opp110, params); + set_temporal_dither(opp110, params); +} + +void dce110_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + dce110_opp_set_clamping(opp110, params); + set_pixel_encoding(opp110, params); +} + +static void program_formatter_420_memory(struct output_pixel_processor *opp) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + uint32_t fmt_mem_cntl_value; + + /* Program source select*/ + /* Use HW default source select for FMT_MEMORYx_CONTROL */ + /* Use that value for FMT_SRC_SELECT as well*/ + REG_GET(CONTROL, + FMT420_MEM0_SOURCE_SEL, &fmt_mem_cntl_value); + + REG_UPDATE(FMT_CONTROL, + FMT_SRC_SELECT, fmt_mem_cntl_value); + + /* Turn on the memory */ + REG_UPDATE(CONTROL, + FMT420_MEM0_PWR_FORCE, 0); +} + +void dce110_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 0, + FMT_DYNAMIC_EXP_MODE, 0); + + /*00 - 10-bit -> 12-bit dynamic expansion*/ + /*01 - 8-bit -> 12-bit dynamic expansion*/ + if (signal == SIGNAL_TYPE_HDMI_TYPE_A || + signal == SIGNAL_TYPE_DISPLAY_PORT || + signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + switch (color_dpth) { + case COLOR_DEPTH_888: + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1, + FMT_DYNAMIC_EXP_MODE, 1); + break; + case COLOR_DEPTH_101010: + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1, + FMT_DYNAMIC_EXP_MODE, 0); + break; + case COLOR_DEPTH_121212: + REG_UPDATE_2( + FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1,/*otherwise last two bits are zero*/ + FMT_DYNAMIC_EXP_MODE, 0); + break; + default: + break; + } + } +} + +static void program_formatter_reset_dig_resync_fifo(struct output_pixel_processor *opp) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + /* clear previous phase lock status*/ + REG_UPDATE(FMT_CONTROL, + FMT_420_PIXEL_PHASE_LOCKED_CLEAR, 1); + + /* poll until FMT_420_PIXEL_PHASE_LOCKED become 1*/ + REG_WAIT(FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED, 1, 10, 10); + +} + +void dce110_opp_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping) +{ + /* dithering is affected by , hence should be + * programmed afterwards */ + + if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) + program_formatter_420_memory(opp); + + dce110_opp_program_bit_depth_reduction( + opp, + fmt_bit_depth); + + dce110_opp_program_clamping_and_pixel_encoding( + opp, + clamping); + + if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) + program_formatter_reset_dig_resync_fifo(opp); + + return; +} + + + + + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static const struct opp_funcs funcs = { + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_destroy = dce110_opp_destroy, + .opp_program_fmt = dce110_opp_program_fmt, + .opp_program_bit_depth_reduction = dce110_opp_program_bit_depth_reduction +}; + +void dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce_opp_registers *regs, + const struct dce_opp_shift *opp_shift, + const struct dce_opp_mask *opp_mask) +{ + opp110->base.funcs = &funcs; + + opp110->base.ctx = ctx; + + opp110->base.inst = inst; + + opp110->regs = regs; + opp110->opp_shift = opp_shift; + opp110->opp_mask = opp_mask; +} + +void dce110_opp_destroy(struct output_pixel_processor **opp) +{ + if (*opp) + kfree(FROM_DCE11_OPP(*opp)); + *opp = NULL; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_opp.h b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.h new file mode 100644 index 000000000000..2ab0147cbd9d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.h @@ -0,0 +1,310 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_OPP_DCE_H__ +#define __DC_OPP_DCE_H__ + +#include "dc_types.h" +#include "opp.h" +#include "core_types.h" + +#define FROM_DCE11_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +enum dce110_opp_reg_type { + DCE110_OPP_REG_DCP = 0, + DCE110_OPP_REG_DCFE, + DCE110_OPP_REG_FMT, + + DCE110_OPP_REG_MAX +}; + +#define OPP_COMMON_REG_LIST_BASE(id) \ + SRI(FMT_DYNAMIC_EXP_CNTL, FMT, id), \ + SRI(FMT_BIT_DEPTH_CONTROL, FMT, id), \ + SRI(FMT_CONTROL, FMT, id), \ + SRI(FMT_DITHER_RAND_R_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_G_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_B_SEED, FMT, id), \ + SRI(FMT_CLAMP_CNTL, FMT, id), \ + SRI(FMT_CLAMP_COMPONENT_R, FMT, id), \ + SRI(FMT_CLAMP_COMPONENT_G, FMT, id), \ + SRI(FMT_CLAMP_COMPONENT_B, FMT, id) + +#define OPP_DCE_80_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id) + +#define OPP_DCE_100_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id) + +#define OPP_DCE_110_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id) + +#define OPP_DCE_112_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id), \ + SRI(CONTROL, FMT_MEMORY, id) + +#define OPP_DCE_120_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(CONTROL, FMT_MEMORY, id) + +#define OPP_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + OPP_SF(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, mask_sh),\ + OPP_SF(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_R_SEED, FMT_RAND_R_SEED, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_G_SEED, FMT_RAND_G_SEED, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_B_SEED, FMT_RAND_B_SEED, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_RESET, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_OFFSET, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_LEVEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_25FRC_SEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_50FRC_SEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_75FRC_SEL, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SRC_SELECT, mask_sh),\ + OPP_SF(FMT_CLAMP_CNTL, FMT_CLAMP_DATA_EN, mask_sh),\ + OPP_SF(FMT_CLAMP_CNTL, FMT_CLAMP_COLOR_FORMAT, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_R, FMT_CLAMP_LOWER_R, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_R, FMT_CLAMP_UPPER_R, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_G, FMT_CLAMP_LOWER_G, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_G, FMT_CLAMP_UPPER_G, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_B, FMT_CLAMP_LOWER_B, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_B, FMT_CLAMP_UPPER_B, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_PIXEL_ENCODING, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SUBSAMPLING_MODE, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SUBSAMPLING_ORDER, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_110(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_100(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_112(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_SOURCE_SEL, mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_PWR_FORCE, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED_CLEAR, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_CBCR_BIT_REDUCTION_BYPASS, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_80(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_120(mask_sh)\ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, mask_sh),\ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_RESET, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_OFFSET, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_LEVEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_25FRC_SEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_50FRC_SEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_75FRC_SEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh),\ + OPP_SF(FMT0_FMT_DITHER_RAND_R_SEED, FMT_RAND_R_SEED, mask_sh),\ + OPP_SF(FMT0_FMT_DITHER_RAND_G_SEED, FMT_RAND_G_SEED, mask_sh),\ + OPP_SF(FMT0_FMT_DITHER_RAND_B_SEED, FMT_RAND_B_SEED, mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_SOURCE_SEL, mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_PWR_FORCE, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SRC_SELECT, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED_CLEAR, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_DATA_EN, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_COLOR_FORMAT, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_R, FMT_CLAMP_LOWER_R, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_R, FMT_CLAMP_UPPER_R, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_G, FMT_CLAMP_LOWER_G, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_G, FMT_CLAMP_UPPER_G, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_B, FMT_CLAMP_LOWER_B, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_B, FMT_CLAMP_UPPER_B, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_PIXEL_ENCODING, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SUBSAMPLING_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SUBSAMPLING_ORDER, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_CBCR_BIT_REDUCTION_BYPASS, mask_sh) + +#define OPP_REG_FIELD_LIST(type) \ + type FMT_DYNAMIC_EXP_EN; \ + type FMT_DYNAMIC_EXP_MODE; \ + type FMT_TRUNCATE_EN; \ + type FMT_TRUNCATE_DEPTH; \ + type FMT_TRUNCATE_MODE; \ + type FMT_SPATIAL_DITHER_EN; \ + type FMT_SPATIAL_DITHER_DEPTH; \ + type FMT_SPATIAL_DITHER_MODE; \ + type FMT_TEMPORAL_DITHER_EN; \ + type FMT_TEMPORAL_DITHER_RESET; \ + type FMT_TEMPORAL_DITHER_OFFSET; \ + type FMT_TEMPORAL_DITHER_DEPTH; \ + type FMT_TEMPORAL_LEVEL; \ + type FMT_25FRC_SEL; \ + type FMT_50FRC_SEL; \ + type FMT_75FRC_SEL; \ + type FMT_HIGHPASS_RANDOM_ENABLE; \ + type FMT_FRAME_RANDOM_ENABLE; \ + type FMT_RGB_RANDOM_ENABLE; \ + type FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX; \ + type FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP; \ + type FMT_STEREOSYNC_OVERRIDE; \ + type FMT_RAND_R_SEED; \ + type FMT_RAND_G_SEED; \ + type FMT_RAND_B_SEED; \ + type FMT420_MEM0_SOURCE_SEL; \ + type FMT420_MEM0_PWR_FORCE; \ + type FMT_SRC_SELECT; \ + type FMT_420_PIXEL_PHASE_LOCKED_CLEAR; \ + type FMT_420_PIXEL_PHASE_LOCKED; \ + type FMT_CLAMP_DATA_EN; \ + type FMT_CLAMP_COLOR_FORMAT; \ + type FMT_CLAMP_LOWER_R; \ + type FMT_CLAMP_UPPER_R; \ + type FMT_CLAMP_LOWER_G; \ + type FMT_CLAMP_UPPER_G; \ + type FMT_CLAMP_LOWER_B; \ + type FMT_CLAMP_UPPER_B; \ + type FMT_PIXEL_ENCODING; \ + type FMT_SUBSAMPLING_ORDER; \ + type FMT_SUBSAMPLING_MODE; \ + type FMT_CBCR_BIT_REDUCTION_BYPASS;\ + +struct dce_opp_shift { + OPP_REG_FIELD_LIST(uint8_t) +}; + +struct dce_opp_mask { + OPP_REG_FIELD_LIST(uint32_t) +}; + +struct dce_opp_registers { + uint32_t FMT_DYNAMIC_EXP_CNTL; + uint32_t FMT_BIT_DEPTH_CONTROL; + uint32_t FMT_CONTROL; + uint32_t FMT_DITHER_RAND_R_SEED; + uint32_t FMT_DITHER_RAND_G_SEED; + uint32_t FMT_DITHER_RAND_B_SEED; + uint32_t FMT_TEMPORAL_DITHER_PATTERN_CONTROL; + uint32_t FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX; + uint32_t FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX; + uint32_t CONTROL; + uint32_t FMT_CLAMP_CNTL; + uint32_t FMT_CLAMP_COMPONENT_R; + uint32_t FMT_CLAMP_COMPONENT_G; + uint32_t FMT_CLAMP_COMPONENT_B; +}; + +/* OPP RELATED */ +#define TO_DCE110_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +struct dce110_opp { + struct output_pixel_processor base; + const struct dce_opp_registers *regs; + const struct dce_opp_shift *opp_shift; + const struct dce_opp_mask *opp_mask; +}; + +void dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce_opp_registers *regs, + const struct dce_opp_shift *opp_shift, + const struct dce_opp_mask *opp_mask); + +void dce110_opp_destroy(struct output_pixel_processor **opp); + + + +/* FORMATTER RELATED */ +void dce110_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params); + +void dce110_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params); + +void dce110_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal); + +void dce110_opp_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping); + +void dce110_opp_set_clamping( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters.c b/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters.c new file mode 100644 index 000000000000..6243450b41b7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters.c @@ -0,0 +1,1119 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "transform.h" + +static const uint16_t filter_2tap_16p[18] = { + 4096, 0, + 3840, 256, + 3584, 512, + 3328, 768, + 3072, 1024, + 2816, 1280, + 2560, 1536, + 2304, 1792, + 2048, 2048 +}; + +static const uint16_t filter_3tap_16p_upscale[27] = { + 2048, 2048, 0, + 1708, 2424, 16348, + 1372, 2796, 16308, + 1056, 3148, 16272, + 768, 3464, 16244, + 512, 3728, 16236, + 296, 3928, 16252, + 124, 4052, 16296, + 0, 4096, 0 +}; + +static const uint16_t filter_3tap_16p_117[27] = { + 2048, 2048, 0, + 1824, 2276, 16376, + 1600, 2496, 16380, + 1376, 2700, 16, + 1156, 2880, 52, + 948, 3032, 108, + 756, 3144, 192, + 580, 3212, 296, + 428, 3236, 428 +}; + +static const uint16_t filter_3tap_16p_150[27] = { + 2048, 2048, 0, + 1872, 2184, 36, + 1692, 2308, 88, + 1516, 2420, 156, + 1340, 2516, 236, + 1168, 2592, 328, + 1004, 2648, 440, + 844, 2684, 560, + 696, 2696, 696 +}; + +static const uint16_t filter_3tap_16p_183[27] = { + 2048, 2048, 0, + 1892, 2104, 92, + 1744, 2152, 196, + 1592, 2196, 300, + 1448, 2232, 412, + 1304, 2256, 528, + 1168, 2276, 648, + 1032, 2288, 772, + 900, 2292, 900 +}; + +static const uint16_t filter_4tap_16p_upscale[36] = { + 0, 4096, 0, 0, + 16240, 4056, 180, 16380, + 16136, 3952, 404, 16364, + 16072, 3780, 664, 16344, + 16040, 3556, 952, 16312, + 16036, 3284, 1268, 16272, + 16052, 2980, 1604, 16224, + 16084, 2648, 1952, 16176, + 16128, 2304, 2304, 16128 +}; + +static const uint16_t filter_4tap_16p_117[36] = { + 428, 3236, 428, 0, + 276, 3232, 604, 16364, + 148, 3184, 800, 16340, + 44, 3104, 1016, 16312, + 16344, 2984, 1244, 16284, + 16284, 2832, 1488, 16256, + 16244, 2648, 1732, 16236, + 16220, 2440, 1976, 16220, + 16212, 2216, 2216, 16212 +}; + +static const uint16_t filter_4tap_16p_150[36] = { + 696, 2700, 696, 0, + 560, 2700, 848, 16364, + 436, 2676, 1008, 16348, + 328, 2628, 1180, 16336, + 232, 2556, 1356, 16328, + 152, 2460, 1536, 16328, + 84, 2344, 1716, 16332, + 28, 2208, 1888, 16348, + 16376, 2052, 2052, 16376 +}; + +static const uint16_t filter_4tap_16p_183[36] = { + 940, 2208, 940, 0, + 832, 2200, 1052, 4, + 728, 2180, 1164, 16, + 628, 2148, 1280, 36, + 536, 2100, 1392, 60, + 448, 2044, 1504, 92, + 368, 1976, 1612, 132, + 296, 1900, 1716, 176, + 232, 1812, 1812, 232 +}; + +static const uint16_t filter_2tap_64p[66] = { + 4096, 0, + 4032, 64, + 3968, 128, + 3904, 192, + 3840, 256, + 3776, 320, + 3712, 384, + 3648, 448, + 3584, 512, + 3520, 576, + 3456, 640, + 3392, 704, + 3328, 768, + 3264, 832, + 3200, 896, + 3136, 960, + 3072, 1024, + 3008, 1088, + 2944, 1152, + 2880, 1216, + 2816, 1280, + 2752, 1344, + 2688, 1408, + 2624, 1472, + 2560, 1536, + 2496, 1600, + 2432, 1664, + 2368, 1728, + 2304, 1792, + 2240, 1856, + 2176, 1920, + 2112, 1984, + 2048, 2048 }; + +static const uint16_t filter_3tap_64p_upscale[99] = { + 2048, 2048, 0, + 1960, 2140, 16376, + 1876, 2236, 16364, + 1792, 2328, 16356, + 1708, 2424, 16348, + 1620, 2516, 16336, + 1540, 2612, 16328, + 1456, 2704, 16316, + 1372, 2796, 16308, + 1292, 2884, 16296, + 1212, 2976, 16288, + 1136, 3060, 16280, + 1056, 3148, 16272, + 984, 3228, 16264, + 908, 3312, 16256, + 836, 3388, 16248, + 768, 3464, 16244, + 700, 3536, 16240, + 636, 3604, 16236, + 572, 3668, 16236, + 512, 3728, 16236, + 456, 3784, 16236, + 400, 3836, 16240, + 348, 3884, 16244, + 296, 3928, 16252, + 252, 3964, 16260, + 204, 4000, 16268, + 164, 4028, 16284, + 124, 4052, 16296, + 88, 4072, 16316, + 56, 4084, 16336, + 24, 4092, 16356, + 0, 4096, 0 +}; + +static const uint16_t filter_3tap_64p_117[99] = { + 2048, 2048, 0, + 1992, 2104, 16380, + 1936, 2160, 16380, + 1880, 2220, 16376, + 1824, 2276, 16376, + 1768, 2332, 16376, + 1712, 2388, 16376, + 1656, 2444, 16376, + 1600, 2496, 16380, + 1544, 2548, 0, + 1488, 2600, 4, + 1432, 2652, 8, + 1376, 2700, 16, + 1320, 2748, 20, + 1264, 2796, 32, + 1212, 2840, 40, + 1156, 2880, 52, + 1104, 2920, 64, + 1052, 2960, 80, + 1000, 2996, 92, + 948, 3032, 108, + 900, 3060, 128, + 852, 3092, 148, + 804, 3120, 168, + 756, 3144, 192, + 712, 3164, 216, + 668, 3184, 240, + 624, 3200, 268, + 580, 3212, 296, + 540, 3220, 328, + 500, 3228, 360, + 464, 3232, 392, + 428, 3236, 428 +}; + +static const uint16_t filter_3tap_64p_150[99] = { + 2048, 2048, 0, + 2004, 2080, 8, + 1960, 2116, 16, + 1916, 2148, 28, + 1872, 2184, 36, + 1824, 2216, 48, + 1780, 2248, 60, + 1736, 2280, 76, + 1692, 2308, 88, + 1648, 2336, 104, + 1604, 2368, 120, + 1560, 2392, 136, + 1516, 2420, 156, + 1472, 2444, 172, + 1428, 2472, 192, + 1384, 2492, 212, + 1340, 2516, 236, + 1296, 2536, 256, + 1252, 2556, 280, + 1212, 2576, 304, + 1168, 2592, 328, + 1124, 2608, 356, + 1084, 2624, 384, + 1044, 2636, 412, + 1004, 2648, 440, + 964, 2660, 468, + 924, 2668, 500, + 884, 2676, 528, + 844, 2684, 560, + 808, 2688, 596, + 768, 2692, 628, + 732, 2696, 664, + 696, 2696, 696 +}; + +static const uint16_t filter_3tap_64p_183[99] = { + 2048, 2048, 0, + 2008, 2060, 20, + 1968, 2076, 44, + 1932, 2088, 68, + 1892, 2104, 92, + 1856, 2116, 120, + 1816, 2128, 144, + 1780, 2140, 168, + 1744, 2152, 196, + 1704, 2164, 220, + 1668, 2176, 248, + 1632, 2188, 272, + 1592, 2196, 300, + 1556, 2204, 328, + 1520, 2216, 356, + 1484, 2224, 384, + 1448, 2232, 412, + 1412, 2240, 440, + 1376, 2244, 468, + 1340, 2252, 496, + 1304, 2256, 528, + 1272, 2264, 556, + 1236, 2268, 584, + 1200, 2272, 616, + 1168, 2276, 648, + 1132, 2280, 676, + 1100, 2284, 708, + 1064, 2288, 740, + 1032, 2288, 772, + 996, 2292, 800, + 964, 2292, 832, + 932, 2292, 868, + 900, 2292, 900 +}; + +static const uint16_t filter_4tap_64p_upscale[132] = { + 0, 4096, 0, 0, + 16344, 4092, 40, 0, + 16308, 4084, 84, 16380, + 16272, 4072, 132, 16380, + 16240, 4056, 180, 16380, + 16212, 4036, 232, 16376, + 16184, 4012, 288, 16372, + 16160, 3984, 344, 16368, + 16136, 3952, 404, 16364, + 16116, 3916, 464, 16360, + 16100, 3872, 528, 16356, + 16084, 3828, 596, 16348, + 16072, 3780, 664, 16344, + 16060, 3728, 732, 16336, + 16052, 3676, 804, 16328, + 16044, 3616, 876, 16320, + 16040, 3556, 952, 16312, + 16036, 3492, 1028, 16300, + 16032, 3424, 1108, 16292, + 16032, 3356, 1188, 16280, + 16036, 3284, 1268, 16272, + 16036, 3212, 1352, 16260, + 16040, 3136, 1436, 16248, + 16044, 3056, 1520, 16236, + 16052, 2980, 1604, 16224, + 16060, 2896, 1688, 16212, + 16064, 2816, 1776, 16200, + 16076, 2732, 1864, 16188, + 16084, 2648, 1952, 16176, + 16092, 2564, 2040, 16164, + 16104, 2476, 2128, 16152, + 16116, 2388, 2216, 16140, + 16128, 2304, 2304, 16128 }; + +static const uint16_t filter_4tap_64p_117[132] = { + 420, 3248, 420, 0, + 380, 3248, 464, 16380, + 344, 3248, 508, 16372, + 308, 3248, 552, 16368, + 272, 3240, 596, 16364, + 236, 3236, 644, 16356, + 204, 3224, 692, 16352, + 172, 3212, 744, 16344, + 144, 3196, 796, 16340, + 116, 3180, 848, 16332, + 88, 3160, 900, 16324, + 60, 3136, 956, 16320, + 36, 3112, 1012, 16312, + 16, 3084, 1068, 16304, + 16380, 3056, 1124, 16296, + 16360, 3024, 1184, 16292, + 16340, 2992, 1244, 16284, + 16324, 2956, 1304, 16276, + 16308, 2920, 1364, 16268, + 16292, 2880, 1424, 16264, + 16280, 2836, 1484, 16256, + 16268, 2792, 1548, 16252, + 16256, 2748, 1608, 16244, + 16248, 2700, 1668, 16240, + 16240, 2652, 1732, 16232, + 16232, 2604, 1792, 16228, + 16228, 2552, 1856, 16224, + 16220, 2500, 1916, 16220, + 16216, 2444, 1980, 16216, + 16216, 2388, 2040, 16216, + 16212, 2332, 2100, 16212, + 16212, 2276, 2160, 16212, + 16212, 2220, 2220, 16212 }; + +static const uint16_t filter_4tap_64p_150[132] = { + 696, 2700, 696, 0, + 660, 2704, 732, 16380, + 628, 2704, 768, 16376, + 596, 2704, 804, 16372, + 564, 2700, 844, 16364, + 532, 2696, 884, 16360, + 500, 2692, 924, 16356, + 472, 2684, 964, 16352, + 440, 2676, 1004, 16352, + 412, 2668, 1044, 16348, + 384, 2656, 1088, 16344, + 360, 2644, 1128, 16340, + 332, 2632, 1172, 16336, + 308, 2616, 1216, 16336, + 284, 2600, 1260, 16332, + 260, 2580, 1304, 16332, + 236, 2560, 1348, 16328, + 216, 2540, 1392, 16328, + 196, 2516, 1436, 16328, + 176, 2492, 1480, 16324, + 156, 2468, 1524, 16324, + 136, 2440, 1568, 16328, + 120, 2412, 1612, 16328, + 104, 2384, 1656, 16328, + 88, 2352, 1700, 16332, + 72, 2324, 1744, 16332, + 60, 2288, 1788, 16336, + 48, 2256, 1828, 16340, + 36, 2220, 1872, 16344, + 24, 2184, 1912, 16352, + 12, 2148, 1952, 16356, + 4, 2112, 1996, 16364, + 16380, 2072, 2036, 16372 }; + +static const uint16_t filter_4tap_64p_183[132] = { + 944, 2204, 944, 0, + 916, 2204, 972, 0, + 888, 2200, 996, 0, + 860, 2200, 1024, 4, + 832, 2196, 1052, 4, + 808, 2192, 1080, 8, + 780, 2188, 1108, 12, + 756, 2180, 1140, 12, + 728, 2176, 1168, 16, + 704, 2168, 1196, 20, + 680, 2160, 1224, 24, + 656, 2152, 1252, 28, + 632, 2144, 1280, 36, + 608, 2132, 1308, 40, + 584, 2120, 1336, 48, + 560, 2112, 1364, 52, + 536, 2096, 1392, 60, + 516, 2084, 1420, 68, + 492, 2072, 1448, 76, + 472, 2056, 1476, 84, + 452, 2040, 1504, 92, + 428, 2024, 1532, 100, + 408, 2008, 1560, 112, + 392, 1992, 1584, 120, + 372, 1972, 1612, 132, + 352, 1956, 1636, 144, + 336, 1936, 1664, 156, + 316, 1916, 1688, 168, + 300, 1896, 1712, 180, + 284, 1876, 1736, 192, + 268, 1852, 1760, 208, + 252, 1832, 1784, 220, + 236, 1808, 1808, 236 }; + +static const uint16_t filter_5tap_64p_upscale[165] = { + 15936, 2496, 2496, 15936, 0, + 15948, 2404, 2580, 15924, 0, + 15960, 2312, 2664, 15912, 4, + 15976, 2220, 2748, 15904, 8, + 15992, 2128, 2832, 15896, 12, + 16004, 2036, 2912, 15888, 16, + 16020, 1944, 2992, 15880, 20, + 16036, 1852, 3068, 15876, 20, + 16056, 1760, 3140, 15876, 24, + 16072, 1668, 3216, 15872, 28, + 16088, 1580, 3284, 15872, 32, + 16104, 1492, 3352, 15876, 32, + 16120, 1404, 3420, 15876, 36, + 16140, 1316, 3480, 15884, 40, + 16156, 1228, 3540, 15892, 40, + 16172, 1144, 3600, 15900, 40, + 16188, 1060, 3652, 15908, 44, + 16204, 980, 3704, 15924, 44, + 16220, 900, 3756, 15936, 44, + 16236, 824, 3800, 15956, 44, + 16248, 744, 3844, 15972, 44, + 16264, 672, 3884, 15996, 44, + 16276, 600, 3920, 16020, 44, + 16292, 528, 3952, 16044, 40, + 16304, 460, 3980, 16072, 40, + 16316, 396, 4008, 16104, 36, + 16328, 332, 4032, 16136, 32, + 16336, 272, 4048, 16172, 28, + 16348, 212, 4064, 16208, 24, + 16356, 156, 4080, 16248, 16, + 16368, 100, 4088, 16292, 12, + 16376, 48, 4092, 16336, 4, + 0, 0, 4096, 0, 0 }; + +static const uint16_t filter_5tap_64p_117[165] = { + 16056, 2372, 2372, 16056, 0, + 16052, 2312, 2432, 16060, 0, + 16052, 2252, 2488, 16064, 0, + 16052, 2188, 2548, 16072, 0, + 16052, 2124, 2600, 16076, 0, + 16052, 2064, 2656, 16088, 0, + 16052, 2000, 2708, 16096, 0, + 16056, 1932, 2760, 16108, 0, + 16060, 1868, 2808, 16120, 0, + 16064, 1804, 2856, 16132, 0, + 16068, 1740, 2904, 16148, 16380, + 16076, 1676, 2948, 16164, 16380, + 16080, 1612, 2992, 16180, 16376, + 16088, 1544, 3032, 16200, 16372, + 16096, 1480, 3072, 16220, 16372, + 16104, 1420, 3108, 16244, 16368, + 16112, 1356, 3144, 16268, 16364, + 16120, 1292, 3180, 16292, 16360, + 16128, 1232, 3212, 16320, 16356, + 16136, 1168, 3240, 16344, 16352, + 16144, 1108, 3268, 16376, 16344, + 16156, 1048, 3292, 20, 16340, + 16164, 988, 3316, 52, 16332, + 16172, 932, 3336, 88, 16328, + 16184, 872, 3356, 124, 16320, + 16192, 816, 3372, 160, 16316, + 16204, 760, 3388, 196, 16308, + 16212, 708, 3400, 236, 16300, + 16220, 656, 3412, 276, 16292, + 16232, 604, 3420, 320, 16284, + 16240, 552, 3424, 364, 16276, + 16248, 504, 3428, 408, 16268, + 16256, 456, 3428, 456, 16256 }; + +static const uint16_t filter_5tap_64p_150[165] = { + 16368, 2064, 2064, 16368, 0, + 16352, 2028, 2100, 16380, 16380, + 16340, 1996, 2132, 12, 16376, + 16328, 1960, 2168, 24, 16376, + 16316, 1924, 2204, 44, 16372, + 16308, 1888, 2236, 60, 16368, + 16296, 1848, 2268, 76, 16364, + 16288, 1812, 2300, 96, 16360, + 16280, 1772, 2328, 116, 16356, + 16272, 1736, 2360, 136, 16352, + 16268, 1696, 2388, 160, 16348, + 16260, 1656, 2416, 180, 16344, + 16256, 1616, 2440, 204, 16340, + 16248, 1576, 2464, 228, 16336, + 16244, 1536, 2492, 252, 16332, + 16240, 1496, 2512, 276, 16324, + 16240, 1456, 2536, 304, 16320, + 16236, 1416, 2556, 332, 16316, + 16232, 1376, 2576, 360, 16312, + 16232, 1336, 2592, 388, 16308, + 16232, 1296, 2612, 416, 16300, + 16232, 1256, 2628, 448, 16296, + 16232, 1216, 2640, 480, 16292, + 16232, 1172, 2652, 512, 16288, + 16232, 1132, 2664, 544, 16284, + 16232, 1092, 2676, 576, 16280, + 16236, 1056, 2684, 608, 16272, + 16236, 1016, 2692, 644, 16268, + 16240, 976, 2700, 680, 16264, + 16240, 936, 2704, 712, 16260, + 16244, 900, 2708, 748, 16256, + 16248, 860, 2708, 788, 16252, + 16248, 824, 2708, 824, 16248 }; + +static const uint16_t filter_5tap_64p_183[165] = { + 228, 1816, 1816, 228, 0, + 216, 1792, 1836, 248, 16380, + 200, 1772, 1860, 264, 16376, + 184, 1748, 1884, 280, 16376, + 168, 1728, 1904, 300, 16372, + 156, 1704, 1928, 316, 16368, + 144, 1680, 1948, 336, 16364, + 128, 1656, 1968, 356, 16364, + 116, 1632, 1988, 376, 16360, + 104, 1604, 2008, 396, 16356, + 96, 1580, 2024, 416, 16356, + 84, 1556, 2044, 440, 16352, + 72, 1528, 2060, 460, 16348, + 64, 1504, 2076, 484, 16348, + 52, 1476, 2092, 504, 16344, + 44, 1448, 2104, 528, 16344, + 36, 1424, 2120, 552, 16340, + 28, 1396, 2132, 576, 16340, + 20, 1368, 2144, 600, 16340, + 12, 1340, 2156, 624, 16336, + 4, 1312, 2168, 652, 16336, + 0, 1284, 2180, 676, 16336, + 16376, 1256, 2188, 700, 16332, + 16372, 1228, 2196, 728, 16332, + 16368, 1200, 2204, 752, 16332, + 16364, 1172, 2212, 780, 16332, + 16356, 1144, 2216, 808, 16332, + 16352, 1116, 2220, 836, 16332, + 16352, 1084, 2224, 860, 16332, + 16348, 1056, 2228, 888, 16336, + 16344, 1028, 2232, 916, 16336, + 16340, 1000, 2232, 944, 16336, + 16340, 972, 2232, 972, 16340 }; + +static const uint16_t filter_6tap_64p_upscale[198] = { + 0, 0, 4092, 0, 0, 0, + 12, 16332, 4092, 52, 16368, 0, + 24, 16280, 4088, 108, 16356, 0, + 36, 16236, 4080, 168, 16340, 0, + 44, 16188, 4064, 228, 16324, 0, + 56, 16148, 4052, 292, 16308, 0, + 64, 16108, 4032, 356, 16292, 4, + 72, 16072, 4008, 424, 16276, 4, + 80, 16036, 3980, 492, 16256, 4, + 88, 16004, 3952, 564, 16240, 8, + 96, 15972, 3920, 636, 16220, 8, + 100, 15944, 3884, 712, 16204, 12, + 108, 15916, 3844, 788, 16184, 16, + 112, 15896, 3800, 864, 16164, 20, + 116, 15872, 3756, 944, 16144, 20, + 120, 15852, 3708, 1024, 16124, 24, + 120, 15836, 3656, 1108, 16104, 28, + 124, 15824, 3600, 1192, 16084, 32, + 124, 15808, 3544, 1276, 16064, 36, + 124, 15800, 3484, 1360, 16044, 40, + 128, 15792, 3420, 1448, 16024, 44, + 128, 15784, 3352, 1536, 16004, 48, + 124, 15780, 3288, 1624, 15988, 52, + 124, 15776, 3216, 1712, 15968, 56, + 124, 15776, 3144, 1800, 15948, 64, + 120, 15776, 3068, 1888, 15932, 68, + 120, 15780, 2992, 1976, 15912, 72, + 116, 15784, 2916, 2064, 15896, 76, + 112, 15792, 2836, 2152, 15880, 80, + 108, 15796, 2752, 2244, 15868, 84, + 104, 15804, 2672, 2328, 15852, 88, + 104, 15816, 2588, 2416, 15840, 92, + 100, 15828, 2504, 2504, 15828, 100 }; + +static const uint16_t filter_6tap_64p_117[198] = { + 16168, 476, 3568, 476, 16168, 0, + 16180, 428, 3564, 528, 16156, 0, + 16192, 376, 3556, 584, 16144, 4, + 16204, 328, 3548, 636, 16128, 4, + 16216, 280, 3540, 692, 16116, 8, + 16228, 232, 3524, 748, 16104, 12, + 16240, 188, 3512, 808, 16092, 12, + 16252, 148, 3492, 864, 16080, 16, + 16264, 104, 3472, 924, 16068, 16, + 16276, 64, 3452, 984, 16056, 20, + 16284, 28, 3428, 1044, 16048, 24, + 16296, 16376, 3400, 1108, 16036, 24, + 16304, 16340, 3372, 1168, 16024, 28, + 16316, 16304, 3340, 1232, 16016, 32, + 16324, 16272, 3308, 1296, 16004, 32, + 16332, 16244, 3272, 1360, 15996, 36, + 16344, 16212, 3236, 1424, 15988, 36, + 16352, 16188, 3200, 1488, 15980, 40, + 16360, 16160, 3160, 1552, 15972, 40, + 16368, 16136, 3116, 1616, 15964, 40, + 16372, 16112, 3072, 1680, 15956, 44, + 16380, 16092, 3028, 1744, 15952, 44, + 0, 16072, 2980, 1808, 15948, 44, + 8, 16052, 2932, 1872, 15944, 48, + 12, 16036, 2880, 1936, 15940, 48, + 16, 16020, 2828, 2000, 15936, 48, + 20, 16008, 2776, 2064, 15936, 48, + 24, 15996, 2724, 2128, 15936, 48, + 28, 15984, 2668, 2192, 15936, 48, + 32, 15972, 2612, 2252, 15940, 44, + 36, 15964, 2552, 2316, 15940, 44, + 40, 15956, 2496, 2376, 15944, 44, + 40, 15952, 2436, 2436, 15952, 40 }; + +static const uint16_t filter_6tap_64p_150[198] = { + 16148, 920, 2724, 920, 16148, 0, + 16152, 880, 2724, 956, 16148, 0, + 16152, 844, 2720, 996, 16144, 0, + 16156, 804, 2716, 1032, 16144, 0, + 16156, 768, 2712, 1072, 16144, 0, + 16160, 732, 2708, 1112, 16144, 16380, + 16164, 696, 2700, 1152, 16144, 16380, + 16168, 660, 2692, 1192, 16148, 16380, + 16172, 628, 2684, 1232, 16148, 16380, + 16176, 592, 2672, 1272, 16152, 16376, + 16180, 560, 2660, 1312, 16152, 16376, + 16184, 524, 2648, 1348, 16156, 16376, + 16192, 492, 2632, 1388, 16160, 16372, + 16196, 460, 2616, 1428, 16164, 16372, + 16200, 432, 2600, 1468, 16168, 16368, + 16204, 400, 2584, 1508, 16176, 16364, + 16212, 368, 2564, 1548, 16180, 16364, + 16216, 340, 2544, 1588, 16188, 16360, + 16220, 312, 2524, 1628, 16196, 16356, + 16228, 284, 2504, 1668, 16204, 16356, + 16232, 256, 2480, 1704, 16212, 16352, + 16240, 232, 2456, 1744, 16224, 16348, + 16244, 204, 2432, 1780, 16232, 16344, + 16248, 180, 2408, 1820, 16244, 16340, + 16256, 156, 2380, 1856, 16256, 16336, + 16260, 132, 2352, 1896, 16268, 16332, + 16268, 108, 2324, 1932, 16280, 16328, + 16272, 88, 2296, 1968, 16292, 16324, + 16276, 64, 2268, 2004, 16308, 16320, + 16284, 44, 2236, 2036, 16324, 16312, + 16288, 24, 2204, 2072, 16340, 16308, + 16292, 8, 2172, 2108, 16356, 16304, + 16300, 16372, 2140, 2140, 16372, 16300 }; + +static const uint16_t filter_6tap_64p_183[198] = { + 16296, 1032, 2196, 1032, 16296, 0, + 16292, 1004, 2200, 1060, 16304, 16380, + 16288, 976, 2200, 1088, 16308, 16380, + 16284, 952, 2196, 1116, 16312, 16376, + 16284, 924, 2196, 1144, 16320, 16376, + 16280, 900, 2192, 1172, 16324, 16372, + 16276, 872, 2192, 1200, 16332, 16368, + 16276, 848, 2188, 1228, 16340, 16368, + 16272, 820, 2180, 1256, 16348, 16364, + 16272, 796, 2176, 1280, 16356, 16360, + 16268, 768, 2168, 1308, 16364, 16360, + 16268, 744, 2164, 1336, 16372, 16356, + 16268, 716, 2156, 1364, 16380, 16352, + 16264, 692, 2148, 1392, 4, 16352, + 16264, 668, 2136, 1420, 16, 16348, + 16264, 644, 2128, 1448, 28, 16344, + 16264, 620, 2116, 1472, 36, 16340, + 16264, 596, 2108, 1500, 48, 16340, + 16268, 572, 2096, 1524, 60, 16336, + 16268, 548, 2080, 1552, 72, 16332, + 16268, 524, 2068, 1576, 88, 16328, + 16268, 504, 2056, 1604, 100, 16324, + 16272, 480, 2040, 1628, 112, 16324, + 16272, 456, 2024, 1652, 128, 16320, + 16272, 436, 2008, 1680, 144, 16316, + 16276, 416, 1992, 1704, 156, 16312, + 16276, 392, 1976, 1724, 172, 16308, + 16280, 372, 1956, 1748, 188, 16308, + 16280, 352, 1940, 1772, 204, 16304, + 16284, 332, 1920, 1796, 224, 16300, + 16288, 312, 1900, 1816, 240, 16296, + 16288, 296, 1880, 1840, 256, 16296, + 16292, 276, 1860, 1860, 276, 16292 }; + +static const uint16_t filter_7tap_64p_upscale[231] = { + 176, 15760, 2488, 2488, 15760, 176, 0, + 172, 15772, 2404, 2572, 15752, 180, 16380, + 168, 15784, 2324, 2656, 15740, 184, 16380, + 164, 15800, 2240, 2736, 15732, 188, 16376, + 160, 15812, 2152, 2816, 15728, 192, 16376, + 152, 15828, 2068, 2896, 15724, 192, 16376, + 148, 15848, 1984, 2972, 15720, 196, 16372, + 140, 15864, 1896, 3048, 15720, 196, 16372, + 136, 15884, 1812, 3124, 15720, 196, 16368, + 128, 15900, 1724, 3196, 15720, 196, 16368, + 120, 15920, 1640, 3268, 15724, 196, 16368, + 116, 15940, 1552, 3336, 15732, 196, 16364, + 108, 15964, 1468, 3400, 15740, 196, 16364, + 104, 15984, 1384, 3464, 15748, 192, 16364, + 96, 16004, 1300, 3524, 15760, 188, 16364, + 88, 16028, 1216, 3584, 15776, 184, 16364, + 84, 16048, 1132, 3640, 15792, 180, 16360, + 76, 16072, 1048, 3692, 15812, 176, 16360, + 68, 16092, 968, 3744, 15832, 168, 16360, + 64, 16116, 888, 3788, 15856, 160, 16360, + 56, 16140, 812, 3832, 15884, 152, 16360, + 52, 16160, 732, 3876, 15912, 144, 16360, + 44, 16184, 656, 3912, 15944, 136, 16364, + 40, 16204, 584, 3944, 15976, 124, 16364, + 32, 16228, 512, 3976, 16012, 116, 16364, + 28, 16248, 440, 4004, 16048, 104, 16364, + 24, 16268, 372, 4028, 16092, 88, 16368, + 20, 16288, 304, 4048, 16132, 76, 16368, + 12, 16308, 240, 4064, 16180, 60, 16372, + 8, 16328, 176, 4076, 16228, 48, 16372, + 4, 16348, 112, 4088, 16276, 32, 16376, + 0, 16364, 56, 4092, 16328, 16, 16380, + 0, 0, 0, 4096, 0, 0, 0 }; + +static const uint16_t filter_7tap_64p_117[231] = { + 92, 15868, 2464, 2464, 15868, 92, 0, + 96, 15864, 2404, 2528, 15876, 88, 0, + 100, 15860, 2344, 2584, 15884, 84, 0, + 104, 15856, 2280, 2644, 15892, 76, 0, + 108, 15852, 2216, 2700, 15904, 72, 0, + 108, 15852, 2152, 2756, 15916, 64, 0, + 112, 15852, 2088, 2812, 15932, 60, 0, + 112, 15852, 2024, 2864, 15948, 52, 0, + 112, 15856, 1960, 2916, 15964, 44, 0, + 116, 15860, 1892, 2964, 15984, 36, 0, + 116, 15864, 1828, 3016, 16004, 24, 4, + 116, 15868, 1760, 3060, 16024, 16, 4, + 116, 15876, 1696, 3108, 16048, 8, 8, + 116, 15884, 1628, 3152, 16072, 16380, 8, + 112, 15892, 1564, 3192, 16100, 16372, 8, + 112, 15900, 1496, 3232, 16124, 16360, 12, + 112, 15908, 1428, 3268, 16156, 16348, 12, + 108, 15920, 1364, 3304, 16188, 16336, 16, + 108, 15928, 1300, 3340, 16220, 16324, 20, + 104, 15940, 1232, 3372, 16252, 16312, 20, + 104, 15952, 1168, 3400, 16288, 16300, 24, + 100, 15964, 1104, 3428, 16328, 16284, 28, + 96, 15980, 1040, 3452, 16364, 16272, 28, + 96, 15992, 976, 3476, 20, 16256, 32, + 92, 16004, 916, 3496, 64, 16244, 36, + 88, 16020, 856, 3516, 108, 16228, 40, + 84, 16032, 792, 3532, 152, 16216, 44, + 80, 16048, 732, 3544, 200, 16200, 48, + 80, 16064, 676, 3556, 248, 16184, 48, + 76, 16080, 616, 3564, 296, 16168, 52, + 72, 16092, 560, 3568, 344, 16156, 56, + 68, 16108, 504, 3572, 396, 16140, 60, + 64, 16124, 452, 3576, 452, 16124, 64 }; + +static const uint16_t filter_7tap_64p_150[231] = { + 16224, 16380, 2208, 2208, 16380, 16224, 0, + 16232, 16360, 2172, 2236, 16, 16216, 0, + 16236, 16340, 2140, 2268, 40, 16212, 0, + 16244, 16324, 2104, 2296, 60, 16204, 4, + 16252, 16304, 2072, 2324, 84, 16196, 4, + 16256, 16288, 2036, 2352, 108, 16192, 4, + 16264, 16268, 2000, 2380, 132, 16184, 8, + 16272, 16252, 1960, 2408, 160, 16176, 8, + 16276, 16240, 1924, 2432, 184, 16172, 8, + 16284, 16224, 1888, 2456, 212, 16164, 8, + 16288, 16212, 1848, 2480, 240, 16160, 12, + 16296, 16196, 1812, 2500, 268, 16152, 12, + 16300, 16184, 1772, 2524, 296, 16144, 12, + 16308, 16172, 1736, 2544, 324, 16140, 12, + 16312, 16164, 1696, 2564, 356, 16136, 12, + 16320, 16152, 1656, 2584, 388, 16128, 12, + 16324, 16144, 1616, 2600, 416, 16124, 12, + 16328, 16136, 1576, 2616, 448, 16116, 12, + 16332, 16128, 1536, 2632, 480, 16112, 12, + 16340, 16120, 1496, 2648, 516, 16108, 12, + 16344, 16112, 1456, 2660, 548, 16104, 12, + 16348, 16104, 1416, 2672, 580, 16100, 12, + 16352, 16100, 1376, 2684, 616, 16096, 12, + 16356, 16096, 1336, 2696, 652, 16092, 12, + 16360, 16092, 1296, 2704, 688, 16088, 12, + 16364, 16088, 1256, 2712, 720, 16084, 12, + 16368, 16084, 1220, 2720, 760, 16084, 8, + 16368, 16080, 1180, 2724, 796, 16080, 8, + 16372, 16080, 1140, 2732, 832, 16080, 8, + 16376, 16076, 1100, 2732, 868, 16076, 4, + 16380, 16076, 1060, 2736, 908, 16076, 4, + 16380, 16076, 1020, 2740, 944, 16076, 0, + 0, 16076, 984, 2740, 984, 16076, 0 }; + +static const uint16_t filter_7tap_64p_183[231] = { + 16216, 324, 1884, 1884, 324, 16216, 0, + 16220, 304, 1864, 1904, 344, 16216, 0, + 16224, 284, 1844, 1924, 364, 16216, 0, + 16224, 264, 1824, 1944, 384, 16212, 16380, + 16228, 248, 1804, 1960, 408, 16212, 16380, + 16228, 228, 1784, 1976, 428, 16208, 16380, + 16232, 212, 1760, 1996, 452, 16208, 16380, + 16236, 192, 1740, 2012, 472, 16208, 16376, + 16240, 176, 1716, 2028, 496, 16208, 16376, + 16240, 160, 1696, 2040, 516, 16208, 16376, + 16244, 144, 1672, 2056, 540, 16208, 16376, + 16248, 128, 1648, 2068, 564, 16208, 16372, + 16252, 112, 1624, 2084, 588, 16208, 16372, + 16256, 96, 1600, 2096, 612, 16208, 16368, + 16256, 84, 1576, 2108, 636, 16208, 16368, + 16260, 68, 1552, 2120, 660, 16208, 16368, + 16264, 56, 1524, 2132, 684, 16212, 16364, + 16268, 40, 1500, 2140, 712, 16212, 16364, + 16272, 28, 1476, 2152, 736, 16216, 16360, + 16276, 16, 1448, 2160, 760, 16216, 16356, + 16280, 4, 1424, 2168, 788, 16220, 16356, + 16284, 16376, 1396, 2176, 812, 16224, 16352, + 16288, 16368, 1372, 2184, 840, 16224, 16352, + 16292, 16356, 1344, 2188, 864, 16228, 16348, + 16292, 16344, 1320, 2196, 892, 16232, 16344, + 16296, 16336, 1292, 2200, 916, 16236, 16344, + 16300, 16324, 1264, 2204, 944, 16240, 16340, + 16304, 16316, 1240, 2208, 972, 16248, 16336, + 16308, 16308, 1212, 2212, 996, 16252, 16332, + 16312, 16300, 1184, 2216, 1024, 16256, 16332, + 16316, 16292, 1160, 2216, 1052, 16264, 16328, + 16316, 16284, 1132, 2216, 1076, 16268, 16324, + 16320, 16276, 1104, 2216, 1104, 16276, 16320 }; + +static const uint16_t filter_8tap_64p_upscale[264] = { + 0, 0, 0, 4096, 0, 0, 0, 0, + 16376, 20, 16328, 4092, 56, 16364, 4, 0, + 16372, 36, 16272, 4088, 116, 16340, 12, 0, + 16364, 56, 16220, 4080, 180, 16320, 20, 0, + 16360, 76, 16172, 4064, 244, 16296, 24, 16380, + 16356, 92, 16124, 4048, 312, 16276, 32, 16380, + 16352, 108, 16080, 4032, 380, 16252, 40, 16380, + 16344, 124, 16036, 4008, 452, 16228, 48, 16380, + 16340, 136, 15996, 3980, 524, 16204, 56, 16380, + 16340, 152, 15956, 3952, 600, 16180, 64, 16376, + 16336, 164, 15920, 3920, 672, 16156, 76, 16376, + 16332, 176, 15888, 3884, 752, 16132, 84, 16376, + 16328, 188, 15860, 3844, 828, 16104, 92, 16372, + 16328, 200, 15828, 3800, 908, 16080, 100, 16372, + 16324, 208, 15804, 3756, 992, 16056, 108, 16372, + 16324, 216, 15780, 3708, 1072, 16032, 120, 16368, + 16320, 224, 15760, 3656, 1156, 16008, 128, 16368, + 16320, 232, 15740, 3604, 1240, 15984, 136, 16364, + 16320, 240, 15724, 3548, 1324, 15960, 144, 16364, + 16320, 244, 15708, 3488, 1412, 15936, 152, 16360, + 16320, 248, 15696, 3428, 1496, 15912, 160, 16360, + 16320, 252, 15688, 3364, 1584, 15892, 172, 16356, + 16320, 256, 15680, 3296, 1672, 15868, 180, 16352, + 16320, 256, 15672, 3228, 1756, 15848, 188, 16352, + 16320, 256, 15668, 3156, 1844, 15828, 192, 16348, + 16320, 260, 15668, 3084, 1932, 15808, 200, 16348, + 16320, 256, 15668, 3012, 2020, 15792, 208, 16344, + 16324, 256, 15668, 2936, 2108, 15772, 216, 16344, + 16324, 256, 15672, 2856, 2192, 15756, 220, 16340, + 16324, 252, 15676, 2776, 2280, 15740, 228, 16336, + 16328, 252, 15684, 2696, 2364, 15728, 232, 16336, + 16328, 248, 15692, 2616, 2448, 15716, 240, 16332, + 16332, 244, 15704, 2532, 2532, 15704, 244, 16332 }; + +static const uint16_t filter_8tap_64p_117[264] = { + 116, 16100, 428, 3564, 428, 16100, 116, 0, + 112, 16116, 376, 3564, 484, 16084, 120, 16380, + 104, 16136, 324, 3560, 540, 16064, 124, 16380, + 100, 16152, 272, 3556, 600, 16048, 128, 16380, + 96, 16168, 220, 3548, 656, 16032, 136, 16376, + 88, 16188, 172, 3540, 716, 16016, 140, 16376, + 84, 16204, 124, 3528, 780, 16000, 144, 16376, + 80, 16220, 76, 3512, 840, 15984, 148, 16372, + 76, 16236, 32, 3496, 904, 15968, 152, 16372, + 68, 16252, 16376, 3480, 968, 15952, 156, 16372, + 64, 16268, 16332, 3456, 1032, 15936, 160, 16372, + 60, 16284, 16292, 3432, 1096, 15920, 164, 16368, + 56, 16300, 16252, 3408, 1164, 15908, 164, 16368, + 48, 16316, 16216, 3380, 1228, 15892, 168, 16368, + 44, 16332, 16180, 3348, 1296, 15880, 168, 16368, + 40, 16348, 16148, 3316, 1364, 15868, 172, 16364, + 36, 16360, 16116, 3284, 1428, 15856, 172, 16364, + 32, 16376, 16084, 3248, 1496, 15848, 176, 16364, + 28, 4, 16052, 3208, 1564, 15836, 176, 16364, + 24, 16, 16028, 3168, 1632, 15828, 176, 16364, + 20, 28, 16000, 3124, 1700, 15820, 176, 16364, + 16, 40, 15976, 3080, 1768, 15812, 176, 16364, + 12, 52, 15952, 3036, 1836, 15808, 176, 16364, + 8, 64, 15932, 2988, 1904, 15800, 176, 16364, + 4, 76, 15912, 2940, 1972, 15800, 172, 16364, + 4, 84, 15892, 2888, 2040, 15796, 172, 16364, + 0, 96, 15876, 2836, 2104, 15792, 168, 16364, + 16380, 104, 15864, 2780, 2172, 15792, 164, 16364, + 16380, 112, 15848, 2724, 2236, 15792, 160, 16364, + 16376, 120, 15836, 2668, 2300, 15796, 156, 16368, + 16376, 128, 15828, 2608, 2364, 15800, 152, 16368, + 16372, 136, 15816, 2548, 2428, 15804, 148, 16368, + 16372, 140, 15812, 2488, 2488, 15812, 140, 16372 }; + +static const uint16_t filter_8tap_64p_150[264] = { + 16380, 16020, 1032, 2756, 1032, 16020, 16380, 0, + 0, 16020, 992, 2756, 1068, 16024, 16376, 0, + 4, 16020, 952, 2752, 1108, 16024, 16372, 0, + 8, 16020, 916, 2748, 1148, 16028, 16368, 0, + 12, 16020, 876, 2744, 1184, 16032, 16364, 4, + 16, 16020, 840, 2740, 1224, 16036, 16356, 4, + 20, 16024, 800, 2732, 1264, 16040, 16352, 4, + 20, 16024, 764, 2724, 1304, 16044, 16348, 8, + 24, 16028, 728, 2716, 1344, 16052, 16340, 8, + 28, 16028, 692, 2704, 1380, 16056, 16336, 12, + 28, 16032, 656, 2696, 1420, 16064, 16328, 12, + 32, 16036, 620, 2684, 1460, 16072, 16324, 12, + 36, 16040, 584, 2668, 1500, 16080, 16316, 16, + 36, 16044, 548, 2656, 1536, 16088, 16308, 16, + 36, 16048, 516, 2640, 1576, 16096, 16304, 20, + 40, 16052, 480, 2624, 1612, 16108, 16296, 20, + 40, 16060, 448, 2608, 1652, 16120, 16288, 20, + 44, 16064, 416, 2588, 1692, 16132, 16280, 24, + 44, 16068, 384, 2568, 1728, 16144, 16276, 24, + 44, 16076, 352, 2548, 1764, 16156, 16268, 28, + 44, 16080, 320, 2528, 1804, 16168, 16260, 28, + 44, 16088, 292, 2508, 1840, 16184, 16252, 28, + 44, 16096, 264, 2484, 1876, 16200, 16244, 32, + 48, 16100, 232, 2460, 1912, 16216, 16236, 32, + 48, 16108, 204, 2436, 1948, 16232, 16228, 32, + 48, 16116, 176, 2412, 1980, 16248, 16220, 36, + 48, 16124, 152, 2384, 2016, 16264, 16216, 36, + 44, 16128, 124, 2356, 2052, 16284, 16208, 36, + 44, 16136, 100, 2328, 2084, 16304, 16200, 40, + 44, 16144, 72, 2300, 2116, 16324, 16192, 40, + 44, 16152, 48, 2272, 2148, 16344, 16184, 40, + 44, 16160, 24, 2244, 2180, 16364, 16176, 40, + 44, 16168, 4, 2212, 2212, 4, 16168, 44 }; + +static const uint16_t filter_8tap_64p_183[264] = { + 16264, 16264, 1164, 2244, 1164, 16264, 16264, 0, + 16268, 16256, 1136, 2240, 1188, 16272, 16260, 0, + 16272, 16248, 1108, 2240, 1216, 16280, 16256, 0, + 16276, 16240, 1080, 2236, 1240, 16292, 16252, 0, + 16280, 16232, 1056, 2236, 1268, 16300, 16248, 0, + 16284, 16224, 1028, 2232, 1292, 16312, 16244, 0, + 16288, 16216, 1000, 2228, 1320, 16324, 16240, 0, + 16292, 16212, 976, 2224, 1344, 16336, 16236, 0, + 16296, 16204, 948, 2220, 1372, 16348, 16232, 0, + 16300, 16200, 920, 2212, 1396, 16360, 16228, 4, + 16304, 16196, 896, 2204, 1424, 16372, 16224, 4, + 16308, 16188, 868, 2200, 1448, 0, 16220, 4, + 16312, 16184, 844, 2192, 1472, 12, 16216, 4, + 16316, 16180, 816, 2184, 1500, 28, 16212, 4, + 16320, 16176, 792, 2172, 1524, 40, 16208, 4, + 16324, 16172, 764, 2164, 1548, 56, 16204, 0, + 16328, 16172, 740, 2156, 1572, 72, 16200, 0, + 16328, 16168, 712, 2144, 1596, 88, 16196, 0, + 16332, 16164, 688, 2132, 1620, 100, 16192, 0, + 16336, 16164, 664, 2120, 1644, 120, 16192, 0, + 16340, 16160, 640, 2108, 1668, 136, 16188, 0, + 16344, 16160, 616, 2096, 1688, 152, 16184, 0, + 16344, 16160, 592, 2080, 1712, 168, 16180, 0, + 16348, 16156, 568, 2068, 1736, 188, 16176, 16380, + 16352, 16156, 544, 2052, 1756, 204, 16176, 16380, + 16352, 16156, 520, 2036, 1780, 224, 16172, 16380, + 16356, 16156, 496, 2024, 1800, 244, 16172, 16380, + 16360, 16156, 472, 2008, 1820, 260, 16168, 16376, + 16360, 16156, 452, 1988, 1840, 280, 16164, 16376, + 16364, 16156, 428, 1972, 1860, 300, 16164, 16376, + 16364, 16156, 408, 1956, 1880, 320, 16164, 16372, + 16368, 16160, 384, 1936, 1900, 344, 16160, 16372, + 16368, 16160, 364, 1920, 1920, 364, 16160, 16368 }; + +const uint16_t *get_filter_3tap_16p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_3tap_16p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_3tap_16p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_3tap_16p_150; + else + return filter_3tap_16p_183; +} + +const uint16_t *get_filter_3tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_3tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_3tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_3tap_64p_150; + else + return filter_3tap_64p_183; +} + +const uint16_t *get_filter_4tap_16p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_4tap_16p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_4tap_16p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_4tap_16p_150; + else + return filter_4tap_16p_183; +} + +const uint16_t *get_filter_4tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_4tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_4tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_4tap_64p_150; + else + return filter_4tap_64p_183; +} + +const uint16_t *get_filter_5tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_5tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_5tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_5tap_64p_150; + else + return filter_5tap_64p_183; +} + +const uint16_t *get_filter_6tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_6tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_6tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_6tap_64p_150; + else + return filter_6tap_64p_183; +} + +const uint16_t *get_filter_7tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_7tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_7tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_7tap_64p_150; + else + return filter_7tap_64p_183; +} + +const uint16_t *get_filter_8tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_8tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_8tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_8tap_64p_150; + else + return filter_8tap_64p_183; +} + +const uint16_t *get_filter_2tap_16p(void) +{ + return filter_2tap_16p; +} + +const uint16_t *get_filter_2tap_64p(void) +{ + return filter_2tap_64p; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c new file mode 100644 index 000000000000..4fd49a16c3b6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c @@ -0,0 +1,1617 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dc_bios_types.h" +#include "dce_stream_encoder.h" +#include "reg_helper.h" + +enum DP_PIXEL_ENCODING { +DP_PIXEL_ENCODING_RGB444 = 0x00000000, +DP_PIXEL_ENCODING_YCBCR422 = 0x00000001, +DP_PIXEL_ENCODING_YCBCR444 = 0x00000002, +DP_PIXEL_ENCODING_RGB_WIDE_GAMUT = 0x00000003, +DP_PIXEL_ENCODING_Y_ONLY = 0x00000004, +DP_PIXEL_ENCODING_YCBCR420 = 0x00000005, +DP_PIXEL_ENCODING_RESERVED = 0x00000006, +}; + + +enum DP_COMPONENT_DEPTH { +DP_COMPONENT_DEPTH_6BPC = 0x00000000, +DP_COMPONENT_DEPTH_8BPC = 0x00000001, +DP_COMPONENT_DEPTH_10BPC = 0x00000002, +DP_COMPONENT_DEPTH_12BPC = 0x00000003, +DP_COMPONENT_DEPTH_16BPC = 0x00000004, +DP_COMPONENT_DEPTH_RESERVED = 0x00000005, +}; + + +#define REG(reg)\ + (enc110->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + enc110->se_shift->field_name, enc110->se_mask->field_name + +#define VBI_LINE_0 0 +#define DP_BLANK_MAX_RETRY 20 +#define HDMI_CLOCK_CHANNEL_RATE_MORE_340M 340000 + +#ifndef TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK + #define TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK 0x00000010L + #define TMDS_CNTL__TMDS_COLOR_FORMAT_MASK 0x00000300L + #define TMDS_CNTL__TMDS_PIXEL_ENCODING__SHIFT 0x00000004 + #define TMDS_CNTL__TMDS_COLOR_FORMAT__SHIFT 0x00000008 +#endif + +enum { + DP_MST_UPDATE_MAX_RETRY = 50 +}; + +#define DCE110_SE(audio)\ + container_of(audio, struct dce110_stream_encoder, base) + +#define CTX \ + enc110->base.ctx + +static void dce110_update_generic_info_packet( + struct dce110_stream_encoder *enc110, + uint32_t packet_index, + const struct encoder_info_packet *info_packet) +{ + uint32_t regval; + /* TODOFPGA Figure out a proper number for max_retries polling for lock + * use 50 for now. + */ + uint32_t max_retries = 50; + + if (REG(AFMT_VBI_PACKET_CONTROL1)) { + if (packet_index >= 8) + ASSERT(0); + + /* poll dig_update_lock is not locked -> asic internal signal + * assume otg master lock will unlock it + */ +/* REG_WAIT(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_LOCK_STATUS, + 0, 10, max_retries);*/ + + /* check if HW reading GSP memory */ + REG_WAIT(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT, + 0, 10, max_retries); + + /* HW does is not reading GSP memory not reading too long -> + * something wrong. clear GPS memory access and notify? + * hw SW is writing to GSP memory + */ + REG_UPDATE(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT_CLR, 1); + } + /* choose which generic packet to use */ + { + regval = REG_READ(AFMT_VBI_PACKET_CONTROL); + REG_UPDATE(AFMT_VBI_PACKET_CONTROL, + AFMT_GENERIC_INDEX, packet_index); + } + + /* write generic packet header + * (4th byte is for GENERIC0 only) */ + { + REG_SET_4(AFMT_GENERIC_HDR, 0, + AFMT_GENERIC_HB0, info_packet->hb0, + AFMT_GENERIC_HB1, info_packet->hb1, + AFMT_GENERIC_HB2, info_packet->hb2, + AFMT_GENERIC_HB3, info_packet->hb3); + } + + /* write generic packet contents + * (we never use last 4 bytes) + * there are 8 (0-7) mmDIG0_AFMT_GENERIC0_x registers */ + { + const uint32_t *content = + (const uint32_t *) &info_packet->sb[0]; + + REG_WRITE(AFMT_GENERIC_0, *content++); + REG_WRITE(AFMT_GENERIC_1, *content++); + REG_WRITE(AFMT_GENERIC_2, *content++); + REG_WRITE(AFMT_GENERIC_3, *content++); + REG_WRITE(AFMT_GENERIC_4, *content++); + REG_WRITE(AFMT_GENERIC_5, *content++); + REG_WRITE(AFMT_GENERIC_6, *content++); + REG_WRITE(AFMT_GENERIC_7, *content); + } + + if (!REG(AFMT_VBI_PACKET_CONTROL1)) { + /* force double-buffered packet update */ + REG_UPDATE_2(AFMT_VBI_PACKET_CONTROL, + AFMT_GENERIC0_UPDATE, (packet_index == 0), + AFMT_GENERIC2_UPDATE, (packet_index == 2)); + } +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (REG(AFMT_VBI_PACKET_CONTROL1)) { + switch (packet_index) { + case 0: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC0_FRAME_UPDATE, 1); + break; + case 1: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC1_FRAME_UPDATE, 1); + break; + case 2: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC2_FRAME_UPDATE, 1); + break; + case 3: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC3_FRAME_UPDATE, 1); + break; + case 4: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC4_FRAME_UPDATE, 1); + break; + case 5: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC5_FRAME_UPDATE, 1); + break; + case 6: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC6_FRAME_UPDATE, 1); + break; + case 7: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC7_FRAME_UPDATE, 1); + break; + default: + break; + } + } +#endif +} + +static void dce110_update_hdmi_info_packet( + struct dce110_stream_encoder *enc110, + uint32_t packet_index, + const struct encoder_info_packet *info_packet) +{ + struct dc_context *ctx = enc110->base.ctx; + uint32_t cont, send, line; + + if (info_packet->valid) { + dce110_update_generic_info_packet( + enc110, + packet_index, + info_packet); + + /* enable transmission of packet(s) - + * packet transmission begins on the next frame */ + cont = 1; + /* send packet(s) every frame */ + send = 1; + /* select line number to send packets on */ + line = 2; + } else { + cont = 0; + send = 0; + line = 0; + } + + /* choose which generic packet control to use */ + switch (packet_index) { + case 0: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 1: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; + case 2: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 3: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case 4: + if (REG(HDMI_GENERIC_PACKET_CONTROL2)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL2, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 5: + if (REG(HDMI_GENERIC_PACKET_CONTROL2)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL2, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; + case 6: + if (REG(HDMI_GENERIC_PACKET_CONTROL3)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL3, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 7: + if (REG(HDMI_GENERIC_PACKET_CONTROL3)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL3, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; +#endif + default: + /* invalid HW packet index */ + dm_logger_write( + ctx->logger, LOG_WARNING, + "Invalid HW packet index: %s()\n", + __func__); + return; + } +} + +/* setup stream encoder in dp mode */ +static void dce110_stream_encoder_dp_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + enum dc_color_space output_color_space) +{ +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + uint32_t h_active_start; + uint32_t v_active_start; + uint32_t misc0 = 0; + uint32_t misc1 = 0; + uint32_t h_blank; + uint32_t h_back_porch; + uint8_t synchronous_clock = 0; /* asynchronous mode */ + uint8_t colorimetry_bpc; +#endif + + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (REG(DP_DB_CNTL)) + REG_UPDATE(DP_DB_CNTL, DP_DB_DISABLE, 1); +#endif + + /* set pixel encoding */ + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_YCBCR422); + break; + case PIXEL_ENCODING_YCBCR444: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_YCBCR444); + + if (crtc_timing->flags.Y_ONLY) + if (crtc_timing->display_color_depth != COLOR_DEPTH_666) + /* HW testing only, no use case yet. + * Color depth of Y-only could be + * 8, 10, 12, 16 bits */ + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_Y_ONLY); + /* Note: DP_MSA_MISC1 bit 7 is the indicator + * of Y-only mode. + * This bit is set in HW if register + * DP_PIXEL_ENCODING is programmed to 0x4 */ + break; + case PIXEL_ENCODING_YCBCR420: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_YCBCR420); + if (enc110->se_mask->DP_VID_M_DOUBLE_VALUE_EN) + REG_UPDATE(DP_VID_TIMING, DP_VID_M_DOUBLE_VALUE_EN, 1); + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (enc110->se_mask->DP_VID_N_MUL) + REG_UPDATE(DP_VID_TIMING, DP_VID_N_MUL, 1); +#endif + break; + default: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_RGB444); + break; + } + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (REG(DP_MSA_MISC)) + misc1 = REG_READ(DP_MSA_MISC); +#endif + + /* set color depth */ + + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_666: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + 0); + break; + case COLOR_DEPTH_888: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_8BPC); + break; + case COLOR_DEPTH_101010: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_10BPC); + + break; + case COLOR_DEPTH_121212: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_12BPC); + break; + default: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_6BPC); + break; + } + + /* set dynamic range and YCbCr range */ + if (enc110->se_mask->DP_DYN_RANGE && enc110->se_mask->DP_YCBCR_RANGE) + REG_UPDATE_2( + DP_PIXEL_FORMAT, + DP_DYN_RANGE, 0, + DP_YCBCR_RANGE, 0); + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_666: + colorimetry_bpc = 0; + break; + case COLOR_DEPTH_888: + colorimetry_bpc = 1; + break; + case COLOR_DEPTH_101010: + colorimetry_bpc = 2; + break; + case COLOR_DEPTH_121212: + colorimetry_bpc = 3; + break; + default: + colorimetry_bpc = 0; + break; + } + + misc0 = misc0 | synchronous_clock; + misc0 = colorimetry_bpc << 5; + + if (REG(DP_MSA_TIMING_PARAM1)) { + switch (output_color_space) { + case COLOR_SPACE_SRGB: + misc0 = misc0 | 0x0; + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + break; + case COLOR_SPACE_SRGB_LIMITED: + misc0 = misc0 | 0x8; /* bit3=1 */ + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + break; + case COLOR_SPACE_YCBCR601: + misc0 = misc0 | 0x8; /* bit3=1, bit4=0 */ + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) + misc0 = misc0 | 0x2; /* bit2=0, bit1=1 */ + else if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) + misc0 = misc0 | 0x4; /* bit2=1, bit1=0 */ + break; + case COLOR_SPACE_YCBCR709: + misc0 = misc0 | 0x18; /* bit3=1, bit4=1 */ + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) + misc0 = misc0 | 0x2; /* bit2=0, bit1=1 */ + else if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) + misc0 = misc0 | 0x4; /* bit2=1, bit1=0 */ + break; + case COLOR_SPACE_2020_RGB_FULLRANGE: + case COLOR_SPACE_2020_RGB_LIMITEDRANGE: + case COLOR_SPACE_2020_YCBCR: + case COLOR_SPACE_ADOBERGB: + case COLOR_SPACE_UNKNOWN: + case COLOR_SPACE_YCBCR601_LIMITED: + case COLOR_SPACE_YCBCR709_LIMITED: + /* do nothing */ + break; + } + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (REG(DP_MSA_COLORIMETRY)) + REG_SET(DP_MSA_COLORIMETRY, 0, DP_MSA_MISC0, misc0); + + if (REG(DP_MSA_MISC)) + REG_WRITE(DP_MSA_MISC, misc1); /* MSA_MISC1 */ + + /* dcn new register + * dc_crtc_timing is vesa dmt struct. data from edid + */ + if (REG(DP_MSA_TIMING_PARAM1)) + REG_SET_2(DP_MSA_TIMING_PARAM1, 0, + DP_MSA_HTOTAL, crtc_timing->h_total, + DP_MSA_VTOTAL, crtc_timing->v_total); +#endif + + /* calcuate from vesa timing parameters + * h_active_start related to leading edge of sync + */ + + h_blank = crtc_timing->h_total - crtc_timing->h_border_left - + crtc_timing->h_addressable - crtc_timing->h_border_right; + + h_back_porch = h_blank - crtc_timing->h_front_porch - + crtc_timing->h_sync_width; + + /* start at begining of left border */ + h_active_start = crtc_timing->h_sync_width + h_back_porch; + + + v_active_start = crtc_timing->v_total - crtc_timing->v_border_top - + crtc_timing->v_addressable - crtc_timing->v_border_bottom - + crtc_timing->v_front_porch; + + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + /* start at begining of left border */ + if (REG(DP_MSA_TIMING_PARAM2)) + REG_SET_2(DP_MSA_TIMING_PARAM2, 0, + DP_MSA_HSTART, h_active_start, + DP_MSA_VSTART, v_active_start); + + if (REG(DP_MSA_TIMING_PARAM3)) + REG_SET_4(DP_MSA_TIMING_PARAM3, 0, + DP_MSA_HSYNCWIDTH, + crtc_timing->h_sync_width, + DP_MSA_HSYNCPOLARITY, + !crtc_timing->flags.HSYNC_POSITIVE_POLARITY, + DP_MSA_VSYNCWIDTH, + crtc_timing->v_sync_width, + DP_MSA_VSYNCPOLARITY, + !crtc_timing->flags.VSYNC_POSITIVE_POLARITY); + + /* HWDITH include border or overscan */ + if (REG(DP_MSA_TIMING_PARAM4)) + REG_SET_2(DP_MSA_TIMING_PARAM4, 0, + DP_MSA_HWIDTH, crtc_timing->h_border_left + + crtc_timing->h_addressable + crtc_timing->h_border_right, + DP_MSA_VHEIGHT, crtc_timing->v_border_top + + crtc_timing->v_addressable + crtc_timing->v_border_bottom); +#endif + } +#endif +} + +static void dce110_stream_encoder_set_stream_attribute_helper( + struct dce110_stream_encoder *enc110, + struct dc_crtc_timing *crtc_timing) +{ + if (enc110->regs->TMDS_CNTL) { + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(TMDS_CNTL, TMDS_PIXEL_ENCODING, 1); + break; + default: + REG_UPDATE(TMDS_CNTL, TMDS_PIXEL_ENCODING, 0); + break; + } + REG_UPDATE(TMDS_CNTL, TMDS_COLOR_FORMAT, 0); + } else if (enc110->regs->DIG_FE_CNTL) { + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, 1); + break; + default: + REG_UPDATE(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, 0); + break; + } + REG_UPDATE(DIG_FE_CNTL, TMDS_COLOR_FORMAT, 0); + } + +} + +/* setup stream encoder in hdmi mode */ +static void dce110_stream_encoder_hdmi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + int actual_pix_clk_khz, + bool enable_audio) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc110->base.id; + cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A; + cntl.enable_dp_audio = enable_audio; + cntl.pixel_clock = actual_pix_clk_khz; + cntl.lanes_number = LANE_COUNT_FOUR; + + if (enc110->base.bp->funcs->encoder_control( + enc110->base.bp, &cntl) != BP_RESULT_OK) + return; + + dce110_stream_encoder_set_stream_attribute_helper(enc110, crtc_timing); + + /* setup HDMI engine */ + if (!enc110->se_mask->HDMI_DATA_SCRAMBLE_EN) { + REG_UPDATE_3(HDMI_CONTROL, + HDMI_PACKET_GEN_VERSION, 1, + HDMI_KEEPOUT_MODE, 1, + HDMI_DEEP_COLOR_ENABLE, 0); + } else if (enc110->regs->DIG_FE_CNTL) { + REG_UPDATE_5(HDMI_CONTROL, + HDMI_PACKET_GEN_VERSION, 1, + HDMI_KEEPOUT_MODE, 1, + HDMI_DEEP_COLOR_ENABLE, 0, + HDMI_DATA_SCRAMBLE_EN, 0, + HDMI_CLOCK_CHANNEL_RATE, 0); + } + + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_888: + REG_UPDATE(HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, 0); + break; + case COLOR_DEPTH_101010: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + case COLOR_DEPTH_121212: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + case COLOR_DEPTH_161616: + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 3, + HDMI_DEEP_COLOR_ENABLE, 1); + break; + default: + break; + } + + if (enc110->se_mask->HDMI_DATA_SCRAMBLE_EN) { + if (actual_pix_clk_khz >= HDMI_CLOCK_CHANNEL_RATE_MORE_340M) { + /* enable HDMI data scrambler + * HDMI_CLOCK_CHANNEL_RATE_MORE_340M + * Clock channel frequency is 1/4 of character rate. + */ + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DATA_SCRAMBLE_EN, 1, + HDMI_CLOCK_CHANNEL_RATE, 1); + } else if (crtc_timing->flags.LTE_340MCSC_SCRAMBLE) { + + /* TODO: New feature for DCE11, still need to implement */ + + /* enable HDMI data scrambler + * HDMI_CLOCK_CHANNEL_FREQ_EQUAL_TO_CHAR_RATE + * Clock channel frequency is the same + * as character rate + */ + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DATA_SCRAMBLE_EN, 1, + HDMI_CLOCK_CHANNEL_RATE, 0); + } + } + + REG_UPDATE_3(HDMI_VBI_PACKET_CONTROL, + HDMI_GC_CONT, 1, + HDMI_GC_SEND, 1, + HDMI_NULL_SEND, 1); + + /* following belongs to audio */ + REG_UPDATE(HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, 1); + + REG_UPDATE(AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, 1); + + REG_UPDATE(HDMI_INFOFRAME_CONTROL1, HDMI_AUDIO_INFO_LINE, + VBI_LINE_0 + 2); + + REG_UPDATE(HDMI_GC, HDMI_GC_AVMUTE, 0); + +} + +/* setup stream encoder in dvi mode */ +static void dce110_stream_encoder_dvi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool is_dual_link) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc110->base.id; + cntl.signal = is_dual_link ? + SIGNAL_TYPE_DVI_DUAL_LINK : SIGNAL_TYPE_DVI_SINGLE_LINK; + cntl.enable_dp_audio = false; + cntl.pixel_clock = crtc_timing->pix_clk_khz; + cntl.lanes_number = (is_dual_link) ? LANE_COUNT_EIGHT : LANE_COUNT_FOUR; + + if (enc110->base.bp->funcs->encoder_control( + enc110->base.bp, &cntl) != BP_RESULT_OK) + return; + + ASSERT(crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB); + ASSERT(crtc_timing->display_color_depth == COLOR_DEPTH_888); + dce110_stream_encoder_set_stream_attribute_helper(enc110, crtc_timing); +} + +static void dce110_stream_encoder_set_mst_bandwidth( + struct stream_encoder *enc, + struct fixed31_32 avg_time_slots_per_mtp) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t x = dal_fixed31_32_floor( + avg_time_slots_per_mtp); + uint32_t y = dal_fixed31_32_ceil( + dal_fixed31_32_shl( + dal_fixed31_32_sub_int( + avg_time_slots_per_mtp, + x), + 26)); + + { + REG_SET_2(DP_MSE_RATE_CNTL, 0, + DP_MSE_RATE_X, x, + DP_MSE_RATE_Y, y); + } + + /* wait for update to be completed on the link */ + /* i.e. DP_MSE_RATE_UPDATE_PENDING field (read only) */ + /* is reset to 0 (not pending) */ + REG_WAIT(DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, + 0, + 10, DP_MST_UPDATE_MAX_RETRY); +} + +static void dce110_stream_encoder_update_hdmi_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + if (enc110->se_mask->HDMI_AVI_INFO_CONT && + enc110->se_mask->HDMI_AVI_INFO_SEND) { + + if (info_frame->avi.valid) { + const uint32_t *content = + (const uint32_t *) &info_frame->avi.sb[0]; + + REG_WRITE(AFMT_AVI_INFO0, content[0]); + + REG_WRITE(AFMT_AVI_INFO1, content[1]); + + REG_WRITE(AFMT_AVI_INFO2, content[2]); + + REG_WRITE(AFMT_AVI_INFO3, content[3]); + + REG_UPDATE(AFMT_AVI_INFO3, AFMT_AVI_INFO_VERSION, + info_frame->avi.hb1); + + REG_UPDATE_2(HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_SEND, 1, + HDMI_AVI_INFO_CONT, 1); + + REG_UPDATE(HDMI_INFOFRAME_CONTROL1, HDMI_AVI_INFO_LINE, + VBI_LINE_0 + 2); + + } else { + REG_UPDATE_2(HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_SEND, 0, + HDMI_AVI_INFO_CONT, 0); + } + } + + if (enc110->se_mask->HDMI_AVI_INFO_CONT && + enc110->se_mask->HDMI_AVI_INFO_SEND) { + dce110_update_hdmi_info_packet(enc110, 0, &info_frame->vendor); + dce110_update_hdmi_info_packet(enc110, 1, &info_frame->gamut); + dce110_update_hdmi_info_packet(enc110, 2, &info_frame->spd); + dce110_update_hdmi_info_packet(enc110, 3, &info_frame->hdrsmd); + } + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (enc110->se_mask->HDMI_DB_DISABLE) { + /* for bring up, disable dp double TODO */ + if (REG(HDMI_DB_CONTROL)) + REG_UPDATE(HDMI_DB_CONTROL, HDMI_DB_DISABLE, 1); + + dce110_update_hdmi_info_packet(enc110, 0, &info_frame->avi); + dce110_update_hdmi_info_packet(enc110, 1, &info_frame->vendor); + dce110_update_hdmi_info_packet(enc110, 2, &info_frame->gamut); + dce110_update_hdmi_info_packet(enc110, 3, &info_frame->spd); + dce110_update_hdmi_info_packet(enc110, 4, &info_frame->hdrsmd); + } +#endif +} + +static void dce110_stream_encoder_stop_hdmi_info_packets( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + /* stop generic packets 0 & 1 on HDMI */ + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_LINE, 0, + HDMI_GENERIC1_SEND, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC0_SEND, 0); + + /* stop generic packets 2 & 3 on HDMI */ + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC0_SEND, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_LINE, 0, + HDMI_GENERIC1_SEND, 0); + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + /* stop generic packets 2 & 3 on HDMI */ + if (REG(HDMI_GENERIC_PACKET_CONTROL2)) + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL2, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC0_SEND, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_LINE, 0, + HDMI_GENERIC1_SEND, 0); + + if (REG(HDMI_GENERIC_PACKET_CONTROL3)) + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL3, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC0_SEND, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_LINE, 0, + HDMI_GENERIC1_SEND, 0); +#endif +} + +static void dce110_stream_encoder_update_dp_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t value = REG_READ(DP_SEC_CNTL); + + if (info_frame->vsc.valid) + dce110_update_generic_info_packet( + enc110, + 0, /* packetIndex */ + &info_frame->vsc); + + if (info_frame->spd.valid) + dce110_update_generic_info_packet( + enc110, + 2, /* packetIndex */ + &info_frame->spd); + + if (info_frame->hdrsmd.valid) + dce110_update_generic_info_packet( + enc110, + 3, /* packetIndex */ + &info_frame->hdrsmd); + + /* enable/disable transmission of packet(s). + * If enabled, packet transmission begins on the next frame + */ + REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, info_frame->vsc.valid); + REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, info_frame->spd.valid); + REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, info_frame->hdrsmd.valid); + + /* This bit is the master enable bit. + * When enabling secondary stream engine, + * this master bit must also be set. + * This register shared with audio info frame. + * Therefore we need to enable master bit + * if at least on of the fields is not 0 + */ + if (value) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); +} + +static void dce110_stream_encoder_stop_dp_info_packets( + struct stream_encoder *enc) +{ + /* stop generic packets on DP */ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t value = REG_READ(DP_SEC_CNTL); + + if (enc110->se_mask->DP_SEC_AVI_ENABLE) { + REG_SET_7(DP_SEC_CNTL, 0, + DP_SEC_GSP0_ENABLE, 0, + DP_SEC_GSP1_ENABLE, 0, + DP_SEC_GSP2_ENABLE, 0, + DP_SEC_GSP3_ENABLE, 0, + DP_SEC_AVI_ENABLE, 0, + DP_SEC_MPG_ENABLE, 0, + DP_SEC_STREAM_ENABLE, 0); + } + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (enc110->se_mask->DP_SEC_GSP7_ENABLE) { + REG_SET_10(DP_SEC_CNTL, 0, + DP_SEC_GSP0_ENABLE, 0, + DP_SEC_GSP1_ENABLE, 0, + DP_SEC_GSP2_ENABLE, 0, + DP_SEC_GSP3_ENABLE, 0, + DP_SEC_GSP4_ENABLE, 0, + DP_SEC_GSP5_ENABLE, 0, + DP_SEC_GSP6_ENABLE, 0, + DP_SEC_GSP7_ENABLE, 0, + DP_SEC_MPG_ENABLE, 0, + DP_SEC_STREAM_ENABLE, 0); + } +#endif + /* this register shared with audio info frame. + * therefore we need to keep master enabled + * if at least one of the fields is not 0 */ + + if (value) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); + +} + +static void dce110_stream_encoder_dp_blank( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t retries = 0; + uint32_t max_retries = DP_BLANK_MAX_RETRY * 10; + + /* Note: For CZ, we are changing driver default to disable + * stream deferred to next VBLANK. If results are positive, we + * will make the same change to all DCE versions. There are a + * handful of panels that cannot handle disable stream at + * HBLANK and will result in a white line flash across the + * screen on stream disable. */ + + /* Specify the video stream disable point + * (2 = start of the next vertical blank) */ + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, 2); + /* Larger delay to wait until VBLANK - use max retry of + * 10us*3000=30ms. This covers 16.6ms of typical 60 Hz mode + + * a little more because we may not trust delay accuracy. + */ + max_retries = DP_BLANK_MAX_RETRY * 150; + + /* disable DP stream */ + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, 0); + + /* the encoder stops sending the video stream + * at the start of the vertical blanking. + * Poll for DP_VID_STREAM_STATUS == 0 + */ + + REG_WAIT(DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, + 0, + 10, max_retries); + + ASSERT(retries <= max_retries); + + /* Tell the DP encoder to ignore timing from CRTC, must be done after + * the polling. If we set DP_STEER_FIFO_RESET before DP stream blank is + * complete, stream status will be stuck in video stream enabled state, + * i.e. DP_VID_STREAM_STATUS stuck at 1. + */ + + REG_UPDATE(DP_STEER_FIFO, DP_STEER_FIFO_RESET, true); +} + +/* output video stream to link encoder */ +static void dce110_stream_encoder_dp_unblank( + struct stream_encoder *enc, + const struct encoder_unblank_param *param) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + if (param->link_settings.link_rate != LINK_RATE_UNKNOWN) { + uint32_t n_vid = 0x8000; + uint32_t m_vid; + + /* M / N = Fstream / Flink + * m_vid / n_vid = pixel rate / link rate + */ + + uint64_t m_vid_l = n_vid; + + m_vid_l *= param->pixel_clk_khz; + m_vid_l = div_u64(m_vid_l, + param->link_settings.link_rate + * LINK_RATE_REF_FREQ_IN_KHZ); + + m_vid = (uint32_t) m_vid_l; + + /* enable auto measurement */ + + REG_UPDATE(DP_VID_TIMING, DP_VID_M_N_GEN_EN, 0); + + /* auto measurement need 1 full 0x8000 symbol cycle to kick in, + * therefore program initial value for Mvid and Nvid + */ + + REG_UPDATE(DP_VID_N, DP_VID_N, n_vid); + + REG_UPDATE(DP_VID_M, DP_VID_M, m_vid); + + REG_UPDATE(DP_VID_TIMING, DP_VID_M_N_GEN_EN, 1); + } + + /* set DIG_START to 0x1 to resync FIFO */ + + REG_UPDATE(DIG_FE_CNTL, DIG_START, 1); + + /* switch DP encoder to CRTC data */ + + REG_UPDATE(DP_STEER_FIFO, DP_STEER_FIFO_RESET, 0); + + /* wait 100us for DIG/DP logic to prime + * (i.e. a few video lines) + */ + udelay(100); + + /* the hardware would start sending video at the start of the next DP + * frame (i.e. rising edge of the vblank). + * NOTE: We used to program DP_VID_STREAM_DIS_DEFER = 2 here, but this + * register has no effect on enable transition! HW always guarantees + * VID_STREAM enable at start of next frame, and this is not + * programmable + */ + + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, true); +} + +static void dce110_stream_encoder_set_avmute( + struct stream_encoder *enc, + bool enable) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + unsigned int value = enable ? 1 : 0; + + REG_UPDATE(HDMI_GC, HDMI_GC_AVMUTE, value); +} + + +#define DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT 0x8000 +#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC 1 + +#include "include/audio_types.h" + +/** +* speakersToChannels +* +* @brief +* translate speakers to channels +* +* FL - Front Left +* FR - Front Right +* RL - Rear Left +* RR - Rear Right +* RC - Rear Center +* FC - Front Center +* FLC - Front Left Center +* FRC - Front Right Center +* RLC - Rear Left Center +* RRC - Rear Right Center +* LFE - Low Freq Effect +* +* FC +* FLC FRC +* FL FR +* +* LFE +* () +* +* +* RL RR +* RLC RRC +* RC +* +* ch 8 7 6 5 4 3 2 1 +* 0b00000011 - - - - - - FR FL +* 0b00000111 - - - - - LFE FR FL +* 0b00001011 - - - - FC - FR FL +* 0b00001111 - - - - FC LFE FR FL +* 0b00010011 - - - RC - - FR FL +* 0b00010111 - - - RC - LFE FR FL +* 0b00011011 - - - RC FC - FR FL +* 0b00011111 - - - RC FC LFE FR FL +* 0b00110011 - - RR RL - - FR FL +* 0b00110111 - - RR RL - LFE FR FL +* 0b00111011 - - RR RL FC - FR FL +* 0b00111111 - - RR RL FC LFE FR FL +* 0b01110011 - RC RR RL - - FR FL +* 0b01110111 - RC RR RL - LFE FR FL +* 0b01111011 - RC RR RL FC - FR FL +* 0b01111111 - RC RR RL FC LFE FR FL +* 0b11110011 RRC RLC RR RL - - FR FL +* 0b11110111 RRC RLC RR RL - LFE FR FL +* 0b11111011 RRC RLC RR RL FC - FR FL +* 0b11111111 RRC RLC RR RL FC LFE FR FL +* 0b11000011 FRC FLC - - - - FR FL +* 0b11000111 FRC FLC - - - LFE FR FL +* 0b11001011 FRC FLC - - FC - FR FL +* 0b11001111 FRC FLC - - FC LFE FR FL +* 0b11010011 FRC FLC - RC - - FR FL +* 0b11010111 FRC FLC - RC - LFE FR FL +* 0b11011011 FRC FLC - RC FC - FR FL +* 0b11011111 FRC FLC - RC FC LFE FR FL +* 0b11110011 FRC FLC RR RL - - FR FL +* 0b11110111 FRC FLC RR RL - LFE FR FL +* 0b11111011 FRC FLC RR RL FC - FR FL +* 0b11111111 FRC FLC RR RL FC LFE FR FL +* +* @param +* speakers - speaker information as it comes from CEA audio block +*/ +/* translate speakers to channels */ + +union audio_cea_channels { + uint8_t all; + struct audio_cea_channels_bits { + uint32_t FL:1; + uint32_t FR:1; + uint32_t LFE:1; + uint32_t FC:1; + uint32_t RL_RC:1; + uint32_t RR:1; + uint32_t RC_RLC_FLC:1; + uint32_t RRC_FRC:1; + } channels; +}; + +struct audio_clock_info { + /* pixel clock frequency*/ + uint32_t pixel_clock_in_10khz; + /* N - 32KHz audio */ + uint32_t n_32khz; + /* CTS - 32KHz audio*/ + uint32_t cts_32khz; + uint32_t n_44khz; + uint32_t cts_44khz; + uint32_t n_48khz; + uint32_t cts_48khz; +}; + +/* 25.2MHz/1.001*/ +/* 25.2MHz/1.001*/ +/* 25.2MHz*/ +/* 27MHz */ +/* 27MHz*1.001*/ +/* 27MHz*1.001*/ +/* 54MHz*/ +/* 54MHz*1.001*/ +/* 74.25MHz/1.001*/ +/* 74.25MHz*/ +/* 148.5MHz/1.001*/ +/* 148.5MHz*/ + +static const struct audio_clock_info audio_clock_info_table[16] = { + {2517, 4576, 28125, 7007, 31250, 6864, 28125}, + {2518, 4576, 28125, 7007, 31250, 6864, 28125}, + {2520, 4096, 25200, 6272, 28000, 6144, 25200}, + {2700, 4096, 27000, 6272, 30000, 6144, 27000}, + {2702, 4096, 27027, 6272, 30030, 6144, 27027}, + {2703, 4096, 27027, 6272, 30030, 6144, 27027}, + {5400, 4096, 54000, 6272, 60000, 6144, 54000}, + {5405, 4096, 54054, 6272, 60060, 6144, 54054}, + {7417, 11648, 210937, 17836, 234375, 11648, 140625}, + {7425, 4096, 74250, 6272, 82500, 6144, 74250}, + {14835, 11648, 421875, 8918, 234375, 5824, 140625}, + {14850, 4096, 148500, 6272, 165000, 6144, 148500}, + {29670, 5824, 421875, 4459, 234375, 5824, 281250}, + {29700, 3072, 222750, 4704, 247500, 5120, 247500}, + {59340, 5824, 843750, 8918, 937500, 5824, 562500}, + {59400, 3072, 445500, 9408, 990000, 6144, 594000} +}; + +static const struct audio_clock_info audio_clock_info_table_36bpc[14] = { + {2517, 9152, 84375, 7007, 48875, 9152, 56250}, + {2518, 9152, 84375, 7007, 48875, 9152, 56250}, + {2520, 4096, 37800, 6272, 42000, 6144, 37800}, + {2700, 4096, 40500, 6272, 45000, 6144, 40500}, + {2702, 8192, 81081, 6272, 45045, 8192, 54054}, + {2703, 8192, 81081, 6272, 45045, 8192, 54054}, + {5400, 4096, 81000, 6272, 90000, 6144, 81000}, + {5405, 4096, 81081, 6272, 90090, 6144, 81081}, + {7417, 11648, 316406, 17836, 351562, 11648, 210937}, + {7425, 4096, 111375, 6272, 123750, 6144, 111375}, + {14835, 11648, 632812, 17836, 703125, 11648, 421875}, + {14850, 4096, 222750, 6272, 247500, 6144, 222750}, + {29670, 5824, 632812, 8918, 703125, 5824, 421875}, + {29700, 4096, 445500, 4704, 371250, 5120, 371250} +}; + +static const struct audio_clock_info audio_clock_info_table_48bpc[14] = { + {2517, 4576, 56250, 7007, 62500, 6864, 56250}, + {2518, 4576, 56250, 7007, 62500, 6864, 56250}, + {2520, 4096, 50400, 6272, 56000, 6144, 50400}, + {2700, 4096, 54000, 6272, 60000, 6144, 54000}, + {2702, 4096, 54054, 6267, 60060, 8192, 54054}, + {2703, 4096, 54054, 6272, 60060, 8192, 54054}, + {5400, 4096, 108000, 6272, 120000, 6144, 108000}, + {5405, 4096, 108108, 6272, 120120, 6144, 108108}, + {7417, 11648, 421875, 17836, 468750, 11648, 281250}, + {7425, 4096, 148500, 6272, 165000, 6144, 148500}, + {14835, 11648, 843750, 8918, 468750, 11648, 281250}, + {14850, 4096, 297000, 6272, 330000, 6144, 297000}, + {29670, 5824, 843750, 4459, 468750, 5824, 562500}, + {29700, 3072, 445500, 4704, 495000, 5120, 495000} + + +}; + +static union audio_cea_channels speakers_to_channels( + struct audio_speaker_flags speaker_flags) +{ + union audio_cea_channels cea_channels = {0}; + + /* these are one to one */ + cea_channels.channels.FL = speaker_flags.FL_FR; + cea_channels.channels.FR = speaker_flags.FL_FR; + cea_channels.channels.LFE = speaker_flags.LFE; + cea_channels.channels.FC = speaker_flags.FC; + + /* if Rear Left and Right exist move RC speaker to channel 7 + * otherwise to channel 5 + */ + if (speaker_flags.RL_RR) { + cea_channels.channels.RL_RC = speaker_flags.RL_RR; + cea_channels.channels.RR = speaker_flags.RL_RR; + cea_channels.channels.RC_RLC_FLC = speaker_flags.RC; + } else { + cea_channels.channels.RL_RC = speaker_flags.RC; + } + + /* FRONT Left Right Center and REAR Left Right Center are exclusive */ + if (speaker_flags.FLC_FRC) { + cea_channels.channels.RC_RLC_FLC = speaker_flags.FLC_FRC; + cea_channels.channels.RRC_FRC = speaker_flags.FLC_FRC; + } else { + cea_channels.channels.RC_RLC_FLC = speaker_flags.RLC_RRC; + cea_channels.channels.RRC_FRC = speaker_flags.RLC_RRC; + } + + return cea_channels; +} + +static uint32_t calc_max_audio_packets_per_line( + const struct audio_crtc_info *crtc_info) +{ + uint32_t max_packets_per_line; + + max_packets_per_line = + crtc_info->h_total - crtc_info->h_active; + + if (crtc_info->pixel_repetition) + max_packets_per_line *= crtc_info->pixel_repetition; + + /* for other hdmi features */ + max_packets_per_line -= 58; + /* for Control Period */ + max_packets_per_line -= 16; + /* Number of Audio Packets per Line */ + max_packets_per_line /= 32; + + return max_packets_per_line; +} + +static void get_audio_clock_info( + enum dc_color_depth color_depth, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct audio_clock_info *audio_clock_info) +{ + const struct audio_clock_info *clock_info; + uint32_t index; + uint32_t crtc_pixel_clock_in_10khz = crtc_pixel_clock_in_khz / 10; + uint32_t audio_array_size; + + switch (color_depth) { + case COLOR_DEPTH_161616: + clock_info = audio_clock_info_table_48bpc; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table_48bpc); + break; + case COLOR_DEPTH_121212: + clock_info = audio_clock_info_table_36bpc; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table_36bpc); + break; + default: + clock_info = audio_clock_info_table; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table); + break; + } + + if (clock_info != NULL) { + /* search for exact pixel clock in table */ + for (index = 0; index < audio_array_size; index++) { + if (clock_info[index].pixel_clock_in_10khz > + crtc_pixel_clock_in_10khz) + break; /* not match */ + else if (clock_info[index].pixel_clock_in_10khz == + crtc_pixel_clock_in_10khz) { + /* match found */ + *audio_clock_info = clock_info[index]; + return; + } + } + } + + /* not found */ + if (actual_pixel_clock_in_khz == 0) + actual_pixel_clock_in_khz = crtc_pixel_clock_in_khz; + + /* See HDMI spec the table entry under + * pixel clock of "Other". */ + audio_clock_info->pixel_clock_in_10khz = + actual_pixel_clock_in_khz / 10; + audio_clock_info->cts_32khz = actual_pixel_clock_in_khz; + audio_clock_info->cts_44khz = actual_pixel_clock_in_khz; + audio_clock_info->cts_48khz = actual_pixel_clock_in_khz; + + audio_clock_info->n_32khz = 4096; + audio_clock_info->n_44khz = 6272; + audio_clock_info->n_48khz = 6144; +} + +static void dce110_se_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *audio_info) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + uint32_t speakers = 0; + uint32_t channels = 0; + + ASSERT(audio_info); + if (audio_info == NULL) + /* This should not happen.it does so we don't get BSOD*/ + return; + + speakers = audio_info->flags.info.ALLSPEAKERS; + channels = speakers_to_channels(audio_info->flags.speaker_flags).all; + + /* setup the audio stream source select (audio -> dig mapping) */ + REG_SET(AFMT_AUDIO_SRC_CONTROL, 0, AFMT_AUDIO_SRC_SELECT, az_inst); + + /* Channel allocation */ + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_CHANNEL_ENABLE, channels); +} + +static void dce110_se_setup_hdmi_audio( + struct stream_encoder *enc, + const struct audio_crtc_info *crtc_info) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + struct audio_clock_info audio_clock_info = {0}; + uint32_t max_packets_per_line; + + /* For now still do calculation, although this field is ignored when + above HDMI_PACKET_GEN_VERSION set to 1 */ + max_packets_per_line = calc_max_audio_packets_per_line(crtc_info); + + /* HDMI_AUDIO_PACKET_CONTROL */ + REG_UPDATE_2(HDMI_AUDIO_PACKET_CONTROL, + HDMI_AUDIO_PACKETS_PER_LINE, max_packets_per_line, + HDMI_AUDIO_DELAY_EN, 1); + + /* AFMT_AUDIO_PACKET_CONTROL */ + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, 1); + + /* AFMT_AUDIO_PACKET_CONTROL2 */ + REG_UPDATE_2(AFMT_AUDIO_PACKET_CONTROL2, + AFMT_AUDIO_LAYOUT_OVRD, 0, + AFMT_60958_OSF_OVRD, 0); + + /* HDMI_ACR_PACKET_CONTROL */ + REG_UPDATE_3(HDMI_ACR_PACKET_CONTROL, + HDMI_ACR_AUTO_SEND, 1, + HDMI_ACR_SOURCE, 0, + HDMI_ACR_AUDIO_PRIORITY, 0); + + /* Program audio clock sample/regeneration parameters */ + get_audio_clock_info(crtc_info->color_depth, + crtc_info->requested_pixel_clock, + crtc_info->calculated_pixel_clock, + &audio_clock_info); + dm_logger_write(enc->ctx->logger, LOG_HW_AUDIO, + "\n%s:Input::requested_pixel_clock = %d" \ + "calculated_pixel_clock = %d \n", __func__, \ + crtc_info->requested_pixel_clock, \ + crtc_info->calculated_pixel_clock); + + /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ + REG_UPDATE(HDMI_ACR_32_0, HDMI_ACR_CTS_32, audio_clock_info.cts_32khz); + + /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ + REG_UPDATE(HDMI_ACR_32_1, HDMI_ACR_N_32, audio_clock_info.n_32khz); + + /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ + REG_UPDATE(HDMI_ACR_44_0, HDMI_ACR_CTS_44, audio_clock_info.cts_44khz); + + /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ + REG_UPDATE(HDMI_ACR_44_1, HDMI_ACR_N_44, audio_clock_info.n_44khz); + + /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ + REG_UPDATE(HDMI_ACR_48_0, HDMI_ACR_CTS_48, audio_clock_info.cts_48khz); + + /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ + REG_UPDATE(HDMI_ACR_48_1, HDMI_ACR_N_48, audio_clock_info.n_48khz); + + /* Video driver cannot know in advance which sample rate will + be used by HD Audio driver + HDMI_ACR_PACKET_CONTROL__HDMI_ACR_N_MULTIPLE field is + programmed below in interruppt callback */ + + /* AFMT_60958_0__AFMT_60958_CS_CHANNEL_NUMBER_L_MASK & + AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ + REG_UPDATE_2(AFMT_60958_0, + AFMT_60958_CS_CHANNEL_NUMBER_L, 1, + AFMT_60958_CS_CLOCK_ACCURACY, 0); + + /* AFMT_60958_1 AFMT_60958_CS_CHALNNEL_NUMBER_R */ + REG_UPDATE(AFMT_60958_1, AFMT_60958_CS_CHANNEL_NUMBER_R, 2); + + /*AFMT_60958_2 now keep this settings until + * Programming guide comes out*/ + REG_UPDATE_6(AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_2, 3, + AFMT_60958_CS_CHANNEL_NUMBER_3, 4, + AFMT_60958_CS_CHANNEL_NUMBER_4, 5, + AFMT_60958_CS_CHANNEL_NUMBER_5, 6, + AFMT_60958_CS_CHANNEL_NUMBER_6, 7, + AFMT_60958_CS_CHANNEL_NUMBER_7, 8); +} + +static void dce110_se_setup_dp_audio( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + /* --- DP Audio packet configurations --- */ + + /* ATP Configuration */ + REG_SET(DP_SEC_AUD_N, 0, + DP_SEC_AUD_N, DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT); + + /* Async/auto-calc timestamp mode */ + REG_SET(DP_SEC_TIMESTAMP, 0, DP_SEC_TIMESTAMP_MODE, + DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC); + + /* --- The following are the registers + * copied from the SetupHDMI --- */ + + /* AFMT_AUDIO_PACKET_CONTROL */ + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, 1); + + /* AFMT_AUDIO_PACKET_CONTROL2 */ + /* Program the ATP and AIP next */ + REG_UPDATE_2(AFMT_AUDIO_PACKET_CONTROL2, + AFMT_AUDIO_LAYOUT_OVRD, 0, + AFMT_60958_OSF_OVRD, 0); + + /* AFMT_INFOFRAME_CONTROL0 */ + REG_UPDATE(AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, 1); + + /* AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ + REG_UPDATE(AFMT_60958_0, AFMT_60958_CS_CLOCK_ACCURACY, 0); +} + +static void dce110_se_enable_audio_clock( + struct stream_encoder *enc, + bool enable) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + if (REG(AFMT_CNTL) == 0) + return; /* DCE8/10 does not have this register */ + + REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, !!enable); + + /* wait for AFMT clock to turn on, + * expectation: this should complete in 1-2 reads + * + * REG_WAIT(AFMT_CNTL, AFMT_AUDIO_CLOCK_ON, !!enable, 1, 10); + * + * TODO: wait for clock_on does not work well. May need HW + * program sequence. But audio seems work normally even without wait + * for clock_on status change + */ +} + +static void dce110_se_enable_dp_audio( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + /* Enable Audio packets */ + REG_UPDATE(DP_SEC_CNTL, DP_SEC_ASP_ENABLE, 1); + + /* Program the ATP and AIP next */ + REG_UPDATE_2(DP_SEC_CNTL, + DP_SEC_ATP_ENABLE, 1, + DP_SEC_AIP_ENABLE, 1); + + /* Program STREAM_ENABLE after all the other enables. */ + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); +} + +static void dce110_se_disable_dp_audio( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t value = REG_READ(DP_SEC_CNTL); + + /* Disable Audio packets */ + REG_UPDATE_5(DP_SEC_CNTL, + DP_SEC_ASP_ENABLE, 0, + DP_SEC_ATP_ENABLE, 0, + DP_SEC_AIP_ENABLE, 0, + DP_SEC_ACM_ENABLE, 0, + DP_SEC_STREAM_ENABLE, 0); + + /* This register shared with encoder info frame. Therefore we need to + keep master enabled if at least on of the fields is not 0 */ + if (value != 0) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); + +} + +void dce110_se_audio_mute_control( + struct stream_encoder *enc, + bool mute) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND, !mute); +} + +void dce110_se_dp_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info) +{ + dce110_se_audio_setup(enc, az_inst, info); +} + +void dce110_se_dp_audio_enable( + struct stream_encoder *enc) +{ + dce110_se_enable_audio_clock(enc, true); + dce110_se_setup_dp_audio(enc); + dce110_se_enable_dp_audio(enc); +} + +void dce110_se_dp_audio_disable( + struct stream_encoder *enc) +{ + dce110_se_disable_dp_audio(enc); + dce110_se_enable_audio_clock(enc, false); +} + +void dce110_se_hdmi_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info) +{ + dce110_se_enable_audio_clock(enc, true); + dce110_se_setup_hdmi_audio(enc, audio_crtc_info); + dce110_se_audio_setup(enc, az_inst, info); +} + +void dce110_se_hdmi_audio_disable( + struct stream_encoder *enc) +{ + dce110_se_enable_audio_clock(enc, false); +} + + +static void setup_stereo_sync( + struct stream_encoder *enc, + int tg_inst, bool enable) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + REG_UPDATE(DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, tg_inst); + REG_UPDATE(DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, !enable); +} + + +static const struct stream_encoder_funcs dce110_str_enc_funcs = { + .dp_set_stream_attribute = + dce110_stream_encoder_dp_set_stream_attribute, + .hdmi_set_stream_attribute = + dce110_stream_encoder_hdmi_set_stream_attribute, + .dvi_set_stream_attribute = + dce110_stream_encoder_dvi_set_stream_attribute, + .set_mst_bandwidth = + dce110_stream_encoder_set_mst_bandwidth, + .update_hdmi_info_packets = + dce110_stream_encoder_update_hdmi_info_packets, + .stop_hdmi_info_packets = + dce110_stream_encoder_stop_hdmi_info_packets, + .update_dp_info_packets = + dce110_stream_encoder_update_dp_info_packets, + .stop_dp_info_packets = + dce110_stream_encoder_stop_dp_info_packets, + .dp_blank = + dce110_stream_encoder_dp_blank, + .dp_unblank = + dce110_stream_encoder_dp_unblank, + .audio_mute_control = dce110_se_audio_mute_control, + + .dp_audio_setup = dce110_se_dp_audio_setup, + .dp_audio_enable = dce110_se_dp_audio_enable, + .dp_audio_disable = dce110_se_dp_audio_disable, + + .hdmi_audio_setup = dce110_se_hdmi_audio_setup, + .hdmi_audio_disable = dce110_se_hdmi_audio_disable, + .setup_stereo_sync = setup_stereo_sync, + .set_avmute = dce110_stream_encoder_set_avmute, + +}; + +void dce110_stream_encoder_construct( + struct dce110_stream_encoder *enc110, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + const struct dce110_stream_enc_registers *regs, + const struct dce_stream_encoder_shift *se_shift, + const struct dce_stream_encoder_mask *se_mask) +{ + enc110->base.funcs = &dce110_str_enc_funcs; + enc110->base.ctx = ctx; + enc110->base.id = eng_id; + enc110->base.bp = bp; + enc110->regs = regs; + enc110->se_shift = se_shift; + enc110->se_mask = se_mask; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h new file mode 100644 index 000000000000..6c28229c76eb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h @@ -0,0 +1,733 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_STREAM_ENCODER_DCE110_H__ +#define __DC_STREAM_ENCODER_DCE110_H__ + +#include "stream_encoder.h" + +#define DCE110STRENC_FROM_STRENC(stream_encoder)\ + container_of(stream_encoder, struct dce110_stream_encoder, base) + +#ifndef TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK + #define TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK 0x00000010L + #define TMDS_CNTL__TMDS_COLOR_FORMAT_MASK 0x00000300L + #define TMDS_CNTL__TMDS_PIXEL_ENCODING__SHIFT 0x00000004 + #define TMDS_CNTL__TMDS_COLOR_FORMAT__SHIFT 0x00000008 +#endif + + +#define SE_COMMON_REG_LIST_DCE_BASE(id) \ + SE_COMMON_REG_LIST_BASE(id),\ + SRI(AFMT_AVI_INFO0, DIG, id), \ + SRI(AFMT_AVI_INFO1, DIG, id), \ + SRI(AFMT_AVI_INFO2, DIG, id), \ + SRI(AFMT_AVI_INFO3, DIG, id) + +#define SE_COMMON_REG_LIST_BASE(id) \ + SRI(AFMT_GENERIC_0, DIG, id), \ + SRI(AFMT_GENERIC_1, DIG, id), \ + SRI(AFMT_GENERIC_2, DIG, id), \ + SRI(AFMT_GENERIC_3, DIG, id), \ + SRI(AFMT_GENERIC_4, DIG, id), \ + SRI(AFMT_GENERIC_5, DIG, id), \ + SRI(AFMT_GENERIC_6, DIG, id), \ + SRI(AFMT_GENERIC_7, DIG, id), \ + SRI(AFMT_GENERIC_HDR, DIG, id), \ + SRI(AFMT_INFOFRAME_CONTROL0, DIG, id), \ + SRI(AFMT_VBI_PACKET_CONTROL, DIG, id), \ + SRI(AFMT_AUDIO_PACKET_CONTROL, DIG, id), \ + SRI(AFMT_AUDIO_PACKET_CONTROL2, DIG, id), \ + SRI(AFMT_AUDIO_SRC_CONTROL, DIG, id), \ + SRI(AFMT_60958_0, DIG, id), \ + SRI(AFMT_60958_1, DIG, id), \ + SRI(AFMT_60958_2, DIG, id), \ + SRI(DIG_FE_CNTL, DIG, id), \ + SRI(HDMI_CONTROL, DIG, id), \ + SRI(HDMI_GC, DIG, id), \ + SRI(HDMI_GENERIC_PACKET_CONTROL0, DIG, id), \ + SRI(HDMI_GENERIC_PACKET_CONTROL1, DIG, id), \ + SRI(HDMI_INFOFRAME_CONTROL0, DIG, id), \ + SRI(HDMI_INFOFRAME_CONTROL1, DIG, id), \ + SRI(HDMI_VBI_PACKET_CONTROL, DIG, id), \ + SRI(HDMI_AUDIO_PACKET_CONTROL, DIG, id),\ + SRI(HDMI_ACR_PACKET_CONTROL, DIG, id),\ + SRI(HDMI_ACR_32_0, DIG, id),\ + SRI(HDMI_ACR_32_1, DIG, id),\ + SRI(HDMI_ACR_44_0, DIG, id),\ + SRI(HDMI_ACR_44_1, DIG, id),\ + SRI(HDMI_ACR_48_0, DIG, id),\ + SRI(HDMI_ACR_48_1, DIG, id),\ + SRI(TMDS_CNTL, DIG, id), \ + SRI(DP_MSE_RATE_CNTL, DP, id), \ + SRI(DP_MSE_RATE_UPDATE, DP, id), \ + SRI(DP_PIXEL_FORMAT, DP, id), \ + SRI(DP_SEC_CNTL, DP, id), \ + SRI(DP_STEER_FIFO, DP, id), \ + SRI(DP_VID_M, DP, id), \ + SRI(DP_VID_N, DP, id), \ + SRI(DP_VID_STREAM_CNTL, DP, id), \ + SRI(DP_VID_TIMING, DP, id), \ + SRI(DP_SEC_AUD_N, DP, id), \ + SRI(DP_SEC_TIMESTAMP, DP, id) + +#define SE_COMMON_REG_LIST(id)\ + SE_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(AFMT_CNTL, DIG, id) + +#define SE_DCN_REG_LIST(id)\ + SE_COMMON_REG_LIST_BASE(id),\ + SRI(AFMT_CNTL, DIG, id),\ + SRI(AFMT_VBI_PACKET_CONTROL1, DIG, id),\ + SRI(HDMI_GENERIC_PACKET_CONTROL2, DIG, id), \ + SRI(HDMI_GENERIC_PACKET_CONTROL3, DIG, id), \ + SRI(DP_DB_CNTL, DP, id), \ + SRI(DP_MSA_MISC, DP, id), \ + SRI(DP_MSA_COLORIMETRY, DP, id), \ + SRI(DP_MSA_TIMING_PARAM1, DP, id), \ + SRI(DP_MSA_TIMING_PARAM2, DP, id), \ + SRI(DP_MSA_TIMING_PARAM3, DP, id), \ + SRI(DP_MSA_TIMING_PARAM4, DP, id), \ + SRI(HDMI_DB_CONTROL, DIG, id) + +#define SE_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define SE_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + SE_SF(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_INDEX, mask_sh),\ + SE_SF(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC0_UPDATE, mask_sh),\ + SE_SF(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC2_UPDATE, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB0, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB1, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB2, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB3, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_LINE, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_LINE, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_DYN_RANGE, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_YCBCR_RANGE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_PACKET_GEN_VERSION, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_KEEPOUT_MODE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, mask_sh),\ + SE_SF(HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT, mask_sh),\ + SE_SF(HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND, mask_sh),\ + SE_SF(HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, mask_sh),\ + SE_SF(AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL1, HDMI_AUDIO_INFO_LINE, mask_sh),\ + SE_SF(HDMI_GC, HDMI_GC_AVMUTE, mask_sh),\ + SE_SF(DP_MSE_RATE_CNTL, DP_MSE_RATE_X, mask_sh),\ + SE_SF(DP_MSE_RATE_CNTL, DP_MSE_RATE_Y, mask_sh),\ + SE_SF(DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, mask_sh),\ + SE_SF(AFMT_AVI_INFO3, AFMT_AVI_INFO_VERSION, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_SEND, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_CONT, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL1, HDMI_AVI_INFO_LINE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP1_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_AVI_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_MPG_ENABLE, mask_sh),\ + SE_SF(DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, mask_sh),\ + SE_SF(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, mask_sh),\ + SE_SF(DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, mask_sh),\ + SE_SF(DP_STEER_FIFO, DP_STEER_FIFO_RESET, mask_sh),\ + SE_SF(DP_VID_TIMING, DP_VID_M_N_GEN_EN, mask_sh),\ + SE_SF(DP_VID_N, DP_VID_N, mask_sh),\ + SE_SF(DP_VID_M, DP_VID_M, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_START, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh),\ + SE_SF(AFMT_AUDIO_SRC_CONTROL, AFMT_AUDIO_SRC_SELECT, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_CHANNEL_ENABLE, mask_sh),\ + SE_SF(HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_PACKETS_PER_LINE, mask_sh),\ + SE_SF(HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_DELAY_EN, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_LAYOUT_OVRD, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL2, AFMT_60958_OSF_OVRD, mask_sh),\ + SE_SF(HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\ + SE_SF(HDMI_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\ + SE_SF(HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUDIO_PRIORITY, mask_sh),\ + SE_SF(HDMI_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\ + SE_SF(HDMI_ACR_32_1, HDMI_ACR_N_32, mask_sh),\ + SE_SF(HDMI_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\ + SE_SF(HDMI_ACR_44_1, HDMI_ACR_N_44, mask_sh),\ + SE_SF(HDMI_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\ + SE_SF(HDMI_ACR_48_1, HDMI_ACR_N_48, mask_sh),\ + SE_SF(AFMT_60958_0, AFMT_60958_CS_CHANNEL_NUMBER_L, mask_sh),\ + SE_SF(AFMT_60958_0, AFMT_60958_CS_CLOCK_ACCURACY, mask_sh),\ + SE_SF(AFMT_60958_1, AFMT_60958_CS_CHANNEL_NUMBER_R, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_2, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_3, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_4, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_5, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_6, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_7, mask_sh),\ + SE_SF(DP_SEC_AUD_N, DP_SEC_AUD_N, mask_sh),\ + SE_SF(DP_SEC_TIMESTAMP, DP_SEC_TIMESTAMP_MODE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_ASP_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_ATP_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_AIP_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_ACM_ENABLE, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) + +#define SE_COMMON_MASK_SH_LIST_SOC_BASE(mask_sh)\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_INDEX, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB0, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB1, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB2, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB3, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_LINE, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_PACKET_GEN_VERSION, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_KEEPOUT_MODE, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, mask_sh),\ + SE_SF(DIG0_AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL1, HDMI_AUDIO_INFO_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GC, HDMI_GC_AVMUTE, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_X, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_Y, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP1_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_MPG_ENABLE, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, mask_sh),\ + SE_SF(DP0_DP_STEER_FIFO, DP_STEER_FIFO_RESET, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_M_N_GEN_EN, mask_sh),\ + SE_SF(DP0_DP_VID_N, DP_VID_N, mask_sh),\ + SE_SF(DP0_DP_VID_M, DP_VID_M, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_START, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_SRC_CONTROL, AFMT_AUDIO_SRC_SELECT, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_CHANNEL_ENABLE, mask_sh),\ + SE_SF(DIG0_HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_PACKETS_PER_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_DELAY_EN, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_LAYOUT_OVRD, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL2, AFMT_60958_OSF_OVRD, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUDIO_PRIORITY, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_32_1, HDMI_ACR_N_32, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_44_1, HDMI_ACR_N_44, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_48_1, HDMI_ACR_N_48, mask_sh),\ + SE_SF(DIG0_AFMT_60958_0, AFMT_60958_CS_CHANNEL_NUMBER_L, mask_sh),\ + SE_SF(DIG0_AFMT_60958_0, AFMT_60958_CS_CLOCK_ACCURACY, mask_sh),\ + SE_SF(DIG0_AFMT_60958_1, AFMT_60958_CS_CHANNEL_NUMBER_R, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_2, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_3, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_4, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_5, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_6, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_7, mask_sh),\ + SE_SF(DP0_DP_SEC_AUD_N, DP_SEC_AUD_N, mask_sh),\ + SE_SF(DP0_DP_SEC_TIMESTAMP, DP_SEC_TIMESTAMP_MODE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ASP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ATP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_AIP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ACM_ENABLE, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND, mask_sh),\ + SE_SF(DIG0_AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, TMDS_COLOR_FORMAT, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_SOC(mask_sh)\ + SE_COMMON_MASK_SH_LIST_SOC_BASE(mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE80_100(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh),\ + SE_SF(TMDS_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(TMDS_CNTL, TMDS_COLOR_FORMAT, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE110(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh),\ + SE_SF(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_COLOR_FORMAT, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE112(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh),\ + SE_SF(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_COLOR_FORMAT, mask_sh),\ + SE_SF(DP_VID_TIMING, DP_VID_M_DOUBLE_VALUE_EN, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE120(mask_sh)\ + SE_COMMON_MASK_SH_LIST_SOC(mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC0_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC2_UPDATE, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_DYN_RANGE, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_YCBCR_RANGE, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL1, HDMI_AVI_INFO_LINE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_AVI_ENABLE, mask_sh),\ + SE_SF(DIG0_AFMT_AVI_INFO3, AFMT_AVI_INFO_VERSION, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_M_DOUBLE_VALUE_EN, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCN10(mask_sh)\ + SE_COMMON_MASK_SH_LIST_SOC(mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_LOCK_STATUS, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT_CLR, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC0_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC1_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC2_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC3_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC4_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC5_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC6_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC7_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC0_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC1_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC2_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC3_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC4_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC5_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC6_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC7_FRAME_UPDATE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP4_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP5_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP6_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP7_ENABLE, mask_sh),\ + SE_SF(DP0_DP_DB_CNTL, DP_DB_DISABLE, mask_sh),\ + SE_SF(DP0_DP_MSA_COLORIMETRY, DP_MSA_MISC0, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_HTOTAL, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_VTOTAL, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_HSTART, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_VSTART, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCPOLARITY, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCPOLARITY, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_HWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_VHEIGHT, mask_sh),\ + SE_SF(DIG0_HDMI_DB_CONTROL, HDMI_DB_DISABLE, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_N_MUL, mask_sh) + +struct dce_stream_encoder_shift { + uint8_t AFMT_GENERIC_INDEX; + uint8_t AFMT_GENERIC0_UPDATE; + uint8_t AFMT_GENERIC2_UPDATE; + uint8_t AFMT_GENERIC_HB0; + uint8_t AFMT_GENERIC_HB1; + uint8_t AFMT_GENERIC_HB2; + uint8_t AFMT_GENERIC_HB3; + uint8_t AFMT_GENERIC_LOCK_STATUS; + uint8_t AFMT_GENERIC_CONFLICT; + uint8_t AFMT_GENERIC_CONFLICT_CLR; + uint8_t AFMT_GENERIC0_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC1_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC2_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC3_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC4_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC5_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC6_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC7_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC0_FRAME_UPDATE; + uint8_t AFMT_GENERIC1_FRAME_UPDATE; + uint8_t AFMT_GENERIC2_FRAME_UPDATE; + uint8_t AFMT_GENERIC3_FRAME_UPDATE; + uint8_t AFMT_GENERIC4_FRAME_UPDATE; + uint8_t AFMT_GENERIC5_FRAME_UPDATE; + uint8_t AFMT_GENERIC6_FRAME_UPDATE; + uint8_t AFMT_GENERIC7_FRAME_UPDATE; + uint8_t HDMI_GENERIC0_CONT; + uint8_t HDMI_GENERIC0_SEND; + uint8_t HDMI_GENERIC0_LINE; + uint8_t HDMI_GENERIC1_CONT; + uint8_t HDMI_GENERIC1_SEND; + uint8_t HDMI_GENERIC1_LINE; + uint8_t DP_PIXEL_ENCODING; + uint8_t DP_COMPONENT_DEPTH; + uint8_t DP_DYN_RANGE; + uint8_t DP_YCBCR_RANGE; + uint8_t HDMI_PACKET_GEN_VERSION; + uint8_t HDMI_KEEPOUT_MODE; + uint8_t HDMI_DEEP_COLOR_ENABLE; + uint8_t HDMI_CLOCK_CHANNEL_RATE; + uint8_t HDMI_DEEP_COLOR_DEPTH; + uint8_t HDMI_GC_CONT; + uint8_t HDMI_GC_SEND; + uint8_t HDMI_NULL_SEND; + uint8_t HDMI_DATA_SCRAMBLE_EN; + uint8_t HDMI_AUDIO_INFO_SEND; + uint8_t AFMT_AUDIO_INFO_UPDATE; + uint8_t HDMI_AUDIO_INFO_LINE; + uint8_t HDMI_GC_AVMUTE; + uint8_t DP_MSE_RATE_X; + uint8_t DP_MSE_RATE_Y; + uint8_t DP_MSE_RATE_UPDATE_PENDING; + uint8_t AFMT_AVI_INFO_VERSION; + uint8_t HDMI_AVI_INFO_SEND; + uint8_t HDMI_AVI_INFO_CONT; + uint8_t HDMI_AVI_INFO_LINE; + uint8_t DP_SEC_GSP0_ENABLE; + uint8_t DP_SEC_STREAM_ENABLE; + uint8_t DP_SEC_GSP1_ENABLE; + uint8_t DP_SEC_GSP2_ENABLE; + uint8_t DP_SEC_GSP3_ENABLE; + uint8_t DP_SEC_GSP4_ENABLE; + uint8_t DP_SEC_GSP5_ENABLE; + uint8_t DP_SEC_GSP6_ENABLE; + uint8_t DP_SEC_GSP7_ENABLE; + uint8_t DP_SEC_AVI_ENABLE; + uint8_t DP_SEC_MPG_ENABLE; + uint8_t DP_VID_STREAM_DIS_DEFER; + uint8_t DP_VID_STREAM_ENABLE; + uint8_t DP_VID_STREAM_STATUS; + uint8_t DP_STEER_FIFO_RESET; + uint8_t DP_VID_M_N_GEN_EN; + uint8_t DP_VID_N; + uint8_t DP_VID_M; + uint8_t DIG_START; + uint8_t AFMT_AUDIO_SRC_SELECT; + uint8_t AFMT_AUDIO_CHANNEL_ENABLE; + uint8_t HDMI_AUDIO_PACKETS_PER_LINE; + uint8_t HDMI_AUDIO_DELAY_EN; + uint8_t AFMT_60958_CS_UPDATE; + uint8_t AFMT_AUDIO_LAYOUT_OVRD; + uint8_t AFMT_60958_OSF_OVRD; + uint8_t HDMI_ACR_AUTO_SEND; + uint8_t HDMI_ACR_SOURCE; + uint8_t HDMI_ACR_AUDIO_PRIORITY; + uint8_t HDMI_ACR_CTS_32; + uint8_t HDMI_ACR_N_32; + uint8_t HDMI_ACR_CTS_44; + uint8_t HDMI_ACR_N_44; + uint8_t HDMI_ACR_CTS_48; + uint8_t HDMI_ACR_N_48; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_L; + uint8_t AFMT_60958_CS_CLOCK_ACCURACY; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_R; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_2; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_3; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_4; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_5; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_6; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_7; + uint8_t DP_SEC_AUD_N; + uint8_t DP_SEC_TIMESTAMP_MODE; + uint8_t DP_SEC_ASP_ENABLE; + uint8_t DP_SEC_ATP_ENABLE; + uint8_t DP_SEC_AIP_ENABLE; + uint8_t DP_SEC_ACM_ENABLE; + uint8_t AFMT_AUDIO_SAMPLE_SEND; + uint8_t AFMT_AUDIO_CLOCK_EN; + uint8_t TMDS_PIXEL_ENCODING; + uint8_t TMDS_COLOR_FORMAT; + uint8_t DIG_STEREOSYNC_SELECT; + uint8_t DIG_STEREOSYNC_GATE_EN; + uint8_t DP_DB_DISABLE; + uint8_t DP_MSA_MISC0; + uint8_t DP_MSA_HTOTAL; + uint8_t DP_MSA_VTOTAL; + uint8_t DP_MSA_HSTART; + uint8_t DP_MSA_VSTART; + uint8_t DP_MSA_HSYNCWIDTH; + uint8_t DP_MSA_HSYNCPOLARITY; + uint8_t DP_MSA_VSYNCWIDTH; + uint8_t DP_MSA_VSYNCPOLARITY; + uint8_t DP_MSA_HWIDTH; + uint8_t DP_MSA_VHEIGHT; + uint8_t HDMI_DB_DISABLE; + uint8_t DP_VID_N_MUL; + uint8_t DP_VID_M_DOUBLE_VALUE_EN; +}; + +struct dce_stream_encoder_mask { + uint32_t AFMT_GENERIC_INDEX; + uint32_t AFMT_GENERIC0_UPDATE; + uint32_t AFMT_GENERIC2_UPDATE; + uint32_t AFMT_GENERIC_HB0; + uint32_t AFMT_GENERIC_HB1; + uint32_t AFMT_GENERIC_HB2; + uint32_t AFMT_GENERIC_HB3; + uint32_t AFMT_GENERIC_LOCK_STATUS; + uint32_t AFMT_GENERIC_CONFLICT; + uint32_t AFMT_GENERIC_CONFLICT_CLR; + uint32_t AFMT_GENERIC0_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC1_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC2_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC3_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC4_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC5_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC6_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC7_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC0_FRAME_UPDATE; + uint32_t AFMT_GENERIC1_FRAME_UPDATE; + uint32_t AFMT_GENERIC2_FRAME_UPDATE; + uint32_t AFMT_GENERIC3_FRAME_UPDATE; + uint32_t AFMT_GENERIC4_FRAME_UPDATE; + uint32_t AFMT_GENERIC5_FRAME_UPDATE; + uint32_t AFMT_GENERIC6_FRAME_UPDATE; + uint32_t AFMT_GENERIC7_FRAME_UPDATE; + uint32_t HDMI_GENERIC0_CONT; + uint32_t HDMI_GENERIC0_SEND; + uint32_t HDMI_GENERIC0_LINE; + uint32_t HDMI_GENERIC1_CONT; + uint32_t HDMI_GENERIC1_SEND; + uint32_t HDMI_GENERIC1_LINE; + uint32_t DP_PIXEL_ENCODING; + uint32_t DP_COMPONENT_DEPTH; + uint32_t DP_DYN_RANGE; + uint32_t DP_YCBCR_RANGE; + uint32_t HDMI_PACKET_GEN_VERSION; + uint32_t HDMI_KEEPOUT_MODE; + uint32_t HDMI_DEEP_COLOR_ENABLE; + uint32_t HDMI_CLOCK_CHANNEL_RATE; + uint32_t HDMI_DEEP_COLOR_DEPTH; + uint32_t HDMI_GC_CONT; + uint32_t HDMI_GC_SEND; + uint32_t HDMI_NULL_SEND; + uint32_t HDMI_DATA_SCRAMBLE_EN; + uint32_t HDMI_AUDIO_INFO_SEND; + uint32_t AFMT_AUDIO_INFO_UPDATE; + uint32_t HDMI_AUDIO_INFO_LINE; + uint32_t HDMI_GC_AVMUTE; + uint32_t DP_MSE_RATE_X; + uint32_t DP_MSE_RATE_Y; + uint32_t DP_MSE_RATE_UPDATE_PENDING; + uint32_t AFMT_AVI_INFO_VERSION; + uint32_t HDMI_AVI_INFO_SEND; + uint32_t HDMI_AVI_INFO_CONT; + uint32_t HDMI_AVI_INFO_LINE; + uint32_t DP_SEC_GSP0_ENABLE; + uint32_t DP_SEC_STREAM_ENABLE; + uint32_t DP_SEC_GSP1_ENABLE; + uint32_t DP_SEC_GSP2_ENABLE; + uint32_t DP_SEC_GSP3_ENABLE; + uint32_t DP_SEC_GSP4_ENABLE; + uint32_t DP_SEC_GSP5_ENABLE; + uint32_t DP_SEC_GSP6_ENABLE; + uint32_t DP_SEC_GSP7_ENABLE; + uint32_t DP_SEC_AVI_ENABLE; + uint32_t DP_SEC_MPG_ENABLE; + uint32_t DP_VID_STREAM_DIS_DEFER; + uint32_t DP_VID_STREAM_ENABLE; + uint32_t DP_VID_STREAM_STATUS; + uint32_t DP_STEER_FIFO_RESET; + uint32_t DP_VID_M_N_GEN_EN; + uint32_t DP_VID_N; + uint32_t DP_VID_M; + uint32_t DIG_START; + uint32_t AFMT_AUDIO_SRC_SELECT; + uint32_t AFMT_AUDIO_CHANNEL_ENABLE; + uint32_t HDMI_AUDIO_PACKETS_PER_LINE; + uint32_t HDMI_AUDIO_DELAY_EN; + uint32_t AFMT_60958_CS_UPDATE; + uint32_t AFMT_AUDIO_LAYOUT_OVRD; + uint32_t AFMT_60958_OSF_OVRD; + uint32_t HDMI_ACR_AUTO_SEND; + uint32_t HDMI_ACR_SOURCE; + uint32_t HDMI_ACR_AUDIO_PRIORITY; + uint32_t HDMI_ACR_CTS_32; + uint32_t HDMI_ACR_N_32; + uint32_t HDMI_ACR_CTS_44; + uint32_t HDMI_ACR_N_44; + uint32_t HDMI_ACR_CTS_48; + uint32_t HDMI_ACR_N_48; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_L; + uint32_t AFMT_60958_CS_CLOCK_ACCURACY; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_R; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_2; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_3; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_4; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_5; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_6; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_7; + uint32_t DP_SEC_AUD_N; + uint32_t DP_SEC_TIMESTAMP_MODE; + uint32_t DP_SEC_ASP_ENABLE; + uint32_t DP_SEC_ATP_ENABLE; + uint32_t DP_SEC_AIP_ENABLE; + uint32_t DP_SEC_ACM_ENABLE; + uint32_t AFMT_AUDIO_SAMPLE_SEND; + uint32_t AFMT_AUDIO_CLOCK_EN; + uint32_t TMDS_PIXEL_ENCODING; + uint32_t DIG_STEREOSYNC_SELECT; + uint32_t DIG_STEREOSYNC_GATE_EN; + uint32_t TMDS_COLOR_FORMAT; + uint32_t DP_DB_DISABLE; + uint32_t DP_MSA_MISC0; + uint32_t DP_MSA_HTOTAL; + uint32_t DP_MSA_VTOTAL; + uint32_t DP_MSA_HSTART; + uint32_t DP_MSA_VSTART; + uint32_t DP_MSA_HSYNCWIDTH; + uint32_t DP_MSA_HSYNCPOLARITY; + uint32_t DP_MSA_VSYNCWIDTH; + uint32_t DP_MSA_VSYNCPOLARITY; + uint32_t DP_MSA_HWIDTH; + uint32_t DP_MSA_VHEIGHT; + uint32_t HDMI_DB_DISABLE; + uint32_t DP_VID_N_MUL; + uint32_t DP_VID_M_DOUBLE_VALUE_EN; +}; + +struct dce110_stream_enc_registers { + uint32_t AFMT_CNTL; + uint32_t AFMT_AVI_INFO0; + uint32_t AFMT_AVI_INFO1; + uint32_t AFMT_AVI_INFO2; + uint32_t AFMT_AVI_INFO3; + uint32_t AFMT_GENERIC_0; + uint32_t AFMT_GENERIC_1; + uint32_t AFMT_GENERIC_2; + uint32_t AFMT_GENERIC_3; + uint32_t AFMT_GENERIC_4; + uint32_t AFMT_GENERIC_5; + uint32_t AFMT_GENERIC_6; + uint32_t AFMT_GENERIC_7; + uint32_t AFMT_GENERIC_HDR; + uint32_t AFMT_INFOFRAME_CONTROL0; + uint32_t AFMT_VBI_PACKET_CONTROL; + uint32_t AFMT_VBI_PACKET_CONTROL1; + uint32_t AFMT_AUDIO_PACKET_CONTROL; + uint32_t AFMT_AUDIO_PACKET_CONTROL2; + uint32_t AFMT_AUDIO_SRC_CONTROL; + uint32_t AFMT_60958_0; + uint32_t AFMT_60958_1; + uint32_t AFMT_60958_2; + uint32_t DIG_FE_CNTL; + uint32_t DP_MSE_RATE_CNTL; + uint32_t DP_MSE_RATE_UPDATE; + uint32_t DP_PIXEL_FORMAT; + uint32_t DP_SEC_CNTL; + uint32_t DP_STEER_FIFO; + uint32_t DP_VID_M; + uint32_t DP_VID_N; + uint32_t DP_VID_STREAM_CNTL; + uint32_t DP_VID_TIMING; + uint32_t DP_SEC_AUD_N; + uint32_t DP_SEC_TIMESTAMP; + uint32_t HDMI_CONTROL; + uint32_t HDMI_GC; + uint32_t HDMI_GENERIC_PACKET_CONTROL0; + uint32_t HDMI_GENERIC_PACKET_CONTROL1; + uint32_t HDMI_GENERIC_PACKET_CONTROL2; + uint32_t HDMI_GENERIC_PACKET_CONTROL3; + uint32_t HDMI_INFOFRAME_CONTROL0; + uint32_t HDMI_INFOFRAME_CONTROL1; + uint32_t HDMI_VBI_PACKET_CONTROL; + uint32_t HDMI_AUDIO_PACKET_CONTROL; + uint32_t HDMI_ACR_PACKET_CONTROL; + uint32_t HDMI_ACR_32_0; + uint32_t HDMI_ACR_32_1; + uint32_t HDMI_ACR_44_0; + uint32_t HDMI_ACR_44_1; + uint32_t HDMI_ACR_48_0; + uint32_t HDMI_ACR_48_1; + uint32_t TMDS_CNTL; + uint32_t DP_DB_CNTL; + uint32_t DP_MSA_MISC; + uint32_t DP_MSA_COLORIMETRY; + uint32_t DP_MSA_TIMING_PARAM1; + uint32_t DP_MSA_TIMING_PARAM2; + uint32_t DP_MSA_TIMING_PARAM3; + uint32_t DP_MSA_TIMING_PARAM4; + uint32_t HDMI_DB_CONTROL; +}; + +struct dce110_stream_encoder { + struct stream_encoder base; + const struct dce110_stream_enc_registers *regs; + const struct dce_stream_encoder_shift *se_shift; + const struct dce_stream_encoder_mask *se_mask; +}; + +void dce110_stream_encoder_construct( + struct dce110_stream_encoder *enc110, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + const struct dce110_stream_enc_registers *regs, + const struct dce_stream_encoder_shift *se_shift, + const struct dce_stream_encoder_mask *se_mask); + + +void dce110_se_audio_mute_control( + struct stream_encoder *enc, bool mute); + +void dce110_se_dp_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info); + +void dce110_se_dp_audio_enable( + struct stream_encoder *enc); + +void dce110_se_dp_audio_disable( + struct stream_encoder *enc); + +void dce110_se_hdmi_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info); + +void dce110_se_hdmi_audio_disable( + struct stream_encoder *enc); + +#endif /* __DC_STREAM_ENCODER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c new file mode 100644 index 000000000000..ae32af31eff1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -0,0 +1,1463 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce_transform.h" +#include "reg_helper.h" +#include "opp.h" +#include "basics/conversion.h" +#include "dc.h" + +#define REG(reg) \ + (xfm_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + xfm_dce->xfm_shift->field_name, xfm_dce->xfm_mask->field_name + +#define CTX \ + xfm_dce->base.ctx + +#define IDENTITY_RATIO(ratio) (dal_fixed31_32_u2d19(ratio) == (1 << 19)) +#define GAMUT_MATRIX_SIZE 12 +#define SCL_PHASES 16 + +enum dcp_out_trunc_round_mode { + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_MODE_ROUND +}; + +enum dcp_out_trunc_round_depth { + DCP_OUT_TRUNC_ROUND_DEPTH_14BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_13BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_11BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_9BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_8BIT +}; + +/* defines the various methods of bit reduction available for use */ +enum dcp_bit_depth_reduction_mode { + DCP_BIT_DEPTH_REDUCTION_MODE_DITHER, + DCP_BIT_DEPTH_REDUCTION_MODE_ROUND, + DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE, + DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED, + DCP_BIT_DEPTH_REDUCTION_MODE_INVALID +}; + +enum dcp_spatial_dither_mode { + DCP_SPATIAL_DITHER_MODE_AAAA, + DCP_SPATIAL_DITHER_MODE_A_AA_A, + DCP_SPATIAL_DITHER_MODE_AABBAABB, + DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC, + DCP_SPATIAL_DITHER_MODE_INVALID +}; + +enum dcp_spatial_dither_depth { + DCP_SPATIAL_DITHER_DEPTH_30BPP, + DCP_SPATIAL_DITHER_DEPTH_24BPP +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +enum grph_color_adjust_option { + GRPH_COLOR_MATRIX_HW_DEFAULT = 1, + GRPH_COLOR_MATRIX_SW +}; + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* TODO: correct values below */ +{ COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +static bool setup_scaling_configuration( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + REG_SET(SCL_BYPASS_CONTROL, 0, SCL_BYPASS_MODE, 0); + + if (data->taps.h_taps + data->taps.v_taps <= 2) { + /* Set bypass */ + if (xfm_dce->xfm_mask->SCL_PSCL_EN != 0) + REG_UPDATE_2(SCL_MODE, SCL_MODE, 0, SCL_PSCL_EN, 0); + else + REG_UPDATE(SCL_MODE, SCL_MODE, 0); + return false; + } + + REG_SET_2(SCL_TAP_CONTROL, 0, + SCL_H_NUM_OF_TAPS, data->taps.h_taps - 1, + SCL_V_NUM_OF_TAPS, data->taps.v_taps - 1); + + if (data->format <= PIXEL_FORMAT_GRPH_END) + REG_UPDATE(SCL_MODE, SCL_MODE, 1); + else + REG_UPDATE(SCL_MODE, SCL_MODE, 2); + + if (xfm_dce->xfm_mask->SCL_PSCL_EN != 0) + REG_UPDATE(SCL_MODE, SCL_PSCL_EN, 1); + + /* 1 - Replace out of bound pixels with edge */ + REG_SET(SCL_CONTROL, 0, SCL_BOUNDARY_MODE, 1); + + return true; +} + +static void program_overscan( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + int overscan_right = data->h_active + - data->recout.x - data->recout.width; + int overscan_bottom = data->v_active + - data->recout.y - data->recout.height; + + if (xfm_dce->base.ctx->dc->debug.surface_visual_confirm) { + overscan_bottom += 2; + overscan_right += 2; + } + + if (overscan_right < 0) { + BREAK_TO_DEBUGGER(); + overscan_right = 0; + } + if (overscan_bottom < 0) { + BREAK_TO_DEBUGGER(); + overscan_bottom = 0; + } + + REG_SET_2(EXT_OVERSCAN_LEFT_RIGHT, 0, + EXT_OVERSCAN_LEFT, data->recout.x, + EXT_OVERSCAN_RIGHT, overscan_right); + REG_SET_2(EXT_OVERSCAN_TOP_BOTTOM, 0, + EXT_OVERSCAN_TOP, data->recout.y, + EXT_OVERSCAN_BOTTOM, overscan_bottom); +} + +static void program_multi_taps_filter( + struct dce_transform *xfm_dce, + int taps, + const uint16_t *coeffs, + enum ram_filter_type filter_type) +{ + int phase, pair; + int array_idx = 0; + int taps_pairs = (taps + 1) / 2; + int phases_to_program = SCL_PHASES / 2 + 1; + + uint32_t power_ctl = 0; + + if (!coeffs) + return; + + /*We need to disable power gating on coeff memory to do programming*/ + if (REG(DCFE_MEM_PWR_CTRL)) { + power_ctl = REG_READ(DCFE_MEM_PWR_CTRL); + REG_SET(DCFE_MEM_PWR_CTRL, power_ctl, SCL_COEFF_MEM_PWR_DIS, 1); + + REG_WAIT(DCFE_MEM_PWR_STATUS, SCL_COEFF_MEM_PWR_STATE, 0, 1, 10); + } + for (phase = 0; phase < phases_to_program; phase++) { + /*we always program N/2 + 1 phases, total phases N, but N/2-1 are just mirror + phase 0 is unique and phase N/2 is unique if N is even*/ + for (pair = 0; pair < taps_pairs; pair++) { + uint16_t odd_coeff = 0; + uint16_t even_coeff = coeffs[array_idx]; + + REG_SET_3(SCL_COEF_RAM_SELECT, 0, + SCL_C_RAM_FILTER_TYPE, filter_type, + SCL_C_RAM_PHASE, phase, + SCL_C_RAM_TAP_PAIR_IDX, pair); + + if (taps % 2 && pair == taps_pairs - 1) + array_idx++; + else { + odd_coeff = coeffs[array_idx + 1]; + array_idx += 2; + } + + REG_SET_4(SCL_COEF_RAM_TAP_DATA, 0, + SCL_C_RAM_EVEN_TAP_COEF_EN, 1, + SCL_C_RAM_EVEN_TAP_COEF, even_coeff, + SCL_C_RAM_ODD_TAP_COEF_EN, 1, + SCL_C_RAM_ODD_TAP_COEF, odd_coeff); + } + } + + /*We need to restore power gating on coeff memory to initial state*/ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_WRITE(DCFE_MEM_PWR_CTRL, power_ctl); +} + +static void program_viewport( + struct dce_transform *xfm_dce, + const struct rect *view_port) +{ + REG_SET_2(VIEWPORT_START, 0, + VIEWPORT_X_START, view_port->x, + VIEWPORT_Y_START, view_port->y); + + REG_SET_2(VIEWPORT_SIZE, 0, + VIEWPORT_HEIGHT, view_port->height, + VIEWPORT_WIDTH, view_port->width); + + /* TODO: add stereo support */ +} + +static void calculate_inits( + struct dce_transform *xfm_dce, + const struct scaler_data *data, + struct scl_ratios_inits *inits) +{ + struct fixed31_32 h_init; + struct fixed31_32 v_init; + + inits->h_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios.horz) << 5; + inits->v_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios.vert) << 5; + + h_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios.horz, + dal_fixed31_32_from_int(data->taps.h_taps + 1)), + 2); + inits->h_init.integer = dal_fixed31_32_floor(h_init); + inits->h_init.fraction = dal_fixed31_32_u0d19(h_init) << 5; + + v_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios.vert, + dal_fixed31_32_from_int(data->taps.v_taps + 1)), + 2); + inits->v_init.integer = dal_fixed31_32_floor(v_init); + inits->v_init.fraction = dal_fixed31_32_u0d19(v_init) << 5; +} + +static void program_scl_ratios_inits( + struct dce_transform *xfm_dce, + struct scl_ratios_inits *inits) +{ + + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO, 0, + SCL_H_SCALE_RATIO, inits->h_int_scale_ratio); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO, 0, + SCL_V_SCALE_RATIO, inits->v_int_scale_ratio); + + REG_SET_2(SCL_HORZ_FILTER_INIT, 0, + SCL_H_INIT_INT, inits->h_init.integer, + SCL_H_INIT_FRAC, inits->h_init.fraction); + + REG_SET_2(SCL_VERT_FILTER_INIT, 0, + SCL_V_INIT_INT, inits->v_init.integer, + SCL_V_INIT_FRAC, inits->v_init.fraction); + + REG_WRITE(SCL_AUTOMATIC_MODE_CONTROL, 0); +} + +static const uint16_t *get_filter_coeffs_16p(int taps, struct fixed31_32 ratio) +{ + if (taps == 4) + return get_filter_4tap_16p(ratio); + else if (taps == 3) + return get_filter_3tap_16p(ratio); + else if (taps == 2) + return get_filter_2tap_16p(); + else if (taps == 1) + return NULL; + else { + /* should never happen, bug */ + BREAK_TO_DEBUGGER(); + return NULL; + } +} + +static void dce_transform_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + bool is_scaling_required; + bool filter_updated = false; + const uint16_t *coeffs_v, *coeffs_h; + + /*Use all three pieces of memory always*/ + REG_SET_2(LB_MEMORY_CTRL, 0, + LB_MEMORY_CONFIG, 0, + LB_MEMORY_SIZE, xfm_dce->lb_memory_size); + + /* Clear SCL_F_SHARP_CONTROL value to 0 */ + REG_WRITE(SCL_F_SHARP_CONTROL, 0); + + /* 1. Program overscan */ + program_overscan(xfm_dce, data); + + /* 2. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm_dce, data); + + if (is_scaling_required) { + /* 3. Calculate and program ratio, filter initialization */ + struct scl_ratios_inits inits = { 0 }; + + calculate_inits(xfm_dce, data, &inits); + + program_scl_ratios_inits(xfm_dce, &inits); + + coeffs_v = get_filter_coeffs_16p(data->taps.v_taps, data->ratios.vert); + coeffs_h = get_filter_coeffs_16p(data->taps.h_taps, data->ratios.horz); + + if (coeffs_v != xfm_dce->filter_v || coeffs_h != xfm_dce->filter_h) { + /* 4. Program vertical filters */ + if (xfm_dce->filter_v == NULL) + REG_SET(SCL_VERT_FILTER_CONTROL, 0, + SCL_V_2TAP_HARDCODE_COEF_EN, 0); + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps, + coeffs_v, + FILTER_TYPE_RGB_Y_VERTICAL); + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps, + coeffs_v, + FILTER_TYPE_ALPHA_VERTICAL); + + /* 5. Program horizontal filters */ + if (xfm_dce->filter_h == NULL) + REG_SET(SCL_HORZ_FILTER_CONTROL, 0, + SCL_H_2TAP_HARDCODE_COEF_EN, 0); + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps, + coeffs_h, + FILTER_TYPE_RGB_Y_HORIZONTAL); + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps, + coeffs_h, + FILTER_TYPE_ALPHA_HORIZONTAL); + + xfm_dce->filter_v = coeffs_v; + xfm_dce->filter_h = coeffs_h; + filter_updated = true; + } + } + + /* 6. Program the viewport */ + program_viewport(xfm_dce, &data->viewport); + + /* 7. Set bit to flip to new coefficient memory */ + if (filter_updated) + REG_UPDATE(SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE, 1); + + REG_UPDATE(LB_DATA_FORMAT, ALPHA_EN, data->lb_params.alpha_en); +} + +/***************************************************************************** + * set_clamp + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @brief + * Programs clamp according to panel bit depth. + * + *******************************************************************************/ +static void set_clamp( + struct dce_transform *xfm_dce, + enum dc_color_depth depth) +{ + int clamp_max = 0; + + /* At the clamp block the data will be MSB aligned, so we set the max + * clamp accordingly. + * For example, the max value for 6 bits MSB aligned (14 bit bus) would + * be "11 1111 0000 0000" in binary, so 0x3F00. + */ + switch (depth) { + case COLOR_DEPTH_666: + /* 6bit MSB aligned on 14 bit bus '11 1111 0000 0000' */ + clamp_max = 0x3F00; + break; + case COLOR_DEPTH_888: + /* 8bit MSB aligned on 14 bit bus '11 1111 1100 0000' */ + clamp_max = 0x3FC0; + break; + case COLOR_DEPTH_101010: + /* 10bit MSB aligned on 14 bit bus '11 1111 1111 1100' */ + clamp_max = 0x3FFC; + break; + case COLOR_DEPTH_121212: + /* 12bit MSB aligned on 14 bit bus '11 1111 1111 1111' */ + clamp_max = 0x3FFF; + break; + default: + clamp_max = 0x3FC0; + BREAK_TO_DEBUGGER(); /* Invalid clamp bit depth */ + } + REG_SET_2(OUT_CLAMP_CONTROL_B_CB, 0, + OUT_CLAMP_MIN_B_CB, 0, + OUT_CLAMP_MAX_B_CB, clamp_max); + + REG_SET_2(OUT_CLAMP_CONTROL_G_Y, 0, + OUT_CLAMP_MIN_G_Y, 0, + OUT_CLAMP_MAX_G_Y, clamp_max); + + REG_SET_2(OUT_CLAMP_CONTROL_R_CR, 0, + OUT_CLAMP_MIN_R_CR, 0, + OUT_CLAMP_MAX_R_CR, clamp_max); +} + +/******************************************************************************* + * set_round + * + * @brief + * Programs Round/Truncate + * + * @param [in] mode :round or truncate + * @param [in] depth :bit depth to round/truncate to + OUT_ROUND_TRUNC_MODE 3:0 0xA Output data round or truncate mode + POSSIBLE VALUES: + 00 - truncate to u0.12 + 01 - truncate to u0.11 + 02 - truncate to u0.10 + 03 - truncate to u0.9 + 04 - truncate to u0.8 + 05 - reserved + 06 - truncate to u0.14 + 07 - truncate to u0.13 set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + 08 - round to u0.12 + 09 - round to u0.11 + 10 - round to u0.10 + 11 - round to u0.9 + 12 - round to u0.8 + 13 - reserved + 14 - round to u0.14 + 15 - round to u0.13 + + ******************************************************************************/ +static void set_round( + struct dce_transform *xfm_dce, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth) +{ + int depth_bits = 0; + int mode_bit = 0; + + /* set up bit depth */ + switch (depth) { + case DCP_OUT_TRUNC_ROUND_DEPTH_14BIT: + depth_bits = 6; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_13BIT: + depth_bits = 7; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_12BIT: + depth_bits = 0; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_11BIT: + depth_bits = 1; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_10BIT: + depth_bits = 2; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_9BIT: + depth_bits = 3; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_8BIT: + depth_bits = 4; + break; + default: + depth_bits = 4; + BREAK_TO_DEBUGGER(); /* Invalid dcp_out_trunc_round_depth */ + } + + /* set up round or truncate */ + switch (mode) { + case DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE: + mode_bit = 0; + break; + case DCP_OUT_TRUNC_ROUND_MODE_ROUND: + mode_bit = 1; + break; + default: + BREAK_TO_DEBUGGER(); /* Invalid dcp_out_trunc_round_mode */ + } + + depth_bits |= mode_bit << 3; + + REG_SET(OUT_ROUND_CONTROL, 0, OUT_ROUND_TRUNC_MODE, depth_bits); +} + +/***************************************************************************** + * set_dither + * + * @brief + * Programs Dither + * + * @param [in] dither_enable : enable dither + * @param [in] dither_mode : dither mode to set + * @param [in] dither_depth : bit depth to dither to + * @param [in] frame_random_enable : enable frame random + * @param [in] rgb_random_enable : enable rgb random + * @param [in] highpass_random_enable : enable highpass random + * + ******************************************************************************/ + +static void set_dither( + struct dce_transform *xfm_dce, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable) +{ + int dither_depth_bits = 0; + int dither_mode_bits = 0; + + switch (dither_mode) { + case DCP_SPATIAL_DITHER_MODE_AAAA: + dither_mode_bits = 0; + break; + case DCP_SPATIAL_DITHER_MODE_A_AA_A: + dither_mode_bits = 1; + break; + case DCP_SPATIAL_DITHER_MODE_AABBAABB: + dither_mode_bits = 2; + break; + case DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC: + dither_mode_bits = 3; + break; + default: + /* Invalid dcp_spatial_dither_mode */ + BREAK_TO_DEBUGGER(); + } + + switch (dither_depth) { + case DCP_SPATIAL_DITHER_DEPTH_30BPP: + dither_depth_bits = 0; + break; + case DCP_SPATIAL_DITHER_DEPTH_24BPP: + dither_depth_bits = 1; + break; + default: + /* Invalid dcp_spatial_dither_depth */ + BREAK_TO_DEBUGGER(); + } + + /* write the register */ + REG_SET_6(DCP_SPATIAL_DITHER_CNTL, 0, + DCP_SPATIAL_DITHER_EN, dither_enable, + DCP_SPATIAL_DITHER_MODE, dither_mode_bits, + DCP_SPATIAL_DITHER_DEPTH, dither_depth_bits, + DCP_FRAME_RANDOM_ENABLE, frame_random_enable, + DCP_RGB_RANDOM_ENABLE, rgb_random_enable, + DCP_HIGHPASS_RANDOM_ENABLE, highpass_random_enable); +} + +/***************************************************************************** + * dce_transform_bit_depth_reduction_program + * + * @brief + * Programs the DCP bit depth reduction registers (Clamp, Round/Truncate, + * Dither) for dce + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + ******************************************************************************/ +static void program_bit_depth_reduction( + struct dce_transform *xfm_dce, + enum dc_color_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + enum dcp_bit_depth_reduction_mode depth_reduction_mode; + enum dcp_spatial_dither_mode spatial_dither_mode; + bool frame_random_enable; + bool rgb_random_enable; + bool highpass_random_enable; + + ASSERT(depth < COLOR_DEPTH_121212); /* Invalid clamp bit depth */ + + if (bit_depth_params->flags.SPATIAL_DITHER_ENABLED) { + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DITHER; + frame_random_enable = true; + rgb_random_enable = true; + highpass_random_enable = true; + + } else { + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED; + frame_random_enable = false; + rgb_random_enable = false; + highpass_random_enable = false; + } + + spatial_dither_mode = DCP_SPATIAL_DITHER_MODE_A_AA_A; + + set_clamp(xfm_dce, depth); + + switch (depth_reduction_mode) { + case DCP_BIT_DEPTH_REDUCTION_MODE_DITHER: + /* Spatial Dither: Set round/truncate to bypass (12bit), + * enable Dither (30bpp) */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm_dce, true, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_ROUND: + /* Round: Enable round (10bit), disable Dither */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_ROUND, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm_dce, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE: /* Truncate */ + /* Truncate: Enable truncate (10bit), disable Dither */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm_dce, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + + case DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED: /* Disabled */ + /* Truncate: Set round/truncate to bypass (12bit), + * disable Dither */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm_dce, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + default: + /* Invalid DCP Depth reduction mode */ + BREAK_TO_DEBUGGER(); + break; + } +} + +static int dce_transform_get_max_num_of_supported_lines( + struct dce_transform *xfm_dce, + enum lb_pixel_depth depth, + int pixel_width) +{ + int pixels_per_entries = 0; + int max_pixels_supports = 0; + + ASSERT(pixel_width); + + /* Find number of pixels that can fit into a single LB entry and + * take floor of the value since we cannot store a single pixel + * across multiple entries. */ + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 18; + break; + + case LB_PIXEL_DEPTH_24BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 24; + break; + + case LB_PIXEL_DEPTH_30BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 30; + break; + + case LB_PIXEL_DEPTH_36BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 36; + break; + + default: + dm_logger_write(xfm_dce->base.ctx->logger, LOG_WARNING, + "%s: Invalid LB pixel depth", + __func__); + BREAK_TO_DEBUGGER(); + break; + } + + ASSERT(pixels_per_entries); + + max_pixels_supports = + pixels_per_entries * + xfm_dce->lb_memory_size; + + return (max_pixels_supports / pixel_width); +} + +static void set_denormalization( + struct dce_transform *xfm_dce, + enum dc_color_depth depth) +{ + int denorm_mode = 0; + + switch (depth) { + case COLOR_DEPTH_666: + /* 63/64 for 6 bit output color depth */ + denorm_mode = 1; + break; + case COLOR_DEPTH_888: + /* Unity for 8 bit output color depth + * because prescale is disabled by default */ + denorm_mode = 0; + break; + case COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + denorm_mode = 3; + break; + case COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + denorm_mode = 5; + break; + case COLOR_DEPTH_141414: + case COLOR_DEPTH_161616: + default: + /* not valid used case! */ + break; + } + + REG_SET(DENORM_CONTROL, 0, DENORM_MODE, denorm_mode); +} + +static void dce_transform_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + int pixel_depth, expan_mode; + enum dc_color_depth color_depth; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + color_depth = COLOR_DEPTH_666; + pixel_depth = 2; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_24BPP: + color_depth = COLOR_DEPTH_888; + pixel_depth = 1; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_30BPP: + color_depth = COLOR_DEPTH_101010; + pixel_depth = 0; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_36BPP: + color_depth = COLOR_DEPTH_121212; + pixel_depth = 3; + expan_mode = 0; + break; + default: + color_depth = COLOR_DEPTH_101010; + pixel_depth = 0; + expan_mode = 1; + BREAK_TO_DEBUGGER(); + break; + } + + set_denormalization(xfm_dce, color_depth); + program_bit_depth_reduction(xfm_dce, color_depth, bit_depth_params); + + REG_UPDATE_2(LB_DATA_FORMAT, + PIXEL_DEPTH, pixel_depth, + PIXEL_EXPAN_MODE, expan_mode); + + if (!(xfm_dce->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dm_logger_write(xfm->ctx->logger, LOG_WARNING, + "%s: Capability not supported", + __func__); + } +} + +static void program_gamut_remap( + struct dce_transform *xfm_dce, + const uint16_t *reg_val) +{ + if (reg_val) { + REG_SET_2(GAMUT_REMAP_C11_C12, 0, + GAMUT_REMAP_C11, reg_val[0], + GAMUT_REMAP_C12, reg_val[1]); + REG_SET_2(GAMUT_REMAP_C13_C14, 0, + GAMUT_REMAP_C13, reg_val[2], + GAMUT_REMAP_C14, reg_val[3]); + REG_SET_2(GAMUT_REMAP_C21_C22, 0, + GAMUT_REMAP_C21, reg_val[4], + GAMUT_REMAP_C22, reg_val[5]); + REG_SET_2(GAMUT_REMAP_C23_C24, 0, + GAMUT_REMAP_C23, reg_val[6], + GAMUT_REMAP_C24, reg_val[7]); + REG_SET_2(GAMUT_REMAP_C31_C32, 0, + GAMUT_REMAP_C31, reg_val[8], + GAMUT_REMAP_C32, reg_val[9]); + REG_SET_2(GAMUT_REMAP_C33_C34, 0, + GAMUT_REMAP_C33, reg_val[10], + GAMUT_REMAP_C34, reg_val[11]); + + REG_SET(GAMUT_REMAP_CONTROL, 0, GRPH_GAMUT_REMAP_MODE, 1); + } else + REG_SET(GAMUT_REMAP_CONTROL, 0, GRPH_GAMUT_REMAP_MODE, 0); + +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_gamut_remap + * + * @param [in] const struct xfm_grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and apply color temperature adjustment to in Rgb color space + * + * @see + * + ***************************************************************************** + */ +static void dce_transform_set_gamut_remap( + struct transform *xfm, + const struct xfm_grph_csc_adjustment *adjust) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW) + /* Bypass if type is bypass or hw */ + program_gamut_remap(xfm_dce, NULL); + else { + struct fixed31_32 arr_matrix[GAMUT_MATRIX_SIZE]; + uint16_t arr_reg_val[GAMUT_MATRIX_SIZE]; + + arr_matrix[0] = adjust->temperature_matrix[0]; + arr_matrix[1] = adjust->temperature_matrix[1]; + arr_matrix[2] = adjust->temperature_matrix[2]; + arr_matrix[3] = dal_fixed31_32_zero; + + arr_matrix[4] = adjust->temperature_matrix[3]; + arr_matrix[5] = adjust->temperature_matrix[4]; + arr_matrix[6] = adjust->temperature_matrix[5]; + arr_matrix[7] = dal_fixed31_32_zero; + + arr_matrix[8] = adjust->temperature_matrix[6]; + arr_matrix[9] = adjust->temperature_matrix[7]; + arr_matrix[10] = adjust->temperature_matrix[8]; + arr_matrix[11] = dal_fixed31_32_zero; + + convert_float_matrix( + arr_reg_val, arr_matrix, GAMUT_MATRIX_SIZE); + + program_gamut_remap(xfm_dce, arr_reg_val); + } +} + +static uint32_t decide_taps(struct fixed31_32 ratio, uint32_t in_taps, bool chroma) +{ + uint32_t taps; + + if (IDENTITY_RATIO(ratio)) { + return 1; + } else if (in_taps != 0) { + taps = in_taps; + } else { + taps = 4; + } + + if (chroma) { + taps /= 2; + if (taps < 2) + taps = 2; + } + + return taps; +} + + +bool dce_transform_get_optimal_number_of_taps( + struct transform *xfm, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + int pixel_width = scl_data->viewport.width; + int max_num_of_lines; + + if (xfm_dce->prescaler_on && + (scl_data->viewport.width > scl_data->recout.width)) + pixel_width = scl_data->recout.width; + + max_num_of_lines = dce_transform_get_max_num_of_supported_lines( + xfm_dce, + scl_data->lb_params.depth, + pixel_width); + + /* Fail if in_taps are impossible */ + if (in_taps->v_taps >= max_num_of_lines) + return false; + + /* + * Set taps according to this policy (in this order) + * - Use 1 for no scaling + * - Use input taps + * - Use 4 and reduce as required by line buffer size + * - Decide chroma taps if chroma is scaled + * + * Ignore input chroma taps. Decide based on non-chroma + */ + scl_data->taps.h_taps = decide_taps(scl_data->ratios.horz, in_taps->h_taps, false); + scl_data->taps.v_taps = decide_taps(scl_data->ratios.vert, in_taps->v_taps, false); + scl_data->taps.h_taps_c = decide_taps(scl_data->ratios.horz_c, in_taps->h_taps, true); + scl_data->taps.v_taps_c = decide_taps(scl_data->ratios.vert_c, in_taps->v_taps, true); + + if (!IDENTITY_RATIO(scl_data->ratios.vert)) { + /* reduce v_taps if needed but ensure we have at least two */ + if (in_taps->v_taps == 0 + && max_num_of_lines <= scl_data->taps.v_taps + && scl_data->taps.v_taps > 1) { + scl_data->taps.v_taps = max_num_of_lines - 1; + } + + if (scl_data->taps.v_taps <= 1) + return false; + } + + if (!IDENTITY_RATIO(scl_data->ratios.vert_c)) { + /* reduce chroma v_taps if needed but ensure we have at least two */ + if (max_num_of_lines <= scl_data->taps.v_taps_c && scl_data->taps.v_taps_c > 1) { + scl_data->taps.v_taps_c = max_num_of_lines - 1; + } + + if (scl_data->taps.v_taps_c <= 1) + return false; + } + + /* we've got valid taps */ + return true; +} + +static void dce_transform_reset(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + xfm_dce->filter_h = NULL; + xfm_dce->filter_v = NULL; +} + +static void program_color_matrix( + struct dce_transform *xfm_dce, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + { + REG_SET_2(OUTPUT_CSC_C11_C12, 0, + OUTPUT_CSC_C11, tbl_entry->regval[0], + OUTPUT_CSC_C12, tbl_entry->regval[1]); + } + { + REG_SET_2(OUTPUT_CSC_C13_C14, 0, + OUTPUT_CSC_C11, tbl_entry->regval[2], + OUTPUT_CSC_C12, tbl_entry->regval[3]); + } + { + REG_SET_2(OUTPUT_CSC_C21_C22, 0, + OUTPUT_CSC_C11, tbl_entry->regval[4], + OUTPUT_CSC_C12, tbl_entry->regval[5]); + } + { + REG_SET_2(OUTPUT_CSC_C23_C24, 0, + OUTPUT_CSC_C11, tbl_entry->regval[6], + OUTPUT_CSC_C12, tbl_entry->regval[7]); + } + { + REG_SET_2(OUTPUT_CSC_C31_C32, 0, + OUTPUT_CSC_C11, tbl_entry->regval[8], + OUTPUT_CSC_C12, tbl_entry->regval[9]); + } + { + REG_SET_2(OUTPUT_CSC_C33_C34, 0, + OUTPUT_CSC_C11, tbl_entry->regval[10], + OUTPUT_CSC_C12, tbl_entry->regval[11]); + } +} + +static bool configure_graphics_mode( + struct dce_transform *xfm_dce, + enum csc_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum dc_color_space color_space) +{ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) { + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 4); + } else { + + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* TV RGB */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 1); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 2); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 3); + break; + default: + return false; + } + } + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + break; + break; + case COLOR_SPACE_SRGB_LIMITED: + /* TV RGB */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 1); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 2); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 3); + break; + default: + return false; + } + + } else + /* by pass */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + + return true; +} + +void dce110_opp_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + program_color_matrix( + xfm_dce, tbl_entry, GRAPHICS_CSC_ADJUST_TYPE_SW); + + /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ + configure_graphics_mode(xfm_dce, config, GRAPHICS_CSC_ADJUST_TYPE_SW, + tbl_entry->color_space); +} + +void dce110_opp_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct out_csc_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option = + GRPH_COLOR_MATRIX_HW_DEFAULT; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->out_color_space) + continue; + /* program the matrix with default values from this + * file */ + program_color_matrix(xfm_dce, elm, option); + config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix */ + + configure_graphics_mode(xfm_dce, config, + default_adjust->csc_adjust_type, + default_adjust->out_color_space); +} + +static void program_pwl( + struct dce_transform *xfm_dce, + const struct pwl_params *params) +{ + uint32_t value; + int retval; + + { + uint8_t max_tries = 10; + uint8_t counter = 0; + + /* Power on LUT memory */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_UPDATE(DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS, 1); + else + REG_UPDATE(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS, 1); + + while (counter < max_tries) { + if (REG(DCFE_MEM_PWR_STATUS)) { + value = REG_READ(DCFE_MEM_PWR_STATUS); + REG_GET(DCFE_MEM_PWR_STATUS, + DCP_REGAMMA_MEM_PWR_STATE, + &retval); + + if (retval == 0) + break; + ++counter; + } else { + value = REG_READ(DCFE_MEM_LIGHT_SLEEP_CNTL); + REG_GET(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_MEM_PWR_STATE, + &retval); + + if (retval == 0) + break; + ++counter; + } + } + + if (counter == max_tries) { + dm_logger_write(xfm_dce->base.ctx->logger, LOG_WARNING, + "%s: regamma lut was not powered on " + "in a timely manner," + " programming still proceeds\n", + __func__); + } + } + + REG_UPDATE(REGAMMA_LUT_WRITE_EN_MASK, + REGAMMA_LUT_WRITE_EN_MASK, 7); + + REG_WRITE(REGAMMA_LUT_INDEX, 0); + + /* Program REGAMMA_LUT_DATA */ + { + uint32_t i = 0; + const struct pwl_result_data *rgb = params->rgb_resulted; + + while (i != params->hw_points_num) { + + REG_WRITE(REGAMMA_LUT_DATA, rgb->red_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->green_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->blue_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->delta_red_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->delta_green_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } + + /* we are done with DCP LUT memory; re-enable low power mode */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_UPDATE(DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS, 0); + else + REG_UPDATE(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS, 0); +} + +static void regamma_config_regions_and_segments( + struct dce_transform *xfm_dce, + const struct pwl_params *params) +{ + const struct gamma_curve *curve; + + { + REG_SET_2(REGAMMA_CNTLA_START_CNTL, 0, + REGAMMA_CNTLA_EXP_REGION_START, params->arr_points[0].custom_float_x, + REGAMMA_CNTLA_EXP_REGION_START_SEGMENT, 0); + } + { + REG_SET(REGAMMA_CNTLA_SLOPE_CNTL, 0, + REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE, params->arr_points[0].custom_float_slope); + + } + { + REG_SET(REGAMMA_CNTLA_END_CNTL1, 0, + REGAMMA_CNTLA_EXP_REGION_END, params->arr_points[1].custom_float_x); + } + { + REG_SET_2(REGAMMA_CNTLA_END_CNTL2, 0, + REGAMMA_CNTLA_EXP_REGION_END_BASE, params->arr_points[1].custom_float_y, + REGAMMA_CNTLA_EXP_REGION_END_SLOPE, params->arr_points[2].custom_float_slope); + } + + curve = params->arr_curve_points; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_0_1, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + } + + curve += 2; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_2_3, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + } + + curve += 2; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_4_5, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + } + + curve += 2; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_6_7, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + } + + curve += 2; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_8_9, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + } + + curve += 2; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_10_11, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + } + + curve += 2; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_12_13, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + } + + curve += 2; + + { + REG_SET_4(REGAMMA_CNTLA_REGION_14_15, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + } +} + + + +void dce110_opp_program_regamma_pwl( + struct transform *xfm, + const struct pwl_params *params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + /* Setup regions */ + regamma_config_regions_and_segments(xfm_dce, params); + + /* Program PWL */ + program_pwl(xfm_dce, params); +} + +void dce110_opp_power_on_regamma_lut( + struct transform *xfm, + bool power_on) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + if (REG(DCFE_MEM_PWR_CTRL)) + REG_UPDATE_2(DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS, power_on, + DCP_LUT_MEM_PWR_DIS, power_on); + else + REG_UPDATE_2(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS, power_on, + DCP_LUT_LIGHT_SLEEP_DIS, power_on); + +} + +void dce110_opp_set_regamma_mode(struct transform *xfm, + enum opp_regamma mode) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + REG_SET(REGAMMA_CONTROL, 0, + GRPH_REGAMMA_MODE, mode); +} + +static const struct transform_funcs dce_transform_funcs = { + .transform_reset = dce_transform_reset, + .transform_set_scaler = + dce_transform_set_scaler, + .transform_set_gamut_remap = + dce_transform_set_gamut_remap, + .opp_set_csc_adjustment = dce110_opp_set_csc_adjustment, + .opp_set_csc_default = dce110_opp_set_csc_default, + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut, + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl, + .opp_set_regamma_mode = dce110_opp_set_regamma_mode, + .transform_set_pixel_storage_depth = + dce_transform_set_pixel_storage_depth, + .transform_get_optimal_number_of_taps = + dce_transform_get_optimal_number_of_taps +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void dce_transform_construct( + struct dce_transform *xfm_dce, + struct dc_context *ctx, + uint32_t inst, + const struct dce_transform_registers *regs, + const struct dce_transform_shift *xfm_shift, + const struct dce_transform_mask *xfm_mask) +{ + xfm_dce->base.ctx = ctx; + + xfm_dce->base.inst = inst; + xfm_dce->base.funcs = &dce_transform_funcs; + + xfm_dce->regs = regs; + xfm_dce->xfm_shift = xfm_shift; + xfm_dce->xfm_mask = xfm_mask; + + xfm_dce->prescaler_on = true; + xfm_dce->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP; + + xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; + xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.h b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.h new file mode 100644 index 000000000000..bfc94b4927b9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.h @@ -0,0 +1,516 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _DCE_DCE_TRANSFORM_H_ +#define _DCE_DCE_TRANSFORM_H_ + + +#include "transform.h" + +#define TO_DCE_TRANSFORM(transform)\ + container_of(transform, struct dce_transform, base) + +#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 +#define LB_BITS_PER_ENTRY 144 + +#define XFM_COMMON_REG_LIST_DCE_BASE(id) \ + SRI(LB_DATA_FORMAT, LB, id), \ + SRI(GAMUT_REMAP_CONTROL, DCP, id), \ + SRI(GAMUT_REMAP_C11_C12, DCP, id), \ + SRI(GAMUT_REMAP_C13_C14, DCP, id), \ + SRI(GAMUT_REMAP_C21_C22, DCP, id), \ + SRI(GAMUT_REMAP_C23_C24, DCP, id), \ + SRI(GAMUT_REMAP_C31_C32, DCP, id), \ + SRI(GAMUT_REMAP_C33_C34, DCP, id), \ + SRI(OUTPUT_CSC_C11_C12, DCP, id), \ + SRI(OUTPUT_CSC_C13_C14, DCP, id), \ + SRI(OUTPUT_CSC_C21_C22, DCP, id), \ + SRI(OUTPUT_CSC_C23_C24, DCP, id), \ + SRI(OUTPUT_CSC_C31_C32, DCP, id), \ + SRI(OUTPUT_CSC_C33_C34, DCP, id), \ + SRI(OUTPUT_CSC_CONTROL, DCP, id), \ + SRI(REGAMMA_CNTLA_START_CNTL, DCP, id), \ + SRI(REGAMMA_CNTLA_SLOPE_CNTL, DCP, id), \ + SRI(REGAMMA_CNTLA_END_CNTL1, DCP, id), \ + SRI(REGAMMA_CNTLA_END_CNTL2, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_0_1, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_2_3, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_4_5, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_6_7, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_8_9, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_10_11, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_12_13, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_14_15, DCP, id), \ + SRI(REGAMMA_LUT_WRITE_EN_MASK, DCP, id), \ + SRI(REGAMMA_LUT_INDEX, DCP, id), \ + SRI(REGAMMA_LUT_DATA, DCP, id), \ + SRI(REGAMMA_CONTROL, DCP, id), \ + SRI(DENORM_CONTROL, DCP, id), \ + SRI(DCP_SPATIAL_DITHER_CNTL, DCP, id), \ + SRI(OUT_ROUND_CONTROL, DCP, id), \ + SRI(OUT_CLAMP_CONTROL_R_CR, DCP, id), \ + SRI(OUT_CLAMP_CONTROL_G_Y, DCP, id), \ + SRI(OUT_CLAMP_CONTROL_B_CB, DCP, id), \ + SRI(SCL_MODE, SCL, id), \ + SRI(SCL_TAP_CONTROL, SCL, id), \ + SRI(SCL_CONTROL, SCL, id), \ + SRI(SCL_BYPASS_CONTROL, SCL, id), \ + SRI(EXT_OVERSCAN_LEFT_RIGHT, SCL, id), \ + SRI(EXT_OVERSCAN_TOP_BOTTOM, SCL, id), \ + SRI(SCL_VERT_FILTER_CONTROL, SCL, id), \ + SRI(SCL_HORZ_FILTER_CONTROL, SCL, id), \ + SRI(SCL_COEF_RAM_SELECT, SCL, id), \ + SRI(SCL_COEF_RAM_TAP_DATA, SCL, id), \ + SRI(VIEWPORT_START, SCL, id), \ + SRI(VIEWPORT_SIZE, SCL, id), \ + SRI(SCL_HORZ_FILTER_SCALE_RATIO, SCL, id), \ + SRI(SCL_VERT_FILTER_SCALE_RATIO, SCL, id), \ + SRI(SCL_HORZ_FILTER_INIT, SCL, id), \ + SRI(SCL_VERT_FILTER_INIT, SCL, id), \ + SRI(SCL_AUTOMATIC_MODE_CONTROL, SCL, id), \ + SRI(LB_MEMORY_CTRL, LB, id), \ + SRI(SCL_UPDATE, SCL, id), \ + SRI(SCL_F_SHARP_CONTROL, SCL, id) + +#define XFM_COMMON_REG_LIST_DCE80(id) \ + XFM_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_LIGHT_SLEEP_CNTL, CRTC, id) + +#define XFM_COMMON_REG_LIST_DCE100(id) \ + XFM_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, CRTC, id), \ + SRI(DCFE_MEM_PWR_STATUS, CRTC, id) + +#define XFM_COMMON_REG_LIST_DCE110(id) \ + XFM_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, DCFE, id), \ + SRI(DCFE_MEM_PWR_STATUS, DCFE, id) + +#define XFM_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define XFM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + XFM_SF(OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MIN_B_CB, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MAX_B_CB, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MIN_G_Y, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MAX_G_Y, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MIN_R_CR, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MAX_R_CR, mask_sh), \ + XFM_SF(OUT_ROUND_CONTROL, OUT_ROUND_TRUNC_MODE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_EN, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_MODE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_DEPTH, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_FRAME_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_RGB_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_HIGHPASS_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DENORM_CONTROL, DENORM_MODE, mask_sh), \ + XFM_SF(LB_DATA_FORMAT, PIXEL_DEPTH, mask_sh), \ + XFM_SF(LB_DATA_FORMAT, PIXEL_EXPAN_MODE, mask_sh), \ + XFM_SF(GAMUT_REMAP_C11_C12, GAMUT_REMAP_C11, mask_sh), \ + XFM_SF(GAMUT_REMAP_C11_C12, GAMUT_REMAP_C12, mask_sh), \ + XFM_SF(GAMUT_REMAP_C13_C14, GAMUT_REMAP_C13, mask_sh), \ + XFM_SF(GAMUT_REMAP_C13_C14, GAMUT_REMAP_C14, mask_sh), \ + XFM_SF(GAMUT_REMAP_C21_C22, GAMUT_REMAP_C21, mask_sh), \ + XFM_SF(GAMUT_REMAP_C21_C22, GAMUT_REMAP_C22, mask_sh), \ + XFM_SF(GAMUT_REMAP_C23_C24, GAMUT_REMAP_C23, mask_sh), \ + XFM_SF(GAMUT_REMAP_C23_C24, GAMUT_REMAP_C24, mask_sh), \ + XFM_SF(GAMUT_REMAP_C31_C32, GAMUT_REMAP_C31, mask_sh), \ + XFM_SF(GAMUT_REMAP_C31_C32, GAMUT_REMAP_C32, mask_sh), \ + XFM_SF(GAMUT_REMAP_C33_C34, GAMUT_REMAP_C33, mask_sh), \ + XFM_SF(GAMUT_REMAP_C33_C34, GAMUT_REMAP_C34, mask_sh), \ + XFM_SF(GAMUT_REMAP_CONTROL, GRPH_GAMUT_REMAP_MODE, mask_sh), \ + XFM_SF(OUTPUT_CSC_C11_C12, OUTPUT_CSC_C11, mask_sh),\ + XFM_SF(OUTPUT_CSC_C11_C12, OUTPUT_CSC_C12, mask_sh),\ + XFM_SF(OUTPUT_CSC_CONTROL, OUTPUT_CSC_GRPH_MODE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START_SEGMENT, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_SLOPE_CNTL, REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL1, REGAMMA_CNTLA_EXP_REGION_END, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_BASE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_SLOPE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + XFM_SF(REGAMMA_LUT_WRITE_EN_MASK, REGAMMA_LUT_WRITE_EN_MASK, mask_sh),\ + XFM_SF(REGAMMA_CONTROL, GRPH_REGAMMA_MODE, mask_sh),\ + XFM_SF(SCL_MODE, SCL_MODE, mask_sh), \ + XFM_SF(SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL_CONTROL, SCL_BOUNDARY_MODE, mask_sh), \ + XFM_SF(SCL_BYPASS_CONTROL, SCL_BYPASS_MODE, mask_sh), \ + XFM_SF(EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT, mask_sh), \ + XFM_SF(EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT, mask_sh), \ + XFM_SF(EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP, mask_sh), \ + XFM_SF(EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM, mask_sh), \ + XFM_SF(SCL_COEF_RAM_SELECT, SCL_C_RAM_FILTER_TYPE, mask_sh), \ + XFM_SF(SCL_COEF_RAM_SELECT, SCL_C_RAM_PHASE, mask_sh), \ + XFM_SF(SCL_COEF_RAM_SELECT, SCL_C_RAM_TAP_PAIR_IDX, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF, mask_sh), \ + XFM_SF(VIEWPORT_START, VIEWPORT_X_START, mask_sh), \ + XFM_SF(VIEWPORT_START, VIEWPORT_Y_START, mask_sh), \ + XFM_SF(VIEWPORT_SIZE, VIEWPORT_HEIGHT, mask_sh), \ + XFM_SF(VIEWPORT_SIZE, VIEWPORT_WIDTH, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_SCALE_RATIO, SCL_H_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_SCALE_RATIO, SCL_V_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_INIT, SCL_H_INIT_INT, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_INIT, SCL_H_INIT_FRAC, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_INIT, SCL_V_INIT_INT, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_INIT, SCL_V_INIT_FRAC, mask_sh), \ + XFM_SF(LB_MEMORY_CTRL, LB_MEMORY_CONFIG, mask_sh), \ + XFM_SF(LB_MEMORY_CTRL, LB_MEMORY_SIZE, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_CONTROL, SCL_V_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_CONTROL, SCL_H_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE, mask_sh), \ + XFM_SF(LB_DATA_FORMAT, ALPHA_EN, mask_sh) + +#define XFM_COMMON_MASK_SH_LIST_DCE80(mask_sh) \ + XFM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, REGAMMA_LUT_LIGHT_SLEEP_DIS, mask_sh),\ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, DCP_LUT_LIGHT_SLEEP_DIS, mask_sh),\ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, REGAMMA_LUT_MEM_PWR_STATE, mask_sh) + +#define XFM_COMMON_MASK_SH_LIST_DCE110(mask_sh) \ + XFM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + XFM_SF(DCFE_MEM_PWR_CTRL, SCL_COEFF_MEM_PWR_DIS, mask_sh), \ + XFM_SF(DCFE_MEM_PWR_STATUS, SCL_COEFF_MEM_PWR_STATE, mask_sh), \ + XFM_SF(DCFE_MEM_PWR_CTRL, DCP_REGAMMA_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE_MEM_PWR_STATUS, DCP_REGAMMA_MEM_PWR_STATE, mask_sh),\ + XFM_SF(SCL_MODE, SCL_PSCL_EN, mask_sh) + +#define XFM_COMMON_MASK_SH_LIST_SOC_BASE(mask_sh) \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MIN_B_CB, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MAX_B_CB, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MIN_G_Y, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MAX_G_Y, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MIN_R_CR, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MAX_R_CR, mask_sh), \ + XFM_SF(DCP0_OUT_ROUND_CONTROL, OUT_ROUND_TRUNC_MODE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_EN, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_MODE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_DEPTH, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_FRAME_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_RGB_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_HIGHPASS_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP0_DENORM_CONTROL, DENORM_MODE, mask_sh), \ + XFM_SF(LB0_LB_DATA_FORMAT, PIXEL_DEPTH, mask_sh), \ + XFM_SF(LB0_LB_DATA_FORMAT, PIXEL_EXPAN_MODE, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C11_C12, GAMUT_REMAP_C11, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C11_C12, GAMUT_REMAP_C12, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C13_C14, GAMUT_REMAP_C13, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C13_C14, GAMUT_REMAP_C14, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C21_C22, GAMUT_REMAP_C21, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C21_C22, GAMUT_REMAP_C22, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C23_C24, GAMUT_REMAP_C23, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C23_C24, GAMUT_REMAP_C24, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C31_C32, GAMUT_REMAP_C31, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C31_C32, GAMUT_REMAP_C32, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C33_C34, GAMUT_REMAP_C33, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C33_C34, GAMUT_REMAP_C34, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_CONTROL, GRPH_GAMUT_REMAP_MODE, mask_sh), \ + XFM_SF(DCP0_OUTPUT_CSC_C11_C12, OUTPUT_CSC_C11, mask_sh),\ + XFM_SF(DCP0_OUTPUT_CSC_C11_C12, OUTPUT_CSC_C12, mask_sh),\ + XFM_SF(DCP0_OUTPUT_CSC_CONTROL, OUTPUT_CSC_GRPH_MODE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START_SEGMENT, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_SLOPE_CNTL, REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_END_CNTL1, REGAMMA_CNTLA_EXP_REGION_END, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_BASE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_SLOPE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CONTROL, GRPH_REGAMMA_MODE, mask_sh),\ + XFM_SF(SCL0_SCL_MODE, SCL_MODE, mask_sh), \ + XFM_SF(SCL0_SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL0_SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL0_SCL_CONTROL, SCL_BOUNDARY_MODE, mask_sh), \ + XFM_SF(SCL0_SCL_BYPASS_CONTROL, SCL_BYPASS_MODE, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_SELECT, SCL_C_RAM_FILTER_TYPE, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_SELECT, SCL_C_RAM_PHASE, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_SELECT, SCL_C_RAM_TAP_PAIR_IDX, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_START, VIEWPORT_X_START, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_START, VIEWPORT_Y_START, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_SIZE, VIEWPORT_HEIGHT, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_SIZE, VIEWPORT_WIDTH, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_SCALE_RATIO, SCL_H_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_SCALE_RATIO, SCL_V_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_INT, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_FRAC, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_INT, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_FRAC, mask_sh), \ + XFM_SF(LB0_LB_MEMORY_CTRL, LB_MEMORY_CONFIG, mask_sh), \ + XFM_SF(LB0_LB_MEMORY_CTRL, LB_MEMORY_SIZE, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_CONTROL, SCL_V_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_CONTROL, SCL_H_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE, mask_sh), \ + XFM_SF(LB0_LB_DATA_FORMAT, ALPHA_EN, mask_sh), \ + XFM_SF(DCFE0_DCFE_MEM_PWR_CTRL, SCL_COEFF_MEM_PWR_DIS, mask_sh), \ + XFM_SF(DCFE0_DCFE_MEM_PWR_CTRL, DCP_REGAMMA_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE0_DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE0_DCFE_MEM_PWR_STATUS, SCL_COEFF_MEM_PWR_STATE, mask_sh), \ + XFM_SF(SCL0_SCL_MODE, SCL_PSCL_EN, mask_sh) + +#define XFM_REG_FIELD_LIST(type) \ + type OUT_CLAMP_MIN_B_CB; \ + type OUT_CLAMP_MAX_B_CB; \ + type OUT_CLAMP_MIN_G_Y; \ + type OUT_CLAMP_MAX_G_Y; \ + type OUT_CLAMP_MIN_R_CR; \ + type OUT_CLAMP_MAX_R_CR; \ + type OUT_ROUND_TRUNC_MODE; \ + type DCP_SPATIAL_DITHER_EN; \ + type DCP_SPATIAL_DITHER_MODE; \ + type DCP_SPATIAL_DITHER_DEPTH; \ + type DCP_FRAME_RANDOM_ENABLE; \ + type DCP_RGB_RANDOM_ENABLE; \ + type DCP_HIGHPASS_RANDOM_ENABLE; \ + type DENORM_MODE; \ + type PIXEL_DEPTH; \ + type PIXEL_EXPAN_MODE; \ + type GAMUT_REMAP_C11; \ + type GAMUT_REMAP_C12; \ + type GAMUT_REMAP_C13; \ + type GAMUT_REMAP_C14; \ + type GAMUT_REMAP_C21; \ + type GAMUT_REMAP_C22; \ + type GAMUT_REMAP_C23; \ + type GAMUT_REMAP_C24; \ + type GAMUT_REMAP_C31; \ + type GAMUT_REMAP_C32; \ + type GAMUT_REMAP_C33; \ + type GAMUT_REMAP_C34; \ + type GRPH_GAMUT_REMAP_MODE; \ + type OUTPUT_CSC_C11; \ + type OUTPUT_CSC_C12; \ + type OUTPUT_CSC_GRPH_MODE; \ + type DCP_REGAMMA_MEM_PWR_DIS; \ + type DCP_LUT_MEM_PWR_DIS; \ + type REGAMMA_LUT_LIGHT_SLEEP_DIS; \ + type DCP_LUT_LIGHT_SLEEP_DIS; \ + type REGAMMA_CNTLA_EXP_REGION_START; \ + type REGAMMA_CNTLA_EXP_REGION_START_SEGMENT; \ + type REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE; \ + type REGAMMA_CNTLA_EXP_REGION_END; \ + type REGAMMA_CNTLA_EXP_REGION_END_BASE; \ + type REGAMMA_CNTLA_EXP_REGION_END_SLOPE; \ + type REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET; \ + type REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS; \ + type REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET; \ + type REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS; \ + type DCP_REGAMMA_MEM_PWR_STATE; \ + type REGAMMA_LUT_MEM_PWR_STATE; \ + type REGAMMA_LUT_WRITE_EN_MASK; \ + type GRPH_REGAMMA_MODE; \ + type SCL_MODE; \ + type SCL_BYPASS_MODE; \ + type SCL_PSCL_EN; \ + type SCL_H_NUM_OF_TAPS; \ + type SCL_V_NUM_OF_TAPS; \ + type SCL_BOUNDARY_MODE; \ + type EXT_OVERSCAN_LEFT; \ + type EXT_OVERSCAN_RIGHT; \ + type EXT_OVERSCAN_TOP; \ + type EXT_OVERSCAN_BOTTOM; \ + type SCL_COEFF_MEM_PWR_DIS; \ + type SCL_COEFF_MEM_PWR_STATE; \ + type SCL_C_RAM_FILTER_TYPE; \ + type SCL_C_RAM_PHASE; \ + type SCL_C_RAM_TAP_PAIR_IDX; \ + type SCL_C_RAM_EVEN_TAP_COEF_EN; \ + type SCL_C_RAM_EVEN_TAP_COEF; \ + type SCL_C_RAM_ODD_TAP_COEF_EN; \ + type SCL_C_RAM_ODD_TAP_COEF; \ + type VIEWPORT_X_START; \ + type VIEWPORT_Y_START; \ + type VIEWPORT_HEIGHT; \ + type VIEWPORT_WIDTH; \ + type SCL_H_SCALE_RATIO; \ + type SCL_V_SCALE_RATIO; \ + type SCL_H_INIT_INT; \ + type SCL_H_INIT_FRAC; \ + type SCL_V_INIT_INT; \ + type SCL_V_INIT_FRAC; \ + type LB_MEMORY_CONFIG; \ + type LB_MEMORY_SIZE; \ + type SCL_V_2TAP_HARDCODE_COEF_EN; \ + type SCL_H_2TAP_HARDCODE_COEF_EN; \ + type SCL_COEF_UPDATE_COMPLETE; \ + type ALPHA_EN + +struct dce_transform_shift { + XFM_REG_FIELD_LIST(uint8_t); +}; + +struct dce_transform_mask { + XFM_REG_FIELD_LIST(uint32_t); +}; + +struct dce_transform_registers { + uint32_t LB_DATA_FORMAT; + uint32_t GAMUT_REMAP_CONTROL; + uint32_t GAMUT_REMAP_C11_C12; + uint32_t GAMUT_REMAP_C13_C14; + uint32_t GAMUT_REMAP_C21_C22; + uint32_t GAMUT_REMAP_C23_C24; + uint32_t GAMUT_REMAP_C31_C32; + uint32_t GAMUT_REMAP_C33_C34; + uint32_t OUTPUT_CSC_C11_C12; + uint32_t OUTPUT_CSC_C13_C14; + uint32_t OUTPUT_CSC_C21_C22; + uint32_t OUTPUT_CSC_C23_C24; + uint32_t OUTPUT_CSC_C31_C32; + uint32_t OUTPUT_CSC_C33_C34; + uint32_t OUTPUT_CSC_CONTROL; + uint32_t DCFE_MEM_LIGHT_SLEEP_CNTL; + uint32_t REGAMMA_CNTLA_START_CNTL; + uint32_t REGAMMA_CNTLA_SLOPE_CNTL; + uint32_t REGAMMA_CNTLA_END_CNTL1; + uint32_t REGAMMA_CNTLA_END_CNTL2; + uint32_t REGAMMA_CNTLA_REGION_0_1; + uint32_t REGAMMA_CNTLA_REGION_2_3; + uint32_t REGAMMA_CNTLA_REGION_4_5; + uint32_t REGAMMA_CNTLA_REGION_6_7; + uint32_t REGAMMA_CNTLA_REGION_8_9; + uint32_t REGAMMA_CNTLA_REGION_10_11; + uint32_t REGAMMA_CNTLA_REGION_12_13; + uint32_t REGAMMA_CNTLA_REGION_14_15; + uint32_t REGAMMA_LUT_WRITE_EN_MASK; + uint32_t REGAMMA_LUT_INDEX; + uint32_t REGAMMA_LUT_DATA; + uint32_t REGAMMA_CONTROL; + uint32_t DENORM_CONTROL; + uint32_t DCP_SPATIAL_DITHER_CNTL; + uint32_t OUT_ROUND_CONTROL; + uint32_t OUT_CLAMP_CONTROL_R_CR; + uint32_t OUT_CLAMP_CONTROL_G_Y; + uint32_t OUT_CLAMP_CONTROL_B_CB; + uint32_t SCL_MODE; + uint32_t SCL_TAP_CONTROL; + uint32_t SCL_CONTROL; + uint32_t SCL_BYPASS_CONTROL; + uint32_t EXT_OVERSCAN_LEFT_RIGHT; + uint32_t EXT_OVERSCAN_TOP_BOTTOM; + uint32_t SCL_VERT_FILTER_CONTROL; + uint32_t SCL_HORZ_FILTER_CONTROL; + uint32_t DCFE_MEM_PWR_CTRL; + uint32_t DCFE_MEM_PWR_STATUS; + uint32_t SCL_COEF_RAM_SELECT; + uint32_t SCL_COEF_RAM_TAP_DATA; + uint32_t VIEWPORT_START; + uint32_t VIEWPORT_SIZE; + uint32_t SCL_HORZ_FILTER_SCALE_RATIO; + uint32_t SCL_VERT_FILTER_SCALE_RATIO; + uint32_t SCL_HORZ_FILTER_INIT; + uint32_t SCL_VERT_FILTER_INIT; + uint32_t SCL_AUTOMATIC_MODE_CONTROL; + uint32_t LB_MEMORY_CTRL; + uint32_t SCL_UPDATE; + uint32_t SCL_F_SHARP_CONTROL; +}; + +struct init_int_and_frac { + uint32_t integer; + uint32_t fraction; +}; + +struct scl_ratios_inits { + uint32_t h_int_scale_ratio; + uint32_t v_int_scale_ratio; + struct init_int_and_frac h_init; + struct init_int_and_frac v_init; +}; + +enum ram_filter_type { + FILTER_TYPE_RGB_Y_VERTICAL = 0, /* 0 - RGB/Y Vertical filter */ + FILTER_TYPE_CBCR_VERTICAL = 1, /* 1 - CbCr Vertical filter */ + FILTER_TYPE_RGB_Y_HORIZONTAL = 2, /* 1 - RGB/Y Horizontal filter */ + FILTER_TYPE_CBCR_HORIZONTAL = 3, /* 3 - CbCr Horizontal filter */ + FILTER_TYPE_ALPHA_VERTICAL = 4, /* 4 - Alpha Vertical filter. */ + FILTER_TYPE_ALPHA_HORIZONTAL = 5, /* 5 - Alpha Horizontal filter. */ +}; + +struct dce_transform { + struct transform base; + const struct dce_transform_registers *regs; + const struct dce_transform_shift *xfm_shift; + const struct dce_transform_mask *xfm_mask; + + const uint16_t *filter_v; + const uint16_t *filter_h; + const uint16_t *filter_v_c; + const uint16_t *filter_h_c; + int lb_pixel_depth_supported; + int lb_memory_size; + int lb_bits_per_entry; + bool prescaler_on; +}; + +void dce_transform_construct(struct dce_transform *xfm_dce, + struct dc_context *ctx, + uint32_t inst, + const struct dce_transform_registers *regs, + const struct dce_transform_shift *xfm_shift, + const struct dce_transform_mask *xfm_mask); + +bool dce_transform_get_optimal_number_of_taps( + struct transform *xfm, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps); + +void dce110_opp_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry); + +void dce110_opp_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust); + +/* REGAMMA RELATED */ +void dce110_opp_power_on_regamma_lut( + struct transform *xfm, + bool power_on); + +void dce110_opp_program_regamma_pwl( + struct transform *xfm, + const struct pwl_params *params); + +void dce110_opp_set_regamma_mode(struct transform *xfm, + enum opp_regamma mode); + +#endif /* _DCE_DCE_TRANSFORM_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce100/Makefile b/drivers/gpu/drm/amd/display/dc/dce100/Makefile new file mode 100644 index 000000000000..ea40870624b3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +DCE100 = dce100_resource.o dce100_hw_sequencer.o + +AMD_DAL_DCE100 = $(addprefix $(AMDDALPATH)/dc/dce100/,$(DCE100)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE100) + + +############################################################################### +# DCE 10x +############################################################################### +ifdef 0#CONFIG_DRM_AMD_DC_DCE11_0 +TG_DCE100 = dce100_resource.o + +AMD_DAL_TG_DCE100 = $(addprefix \ + $(AMDDALPATH)/dc/dce100/,$(TG_DCE100)) + +AMD_DISPLAY_FILES += $(AMD_DAL_TG_DCE100) +endif + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.c new file mode 100644 index 000000000000..e7a694835e3e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.c @@ -0,0 +1,152 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" +#include "dc.h" +#include "core_types.h" +#include "hw_sequencer.h" +#include "dce100_hw_sequencer.h" +#include "resource.h" + +#include "dce110/dce110_hw_sequencer.h" + +/* include DCE10 register header files */ +#include "dce/dce_10_0_d.h" +#include "dce/dce_10_0_sh_mask.h" + +struct dce100_hw_seq_reg_offsets { + uint32_t blnd; + uint32_t crtc; +}; + +static const struct dce100_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +/******************************************************************************* + * Private definitions + ******************************************************************************/ +/***************************PIPE_CONTROL***********************************/ + +static bool dce100_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (!(power_gating == PIPE_GATING_CONTROL_INIT && controller_id != 0)){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmMASTER_UPDATE_MODE, controller_id), + 0); + } + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +static void dce100_pplib_apply_display_requirements( + struct dc *dc, + struct dc_state *context) +{ + struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; + + pp_display_cfg->avail_mclk_switch_time_us = + dce110_get_min_vblank_time_us(context); + /*pp_display_cfg->min_memory_clock_khz = context->bw.dce.yclk_khz + / MEMORY_TYPE_MULTIPLIER;*/ + + dce110_fill_display_configs(context, pp_display_cfg); + + if (memcmp(&dc->prev_display_config, pp_display_cfg, sizeof( + struct dm_pp_display_configuration)) != 0) + dm_pp_apply_display_requirements(dc->ctx, pp_display_cfg); + + dc->prev_display_config = *pp_display_cfg; +} + +void dce100_set_bandwidth( + struct dc *dc, + struct dc_state *context, + bool decrease_allowed) +{ + if (decrease_allowed || context->bw.dce.dispclk_khz > dc->current_state->bw.dce.dispclk_khz) { + dc->res_pool->display_clock->funcs->set_clock( + dc->res_pool->display_clock, + context->bw.dce.dispclk_khz * 115 / 100); + dc->current_state->bw.dce.dispclk_khz = context->bw.dce.dispclk_khz; + } + dce100_pplib_apply_display_requirements(dc, context); +} + + +/**************************************************************************/ + +void dce100_hw_sequencer_construct(struct dc *dc) +{ + dce110_hw_sequencer_construct(dc); + + dc->hwss.enable_display_power_gating = dce100_enable_display_power_gating; + dc->hwss.set_bandwidth = dce100_set_bandwidth; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.h new file mode 100644 index 000000000000..cb5384ef46c3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.h @@ -0,0 +1,42 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HWSS_DCE100_H__ +#define __DC_HWSS_DCE100_H__ + +#include "core_types.h" + +struct dc; +struct dc_state; + +void dce100_hw_sequencer_construct(struct dc *dc); + +void dce100_set_bandwidth( + struct dc *dc, + struct dc_state *context, + bool decrease_allowed); + +#endif /* __DC_HWSS_DCE100_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c new file mode 100644 index 000000000000..90911258bdb3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c @@ -0,0 +1,933 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" + +#include "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "../virtual/virtual_stream_encoder.h" +#include "dce110/dce110_resource.h" +#include "dce110/dce110_timing_generator.h" +#include "irq/dce110/irq_service_dce110.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" + +#include "dce/dce_mem_input.h" +#include "dce/dce_ipp.h" +#include "dce/dce_transform.h" +#include "dce/dce_opp.h" +#include "dce/dce_clocks.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_audio.h" +#include "dce/dce_hwseq.h" +#include "dce100/dce100_hw_sequencer.h" + +#include "reg_helper.h" + +#include "dce/dce_10_0_d.h" +#include "dce/dce_10_0_sh_mask.h" + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_BS_SR_SWAP_CNTL + #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4BDC + #define mmDP2_DP_DPHY_BS_SR_SWAP_CNTL 0x4CDC + #define mmDP3_DP_DPHY_BS_SR_SWAP_CNTL 0x4DDC + #define mmDP4_DP_DPHY_BS_SR_SWAP_CNTL 0x4EDC + #define mmDP5_DP_DPHY_BS_SR_SWAP_CNTL 0x4FDC + #define mmDP6_DP_DPHY_BS_SR_SWAP_CNTL 0x54DC +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP0_DP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP1_DP_DPHY_FAST_TRAINING 0x4BBC + #define mmDP2_DP_DPHY_FAST_TRAINING 0x4CBC + #define mmDP3_DP_DPHY_FAST_TRAINING 0x4DBC + #define mmDP4_DP_DPHY_FAST_TRAINING 0x4EBC + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4FBC + #define mmDP6_DP_DPHY_FAST_TRAINING 0x54BC +#endif + +static const struct dce110_timing_generator_offsets dce100_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + + +static const struct dce_disp_clk_registers disp_clk_regs = { + CLK_COMMON_REG_LIST_DCE_BASE() +}; + +static const struct dce_disp_clk_shift disp_clk_shift = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_disp_clk_mask disp_clk_mask = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE100_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE100(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE100_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST_DCE_BASE(id),\ + .AFMT_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5), + stream_enc_regs(6) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE80_100(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE80_100(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_100_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_100(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_100(_MASK) +}; + + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_aduio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_100_110(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + + + +#define DCFE_MEM_PWR_CTRL_REG_BASE 0x1b03 + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 3 +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x1918 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce100_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce110_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct stream_encoder *dce100_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE10_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE10_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE10_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce100_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce100_stream_encoder_create, + .create_hwseq = dce100_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_DCE8_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE8_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE8_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + +static struct mem_input *dce100_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + dce_mi->wa.single_head_rdreq_dmif_limit = 2; + return &dce_mi->base; +} + +static void dce100_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce100_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + return &transform->base; +} + +static struct input_pixel_processor *dce100_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 300000, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_YCBCR_CAPABLE = true +}; + +struct link_encoder *dce100_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +struct output_pixel_processor *dce100_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +struct clock_source *dce100_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce100_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static void destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce100_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) + dce100_clock_source_destroy(&pool->base.clock_sources[i]); + } + + if (pool->base.dp_clock_source != NULL) + dce100_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) + dce_aud_destroy(&pool->base.audios[i]); + } + + if (pool->base.display_clock != NULL) + dce_disp_clk_destroy(&pool->base.display_clock); + + if (pool->base.irqs != NULL) + dal_irq_service_destroy(&pool->base.irqs); +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + dce110_resource_build_pipe_hw_param(pipe_ctx); + + resource_build_info_frame(pipe_ctx); + + return DC_OK; +} + +bool dce100_validate_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + /* TODO implement when needed but for now hardcode max value*/ + context->bw.dce.dispclk_khz = 681000; + context->bw.dce.yclk_khz = 250000 * MEMORY_TYPE_MULTIPLIER; + + return true; +} + +static bool dce100_validate_surface_sets( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 1) + return false; + + if (context->stream_status[i].plane_states[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + } + + return true; +} + +enum dc_status dce100_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce100_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +enum dc_status dce100_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + result = resource_map_pool_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = build_mapped_resource(dc, new_ctx, dc_stream); + + return result; +} + +enum dc_status dce100_validate_guaranteed( + struct dc *dc, + struct dc_stream_state *dc_stream, + struct dc_state *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + context->streams[0] = dc_stream; + dc_stream_retain(context->streams[0]); + context->stream_count++; + + result = resource_map_pool_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = build_mapped_resource(dc, context, dc_stream); + + if (result == DC_OK) { + validate_guaranteed_copy_streams( + context, dc->caps.max_streams); + result = resource_build_scaling_params_for_context(dc, context); + } + + if (result == DC_OK) + if (!dce100_validate_bandwidth(dc, context)) + result = DC_FAIL_BANDWIDTH_VALIDATE; + + return result; +} + +static void dce100_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +enum dc_status dce100_validate_plane(const struct dc_plane_state *plane_state, struct dc_caps *caps) +{ + + if (plane_state->format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return DC_OK; + + return DC_FAIL_SURFACE_VALIDATE; +} + +static const struct resource_funcs dce100_res_pool_funcs = { + .destroy = dce100_destroy_resource_pool, + .link_enc_create = dce100_link_encoder_create, + .validate_guaranteed = dce100_validate_guaranteed, + .validate_bandwidth = dce100_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce100_add_stream_to_ctx, + .validate_global = dce100_validate_global +}; + +static bool construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_firmware_info info; + struct dc_bios *bp; + struct dm_pp_static_clock_info static_clk_info = {0}; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce100_res_pool_funcs; + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + + bp = ctx->dc_bios; + + if ((bp->funcs->get_firmware_info(bp, &info) == BP_RESULT_OK) && + info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[2] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 3; + + } else { + pool->base.dp_clock_source = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.display_clock = dce_disp_clk_create(ctx, + &disp_clk_regs, + &disp_clk_shift, + &disp_clk_mask); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + + /* get static clock information for PPLIB or firmware, save + * max_clock_state + */ + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) + pool->base.display_clock->max_clks_state = + static_clk_info.max_clocks_state; + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce110_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 128; + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = + dce100_timing_generator_create( + ctx, + i, + &dce100_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce100_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce100_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce100_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce100_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce100_hw_sequencer_construct(dc); + return true; + +res_create_fail: + destruct(pool); + + return false; +} + +struct resource_pool *dce100_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.h b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.h new file mode 100644 index 000000000000..de8fdf438f9b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.h @@ -0,0 +1,26 @@ +/* + * dce100_resource.h + * + * Created on: 2016-01-20 + * Author: qyang + */ + +#ifndef DCE100_RESOURCE_H_ +#define DCE100_RESOURCE_H_ + +struct dc; +struct resource_pool; +struct dc_validation_set; + +struct resource_pool *dce100_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +enum dc_status dce100_validate_plane(const struct dc_plane_state *plane_state, struct dc_caps *caps); + +enum dc_status dce100_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream); + +#endif /* DCE100_RESOURCE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/Makefile b/drivers/gpu/drm/amd/display/dc/dce110/Makefile new file mode 100644 index 000000000000..98d956e2f218 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +DCE110 = dce110_timing_generator.o \ +dce110_compressor.o dce110_hw_sequencer.o dce110_resource.o \ +dce110_opp_regamma_v.o dce110_opp_csc_v.o dce110_timing_generator_v.o \ +dce110_mem_input_v.o dce110_opp_v.o dce110_transform_v.o + +AMD_DAL_DCE110 = $(addprefix $(AMDDALPATH)/dc/dce110/,$(DCE110)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE110) diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c new file mode 100644 index 000000000000..6923662413cd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c @@ -0,0 +1,522 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "gmc/gmc_8_2_sh_mask.h" +#include "gmc/gmc_8_2_d.h" + +#include "include/logger_interface.h" + +#include "dce110_compressor.h" + +#define DCP_REG(reg)\ + (reg + cp110->offsets.dcp_offset) +#define DMIF_REG(reg)\ + (reg + cp110->offsets.dmif_offset) + +static const struct dce110_compressor_reg_offsets reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG0_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG1_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG2_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +} +}; + +static const uint32_t dce11_one_lpt_channel_max_resolution = 2560 * 1600; + +enum fbc_idle_force { + /* Bit 0 - Display registers updated */ + FBC_IDLE_FORCE_DISPLAY_REGISTER_UPDATE = 0x00000001, + + /* Bit 2 - FBC_GRPH_COMP_EN register updated */ + FBC_IDLE_FORCE_GRPH_COMP_EN = 0x00000002, + /* Bit 3 - FBC_SRC_SEL register updated */ + FBC_IDLE_FORCE_SRC_SEL_CHANGE = 0x00000004, + /* Bit 4 - FBC_MIN_COMPRESSION register updated */ + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE = 0x00000008, + /* Bit 5 - FBC_ALPHA_COMP_EN register updated */ + FBC_IDLE_FORCE_ALPHA_COMP_EN = 0x00000010, + /* Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated */ + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN = 0x00000020, + /* Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated */ + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF = 0x00000040, + + /* Bit 24 - Memory write to region 0 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION0 = 0x01000000, + /* Bit 25 - Memory write to region 1 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION1 = 0x02000000, + /* Bit 26 - Memory write to region 2 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION2 = 0x04000000, + /* Bit 27 - Memory write to region 3 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION3 = 0x08000000, + + /* Bit 28 - Memory write from any client other than MCIF */ + FBC_IDLE_FORCE_MEMORY_WRITE_OTHER_THAN_MCIF = 0x10000000, + /* Bit 29 - CG statics screen signal is inactive */ + FBC_IDLE_FORCE_CG_STATIC_SCREEN_IS_INACTIVE = 0x20000000, +}; + + +static uint32_t align_to_chunks_number_per_line(uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +static void wait_for_fbc_state_changed( + struct dce110_compressor *cp110, + bool enabled) +{ + uint8_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 10) { + value = dm_read_reg(cp110->base.ctx, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + msleep(10); + counter++; + } + + if (counter == 10) { + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: wait counter exceeded, changes to HW not applied", + __func__); + } else { + dm_logger_write( + cp110->base.ctx->logger, LOG_SYNC, + "FBC status changed to %d", enabled); + } + + +} + +void dce110_compressor_power_up_fbc(struct compressor *compressor) +{ + uint32_t value; + uint32_t addr; + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); + set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); + if (compressor->options.bits.CLK_GATING_DISABLED == 1) { + /* HW needs to do power measurement comparison. */ + set_reg_field_value( + value, + 0, + FBC_CNTL, + FBC_COMP_CLK_GATE_EN); + } + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_MODE; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); + dm_write_reg(compressor->ctx, addr, value); + /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ + /* 1 ==> 4:1 */ + /* 2 ==> 8:1 */ + /* 0xF ==> 1:1 */ + set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); + dm_write_reg(compressor->ctx, addr, value); + compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; + + value = 0; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); + + value = 0xFFFFFF; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); +} + +void dce110_compressor_enable_fbc( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + (!dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL))) { + + uint32_t addr; + uint32_t value, misc_value; + + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value( + value, + params->inst, + FBC_CNTL, FBC_SRC_SEL); + dm_write_reg(compressor->ctx, addr, value); + + /* Keep track of enum controller_id FBC is attached to */ + compressor->is_enabled = true; + compressor->attached_inst = params->inst; + cp110->offsets = reg_offsets[params->inst]; + + /* Toggle it as there is bug in HW */ + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + /* FBC usage with scatter & gather for dce110 */ + misc_value = dm_read_reg(compressor->ctx, mmFBC_MISC); + + set_reg_field_value(misc_value, 1, + FBC_MISC, FBC_INVALIDATE_ON_ERROR); + set_reg_field_value(misc_value, 1, + FBC_MISC, FBC_DECOMPRESS_ERROR_CLEAR); + set_reg_field_value(misc_value, 0x14, + FBC_MISC, FBC_SLOW_REQ_INTERVAL); + + dm_write_reg(compressor->ctx, mmFBC_MISC, misc_value); + + /* Enable FBC */ + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + wait_for_fbc_state_changed(cp110, true); + } +} + +void dce110_compressor_disable_fbc(struct compressor *compressor) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL)) { + uint32_t reg_data; + /* Turn off compression */ + reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL); + set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data); + + /* Reset enum controller_id to undefined */ + compressor->attached_inst = 0; + compressor->is_enabled = false; + + wait_for_fbc_state_changed(cp110, false); + } +} + +bool dce110_compressor_is_fbc_enabled_in_hw( + struct compressor *compressor, + uint32_t *inst) +{ + /* Check the hardware register */ + uint32_t value; + + value = dm_read_reg(compressor->ctx, mmFBC_STATUS); + if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { + if (inst != NULL) + *inst = compressor->attached_inst; + return true; + } + + value = dm_read_reg(compressor->ctx, mmFBC_MISC); + if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { + value = dm_read_reg(compressor->ctx, mmFBC_CNTL); + + if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { + if (inst != NULL) + *inst = + compressor->attached_inst; + return true; + } + } + return false; +} + + +void dce110_compressor_program_compressed_surface_address_and_pitch( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + uint32_t value = 0; + uint32_t fbc_pitch = 0; + uint32_t compressed_surf_address_low_part = + compressor->compr_surface_address.addr.low_part; + + /* Clear content first. */ + dm_write_reg( + compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + 0); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), 0); + + /* Write address, HIGH has to be first. */ + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + compressor->compr_surface_address.addr.high_part); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), + compressed_surf_address_low_part); + + fbc_pitch = align_to_chunks_number_per_line(params->source_view_width); + + if (compressor->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) + fbc_pitch = fbc_pitch / 8; + else + dm_logger_write( + compressor->ctx->logger, LOG_WARNING, + "%s: Unexpected DCE11 compression ratio", + __func__); + + /* Clear content first. */ + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), 0); + + /* Write FBC Pitch. */ + set_reg_field_value( + value, + fbc_pitch, + GRPH_COMPRESS_PITCH, + GRPH_COMPRESS_PITCH); + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); + +} + +void dce110_compressor_set_fbc_invalidation_triggers( + struct compressor *compressor, + uint32_t fbc_trigger) +{ + /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) + * for DCE 11 regions cannot be used - does not work with S/G + */ + uint32_t addr = mmFBC_CLIENT_REGION_MASK; + uint32_t value = dm_read_reg(compressor->ctx, addr); + + set_reg_field_value( + value, + 0, + FBC_CLIENT_REGION_MASK, + FBC_MEMORY_REGION_MASK); + dm_write_reg(compressor->ctx, addr, value); + + /* Setup events when to clear all CSM entries (effectively marking + * current compressed data invalid) + * For DCE 11 CSM metadata 11111 means - "Not Compressed" + * Used as the initial value of the metadata sent to the compressor + * after invalidation, to indicate that the compressor should attempt + * to compress all chunks on the current pass. Also used when the chunk + * is not successfully written to memory. + * When this CSM value is detected, FBC reads from the uncompressed + * buffer. Set events according to passed in value, these events are + * valid for DCE11: + * - bit 0 - display register updated + * - bit 28 - memory write from any client except from MCIF + * - bit 29 - CG static screen signal is inactive + * In addition, DCE11.1 also needs to set new DCE11.1 specific events + * that are used to trigger invalidation on certain register changes, + * for example enabling of Alpha Compression may trigger invalidation of + * FBC once bit is set. These events are as follows: + * - Bit 2 - FBC_GRPH_COMP_EN register updated + * - Bit 3 - FBC_SRC_SEL register updated + * - Bit 4 - FBC_MIN_COMPRESSION register updated + * - Bit 5 - FBC_ALPHA_COMP_EN register updated + * - Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated + * - Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated + */ + addr = mmFBC_IDLE_FORCE_CLEAR_MASK; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + fbc_trigger | + FBC_IDLE_FORCE_GRPH_COMP_EN | + FBC_IDLE_FORCE_SRC_SEL_CHANGE | + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE | + FBC_IDLE_FORCE_ALPHA_COMP_EN | + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN | + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF, + FBC_IDLE_FORCE_CLEAR_MASK, + FBC_IDLE_FORCE_CLEAR_MASK); + dm_write_reg(compressor->ctx, addr, value); +} + +struct compressor *dce110_compressor_create(struct dc_context *ctx) +{ + struct dce110_compressor *cp110 = + kzalloc(sizeof(struct dce110_compressor), GFP_KERNEL); + + if (!cp110) + return NULL; + + dce110_compressor_construct(cp110, ctx); + return &cp110->base; +} + +void dce110_compressor_destroy(struct compressor **compressor) +{ + kfree(TO_DCE110_COMPRESSOR(*compressor)); + *compressor = NULL; +} + +bool dce110_get_required_compressed_surfacesize(struct fbc_input_info fbc_input_info, + struct fbc_requested_compressed_size size) +{ + bool result = false; + + unsigned int max_x = FBC_MAX_X, max_y = FBC_MAX_Y; + + get_max_support_fbc_buffersize(&max_x, &max_y); + + if (fbc_input_info.dynamic_fbc_buffer_alloc == 0) { + /* + * For DCE11 here use Max HW supported size: HW Support up to 3840x2400 resolution + * or 18000 chunks. + */ + size.preferred_size = size.min_size = align_to_chunks_number_per_line(max_x) * max_y * 4; /* (For FBC when LPT not supported). */ + size.preferred_size_alignment = size.min_size_alignment = 0x100; /* For FBC when LPT not supported */ + size.bits.preferred_must_be_framebuffer_pool = 1; + size.bits.min_must_be_framebuffer_pool = 1; + + result = true; + } + /* + * Maybe to add registry key support with optional size here to override above + * for debugging purposes + */ + + return result; +} + + +void get_max_support_fbc_buffersize(unsigned int *max_x, unsigned int *max_y) +{ + *max_x = FBC_MAX_X; + *max_y = FBC_MAX_Y; + + /* if (m_smallLocalFrameBufferMemory == 1) + * { + * *max_x = FBC_MAX_X_SG; + * *max_y = FBC_MAX_Y_SG; + * } + */ +} + + +unsigned int controller_id_to_index(enum controller_id controller_id) +{ + unsigned int index = 0; + + switch (controller_id) { + case CONTROLLER_ID_D0: + index = 0; + break; + case CONTROLLER_ID_D1: + index = 1; + break; + case CONTROLLER_ID_D2: + index = 2; + break; + case CONTROLLER_ID_D3: + index = 3; + break; + default: + break; + } + return index; +} + + +static const struct compressor_funcs dce110_compressor_funcs = { + .power_up_fbc = dce110_compressor_power_up_fbc, + .enable_fbc = dce110_compressor_enable_fbc, + .disable_fbc = dce110_compressor_disable_fbc, + .set_fbc_invalidation_triggers = dce110_compressor_set_fbc_invalidation_triggers, + .surface_address_and_pitch = dce110_compressor_program_compressed_surface_address_and_pitch, + .is_fbc_enabled_in_hw = dce110_compressor_is_fbc_enabled_in_hw +}; + + +void dce110_compressor_construct(struct dce110_compressor *compressor, + struct dc_context *ctx) +{ + + compressor->base.options.raw = 0; + compressor->base.options.bits.FBC_SUPPORT = true; + + /* for dce 11 always use one dram channel for lpt */ + compressor->base.lpt_channels_num = 1; + compressor->base.options.bits.DUMMY_BACKEND = false; + + /* + * check if this system has more than 1 dram channel; if only 1 then lpt + * should not be supported + */ + + + compressor->base.options.bits.CLK_GATING_DISABLED = false; + + compressor->base.ctx = ctx; + compressor->base.embedded_panel_h_size = 0; + compressor->base.embedded_panel_v_size = 0; + compressor->base.memory_bus_width = ctx->asic_id.vram_width; + compressor->base.allocated_size = 0; + compressor->base.preferred_requested_size = 0; + compressor->base.min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; + compressor->base.banks_num = 0; + compressor->base.raw_size = 0; + compressor->base.channel_interleave_size = 0; + compressor->base.dram_channels_num = 0; + compressor->base.lpt_channels_num = 0; + compressor->base.attached_inst = 0; + compressor->base.is_enabled = false; +#if defined(CONFIG_DRM_AMD_DC_FBC) + compressor->base.funcs = &dce110_compressor_funcs; + +#endif +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h new file mode 100644 index 000000000000..26c7335a1cbf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h @@ -0,0 +1,81 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_COMPRESSOR_DCE110_H__ +#define __DC_COMPRESSOR_DCE110_H__ + +#include "../inc/compressor.h" + +#define TO_DCE110_COMPRESSOR(compressor)\ + container_of(compressor, struct dce110_compressor, base) + +struct dce110_compressor_reg_offsets { + uint32_t dcp_offset; + uint32_t dmif_offset; +}; + +struct dce110_compressor { + struct compressor base; + struct dce110_compressor_reg_offsets offsets; +}; + +struct compressor *dce110_compressor_create(struct dc_context *ctx); + +void dce110_compressor_construct(struct dce110_compressor *cp110, + struct dc_context *ctx); + +void dce110_compressor_destroy(struct compressor **cp); + +/* FBC RELATED */ +void dce110_compressor_power_up_fbc(struct compressor *cp); + +void dce110_compressor_enable_fbc(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +void dce110_compressor_disable_fbc(struct compressor *cp); + +void dce110_compressor_set_fbc_invalidation_triggers(struct compressor *cp, + uint32_t fbc_trigger); + +void dce110_compressor_program_compressed_surface_address_and_pitch( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce110_compressor_is_fbc_enabled_in_hw(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); + +/* LPT RELATED */ +void dce110_compressor_enable_lpt(struct compressor *cp); + +void dce110_compressor_disable_lpt(struct compressor *cp); + +void dce110_compressor_program_lpt_control(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce110_compressor_is_lpt_enabled_in_hw(struct compressor *cp); + +void get_max_support_fbc_buffersize(unsigned int *max_x, unsigned int *max_y); + +#endif + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c new file mode 100644 index 000000000000..1229a3315018 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -0,0 +1,2987 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" +#include "dc.h" +#include "dc_bios_types.h" +#include "core_types.h" +#include "core_status.h" +#include "resource.h" +#include "dm_helpers.h" +#include "dce110_hw_sequencer.h" +#include "dce110_timing_generator.h" +#include "dce/dce_hwseq.h" +#include "gpio_service_interface.h" + +#if defined(CONFIG_DRM_AMD_DC_FBC) +#include "dce110_compressor.h" +#endif + +#include "bios/bios_parser_helper.h" +#include "timing_generator.h" +#include "mem_input.h" +#include "opp.h" +#include "ipp.h" +#include "transform.h" +#include "stream_encoder.h" +#include "link_encoder.h" +#include "link_hwss.h" +#include "clock_source.h" +#include "abm.h" +#include "audio.h" +#include "reg_helper.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "custom_float.h" + +/* + * All values are in milliseconds; + * For eDP, after power-up/power/down, + * 300/500 msec max. delay from LCDVCC to black video generation + */ +#define PANEL_POWER_UP_TIMEOUT 300 +#define PANEL_POWER_DOWN_TIMEOUT 500 +#define HPD_CHECK_INTERVAL 10 + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +struct dce110_hw_seq_reg_offsets { + uint32_t crtc; +}; + +static const struct dce110_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTCV_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_BLND(reg, id)\ + (reg + reg_offsets[id].blnd) + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +#define MAX_WATERMARK 0xFFFF +#define SAFE_NBP_MARK 0x7FFF + +/******************************************************************************* + * Private definitions + ******************************************************************************/ +/***************************PIPE_CONTROL***********************************/ +static void dce110_init_pte(struct dc_context *ctx) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; + + addr = mmUNP_DVMM_PTE_CONTROL; + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + + dm_write_reg(ctx, addr, value); + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} +/**************************************************************************/ + +static void enable_display_pipe_clock_gating( + struct dc_context *ctx, + bool clock_gating) +{ + /*TODO*/ +} + +static bool dce110_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (controller_id == underlay_idx) + controller_id = CONTROLLER_ID_UNDERLAY0 - 1; + + if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + * + * Bios parser accepts controller_id = 6 as indicative of + * underlay pipe in dce110. But we do not support more + * than 3. + */ + if (controller_id < CONTROLLER_ID_MAX - 1) + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce110_init_pte(ctx); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +static void build_prescale_params(struct ipp_prescale_params *prescale_params, + const struct dc_plane_state *plane_state) +{ + prescale_params->mode = IPP_PRESCALE_MODE_FIXED_UNSIGNED; + + switch (plane_state->format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + prescale_params->scale = 0x2020; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + prescale_params->scale = 0x2008; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + prescale_params->scale = 0x2000; + break; + default: + ASSERT(false); + break; + } +} + +static bool dce110_set_input_transfer_func( + struct pipe_ctx *pipe_ctx, + const struct dc_plane_state *plane_state) +{ + struct input_pixel_processor *ipp = pipe_ctx->plane_res.ipp; + const struct dc_transfer_func *tf = NULL; + struct ipp_prescale_params prescale_params = { 0 }; + bool result = true; + + if (ipp == NULL) + return false; + + if (plane_state->in_transfer_func) + tf = plane_state->in_transfer_func; + + build_prescale_params(&prescale_params, plane_state); + ipp->funcs->ipp_program_prescale(ipp, &prescale_params); + + if (plane_state->gamma_correction && dce_use_lut(plane_state)) + ipp->funcs->ipp_program_input_lut(ipp, plane_state->gamma_correction); + + if (tf == NULL) { + /* Default case if no input transfer function specified */ + ipp->funcs->ipp_set_degamma(ipp, + IPP_DEGAMMA_MODE_HW_sRGB); + } else if (tf->type == TF_TYPE_PREDEFINED) { + switch (tf->tf) { + case TRANSFER_FUNCTION_SRGB: + ipp->funcs->ipp_set_degamma(ipp, + IPP_DEGAMMA_MODE_HW_sRGB); + break; + case TRANSFER_FUNCTION_BT709: + ipp->funcs->ipp_set_degamma(ipp, + IPP_DEGAMMA_MODE_HW_xvYCC); + break; + case TRANSFER_FUNCTION_LINEAR: + ipp->funcs->ipp_set_degamma(ipp, + IPP_DEGAMMA_MODE_BYPASS); + break; + case TRANSFER_FUNCTION_PQ: + result = false; + break; + default: + result = false; + break; + } + } else if (tf->type == TF_TYPE_BYPASS) { + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_BYPASS); + } else { + /*TF_TYPE_DISTRIBUTED_POINTS - Not supported in DCE 11*/ + result = false; + } + + return result; +} + +static bool convert_to_custom_float( + struct pwl_result_data *rgb_resulted, + struct curve_points *arr_points, + uint32_t hw_points_num) +{ + struct custom_float_format fmt; + + struct pwl_result_data *rgb = rgb_resulted; + + uint32_t i = 0; + + fmt.exponenta_bits = 6; + fmt.mantissa_bits = 12; + fmt.sign = true; + + if (!convert_to_custom_float_format( + arr_points[0].x, + &fmt, + &arr_points[0].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[0].offset, + &fmt, + &arr_points[0].custom_float_offset)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[0].slope, + &fmt, + &arr_points[0].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 10; + fmt.sign = false; + + if (!convert_to_custom_float_format( + arr_points[1].x, + &fmt, + &arr_points[1].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[1].y, + &fmt, + &arr_points[1].custom_float_y)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[2].slope, + &fmt, + &arr_points[2].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 12; + fmt.sign = true; + + while (i != hw_points_num) { + if (!convert_to_custom_float_format( + rgb->red, + &fmt, + &rgb->red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->green, + &fmt, + &rgb->green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->blue, + &fmt, + &rgb->blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_red, + &fmt, + &rgb->delta_red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_green, + &fmt, + &rgb->delta_green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_blue, + &fmt, + &rgb->delta_blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + ++rgb; + ++i; + } + + return true; +} + +static bool dce110_translate_regamma_to_hw_format(const struct dc_transfer_func + *output_tf, struct pwl_params *regamma_params) +{ + struct curve_points *arr_points; + struct pwl_result_data *rgb_resulted; + struct pwl_result_data *rgb; + struct pwl_result_data *rgb_plus_1; + struct fixed31_32 y_r; + struct fixed31_32 y_g; + struct fixed31_32 y_b; + struct fixed31_32 y1_min; + struct fixed31_32 y3_max; + + int32_t segment_start, segment_end; + uint32_t i, j, k, seg_distr[16], increment, start_index, hw_points; + + if (output_tf == NULL || regamma_params == NULL || + output_tf->type == TF_TYPE_BYPASS) + return false; + + arr_points = regamma_params->arr_points; + rgb_resulted = regamma_params->rgb_resulted; + hw_points = 0; + + memset(regamma_params, 0, sizeof(struct pwl_params)); + + if (output_tf->tf == TRANSFER_FUNCTION_PQ) { + /* 16 segments + * segments are from 2^-11 to 2^5 + */ + segment_start = -11; + segment_end = 5; + + seg_distr[0] = 2; + seg_distr[1] = 2; + seg_distr[2] = 2; + seg_distr[3] = 2; + seg_distr[4] = 2; + seg_distr[5] = 2; + seg_distr[6] = 3; + seg_distr[7] = 4; + seg_distr[8] = 4; + seg_distr[9] = 4; + seg_distr[10] = 4; + seg_distr[11] = 5; + seg_distr[12] = 5; + seg_distr[13] = 5; + seg_distr[14] = 5; + seg_distr[15] = 5; + + } else { + /* 10 segments + * segment is from 2^-10 to 2^0 + */ + segment_start = -10; + segment_end = 0; + + seg_distr[0] = 3; + seg_distr[1] = 4; + seg_distr[2] = 4; + seg_distr[3] = 4; + seg_distr[4] = 4; + seg_distr[5] = 4; + seg_distr[6] = 4; + seg_distr[7] = 4; + seg_distr[8] = 5; + seg_distr[9] = 5; + seg_distr[10] = -1; + seg_distr[11] = -1; + seg_distr[12] = -1; + seg_distr[13] = -1; + seg_distr[14] = -1; + seg_distr[15] = -1; + } + + for (k = 0; k < 16; k++) { + if (seg_distr[k] != -1) + hw_points += (1 << seg_distr[k]); + } + + j = 0; + for (k = 0; k < (segment_end - segment_start); k++) { + increment = 32 / (1 << seg_distr[k]); + start_index = (segment_start + k + 25) * 32; + for (i = start_index; i < start_index + 32; i += increment) { + if (j == hw_points - 1) + break; + rgb_resulted[j].red = output_tf->tf_pts.red[i]; + rgb_resulted[j].green = output_tf->tf_pts.green[i]; + rgb_resulted[j].blue = output_tf->tf_pts.blue[i]; + j++; + } + } + + /* last point */ + start_index = (segment_end + 25) * 32; + rgb_resulted[hw_points - 1].red = + output_tf->tf_pts.red[start_index]; + rgb_resulted[hw_points - 1].green = + output_tf->tf_pts.green[start_index]; + rgb_resulted[hw_points - 1].blue = + output_tf->tf_pts.blue[start_index]; + + arr_points[0].x = dal_fixed31_32_pow(dal_fixed31_32_from_int(2), + dal_fixed31_32_from_int(segment_start)); + arr_points[1].x = dal_fixed31_32_pow(dal_fixed31_32_from_int(2), + dal_fixed31_32_from_int(segment_end)); + arr_points[2].x = dal_fixed31_32_pow(dal_fixed31_32_from_int(2), + dal_fixed31_32_from_int(segment_end)); + + y_r = rgb_resulted[0].red; + y_g = rgb_resulted[0].green; + y_b = rgb_resulted[0].blue; + + y1_min = dal_fixed31_32_min(y_r, dal_fixed31_32_min(y_g, y_b)); + + arr_points[0].y = y1_min; + arr_points[0].slope = dal_fixed31_32_div( + arr_points[0].y, + arr_points[0].x); + + y_r = rgb_resulted[hw_points - 1].red; + y_g = rgb_resulted[hw_points - 1].green; + y_b = rgb_resulted[hw_points - 1].blue; + + /* see comment above, m_arrPoints[1].y should be the Y value for the + * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1) + */ + y3_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); + + arr_points[1].y = y3_max; + arr_points[2].y = y3_max; + + arr_points[1].slope = dal_fixed31_32_zero; + arr_points[2].slope = dal_fixed31_32_zero; + + if (output_tf->tf == TRANSFER_FUNCTION_PQ) { + /* for PQ, we want to have a straight line from last HW X point, + * and the slope to be such that we hit 1.0 at 10000 nits. + */ + const struct fixed31_32 end_value = + dal_fixed31_32_from_int(125); + + arr_points[1].slope = dal_fixed31_32_div( + dal_fixed31_32_sub(dal_fixed31_32_one, arr_points[1].y), + dal_fixed31_32_sub(end_value, arr_points[1].x)); + arr_points[2].slope = dal_fixed31_32_div( + dal_fixed31_32_sub(dal_fixed31_32_one, arr_points[1].y), + dal_fixed31_32_sub(end_value, arr_points[1].x)); + } + + regamma_params->hw_points_num = hw_points; + + i = 1; + for (k = 0; k < 16 && i < 16; k++) { + if (seg_distr[k] != -1) { + regamma_params->arr_curve_points[k].segments_num = + seg_distr[k]; + regamma_params->arr_curve_points[i].offset = + regamma_params->arr_curve_points[k]. + offset + (1 << seg_distr[k]); + } + i++; + } + + if (seg_distr[k] != -1) + regamma_params->arr_curve_points[k].segments_num = + seg_distr[k]; + + rgb = rgb_resulted; + rgb_plus_1 = rgb_resulted + 1; + + i = 1; + + while (i != hw_points + 1) { + if (dal_fixed31_32_lt(rgb_plus_1->red, rgb->red)) + rgb_plus_1->red = rgb->red; + if (dal_fixed31_32_lt(rgb_plus_1->green, rgb->green)) + rgb_plus_1->green = rgb->green; + if (dal_fixed31_32_lt(rgb_plus_1->blue, rgb->blue)) + rgb_plus_1->blue = rgb->blue; + + rgb->delta_red = dal_fixed31_32_sub( + rgb_plus_1->red, + rgb->red); + rgb->delta_green = dal_fixed31_32_sub( + rgb_plus_1->green, + rgb->green); + rgb->delta_blue = dal_fixed31_32_sub( + rgb_plus_1->blue, + rgb->blue); + + ++rgb_plus_1; + ++rgb; + ++i; + } + + convert_to_custom_float(rgb_resulted, arr_points, hw_points); + + return true; +} + +static bool dce110_set_output_transfer_func( + struct pipe_ctx *pipe_ctx, + const struct dc_stream_state *stream) +{ + struct transform *xfm = pipe_ctx->plane_res.xfm; + + xfm->funcs->opp_power_on_regamma_lut(xfm, true); + xfm->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM; + + if (stream->out_transfer_func && + stream->out_transfer_func->type == + TF_TYPE_PREDEFINED && + stream->out_transfer_func->tf == + TRANSFER_FUNCTION_SRGB) { + xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_SRGB); + } else if (dce110_translate_regamma_to_hw_format( + stream->out_transfer_func, &xfm->regamma_params)) { + xfm->funcs->opp_program_regamma_pwl(xfm, &xfm->regamma_params); + xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_USER); + } else { + xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_BYPASS); + } + + xfm->funcs->opp_power_on_regamma_lut(xfm, false); + + return true; +} + +static enum dc_status bios_parser_crtc_source_select( + struct pipe_ctx *pipe_ctx) +{ + struct dc_bios *dcb; + /* call VBIOS table to set CRTC source for the HW + * encoder block + * note: video bios clears all FMT setting here. */ + struct bp_crtc_source_select crtc_source_select = {0}; + const struct dc_sink *sink = pipe_ctx->stream->sink; + + crtc_source_select.engine_id = pipe_ctx->stream_res.stream_enc->id; + crtc_source_select.controller_id = pipe_ctx->pipe_idx + 1; + /*TODO: Need to un-hardcode color depth, dp_audio and account for + * the case where signal and sink signal is different (translator + * encoder)*/ + crtc_source_select.signal = pipe_ctx->stream->signal; + crtc_source_select.enable_dp_audio = false; + crtc_source_select.sink_signal = pipe_ctx->stream->signal; + + switch (pipe_ctx->stream->timing.display_color_depth) { + case COLOR_DEPTH_666: + crtc_source_select.display_output_bit_depth = PANEL_6BIT_COLOR; + break; + case COLOR_DEPTH_888: + crtc_source_select.display_output_bit_depth = PANEL_8BIT_COLOR; + break; + case COLOR_DEPTH_101010: + crtc_source_select.display_output_bit_depth = PANEL_10BIT_COLOR; + break; + case COLOR_DEPTH_121212: + crtc_source_select.display_output_bit_depth = PANEL_12BIT_COLOR; + break; + default: + BREAK_TO_DEBUGGER(); + crtc_source_select.display_output_bit_depth = PANEL_8BIT_COLOR; + break; + } + + dcb = sink->ctx->dc_bios; + + if (BP_RESULT_OK != dcb->funcs->crtc_source_select( + dcb, + &crtc_source_select)) { + return DC_ERROR_UNEXPECTED; + } + + return DC_OK; +} + +void dce110_update_info_frame(struct pipe_ctx *pipe_ctx) +{ + ASSERT(pipe_ctx->stream); + + if (pipe_ctx->stream_res.stream_enc == NULL) + return; /* this is not root pipe */ + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets( + pipe_ctx->stream_res.stream_enc, + &pipe_ctx->stream_res.encoder_info_frame); + else if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets( + pipe_ctx->stream_res.stream_enc, + &pipe_ctx->stream_res.encoder_info_frame); +} + +void dce110_enable_stream(struct pipe_ctx *pipe_ctx) +{ + enum dc_lane_count lane_count = + pipe_ctx->stream->sink->link->cur_link_settings.lane_count; + + struct dc_crtc_timing *timing = &pipe_ctx->stream->timing; + struct dc_link *link = pipe_ctx->stream->sink->link; + + /* 1. update AVI info frame (HDMI, DP) + * we always need to update info frame + */ + uint32_t active_total_with_borders; + uint32_t early_control = 0; + struct timing_generator *tg = pipe_ctx->stream_res.tg; + + /* TODOFPGA may change to hwss.update_info_frame */ + dce110_update_info_frame(pipe_ctx); + /* enable early control to avoid corruption on DP monitor*/ + active_total_with_borders = + timing->h_addressable + + timing->h_border_left + + timing->h_border_right; + + if (lane_count != 0) + early_control = active_total_with_borders % lane_count; + + if (early_control == 0) + early_control = lane_count; + + tg->funcs->set_early_control(tg, early_control); + + /* enable audio only within mode set */ + if (pipe_ctx->stream_res.audio != NULL) { + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dp_audio_enable(pipe_ctx->stream_res.stream_enc); + } + + /* For MST, there are multiply stream go to only one link. + * connect DIG back_end to front_end while enable_stream and + * disconnect them during disable_stream + * BY this, it is logic clean to separate stream and link */ + link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc, + pipe_ctx->stream_res.stream_enc->id, true); + +} + +/*todo: cloned in stream enc, fix*/ +static bool is_panel_backlight_on(struct dce_hwseq *hws) +{ + uint32_t value; + + REG_GET(LVTMA_PWRSEQ_CNTL, LVTMA_BLON, &value); + + return value; +} + +static bool is_panel_powered_on(struct dce_hwseq *hws) +{ + uint32_t value; + + REG_GET(LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, &value); + return value == 1; +} + +static enum bp_result link_transmitter_control( + struct dc_bios *bios, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + + result = bios->funcs->transmitter_control(bios, cntl); + + return result; +} + +/* + * @brief + * eDP only. + */ +void hwss_edp_wait_for_hpd_ready( + struct link_encoder *enc, + bool power_up) +{ + struct dc_context *ctx = enc->ctx; + struct graphics_object_id connector = enc->connector; + struct gpio *hpd; + bool edp_hpd_high = false; + uint32_t time_elapsed = 0; + uint32_t timeout = power_up ? + PANEL_POWER_UP_TIMEOUT : PANEL_POWER_DOWN_TIMEOUT; + + if (dal_graphics_object_id_get_connector_id(connector) + != CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (!power_up) + /* + * From KV, we will not HPD low after turning off VCC - + * instead, we will check the SW timer in power_up(). + */ + return; + + /* + * When we power on/off the eDP panel, + * we need to wait until SENSE bit is high/low. + */ + + /* obtain HPD */ + /* TODO what to do with this? */ + hpd = get_hpd_gpio(ctx->dc_bios, connector, ctx->gpio_service); + + if (!hpd) { + BREAK_TO_DEBUGGER(); + return; + } + + dal_gpio_open(hpd, GPIO_MODE_INTERRUPT); + + /* wait until timeout or panel detected */ + + do { + uint32_t detected = 0; + + dal_gpio_get_value(hpd, &detected); + + if (!(detected ^ power_up)) { + edp_hpd_high = true; + break; + } + + msleep(HPD_CHECK_INTERVAL); + + time_elapsed += HPD_CHECK_INTERVAL; + } while (time_elapsed < timeout); + + dal_gpio_close(hpd); + + dal_gpio_destroy_irq(&hpd); + + if (false == edp_hpd_high) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: wait timed out!\n", __func__); + } +} + +void hwss_edp_power_control( + struct link_encoder *enc, + bool power_up) +{ + struct dc_context *ctx = enc->ctx; + struct dce_hwseq *hwseq = ctx->dc->hwseq; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result bp_result; + + + if (dal_graphics_object_id_get_connector_id(enc->connector) + != CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (power_up != is_panel_powered_on(hwseq)) { + /* Send VBIOS command to prompt eDP panel power */ + + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: Panel Power action: %s\n", + __func__, (power_up ? "On":"Off")); + + cntl.action = power_up ? + TRANSMITTER_CONTROL_POWER_ON : + TRANSMITTER_CONTROL_POWER_OFF; + cntl.transmitter = enc->transmitter; + cntl.connector_obj_id = enc->connector; + cntl.coherent = false; + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.hpd_sel = enc->hpd_source; + + bp_result = link_transmitter_control(ctx->dc_bios, &cntl); + + if (bp_result != BP_RESULT_OK) + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Panel Power bp_result: %d\n", + __func__, bp_result); + } else { + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: Skipping Panel Power action: %s\n", + __func__, (power_up ? "On":"Off")); + } + + hwss_edp_wait_for_hpd_ready(enc, true); +} + +/*todo: cloned in stream enc, fix*/ +/* + * @brief + * eDP only. Control the backlight of the eDP panel + */ +void hwss_edp_backlight_control( + struct dc_link *link, + bool enable) +{ + struct dce_hwseq *hws = link->dc->hwseq; + struct dc_context *ctx = link->dc->ctx; + struct bp_transmitter_control cntl = { 0 }; + + if (dal_graphics_object_id_get_connector_id(link->link_id) + != CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (enable && is_panel_backlight_on(hws)) { + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: panel already powered up. Do nothing.\n", + __func__); + return; + } + + /* Send VBIOS command to control eDP panel backlight */ + + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: backlight action: %s\n", + __func__, (enable ? "On":"Off")); + + cntl.action = enable ? + TRANSMITTER_CONTROL_BACKLIGHT_ON : + TRANSMITTER_CONTROL_BACKLIGHT_OFF; + + /*cntl.engine_id = ctx->engine;*/ + cntl.transmitter = link->link_enc->transmitter; + cntl.connector_obj_id = link->link_enc->connector; + /*todo: unhardcode*/ + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.hpd_sel = link->link_enc->hpd_source; + + /* For eDP, the following delays might need to be considered + * after link training completed: + * idle period - min. accounts for required BS-Idle pattern, + * max. allows for source frame synchronization); + * 50 msec max. delay from valid video data from source + * to video on dislpay or backlight enable. + * + * Disable the delay for now. + * Enable it in the future if necessary. + */ + /* dc_service_sleep_in_milliseconds(50); */ + link_transmitter_control(link->dc->ctx->dc_bios, &cntl); +} + +void dce110_disable_stream(struct pipe_ctx *pipe_ctx, int option) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->sink->link; + struct dc *dc = pipe_ctx->stream->ctx->dc; + + if (pipe_ctx->stream_res.audio) { + pipe_ctx->stream_res.audio->funcs->az_disable(pipe_ctx->stream_res.audio); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dp_audio_disable( + pipe_ctx->stream_res.stream_enc); + else + pipe_ctx->stream_res.stream_enc->funcs->hdmi_audio_disable( + pipe_ctx->stream_res.stream_enc); + /*don't free audio if it is from retrain or internal disable stream*/ + if (option == FREE_ACQUIRED_RESOURCE && dc->caps.dynamic_audio == true) { + /*we have to dynamic arbitrate the audio endpoints*/ + pipe_ctx->stream_res.audio = NULL; + /*we free the resource, need reset is_audio_acquired*/ + update_audio_usage(&dc->current_state->res_ctx, dc->res_pool, pipe_ctx->stream_res.audio, false); + } + + /* TODO: notify audio driver for if audio modes list changed + * add audio mode list change flag */ + /* dal_audio_disable_azalia_audio_jack_presence(stream->audio, + * stream->stream_engine_id); + */ + } + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->stop_hdmi_info_packets( + pipe_ctx->stream_res.stream_enc); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->stop_dp_info_packets( + pipe_ctx->stream_res.stream_enc); + + pipe_ctx->stream_res.stream_enc->funcs->audio_mute_control( + pipe_ctx->stream_res.stream_enc, true); + + + /* blank at encoder level */ + if (dc_is_dp_signal(pipe_ctx->stream->signal)) { + if (pipe_ctx->stream->sink->link->connector_signal == SIGNAL_TYPE_EDP) + hwss_edp_backlight_control(link, false); + pipe_ctx->stream_res.stream_enc->funcs->dp_blank(pipe_ctx->stream_res.stream_enc); + } + link->link_enc->funcs->connect_dig_be_to_fe( + link->link_enc, + pipe_ctx->stream_res.stream_enc->id, + false); + +} + +void dce110_unblank_stream(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings) +{ + struct encoder_unblank_param params = { { 0 } }; + struct dc_link *link = pipe_ctx->stream->sink->link; + + /* only 3 items below are used by unblank */ + params.pixel_clk_khz = + pipe_ctx->stream->timing.pix_clk_khz; + params.link_settings.link_rate = link_settings->link_rate; + pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(pipe_ctx->stream_res.stream_enc, ¶ms); + if (link->connector_signal == SIGNAL_TYPE_EDP) + hwss_edp_backlight_control(link, true); +} + + +void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) +{ + if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL) + pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable); +} + +static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) +{ + switch (crtc_id) { + case CONTROLLER_ID_D0: + return DTO_SOURCE_ID0; + case CONTROLLER_ID_D1: + return DTO_SOURCE_ID1; + case CONTROLLER_ID_D2: + return DTO_SOURCE_ID2; + case CONTROLLER_ID_D3: + return DTO_SOURCE_ID3; + case CONTROLLER_ID_D4: + return DTO_SOURCE_ID4; + case CONTROLLER_ID_D5: + return DTO_SOURCE_ID5; + default: + return DTO_SOURCE_UNKNOWN; + } +} + +static void build_audio_output( + struct dc_state *state, + const struct pipe_ctx *pipe_ctx, + struct audio_output *audio_output) +{ + const struct dc_stream_state *stream = pipe_ctx->stream; + audio_output->engine_id = pipe_ctx->stream_res.stream_enc->id; + + audio_output->signal = pipe_ctx->stream->signal; + + /* audio_crtc_info */ + + audio_output->crtc_info.h_total = + stream->timing.h_total; + + /* + * Audio packets are sent during actual CRTC blank physical signal, we + * need to specify actual active signal portion + */ + audio_output->crtc_info.h_active = + stream->timing.h_addressable + + stream->timing.h_border_left + + stream->timing.h_border_right; + + audio_output->crtc_info.v_active = + stream->timing.v_addressable + + stream->timing.v_border_top + + stream->timing.v_border_bottom; + + audio_output->crtc_info.pixel_repetition = 1; + + audio_output->crtc_info.interlaced = + stream->timing.flags.INTERLACE; + + audio_output->crtc_info.refresh_rate = + (stream->timing.pix_clk_khz*1000)/ + (stream->timing.h_total*stream->timing.v_total); + + audio_output->crtc_info.color_depth = + stream->timing.display_color_depth; + + audio_output->crtc_info.requested_pixel_clock = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk; + + audio_output->crtc_info.calculated_pixel_clock = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk; + +/*for HDMI, audio ACR is with deep color ratio factor*/ + if (dc_is_hdmi_signal(pipe_ctx->stream->signal) && + audio_output->crtc_info.requested_pixel_clock == + stream->timing.pix_clk_khz) { + if (pipe_ctx->stream_res.pix_clk_params.pixel_encoding == PIXEL_ENCODING_YCBCR420) { + audio_output->crtc_info.requested_pixel_clock = + audio_output->crtc_info.requested_pixel_clock/2; + audio_output->crtc_info.calculated_pixel_clock = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk/2; + + } + } + + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT || + pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + audio_output->pll_info.dp_dto_source_clock_in_khz = + state->dis_clk->funcs->get_dp_ref_clk_frequency( + state->dis_clk); + } + + audio_output->pll_info.feed_back_divider = + pipe_ctx->pll_settings.feedback_divider; + + audio_output->pll_info.dto_source = + translate_to_dto_source( + pipe_ctx->pipe_idx + 1); + + /* TODO hard code to enable for now. Need get from stream */ + audio_output->pll_info.ss_enabled = true; + + audio_output->pll_info.ss_percentage = + pipe_ctx->pll_settings.ss_percentage; +} + +static void get_surface_visual_confirm_color(const struct pipe_ctx *pipe_ctx, + struct tg_color *color) +{ + uint32_t color_value = MAX_TG_COLOR_VALUE * (4 - pipe_ctx->pipe_idx) / 4; + + switch (pipe_ctx->plane_res.scl_data.format) { + case PIXEL_FORMAT_ARGB8888: + /* set boarder color to red */ + color->color_r_cr = color_value; + break; + + case PIXEL_FORMAT_ARGB2101010: + /* set boarder color to blue */ + color->color_b_cb = color_value; + break; + case PIXEL_FORMAT_420BPP8: + /* set boarder color to green */ + color->color_g_y = color_value; + break; + case PIXEL_FORMAT_420BPP10: + /* set boarder color to yellow */ + color->color_g_y = color_value; + color->color_r_cr = color_value; + break; + case PIXEL_FORMAT_FP16: + /* set boarder color to white */ + color->color_r_cr = color_value; + color->color_b_cb = color_value; + color->color_g_y = color_value; + break; + default: + break; + } +} + +static void program_scaler(const struct dc *dc, + const struct pipe_ctx *pipe_ctx) +{ + struct tg_color color = {0}; + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + /* TOFPGA */ + if (pipe_ctx->plane_res.xfm->funcs->transform_set_pixel_storage_depth == NULL) + return; +#endif + + if (dc->debug.surface_visual_confirm) + get_surface_visual_confirm_color(pipe_ctx, &color); + else + color_space_to_black_color(dc, + pipe_ctx->stream->output_color_space, + &color); + + pipe_ctx->plane_res.xfm->funcs->transform_set_pixel_storage_depth( + pipe_ctx->plane_res.xfm, + pipe_ctx->plane_res.scl_data.lb_params.depth, + &pipe_ctx->stream->bit_depth_params); + + if (pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color) + pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color( + pipe_ctx->stream_res.tg, + &color); + + pipe_ctx->plane_res.xfm->funcs->transform_set_scaler(pipe_ctx->plane_res.xfm, + &pipe_ctx->plane_res.scl_data); +} + +static enum dc_status dce110_prog_pixclk_crtc_otg( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct pipe_ctx *pipe_ctx_old = &dc->current_state->res_ctx. + pipe_ctx[pipe_ctx->pipe_idx]; + struct tg_color black_color = {0}; + + if (!pipe_ctx_old->stream) { + + /* program blank color */ + color_space_to_black_color(dc, + stream->output_color_space, &black_color); + pipe_ctx->stream_res.tg->funcs->set_blank_color( + pipe_ctx->stream_res.tg, + &black_color); + + /* + * Must blank CRTC after disabling power gating and before any + * programming, otherwise CRTC will be hung in bad state + */ + pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true); + + if (false == pipe_ctx->clock_source->funcs->program_pix_clk( + pipe_ctx->clock_source, + &pipe_ctx->stream_res.pix_clk_params, + &pipe_ctx->pll_settings)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + pipe_ctx->stream_res.tg->funcs->program_timing( + pipe_ctx->stream_res.tg, + &stream->timing, + true); + + pipe_ctx->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx->stream_res.tg, + 0x182); + } + + if (!pipe_ctx_old->stream) { + if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc( + pipe_ctx->stream_res.tg)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + } + + + + return DC_OK; +} + +static enum dc_status apply_single_controller_ctx_to_hw( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct pipe_ctx *pipe_ctx_old = &dc->current_state->res_ctx. + pipe_ctx[pipe_ctx->pipe_idx]; + + /* */ + dc->hwss.prog_pixclk_crtc_otg(pipe_ctx, context, dc); + + /* FPGA does not program backend */ + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + pipe_ctx->stream_res.opp->funcs->opp_set_dyn_expansion( + pipe_ctx->stream_res.opp, + COLOR_SPACE_YCBCR601, + stream->timing.display_color_depth, + pipe_ctx->stream->signal); + + pipe_ctx->stream_res.opp->funcs->opp_program_fmt( + pipe_ctx->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + return DC_OK; + } + /* TODO: move to stream encoder */ + if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) + if (DC_OK != bios_parser_crtc_source_select(pipe_ctx)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + pipe_ctx->stream_res.opp->funcs->opp_set_dyn_expansion( + pipe_ctx->stream_res.opp, + COLOR_SPACE_YCBCR601, + stream->timing.display_color_depth, + pipe_ctx->stream->signal); + + if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) + stream->sink->link->link_enc->funcs->setup( + stream->sink->link->link_enc, + pipe_ctx->stream->signal); + + if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) + pipe_ctx->stream_res.stream_enc->funcs->setup_stereo_sync( + pipe_ctx->stream_res.stream_enc, + pipe_ctx->stream_res.tg->inst, + stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE); + + + pipe_ctx->stream_res.opp->funcs->opp_program_fmt( + pipe_ctx->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dp_set_stream_attribute( + pipe_ctx->stream_res.stream_enc, + &stream->timing, + stream->output_color_space); + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->hdmi_set_stream_attribute( + pipe_ctx->stream_res.stream_enc, + &stream->timing, + stream->phy_pix_clk, + pipe_ctx->stream_res.audio != NULL); + + if (dc_is_dvi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dvi_set_stream_attribute( + pipe_ctx->stream_res.stream_enc, + &stream->timing, + (pipe_ctx->stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? + true : false); + + resource_build_info_frame(pipe_ctx); + dce110_update_info_frame(pipe_ctx); + if (!pipe_ctx_old->stream) { + if (!pipe_ctx->stream->dpms_off) + core_link_enable_stream(context, pipe_ctx); + } + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; + + pipe_ctx->stream->sink->link->psr_enabled = false; + + return DC_OK; +} + +/******************************************************************************/ + +static void power_down_encoders(struct dc *dc) +{ + int i; + enum connector_id connector_id; + enum signal_type signal = SIGNAL_TYPE_NONE; + + /* do not know BIOS back-front mapping, simply blank all. It will not + * hurt for non-DP + */ + for (i = 0; i < dc->res_pool->stream_enc_count; i++) { + dc->res_pool->stream_enc[i]->funcs->dp_blank( + dc->res_pool->stream_enc[i]); + } + + for (i = 0; i < dc->link_count; i++) { + connector_id = dal_graphics_object_id_get_connector_id(dc->links[i]->link_id); + if ((connector_id == CONNECTOR_ID_DISPLAY_PORT) || + (connector_id == CONNECTOR_ID_EDP)) { + + if (!dc->links[i]->wa_flags.dp_keep_receiver_powered) + dp_receiver_power_ctrl(dc->links[i], false); + if (connector_id == CONNECTOR_ID_EDP) + signal = SIGNAL_TYPE_EDP; + } + + dc->links[i]->link_enc->funcs->disable_output( + dc->links[i]->link_enc, signal, dc->links[i]); + } +} + +static void power_down_controllers(struct dc *dc) +{ + int i; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + dc->res_pool->timing_generators[i]->funcs->disable_crtc( + dc->res_pool->timing_generators[i]); + } +} + +static void power_down_clock_sources(struct dc *dc) +{ + int i; + + if (dc->res_pool->dp_clock_source->funcs->cs_power_down( + dc->res_pool->dp_clock_source) == false) + dm_error("Failed to power down pll! (dp clk src)\n"); + + for (i = 0; i < dc->res_pool->clk_src_count; i++) { + if (dc->res_pool->clock_sources[i]->funcs->cs_power_down( + dc->res_pool->clock_sources[i]) == false) + dm_error("Failed to power down pll! (clk src index=%d)\n", i); + } +} + +static void power_down_all_hw_blocks(struct dc *dc) +{ + power_down_encoders(dc); + + power_down_controllers(dc); + + power_down_clock_sources(dc); + +#if defined(CONFIG_DRM_AMD_DC_FBC) + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor); +#endif +} + +static void disable_vga_and_power_gate_all_controllers( + struct dc *dc) +{ + int i; + struct timing_generator *tg; + struct dc_context *ctx = dc->ctx; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + tg = dc->res_pool->timing_generators[i]; + + if (tg->funcs->disable_vga) + tg->funcs->disable_vga(tg); + + /* Enable CLOCK gating for each pipe BEFORE controller + * powergating. */ + enable_display_pipe_clock_gating(ctx, + true); + + dc->hwss.power_down_front_end(dc, i); + } +} + +/** + * When ASIC goes from VBIOS/VGA mode to driver/accelerated mode we need: + * 1. Power down all DC HW blocks + * 2. Disable VGA engine on all controllers + * 3. Enable power gating for controller + * 4. Set acc_mode_change bit (VBIOS will clear this bit when going to FSDOS) + */ +void dce110_enable_accelerated_mode(struct dc *dc) +{ + power_down_all_hw_blocks(dc); + + disable_vga_and_power_gate_all_controllers(dc); + bios_set_scratch_acc_mode_change(dc->ctx->dc_bios); +} + +static uint32_t compute_pstate_blackout_duration( + struct bw_fixed blackout_duration, + const struct dc_stream_state *stream) +{ + uint32_t total_dest_line_time_ns; + uint32_t pstate_blackout_duration_ns; + + pstate_blackout_duration_ns = 1000 * blackout_duration.value >> 24; + + total_dest_line_time_ns = 1000000UL * + stream->timing.h_total / + stream->timing.pix_clk_khz + + pstate_blackout_duration_ns; + + return total_dest_line_time_ns; +} + +void dce110_set_displaymarks( + const struct dc *dc, + struct dc_state *context) +{ + uint8_t i, num_pipes; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + for (i = 0, num_pipes = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + uint32_t total_dest_line_time_ns; + + if (pipe_ctx->stream == NULL) + continue; + + total_dest_line_time_ns = compute_pstate_blackout_duration( + dc->bw_vbios->blackout_duration, pipe_ctx->stream); + pipe_ctx->plane_res.mi->funcs->mem_input_program_display_marks( + pipe_ctx->plane_res.mi, + context->bw.dce.nbp_state_change_wm_ns[num_pipes], + context->bw.dce.stutter_exit_wm_ns[num_pipes], + context->bw.dce.urgent_wm_ns[num_pipes], + total_dest_line_time_ns); + if (i == underlay_idx) { + num_pipes++; + pipe_ctx->plane_res.mi->funcs->mem_input_program_chroma_display_marks( + pipe_ctx->plane_res.mi, + context->bw.dce.nbp_state_change_wm_ns[num_pipes], + context->bw.dce.stutter_exit_wm_ns[num_pipes], + context->bw.dce.urgent_wm_ns[num_pipes], + total_dest_line_time_ns); + } + num_pipes++; + } +} + +static void set_safe_displaymarks( + struct resource_context *res_ctx, + const struct resource_pool *pool) +{ + int i; + int underlay_idx = pool->underlay_pipe_index; + struct dce_watermarks max_marks = { + MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK }; + struct dce_watermarks nbp_marks = { + SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK }; + + for (i = 0; i < MAX_PIPES; i++) { + if (res_ctx->pipe_ctx[i].stream == NULL || res_ctx->pipe_ctx[i].plane_res.mi == NULL) + continue; + + res_ctx->pipe_ctx[i].plane_res.mi->funcs->mem_input_program_display_marks( + res_ctx->pipe_ctx[i].plane_res.mi, + nbp_marks, + max_marks, + max_marks, + MAX_WATERMARK); + + if (i == underlay_idx) + res_ctx->pipe_ctx[i].plane_res.mi->funcs->mem_input_program_chroma_display_marks( + res_ctx->pipe_ctx[i].plane_res.mi, + nbp_marks, + max_marks, + max_marks, + MAX_WATERMARK); + + } +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ + +static void set_drr(struct pipe_ctx **pipe_ctx, + int num_pipes, int vmin, int vmax) +{ + int i = 0; + struct drr_params params = {0}; + + params.vertical_total_max = vmax; + params.vertical_total_min = vmin; + + /* TODO: If multiple pipes are to be supported, you need + * some GSL stuff + */ + + for (i = 0; i < num_pipes; i++) { + pipe_ctx[i]->stream_res.tg->funcs->set_drr(pipe_ctx[i]->stream_res.tg, ¶ms); + } +} + +static void get_position(struct pipe_ctx **pipe_ctx, + int num_pipes, + struct crtc_position *position) +{ + int i = 0; + + /* TODO: handle pipes > 1 + */ + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->stream_res.tg->funcs->get_position(pipe_ctx[i]->stream_res.tg, position); +} + +static void set_static_screen_control(struct pipe_ctx **pipe_ctx, + int num_pipes, const struct dc_static_screen_events *events) +{ + unsigned int i; + unsigned int value = 0; + + if (events->overlay_update) + value |= 0x100; + if (events->surface_update) + value |= 0x80; + if (events->cursor_update) + value |= 0x2; + +#if defined(CONFIG_DRM_AMD_DC_FBC) + value |= 0x84; +#endif + + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->stream_res.tg->funcs-> + set_static_screen_control(pipe_ctx[i]->stream_res.tg, value); +} + +/* unit: in_khz before mode set, get pixel clock from context. ASIC register + * may not be programmed yet. + * TODO: after mode set, pre_mode_set = false, + * may read PLL register to get pixel clock + */ +static uint32_t get_max_pixel_clock_for_all_paths( + struct dc *dc, + struct dc_state *context, + bool pre_mode_set) +{ + uint32_t max_pix_clk = 0; + int i; + + if (!pre_mode_set) { + /* TODO: read ASIC register to get pixel clock */ + ASSERT(0); + } + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + /* do not check under lay */ + if (pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->stream_res.pix_clk_params.requested_pix_clk > max_pix_clk) + max_pix_clk = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk; + } + + if (max_pix_clk == 0) + ASSERT(0); + + return max_pix_clk; +} + +/* + * Find clock state based on clock requested. if clock value is 0, simply + * set clock state as requested without finding clock state by clock value + */ + +static void apply_min_clocks( + struct dc *dc, + struct dc_state *context, + enum dm_pp_clocks_state *clocks_state, + bool pre_mode_set) +{ + struct state_dependent_clocks req_clocks = {0}; + + if (!pre_mode_set) { + /* set clock_state without verification */ + if (context->dis_clk->funcs->set_min_clocks_state) { + context->dis_clk->funcs->set_min_clocks_state( + context->dis_clk, *clocks_state); + return; + } + + /* TODO: This is incorrect. Figure out how to fix. */ + context->dis_clk->funcs->apply_clock_voltage_request( + context->dis_clk, + DM_PP_CLOCK_TYPE_DISPLAY_CLK, + context->dis_clk->cur_clocks_value.dispclk_in_khz, + pre_mode_set, + false); + + context->dis_clk->funcs->apply_clock_voltage_request( + context->dis_clk, + DM_PP_CLOCK_TYPE_PIXELCLK, + context->dis_clk->cur_clocks_value.max_pixelclk_in_khz, + pre_mode_set, + false); + + context->dis_clk->funcs->apply_clock_voltage_request( + context->dis_clk, + DM_PP_CLOCK_TYPE_DISPLAYPHYCLK, + context->dis_clk->cur_clocks_value.max_non_dp_phyclk_in_khz, + pre_mode_set, + false); + return; + } + + /* get the required state based on state dependent clocks: + * display clock and pixel clock + */ + req_clocks.display_clk_khz = context->bw.dce.dispclk_khz; + + req_clocks.pixel_clk_khz = get_max_pixel_clock_for_all_paths( + dc, context, true); + + if (context->dis_clk->funcs->get_required_clocks_state) { + *clocks_state = context->dis_clk->funcs->get_required_clocks_state( + context->dis_clk, &req_clocks); + context->dis_clk->funcs->set_min_clocks_state( + context->dis_clk, *clocks_state); + } else { + context->dis_clk->funcs->apply_clock_voltage_request( + context->dis_clk, + DM_PP_CLOCK_TYPE_DISPLAY_CLK, + req_clocks.display_clk_khz, + pre_mode_set, + false); + + context->dis_clk->funcs->apply_clock_voltage_request( + context->dis_clk, + DM_PP_CLOCK_TYPE_PIXELCLK, + req_clocks.pixel_clk_khz, + pre_mode_set, + false); + + context->dis_clk->funcs->apply_clock_voltage_request( + context->dis_clk, + DM_PP_CLOCK_TYPE_DISPLAYPHYCLK, + req_clocks.pixel_clk_khz, + pre_mode_set, + false); + } +} + +#if defined(CONFIG_DRM_AMD_DC_FBC) + +/* + * Check if FBC can be enabled + */ +static enum dc_status validate_fbc(struct dc *dc, + struct dc_state *context) +{ + struct pipe_ctx *pipe_ctx = + &context->res_ctx.pipe_ctx[0]; + + ASSERT(dc->fbc_compressor); + + /* FBC memory should be allocated */ + if (!dc->ctx->fbc_gpu_addr) + return DC_ERROR_UNEXPECTED; + + /* Only supports single display */ + if (context->stream_count != 1) + return DC_ERROR_UNEXPECTED; + + /* Only supports eDP */ + if (pipe_ctx->stream->sink->link->connector_signal != SIGNAL_TYPE_EDP) + return DC_ERROR_UNEXPECTED; + + /* PSR should not be enabled */ + if (pipe_ctx->stream->sink->link->psr_enabled) + return DC_ERROR_UNEXPECTED; + + /* Only for non-linear tiling */ + if (pipe_ctx->plane_state->tiling_info.gfx8.array_mode == DC_ARRAY_LINEAR_GENERAL) + return DC_ERROR_UNEXPECTED; + + return DC_OK; +} + +/* + * Enable FBC + */ +static enum dc_status enable_fbc(struct dc *dc, + struct dc_state *context) +{ + enum dc_status status = validate_fbc(dc, context); + + if (status == DC_OK) { + /* Program GRPH COMPRESSED ADDRESS and PITCH */ + struct compr_addr_and_pitch_params params = {0, 0, 0}; + struct compressor *compr = dc->fbc_compressor; + struct pipe_ctx *pipe_ctx = + &context->res_ctx.pipe_ctx[0]; + + params.source_view_width = + pipe_ctx->stream->timing.h_addressable; + params.source_view_height = + pipe_ctx->stream->timing.v_addressable; + + compr->compr_surface_address.quad_part = dc->ctx->fbc_gpu_addr; + + compr->funcs->surface_address_and_pitch(compr, ¶ms); + compr->funcs->set_fbc_invalidation_triggers(compr, 1); + + compr->funcs->enable_fbc(compr, ¶ms); + } + return status; +} +#endif + +static enum dc_status apply_ctx_to_hw_fpga( + struct dc *dc, + struct dc_state *context) +{ + enum dc_status status = DC_ERROR_UNEXPECTED; + int i; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) + continue; + + status = apply_single_controller_ctx_to_hw( + pipe_ctx, + context, + dc); + + if (status != DC_OK) + return status; + } + + return DC_OK; +} + +static void dce110_reset_hw_ctx_wrap( + struct dc *dc, + struct dc_state *context) +{ + int i; + + /* Reset old context */ + /* look up the targets that have been removed since last commit */ + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + /* Note: We need to disable output if clock sources change, + * since bios does optimization and doesn't apply if changing + * PHY when not already disabled. + */ + + /* Skip underlay pipe since it will be handled in commit surface*/ + if (!pipe_ctx_old->stream || pipe_ctx_old->top_pipe) + continue; + + if (!pipe_ctx->stream || + pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { + struct clock_source *old_clk = pipe_ctx_old->clock_source; + + /* disable already, no need to disable again */ + if (pipe_ctx->stream && !pipe_ctx->stream->dpms_off) + core_link_disable_stream(pipe_ctx_old, FREE_ACQUIRED_RESOURCE); + + pipe_ctx_old->stream_res.tg->funcs->set_blank(pipe_ctx_old->stream_res.tg, true); + if (!hwss_wait_for_blank_complete(pipe_ctx_old->stream_res.tg)) { + dm_error("DC: failed to blank crtc!\n"); + BREAK_TO_DEBUGGER(); + } + pipe_ctx_old->stream_res.tg->funcs->disable_crtc(pipe_ctx_old->stream_res.tg); + pipe_ctx_old->plane_res.mi->funcs->free_mem_input( + pipe_ctx_old->plane_res.mi, dc->current_state->stream_count); + + if (old_clk) + old_clk->funcs->cs_power_down(old_clk); + + dc->hwss.power_down_front_end(dc, pipe_ctx_old->pipe_idx); + + pipe_ctx_old->stream = NULL; + } + } +} + + +enum dc_status dce110_apply_ctx_to_hw( + struct dc *dc, + struct dc_state *context) +{ + struct dc_bios *dcb = dc->ctx->dc_bios; + enum dc_status status; + int i; + enum dm_pp_clocks_state clocks_state = DM_PP_CLOCKS_STATE_INVALID; + + /* Reset old context */ + /* look up the targets that have been removed since last commit */ + dc->hwss.reset_hw_ctx_wrap(dc, context); + + /* Skip applying if no targets */ + if (context->stream_count <= 0) + return DC_OK; + + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + apply_ctx_to_hw_fpga(dc, context); + return DC_OK; + } + + /* Apply new context */ + dcb->funcs->set_scratch_critical_state(dcb, true); + + /* below is for real asic only */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL || pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) { + if (pipe_ctx_old->clock_source != pipe_ctx->clock_source) + dce_crtc_switch_to_clk_src(dc->hwseq, + pipe_ctx->clock_source, i); + continue; + } + + dc->hwss.enable_display_power_gating( + dc, i, dc->ctx->dc_bios, + PIPE_GATING_CONTROL_DISABLE); + } + + set_safe_displaymarks(&context->res_ctx, dc->res_pool); + +#if defined(CONFIG_DRM_AMD_DC_FBC) + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor); +#endif + /*TODO: when pplib works*/ + apply_min_clocks(dc, context, &clocks_state, true); + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + if (dc->ctx->dce_version >= DCN_VERSION_1_0) { + if (context->bw.dcn.calc_clk.fclk_khz + > dc->current_state->bw.dcn.cur_clk.fclk_khz) { + struct dm_pp_clock_for_voltage_req clock; + + clock.clk_type = DM_PP_CLOCK_TYPE_FCLK; + clock.clocks_in_khz = context->bw.dcn.calc_clk.fclk_khz; + dm_pp_apply_clock_for_voltage_request(dc->ctx, &clock); + dc->current_state->bw.dcn.cur_clk.fclk_khz = clock.clocks_in_khz; + context->bw.dcn.cur_clk.fclk_khz = clock.clocks_in_khz; + } + if (context->bw.dcn.calc_clk.dcfclk_khz + > dc->current_state->bw.dcn.cur_clk.dcfclk_khz) { + struct dm_pp_clock_for_voltage_req clock; + + clock.clk_type = DM_PP_CLOCK_TYPE_DCFCLK; + clock.clocks_in_khz = context->bw.dcn.calc_clk.dcfclk_khz; + dm_pp_apply_clock_for_voltage_request(dc->ctx, &clock); + dc->current_state->bw.dcn.cur_clk.dcfclk_khz = clock.clocks_in_khz; + context->bw.dcn.cur_clk.dcfclk_khz = clock.clocks_in_khz; + } + if (context->bw.dcn.calc_clk.dispclk_khz + > dc->current_state->bw.dcn.cur_clk.dispclk_khz) { + dc->res_pool->display_clock->funcs->set_clock( + dc->res_pool->display_clock, + context->bw.dcn.calc_clk.dispclk_khz); + dc->current_state->bw.dcn.cur_clk.dispclk_khz = + context->bw.dcn.calc_clk.dispclk_khz; + context->bw.dcn.cur_clk.dispclk_khz = + context->bw.dcn.calc_clk.dispclk_khz; + } + } else +#endif + if (context->bw.dce.dispclk_khz + > dc->current_state->bw.dce.dispclk_khz) { + dc->res_pool->display_clock->funcs->set_clock( + dc->res_pool->display_clock, + context->bw.dce.dispclk_khz * 115 / 100); + } + /* program audio wall clock. use HDMI as clock source if HDMI + * audio active. Otherwise, use DP as clock source + * first, loop to find any HDMI audio, if not, loop find DP audio + */ + /* Setup audio rate clock source */ + /* Issue: + * Audio lag happened on DP monitor when unplug a HDMI monitor + * + * Cause: + * In case of DP and HDMI connected or HDMI only, DCCG_AUDIO_DTO_SEL + * is set to either dto0 or dto1, audio should work fine. + * In case of DP connected only, DCCG_AUDIO_DTO_SEL should be dto1, + * set to dto0 will cause audio lag. + * + * Solution: + * Not optimized audio wall dto setup. When mode set, iterate pipe_ctx, + * find first available pipe with audio, setup audio wall DTO per topology + * instead of per pipe. + */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A) + continue; + + if (pipe_ctx->stream_res.audio != NULL) { + struct audio_output audio_output; + + build_audio_output(context, pipe_ctx, &audio_output); + + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + break; + } + } + + /* no HDMI audio is found, try DP audio */ + if (i == dc->res_pool->pipe_count) { + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->top_pipe) + continue; + + if (!dc_is_dp_signal(pipe_ctx->stream->signal)) + continue; + + if (pipe_ctx->stream_res.audio != NULL) { + struct audio_output audio_output; + + build_audio_output(context, pipe_ctx, &audio_output); + + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + break; + } + } + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) + continue; + + if (pipe_ctx->stream && pipe_ctx_old->stream + && !pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + continue; + + if (pipe_ctx->top_pipe) + continue; + + if (context->res_ctx.pipe_ctx[i].stream_res.audio != NULL) { + + struct audio_output audio_output; + + build_audio_output(context, pipe_ctx, &audio_output); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dp_audio_setup( + pipe_ctx->stream_res.stream_enc, + pipe_ctx->stream_res.audio->inst, + &pipe_ctx->stream->audio_info); + else + pipe_ctx->stream_res.stream_enc->funcs->hdmi_audio_setup( + pipe_ctx->stream_res.stream_enc, + pipe_ctx->stream_res.audio->inst, + &pipe_ctx->stream->audio_info, + &audio_output.crtc_info); + + pipe_ctx->stream_res.audio->funcs->az_configure( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &pipe_ctx->stream->audio_info); + } + + status = apply_single_controller_ctx_to_hw( + pipe_ctx, + context, + dc); + + if (dc->hwss.power_on_front_end) + dc->hwss.power_on_front_end(dc, pipe_ctx, context); + + if (DC_OK != status) + return status; + } + + /* pplib is notified if disp_num changed */ + dc->hwss.set_bandwidth(dc, context, true); + + /* to save power */ + apply_min_clocks(dc, context, &clocks_state, false); + + dcb->funcs->set_scratch_critical_state(dcb, false); + +#if defined(CONFIG_DRM_AMD_DC_FBC) + if (dc->fbc_compressor) + enable_fbc(dc, context); + +#endif + + return DC_OK; +} + +/******************************************************************************* + * Front End programming + ******************************************************************************/ +static void set_default_colors(struct pipe_ctx *pipe_ctx) +{ + struct default_adjustment default_adjust = { 0 }; + + default_adjust.force_hw_default = false; + if (pipe_ctx->plane_state == NULL) + default_adjust.in_color_space = COLOR_SPACE_SRGB; + else + default_adjust.in_color_space = + pipe_ctx->plane_state->color_space; + if (pipe_ctx->stream == NULL) + default_adjust.out_color_space = COLOR_SPACE_SRGB; + else + default_adjust.out_color_space = + pipe_ctx->stream->output_color_space; + default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW; + default_adjust.surface_pixel_format = pipe_ctx->plane_res.scl_data.format; + + /* display color depth */ + default_adjust.color_depth = + pipe_ctx->stream->timing.display_color_depth; + + /* Lb color depth */ + default_adjust.lb_color_depth = pipe_ctx->plane_res.scl_data.lb_params.depth; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_default( + pipe_ctx->plane_res.xfm, &default_adjust); +} + + +/******************************************************************************* + * In order to turn on/off specific surface we will program + * Blender + CRTC + * + * In case that we have two surfaces and they have a different visibility + * we can't turn off the CRTC since it will turn off the entire display + * + * |----------------------------------------------- | + * |bottom pipe|curr pipe | | | + * |Surface |Surface | Blender | CRCT | + * |visibility |visibility | Configuration| | + * |------------------------------------------------| + * | off | off | CURRENT_PIPE | blank | + * | off | on | CURRENT_PIPE | unblank | + * | on | off | OTHER_PIPE | unblank | + * | on | on | BLENDING | unblank | + * -------------------------------------------------| + * + ******************************************************************************/ +static void program_surface_visibility(const struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + enum blnd_mode blender_mode = BLND_MODE_CURRENT_PIPE; + bool blank_target = false; + + if (pipe_ctx->bottom_pipe) { + + /* For now we are supporting only two pipes */ + ASSERT(pipe_ctx->bottom_pipe->bottom_pipe == NULL); + + if (pipe_ctx->bottom_pipe->plane_state->visible) { + if (pipe_ctx->plane_state->visible) + blender_mode = BLND_MODE_BLENDING; + else + blender_mode = BLND_MODE_OTHER_PIPE; + + } else if (!pipe_ctx->plane_state->visible) + blank_target = true; + + } else if (!pipe_ctx->plane_state->visible) + blank_target = true; + + dce_set_blender_mode(dc->hwseq, pipe_ctx->pipe_idx, blender_mode); + pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, blank_target); + +} + +static void program_gamut_remap(struct pipe_ctx *pipe_ctx) +{ + struct xfm_grph_csc_adjustment adjust; + memset(&adjust, 0, sizeof(adjust)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + + if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + adjust.temperature_matrix[0] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[0]; + adjust.temperature_matrix[1] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[1]; + adjust.temperature_matrix[2] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[2]; + adjust.temperature_matrix[3] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[4]; + adjust.temperature_matrix[4] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[5]; + adjust.temperature_matrix[5] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[6]; + adjust.temperature_matrix[6] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[8]; + adjust.temperature_matrix[7] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[9]; + adjust.temperature_matrix[8] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[10]; + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust); +} + +/** + * TODO REMOVE, USE UPDATE INSTEAD + */ +static void set_plane_config( + const struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct resource_context *res_ctx) +{ + struct mem_input *mi = pipe_ctx->plane_res.mi; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct xfm_grph_csc_adjustment adjust; + struct out_csc_color_matrix tbl_entry; + unsigned int i; + + memset(&adjust, 0, sizeof(adjust)); + memset(&tbl_entry, 0, sizeof(tbl_entry)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + dce_enable_fe_clock(dc->hwseq, pipe_ctx->pipe_idx, true); + + set_default_colors(pipe_ctx); + if (pipe_ctx->stream->csc_color_matrix.enable_adjustment + == true) { + tbl_entry.color_space = + pipe_ctx->stream->output_color_space; + + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = + pipe_ctx->stream->csc_color_matrix.matrix[i]; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment + (pipe_ctx->plane_res.xfm, &tbl_entry); + } + + if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + adjust.temperature_matrix[0] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[0]; + adjust.temperature_matrix[1] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[1]; + adjust.temperature_matrix[2] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[2]; + adjust.temperature_matrix[3] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[4]; + adjust.temperature_matrix[4] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[5]; + adjust.temperature_matrix[5] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[6]; + adjust.temperature_matrix[6] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[8]; + adjust.temperature_matrix[7] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[9]; + adjust.temperature_matrix[8] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[10]; + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust); + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; + program_scaler(dc, pipe_ctx); + + program_surface_visibility(dc, pipe_ctx); + + mi->funcs->mem_input_program_surface_config( + mi, + plane_state->format, + &plane_state->tiling_info, + &plane_state->plane_size, + plane_state->rotation, + NULL, + false); + if (mi->funcs->set_blank) + mi->funcs->set_blank(mi, pipe_ctx->plane_state->visible); + + if (dc->config.gpu_vm_support) + mi->funcs->mem_input_program_pte_vm( + pipe_ctx->plane_res.mi, + plane_state->format, + &plane_state->tiling_info, + plane_state->rotation); +} + +static void update_plane_addr(const struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + + if (plane_state == NULL) + return; + + pipe_ctx->plane_res.mi->funcs->mem_input_program_surface_flip_and_addr( + pipe_ctx->plane_res.mi, + &plane_state->address, + plane_state->flip_immediate); + + plane_state->status.requested_address = plane_state->address; +} + +void dce110_update_pending_status(struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + + if (plane_state == NULL) + return; + + plane_state->status.is_flip_pending = + pipe_ctx->plane_res.mi->funcs->mem_input_is_flip_pending( + pipe_ctx->plane_res.mi); + + if (plane_state->status.is_flip_pending && !plane_state->visible) + pipe_ctx->plane_res.mi->current_address = pipe_ctx->plane_res.mi->request_address; + + plane_state->status.current_address = pipe_ctx->plane_res.mi->current_address; + if (pipe_ctx->plane_res.mi->current_address.type == PLN_ADDR_TYPE_GRPH_STEREO && + pipe_ctx->stream_res.tg->funcs->is_stereo_left_eye) { + plane_state->status.is_right_eye =\ + !pipe_ctx->stream_res.tg->funcs->is_stereo_left_eye(pipe_ctx->stream_res.tg); + } +} + +void dce110_power_down(struct dc *dc) +{ + power_down_all_hw_blocks(dc); + disable_vga_and_power_gate_all_controllers(dc); +} + +static bool wait_for_reset_trigger_to_occur( + struct dc_context *dc_ctx, + struct timing_generator *tg) +{ + bool rc = false; + + /* To avoid endless loop we wait at most + * frames_to_wait_on_triggered_reset frames for the reset to occur. */ + const uint32_t frames_to_wait_on_triggered_reset = 10; + uint32_t i; + + for (i = 0; i < frames_to_wait_on_triggered_reset; i++) { + + if (!tg->funcs->is_counter_moving(tg)) { + DC_ERROR("TG counter is not moving!\n"); + break; + } + + if (tg->funcs->did_triggered_reset_occur(tg)) { + rc = true; + /* usually occurs at i=1 */ + DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n", + i); + break; + } + + /* Wait for one frame. */ + tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE); + tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK); + } + + if (false == rc) + DC_ERROR("GSL: Timeout on reset trigger!\n"); + + return rc; +} + +/* Enable timing synchronization for a group of Timing Generators. */ +static void dce110_enable_timing_synchronization( + struct dc *dc, + int group_index, + int group_size, + struct pipe_ctx *grouped_pipes[]) +{ + struct dc_context *dc_ctx = dc->ctx; + struct dcp_gsl_params gsl_params = { 0 }; + int i; + + DC_SYNC_INFO("GSL: Setting-up...\n"); + + /* Designate a single TG in the group as a master. + * Since HW doesn't care which one, we always assign + * the 1st one in the group. */ + gsl_params.gsl_group = 0; + gsl_params.gsl_master = grouped_pipes[0]->stream_res.tg->inst; + + for (i = 0; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->setup_global_swap_lock( + grouped_pipes[i]->stream_res.tg, &gsl_params); + + /* Reset slave controllers on master VSync */ + DC_SYNC_INFO("GSL: enabling trigger-reset\n"); + + for (i = 1 /* skip the master */; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger( + grouped_pipes[i]->stream_res.tg, gsl_params.gsl_group); + + + + for (i = 1 /* skip the master */; i < group_size; i++) { + DC_SYNC_INFO("GSL: waiting for reset to occur.\n"); + wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg); + /* Regardless of success of the wait above, remove the reset or + * the driver will start timing out on Display requests. */ + DC_SYNC_INFO("GSL: disabling trigger-reset.\n"); + grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger(grouped_pipes[i]->stream_res.tg); + } + + + /* GSL Vblank synchronization is a one time sync mechanism, assumption + * is that the sync'ed displays will not drift out of sync over time*/ + DC_SYNC_INFO("GSL: Restoring register states.\n"); + for (i = 0; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->tear_down_global_swap_lock(grouped_pipes[i]->stream_res.tg); + + DC_SYNC_INFO("GSL: Set-up complete.\n"); +} + +static void init_hw(struct dc *dc) +{ + int i; + struct dc_bios *bp; + struct transform *xfm; + struct abm *abm; + + bp = dc->ctx->dc_bios; + for (i = 0; i < dc->res_pool->pipe_count; i++) { + xfm = dc->res_pool->transforms[i]; + xfm->funcs->transform_reset(xfm); + + dc->hwss.enable_display_power_gating( + dc, i, bp, + PIPE_GATING_CONTROL_INIT); + dc->hwss.enable_display_power_gating( + dc, i, bp, + PIPE_GATING_CONTROL_DISABLE); + dc->hwss.enable_display_pipe_clock_gating( + dc->ctx, + true); + } + + dce_clock_gating_power_up(dc->hwseq, false); + /***************************************/ + + for (i = 0; i < dc->link_count; i++) { + /****************************************/ + /* Power up AND update implementation according to the + * required signal (which may be different from the + * default signal on connector). */ + struct dc_link *link = dc->links[i]; + link->link_enc->funcs->hw_init(link->link_enc); + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct timing_generator *tg = dc->res_pool->timing_generators[i]; + + tg->funcs->disable_vga(tg); + + /* Blank controller using driver code instead of + * command table. */ + tg->funcs->set_blank(tg, true); + hwss_wait_for_blank_complete(tg); + } + + for (i = 0; i < dc->res_pool->audio_count; i++) { + struct audio *audio = dc->res_pool->audios[i]; + audio->funcs->hw_init(audio); + } + + abm = dc->res_pool->abm; + if (abm != NULL) { + abm->funcs->init_backlight(abm); + abm->funcs->abm_init(abm); + } +#if defined(CONFIG_DRM_AMD_DC_FBC) + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->power_up_fbc(dc->fbc_compressor); +#endif + +} + +void dce110_fill_display_configs( + const struct dc_state *context, + struct dm_pp_display_configuration *pp_display_cfg) +{ + int j; + int num_cfgs = 0; + + for (j = 0; j < context->stream_count; j++) { + int k; + + const struct dc_stream_state *stream = context->streams[j]; + struct dm_pp_single_disp_config *cfg = + &pp_display_cfg->disp_configs[num_cfgs]; + const struct pipe_ctx *pipe_ctx = NULL; + + for (k = 0; k < MAX_PIPES; k++) + if (stream == context->res_ctx.pipe_ctx[k].stream) { + pipe_ctx = &context->res_ctx.pipe_ctx[k]; + break; + } + + ASSERT(pipe_ctx != NULL); + + num_cfgs++; + cfg->signal = pipe_ctx->stream->signal; + cfg->pipe_idx = pipe_ctx->pipe_idx; + cfg->src_height = stream->src.height; + cfg->src_width = stream->src.width; + cfg->ddi_channel_mapping = + stream->sink->link->ddi_channel_mapping.raw; + cfg->transmitter = + stream->sink->link->link_enc->transmitter; + cfg->link_settings.lane_count = + stream->sink->link->cur_link_settings.lane_count; + cfg->link_settings.link_rate = + stream->sink->link->cur_link_settings.link_rate; + cfg->link_settings.link_spread = + stream->sink->link->cur_link_settings.link_spread; + cfg->sym_clock = stream->phy_pix_clk; + /* Round v_refresh*/ + cfg->v_refresh = stream->timing.pix_clk_khz * 1000; + cfg->v_refresh /= stream->timing.h_total; + cfg->v_refresh = (cfg->v_refresh + stream->timing.v_total / 2) + / stream->timing.v_total; + } + + pp_display_cfg->display_count = num_cfgs; +} + +uint32_t dce110_get_min_vblank_time_us(const struct dc_state *context) +{ + uint8_t j; + uint32_t min_vertical_blank_time = -1; + + for (j = 0; j < context->stream_count; j++) { + struct dc_stream_state *stream = context->streams[j]; + uint32_t vertical_blank_in_pixels = 0; + uint32_t vertical_blank_time = 0; + + vertical_blank_in_pixels = stream->timing.h_total * + (stream->timing.v_total + - stream->timing.v_addressable); + + vertical_blank_time = vertical_blank_in_pixels + * 1000 / stream->timing.pix_clk_khz; + + if (min_vertical_blank_time > vertical_blank_time) + min_vertical_blank_time = vertical_blank_time; + } + + return min_vertical_blank_time; +} + +static int determine_sclk_from_bounding_box( + const struct dc *dc, + int required_sclk) +{ + int i; + + /* + * Some asics do not give us sclk levels, so we just report the actual + * required sclk + */ + if (dc->sclk_lvls.num_levels == 0) + return required_sclk; + + for (i = 0; i < dc->sclk_lvls.num_levels; i++) { + if (dc->sclk_lvls.clocks_in_khz[i] >= required_sclk) + return dc->sclk_lvls.clocks_in_khz[i]; + } + /* + * even maximum level could not satisfy requirement, this + * is unexpected at this stage, should have been caught at + * validation time + */ + ASSERT(0); + return dc->sclk_lvls.clocks_in_khz[dc->sclk_lvls.num_levels - 1]; +} + +static void pplib_apply_display_requirements( + struct dc *dc, + struct dc_state *context) +{ + struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; + + pp_display_cfg->all_displays_in_sync = + context->bw.dce.all_displays_in_sync; + pp_display_cfg->nb_pstate_switch_disable = + context->bw.dce.nbp_state_change_enable == false; + pp_display_cfg->cpu_cc6_disable = + context->bw.dce.cpuc_state_change_enable == false; + pp_display_cfg->cpu_pstate_disable = + context->bw.dce.cpup_state_change_enable == false; + pp_display_cfg->cpu_pstate_separation_time = + context->bw.dce.blackout_recovery_time_us; + + pp_display_cfg->min_memory_clock_khz = context->bw.dce.yclk_khz + / MEMORY_TYPE_MULTIPLIER; + + pp_display_cfg->min_engine_clock_khz = determine_sclk_from_bounding_box( + dc, + context->bw.dce.sclk_khz); + + pp_display_cfg->min_engine_clock_deep_sleep_khz + = context->bw.dce.sclk_deep_sleep_khz; + + pp_display_cfg->avail_mclk_switch_time_us = + dce110_get_min_vblank_time_us(context); + /* TODO: dce11.2*/ + pp_display_cfg->avail_mclk_switch_time_in_disp_active_us = 0; + + pp_display_cfg->disp_clk_khz = context->bw.dce.dispclk_khz; + + dce110_fill_display_configs(context, pp_display_cfg); + + /* TODO: is this still applicable?*/ + if (pp_display_cfg->display_count == 1) { + const struct dc_crtc_timing *timing = + &context->streams[0]->timing; + + pp_display_cfg->crtc_index = + pp_display_cfg->disp_configs[0].pipe_idx; + pp_display_cfg->line_time_in_us = timing->h_total * 1000 + / timing->pix_clk_khz; + } + + if (memcmp(&dc->prev_display_config, pp_display_cfg, sizeof( + struct dm_pp_display_configuration)) != 0) + dm_pp_apply_display_requirements(dc->ctx, pp_display_cfg); + + dc->prev_display_config = *pp_display_cfg; +} + +static void dce110_set_bandwidth( + struct dc *dc, + struct dc_state *context, + bool decrease_allowed) +{ + dce110_set_displaymarks(dc, context); + + if (decrease_allowed || context->bw.dce.dispclk_khz > dc->current_state->bw.dce.dispclk_khz) { + dc->res_pool->display_clock->funcs->set_clock( + dc->res_pool->display_clock, + context->bw.dce.dispclk_khz * 115 / 100); + dc->current_state->bw.dce.dispclk_khz = context->bw.dce.dispclk_khz; + } + + pplib_apply_display_requirements(dc, context); +} + +static void dce110_program_front_end_for_pipe( + struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct mem_input *mi = pipe_ctx->plane_res.mi; + struct pipe_ctx *old_pipe = NULL; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct xfm_grph_csc_adjustment adjust; + struct out_csc_color_matrix tbl_entry; + struct pipe_ctx *cur_pipe_ctx = + &dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx]; + unsigned int i; + + memset(&tbl_entry, 0, sizeof(tbl_entry)); + + if (dc->current_state) + old_pipe = &dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx]; + + memset(&adjust, 0, sizeof(adjust)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + dce_enable_fe_clock(dc->hwseq, pipe_ctx->pipe_idx, true); + + set_default_colors(pipe_ctx); + if (pipe_ctx->stream->csc_color_matrix.enable_adjustment + == true) { + tbl_entry.color_space = + pipe_ctx->stream->output_color_space; + + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = + pipe_ctx->stream->csc_color_matrix.matrix[i]; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment + (pipe_ctx->plane_res.xfm, &tbl_entry); + } + + if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + adjust.temperature_matrix[0] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[0]; + adjust.temperature_matrix[1] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[1]; + adjust.temperature_matrix[2] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[2]; + adjust.temperature_matrix[3] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[4]; + adjust.temperature_matrix[4] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[5]; + adjust.temperature_matrix[5] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[6]; + adjust.temperature_matrix[6] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[8]; + adjust.temperature_matrix[7] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[9]; + adjust.temperature_matrix[8] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[10]; + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust); + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; + + program_scaler(dc, pipe_ctx); + +#if defined(CONFIG_DRM_AMD_DC_FBC) + if (dc->fbc_compressor && old_pipe->stream) { + if (plane_state->tiling_info.gfx8.array_mode == DC_ARRAY_LINEAR_GENERAL) + dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor); + else + enable_fbc(dc, dc->current_state); + } +#endif + + mi->funcs->mem_input_program_surface_config( + mi, + plane_state->format, + &plane_state->tiling_info, + &plane_state->plane_size, + plane_state->rotation, + NULL, + false); + if (mi->funcs->set_blank) + mi->funcs->set_blank(mi, pipe_ctx->plane_state->visible); + + if (dc->config.gpu_vm_support) + mi->funcs->mem_input_program_pte_vm( + pipe_ctx->plane_res.mi, + plane_state->format, + &plane_state->tiling_info, + plane_state->rotation); + + /* Moved programming gamma from dc to hwss */ + if (cur_pipe_ctx->plane_state != pipe_ctx->plane_state) { + dc->hwss.set_input_transfer_func( + pipe_ctx, pipe_ctx->plane_state); + dc->hwss.set_output_transfer_func( + pipe_ctx, pipe_ctx->stream); + } + + dm_logger_write(dc->ctx->logger, LOG_SURFACE, + "Pipe:%d 0x%x: addr hi:0x%x, " + "addr low:0x%x, " + "src: %d, %d, %d," + " %d; dst: %d, %d, %d, %d;" + "clip: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + pipe_ctx->plane_state, + pipe_ctx->plane_state->address.grph.addr.high_part, + pipe_ctx->plane_state->address.grph.addr.low_part, + pipe_ctx->plane_state->src_rect.x, + pipe_ctx->plane_state->src_rect.y, + pipe_ctx->plane_state->src_rect.width, + pipe_ctx->plane_state->src_rect.height, + pipe_ctx->plane_state->dst_rect.x, + pipe_ctx->plane_state->dst_rect.y, + pipe_ctx->plane_state->dst_rect.width, + pipe_ctx->plane_state->dst_rect.height, + pipe_ctx->plane_state->clip_rect.x, + pipe_ctx->plane_state->clip_rect.y, + pipe_ctx->plane_state->clip_rect.width, + pipe_ctx->plane_state->clip_rect.height); + + dm_logger_write(dc->ctx->logger, LOG_SURFACE, + "Pipe %d: width, height, x, y\n" + "viewport:%d, %d, %d, %d\n" + "recout: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + pipe_ctx->plane_res.scl_data.viewport.width, + pipe_ctx->plane_res.scl_data.viewport.height, + pipe_ctx->plane_res.scl_data.viewport.x, + pipe_ctx->plane_res.scl_data.viewport.y, + pipe_ctx->plane_res.scl_data.recout.width, + pipe_ctx->plane_res.scl_data.recout.height, + pipe_ctx->plane_res.scl_data.recout.x, + pipe_ctx->plane_res.scl_data.recout.y); +} + +static void dce110_apply_ctx_for_surface( + struct dc *dc, + const struct dc_stream_state *stream, + int num_planes, + struct dc_state *context) +{ + int i, be_idx; + + if (num_planes == 0) + return; + + be_idx = -1; + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (stream == context->res_ctx.pipe_ctx[i].stream) { + be_idx = context->res_ctx.pipe_ctx[i].stream_res.tg->inst; + break; + } + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream != stream) + continue; + + /* Need to allocate mem before program front end for Fiji */ + if (pipe_ctx->plane_res.mi != NULL) + pipe_ctx->plane_res.mi->funcs->allocate_mem_input( + pipe_ctx->plane_res.mi, + pipe_ctx->stream->timing.h_total, + pipe_ctx->stream->timing.v_total, + pipe_ctx->stream->timing.pix_clk_khz, + context->stream_count); + + dce110_program_front_end_for_pipe(dc, pipe_ctx); + program_surface_visibility(dc, pipe_ctx); + + } +} + +static void dce110_power_down_fe(struct dc *dc, int fe_idx) +{ + /* Do not power down fe when stream is active on dce*/ + if (dc->current_state->res_ctx.pipe_ctx[fe_idx].stream) + return; + + dc->hwss.enable_display_power_gating( + dc, fe_idx, dc->ctx->dc_bios, PIPE_GATING_CONTROL_ENABLE); + + dc->res_pool->transforms[fe_idx]->funcs->transform_reset( + dc->res_pool->transforms[fe_idx]); +} + +static void dce110_wait_for_mpcc_disconnect( + struct dc *dc, + struct resource_pool *res_pool, + struct pipe_ctx *pipe_ctx) +{ + /* do nothing*/ +} + +static void program_csc_matrix(struct pipe_ctx *pipe_ctx, + enum dc_color_space colorspace, + uint16_t *matrix) +{ + int i; + struct out_csc_color_matrix tbl_entry; + + if (pipe_ctx->stream->csc_color_matrix.enable_adjustment + == true) { + enum dc_color_space color_space = + pipe_ctx->stream->output_color_space; + + //uint16_t matrix[12]; + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = pipe_ctx->stream->csc_color_matrix.matrix[i]; + + tbl_entry.color_space = color_space; + //tbl_entry.regval = matrix; + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment(pipe_ctx->plane_res.xfm, &tbl_entry); + } +} + +static void ready_shared_resources(struct dc *dc, struct dc_state *context) {} + +static void optimize_shared_resources(struct dc *dc) {} + +static const struct hw_sequencer_funcs dce110_funcs = { + .program_gamut_remap = program_gamut_remap, + .program_csc_matrix = program_csc_matrix, + .init_hw = init_hw, + .apply_ctx_to_hw = dce110_apply_ctx_to_hw, + .apply_ctx_for_surface = dce110_apply_ctx_for_surface, + .set_plane_config = set_plane_config, + .update_plane_addr = update_plane_addr, + .update_pending_status = dce110_update_pending_status, + .set_input_transfer_func = dce110_set_input_transfer_func, + .set_output_transfer_func = dce110_set_output_transfer_func, + .power_down = dce110_power_down, + .enable_accelerated_mode = dce110_enable_accelerated_mode, + .enable_timing_synchronization = dce110_enable_timing_synchronization, + .update_info_frame = dce110_update_info_frame, + .enable_stream = dce110_enable_stream, + .disable_stream = dce110_disable_stream, + .unblank_stream = dce110_unblank_stream, + .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating, + .enable_display_power_gating = dce110_enable_display_power_gating, + .power_down_front_end = dce110_power_down_fe, + .pipe_control_lock = dce_pipe_control_lock, + .set_bandwidth = dce110_set_bandwidth, + .set_drr = set_drr, + .get_position = get_position, + .set_static_screen_control = set_static_screen_control, + .reset_hw_ctx_wrap = dce110_reset_hw_ctx_wrap, + .prog_pixclk_crtc_otg = dce110_prog_pixclk_crtc_otg, + .setup_stereo = NULL, + .set_avmute = dce110_set_avmute, + .wait_for_mpcc_disconnect = dce110_wait_for_mpcc_disconnect, + .ready_shared_resources = ready_shared_resources, + .optimize_shared_resources = optimize_shared_resources, + .edp_backlight_control = hwss_edp_backlight_control, + .edp_power_control = hwss_edp_power_control, +}; + +void dce110_hw_sequencer_construct(struct dc *dc) +{ + dc->hwss = dce110_funcs; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h new file mode 100644 index 000000000000..4d72bb99be93 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h @@ -0,0 +1,81 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HWSS_DCE110_H__ +#define __DC_HWSS_DCE110_H__ + +#include "core_types.h" + +#define GAMMA_HW_POINTS_NUM 256 +struct dc; +struct dc_state; +struct dm_pp_display_configuration; + +void dce110_hw_sequencer_construct(struct dc *dc); + +enum dc_status dce110_apply_ctx_to_hw( + struct dc *dc, + struct dc_state *context); + +void dce110_set_display_clock(struct dc_state *context); + +void dce110_set_displaymarks( + const struct dc *dc, + struct dc_state *context); + +void dce110_enable_stream(struct pipe_ctx *pipe_ctx); + +void dce110_disable_stream(struct pipe_ctx *pipe_ctx, int option); + +void dce110_unblank_stream(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings); + +void dce110_update_info_frame(struct pipe_ctx *pipe_ctx); + +void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable); +void dce110_enable_accelerated_mode(struct dc *dc); + +void dce110_power_down(struct dc *dc); + +void dce110_update_pending_status(struct pipe_ctx *pipe_ctx); + +void dce110_fill_display_configs( + const struct dc_state *context, + struct dm_pp_display_configuration *pp_display_cfg); + +uint32_t dce110_get_min_vblank_time_us(const struct dc_state *context); + +void dp_receiver_power_ctrl(struct dc_link *link, bool on); + +void hwss_edp_power_control( + struct link_encoder *enc, + bool power_up); + +void hwss_edp_backlight_control( + struct dc_link *link, + bool enable); + +#endif /* __DC_HWSS_DCE110_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c new file mode 100644 index 000000000000..a06c6024deb4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c @@ -0,0 +1,1052 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +/* TODO: this needs to be looked at, used by Stella's workaround*/ +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" + +#include "include/logger_interface.h" +#include "inc/dce_calcs.h" + +#include "dce/dce_mem_input.h" + +static void set_flip_control( + struct dce_mem_input *mem_input110, + bool immediate) +{ + uint32_t value = 0; + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_FLIP_CONTROL); + + set_reg_field_value(value, 1, + UNP_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_PENDING_MODE); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_FLIP_CONTROL, + value); +} + +/* chroma part */ +static void program_pri_addr_c( + struct dce_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + /*high register MUST be programmed first*/ + temp = address.high_part & +UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C, + value); + + temp = 0; + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C__GRPH_PRIMARY_SURFACE_ADDRESS_C__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C, + GRPH_PRIMARY_SURFACE_ADDRESS_C); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_C, + value); +} + +/* luma part */ +static void program_pri_addr_l( + struct dce_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + + /*high register MUST be programmed first*/ + temp = address.high_part & +UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L, + value); + + temp = 0; + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L__GRPH_PRIMARY_SURFACE_ADDRESS_L__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L, + GRPH_PRIMARY_SURFACE_ADDRESS_L); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_L, + value); +} + +static void program_addr( + struct dce_mem_input *mem_input110, + const struct dc_plane_address *addr) +{ + switch (addr->type) { + case PLN_ADDR_TYPE_GRAPHICS: + program_pri_addr_l( + mem_input110, + addr->grph.addr); + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + program_pri_addr_c( + mem_input110, + addr->video_progressive.chroma_addr); + program_pri_addr_l( + mem_input110, + addr->video_progressive.luma_addr); + break; + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + } +} + +static void enable(struct dce_mem_input *mem_input110) +{ + uint32_t value = 0; + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_GRPH_ENABLE); + set_reg_field_value(value, 1, UNP_GRPH_ENABLE, GRPH_ENABLE); + dm_write_reg(mem_input110->base.ctx, + mmUNP_GRPH_ENABLE, + value); +} + +static void program_tiling( + struct dce_mem_input *mem_input110, + const union dc_tiling_info *info, + const enum surface_pixel_format pixel_format) +{ + uint32_t value = 0; + + set_reg_field_value(value, info->gfx8.num_banks, + UNP_GRPH_CONTROL, GRPH_NUM_BANKS); + + set_reg_field_value(value, info->gfx8.bank_width, + UNP_GRPH_CONTROL, GRPH_BANK_WIDTH_L); + + set_reg_field_value(value, info->gfx8.bank_height, + UNP_GRPH_CONTROL, GRPH_BANK_HEIGHT_L); + + set_reg_field_value(value, info->gfx8.tile_aspect, + UNP_GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT_L); + + set_reg_field_value(value, info->gfx8.tile_split, + UNP_GRPH_CONTROL, GRPH_TILE_SPLIT_L); + + set_reg_field_value(value, info->gfx8.tile_mode, + UNP_GRPH_CONTROL, GRPH_MICRO_TILE_MODE_L); + + set_reg_field_value(value, info->gfx8.pipe_config, + UNP_GRPH_CONTROL, GRPH_PIPE_CONFIG); + + set_reg_field_value(value, info->gfx8.array_mode, + UNP_GRPH_CONTROL, GRPH_ARRAY_MODE); + + set_reg_field_value(value, 1, + UNP_GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE); + + set_reg_field_value(value, 0, + UNP_GRPH_CONTROL, GRPH_Z); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL, + value); + + value = 0; + + set_reg_field_value(value, info->gfx8.bank_width_c, + UNP_GRPH_CONTROL_C, GRPH_BANK_WIDTH_C); + + set_reg_field_value(value, info->gfx8.bank_height_c, + UNP_GRPH_CONTROL_C, GRPH_BANK_HEIGHT_C); + + set_reg_field_value(value, info->gfx8.tile_aspect_c, + UNP_GRPH_CONTROL_C, GRPH_MACRO_TILE_ASPECT_C); + + set_reg_field_value(value, info->gfx8.tile_split_c, + UNP_GRPH_CONTROL_C, GRPH_TILE_SPLIT_C); + + set_reg_field_value(value, info->gfx8.tile_mode_c, + UNP_GRPH_CONTROL_C, GRPH_MICRO_TILE_MODE_C); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_C, + value); +} + +static void program_size_and_rotation( + struct dce_mem_input *mem_input110, + enum dc_rotation_angle rotation, + const union plane_size *plane_size) +{ + uint32_t value = 0; + union plane_size local_size = *plane_size; + + if (rotation == ROTATION_ANGLE_90 || + rotation == ROTATION_ANGLE_270) { + + uint32_t swap; + swap = local_size.video.luma_size.x; + local_size.video.luma_size.x = + local_size.video.luma_size.y; + local_size.video.luma_size.y = swap; + + swap = local_size.video.luma_size.width; + local_size.video.luma_size.width = + local_size.video.luma_size.height; + local_size.video.luma_size.height = swap; + + swap = local_size.video.chroma_size.x; + local_size.video.chroma_size.x = + local_size.video.chroma_size.y; + local_size.video.chroma_size.y = swap; + + swap = local_size.video.chroma_size.width; + local_size.video.chroma_size.width = + local_size.video.chroma_size.height; + local_size.video.chroma_size.height = swap; + } + + value = 0; + set_reg_field_value(value, local_size.video.luma_pitch, + UNP_GRPH_PITCH_L, GRPH_PITCH_L); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PITCH_L, + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_pitch, + UNP_GRPH_PITCH_C, GRPH_PITCH_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PITCH_C, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_L, GRPH_X_START_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_START_L, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_C, GRPH_X_START_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_START_C, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_L, GRPH_Y_START_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_START_L, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_C, GRPH_Y_START_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_START_C, + value); + + value = 0; + set_reg_field_value(value, local_size.video.luma_size.x + + local_size.video.luma_size.width, + UNP_GRPH_X_END_L, GRPH_X_END_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_END_L, + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_size.x + + local_size.video.chroma_size.width, + UNP_GRPH_X_END_C, GRPH_X_END_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_END_C, + value); + + value = 0; + set_reg_field_value(value, local_size.video.luma_size.y + + local_size.video.luma_size.height, + UNP_GRPH_Y_END_L, GRPH_Y_END_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_END_L, + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_size.y + + local_size.video.chroma_size.height, + UNP_GRPH_Y_END_C, GRPH_Y_END_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_END_C, + value); + + value = 0; + switch (rotation) { + case ROTATION_ANGLE_90: + set_reg_field_value(value, 3, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case ROTATION_ANGLE_180: + set_reg_field_value(value, 2, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case ROTATION_ANGLE_270: + set_reg_field_value(value, 1, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + default: + set_reg_field_value(value, 0, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + } + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_HW_ROTATION, + value); +} + +static void program_pixel_format( + struct dce_mem_input *mem_input110, + enum surface_pixel_format format) +{ + if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + uint32_t value; + uint8_t grph_depth; + uint8_t grph_format; + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + grph_depth = 0; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + grph_depth = 1; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + grph_depth = 2; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + grph_depth = 2; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + grph_depth = 3; + grph_format = 0; + break; + default: + grph_depth = 2; + grph_format = 0; + break; + } + + set_reg_field_value( + value, + grph_depth, + UNP_GRPH_CONTROL, + GRPH_DEPTH); + set_reg_field_value( + value, + grph_format, + UNP_GRPH_CONTROL, + GRPH_FORMAT); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL, + value); + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP); + + /* VIDEO FORMAT 0 */ + set_reg_field_value( + value, + 0, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP, + value); + + } else { + /* Video 422 and 420 needs UNP_GRPH_CONTROL_EXP programmed */ + uint32_t value; + uint8_t video_format; + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP); + + switch (format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + video_format = 2; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + video_format = 3; + break; + default: + video_format = 0; + break; + } + + set_reg_field_value( + value, + video_format, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP, + value); + } +} + +bool dce_mem_input_v_is_surface_pending(struct mem_input *mem_input) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + uint32_t value; + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_GRPH_UPDATE); + + if (get_reg_field_value(value, UNP_GRPH_UPDATE, + GRPH_SURFACE_UPDATE_PENDING)) + return true; + + mem_input->current_address = mem_input->request_address; + return false; +} + +bool dce_mem_input_v_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + + set_flip_control(mem_input110, flip_immediate); + program_addr(mem_input110, + address); + + mem_input->request_address = *address; + + return true; +} + +/* Scatter Gather param tables */ +static const unsigned int dvmm_Hw_Setting_2DTiling[4][9] = { + { 8, 64, 64, 8, 8, 1, 4, 0, 0}, + { 16, 64, 32, 8, 16, 1, 8, 0, 0}, + { 32, 32, 32, 16, 16, 1, 8, 0, 0}, + { 64, 8, 32, 16, 16, 1, 8, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_1DTiling[4][9] = { + { 8, 512, 8, 1, 0, 1, 0, 0, 0}, /* 0 for invalid */ + { 16, 256, 8, 2, 0, 1, 0, 0, 0}, + { 32, 128, 8, 4, 0, 1, 0, 0, 0}, + { 64, 64, 8, 4, 0, 1, 0, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_Linear[4][9] = { + { 8, 4096, 1, 8, 0, 1, 0, 0, 0}, + { 16, 2048, 1, 8, 0, 1, 0, 0, 0}, + { 32, 1024, 1, 8, 0, 1, 0, 0, 0}, + { 64, 512, 1, 8, 0, 1, 0, 0, 0}, /* new for 64bpp from HW */ +}; + +/* Helper to get table entry from surface info */ +static const unsigned int *get_dvmm_hw_setting( + union dc_tiling_info *tiling_info, + enum surface_pixel_format format, + bool chroma) +{ + enum bits_per_pixel { + bpp_8 = 0, + bpp_16, + bpp_32, + bpp_64 + } bpp; + + if (format >= SURFACE_PIXEL_FORMAT_INVALID) + bpp = bpp_32; + else if (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + bpp = chroma ? bpp_16 : bpp_8; + else + bpp = bpp_8; + + switch (tiling_info->gfx8.array_mode) { + case DC_ARRAY_1D_TILED_THIN1: + case DC_ARRAY_1D_TILED_THICK: + case DC_ARRAY_PRT_TILED_THIN1: + return dvmm_Hw_Setting_1DTiling[bpp]; + case DC_ARRAY_2D_TILED_THIN1: + case DC_ARRAY_2D_TILED_THICK: + case DC_ARRAY_2D_TILED_X_THICK: + case DC_ARRAY_PRT_2D_TILED_THIN1: + case DC_ARRAY_PRT_2D_TILED_THICK: + return dvmm_Hw_Setting_2DTiling[bpp]; + case DC_ARRAY_LINEAR_GENERAL: + case DC_ARRAY_LINEAR_ALLIGNED: + return dvmm_Hw_Setting_Linear[bpp]; + default: + return dvmm_Hw_Setting_2DTiling[bpp]; + } +} + +void dce_mem_input_v_program_pte_vm( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + const unsigned int *pte = get_dvmm_hw_setting(tiling_info, format, false); + const unsigned int *pte_chroma = get_dvmm_hw_setting(tiling_info, format, true); + + unsigned int page_width = 0; + unsigned int page_height = 0; + unsigned int page_width_chroma = 0; + unsigned int page_height_chroma = 0; + unsigned int temp_page_width = pte[1]; + unsigned int temp_page_height = pte[2]; + unsigned int min_pte_before_flip = 0; + unsigned int min_pte_before_flip_chroma = 0; + uint32_t value = 0; + + while ((temp_page_width >>= 1) != 0) + page_width++; + while ((temp_page_height >>= 1) != 0) + page_height++; + + temp_page_width = pte_chroma[1]; + temp_page_height = pte_chroma[2]; + while ((temp_page_width >>= 1) != 0) + page_width_chroma++; + while ((temp_page_height >>= 1) != 0) + page_height_chroma++; + + switch (rotation) { + case ROTATION_ANGLE_90: + case ROTATION_ANGLE_270: + min_pte_before_flip = pte[4]; + min_pte_before_flip_chroma = pte_chroma[4]; + break; + default: + min_pte_before_flip = pte[3]; + min_pte_before_flip_chroma = pte_chroma[3]; + break; + } + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_PIPE_OUTSTANDING_REQUEST_LIMIT); + /* TODO: un-hardcode requestlimit */ + set_reg_field_value(value, 0xff, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT_L); + set_reg_field_value(value, 0xff, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT_C); + dm_write_reg(mem_input110->base.ctx, mmUNP_PIPE_OUTSTANDING_REQUEST_LIMIT, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL); + set_reg_field_value(value, page_width, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_WIDTH); + set_reg_field_value(value, page_height, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT); + set_reg_field_value(value, min_pte_before_flip, UNP_DVMM_PTE_CONTROL, DVMM_MIN_PTE_BEFORE_FLIP); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL); + set_reg_field_value(value, pte[5], UNP_DVMM_PTE_ARB_CONTROL, DVMM_PTE_REQ_PER_CHUNK); + set_reg_field_value(value, 0xff, UNP_DVMM_PTE_ARB_CONTROL, DVMM_MAX_PTE_REQ_OUTSTANDING); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL_C); + set_reg_field_value(value, page_width_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_PAGE_WIDTH_C); + set_reg_field_value(value, page_height_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_PAGE_HEIGHT_C); + set_reg_field_value(value, min_pte_before_flip_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_MIN_PTE_BEFORE_FLIP_C); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL_C, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL_C); + set_reg_field_value(value, pte_chroma[5], UNP_DVMM_PTE_ARB_CONTROL_C, DVMM_PTE_REQ_PER_CHUNK_C); + set_reg_field_value(value, 0xff, UNP_DVMM_PTE_ARB_CONTROL_C, DVMM_MAX_PTE_REQ_OUTSTANDING_C); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL_C, value); +} + +void dce_mem_input_v_program_surface_config( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizotal_mirror) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + + enable(mem_input110); + program_tiling(mem_input110, tiling_info, format); + program_size_and_rotation(mem_input110, rotation, plane_size); + program_pixel_format(mem_input110, format); +} + +static void program_urgency_watermark( + const struct dc_context *ctx, + const uint32_t urgency_addr, + const uint32_t wm_addr, + struct dce_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + /* register value */ + uint32_t urgency_cntl = 0; + uint32_t wm_mask_cntl = 0; + + /*Write mask to enable reading/writing of watermark set A*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value( + urgency_cntl, + marks_low.a_mark, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value( + urgency_cntl, + total_dest_line_time_ns, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + dm_write_reg(ctx, urgency_addr, urgency_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value(urgency_cntl, + marks_low.b_mark, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value(urgency_cntl, + total_dest_line_time_ns, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + + dm_write_reg(ctx, urgency_addr, urgency_cntl); +} + +static void program_urgency_watermark_l( + const struct dc_context *ctx, + struct dce_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark( + ctx, + mmDPGV0_PIPE_URGENCY_CONTROL, + mmDPGV0_WATERMARK_MASK_CONTROL, + marks_low, + total_dest_line_time_ns); +} + +static void program_urgency_watermark_c( + const struct dc_context *ctx, + struct dce_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark( + ctx, + mmDPGV1_PIPE_URGENCY_CONTROL, + mmDPGV1_WATERMARK_MASK_CONTROL, + marks_low, + total_dest_line_time_ns); +} + +static void program_stutter_watermark( + const struct dc_context *ctx, + const uint32_t stutter_addr, + const uint32_t wm_addr, + struct dce_watermarks marks) +{ + /* register value */ + uint32_t stutter_cntl = 0; + uint32_t wm_mask_cntl = 0; + + /*Write mask to enable reading/writing of watermark set A*/ + + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + + if (ctx->dc->debug.disable_stutter) { + set_reg_field_value(stutter_cntl, + 0, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } else { + set_reg_field_value(stutter_cntl, + 1, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } + + set_reg_field_value(stutter_cntl, + 1, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_IGNORE_FBC); + + /*Write watermark set A*/ + set_reg_field_value(stutter_cntl, + marks.a_mark, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + /*Write watermark set B*/ + set_reg_field_value(stutter_cntl, + marks.b_mark, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); +} + +static void program_stutter_watermark_l( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_stutter_watermark(ctx, + mmDPGV0_PIPE_STUTTER_CONTROL, + mmDPGV0_WATERMARK_MASK_CONTROL, + marks); +} + +static void program_stutter_watermark_c( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_stutter_watermark(ctx, + mmDPGV1_PIPE_STUTTER_CONTROL, + mmDPGV1_WATERMARK_MASK_CONTROL, + marks); +} + +static void program_nbp_watermark( + const struct dc_context *ctx, + const uint32_t wm_mask_ctrl_addr, + const uint32_t nbp_pstate_ctrl_addr, + struct dce_watermarks marks) +{ + uint32_t value; + + /* Write mask to enable reading/writing of watermark set A */ + + value = dm_read_reg(ctx, wm_mask_ctrl_addr); + + set_reg_field_value( + value, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, wm_mask_ctrl_addr, value); + + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write watermark set A */ + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + marks.a_mark, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write mask to enable reading/writing of watermark set B */ + value = dm_read_reg(ctx, wm_mask_ctrl_addr); + set_reg_field_value( + value, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, wm_mask_ctrl_addr, value); + + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write watermark set B */ + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + marks.b_mark, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); +} + +static void program_nbp_watermark_l( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_nbp_watermark(ctx, + mmDPGV0_WATERMARK_MASK_CONTROL, + mmDPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + marks); +} + +static void program_nbp_watermark_c( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_nbp_watermark(ctx, + mmDPGV1_WATERMARK_MASK_CONTROL, + mmDPGV1_PIPE_NB_PSTATE_CHANGE_CONTROL, + marks); +} + +void dce_mem_input_v_program_display_marks( + struct mem_input *mem_input, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark_l( + mem_input->ctx, + urgent, + total_dest_line_time_ns); + + program_nbp_watermark_l( + mem_input->ctx, + nbp); + + program_stutter_watermark_l( + mem_input->ctx, + stutter); + +} + +void dce_mem_input_program_chroma_display_marks( + struct mem_input *mem_input, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark_c( + mem_input->ctx, + urgent, + total_dest_line_time_ns); + + program_nbp_watermark_c( + mem_input->ctx, + nbp); + + program_stutter_watermark_c( + mem_input->ctx, + stutter); +} + +void dce110_allocate_mem_input_v( + struct mem_input *mi, + uint32_t h_total,/* for current stream */ + uint32_t v_total,/* for current stream */ + uint32_t pix_clk_khz,/* for current stream */ + uint32_t total_stream_num) +{ + uint32_t addr; + uint32_t value; + uint32_t pix_dur; + if (pix_clk_khz != 0) { + addr = mmDPGV0_PIPE_ARBITRATION_CONTROL1; + value = dm_read_reg(mi->ctx, addr); + pix_dur = 1000000000ULL / pix_clk_khz; + set_reg_field_value( + value, + pix_dur, + DPGV0_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV1_PIPE_ARBITRATION_CONTROL1; + value = dm_read_reg(mi->ctx, addr); + pix_dur = 1000000000ULL / pix_clk_khz; + set_reg_field_value( + value, + pix_dur, + DPGV1_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV0_PIPE_ARBITRATION_CONTROL2; + value = 0x4000800; + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV1_PIPE_ARBITRATION_CONTROL2; + value = 0x4000800; + dm_write_reg(mi->ctx, addr, value); + } + +} + +void dce110_free_mem_input_v( + struct mem_input *mi, + uint32_t total_stream_num) +{ +} + +static struct mem_input_funcs dce110_mem_input_v_funcs = { + .mem_input_program_display_marks = + dce_mem_input_v_program_display_marks, + .mem_input_program_chroma_display_marks = + dce_mem_input_program_chroma_display_marks, + .allocate_mem_input = dce110_allocate_mem_input_v, + .free_mem_input = dce110_free_mem_input_v, + .mem_input_program_surface_flip_and_addr = + dce_mem_input_v_program_surface_flip_and_addr, + .mem_input_program_pte_vm = + dce_mem_input_v_program_pte_vm, + .mem_input_program_surface_config = + dce_mem_input_v_program_surface_config, + .mem_input_is_flip_pending = + dce_mem_input_v_is_surface_pending +}; +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void dce110_mem_input_v_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx) +{ + dce_mi->base.funcs = &dce110_mem_input_v_funcs; + dce_mi->base.ctx = ctx; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h new file mode 100644 index 000000000000..f01d4a607fea --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h @@ -0,0 +1,35 @@ +/* Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_MEM_INPUT_V_DCE110_H__ +#define __DC_MEM_INPUT_V_DCE110_H__ + +#include "mem_input.h" +#include "dce/dce_mem_input.h" + +void dce110_mem_input_v_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c new file mode 100644 index 000000000000..feb397b5c1a3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -0,0 +1,738 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dce110_transform_v.h" +#include "basics/conversion.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "dce/dce_11_0_enum.h" + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + +/* constrast:0 - 2.0, default 1.0 */ +#define UNDERLAY_CONTRAST_DEFAULT 100 +#define UNDERLAY_CONTRAST_MAX 200 +#define UNDERLAY_CONTRAST_MIN 0 +#define UNDERLAY_CONTRAST_STEP 1 +#define UNDERLAY_CONTRAST_DIVIDER 100 + +/* Saturation: 0 - 2.0; default 1.0 */ +#define UNDERLAY_SATURATION_DEFAULT 100 /*1.00*/ +#define UNDERLAY_SATURATION_MIN 0 +#define UNDERLAY_SATURATION_MAX 200 /* 2.00 */ +#define UNDERLAY_SATURATION_STEP 1 /* 0.01 */ +/*actual max overlay saturation + * value = UNDERLAY_SATURATION_MAX /UNDERLAY_SATURATION_DIVIDER + */ + +/* Hue */ +#define UNDERLAY_HUE_DEFAULT 0 +#define UNDERLAY_HUE_MIN -300 +#define UNDERLAY_HUE_MAX 300 +#define UNDERLAY_HUE_STEP 5 +#define UNDERLAY_HUE_DIVIDER 10 /* HW range: -30 ~ +30 */ +#define UNDERLAY_SATURATION_DIVIDER 100 + +/* Brightness: in DAL usually -.25 ~ .25. + * In MMD is -100 to +100 in 16-235 range; which when scaled to full range is + * ~-116 to +116. When normalized this is about 0.4566. + * With 100 divider this becomes 46, but we may use another for better precision + * The ideal one is 100/219 ((100/255)*(255/219)), + * i.e. min/max = +-100, divider = 219 + * default 0.0 + */ +#define UNDERLAY_BRIGHTNESS_DEFAULT 0 +#define UNDERLAY_BRIGHTNESS_MIN -46 /* ~116/255 */ +#define UNDERLAY_BRIGHTNESS_MAX 46 +#define UNDERLAY_BRIGHTNESS_STEP 1 /* .01 */ +#define UNDERLAY_BRIGHTNESS_DIVIDER 100 + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* TODO: correct values below */ +{ COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +enum grph_color_adjust_option { + GRPH_COLOR_MATRIX_HW_DEFAULT = 1, + GRPH_COLOR_MATRIX_SW +}; + +static void program_color_matrix_v( + struct dce_transform *xfm_dce, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t cntl_value = dm_read_reg(ctx, mmCOL_MAN_OUTPUT_CSC_CONTROL); + bool use_set_a = (get_reg_field_value(cntl_value, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE) != 4); + + set_reg_field_value( + cntl_value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + if (use_set_a) { + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C11_C12_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12_A, + OUTPUT_CSC_C11_A); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12_A, + OUTPUT_CSC_C12_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C13_C14_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14_A, + OUTPUT_CSC_C13_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14_A, + OUTPUT_CSC_C14_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C21_C22_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22_A, + OUTPUT_CSC_C21_A); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22_A, + OUTPUT_CSC_C22_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C23_C24_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24_A, + OUTPUT_CSC_C23_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24_A, + OUTPUT_CSC_C24_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C31_C32_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32_A, + OUTPUT_CSC_C31_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32_A, + OUTPUT_CSC_C32_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C33_C34_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34_A, + OUTPUT_CSC_C33_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34_A, + OUTPUT_CSC_C34_A); + + dm_write_reg(ctx, addr, value); + } + set_reg_field_value( + cntl_value, + 4, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + } else { + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C11_C12_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12_B, + OUTPUT_CSC_C11_B); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12_B, + OUTPUT_CSC_C12_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C13_C14_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14_B, + OUTPUT_CSC_C13_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14_B, + OUTPUT_CSC_C14_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C21_C22_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22_B, + OUTPUT_CSC_C21_B); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22_B, + OUTPUT_CSC_C22_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C23_C24_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24_B, + OUTPUT_CSC_C23_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24_B, + OUTPUT_CSC_C24_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C31_C32_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32_B, + OUTPUT_CSC_C31_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32_B, + OUTPUT_CSC_C32_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C33_C34_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34_B, + OUTPUT_CSC_C33_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34_B, + OUTPUT_CSC_C34_B); + + dm_write_reg(ctx, addr, value); + } + set_reg_field_value( + cntl_value, + 5, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + } + + dm_write_reg(ctx, mmCOL_MAN_OUTPUT_CSC_CONTROL, cntl_value); +} + +static bool configure_graphics_mode_v( + struct dce_transform *xfm_dce, + enum csc_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum dc_color_space color_space) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t addr = mmCOL_MAN_OUTPUT_CSC_CONTROL; + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) + return true; + + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* not supported for underlay on CZ */ + return false; + + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* not supported for underlay on CZ */ + return false; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + + } else + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + addr = mmCOL_MAN_OUTPUT_CSC_CONTROL; + dm_write_reg(ctx, addr, value); + + return true; +} + +/*TODO: color depth is not correct when this is called*/ +static void set_Denormalization(struct transform *xfm, + enum dc_color_depth color_depth) +{ + uint32_t value = dm_read_reg(xfm->ctx, mmDENORM_CLAMP_CONTROL); + + switch (color_depth) { + case COLOR_DEPTH_888: + /* 255/256 for 8 bit output color depth */ + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + set_reg_field_value( + value, + 2, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + set_reg_field_value( + value, + 3, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + default: + /* not valid case */ + break; + } + + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_10BIT_OUT); + + dm_write_reg(xfm->ctx, mmDENORM_CLAMP_CONTROL, value); +} + +struct input_csc_matrix { + enum dc_color_space color_space; + uint32_t regval[12]; +}; + +static const struct input_csc_matrix input_csc_matrix[] = { + {COLOR_SPACE_SRGB, +/*1_1 1_2 1_3 1_4 2_1 2_2 2_3 2_4 3_1 3_2 3_3 3_4 */ + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_SRGB_LIMITED, + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_YCBCR601, + {0x2cdd, 0x2000, 0x0, 0xe991, 0xe926, 0x2000, 0xf4fd, 0x10ef, + 0x0, 0x2000, 0x38b4, 0xe3a6} }, + {COLOR_SPACE_YCBCR601_LIMITED, + {0x3353, 0x2568, 0x0, 0xe400, 0xe5dc, 0x2568, 0xf367, 0x1108, + 0x0, 0x2568, 0x40de, 0xdd3a} }, + {COLOR_SPACE_YCBCR709, + {0x3265, 0x2000, 0, 0xe6ce, 0xf105, 0x2000, 0xfa01, 0xa7d, 0, + 0x2000, 0x3b61, 0xe24f} }, + {COLOR_SPACE_YCBCR709_LIMITED, + {0x39a6, 0x2568, 0, 0xe0d6, 0xeedd, 0x2568, 0xf925, 0x9a8, 0, + 0x2568, 0x43ee, 0xdbb2} } +}; + +static void program_input_csc( + struct transform *xfm, enum dc_color_space color_space) +{ + int arr_size = sizeof(input_csc_matrix)/sizeof(struct input_csc_matrix); + struct dc_context *ctx = xfm->ctx; + const uint32_t *regval = NULL; + bool use_set_a; + uint32_t value; + int i; + + for (i = 0; i < arr_size; i++) + if (input_csc_matrix[i].color_space == color_space) { + regval = input_csc_matrix[i].regval; + break; + } + if (regval == NULL) { + BREAK_TO_DEBUGGER(); + return; + } + + /* + * 1 == set A, the logic is 'if currently we're not using set A, + * then use set A, otherwise use set B' + */ + value = dm_read_reg(ctx, mmCOL_MAN_INPUT_CSC_CONTROL); + use_set_a = get_reg_field_value( + value, COL_MAN_INPUT_CSC_CONTROL, INPUT_CSC_MODE) != 1; + + if (use_set_a) { + /* fixed S2.13 format */ + value = 0; + set_reg_field_value( + value, regval[0], INPUT_CSC_C11_C12_A, INPUT_CSC_C11_A); + set_reg_field_value( + value, regval[1], INPUT_CSC_C11_C12_A, INPUT_CSC_C12_A); + dm_write_reg(ctx, mmINPUT_CSC_C11_C12_A, value); + + value = 0; + set_reg_field_value( + value, regval[2], INPUT_CSC_C13_C14_A, INPUT_CSC_C13_A); + set_reg_field_value( + value, regval[3], INPUT_CSC_C13_C14_A, INPUT_CSC_C14_A); + dm_write_reg(ctx, mmINPUT_CSC_C13_C14_A, value); + + value = 0; + set_reg_field_value( + value, regval[4], INPUT_CSC_C21_C22_A, INPUT_CSC_C21_A); + set_reg_field_value( + value, regval[5], INPUT_CSC_C21_C22_A, INPUT_CSC_C22_A); + dm_write_reg(ctx, mmINPUT_CSC_C21_C22_A, value); + + value = 0; + set_reg_field_value( + value, regval[6], INPUT_CSC_C23_C24_A, INPUT_CSC_C23_A); + set_reg_field_value( + value, regval[7], INPUT_CSC_C23_C24_A, INPUT_CSC_C24_A); + dm_write_reg(ctx, mmINPUT_CSC_C23_C24_A, value); + + value = 0; + set_reg_field_value( + value, regval[8], INPUT_CSC_C31_C32_A, INPUT_CSC_C31_A); + set_reg_field_value( + value, regval[9], INPUT_CSC_C31_C32_A, INPUT_CSC_C32_A); + dm_write_reg(ctx, mmINPUT_CSC_C31_C32_A, value); + + value = 0; + set_reg_field_value( + value, regval[10], INPUT_CSC_C33_C34_A, INPUT_CSC_C33_A); + set_reg_field_value( + value, regval[11], INPUT_CSC_C33_C34_A, INPUT_CSC_C34_A); + dm_write_reg(ctx, mmINPUT_CSC_C33_C34_A, value); + } else { + /* fixed S2.13 format */ + value = 0; + set_reg_field_value( + value, regval[0], INPUT_CSC_C11_C12_B, INPUT_CSC_C11_B); + set_reg_field_value( + value, regval[1], INPUT_CSC_C11_C12_B, INPUT_CSC_C12_B); + dm_write_reg(ctx, mmINPUT_CSC_C11_C12_B, value); + + value = 0; + set_reg_field_value( + value, regval[2], INPUT_CSC_C13_C14_B, INPUT_CSC_C13_B); + set_reg_field_value( + value, regval[3], INPUT_CSC_C13_C14_B, INPUT_CSC_C14_B); + dm_write_reg(ctx, mmINPUT_CSC_C13_C14_B, value); + + value = 0; + set_reg_field_value( + value, regval[4], INPUT_CSC_C21_C22_B, INPUT_CSC_C21_B); + set_reg_field_value( + value, regval[5], INPUT_CSC_C21_C22_B, INPUT_CSC_C22_B); + dm_write_reg(ctx, mmINPUT_CSC_C21_C22_B, value); + + value = 0; + set_reg_field_value( + value, regval[6], INPUT_CSC_C23_C24_B, INPUT_CSC_C23_B); + set_reg_field_value( + value, regval[7], INPUT_CSC_C23_C24_B, INPUT_CSC_C24_B); + dm_write_reg(ctx, mmINPUT_CSC_C23_C24_B, value); + + value = 0; + set_reg_field_value( + value, regval[8], INPUT_CSC_C31_C32_B, INPUT_CSC_C31_B); + set_reg_field_value( + value, regval[9], INPUT_CSC_C31_C32_B, INPUT_CSC_C32_B); + dm_write_reg(ctx, mmINPUT_CSC_C31_C32_B, value); + + value = 0; + set_reg_field_value( + value, regval[10], INPUT_CSC_C33_C34_B, INPUT_CSC_C33_B); + set_reg_field_value( + value, regval[11], INPUT_CSC_C33_C34_B, INPUT_CSC_C34_B); + dm_write_reg(ctx, mmINPUT_CSC_C33_C34_B, value); + } + + /* KK: leave INPUT_CSC_CONVERSION_MODE at default */ + value = 0; + /* + * select 8.4 input type instead of default 12.0. From the discussion + * with HW team, this format depends on the UNP surface format, so for + * 8-bit we should select 8.4 (4 bits truncated). For 10 it should be + * 10.2. For Carrizo we only support 8-bit surfaces on underlay pipe + * so we can always keep this at 8.4 (input_type=2). If the later asics + * start supporting 10+ bits, we will have a problem: surface + * programming including UNP_GRPH* is being done in DalISR after this, + * so either we pass surface format to here, or move this logic to ISR + */ + + set_reg_field_value( + value, 2, COL_MAN_INPUT_CSC_CONTROL, INPUT_CSC_INPUT_TYPE); + set_reg_field_value( + value, + use_set_a ? 1 : 2, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_MODE); + + dm_write_reg(ctx, mmCOL_MAN_INPUT_CSC_CONTROL, value); +} + +void dce110_opp_v_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct out_csc_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option = + GRPH_COLOR_MATRIX_HW_DEFAULT; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->out_color_space) + continue; + /* program the matrix with default values from this + * file + */ + program_color_matrix_v(xfm_dce, elm, option); + config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + + program_input_csc(xfm, default_adjust->in_color_space); + + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix + */ + + configure_graphics_mode_v(xfm_dce, config, + default_adjust->csc_adjust_type, + default_adjust->out_color_space); + + set_Denormalization(xfm, default_adjust->color_depth); +} + +void dce110_opp_v_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + program_color_matrix_v( + xfm_dce, tbl_entry, GRAPHICS_CSC_ADJUST_TYPE_SW); + + /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ + configure_graphics_mode_v(xfm_dce, config, GRAPHICS_CSC_ADJUST_TYPE_SW, + tbl_entry->color_space); + + /*TODO: Check if denormalization is needed*/ + /*set_Denormalization(opp, adjust->color_depth);*/ +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c new file mode 100644 index 000000000000..e98ed3058ea2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c @@ -0,0 +1,555 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_transform_v.h" + +static void power_on_lut(struct transform *xfm, + bool power_on, bool inputgamma, bool regamma) +{ + uint32_t value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL); + int i; + + if (power_on) { + if (inputgamma) + set_reg_field_value( + value, + 1, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS); + if (regamma) + set_reg_field_value( + value, + 1, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS); + } else { + if (inputgamma) + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS); + if (regamma) + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS); + } + + dm_write_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL, value); + + for (i = 0; i < 3; i++) { + value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL); + if (get_reg_field_value(value, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS) && + get_reg_field_value(value, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS)) + break; + + udelay(2); + } +} + +static void set_bypass_input_gamma(struct dce_transform *xfm_dce) +{ + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, + mmCOL_MAN_INPUT_GAMMA_CONTROL1); + + set_reg_field_value( + value, + 0, + COL_MAN_INPUT_GAMMA_CONTROL1, + INPUT_GAMMA_MODE); + + dm_write_reg(xfm_dce->base.ctx, + mmCOL_MAN_INPUT_GAMMA_CONTROL1, value); +} + +static void configure_regamma_mode(struct dce_transform *xfm_dce, uint32_t mode) +{ + uint32_t value = 0; + + set_reg_field_value( + value, + mode, + GAMMA_CORR_CONTROL, + GAMMA_CORR_MODE); + + dm_write_reg(xfm_dce->base.ctx, mmGAMMA_CORR_CONTROL, 0); +} + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ +static void regamma_config_regions_and_segments( + struct dce_transform *xfm_dce, const struct pwl_params *params) +{ + const struct gamma_curve *curve; + uint32_t value = 0; + + { + set_reg_field_value( + value, + params->arr_points[0].custom_float_x, + GAMMA_CORR_CNTLA_START_CNTL, + GAMMA_CORR_CNTLA_EXP_REGION_START); + + set_reg_field_value( + value, + 0, + GAMMA_CORR_CNTLA_START_CNTL, + GAMMA_CORR_CNTLA_EXP_REGION_START_SEGMENT); + + dm_write_reg(xfm_dce->base.ctx, mmGAMMA_CORR_CNTLA_START_CNTL, + value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[0].custom_float_slope, + GAMMA_CORR_CNTLA_SLOPE_CNTL, + GAMMA_CORR_CNTLA_EXP_REGION_LINEAR_SLOPE); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_SLOPE_CNTL, value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[1].custom_float_x, + GAMMA_CORR_CNTLA_END_CNTL1, + GAMMA_CORR_CNTLA_EXP_REGION_END); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_END_CNTL1, value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[2].custom_float_slope, + GAMMA_CORR_CNTLA_END_CNTL2, + GAMMA_CORR_CNTLA_EXP_REGION_END_BASE); + + set_reg_field_value( + value, + params->arr_points[1].custom_float_y, + GAMMA_CORR_CNTLA_END_CNTL2, + GAMMA_CORR_CNTLA_EXP_REGION_END_SLOPE); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_END_CNTL2, value); + } + + curve = params->arr_curve_points; + + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION0_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION0_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION1_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION1_NUM_SEGMENTS); + + dm_write_reg( + xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_0_1, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION2_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION2_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION3_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION3_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_2_3, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION4_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION4_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION5_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION5_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_4_5, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION6_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION6_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION7_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION7_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_6_7, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION8_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION8_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION9_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION9_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_8_9, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION10_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION10_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION11_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION11_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_10_11, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION12_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION12_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION13_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION13_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_12_13, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION14_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION14_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION15_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION15_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_14_15, + value); + } +} + +static void program_pwl(struct dce_transform *xfm_dce, + const struct pwl_params *params) +{ + uint32_t value = 0; + + set_reg_field_value( + value, + 7, + GAMMA_CORR_LUT_WRITE_EN_MASK, + GAMMA_CORR_LUT_WRITE_EN_MASK); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_LUT_WRITE_EN_MASK, value); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_LUT_INDEX, 0); + + /* Program REGAMMA_LUT_DATA */ + { + const uint32_t addr = mmGAMMA_CORR_LUT_DATA; + uint32_t i = 0; + const struct pwl_result_data *rgb = + params->rgb_resulted; + + while (i != params->hw_points_num) { + dm_write_reg(xfm_dce->base.ctx, addr, rgb->red_reg); + dm_write_reg(xfm_dce->base.ctx, addr, rgb->green_reg); + dm_write_reg(xfm_dce->base.ctx, addr, rgb->blue_reg); + + dm_write_reg(xfm_dce->base.ctx, addr, + rgb->delta_red_reg); + dm_write_reg(xfm_dce->base.ctx, addr, + rgb->delta_green_reg); + dm_write_reg(xfm_dce->base.ctx, addr, + rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } +} + +void dce110_opp_program_regamma_pwl_v( + struct transform *xfm, + const struct pwl_params *params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + /* Setup regions */ + regamma_config_regions_and_segments(xfm_dce, params); + + set_bypass_input_gamma(xfm_dce); + + /* Power on gamma LUT memory */ + power_on_lut(xfm, true, false, true); + + /* Program PWL */ + program_pwl(xfm_dce, params); + + /* program regamma config */ + configure_regamma_mode(xfm_dce, 1); + + /* Power return to auto back */ + power_on_lut(xfm, false, false, true); +} + +void dce110_opp_power_on_regamma_lut_v( + struct transform *xfm, + bool power_on) +{ + uint32_t value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL); + + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_FORCE); + + set_reg_field_value( + value, + power_on, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS); + + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_FORCE); + + set_reg_field_value( + value, + power_on, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS); + + dm_write_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL, value); +} + +void dce110_opp_set_regamma_mode_v( + struct transform *xfm, + enum opp_regamma mode) +{ + // TODO: need to implement the function +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c new file mode 100644 index 000000000000..3545e43a4b77 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c @@ -0,0 +1,54 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce/dce_opp.h" +#include "dce110_opp_v.h" + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static const struct opp_funcs funcs = { + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_destroy = dce110_opp_destroy, + .opp_program_fmt = dce110_opp_program_fmt, + .opp_program_bit_depth_reduction = + dce110_opp_program_bit_depth_reduction +}; + +void dce110_opp_v_construct(struct dce110_opp *opp110, + struct dc_context *ctx) +{ + opp110->base.funcs = &funcs; + + opp110->base.ctx = ctx; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h new file mode 100644 index 000000000000..152af4c418cb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h @@ -0,0 +1,39 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_OPP_DCE110_V_H__ +#define __DC_OPP_DCE110_V_H__ + +#include "dc_types.h" +#include "opp.h" +#include "core_types.h" + +void dce110_opp_v_construct(struct dce110_opp *opp110, + struct dc_context *ctx); + +/* underlay callbacks */ + + + +#endif /* __DC_OPP_DCE110_V_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c new file mode 100644 index 000000000000..db96d2b47ff1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c @@ -0,0 +1,1327 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "dce110/dce110_resource.h" + +#include "include/irq_service_interface.h" +#include "dce/dce_audio.h" +#include "dce110/dce110_timing_generator.h" +#include "irq/dce110/irq_service_dce110.h" +#include "dce110/dce110_timing_generator_v.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_mem_input.h" +#include "dce110/dce110_mem_input_v.h" +#include "dce/dce_ipp.h" +#include "dce/dce_transform.h" +#include "dce110/dce110_transform_v.h" +#include "dce/dce_opp.h" +#include "dce110/dce110_opp_v.h" +#include "dce/dce_clocks.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_hwseq.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" + +#if defined(CONFIG_DRM_AMD_DC_FBC) +#include "dce110/dce110_compressor.h" +#endif + +#include "reg_helper.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_BS_SR_SWAP_CNTL + #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4BDC + #define mmDP2_DP_DPHY_BS_SR_SWAP_CNTL 0x4CDC + #define mmDP3_DP_DPHY_BS_SR_SWAP_CNTL 0x4DDC + #define mmDP4_DP_DPHY_BS_SR_SWAP_CNTL 0x4EDC + #define mmDP5_DP_DPHY_BS_SR_SWAP_CNTL 0x4FDC + #define mmDP6_DP_DPHY_BS_SR_SWAP_CNTL 0x54DC +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP0_DP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP1_DP_DPHY_FAST_TRAINING 0x4BBC + #define mmDP2_DP_DPHY_FAST_TRAINING 0x4CBC + #define mmDP3_DP_DPHY_FAST_TRAINING 0x4DBC + #define mmDP4_DP_DPHY_FAST_TRAINING 0x4EBC + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4FBC + #define mmDP6_DP_DPHY_FAST_TRAINING 0x54BC +#endif + +#ifndef DPHY_RX_FAST_TRAINING_CAPABLE + #define DPHY_RX_FAST_TRAINING_CAPABLE 0x1 +#endif + +static const struct dce110_timing_generator_offsets dce110_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +static const struct dce_disp_clk_registers disp_clk_regs = { + CLK_COMMON_REG_LIST_DCE_BASE() +}; + +static const struct dce_disp_clk_shift disp_clk_shift = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_disp_clk_mask disp_clk_mask = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE110_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE110(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + + +#define link_regs(id)\ +[id] = {\ + LE_DCE110_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST(id),\ + .TMDS_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_110_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_110(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_110(_MASK) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_aduio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +/* AG TBD Needs to be reduced back to 3 pipes once dce10 hw sequencer implemented. */ + + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_100_110(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps carrizo_resource_cap = { + .num_timing_generator = 3, + .num_video_plane = 1, + .num_audio = 3, + .num_stream_encoder = 3, + .num_pll = 2, +}; + +static const struct resource_caps stoney_resource_cap = { + .num_timing_generator = 2, + .num_video_plane = 1, + .num_audio = 3, + .num_stream_encoder = 3, + .num_pll = 2, +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x4819 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce110_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce110_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct stream_encoder *dce110_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_stoney_reg = { + HWSEQ_ST_REG_LIST() +}; + +static const struct dce_hwseq_registers hwseq_cz_reg = { + HWSEQ_CZ_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE11_MASK_SH_LIST(__SHIFT), +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE11_MASK_SH_LIST(_MASK), +}; + +static struct dce_hwseq *dce110_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = ASIC_REV_IS_STONEY(ctx->asic_id.hw_internal_rev) ? + &hwseq_stoney_reg : &hwseq_cz_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + hws->wa.blnd_crtc_trigger = true; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce110_stream_encoder_create, + .create_hwseq = dce110_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_DCE11_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE11_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE11_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + + +static struct mem_input *dce110_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + dce_mi->wa.single_head_rdreq_dmif_limit = 3; + return &dce_mi->base; +} + +static void dce110_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce110_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + return &transform->base; +} + +static struct input_pixel_processor *dce110_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 594000, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_YCBCR_CAPABLE = true +}; + +static struct link_encoder *dce110_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +static struct output_pixel_processor *dce110_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +struct clock_source *dce110_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce110_clock_source_destroy(struct clock_source **clk_src) +{ + struct dce110_clk_src *dce110_clk_src; + + if (!clk_src) + return; + + dce110_clk_src = TO_DCE110_CLK_SRC(*clk_src); + + kfree(dce110_clk_src->dp_ss_params); + kfree(dce110_clk_src->hdmi_ss_params); + kfree(dce110_clk_src->dvi_ss_params); + + kfree(dce110_clk_src); + *clk_src = NULL; +} + +static void destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce110_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce110_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.dp_clock_source != NULL) + dce110_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.display_clock != NULL) + dce_disp_clk_destroy(&pool->base.display_clock); + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + + +static void get_pixel_clock_parameters( + const struct pipe_ctx *pipe_ctx, + struct pixel_clk_params *pixel_clk_params) +{ + const struct dc_stream_state *stream = pipe_ctx->stream; + + /*TODO: is this halved for YCbCr 420? in that case we might want to move + * the pixel clock normalization for hdmi up to here instead of doing it + * in pll_adjust_pix_clk + */ + pixel_clk_params->requested_pix_clk = stream->timing.pix_clk_khz; + pixel_clk_params->encoder_object_id = stream->sink->link->link_enc->id; + pixel_clk_params->signal_type = pipe_ctx->stream->signal; + pixel_clk_params->controller_id = pipe_ctx->pipe_idx + 1; + /* TODO: un-hardcode*/ + pixel_clk_params->requested_sym_clk = LINK_RATE_LOW * + LINK_RATE_REF_FREQ_IN_KHZ; + pixel_clk_params->flags.ENABLE_SS = 0; + pixel_clk_params->color_depth = + stream->timing.display_color_depth; + pixel_clk_params->flags.DISPLAY_BLANKED = 1; + pixel_clk_params->flags.SUPPORT_YCBCR420 = (stream->timing.pixel_encoding == + PIXEL_ENCODING_YCBCR420); + pixel_clk_params->pixel_encoding = stream->timing.pixel_encoding; + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) { + pixel_clk_params->color_depth = COLOR_DEPTH_888; + } + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) { + pixel_clk_params->requested_pix_clk = pixel_clk_params->requested_pix_clk / 2; + } +} + +void dce110_resource_build_pipe_hw_param(struct pipe_ctx *pipe_ctx) +{ + get_pixel_clock_parameters(pipe_ctx, &pipe_ctx->stream_res.pix_clk_params); + pipe_ctx->clock_source->funcs->get_pix_clk_dividers( + pipe_ctx->clock_source, + &pipe_ctx->stream_res.pix_clk_params, + &pipe_ctx->pll_settings); + resource_build_bit_depth_reduction_params(pipe_ctx->stream, + &pipe_ctx->stream->bit_depth_params); + pipe_ctx->stream->clamping.pixel_encoding = pipe_ctx->stream->timing.pixel_encoding; +} + +static bool is_surface_pixel_format_supported(struct pipe_ctx *pipe_ctx, unsigned int underlay_idx) +{ + if (pipe_ctx->pipe_idx != underlay_idx) + return true; + if (!pipe_ctx->plane_state) + return false; + if (pipe_ctx->plane_state->format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + return true; +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + if (!is_surface_pixel_format_supported(pipe_ctx, + dc->res_pool->underlay_pipe_index)) + return DC_SURFACE_PIXEL_FORMAT_UNSUPPORTED; + + dce110_resource_build_pipe_hw_param(pipe_ctx); + + /* TODO: validate audio ASIC caps, encoder */ + + resource_build_info_frame(pipe_ctx); + + return DC_OK; +} + +static bool dce110_validate_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + bool result = false; + + dm_logger_write( + dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "%s: start", + __func__); + + if (bw_calcs( + dc->ctx, + dc->bw_dceip, + dc->bw_vbios, + context->res_ctx.pipe_ctx, + dc->res_pool->pipe_count, + &context->bw.dce)) + result = true; + + if (!result) + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_VALIDATION, + "%s: %dx%d@%d Bandwidth validation failed!\n", + __func__, + context->streams[0]->timing.h_addressable, + context->streams[0]->timing.v_addressable, + context->streams[0]->timing.pix_clk_khz); + + if (memcmp(&dc->current_state->bw.dce, + &context->bw.dce, sizeof(context->bw.dce))) { + struct log_entry log_entry; + dm_logger_open( + dc->ctx->logger, + &log_entry, + LOG_BANDWIDTH_CALCS); + dm_logger_append(&log_entry, "%s: finish,\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + __func__, + context->bw.dce.nbp_state_change_wm_ns[0].b_mark, + context->bw.dce.nbp_state_change_wm_ns[0].a_mark, + context->bw.dce.urgent_wm_ns[0].b_mark, + context->bw.dce.urgent_wm_ns[0].a_mark, + context->bw.dce.stutter_exit_wm_ns[0].b_mark, + context->bw.dce.stutter_exit_wm_ns[0].a_mark); + dm_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + context->bw.dce.nbp_state_change_wm_ns[1].b_mark, + context->bw.dce.nbp_state_change_wm_ns[1].a_mark, + context->bw.dce.urgent_wm_ns[1].b_mark, + context->bw.dce.urgent_wm_ns[1].a_mark, + context->bw.dce.stutter_exit_wm_ns[1].b_mark, + context->bw.dce.stutter_exit_wm_ns[1].a_mark); + dm_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n", + context->bw.dce.nbp_state_change_wm_ns[2].b_mark, + context->bw.dce.nbp_state_change_wm_ns[2].a_mark, + context->bw.dce.urgent_wm_ns[2].b_mark, + context->bw.dce.urgent_wm_ns[2].a_mark, + context->bw.dce.stutter_exit_wm_ns[2].b_mark, + context->bw.dce.stutter_exit_wm_ns[2].a_mark, + context->bw.dce.stutter_mode_enable); + dm_logger_append(&log_entry, + "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n" + "sclk: %d sclk_sleep: %d yclk: %d blackout_recovery_time_us: %d\n", + context->bw.dce.cpuc_state_change_enable, + context->bw.dce.cpup_state_change_enable, + context->bw.dce.nbp_state_change_enable, + context->bw.dce.all_displays_in_sync, + context->bw.dce.dispclk_khz, + context->bw.dce.sclk_khz, + context->bw.dce.sclk_deep_sleep_khz, + context->bw.dce.yclk_khz, + context->bw.dce.blackout_recovery_time_us); + dm_logger_close(&log_entry); + } + return result; +} + +static bool dce110_validate_surface_sets( + struct dc_state *context) +{ + int i, j; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 2) + return false; + + for (j = 0; j < context->stream_status[i].plane_count; j++) { + struct dc_plane_state *plane = + context->stream_status[i].plane_states[j]; + + /* underlay validation */ + if (plane->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + + if ((plane->src_rect.width > 1920 || + plane->src_rect.height > 1080)) + return false; + + /* irrespective of plane format, + * stream should be RGB encoded + */ + if (context->streams[i]->timing.pixel_encoding + != PIXEL_ENCODING_RGB) + return false; + + } + + } + } + + return true; +} + +enum dc_status dce110_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce110_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static enum dc_status dce110_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + result = resource_map_pool_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, new_ctx, dc_stream); + + + if (result == DC_OK) + result = build_mapped_resource(dc, new_ctx, dc_stream); + + return result; +} + +static enum dc_status dce110_validate_guaranteed( + struct dc *dc, + struct dc_stream_state *dc_stream, + struct dc_state *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + context->streams[0] = dc_stream; + dc_stream_retain(context->streams[0]); + context->stream_count++; + + result = resource_map_pool_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = build_mapped_resource(dc, context, dc_stream); + + if (result == DC_OK) { + validate_guaranteed_copy_streams( + context, dc->caps.max_streams); + result = resource_build_scaling_params_for_context(dc, context); + } + + if (result == DC_OK) + if (!dce110_validate_bandwidth(dc, context)) + result = DC_FAIL_BANDWIDTH_VALIDATE; + + return result; +} + +static struct pipe_ctx *dce110_acquire_underlay( + struct dc_state *context, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + struct dc *dc = stream->ctx->dc; + struct resource_context *res_ctx = &context->res_ctx; + unsigned int underlay_idx = pool->underlay_pipe_index; + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[underlay_idx]; + + if (res_ctx->pipe_ctx[underlay_idx].stream) + return NULL; + + pipe_ctx->stream_res.tg = pool->timing_generators[underlay_idx]; + pipe_ctx->plane_res.mi = pool->mis[underlay_idx]; + /*pipe_ctx->plane_res.ipp = res_ctx->pool->ipps[underlay_idx];*/ + pipe_ctx->plane_res.xfm = pool->transforms[underlay_idx]; + pipe_ctx->stream_res.opp = pool->opps[underlay_idx]; + pipe_ctx->pipe_idx = underlay_idx; + + pipe_ctx->stream = stream; + + if (!dc->current_state->res_ctx.pipe_ctx[underlay_idx].stream) { + struct tg_color black_color = {0}; + struct dc_bios *dcb = dc->ctx->dc_bios; + + dc->hwss.enable_display_power_gating( + dc, + pipe_ctx->pipe_idx, + dcb, PIPE_GATING_CONTROL_DISABLE); + + /* + * This is for powering on underlay, so crtc does not + * need to be enabled + */ + + pipe_ctx->stream_res.tg->funcs->program_timing(pipe_ctx->stream_res.tg, + &stream->timing, + false); + + pipe_ctx->stream_res.tg->funcs->enable_advanced_request( + pipe_ctx->stream_res.tg, + true, + &stream->timing); + + pipe_ctx->plane_res.mi->funcs->allocate_mem_input(pipe_ctx->plane_res.mi, + stream->timing.h_total, + stream->timing.v_total, + stream->timing.pix_clk_khz, + context->stream_count); + + color_space_to_black_color(dc, + COLOR_SPACE_YCBCR601, &black_color); + pipe_ctx->stream_res.tg->funcs->set_blank_color( + pipe_ctx->stream_res.tg, + &black_color); + } + + return pipe_ctx; +} + +static void dce110_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + + +static const struct resource_funcs dce110_res_pool_funcs = { + .destroy = dce110_destroy_resource_pool, + .link_enc_create = dce110_link_encoder_create, + .validate_guaranteed = dce110_validate_guaranteed, + .validate_bandwidth = dce110_validate_bandwidth, + .acquire_idle_pipe_for_layer = dce110_acquire_underlay, + .add_stream_to_ctx = dce110_add_stream_to_ctx, + .validate_global = dce110_validate_global +}; + +static bool underlay_create(struct dc_context *ctx, struct resource_pool *pool) +{ + struct dce110_timing_generator *dce110_tgv = kzalloc(sizeof(*dce110_tgv), + GFP_KERNEL); + struct dce_transform *dce110_xfmv = kzalloc(sizeof(*dce110_xfmv), + GFP_KERNEL); + struct dce_mem_input *dce110_miv = kzalloc(sizeof(*dce110_miv), + GFP_KERNEL); + struct dce110_opp *dce110_oppv = kzalloc(sizeof(*dce110_oppv), + GFP_KERNEL); + + if ((dce110_tgv == NULL) || + (dce110_xfmv == NULL) || + (dce110_miv == NULL) || + (dce110_oppv == NULL)) + return false; + + dce110_opp_v_construct(dce110_oppv, ctx); + + dce110_timing_generator_v_construct(dce110_tgv, ctx); + dce110_mem_input_v_construct(dce110_miv, ctx); + dce110_transform_v_construct(dce110_xfmv, ctx); + + pool->opps[pool->pipe_count] = &dce110_oppv->base; + pool->timing_generators[pool->pipe_count] = &dce110_tgv->base; + pool->mis[pool->pipe_count] = &dce110_miv->base; + pool->transforms[pool->pipe_count] = &dce110_xfmv->base; + pool->pipe_count++; + + /* update the public caps to indicate an underlay is available */ + ctx->dc->caps.max_slave_planes = 1; + ctx->dc->caps.max_slave_planes = 1; + + return true; +} + +static void bw_calcs_data_update_from_pplib(struct dc *dc) +{ + struct dm_pp_clock_levels clks = {0}; + + /*do system clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &clks); + /* convert all the clock fro kHz to fix point mHz */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels/8], 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*2/8], 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*3/8], 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*4/8], 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*5/8], 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*6/8], 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + dc->sclk_lvls = clks; + + /*do display clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_DISPLAY_CLK, + &clks); + dc->bw_vbios->high_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios->mid_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1], 1000); + dc->bw_vbios->low_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + + /*do memory clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &clks); + + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[0] * MEMORY_TYPE_MULTIPLIER, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1] * MEMORY_TYPE_MULTIPLIER, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1] * MEMORY_TYPE_MULTIPLIER, + 1000); +} + +const struct resource_caps *dce110_resource_cap( + struct hw_asic_id *asic_id) +{ + if (ASIC_REV_IS_STONEY(asic_id->hw_internal_rev)) + return &stoney_resource_cap; + else + return &carrizo_resource_cap; +} + +static bool construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool, + struct hw_asic_id asic_id) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_firmware_info info; + struct dc_bios *bp; + struct dm_pp_static_clock_info static_clk_info = {0}; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = dce110_resource_cap(&ctx->asic_id); + pool->base.funcs = &dce110_res_pool_funcs; + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + + pool->base.pipe_count = pool->base.res_cap->num_timing_generator; + pool->base.underlay_pipe_index = pool->base.pipe_count; + + dc->caps.max_downscale_ratio = 150; + dc->caps.i2c_speed_in_khz = 100; + dc->caps.max_cursor_size = 128; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if ((bp->funcs->get_firmware_info(bp, &info) == BP_RESULT_OK) && + info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, + &clk_src_regs[1], false); + + pool->base.clk_src_count = 2; + + /* TODO: find out if CZ support 3 PLLs */ + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.display_clock = dce110_disp_clk_create(ctx, + &disp_clk_regs, + &disp_clk_shift, + &disp_clk_mask); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + /* get static clock information for PPLIB or firmware, save + * max_clock_state + */ + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) + pool->base.display_clock->max_clks_state = + static_clk_info.max_clocks_state; + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce110_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce110_timing_generator_create( + ctx, i, &dce110_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce110_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce110_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce110_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce110_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + +#if defined(CONFIG_DRM_AMD_DC_FBC) + dc->fbc_compressor = dce110_compressor_create(ctx); + + + +#endif + if (!underlay_create(ctx, &pool->base)) + goto res_create_fail; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce110_hw_sequencer_construct(dc); + + dc->caps.max_planes = pool->base.pipe_count; + + bw_calcs_init(dc->bw_dceip, dc->bw_vbios, dc->ctx->asic_id); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +res_create_fail: + destruct(pool); + return false; +} + +struct resource_pool *dce110_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc, + struct hw_asic_id asic_id) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (construct(num_virtual_links, dc, pool, asic_id)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h new file mode 100644 index 000000000000..e5f168c1f8c8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h @@ -0,0 +1,49 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_RESOURCE_DCE110_H__ +#define __DC_RESOURCE_DCE110_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +#define TO_DCE110_RES_POOL(pool)\ + container_of(pool, struct dce110_resource_pool, base) + +struct dce110_resource_pool { + struct resource_pool base; +}; + +void dce110_resource_build_pipe_hw_param(struct pipe_ctx *pipe_ctx); + +struct resource_pool *dce110_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc, + struct hw_asic_id asic_id); + +#endif /* __DC_RESOURCE_DCE110_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c new file mode 100644 index 000000000000..67ac737eaa7e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c @@ -0,0 +1,1966 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dc_types.h" +#include "dc_bios_types.h" +#include "dc.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce110_timing_generator.h" + +#include "timing_generator.h" + + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) + +#define CRTC_REG(reg) (reg + tg110->offsets.crtc) +#define DCP_REG(reg) (reg + tg110->offsets.dcp) + +/* Flowing register offsets are same in files of + * dce/dce_11_0_d.h + * dce/vi_polaris10_p/vi_polaris10_d.h + * + * So we can create dce110 timing generator to use it. + */ + + +/* +* apply_front_porch_workaround +* +* This is a workaround for a bug that has existed since R5xx and has not been +* fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. +*/ +static void dce110_timing_generator_apply_front_porch_workaround( + struct timing_generator *tg, + struct dc_crtc_timing *timing) +{ + if (timing->flags.INTERLACE == 1) { + if (timing->v_front_porch < 2) + timing->v_front_porch = 2; + } else { + if (timing->v_front_porch < 1) + timing->v_front_porch = 1; + } +} + +/** + ***************************************************************************** + * Function: is_in_vertical_blank + * + * @brief + * check the current status of CRTC to check if we are in Vertical Blank + * regioneased" state + * + * @return + * true if currently in blank region, false otherwise + * + ***************************************************************************** + */ +static bool dce110_timing_generator_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + addr = CRTC_REG(mmCRTC_STATUS); + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTC_STATUS, CRTC_V_BLANK); + return field == 1; +} + +void dce110_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + uint32_t regval; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = CRTC_REG(mmCRTC_CONTROL); + + regval = dm_read_reg(tg->ctx, address); + set_reg_field_value(regval, early_cntl, + CRTC_CONTROL, CRTC_HBLANK_EARLY_CONTROL); + dm_write_reg(tg->ctx, address, regval); +} + +/** + * Enable CRTC + * Enable CRTC - call ASIC Control Object to enable Timing generator. + */ +bool dce110_timing_generator_enable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + /* + * 3 is used to make sure V_UPDATE occurs at the beginning of the first + * line of vertical front porch + */ + set_reg_field_value( + value, + 0, + CRTC_MASTER_UPDATE_MODE, + MASTER_UPDATE_MODE); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_MODE), value); + + /* TODO: may want this on to catch underflow */ + value = 0; + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_LOCK), value); + + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, true); + + return result == BP_RESULT_OK; +} + +void dce110_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_BLACK_COLOR); + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); +} + +/** + ***************************************************************************** + * Function: disable_stereo + * + * @brief + * Disables active stereo on controller + * Frame Packing need to be disabled in vBlank or when CRTC not running + ***************************************************************************** + */ +#if 0 +@TODOSTEREO +static void disable_stereo(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_3D_STRUCTURE_CONTROL); + uint32_t value = 0; + uint32_t test = 0; + uint32_t field = 0; + uint32_t struc_en = 0; + uint32_t struc_stereo_sel_ovr = 0; + + value = dm_read_reg(tg->ctx, addr); + struc_en = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_EN); + + struc_stereo_sel_ovr = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_STEREO_SEL_OVR); + + /* + * When disabling Frame Packing in 2 step mode, we need to program both + * registers at the same frame + * Programming it in the beginning of VActive makes sure we are ok + */ + + if (struc_en != 0 && struc_stereo_sel_ovr == 0) { + tg->funcs->wait_for_vblank(tg); + tg->funcs->wait_for_vactive(tg); + } + + value = 0; + dm_write_reg(tg->ctx, addr, value); + + addr = tg->regs[IDX_CRTC_STEREO_CONTROL]; + dm_write_reg(tg->ctx, addr, value); +} +#endif + +/** + * disable_crtc - call ASIC Control Object to disable Timing generator. + */ +bool dce110_timing_generator_disable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, false); + + /* Need to make sure stereo is disabled according to the DCE5.0 spec */ + + /* + * @TODOSTEREO call this when adding stereo support + * tg->funcs->disable_stereo(tg); + */ + + return result == BP_RESULT_OK; +} + +/** +* program_horz_count_by_2 +* Programs DxCRTC_HORZ_COUNT_BY2_EN - 1 for DVI 30bpp mode, 0 otherwise +* +*/ +static void program_horz_count_by_2( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t regval; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + regval = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_COUNT_CONTROL)); + + set_reg_field_value(regval, 0, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + if (timing->flags.HORZ_COUNT_BY_TWO) + set_reg_field_value(regval, 1, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + dm_write_reg(tg->ctx, + CRTC_REG(mmCRTC_COUNT_CONTROL), regval); +} + +/** + * program_timing_generator + * Program CRTC Timing Registers - DxCRTC_H_*, DxCRTC_V_*, Pixel repetition. + * Call ASIC Control Object to program Timings. + */ +bool dce110_timing_generator_program_timing_generator( + struct timing_generator *tg, + const struct dc_crtc_timing *dc_crtc_timing) +{ + enum bp_result result; + struct bp_hw_crtc_timing_parameters bp_params; + struct dc_crtc_timing patched_crtc_timing; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + uint32_t vsync_offset = dc_crtc_timing->v_border_bottom + + dc_crtc_timing->v_front_porch; + uint32_t v_sync_start =dc_crtc_timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = dc_crtc_timing->h_border_right + + dc_crtc_timing->h_front_porch; + uint32_t h_sync_start = dc_crtc_timing->h_addressable + hsync_offset; + + memset(&bp_params, 0, sizeof(struct bp_hw_crtc_timing_parameters)); + + /* Due to an asic bug we need to apply the Front Porch workaround prior + * to programming the timing. + */ + + patched_crtc_timing = *dc_crtc_timing; + + dce110_timing_generator_apply_front_porch_workaround(tg, &patched_crtc_timing); + + bp_params.controller_id = tg110->controller_id; + + bp_params.h_total = patched_crtc_timing.h_total; + bp_params.h_addressable = + patched_crtc_timing.h_addressable; + bp_params.v_total = patched_crtc_timing.v_total; + bp_params.v_addressable = patched_crtc_timing.v_addressable; + + bp_params.h_sync_start = h_sync_start; + bp_params.h_sync_width = patched_crtc_timing.h_sync_width; + bp_params.v_sync_start = v_sync_start; + bp_params.v_sync_width = patched_crtc_timing.v_sync_width; + + /* Set overscan */ + bp_params.h_overscan_left = + patched_crtc_timing.h_border_left; + bp_params.h_overscan_right = + patched_crtc_timing.h_border_right; + bp_params.v_overscan_top = patched_crtc_timing.v_border_top; + bp_params.v_overscan_bottom = + patched_crtc_timing.v_border_bottom; + + /* Set flags */ + if (patched_crtc_timing.flags.HSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.HSYNC_POSITIVE_POLARITY = 1; + + if (patched_crtc_timing.flags.VSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.VSYNC_POSITIVE_POLARITY = 1; + + if (patched_crtc_timing.flags.INTERLACE == 1) + bp_params.flags.INTERLACE = 1; + + if (patched_crtc_timing.flags.HORZ_COUNT_BY_TWO == 1) + bp_params.flags.HORZ_COUNT_BY_TWO = 1; + + result = tg->bp->funcs->program_crtc_timing(tg->bp, &bp_params); + + program_horz_count_by_2(tg, &patched_crtc_timing); + + tg110->base.funcs->enable_advanced_request(tg, true, &patched_crtc_timing); + + /* Enable stereo - only when we need to pack 3D frame. Other types + * of stereo handled in explicit call */ + + return result == BP_RESULT_OK; +} + +/** + ***************************************************************************** + * Function: set_drr + * + * @brief + * Program dynamic refresh rate registers m_DxCRTC_V_TOTAL_*. + * + * @param [in] pHwCrtcTiming: point to H + * wCrtcTiming struct + ***************************************************************************** + */ +void dce110_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params) +{ + /* register values */ + uint32_t v_total_min = 0; + uint32_t v_total_max = 0; + uint32_t v_total_cntl = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + uint32_t addr = 0; + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + v_total_min = dm_read_reg(tg->ctx, addr); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + v_total_max = dm_read_reg(tg->ctx, addr); + + addr = CRTC_REG(mmCRTC_V_TOTAL_CONTROL); + v_total_cntl = dm_read_reg(tg->ctx, addr); + + if (params != NULL && + params->vertical_total_max > 0 && + params->vertical_total_min > 0) { + + set_reg_field_value(v_total_max, + params->vertical_total_max - 1, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + + set_reg_field_value(v_total_min, + params->vertical_total_min - 1, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK_EN); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + } else { + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + set_reg_field_value(v_total_min, + 0, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + set_reg_field_value(v_total_max, + 0, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + } + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + dm_write_reg(tg->ctx, addr, v_total_min); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + dm_write_reg(tg->ctx, addr, v_total_max); + + addr = CRTC_REG(mmCRTC_V_TOTAL_CONTROL); + dm_write_reg(tg->ctx, addr, v_total_cntl); +} + +void dce110_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t value) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t static_screen_cntl = 0; + uint32_t addr = 0; + + addr = CRTC_REG(mmCRTC_STATIC_SCREEN_CONTROL); + static_screen_cntl = dm_read_reg(tg->ctx, addr); + + set_reg_field_value(static_screen_cntl, + value, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK); + + set_reg_field_value(static_screen_cntl, + 2, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_FRAME_COUNT); + + dm_write_reg(tg->ctx, addr, static_screen_cntl); +} + +/* + * get_vblank_counter + * + * @brief + * Get counter for vertical blanks. use register CRTC_STATUS_FRAME_COUNT which + * holds the counter of frames. + * + * @param + * struct timing_generator *tg - [in] timing generator which controls the + * desired CRTC + * + * @return + * Counter of frames, which should equal to number of vblanks. + */ +uint32_t dce110_timing_generator_get_vblank_counter(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_STATUS_FRAME_COUNT); + uint32_t value = dm_read_reg(tg->ctx, addr); + uint32_t field = get_reg_field_value( + value, CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +/** + ***************************************************************************** + * Function: dce110_timing_generator_get_position + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] position + ***************************************************************************** + */ +void dce110_timing_generator_get_position(struct timing_generator *tg, + struct crtc_position *position) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_STATUS_POSITION)); + + position->horizontal_count = get_reg_field_value( + value, + CRTC_STATUS_POSITION, + CRTC_HORZ_COUNT); + + position->vertical_count = get_reg_field_value( + value, + CRTC_STATUS_POSITION, + CRTC_VERT_COUNT); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_NOM_VERT_POSITION)); + + position->nominal_vcount = get_reg_field_value( + value, + CRTC_NOM_VERT_POSITION, + CRTC_VERT_COUNT_NOM); +} + +/** + ***************************************************************************** + * Function: get_crtc_scanoutpos + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] vpos, hpos + ***************************************************************************** + */ +void dce110_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + struct crtc_position position; + + uint32_t value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_BLANK_START_END)); + + *v_blank_start = get_reg_field_value(value, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + *v_blank_end = get_reg_field_value(value, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + dce110_timing_generator_get_position( + tg, &position); + + *h_position = position.horizontal_count; + *v_position = position.vertical_count; +} + +/* TODO: is it safe to assume that mask/shift of Primary and Underlay + * are the same? + * For example: today CRTC_H_TOTAL == CRTCV_H_TOTAL but is it always + * guaranteed? */ +void dce110_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start =timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = CRTC_REG(mmCRTC_H_TOTAL); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTC_H_TOTAL, + CRTC_H_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_TOTAL); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL, + CRTC_V_TOTAL); + dm_write_reg(ctx, addr, value); + + /* In case of V_TOTAL_CONTROL is on, make sure V_TOTAL_MAX and + * V_TOTAL_MIN are equal to V_TOTAL. + */ + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_H_BLANK_START_END); + value = dm_read_reg(ctx, addr); + + tmp = timing->h_total - + (h_sync_start + timing->h_border_left); + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_BLANK_START_END); + value = dm_read_reg(ctx, addr); + + tmp = timing->v_total - (v_sync_start + timing->v_border_top); + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dm_write_reg(ctx, addr, value); +} + +void dce110_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value; + uint32_t addr; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + enum test_pattern_color_format bit_depth; + enum test_pattern_dyn_range dyn_range; + enum test_pattern_mode mode; + /* color ramp generator mixes 16-bits color */ + uint32_t src_bpc = 16; + /* requested bpc */ + uint32_t dst_bpc; + uint32_t index; + /* RGB values of the color bars. + * Produce two RGB colors: RGB0 - white (all Fs) + * and RGB1 - black (all 0s) + * (three RGB components for two colors) + */ + uint16_t src_color[6] = {0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0000}; + /* dest color (converted to the specified color format) */ + uint16_t dst_color[6]; + uint32_t inc_base; + + /* translate to bit depth */ + switch (color_depth) { + case COLOR_DEPTH_666: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_6; + break; + case COLOR_DEPTH_888: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + case COLOR_DEPTH_101010: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_10; + break; + case COLOR_DEPTH_121212: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_12; + break; + default: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + } + + switch (test_pattern) { + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: + { + dyn_range = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ? + TEST_PATTERN_DYN_RANGE_CEA : + TEST_PATTERN_DYN_RANGE_VESA); + mode = TEST_PATTERN_MODE_COLORSQUARES_RGB; + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + dyn_range, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + dm_write_reg(ctx, addr, value); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: + case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: + { + mode = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS ? + TEST_PATTERN_MODE_VERTICALBARS : + TEST_PATTERN_MODE_HORIZONTALBARS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* adjust color to the required colorFormat */ + for (index = 0; index < 6; index++) { + /* dst = 2^dstBpc * src / 2^srcBpc = src >> + * (srcBpc - dstBpc); + */ + dst_color[index] = + src_color[index] >> (src_bpc - dst_bpc); + /* CRTC_TEST_PATTERN_DATA has 16 bits, + * lowest 6 are hardwired to ZERO + * color bits should be left aligned aligned to MSB + * XXXXXXXXXX000000 for 10 bit, + * XXXXXXXX00000000 for 8 bit and XXXXXX0000000000 for 6 + */ + dst_color[index] <<= (16 - dst_bpc); + } + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + dm_write_reg(ctx, addr, value); + + /* We have to write the mask before data, similar to pipeline. + * For example, for 8 bpc, if we want RGB0 to be magenta, + * and RGB1 to be cyan, + * we need to make 7 writes: + * MASK DATA + * 000001 00000000 00000000 set mask to R0 + * 000010 11111111 00000000 R0 255, 0xFF00, set mask to G0 + * 000100 00000000 00000000 G0 0, 0x0000, set mask to B0 + * 001000 11111111 00000000 B0 255, 0xFF00, set mask to R1 + * 010000 00000000 00000000 R1 0, 0x0000, set mask to G1 + * 100000 11111111 00000000 G1 255, 0xFF00, set mask to B1 + * 100000 11111111 00000000 B1 255, 0xFF00 + * + * we will make a loop of 6 in which we prepare the mask, + * then write, then prepare the color for next write. + * first iteration will write mask only, + * but each next iteration color prepared in + * previous iteration will be written within new mask, + * the last component will written separately, + * mask is not changing between 6th and 7th write + * and color will be prepared by last iteration + */ + + /* write color, color values mask in CRTC_TEST_PATTERN_MASK + * is B1, G1, R1, B0, G0, R0 + */ + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_COLOR); + for (index = 0; index < 6; index++) { + /* prepare color mask, first write PATTERN_DATA + * will have all zeros + */ + set_reg_field_value( + value, + (1 << index), + CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_MASK); + /* write color component */ + dm_write_reg(ctx, addr, value); + /* prepare next color component, + * will be written in the next iteration + */ + set_reg_field_value( + value, + dst_color[index], + CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_DATA); + } + /* write last color component, + * it's been already prepared in the loop + */ + dm_write_reg(ctx, addr, value); + + /* enable test pattern */ + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + + dm_write_reg(ctx, addr, value); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: + { + mode = (bit_depth == + TEST_PATTERN_COLOR_FORMAT_BPC_10 ? + TEST_PATTERN_MODE_DUALRAMP_RGB : + TEST_PATTERN_MODE_SINGLERAMP_RGB); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* increment for the first ramp for one color gradation + * 1 gradation for 6-bit color is 2^10 + * gradations in 16-bit color + */ + inc_base = (src_bpc - dst_bpc); + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 8, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + inc_base + 2, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 8, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 5, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 384 << 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + default: + break; + } + dm_write_reg(ctx, addr, value); + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_COLOR); + dm_write_reg(ctx, addr, value); + + /* enable test pattern */ + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + /* add color depth translation here */ + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + + dm_write_reg(ctx, addr, value); + } + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + { + value = 0; + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL), value); + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_COLOR), value); + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS), + value); + } + break; + default: + break; + } +} + +/** +* dce110_timing_generator_validate_timing +* The timing generators support a maximum display size of is 8192 x 8192 pixels, +* including both active display and blanking periods. Check H Total and V Total. +*/ +bool dce110_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal) +{ + uint32_t h_blank; + uint32_t h_back_porch; + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + ASSERT(timing != NULL); + + if (!timing) + return false; + + /* Currently we don't support 3D, so block all 3D timings */ + if (timing->timing_3d_format != TIMING_3D_FORMAT_NONE) + return false; + + /* Temporarily blocking interlacing mode until it's supported */ + if (timing->flags.INTERLACE == 1) + return false; + + /* Check maximum number of pixels supported by Timing Generator + * (Currently will never fail, in order to fail needs display which + * needs more than 8192 horizontal and + * more than 8192 vertical total pixels) + */ + if (timing->h_total > tg110->max_h_total || + timing->v_total > tg110->max_v_total) + return false; + + h_blank = (timing->h_total - timing->h_addressable - + timing->h_border_right - + timing->h_border_left); + + if (h_blank < tg110->min_h_blank) + return false; + + if (timing->h_front_porch < tg110->min_h_front_porch) + return false; + + h_back_porch = h_blank - (h_sync_start - + timing->h_addressable - + timing->h_border_right - + timing->h_sync_width); + + if (h_back_porch < tg110->min_h_back_porch) + return false; + + return true; +} + +/** +* Wait till we are at the beginning of VBlank. +*/ +void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/** +* Wait till we are in VActive (anywhere in VActive) +*/ +void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg) +{ + while (dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/** + ***************************************************************************** + * Function: dce110_timing_generator_setup_global_swap_lock + * + * @brief + * Setups Global Swap Lock group for current pipe + * Pipe can join or leave GSL group, become a TimingServer or TimingClient + * + * @param [in] gsl_params: setup data + ***************************************************************************** + */ + +void dce110_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = DCP_REG(mmDCP_GSL_CONTROL); + uint32_t check_point = FLIP_READY_BACK_LOOKUP; + + value = dm_read_reg(tg->ctx, address); + + /* This pipe will belong to GSL Group zero. */ + set_reg_field_value(value, + 1, + DCP_GSL_CONTROL, + DCP_GSL0_EN); + + set_reg_field_value(value, + gsl_params->gsl_master == tg->inst, + DCP_GSL_CONTROL, + DCP_GSL_MASTER_EN); + + set_reg_field_value(value, + HFLIP_READY_DELAY, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_FORCE_DELAY); + + /* Keep signal low (pending high) during 6 lines. + * Also defines minimum interval before re-checking signal. */ + set_reg_field_value(value, + HFLIP_CHECK_DELAY, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_CHECK_DELAY); + + + { + uint32_t value_crtc_vtotal; + + value_crtc_vtotal = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_TOTAL)); + + set_reg_field_value(value, + 0,/* DCP_GSL_PURPOSE_SURFACE_FLIP */ + DCP_GSL_CONTROL, + DCP_GSL_SYNC_SOURCE); + + /* Checkpoint relative to end of frame */ + check_point = get_reg_field_value(value_crtc_vtotal, + CRTC_V_TOTAL, + CRTC_V_TOTAL); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_GSL_WINDOW), 0); + } + + set_reg_field_value(value, + 1, + DCP_GSL_CONTROL, + DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); + + dm_write_reg(tg->ctx, address, value); + + /********************************************************************/ + address = CRTC_REG(mmCRTC_GSL_CONTROL); + + value = 0; + set_reg_field_value(value, + check_point - FLIP_READY_BACK_LOOKUP, + CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM); + + set_reg_field_value(value, + VFLIP_READY_DELAY, + CRTC_GSL_CONTROL, + CRTC_GSL_FORCE_DELAY); + + dm_write_reg(tg->ctx, address, value); +} + +void dce110_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + /* Clear all the register writes done by + * dce110_timing_generator_setup_global_swap_lock + */ + + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = DCP_REG(mmDCP_GSL_CONTROL); + + value = 0; + + /* This pipe will belong to GSL Group zero. */ + /* Settig HW default values from reg specs */ + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL0_EN); + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_MASTER_EN); + + set_reg_field_value(value, + 0x2, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_FORCE_DELAY); + + set_reg_field_value(value, + 0x6, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_CHECK_DELAY); + + /* Restore DCP_GSL_PURPOSE_SURFACE_FLIP */ + { + uint32_t value_crtc_vtotal; + + value_crtc_vtotal = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_TOTAL)); + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_SYNC_SOURCE); + } + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); + + dm_write_reg(tg->ctx, address, value); + + /********************************************************************/ + address = CRTC_REG(mmCRTC_GSL_CONTROL); + + value = 0; + set_reg_field_value(value, + 0, + CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM); + + set_reg_field_value(value, + 0x2, + CRTC_GSL_CONTROL, + CRTC_GSL_FORCE_DELAY); + + dm_write_reg(tg->ctx, address, value); +} +/** + ***************************************************************************** + * Function: is_counter_moving + * + * @brief + * check if the timing generator is currently going + * + * @return + * true if currently going, false if currently paused or stopped. + * + ***************************************************************************** + */ +bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg) +{ + struct crtc_position position1, position2; + + tg->funcs->get_position(tg, &position1); + tg->funcs->get_position(tg, &position2); + + if (position1.horizontal_count == position2.horizontal_count && + position1.vertical_count == position2.vertical_count) + return false; + else + return true; +} + +void dce110_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dm_write_reg(tg->ctx, addr, value); +} + +/*TODO: Figure out if we need this function. */ +void dce110_timing_generator_set_lock_master(struct timing_generator *tg, + bool lock) +{ + struct dc_context *ctx = tg->ctx; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_MASTER_UPDATE_LOCK); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + lock ? 1 : 0, + CRTC_MASTER_UPDATE_LOCK, + MASTER_UPDATE_LOCK); + + dm_write_reg(ctx, addr, value); +} + +void dce110_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source_tg_inst) +{ + uint32_t value; + uint32_t rising_edge = 0; + uint32_t falling_edge = 0; + enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Setup trigger edge */ + { + uint32_t pol_value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_SYNC_A_CNTL)); + + /* Register spec has reversed definition: + * 0 for positive, 1 for negative */ + if (get_reg_field_value(pol_value, + CRTC_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL) == 0) { + rising_edge = 1; + } else { + falling_edge = 1; + } + } + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL)); + + trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; + + set_reg_field_value(value, + trig_src_select, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT); + + set_reg_field_value(value, + TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_POLARITY_SELECT); + + set_reg_field_value(value, + rising_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_RISING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + falling_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + 0, /* send every signal */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_FREQUENCY_SELECT); + + set_reg_field_value(value, + 0, /* no delay */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_DELAY); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL), value); + + /**************************************************************/ + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 2, /* force H count to H_TOTAL and V count to V_TOTAL */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 1, /* TriggerB - we never use TriggerA */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_TRIG_SEL); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); +} + +void dce110_timing_generator_disable_reset_trigger( + struct timing_generator *tg) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 0, /* force counter now mode is disabled */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); + + /********************************************************************/ + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL)); + + set_reg_field_value(value, + TRIGGER_SOURCE_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT); + + set_reg_field_value(value, + TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_POLARITY_SELECT); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL), value); +} + +/** + ***************************************************************************** + * @brief + * Checks whether CRTC triggered reset occurred + * + * @return + * true if triggered reset occurred, false otherwise + ***************************************************************************** + */ +bool dce110_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + return get_reg_field_value(value, + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; +} + +/** + * dce110_timing_generator_disable_vga + * Turn OFF VGA Mode and Timing - DxVGA_CONTROL + * VGA Mode and VGA Timing is used by VBIOS on CRT Monitors; + */ +void dce110_timing_generator_disable_vga( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + switch (tg110->controller_id) { + case CONTROLLER_ID_D0: + addr = mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D1: + addr = mmD2VGA_CONTROL; + break; + case CONTROLLER_ID_D2: + addr = mmD3VGA_CONTROL; + break; + case CONTROLLER_ID_D3: + addr = mmD4VGA_CONTROL; + break; + case CONTROLLER_ID_D4: + addr = mmD5VGA_CONTROL; + break; + case CONTROLLER_ID_D5: + addr = mmD6VGA_CONTROL; + break; + default: + break; + } + value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); + set_reg_field_value( + value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); + + dm_write_reg(tg->ctx, addr, value); +} + +/** +* set_overscan_color_black +* +* @param :black_color is one of the color space +* :this routine will set overscan black color according to the color space. +* @return none +*/ + +void dce110_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t addr; + uint32_t value = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + set_reg_field_value( + value, + color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + set_reg_field_value( + value, + color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + addr = CRTC_REG(mmCRTC_OVERSCAN_COLOR); + dm_write_reg(ctx, addr, value); + addr = CRTC_REG(mmCRTC_BLACK_COLOR); + dm_write_reg(ctx, addr, value); + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + addr = CRTC_REG(mmCRTC_BLANK_DATA_COLOR); + dm_write_reg(ctx, addr, value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ + +} + +void dce110_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_BLACK_COLOR); + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); + + addr = CRTC_REG(mmCRTC_BLANK_DATA_COLOR); + dm_write_reg(tg->ctx, addr, value); +} + +void dce110_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + set_reg_field_value( + value, + overscan_color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + overscan_color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + overscan_color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + addr = CRTC_REG(mmCRTC_OVERSCAN_COLOR); + dm_write_reg(ctx, addr, value); +} + +void dce110_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce110_timing_generator_program_blanking(tg, timing); +} + +bool dce110_tg_is_blanked(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL)); + + if (get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + return true; + return false; +} + +void dce110_tg_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + set_reg_field_value( + value, + 1, + CRTC_DOUBLE_BUFFER_CONTROL, + CRTC_BLANK_DATA_DOUBLE_BUFFER_EN); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_DOUBLE_BUFFER_CONTROL), value); + value = 0; + + if (enable_blanking) { + set_reg_field_value( + value, + 1, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL), value); + + } else + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL), 0); +} + +bool dce110_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + return dce110_timing_generator_validate_timing(tg, timing, SIGNAL_TYPE_NONE); +} + +void dce110_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce110_timing_generator_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce110_timing_generator_wait_for_vactive(tg); + break; + + default: + break; + } +} + +void dce110_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce110_tg_program_blank_color(tg, blank_color); + if (overscan_color != NULL) + dce110_tg_set_overscan_color(tg, overscan_color); +} + +/* Gets first line of blank region of the display timing for CRTC + * and programms is as a trigger to fire vertical interrupt + */ +bool dce110_arm_vert_intr(struct timing_generator *tg, uint8_t width) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t v_blank_start = 0; + uint32_t v_blank_end = 0; + uint32_t val = 0; + uint32_t h_position, v_position; + + tg->funcs->get_scanoutpos( + tg, + &v_blank_start, + &v_blank_end, + &h_position, + &v_position); + + if (v_blank_start == 0 || v_blank_end == 0) + return false; + + set_reg_field_value( + val, + v_blank_start, + CRTC_VERTICAL_INTERRUPT0_POSITION, + CRTC_VERTICAL_INTERRUPT0_LINE_START); + + /* Set interval width for interrupt to fire to 1 scanline */ + set_reg_field_value( + val, + v_blank_start + width, + CRTC_VERTICAL_INTERRUPT0_POSITION, + CRTC_VERTICAL_INTERRUPT0_LINE_END); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_VERTICAL_INTERRUPT0_POSITION), val); + + return true; +} + +static const struct timing_generator_funcs dce110_tg_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = dce110_tg_program_timing, + .enable_crtc = dce110_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + .is_counter_moving = dce110_timing_generator_is_counter_moving, + .get_position = dce110_timing_generator_get_position, + .get_frame_count = dce110_timing_generator_get_vblank_counter, + .get_scanoutpos = dce110_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce110_timing_generator_set_early_control, + .wait_for_state = dce110_tg_wait_for_state, + .set_blank = dce110_tg_set_blank, + .is_blanked = dce110_tg_is_blanked, + .set_colors = dce110_tg_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_program_blank_color, + .disable_vga = dce110_timing_generator_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_tear_down_global_swap_lock, + .enable_advanced_request = + dce110_timing_generator_enable_advanced_request, + .set_drr = + dce110_timing_generator_set_drr, + .set_static_screen_control = + dce110_timing_generator_set_static_screen_control, + .set_test_pattern = dce110_timing_generator_set_test_pattern, + .arm_vert_intr = dce110_arm_vert_intr, +}; + +void dce110_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + + tg110->offsets = *offsets; + + tg110->base.funcs = &dce110_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h new file mode 100644 index 000000000000..82737dea6984 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h @@ -0,0 +1,273 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TIMING_GENERATOR_DCE110_H__ +#define __DC_TIMING_GENERATOR_DCE110_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" + +/* GSL Sync related values */ + +/* In VSync mode, after 4 units of time, master pipe will generate + * flip_ready signal */ +#define VFLIP_READY_DELAY 4 +/* In HSync mode, after 2 units of time, master pipe will generate + * flip_ready signal */ +#define HFLIP_READY_DELAY 2 +/* 6 lines delay between forcing flip and checking all pipes ready */ +#define HFLIP_CHECK_DELAY 6 +/* 3 lines before end of frame */ +#define FLIP_READY_BACK_LOOKUP 3 + +/* Trigger Source Select - ASIC-defendant, actual values for the + * register programming */ +enum trigger_source_select { + TRIGGER_SOURCE_SELECT_LOGIC_ZERO = 0, + TRIGGER_SOURCE_SELECT_CRTC_VSYNCA = 1, + TRIGGER_SOURCE_SELECT_CRTC_HSYNCA = 2, + TRIGGER_SOURCE_SELECT_CRTC_VSYNCB = 3, + TRIGGER_SOURCE_SELECT_CRTC_HSYNCB = 4, + TRIGGER_SOURCE_SELECT_GENERICF = 5, + TRIGGER_SOURCE_SELECT_GENERICE = 6, + TRIGGER_SOURCE_SELECT_VSYNCA = 7, + TRIGGER_SOURCE_SELECT_HSYNCA = 8, + TRIGGER_SOURCE_SELECT_VSYNCB = 9, + TRIGGER_SOURCE_SELECT_HSYNCB = 10, + TRIGGER_SOURCE_SELECT_HPD1 = 11, + TRIGGER_SOURCE_SELECT_HPD2 = 12, + TRIGGER_SOURCE_SELECT_GENERICD = 13, + TRIGGER_SOURCE_SELECT_GENERICC = 14, + TRIGGER_SOURCE_SELECT_VIDEO_CAPTURE = 15, + TRIGGER_SOURCE_SELECT_GSL_GROUP0 = 16, + TRIGGER_SOURCE_SELECT_GSL_GROUP1 = 17, + TRIGGER_SOURCE_SELECT_GSL_GROUP2 = 18, + TRIGGER_SOURCE_SELECT_BLONY = 19, + TRIGGER_SOURCE_SELECT_GENERICA = 20, + TRIGGER_SOURCE_SELECT_GENERICB = 21, + TRIGGER_SOURCE_SELECT_GSL_ALLOW_FLIP = 22, + TRIGGER_SOURCE_SELECT_MANUAL_TRIGGER = 23 +}; + +/* Trigger Source Select - ASIC-dependant, actual values for the + * register programming */ +enum trigger_polarity_select { + TRIGGER_POLARITY_SELECT_LOGIC_ZERO = 0, + TRIGGER_POLARITY_SELECT_CRTC = 1, + TRIGGER_POLARITY_SELECT_GENERICA = 2, + TRIGGER_POLARITY_SELECT_GENERICB = 3, + TRIGGER_POLARITY_SELECT_HSYNCA = 4, + TRIGGER_POLARITY_SELECT_HSYNCB = 5, + TRIGGER_POLARITY_SELECT_VIDEO_CAPTURE = 6, + TRIGGER_POLARITY_SELECT_GENERICC = 7 +}; + + +struct dce110_timing_generator_offsets { + int32_t crtc; + int32_t dcp; + + /* DCE80 use only */ + int32_t dmif; +}; + +struct dce110_timing_generator { + struct timing_generator base; + struct dce110_timing_generator_offsets offsets; + struct dce110_timing_generator_offsets derived_offsets; + + enum controller_id controller_id; + + uint32_t max_h_total; + uint32_t max_v_total; + + uint32_t min_h_blank; + uint32_t min_h_front_porch; + uint32_t min_h_back_porch; + + /* DCE 12 */ + uint32_t min_h_sync_width; + uint32_t min_v_sync_width; + uint32_t min_v_blank; + +}; + +#define DCE110TG_FROM_TG(tg)\ + container_of(tg, struct dce110_timing_generator, base) + +void dce110_timing_generator_construct( + struct dce110_timing_generator *tg, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +/* determine if given timing can be supported by TG */ +bool dce110_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal); + +/******** HW programming ************/ + +/* Program timing generator with given timing */ +bool dce110_timing_generator_program_timing_generator( + struct timing_generator *tg, + const struct dc_crtc_timing *dc_crtc_timing); + +/* Disable/Enable Timing Generator */ +bool dce110_timing_generator_enable_crtc(struct timing_generator *tg); +bool dce110_timing_generator_disable_crtc(struct timing_generator *tg); + +void dce110_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl); + +/**************** TG current status ******************/ + +/* return the current frame counter. Used by Linux kernel DRM */ +uint32_t dce110_timing_generator_get_vblank_counter( + struct timing_generator *tg); + +void dce110_timing_generator_get_position( + struct timing_generator *tg, + struct crtc_position *position); + +/* return true if TG counter is moving. false if TG is stopped */ +bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg); + +/* wait until TG is in beginning of vertical blank region */ +void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg); + +/* wait until TG is in beginning of active region */ +void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg); + +/*********** Timing Generator Synchronization routines ****/ + +/* Setups Global Swap Lock group, TimingServer or TimingClient*/ +void dce110_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params); + +/* Clear all the register writes done by setup_global_swap_lock */ +void dce110_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg); + +/* Reset slave controllers on master VSync */ +void dce110_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source); + +/* disabling trigger-reset */ +void dce110_timing_generator_disable_reset_trigger( + struct timing_generator *tg); + +/* Checks whether CRTC triggered reset occurred */ +bool dce110_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg); + +/******** Stuff to move to other virtual HW objects *****************/ +/* Move to enable accelerated mode */ +void dce110_timing_generator_disable_vga(struct timing_generator *tg); +/* TODO: Should we move it to transform */ +/* Fully program CRTC timing in timing generator */ +void dce110_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +/* TODO: Should we move it to opp? */ +/* Combine with below and move YUV/RGB color conversion to SW layer */ +void dce110_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color); +/* Combine with above and move YUV/RGB color conversion to SW layer */ +void dce110_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color); +void dce110_timing_generator_color_space_to_black_color( + enum dc_color_space colorspace, + struct tg_color *black_color); +/*************** End-of-move ********************/ + +/* Not called yet */ +void dce110_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth); + +void dce110_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params); + +void dce110_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t value); + +void dce110_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position); + +void dce110_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing); + +void dce110_timing_generator_set_lock_master(struct timing_generator *tg, + bool lock); + +void dce110_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color); + +void dce110_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color); + +void dce110_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios); + +bool dce110_tg_is_blanked(struct timing_generator *tg); + +void dce110_tg_set_blank(struct timing_generator *tg, + bool enable_blanking); + +bool dce110_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +void dce110_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state); + +void dce110_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color); + +bool dce110_arm_vert_intr( + struct timing_generator *tg, uint8_t width); + +#endif /* __DC_TIMING_GENERATOR_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c new file mode 100644 index 000000000000..07d9303d5477 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c @@ -0,0 +1,688 @@ +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dc_types.h" +#include "dc_bios_types.h" +#include "dc.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce110_timing_generator.h" +#include "dce110_timing_generator_v.h" + +#include "timing_generator.h" + +/** ******************************************************************************** + * + * DCE11 Timing Generator Implementation + * + **********************************************************************************/ + +/** +* Enable CRTCV +*/ + +static bool dce110_timing_generator_v_enable_crtc(struct timing_generator *tg) +{ +/* +* Set MASTER_UPDATE_MODE to 0 +* This is needed for DRR, and also suggested to be default value by Syed. +*/ + + uint32_t value; + + value = 0; + set_reg_field_value(value, 0, + CRTCV_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); + dm_write_reg(tg->ctx, + mmCRTCV_MASTER_UPDATE_MODE, value); + + /* TODO: may want this on for looking for underflow */ + value = 0; + dm_write_reg(tg->ctx, mmCRTCV_MASTER_UPDATE_MODE, value); + + value = 0; + set_reg_field_value(value, 1, + CRTCV_MASTER_EN, CRTC_MASTER_EN); + dm_write_reg(tg->ctx, + mmCRTCV_MASTER_EN, value); + + return true; +} + +static bool dce110_timing_generator_v_disable_crtc(struct timing_generator *tg) +{ + uint32_t value; + + value = dm_read_reg(tg->ctx, + mmCRTCV_CONTROL); + set_reg_field_value(value, 0, + CRTCV_CONTROL, CRTC_DISABLE_POINT_CNTL); + set_reg_field_value(value, 0, + CRTCV_CONTROL, CRTC_MASTER_EN); + dm_write_reg(tg->ctx, + mmCRTCV_CONTROL, value); + /* + * TODO: call this when adding stereo support + * tg->funcs->disable_stereo(tg); + */ + return true; +} + +static void dce110_timing_generator_v_blank_crtc(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_BLANK_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + 1, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DE_MODE); + + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_unblank_crtc(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_BLANK_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DE_MODE); + + dm_write_reg(tg->ctx, addr, value); +} + +static bool dce110_timing_generator_v_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + + addr = mmCRTCV_STATUS; + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTCV_STATUS, CRTC_V_BLANK); + return field == 1; +} + +static bool dce110_timing_generator_v_is_counter_moving(struct timing_generator *tg) +{ + uint32_t value; + uint32_t h1 = 0; + uint32_t h2 = 0; + uint32_t v1 = 0; + uint32_t v2 = 0; + + value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); + + h1 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_HORZ_COUNT); + + v1 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_VERT_COUNT); + + value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); + + h2 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_HORZ_COUNT); + + v2 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_VERT_COUNT); + + if (h1 == h2 && v1 == v2) + return false; + else + return true; +} + +static void dce110_timing_generator_v_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/** +* Wait till we are in VActive (anywhere in VActive) +*/ +static void dce110_timing_generator_v_wait_for_vactive(struct timing_generator *tg) +{ + while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +static void dce110_timing_generator_v_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce110_timing_generator_v_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce110_timing_generator_v_wait_for_vactive(tg); + break; + + default: + break; + } +} + +static void dce110_timing_generator_v_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start = timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = mmCRTCV_H_TOTAL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTCV_H_TOTAL, + CRTC_H_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_TOTAL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTCV_V_TOTAL, + CRTC_V_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_BLANK_START_END; + value = dm_read_reg(ctx, addr); + + tmp = timing->h_total - + (h_sync_start + timing->h_border_left); + + set_reg_field_value( + value, + tmp, + CRTCV_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + set_reg_field_value( + value, + tmp, + CRTCV_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_BLANK_START_END; + value = dm_read_reg(ctx, addr); + + tmp = timing->v_total - (v_sync_start + timing->v_border_top); + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_SYNC_A; + value = 0; + set_reg_field_value( + value, + timing->h_sync_width, + CRTCV_H_SYNC_A, + CRTC_H_SYNC_A_END); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_SYNC_A_CNTL; + value = dm_read_reg(ctx, addr); + if (timing->flags.HSYNC_POSITIVE_POLARITY) { + set_reg_field_value( + value, + 0, + CRTCV_H_SYNC_A_CNTL, + CRTC_H_SYNC_A_POL); + } else { + set_reg_field_value( + value, + 1, + CRTCV_H_SYNC_A_CNTL, + CRTC_H_SYNC_A_POL); + } + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_SYNC_A; + value = 0; + set_reg_field_value( + value, + timing->v_sync_width, + CRTCV_V_SYNC_A, + CRTC_V_SYNC_A_END); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_SYNC_A_CNTL; + value = dm_read_reg(ctx, addr); + if (timing->flags.VSYNC_POSITIVE_POLARITY) { + set_reg_field_value( + value, + 0, + CRTCV_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL); + } else { + set_reg_field_value( + value, + 1, + CRTCV_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL); + } + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_INTERLACE_CONTROL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->flags.INTERLACE, + CRTCV_INTERLACE_CONTROL, + CRTC_INTERLACE_ENABLE); + dm_write_reg(ctx, addr, value); +} + +static void dce110_timing_generator_v_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + uint32_t addr = mmCRTCV_START_LINE_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + } else { + set_reg_field_value( + value, + 4, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + } + set_reg_field_value( + value, + 0, + CRTCV_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 2, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTCV_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + if (enable_blanking) + dce110_timing_generator_v_blank_crtc(tg); + else + dce110_timing_generator_v_unblank_crtc(tg); +} + +static void dce110_timing_generator_v_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce110_timing_generator_v_program_blanking(tg, timing); +} + +static void dce110_timing_generator_v_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + uint32_t addr = mmCRTCV_BLACK_COLOR; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t addr; + uint32_t value = 0; + + set_reg_field_value( + value, + color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + set_reg_field_value( + value, + color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + addr = mmCRTCV_OVERSCAN_COLOR; + dm_write_reg(ctx, addr, value); + addr = mmCRTCV_BLACK_COLOR; + dm_write_reg(ctx, addr, value); + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + addr = mmCRTCV_BLANK_DATA_COLOR; + dm_write_reg(ctx, addr, value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ +} + +static void dce110_tg_v_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + uint32_t addr = mmCRTCV_BLACK_COLOR; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); + + addr = mmCRTCV_BLANK_DATA_COLOR; + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr; + + set_reg_field_value( + value, + overscan_color->color_b_cb, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + overscan_color->color_g_y, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + overscan_color->color_r_cr, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + addr = mmCRTCV_OVERSCAN_COLOR; + dm_write_reg(ctx, addr, value); +} + +static void dce110_timing_generator_v_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce110_tg_v_program_blank_color(tg, blank_color); + if (overscan_color != NULL) + dce110_timing_generator_v_set_overscan_color(tg, overscan_color); +} + +static void dce110_timing_generator_v_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + uint32_t regval; + uint32_t address = mmCRTC_CONTROL; + + regval = dm_read_reg(tg->ctx, address); + set_reg_field_value(regval, early_cntl, + CRTCV_CONTROL, CRTC_HBLANK_EARLY_CONTROL); + dm_write_reg(tg->ctx, address, regval); +} + +static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_STATUS_FRAME_COUNT; + uint32_t value = dm_read_reg(tg->ctx, addr); + uint32_t field = get_reg_field_value( + value, CRTCV_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +static bool dce110_timing_generator_v_did_triggered_reset_occur( + struct timing_generator *tg) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return false; +} + +static void dce110_timing_generator_v_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_enable_reset_trigger( + struct timing_generator *tg, + int source_tg_inst) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_disable_reset_trigger( + struct timing_generator *tg) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_disable_vga( + struct timing_generator *tg) +{ + return; +} + +static bool dce110_tg_v_is_blanked(struct timing_generator *tg) +{ + /* Signal comes from the primary pipe, underlay is never blanked. */ + return false; +} + +/** ******************************************************************************************** + * + * DCE11 Timing Generator Constructor / Destructor + * + *********************************************************************************************/ +static const struct timing_generator_funcs dce110_tg_v_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = dce110_timing_generator_v_program_timing, + .enable_crtc = dce110_timing_generator_v_enable_crtc, + .disable_crtc = dce110_timing_generator_v_disable_crtc, + .is_counter_moving = dce110_timing_generator_v_is_counter_moving, + .get_position = NULL, /* Not to be implemented for underlay*/ + .get_frame_count = dce110_timing_generator_v_get_vblank_counter, + .set_early_control = dce110_timing_generator_v_set_early_control, + .wait_for_state = dce110_timing_generator_v_wait_for_state, + .set_blank = dce110_timing_generator_v_set_blank, + .is_blanked = dce110_tg_v_is_blanked, + .set_colors = dce110_timing_generator_v_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_v_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_v_program_blank_color, + .disable_vga = dce110_timing_generator_v_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_v_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_v_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_v_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_v_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_v_tear_down_global_swap_lock, + .enable_advanced_request = + dce110_timing_generator_v_enable_advanced_request +}; + +void dce110_timing_generator_v_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx) +{ + tg110->controller_id = CONTROLLER_ID_UNDERLAY0; + + tg110->base.funcs = &dce110_tg_v_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h new file mode 100644 index 000000000000..d2623a5994e8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h @@ -0,0 +1,33 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TIMING_GENERATOR_V_DCE110_H__ +#define __DC_TIMING_GENERATOR_V_DCE110_H__ + +void dce110_timing_generator_v_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx); + +#endif /* __DC_TIMING_GENERATOR_V_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c new file mode 100644 index 000000000000..47390dc58306 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c @@ -0,0 +1,716 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce110_transform_v.h" +#include "dm_services.h" +#include "dc.h" +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define SCLV_PHASES 64 + +struct sclv_ratios_inits { + uint32_t h_int_scale_ratio_luma; + uint32_t h_int_scale_ratio_chroma; + uint32_t v_int_scale_ratio_luma; + uint32_t v_int_scale_ratio_chroma; + struct init_int_and_frac h_init_luma; + struct init_int_and_frac h_init_chroma; + struct init_int_and_frac v_init_luma; + struct init_int_and_frac v_init_chroma; +}; + +static void calculate_viewport( + const struct scaler_data *scl_data, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + /*Do not set chroma vp for rgb444 pixel format*/ + luma_viewport->x = scl_data->viewport.x - scl_data->viewport.x % 2; + luma_viewport->y = scl_data->viewport.y - scl_data->viewport.y % 2; + luma_viewport->width = + scl_data->viewport.width - scl_data->viewport.width % 2; + luma_viewport->height = + scl_data->viewport.height - scl_data->viewport.height % 2; + chroma_viewport->x = luma_viewport->x; + chroma_viewport->y = luma_viewport->y; + chroma_viewport->height = luma_viewport->height; + chroma_viewport->width = luma_viewport->width; + + if (scl_data->format == PIXEL_FORMAT_420BPP8) { + luma_viewport->height += luma_viewport->height % 2; + luma_viewport->width += luma_viewport->width % 2; + /*for 420 video chroma is 1/4 the area of luma, scaled + *vertically and horizontally + */ + chroma_viewport->x = luma_viewport->x / 2; + chroma_viewport->y = luma_viewport->y / 2; + chroma_viewport->height = luma_viewport->height / 2; + chroma_viewport->width = luma_viewport->width / 2; + } +} + +static void program_viewport( + struct dce_transform *xfm_dce, + struct rect *luma_view_port, + struct rect *chroma_view_port) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t value = 0; + uint32_t addr = 0; + + if (luma_view_port->width != 0 && luma_view_port->height != 0) { + addr = mmSCLV_VIEWPORT_START; + value = 0; + set_reg_field_value( + value, + luma_view_port->x, + SCLV_VIEWPORT_START, + VIEWPORT_X_START); + set_reg_field_value( + value, + luma_view_port->y, + SCLV_VIEWPORT_START, + VIEWPORT_Y_START); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VIEWPORT_SIZE; + value = 0; + set_reg_field_value( + value, + luma_view_port->height, + SCLV_VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + set_reg_field_value( + value, + luma_view_port->width, + SCLV_VIEWPORT_SIZE, + VIEWPORT_WIDTH); + dm_write_reg(ctx, addr, value); + } + + if (chroma_view_port->width != 0 && chroma_view_port->height != 0) { + addr = mmSCLV_VIEWPORT_START_C; + value = 0; + set_reg_field_value( + value, + chroma_view_port->x, + SCLV_VIEWPORT_START_C, + VIEWPORT_X_START_C); + set_reg_field_value( + value, + chroma_view_port->y, + SCLV_VIEWPORT_START_C, + VIEWPORT_Y_START_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VIEWPORT_SIZE_C; + value = 0; + set_reg_field_value( + value, + chroma_view_port->height, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_HEIGHT_C); + set_reg_field_value( + value, + chroma_view_port->width, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_WIDTH_C); + dm_write_reg(ctx, addr, value); + } +} + +/* + * Function: + * void setup_scaling_configuration + * + * Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps + * Input: data + * + * Output: + * void + */ +static bool setup_scaling_configuration( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + bool is_scaling_needed = false; + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t value = 0; + + set_reg_field_value(value, data->taps.h_taps - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS); + set_reg_field_value(value, data->taps.v_taps - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS); + set_reg_field_value(value, data->taps.h_taps_c - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS_C); + set_reg_field_value(value, data->taps.v_taps_c - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS_C); + dm_write_reg(ctx, mmSCLV_TAP_CONTROL, value); + + value = 0; + if (data->taps.h_taps + data->taps.v_taps > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN); + is_scaling_needed = true; + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); + } + + if (data->taps.h_taps_c + data->taps.v_taps_c > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN_C); + is_scaling_needed = true; + } else if (data->format != PIXEL_FORMAT_420BPP8) { + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_MODE), + SCLV_MODE, + SCL_MODE_C); + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_PSCL_EN), + SCLV_MODE, + SCL_PSCL_EN_C); + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); + } + dm_write_reg(ctx, mmSCLV_MODE, value); + + value = 0; + /* + * 0 - Replaced out of bound pixels with black pixel + * (or any other required color) + * 1 - Replaced out of bound pixels with the edge pixel + */ + set_reg_field_value(value, 1, SCLV_CONTROL, SCL_BOUNDARY_MODE); + dm_write_reg(ctx, mmSCLV_CONTROL, value); + + return is_scaling_needed; +} + +/** +* Function: +* void program_overscan +* +* Purpose: Programs overscan border +* Input: overscan +* +* Output: + void +*/ +static void program_overscan( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + int overscan_right = data->h_active - data->recout.x - data->recout.width; + int overscan_bottom = data->v_active - data->recout.y - data->recout.height; + + if (xfm_dce->base.ctx->dc->debug.surface_visual_confirm) { + overscan_bottom += 2; + overscan_right += 2; + } + + if (overscan_right < 0) { + BREAK_TO_DEBUGGER(); + overscan_right = 0; + } + if (overscan_bottom < 0) { + BREAK_TO_DEBUGGER(); + overscan_bottom = 0; + } + + set_reg_field_value(overscan_left_right, data->recout.x, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); + + set_reg_field_value(overscan_left_right, overscan_right, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); + + set_reg_field_value(overscan_top_bottom, data->recout.y, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); + + set_reg_field_value(overscan_top_bottom, overscan_bottom, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); + + dm_write_reg(xfm_dce->base.ctx, + mmSCLV_EXT_OVERSCAN_LEFT_RIGHT, + overscan_left_right); + + dm_write_reg(xfm_dce->base.ctx, + mmSCLV_EXT_OVERSCAN_TOP_BOTTOM, + overscan_top_bottom); +} + +static void set_coeff_update_complete( + struct dce_transform *xfm_dce) +{ + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, mmSCLV_UPDATE); + set_reg_field_value(value, 1, SCLV_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dm_write_reg(xfm_dce->base.ctx, mmSCLV_UPDATE, value); +} + +static void program_multi_taps_filter( + struct dce_transform *xfm_dce, + int taps, + const uint16_t *coeffs, + enum ram_filter_type filter_type) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + int i, phase, pair; + int array_idx = 0; + int taps_pairs = (taps + 1) / 2; + int phases_to_program = SCLV_PHASES / 2 + 1; + + uint32_t select = 0; + uint32_t power_ctl, power_ctl_off; + + if (!coeffs) + return; + + /*We need to disable power gating on coeff memory to do programming*/ + power_ctl = dm_read_reg(ctx, mmDCFEV_MEM_PWR_CTRL); + power_ctl_off = power_ctl; + set_reg_field_value(power_ctl_off, 1, DCFEV_MEM_PWR_CTRL, SCLV_COEFF_MEM_PWR_DIS); + dm_write_reg(ctx, mmDCFEV_MEM_PWR_CTRL, power_ctl_off); + + /*Wait to disable gating:*/ + for (i = 0; i < 10; i++) { + if (get_reg_field_value( + dm_read_reg(ctx, mmDCFEV_MEM_PWR_STATUS), + DCFEV_MEM_PWR_STATUS, + SCLV_COEFF_MEM_PWR_STATE) == 0) + break; + + udelay(1); + } + + set_reg_field_value(select, filter_type, SCLV_COEF_RAM_SELECT, SCL_C_RAM_FILTER_TYPE); + + for (phase = 0; phase < phases_to_program; phase++) { + /*we always program N/2 + 1 phases, total phases N, but N/2-1 are just mirror + phase 0 is unique and phase N/2 is unique if N is even*/ + set_reg_field_value(select, phase, SCLV_COEF_RAM_SELECT, SCL_C_RAM_PHASE); + for (pair = 0; pair < taps_pairs; pair++) { + uint32_t data = 0; + + set_reg_field_value(select, pair, + SCLV_COEF_RAM_SELECT, SCL_C_RAM_TAP_PAIR_IDX); + + dm_write_reg(ctx, mmSCLV_COEF_RAM_SELECT, select); + + set_reg_field_value( + data, 1, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF_EN); + set_reg_field_value( + data, coeffs[array_idx], + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF); + + if (taps % 2 && pair == taps_pairs - 1) { + set_reg_field_value( + data, 0, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + array_idx++; + } else { + set_reg_field_value( + data, 1, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + set_reg_field_value( + data, coeffs[array_idx + 1], + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + + array_idx += 2; + } + + dm_write_reg(ctx, mmSCLV_COEF_RAM_TAP_DATA, data); + } + } + + /*We need to restore power gating on coeff memory to initial state*/ + dm_write_reg(ctx, mmDCFEV_MEM_PWR_CTRL, power_ctl); +} + +static void calculate_inits( + struct dce_transform *xfm_dce, + const struct scaler_data *data, + struct sclv_ratios_inits *inits, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + inits->h_int_scale_ratio_luma = + dal_fixed31_32_u2d19(data->ratios.horz) << 5; + inits->v_int_scale_ratio_luma = + dal_fixed31_32_u2d19(data->ratios.vert) << 5; + inits->h_int_scale_ratio_chroma = + dal_fixed31_32_u2d19(data->ratios.horz_c) << 5; + inits->v_int_scale_ratio_chroma = + dal_fixed31_32_u2d19(data->ratios.vert_c) << 5; + + inits->h_init_luma.integer = 1; + inits->v_init_luma.integer = 1; + inits->h_init_chroma.integer = 1; + inits->v_init_chroma.integer = 1; +} + +static void program_scl_ratios_inits( + struct dce_transform *xfm_dce, + struct sclv_ratios_inits *inits) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t addr = mmSCLV_HORZ_FILTER_SCALE_RATIO; + uint32_t value = 0; + + set_reg_field_value( + value, + inits->h_int_scale_ratio_luma, + SCLV_HORZ_FILTER_SCALE_RATIO, + SCL_H_SCALE_RATIO); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_SCALE_RATIO; + value = 0; + set_reg_field_value( + value, + inits->v_int_scale_ratio_luma, + SCLV_VERT_FILTER_SCALE_RATIO, + SCL_V_SCALE_RATIO); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_SCALE_RATIO_C; + value = 0; + set_reg_field_value( + value, + inits->h_int_scale_ratio_chroma, + SCLV_HORZ_FILTER_SCALE_RATIO_C, + SCL_H_SCALE_RATIO_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_SCALE_RATIO_C; + value = 0; + set_reg_field_value( + value, + inits->v_int_scale_ratio_chroma, + SCLV_VERT_FILTER_SCALE_RATIO_C, + SCL_V_SCALE_RATIO_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_INIT; + value = 0; + set_reg_field_value( + value, + inits->h_init_luma.fraction, + SCLV_HORZ_FILTER_INIT, + SCL_H_INIT_FRAC); + set_reg_field_value( + value, + inits->h_init_luma.integer, + SCLV_HORZ_FILTER_INIT, + SCL_H_INIT_INT); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_INIT; + value = 0; + set_reg_field_value( + value, + inits->v_init_luma.fraction, + SCLV_VERT_FILTER_INIT, + SCL_V_INIT_FRAC); + set_reg_field_value( + value, + inits->v_init_luma.integer, + SCLV_VERT_FILTER_INIT, + SCL_V_INIT_INT); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_INIT_C; + value = 0; + set_reg_field_value( + value, + inits->h_init_chroma.fraction, + SCLV_HORZ_FILTER_INIT_C, + SCL_H_INIT_FRAC_C); + set_reg_field_value( + value, + inits->h_init_chroma.integer, + SCLV_HORZ_FILTER_INIT_C, + SCL_H_INIT_INT_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_INIT_C; + value = 0; + set_reg_field_value( + value, + inits->v_init_chroma.fraction, + SCLV_VERT_FILTER_INIT_C, + SCL_V_INIT_FRAC_C); + set_reg_field_value( + value, + inits->v_init_chroma.integer, + SCLV_VERT_FILTER_INIT_C, + SCL_V_INIT_INT_C); + dm_write_reg(ctx, addr, value); +} + +static const uint16_t *get_filter_coeffs_64p(int taps, struct fixed31_32 ratio) +{ + if (taps == 4) + return get_filter_4tap_64p(ratio); + else if (taps == 2) + return get_filter_2tap_64p(); + else if (taps == 1) + return NULL; + else { + /* should never happen, bug */ + BREAK_TO_DEBUGGER(); + return NULL; + } +} + +static bool dce110_xfmv_power_up_line_buffer(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, mmLBV_MEMORY_CTRL); + + /*Use all three pieces of memory always*/ + set_reg_field_value(value, 0, LBV_MEMORY_CTRL, LB_MEMORY_CONFIG); + /*hard coded number DCE11 1712(0x6B0) Partitions: 720/960/1712*/ + set_reg_field_value(value, xfm_dce->lb_memory_size, LBV_MEMORY_CTRL, + LB_MEMORY_SIZE); + + dm_write_reg(xfm_dce->base.ctx, mmLBV_MEMORY_CTRL, value); + + return true; +} + +static void dce110_xfmv_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + bool is_scaling_required = false; + bool filter_updated = false; + const uint16_t *coeffs_v, *coeffs_h, *coeffs_h_c, *coeffs_v_c; + struct rect luma_viewport = {0}; + struct rect chroma_viewport = {0}; + + dce110_xfmv_power_up_line_buffer(xfm); + /* 1. Calculate viewport, viewport programming should happen after init + * calculations as they may require an adjustment in the viewport. + */ + + calculate_viewport(data, &luma_viewport, &chroma_viewport); + + /* 2. Program overscan */ + program_overscan(xfm_dce, data); + + /* 3. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm_dce, data); + + if (is_scaling_required) { + /* 4. Calculate and program ratio, filter initialization */ + + struct sclv_ratios_inits inits = { 0 }; + + calculate_inits( + xfm_dce, + data, + &inits, + &luma_viewport, + &chroma_viewport); + + program_scl_ratios_inits(xfm_dce, &inits); + + coeffs_v = get_filter_coeffs_64p(data->taps.v_taps, data->ratios.vert); + coeffs_h = get_filter_coeffs_64p(data->taps.h_taps, data->ratios.horz); + coeffs_v_c = get_filter_coeffs_64p(data->taps.v_taps_c, data->ratios.vert_c); + coeffs_h_c = get_filter_coeffs_64p(data->taps.h_taps_c, data->ratios.horz_c); + + if (coeffs_v != xfm_dce->filter_v + || coeffs_v_c != xfm_dce->filter_v_c + || coeffs_h != xfm_dce->filter_h + || coeffs_h_c != xfm_dce->filter_h_c) { + /* 5. Program vertical filters */ + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps, + coeffs_v, + FILTER_TYPE_RGB_Y_VERTICAL); + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps_c, + coeffs_v_c, + FILTER_TYPE_CBCR_VERTICAL); + + /* 6. Program horizontal filters */ + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps, + coeffs_h, + FILTER_TYPE_RGB_Y_HORIZONTAL); + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps_c, + coeffs_h_c, + FILTER_TYPE_CBCR_HORIZONTAL); + + xfm_dce->filter_v = coeffs_v; + xfm_dce->filter_v_c = coeffs_v_c; + xfm_dce->filter_h = coeffs_h; + xfm_dce->filter_h_c = coeffs_h_c; + filter_updated = true; + } + } + + /* 7. Program the viewport */ + program_viewport(xfm_dce, &luma_viewport, &chroma_viewport); + + /* 8. Set bit to flip to new coefficient memory */ + if (filter_updated) + set_coeff_update_complete(xfm_dce); +} + +static void dce110_xfmv_reset(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + xfm_dce->filter_h = NULL; + xfm_dce->filter_v = NULL; + xfm_dce->filter_h_c = NULL; + xfm_dce->filter_v_c = NULL; +} + +static void dce110_xfmv_set_gamut_remap( + struct transform *xfm, + const struct xfm_grph_csc_adjustment *adjust) +{ + /* DO NOTHING*/ +} + +static void dce110_xfmv_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + int pixel_depth = 0; + int expan_mode = 0; + uint32_t reg_data = 0; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pixel_depth = 2; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_24BPP: + pixel_depth = 1; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_30BPP: + pixel_depth = 0; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_36BPP: + pixel_depth = 3; + expan_mode = 0; + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + set_reg_field_value( + reg_data, + expan_mode, + LBV_DATA_FORMAT, + PIXEL_EXPAN_MODE); + + set_reg_field_value( + reg_data, + pixel_depth, + LBV_DATA_FORMAT, + PIXEL_DEPTH); + + dm_write_reg(xfm->ctx, mmLBV_DATA_FORMAT, reg_data); + + if (!(xfm_dce->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dm_logger_write(xfm->ctx->logger, LOG_WARNING, + "%s: Capability not supported", + __func__); + } +} + +static const struct transform_funcs dce110_xfmv_funcs = { + .transform_reset = dce110_xfmv_reset, + .transform_set_scaler = dce110_xfmv_set_scaler, + .transform_set_gamut_remap = + dce110_xfmv_set_gamut_remap, + .opp_set_csc_default = dce110_opp_v_set_csc_default, + .opp_set_csc_adjustment = dce110_opp_v_set_csc_adjustment, + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut_v, + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl_v, + .opp_set_regamma_mode = dce110_opp_set_regamma_mode_v, + .transform_set_pixel_storage_depth = + dce110_xfmv_set_pixel_storage_depth, + .transform_get_optimal_number_of_taps = + dce_transform_get_optimal_number_of_taps +}; +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce110_transform_v_construct( + struct dce_transform *xfm_dce, + struct dc_context *ctx) +{ + xfm_dce->base.ctx = ctx; + + xfm_dce->base.funcs = &dce110_xfmv_funcs; + + xfm_dce->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP; + + xfm_dce->prescaler_on = true; + xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; + xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h new file mode 100644 index 000000000000..b70780210aad --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h @@ -0,0 +1,58 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_TRANSFORM_V_DCE110_H__ +#define __DAL_TRANSFORM_V_DCE110_H__ + +#include "../dce/dce_transform.h" + +#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 +#define LB_BITS_PER_ENTRY 144 + +bool dce110_transform_v_construct( + struct dce_transform *xfm110, + struct dc_context *ctx); + +void dce110_opp_v_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust); + +void dce110_opp_v_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry); + + +void dce110_opp_program_regamma_pwl_v( + struct transform *xfm, + const struct pwl_params *params); + +void dce110_opp_power_on_regamma_lut_v( + struct transform *xfm, + bool power_on); + +void dce110_opp_set_regamma_mode_v( + struct transform *xfm, + enum opp_regamma mode); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce112/Makefile b/drivers/gpu/drm/amd/display/dc/dce112/Makefile new file mode 100644 index 000000000000..265ac4310d85 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +DCE112 = dce112_compressor.o dce112_hw_sequencer.o \ +dce112_resource.o + +AMD_DAL_DCE112 = $(addprefix $(AMDDALPATH)/dc/dce112/,$(DCE112)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE112) diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c new file mode 100644 index 000000000000..69649928768c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c @@ -0,0 +1,854 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "dce/dce_11_2_d.h" +#include "dce/dce_11_2_sh_mask.h" +#include "gmc/gmc_8_1_sh_mask.h" +#include "gmc/gmc_8_1_d.h" + +#include "include/logger_interface.h" + +#include "dce112_compressor.h" + +#define DCP_REG(reg)\ + (reg + cp110->offsets.dcp_offset) +#define DMIF_REG(reg)\ + (reg + cp110->offsets.dmif_offset) + +static const struct dce112_compressor_reg_offsets reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG0_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG1_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG2_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +} +}; + +static const uint32_t dce11_one_lpt_channel_max_resolution = 2560 * 1600; + +enum fbc_idle_force { + /* Bit 0 - Display registers updated */ + FBC_IDLE_FORCE_DISPLAY_REGISTER_UPDATE = 0x00000001, + + /* Bit 2 - FBC_GRPH_COMP_EN register updated */ + FBC_IDLE_FORCE_GRPH_COMP_EN = 0x00000002, + /* Bit 3 - FBC_SRC_SEL register updated */ + FBC_IDLE_FORCE_SRC_SEL_CHANGE = 0x00000004, + /* Bit 4 - FBC_MIN_COMPRESSION register updated */ + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE = 0x00000008, + /* Bit 5 - FBC_ALPHA_COMP_EN register updated */ + FBC_IDLE_FORCE_ALPHA_COMP_EN = 0x00000010, + /* Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated */ + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN = 0x00000020, + /* Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated */ + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF = 0x00000040, + + /* Bit 24 - Memory write to region 0 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION0 = 0x01000000, + /* Bit 25 - Memory write to region 1 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION1 = 0x02000000, + /* Bit 26 - Memory write to region 2 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION2 = 0x04000000, + /* Bit 27 - Memory write to region 3 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION3 = 0x08000000, + + /* Bit 28 - Memory write from any client other than MCIF */ + FBC_IDLE_FORCE_MEMORY_WRITE_OTHER_THAN_MCIF = 0x10000000, + /* Bit 29 - CG statics screen signal is inactive */ + FBC_IDLE_FORCE_CG_STATIC_SCREEN_IS_INACTIVE = 0x20000000, +}; + +static uint32_t lpt_size_alignment(struct dce112_compressor *cp110) +{ + /*LPT_ALIGNMENT (in bytes) = ROW_SIZE * #BANKS * # DRAM CHANNELS. */ + return cp110->base.raw_size * cp110->base.banks_num * + cp110->base.dram_channels_num; +} + +static uint32_t lpt_memory_control_config(struct dce112_compressor *cp110, + uint32_t lpt_control) +{ + /*LPT MC Config */ + if (cp110->base.options.bits.LPT_MC_CONFIG == 1) { + /* POSSIBLE VALUES for LPT NUM_PIPES (DRAM CHANNELS): + * 00 - 1 CHANNEL + * 01 - 2 CHANNELS + * 02 - 4 OR 6 CHANNELS + * (Only for discrete GPU, N/A for CZ) + * 03 - 8 OR 12 CHANNELS + * (Only for discrete GPU, N/A for CZ) */ + switch (cp110->base.dram_channels_num) { + case 2: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + case 1: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT NUM_PIPES!!!", + __func__); + break; + } + + /* The mapping for LPT NUM_BANKS is in + * GRPH_CONTROL.GRPH_NUM_BANKS register field + * Specifies the number of memory banks for tiling + * purposes. Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: + * 00 - DCP_GRPH_NUM_BANKS_2BANK: ADDR_SURF_2_BANK + * 01 - DCP_GRPH_NUM_BANKS_4BANK: ADDR_SURF_4_BANK + * 02 - DCP_GRPH_NUM_BANKS_8BANK: ADDR_SURF_8_BANK + * 03 - DCP_GRPH_NUM_BANKS_16BANK: ADDR_SURF_16_BANK */ + switch (cp110->base.banks_num) { + case 16: + set_reg_field_value( + lpt_control, + 3, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 8: + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 4: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 2: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT NUM_BANKS!!!", + __func__); + break; + } + + /* The mapping is in DMIF_ADDR_CALC. + * ADDR_CONFIG_PIPE_INTERLEAVE_SIZE register field for + * Carrizo specifies the memory interleave per pipe. + * It effectively specifies the location of pipe bits in + * the memory address. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_PIPE_INTERLEAVE_256B: 256 byte + * interleave + * 01 - ADDR_CONFIG_PIPE_INTERLEAVE_512B: 512 byte + * interleave + */ + switch (cp110->base.channel_interleave_size) { + case 256: /*256B */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + case 512: /*512B */ + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT INTERLEAVE_SIZE!!!", + __func__); + break; + } + + /* The mapping for LOW_POWER_TILING_ROW_SIZE is in + * DMIF_ADDR_CALC.ADDR_CONFIG_ROW_SIZE register field + * for Carrizo. Specifies the size of dram row in bytes. + * This should match up with NOOFCOLS field in + * MC_ARB_RAMCFG (ROW_SIZE = 4 * 2 ^^ columns). + * This register DMIF_ADDR_CALC is not used by the + * hardware as it is only used for addrlib assertions. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_1KB_ROW: Treat 1KB as DRAM row + * boundary + * 01 - ADDR_CONFIG_2KB_ROW: Treat 2KB as DRAM row + * boundary + * 02 - ADDR_CONFIG_4KB_ROW: Treat 4KB as DRAM row + * boundary */ + switch (cp110->base.raw_size) { + case 4096: /*4 KB */ + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 2048: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 1024: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT ROW_SIZE!!!", + __func__); + break; + } + } else { + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: LPT MC Configuration is not provided", + __func__); + } + + return lpt_control; +} + +static bool is_source_bigger_than_epanel_size( + struct dce112_compressor *cp110, + uint32_t source_view_width, + uint32_t source_view_height) +{ + if (cp110->base.embedded_panel_h_size != 0 && + cp110->base.embedded_panel_v_size != 0 && + ((source_view_width * source_view_height) > + (cp110->base.embedded_panel_h_size * + cp110->base.embedded_panel_v_size))) + return true; + + return false; +} + +static uint32_t align_to_chunks_number_per_line( + struct dce112_compressor *cp110, + uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +static void wait_for_fbc_state_changed( + struct dce112_compressor *cp110, + bool enabled) +{ + uint8_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 10) { + value = dm_read_reg(cp110->base.ctx, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + udelay(10); + counter++; + } + + if (counter == 10) { + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: wait counter exceeded, changes to HW not applied", + __func__); + } +} + +void dce112_compressor_power_up_fbc(struct compressor *compressor) +{ + uint32_t value; + uint32_t addr; + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); + set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); + if (compressor->options.bits.CLK_GATING_DISABLED == 1) { + /* HW needs to do power measurement comparison. */ + set_reg_field_value( + value, + 0, + FBC_CNTL, + FBC_COMP_CLK_GATE_EN); + } + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_MODE; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); + dm_write_reg(compressor->ctx, addr, value); + /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ + /* 1 ==> 4:1 */ + /* 2 ==> 8:1 */ + /* 0xF ==> 1:1 */ + set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); + dm_write_reg(compressor->ctx, addr, value); + compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; + + value = 0; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); + + value = 0xFFFFFF; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); +} + +void dce112_compressor_enable_fbc( + struct compressor *compressor, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + (compressor->options.bits.DUMMY_BACKEND == 0) && + (!dce112_compressor_is_fbc_enabled_in_hw(compressor, NULL)) && + (!is_source_bigger_than_epanel_size( + cp110, + params->source_view_width, + params->source_view_height))) { + + uint32_t addr; + uint32_t value; + + /* Before enabling FBC first need to enable LPT if applicable + * LPT state should always be changed (enable/disable) while FBC + * is disabled */ + if (compressor->options.bits.LPT_SUPPORT && (paths_num < 2) && + (params->source_view_width * + params->source_view_height <= + dce11_one_lpt_channel_max_resolution)) { + dce112_compressor_enable_lpt(compressor); + } + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value( + value, + params->inst, + FBC_CNTL, FBC_SRC_SEL); + dm_write_reg(compressor->ctx, addr, value); + + /* Keep track of enum controller_id FBC is attached to */ + compressor->is_enabled = true; + compressor->attached_inst = params->inst; + cp110->offsets = reg_offsets[params->inst]; + + /*Toggle it as there is bug in HW */ + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + wait_for_fbc_state_changed(cp110, true); + } +} + +void dce112_compressor_disable_fbc(struct compressor *compressor) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + dce112_compressor_is_fbc_enabled_in_hw(compressor, NULL)) { + uint32_t reg_data; + /* Turn off compression */ + reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL); + set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data); + + /* Reset enum controller_id to undefined */ + compressor->attached_inst = 0; + compressor->is_enabled = false; + + /* Whenever disabling FBC make sure LPT is disabled if LPT + * supported */ + if (compressor->options.bits.LPT_SUPPORT) + dce112_compressor_disable_lpt(compressor); + + wait_for_fbc_state_changed(cp110, false); + } +} + +bool dce112_compressor_is_fbc_enabled_in_hw( + struct compressor *compressor, + uint32_t *inst) +{ + /* Check the hardware register */ + uint32_t value; + + value = dm_read_reg(compressor->ctx, mmFBC_STATUS); + if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { + if (inst != NULL) + *inst = compressor->attached_inst; + return true; + } + + value = dm_read_reg(compressor->ctx, mmFBC_MISC); + if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { + value = dm_read_reg(compressor->ctx, mmFBC_CNTL); + + if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { + if (inst != NULL) + *inst = + compressor->attached_inst; + return true; + } + } + return false; +} + +bool dce112_compressor_is_lpt_enabled_in_hw(struct compressor *compressor) +{ + /* Check the hardware register */ + uint32_t value = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + return get_reg_field_value( + value, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); +} + +void dce112_compressor_program_compressed_surface_address_and_pitch( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t value = 0; + uint32_t fbc_pitch = 0; + uint32_t compressed_surf_address_low_part = + compressor->compr_surface_address.addr.low_part; + + /* Clear content first. */ + dm_write_reg( + compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + 0); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), 0); + + if (compressor->options.bits.LPT_SUPPORT) { + uint32_t lpt_alignment = lpt_size_alignment(cp110); + + if (lpt_alignment != 0) { + compressed_surf_address_low_part = + ((compressed_surf_address_low_part + + (lpt_alignment - 1)) / lpt_alignment) + * lpt_alignment; + } + } + + /* Write address, HIGH has to be first. */ + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + compressor->compr_surface_address.addr.high_part); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), + compressed_surf_address_low_part); + + fbc_pitch = align_to_chunks_number_per_line( + cp110, + params->source_view_width); + + if (compressor->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) + fbc_pitch = fbc_pitch / 8; + else + dm_logger_write( + compressor->ctx->logger, LOG_WARNING, + "%s: Unexpected DCE11 compression ratio", + __func__); + + /* Clear content first. */ + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), 0); + + /* Write FBC Pitch. */ + set_reg_field_value( + value, + fbc_pitch, + GRPH_COMPRESS_PITCH, + GRPH_COMPRESS_PITCH); + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); + +} + +void dce112_compressor_disable_lpt(struct compressor *compressor) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t inx; + + /* Disable all pipes LPT Stutter */ + for (inx = 0; inx < 3; inx++) { + value = + dm_read_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 0, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), + value); + } + /* Disable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, addr, value); + + /* Disable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); + + /* Clear selection of Channel(s) containing Compressed Surface */ + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0xFFFFFFFF, + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, mmGMCON_LPT_TARGET, value); +} + +void dce112_compressor_enable_lpt(struct compressor *compressor) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t value_control; + uint32_t channels; + + /* Enable LPT Stutter from Display pipe */ + value = dm_read_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 1, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), value); + + /* Enable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, addr, value); + + /* Selection of Channel(s) containing Compressed Surface: 0xfffffff + * will disable LPT. + * STCTRL_LPT_TARGETn corresponds to channel n. */ + addr = mmLOW_POWER_TILING_CONTROL; + value_control = dm_read_reg(compressor->ctx, addr); + channels = get_reg_field_value(value_control, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + channels + 1, /* not mentioned in programming guide, + but follow DCE8.1 */ + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, addr, value); + + /* Enable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); +} + +void dce112_compressor_program_lpt_control( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t rows_per_channel; + uint32_t lpt_alignment; + uint32_t source_view_width; + uint32_t source_view_height; + uint32_t lpt_control = 0; + + if (!compressor->options.bits.LPT_SUPPORT) + return; + + lpt_control = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + /* POSSIBLE VALUES for Low Power Tiling Mode: + * 00 - Use channel 0 + * 01 - Use Channel 0 and 1 + * 02 - Use Channel 0,1,2,3 + * 03 - reserved */ + switch (compressor->lpt_channels_num) { + /* case 2: + * Use Channel 0 & 1 / Not used for DCE 11 */ + case 1: + /*Use Channel 0 for LPT for DCE 11 */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + break; + default: + dm_logger_write( + compressor->ctx->logger, LOG_WARNING, + "%s: Invalid selected DRAM channels for LPT!!!", + __func__); + break; + } + + lpt_control = lpt_memory_control_config(cp110, lpt_control); + + /* Program LOW_POWER_TILING_ROWS_PER_CHAN field which depends on + * FBC compressed surface pitch. + * LOW_POWER_TILING_ROWS_PER_CHAN = Roundup ((Surface Height * + * Surface Pitch) / (Row Size * Number of Channels * + * Number of Banks)). */ + rows_per_channel = 0; + lpt_alignment = lpt_size_alignment(cp110); + source_view_width = + align_to_chunks_number_per_line( + cp110, + params->source_view_width); + source_view_height = (params->source_view_height + 1) & (~0x1); + + if (lpt_alignment != 0) { + rows_per_channel = source_view_width * source_view_height * 4; + rows_per_channel = + (rows_per_channel % lpt_alignment) ? + (rows_per_channel / lpt_alignment + 1) : + rows_per_channel / lpt_alignment; + } + + set_reg_field_value( + lpt_control, + rows_per_channel, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROWS_PER_CHAN); + + dm_write_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL, lpt_control); +} + +/* + * DCE 11 Frame Buffer Compression Implementation + */ + +void dce112_compressor_set_fbc_invalidation_triggers( + struct compressor *compressor, + uint32_t fbc_trigger) +{ + /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) + * for DCE 11 regions cannot be used - does not work with S/G + */ + uint32_t addr = mmFBC_CLIENT_REGION_MASK; + uint32_t value = dm_read_reg(compressor->ctx, addr); + + set_reg_field_value( + value, + 0, + FBC_CLIENT_REGION_MASK, + FBC_MEMORY_REGION_MASK); + dm_write_reg(compressor->ctx, addr, value); + + /* Setup events when to clear all CSM entries (effectively marking + * current compressed data invalid) + * For DCE 11 CSM metadata 11111 means - "Not Compressed" + * Used as the initial value of the metadata sent to the compressor + * after invalidation, to indicate that the compressor should attempt + * to compress all chunks on the current pass. Also used when the chunk + * is not successfully written to memory. + * When this CSM value is detected, FBC reads from the uncompressed + * buffer. Set events according to passed in value, these events are + * valid for DCE11: + * - bit 0 - display register updated + * - bit 28 - memory write from any client except from MCIF + * - bit 29 - CG static screen signal is inactive + * In addition, DCE11.1 also needs to set new DCE11.1 specific events + * that are used to trigger invalidation on certain register changes, + * for example enabling of Alpha Compression may trigger invalidation of + * FBC once bit is set. These events are as follows: + * - Bit 2 - FBC_GRPH_COMP_EN register updated + * - Bit 3 - FBC_SRC_SEL register updated + * - Bit 4 - FBC_MIN_COMPRESSION register updated + * - Bit 5 - FBC_ALPHA_COMP_EN register updated + * - Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated + * - Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated + */ + addr = mmFBC_IDLE_FORCE_CLEAR_MASK; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + fbc_trigger | + FBC_IDLE_FORCE_GRPH_COMP_EN | + FBC_IDLE_FORCE_SRC_SEL_CHANGE | + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE | + FBC_IDLE_FORCE_ALPHA_COMP_EN | + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN | + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF, + FBC_IDLE_FORCE_CLEAR_MASK, + FBC_IDLE_FORCE_CLEAR_MASK); + dm_write_reg(compressor->ctx, addr, value); +} + +void dce112_compressor_construct(struct dce112_compressor *compressor, + struct dc_context *ctx) +{ + struct dc_bios *bp = ctx->dc_bios; + struct embedded_panel_info panel_info; + + compressor->base.options.raw = 0; + compressor->base.options.bits.FBC_SUPPORT = true; + compressor->base.options.bits.LPT_SUPPORT = true; + /* For DCE 11 always use one DRAM channel for LPT */ + compressor->base.lpt_channels_num = 1; + compressor->base.options.bits.DUMMY_BACKEND = false; + + /* Check if this system has more than 1 DRAM channel; if only 1 then LPT + * should not be supported */ + if (compressor->base.memory_bus_width == 64) + compressor->base.options.bits.LPT_SUPPORT = false; + + compressor->base.options.bits.CLK_GATING_DISABLED = false; + + compressor->base.ctx = ctx; + compressor->base.embedded_panel_h_size = 0; + compressor->base.embedded_panel_v_size = 0; + compressor->base.memory_bus_width = ctx->asic_id.vram_width; + compressor->base.allocated_size = 0; + compressor->base.preferred_requested_size = 0; + compressor->base.min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; + compressor->base.banks_num = 0; + compressor->base.raw_size = 0; + compressor->base.channel_interleave_size = 0; + compressor->base.dram_channels_num = 0; + compressor->base.lpt_channels_num = 0; + compressor->base.attached_inst = 0; + compressor->base.is_enabled = false; + + if (BP_RESULT_OK == + bp->funcs->get_embedded_panel_info(bp, &panel_info)) { + compressor->base.embedded_panel_h_size = + panel_info.lcd_timing.horizontal_addressable; + compressor->base.embedded_panel_v_size = + panel_info.lcd_timing.vertical_addressable; + } +} + +struct compressor *dce112_compressor_create(struct dc_context *ctx) +{ + struct dce112_compressor *cp110 = + kzalloc(sizeof(struct dce112_compressor), GFP_KERNEL); + + if (!cp110) + return NULL; + + dce112_compressor_construct(cp110, ctx); + return &cp110->base; +} + +void dce112_compressor_destroy(struct compressor **compressor) +{ + kfree(TO_DCE112_COMPRESSOR(*compressor)); + *compressor = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.h b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.h new file mode 100644 index 000000000000..f1227133f6df --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.h @@ -0,0 +1,78 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_COMPRESSOR_DCE112_H__ +#define __DC_COMPRESSOR_DCE112_H__ + +#include "../inc/compressor.h" + +#define TO_DCE112_COMPRESSOR(compressor)\ + container_of(compressor, struct dce112_compressor, base) + +struct dce112_compressor_reg_offsets { + uint32_t dcp_offset; + uint32_t dmif_offset; +}; + +struct dce112_compressor { + struct compressor base; + struct dce112_compressor_reg_offsets offsets; +}; + +struct compressor *dce112_compressor_create(struct dc_context *ctx); + +void dce112_compressor_construct(struct dce112_compressor *cp110, + struct dc_context *ctx); + +void dce112_compressor_destroy(struct compressor **cp); + +/* FBC RELATED */ +void dce112_compressor_power_up_fbc(struct compressor *cp); + +void dce112_compressor_enable_fbc(struct compressor *cp, uint32_t paths_num, + struct compr_addr_and_pitch_params *params); + +void dce112_compressor_disable_fbc(struct compressor *cp); + +void dce112_compressor_set_fbc_invalidation_triggers(struct compressor *cp, + uint32_t fbc_trigger); + +void dce112_compressor_program_compressed_surface_address_and_pitch( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce112_compressor_is_fbc_enabled_in_hw(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); + +/* LPT RELATED */ +void dce112_compressor_enable_lpt(struct compressor *cp); + +void dce112_compressor_disable_lpt(struct compressor *cp); + +void dce112_compressor_program_lpt_control(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce112_compressor_is_lpt_enabled_in_hw(struct compressor *cp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.c new file mode 100644 index 000000000000..1e4a7c13f0ed --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.c @@ -0,0 +1,163 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dc.h" +#include "core_types.h" +#include "dce112_hw_sequencer.h" + +#include "dce110/dce110_hw_sequencer.h" + +/* include DCE11.2 register header files */ +#include "dce/dce_11_2_d.h" +#include "dce/dce_11_2_sh_mask.h" + +struct dce112_hw_seq_reg_offsets { + uint32_t crtc; +}; + + +static const struct dce112_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +/******************************************************************************* + * Private definitions + ******************************************************************************/ + +static void dce112_init_pte(struct dc_context *ctx) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} + +static bool dce112_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce112_init_pte(ctx); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +void dce112_hw_sequencer_construct(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce110_hw_sequencer_construct(dc); + dc->hwss.enable_display_power_gating = dce112_enable_display_power_gating; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.h new file mode 100644 index 000000000000..e646f4a37fa2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.h @@ -0,0 +1,36 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HWSS_DCE112_H__ +#define __DC_HWSS_DCE112_H__ + +#include "core_types.h" + +struct dc; + +void dce112_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c new file mode 100644 index 000000000000..663e0a047a4b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c @@ -0,0 +1,1283 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "dce110/dce110_resource.h" +#include "dce110/dce110_timing_generator.h" + +#include "irq/dce110/irq_service_dce110.h" + +#include "dce/dce_mem_input.h" +#include "dce/dce_transform.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_audio.h" +#include "dce/dce_opp.h" +#include "dce/dce_ipp.h" +#include "dce/dce_clocks.h" +#include "dce/dce_clock_source.h" + +#include "dce/dce_hwseq.h" +#include "dce112/dce112_hw_sequencer.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" + +#include "reg_helper.h" + +#include "dce/dce_11_2_d.h" +#include "dce/dce_11_2_sh_mask.h" + +#include "dce100/dce100_resource.h" + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_BS_SR_SWAP_CNTL + #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4BDC + #define mmDP2_DP_DPHY_BS_SR_SWAP_CNTL 0x4CDC + #define mmDP3_DP_DPHY_BS_SR_SWAP_CNTL 0x4DDC + #define mmDP4_DP_DPHY_BS_SR_SWAP_CNTL 0x4EDC + #define mmDP5_DP_DPHY_BS_SR_SWAP_CNTL 0x4FDC + #define mmDP6_DP_DPHY_BS_SR_SWAP_CNTL 0x54DC +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP0_DP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP1_DP_DPHY_FAST_TRAINING 0x4BBC + #define mmDP2_DP_DPHY_FAST_TRAINING 0x4CBC + #define mmDP3_DP_DPHY_FAST_TRAINING 0x4DBC + #define mmDP4_DP_DPHY_FAST_TRAINING 0x4EBC + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4FBC + #define mmDP6_DP_DPHY_FAST_TRAINING 0x54BC +#endif + +enum dce112_clk_src_array_id { + DCE112_CLK_SRC_PLL0, + DCE112_CLK_SRC_PLL1, + DCE112_CLK_SRC_PLL2, + DCE112_CLK_SRC_PLL3, + DCE112_CLK_SRC_PLL4, + DCE112_CLK_SRC_PLL5, + + DCE112_CLK_SRC_TOTAL +}; + +static const struct dce110_timing_generator_offsets dce112_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + + +static const struct dce_disp_clk_registers disp_clk_regs = { + CLK_COMMON_REG_LIST_DCE_BASE() +}; + +static const struct dce_disp_clk_shift disp_clk_shift = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_disp_clk_mask disp_clk_mask = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE110_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE110(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE110_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST(id),\ + .TMDS_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE112(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE112(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_112_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_112(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_112(_MASK) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5) +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_aduio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(index, id)\ +[index] = {\ + CS_COMMON_REG_LIST_DCE_112(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0, A), + clk_src_regs(1, B), + clk_src_regs(2, C), + clk_src_regs(3, D), + clk_src_regs(4, E), + clk_src_regs(5, F) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_112(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_112(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps polaris_10_resource_cap = { + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 8, /* why 8? 6 combo PHY PLL + 2 regular PLLs? */ +}; + +static const struct resource_caps polaris_11_resource_cap = { + .num_timing_generator = 5, + .num_audio = 5, + .num_stream_encoder = 5, + .num_pll = 8, /* why 8? 6 combo PHY PLL + 2 regular PLLs? */ +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x4819 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + + +static struct timing_generator *dce112_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce110_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct stream_encoder *dce112_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE112_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE112_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE112_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce112_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce112_stream_encoder_create, + .create_hwseq = dce112_hwseq_create, +}; + +#define mi_inst_regs(id) { MI_DCE11_2_REG_LIST(id) } +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE11_2_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE11_2_MASK_SH_LIST(_MASK) +}; + +static struct mem_input *dce112_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce112_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + return &dce_mi->base; +} + +static void dce112_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce112_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->lb_memory_size = 0x1404; /*5124*/ + return &transform->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .ycbcr420_supported = true, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true, + .flags.bits.IS_YCBCR_CAPABLE = true +}; + +struct link_encoder *dce112_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +static struct input_pixel_processor *dce112_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +struct output_pixel_processor *dce112_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +struct clock_source *dce112_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce112_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static void destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce112_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce112_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.dp_clock_source != NULL) + dce112_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.display_clock != NULL) + dce_disp_clk_destroy(&pool->base.display_clock); + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + +static struct clock_source *find_matching_pll( + struct resource_context *res_ctx, + const struct resource_pool *pool, + const struct dc_stream_state *const stream) +{ + switch (stream->sink->link->link_enc->transmitter) { + case TRANSMITTER_UNIPHY_A: + return pool->clock_sources[DCE112_CLK_SRC_PLL0]; + case TRANSMITTER_UNIPHY_B: + return pool->clock_sources[DCE112_CLK_SRC_PLL1]; + case TRANSMITTER_UNIPHY_C: + return pool->clock_sources[DCE112_CLK_SRC_PLL2]; + case TRANSMITTER_UNIPHY_D: + return pool->clock_sources[DCE112_CLK_SRC_PLL3]; + case TRANSMITTER_UNIPHY_E: + return pool->clock_sources[DCE112_CLK_SRC_PLL4]; + case TRANSMITTER_UNIPHY_F: + return pool->clock_sources[DCE112_CLK_SRC_PLL5]; + default: + return NULL; + }; + + return 0; +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + dce110_resource_build_pipe_hw_param(pipe_ctx); + + resource_build_info_frame(pipe_ctx); + + return DC_OK; +} + +bool dce112_validate_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + bool result = false; + + dm_logger_write( + dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "%s: start", + __func__); + + if (bw_calcs( + dc->ctx, + dc->bw_dceip, + dc->bw_vbios, + context->res_ctx.pipe_ctx, + dc->res_pool->pipe_count, + &context->bw.dce)) + result = true; + + if (!result) + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_VALIDATION, + "%s: Bandwidth validation failed!", + __func__); + + if (memcmp(&dc->current_state->bw.dce, + &context->bw.dce, sizeof(context->bw.dce))) { + struct log_entry log_entry; + dm_logger_open( + dc->ctx->logger, + &log_entry, + LOG_BANDWIDTH_CALCS); + dm_logger_append(&log_entry, "%s: finish,\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + __func__, + context->bw.dce.nbp_state_change_wm_ns[0].b_mark, + context->bw.dce.nbp_state_change_wm_ns[0].a_mark, + context->bw.dce.urgent_wm_ns[0].b_mark, + context->bw.dce.urgent_wm_ns[0].a_mark, + context->bw.dce.stutter_exit_wm_ns[0].b_mark, + context->bw.dce.stutter_exit_wm_ns[0].a_mark); + dm_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + context->bw.dce.nbp_state_change_wm_ns[1].b_mark, + context->bw.dce.nbp_state_change_wm_ns[1].a_mark, + context->bw.dce.urgent_wm_ns[1].b_mark, + context->bw.dce.urgent_wm_ns[1].a_mark, + context->bw.dce.stutter_exit_wm_ns[1].b_mark, + context->bw.dce.stutter_exit_wm_ns[1].a_mark); + dm_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n", + context->bw.dce.nbp_state_change_wm_ns[2].b_mark, + context->bw.dce.nbp_state_change_wm_ns[2].a_mark, + context->bw.dce.urgent_wm_ns[2].b_mark, + context->bw.dce.urgent_wm_ns[2].a_mark, + context->bw.dce.stutter_exit_wm_ns[2].b_mark, + context->bw.dce.stutter_exit_wm_ns[2].a_mark, + context->bw.dce.stutter_mode_enable); + dm_logger_append(&log_entry, + "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n" + "sclk: %d sclk_sleep: %d yclk: %d blackout_recovery_time_us: %d\n", + context->bw.dce.cpuc_state_change_enable, + context->bw.dce.cpup_state_change_enable, + context->bw.dce.nbp_state_change_enable, + context->bw.dce.all_displays_in_sync, + context->bw.dce.dispclk_khz, + context->bw.dce.sclk_khz, + context->bw.dce.sclk_deep_sleep_khz, + context->bw.dce.yclk_khz, + context->bw.dce.blackout_recovery_time_us); + dm_logger_close(&log_entry); + } + return result; +} + +enum dc_status resource_map_phy_clock_resources( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + + /* acquire new resources */ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream( + &context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + if (dc_is_dp_signal(pipe_ctx->stream->signal) + || pipe_ctx->stream->signal == SIGNAL_TYPE_VIRTUAL) + pipe_ctx->clock_source = + dc->res_pool->dp_clock_source; + else + pipe_ctx->clock_source = find_matching_pll( + &context->res_ctx, dc->res_pool, + stream); + + if (pipe_ctx->clock_source == NULL) + return DC_NO_CLOCK_SOURCE_RESOURCE; + + resource_reference_clock_source( + &context->res_ctx, + dc->res_pool, + pipe_ctx->clock_source); + + return DC_OK; +} + +static bool dce112_validate_surface_sets( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 1) + return false; + + if (context->stream_status[i].plane_states[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + } + + return true; +} + +enum dc_status dce112_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + result = resource_map_pool_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = resource_map_phy_clock_resources(dc, new_ctx, dc_stream); + + + if (result == DC_OK) + result = build_mapped_resource(dc, new_ctx, dc_stream); + + return result; +} + +enum dc_status dce112_validate_guaranteed( + struct dc *dc, + struct dc_stream_state *stream, + struct dc_state *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + context->streams[0] = stream; + dc_stream_retain(context->streams[0]); + context->stream_count++; + + result = resource_map_pool_resources(dc, context, stream); + + if (result == DC_OK) + result = resource_map_phy_clock_resources(dc, context, stream); + + if (result == DC_OK) + result = build_mapped_resource(dc, context, stream); + + if (result == DC_OK) { + validate_guaranteed_copy_streams( + context, dc->caps.max_streams); + result = resource_build_scaling_params_for_context(dc, context); + } + + if (result == DC_OK) + if (!dce112_validate_bandwidth(dc, context)) + result = DC_FAIL_BANDWIDTH_VALIDATE; + + return result; +} + +enum dc_status dce112_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce112_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static void dce112_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce112_res_pool_funcs = { + .destroy = dce112_destroy_resource_pool, + .link_enc_create = dce112_link_encoder_create, + .validate_guaranteed = dce112_validate_guaranteed, + .validate_bandwidth = dce112_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce112_add_stream_to_ctx, + .validate_global = dce112_validate_global +}; + +static void bw_calcs_data_update_from_pplib(struct dc *dc) +{ + struct dm_pp_clock_levels_with_latency eng_clks = {0}; + struct dm_pp_clock_levels_with_latency mem_clks = {0}; + struct dm_pp_wm_sets_with_clock_ranges clk_ranges = {0}; + struct dm_pp_clock_levels clks = {0}; + + /*do system clock TODO PPLIB: after PPLIB implement, + * then remove old way + */ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &eng_clks)) { + + /* This is only for temporary */ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &clks); + /* convert all the clock fro kHz to fix point mHz */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels/8], 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*2/8], 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*3/8], 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*4/8], 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*5/8], 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*6/8], 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + + /*do memory clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &clks); + + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[0] * MEMORY_TYPE_MULTIPLIER, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1] * MEMORY_TYPE_MULTIPLIER, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1] * MEMORY_TYPE_MULTIPLIER, + 1000); + + return; + } + + /* convert all the clock fro kHz to fix point mHz TODO: wloop data */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels-1].clocks_in_khz, 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels/8].clocks_in_khz, 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*2/8].clocks_in_khz, 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz, 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*4/8].clocks_in_khz, 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*5/8].clocks_in_khz, 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*6/8].clocks_in_khz, 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + eng_clks.data[0].clocks_in_khz, 1000); + + /*do memory clock*/ + dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &mem_clks); + + /* we don't need to call PPLIB for validation clock since they + * also give us the highest sclk and highest mclk (UMA clock). + * ALSO always convert UMA clock (from PPLIB) to YCLK (HW formula): + * YCLK = UMACLK*m_memoryTypeMultiplier + */ + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + mem_clks.data[0].clocks_in_khz * MEMORY_TYPE_MULTIPLIER, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * MEMORY_TYPE_MULTIPLIER, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * MEMORY_TYPE_MULTIPLIER, + 1000); + + /* Now notify PPLib/SMU about which Watermarks sets they should select + * depending on DPM state they are in. And update BW MGR GFX Engine and + * Memory clock member variables for Watermarks calculations for each + * Watermark Set + */ + clk_ranges.num_wm_sets = 4; + clk_ranges.wm_clk_ranges[0].wm_set_id = WM_SET_A; + clk_ranges.wm_clk_ranges[0].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[0].wm_min_memg_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[1].wm_set_id = WM_SET_B; + clk_ranges.wm_clk_ranges[1].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[1].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[1].wm_min_memg_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[1].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[2].wm_set_id = WM_SET_C; + clk_ranges.wm_clk_ranges[2].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[2].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[2].wm_min_memg_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[2].wm_max_mem_clk_in_khz = 5000000; + + clk_ranges.wm_clk_ranges[3].wm_set_id = WM_SET_D; + clk_ranges.wm_clk_ranges[3].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[3].wm_min_memg_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_mem_clk_in_khz = 5000000; + + /* Notify PP Lib/SMU which Watermarks to use for which clock ranges */ + dm_pp_notify_wm_clock_changes(dc->ctx, &clk_ranges); +} + +const struct resource_caps *dce112_resource_cap( + struct hw_asic_id *asic_id) +{ + if (ASIC_REV_IS_POLARIS11_M(asic_id->hw_internal_rev) || + ASIC_REV_IS_POLARIS12_V(asic_id->hw_internal_rev)) + return &polaris_11_resource_cap; + else + return &polaris_10_resource_cap; +} + +static bool construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dm_pp_static_clock_info static_clk_info = {0}; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = dce112_resource_cap(&ctx->asic_id); + pool->base.funcs = &dce112_res_pool_funcs; + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = pool->base.res_cap->num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 100; + dc->caps.max_cursor_size = 128; + + /************************************************* + * Create resources * + *************************************************/ + + pool->base.clock_sources[DCE112_CLK_SRC_PLL0] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL1] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL1, + &clk_src_regs[1], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL2] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL2, + &clk_src_regs[2], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL3] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL4] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL4, + &clk_src_regs[4], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL5] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL5, + &clk_src_regs[5], false); + pool->base.clk_src_count = DCE112_CLK_SRC_TOTAL; + + pool->base.dp_clock_source = dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_ID_DP_DTO, &clk_src_regs[0], true); + + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.display_clock = dce112_disp_clk_create(ctx, + &disp_clk_regs, + &disp_clk_shift, + &disp_clk_mask); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + /* get static clock information for PPLIB or firmware, save + * max_clock_state + */ + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) + pool->base.display_clock->max_clks_state = + static_clk_info.max_clocks_state; + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce110_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = + dce112_timing_generator_create( + ctx, + i, + &dce112_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce112_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce112_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce112_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce112_opp_create( + ctx, + i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + dc->caps.max_planes = pool->base.pipe_count; + + /* Create hardware sequencer */ + dce112_hw_sequencer_construct(dc); + + bw_calcs_init(dc->bw_dceip, dc->bw_vbios, dc->ctx->asic_id); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +res_create_fail: + destruct(pool); + return false; +} + +struct resource_pool *dce112_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.h b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.h new file mode 100644 index 000000000000..d5c19d34eb0a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.h @@ -0,0 +1,61 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_RESOURCE_DCE112_H__ +#define __DC_RESOURCE_DCE112_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce112_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +enum dc_status dce112_validate_with_context( + struct dc *dc, + const struct dc_validation_set set[], + int set_count, + struct dc_state *context, + struct dc_state *old_context); + +enum dc_status dce112_validate_guaranteed( + struct dc *dc, + struct dc_stream_state *dc_stream, + struct dc_state *context); + +bool dce112_validate_bandwidth( + struct dc *dc, + struct dc_state *context); + +enum dc_status dce112_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream); + + +#endif /* __DC_RESOURCE_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/Makefile b/drivers/gpu/drm/amd/display/dc/dce120/Makefile new file mode 100644 index 000000000000..1779b963525c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + + +DCE120 = dce120_resource.o dce120_timing_generator.o \ +dce120_hw_sequencer.o + +AMD_DAL_DCE120 = $(addprefix $(AMDDALPATH)/dc/dce120/,$(DCE120)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE120) \ No newline at end of file diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c new file mode 100644 index 000000000000..1a0b54d6034e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c @@ -0,0 +1,257 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dc.h" +#include "core_types.h" +#include "dce120_hw_sequencer.h" +#include "dce/dce_hwseq.h" + +#include "dce110/dce110_hw_sequencer.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" +#include "reg_helper.h" + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +struct dce120_hw_seq_reg_offsets { + uint32_t crtc; +}; + +static const struct dce120_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +} +}; + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +#define CNTL_ID(controller_id)\ + controller_id +/******************************************************************************* + * Private definitions + ******************************************************************************/ +#if 0 +static void dce120_init_pte(struct dc_context *ctx, uint8_t controller_id) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; +/* + addr = mmDCP0_DVMM_PTE_CONTROL + controller_id * + (mmDCP1_DVMM_PTE_CONTROL- mmDCP0_DVMM_PTE_CONTROL); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, 0, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value_soc15( + value, 1, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value_soc15( + value, 1, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + + dm_write_reg(ctx, addr, value);*/ + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} +#endif + +static bool dce120_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + /* disable for bringup */ +#if 0 + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0) { + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC0_CRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce120_init_pte(ctx, controller_id); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +#endif + return false; +} + +static void dce120_update_dchub( + struct dce_hwseq *hws, + struct dchub_init_data *dh_data) +{ + /* TODO: port code from dal2 */ + switch (dh_data->fb_mode) { + case FRAME_BUFFER_MODE_ZFB_ONLY: + /*For ZFB case need to put DCHUB FB BASE and TOP upside down to indicate ZFB mode*/ + REG_UPDATE_2(DCHUB_FB_LOCATION, + FB_TOP, 0, + FB_BASE, 0x0FFFF); + + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, (dh_data->zfb_mc_base_addr + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_MIXED_ZFB_AND_LOCAL: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, (dh_data->zfb_mc_base_addr + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_LOCAL_ONLY: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, 0); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, 0x03FFFF); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, 0); + break; + default: + break; + } + + dh_data->dchub_initialzied = true; + dh_data->dchub_info_valid = false; +} + + + +void dce120_hw_sequencer_construct(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce110_hw_sequencer_construct(dc); + dc->hwss.enable_display_power_gating = dce120_enable_display_power_gating; + dc->hwss.update_dchub = dce120_update_dchub; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h new file mode 100644 index 000000000000..77a6b86d7606 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h @@ -0,0 +1,36 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HWSS_DCE120_H__ +#define __DC_HWSS_DCE120_H__ + +#include "core_types.h" + +struct dc; + +void dce120_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c new file mode 100644 index 000000000000..5c48c22d9d98 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c @@ -0,0 +1,1004 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc.cls +* + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + + +#include "stream_encoder.h" +#include "resource.h" +#include "include/irq_service_interface.h" +#include "dce120_resource.h" +#include "dce112/dce112_resource.h" + +#include "dce110/dce110_resource.h" +#include "../virtual/virtual_stream_encoder.h" +#include "dce120_timing_generator.h" +#include "irq/dce120/irq_service_dce120.h" +#include "dce/dce_opp.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_clocks.h" +#include "dce/dce_ipp.h" +#include "dce/dce_mem_input.h" + +#include "dce110/dce110_hw_sequencer.h" +#include "dce120/dce120_hw_sequencer.h" +#include "dce/dce_transform.h" + +#include "dce/dce_audio.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_hwseq.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" +#include "vega10/NBIO/nbio_6_1_offset.h" +#include "reg_helper.h" + +#include "dce100/dce100_resource.h" + +#ifndef mmDP0_DP_DPHY_INTERNAL_CTRL + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x210f + #define mmDP0_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x220f + #define mmDP1_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x230f + #define mmDP2_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x240f + #define mmDP3_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x250f + #define mmDP4_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x260f + #define mmDP5_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x270f + #define mmDP6_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 +#endif + +enum dce120_clk_src_array_id { + DCE120_CLK_SRC_PLL0, + DCE120_CLK_SRC_PLL1, + DCE120_CLK_SRC_PLL2, + DCE120_CLK_SRC_PLL3, + DCE120_CLK_SRC_PLL4, + DCE120_CLK_SRC_PLL5, + + DCE120_CLK_SRC_TOTAL +}; + +static const struct dce110_timing_generator_offsets dce120_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + } +}; + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +#define NBIO_BASE_INNER(seg) \ + NBIF_BASE__INST0_SEG ## seg + +#define NBIO_BASE(seg) \ + NBIO_BASE_INNER(seg) + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define SR(reg_name)\ + .reg_name = BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +#define SRI(reg_name, block, id)\ + .reg_name = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE110_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE120_MASK_SH_LIST_SOC_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE120_MASK_SH_LIST_SOC_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE110(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_SOC_BASE(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_SOC_BASE(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE120_REG_LIST(id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id) \ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST(id),\ + .TMDS_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE120(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE120(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_120_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_120(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_120(_MASK) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5) +}; + +#define DCE120_AUD_COMMON_MASK_SH_LIST(mask_sh)\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh),\ + AUD_COMMON_MASK_SH_LIST_BASE(mask_sh) + +static const struct dce_audio_shift audio_shift = { + DCE120_AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_aduio_mask audio_mask = { + DCE120_AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(index, id)\ +[index] = {\ + CS_COMMON_REG_LIST_DCE_112(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0, A), + clk_src_regs(1, B), + clk_src_regs(2, C), + clk_src_regs(3, D), + clk_src_regs(4, E), + clk_src_regs(5, F) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_112(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_112(_MASK) +}; + +struct output_pixel_processor *dce120_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 + NBIO_BASE(mmBIOS_SCRATCH_6_BASE_IDX) +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 7, + .num_stream_encoder = 6, + .num_pll = 6, +}; + +static const struct dc_debug debug_defaults = { + .disable_clock_gate = true, +}; + +struct clock_source *dce120_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(*clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce120_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + + +bool dce120_hw_sequencer_create(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce120_hw_sequencer_construct(dc); + + /*TODO Move to separate file and Override what is needed */ + + return true; +} + +static struct timing_generator *dce120_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce120_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static void dce120_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static void destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce120_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i]) + dce_aud_destroy(&pool->base.audios[i]); + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) + dce120_clock_source_destroy( + &pool->base.clock_sources[i]); + } + + if (pool->base.dp_clock_source != NULL) + dce120_clock_source_destroy(&pool->base.dp_clock_source); + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.display_clock != NULL) + dce_disp_clk_destroy(&pool->base.display_clock); +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + uint32_t reg_val = dm_read_reg_soc15(ctx, mmCC_DC_MISC_STRAPS, 0); + + straps->audio_stream_number = get_reg_field_value(reg_val, + CC_DC_MISC_STRAPS, + AUDIO_STREAM_NUMBER); + straps->hdmi_disable = get_reg_field_value(reg_val, + CC_DC_MISC_STRAPS, + HDMI_DISABLE); + + reg_val = dm_read_reg_soc15(ctx, mmDC_PINSTRAPS, 0); + straps->dc_pinstraps_audio = get_reg_field_value(reg_val, + DC_PINSTRAPS, + DC_PINSTRAPS_AUDIO); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .ycbcr420_supported = true, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true, + .flags.bits.IS_YCBCR_CAPABLE = true +}; + +static struct link_encoder *dce120_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + + return &enc110->base; +} + +static struct input_pixel_processor *dce120_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static struct stream_encoder *dce120_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE120_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE12_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE12_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce120_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce120_stream_encoder_create, + .create_hwseq = dce120_hwseq_create, +}; + +#define mi_inst_regs(id) { MI_DCE12_REG_LIST(id) } +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE12_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE12_MASK_SH_LIST(_MASK) +}; + +static struct mem_input *dce120_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce112_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + return &dce_mi->base; +} + +static struct transform *dce120_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->lb_memory_size = 0x1404; /*5124*/ + return &transform->base; +} + +static void dce120_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce120_res_pool_funcs = { + .destroy = dce120_destroy_resource_pool, + .link_enc_create = dce120_link_encoder_create, + .validate_guaranteed = dce112_validate_guaranteed, + .validate_bandwidth = dce112_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce112_add_stream_to_ctx +}; + +static void bw_calcs_data_update_from_pplib(struct dc *dc) +{ + struct dm_pp_clock_levels_with_latency eng_clks = {0}; + struct dm_pp_clock_levels_with_latency mem_clks = {0}; + struct dm_pp_wm_sets_with_clock_ranges clk_ranges = {0}; + int i; + unsigned int clk; + unsigned int latency; + + /*do system clock*/ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &eng_clks) || eng_clks.num_levels == 0) { + + eng_clks.num_levels = 8; + clk = 300000; + + for (i = 0; i < eng_clks.num_levels; i++) { + eng_clks.data[i].clocks_in_khz = clk; + clk += 100000; + } + } + + /* convert all the clock fro kHz to fix point mHz TODO: wloop data */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels-1].clocks_in_khz, 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels/8].clocks_in_khz, 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*2/8].clocks_in_khz, 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz, 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*4/8].clocks_in_khz, 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*5/8].clocks_in_khz, 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*6/8].clocks_in_khz, 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + eng_clks.data[0].clocks_in_khz, 1000); + + /*do memory clock*/ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &mem_clks) || mem_clks.num_levels == 0) { + + mem_clks.num_levels = 3; + clk = 250000; + latency = 45; + + for (i = 0; i < eng_clks.num_levels; i++) { + mem_clks.data[i].clocks_in_khz = clk; + mem_clks.data[i].latency_in_us = latency; + clk += 500000; + latency -= 5; + } + + } + + /* we don't need to call PPLIB for validation clock since they + * also give us the highest sclk and highest mclk (UMA clock). + * ALSO always convert UMA clock (from PPLIB) to YCLK (HW formula): + * YCLK = UMACLK*m_memoryTypeMultiplier + */ + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + mem_clks.data[0].clocks_in_khz * MEMORY_TYPE_MULTIPLIER, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * MEMORY_TYPE_MULTIPLIER, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * MEMORY_TYPE_MULTIPLIER, + 1000); + + /* Now notify PPLib/SMU about which Watermarks sets they should select + * depending on DPM state they are in. And update BW MGR GFX Engine and + * Memory clock member variables for Watermarks calculations for each + * Watermark Set + */ + clk_ranges.num_wm_sets = 4; + clk_ranges.wm_clk_ranges[0].wm_set_id = WM_SET_A; + clk_ranges.wm_clk_ranges[0].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[0].wm_min_memg_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[1].wm_set_id = WM_SET_B; + clk_ranges.wm_clk_ranges[1].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[1].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[1].wm_min_memg_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[1].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[2].wm_set_id = WM_SET_C; + clk_ranges.wm_clk_ranges[2].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[2].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[2].wm_min_memg_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[2].wm_max_mem_clk_in_khz = 5000000; + + clk_ranges.wm_clk_ranges[3].wm_set_id = WM_SET_D; + clk_ranges.wm_clk_ranges[3].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[3].wm_min_memg_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_mem_clk_in_khz = 5000000; + + /* Notify PP Lib/SMU which Watermarks to use for which clock ranges */ + dm_pp_notify_wm_clock_changes(dc->ctx, &clk_ranges); +} + +static bool construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct irq_service_init_data irq_init_data; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce120_res_pool_funcs; + + /* TODO: Fill more data from GreenlandAsicCapability.cpp */ + pool->base.pipe_count = res_cap.num_timing_generator; + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 100; + dc->caps.max_cursor_size = 128; + dc->debug = debug_defaults; + + /************************************************* + * Create resources * + *************************************************/ + + pool->base.clock_sources[DCE120_CLK_SRC_PLL0] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL1] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL1, + &clk_src_regs[1], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL2] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL2, + &clk_src_regs[2], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL3] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL4] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL4, + &clk_src_regs[4], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL5] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL5, + &clk_src_regs[5], false); + pool->base.clk_src_count = DCE120_CLK_SRC_TOTAL; + + pool->base.dp_clock_source = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_ID_DP_DTO, + &clk_src_regs[0], true); + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto clk_src_create_fail; + } + } + + pool->base.display_clock = dce120_disp_clk_create(ctx); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto disp_clk_create_fail; + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + irq_init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce120_create(&irq_init_data); + if (!pool->base.irqs) + goto irqs_create_fail; + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = + dce120_timing_generator_create( + ctx, + i, + &dce120_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto controller_create_fail; + } + + pool->base.mis[i] = dce120_mem_input_create(ctx, i); + + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto controller_create_fail; + } + + pool->base.ipps[i] = dce120_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto controller_create_fail; + } + + pool->base.transforms[i] = dce120_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce120_opp_create( + ctx, + i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + } + } + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + if (!dce120_hw_sequencer_create(dc)) + goto controller_create_fail; + + dc->caps.max_planes = pool->base.pipe_count; + + bw_calcs_init(dc->bw_dceip, dc->bw_vbios, dc->ctx->asic_id); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +irqs_create_fail: +controller_create_fail: +disp_clk_create_fail: +clk_src_create_fail: +res_create_fail: + + destruct(pool); + + return false; +} + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h new file mode 100644 index 000000000000..3d1f3cf012f4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h @@ -0,0 +1,39 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_RESOURCE_DCE120_H__ +#define __DC_RESOURCE_DCE120_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +#endif /* __DC_RESOURCE_DCE120_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c new file mode 100644 index 000000000000..2502182d5e82 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c @@ -0,0 +1,1174 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "dc_types.h" +#include "dc_bios_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce120_timing_generator.h" + +#include "timing_generator.h" + +#define CRTC_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) + +#define CRTC_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) + +#define CRTC_REG_UPDATE(reg, field, val) \ + CRTC_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define CRTC_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + CRTC_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define CRTC_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define CRTC_REG_UPDATE_4(reg, field1, val1, field2, val2, field3, val3, field4, val4) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4) + +#define CRTC_REG_UPDATE_5(reg, field1, val1, field2, val2, field3, val3, field4, val4, field5, val5) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4, FD(reg##__##field5), val5) + +#define CRTC_REG_SET(reg, field, val) \ + CRTC_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define CRTC_REG_SET_2(reg, field1, val1, field2, val2) \ + CRTC_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define CRTC_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + CRTC_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +/** + ***************************************************************************** + * Function: is_in_vertical_blank + * + * @brief + * check the current status of CRTC to check if we are in Vertical Blank + * regioneased" state + * + * @return + * true if currently in blank region, false otherwise + * + ***************************************************************************** + */ +static bool dce120_timing_generator_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS, + tg110->offsets.crtc); + + field = get_reg_field_value(value, CRTC0_CRTC_STATUS, CRTC_V_BLANK); + return field == 1; +} + + +/* determine if given timing can be supported by TG */ +bool dce120_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal) +{ + uint32_t interlace_factor = timing->flags.INTERLACE ? 2 : 1; + uint32_t v_blank = + (timing->v_total - timing->v_addressable - + timing->v_border_top - timing->v_border_bottom) * + interlace_factor; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + if (!dce110_timing_generator_validate_timing( + tg, + timing, + signal)) + return false; + + + if (v_blank < tg110->min_v_blank || + timing->h_sync_width < tg110->min_h_sync_width || + timing->v_sync_width < tg110->min_v_sync_width) + return false; + + return true; +} + +bool dce120_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + return dce120_timing_generator_validate_timing(tg, timing, SIGNAL_TYPE_NONE); +} + +/******** HW programming ************/ +/* Disable/Enable Timing Generator */ +bool dce120_timing_generator_enable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Set MASTER_UPDATE_MODE to 0 + * This is needed for DRR, and also suggested to be default value by Syed.*/ + + CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_MODE, + MASTER_UPDATE_MODE, 0); + + CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_LOCK, + UNDERFLOW_UPDATE_LOCK, 0); + + /* TODO API for AtomFirmware didn't change*/ + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, true); + + return result == BP_RESULT_OK; +} + +void dce120_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE(CRTC0_CRTC_CONTROL, + CRTC_HBLANK_EARLY_CONTROL, early_cntl); +} + +/**************** TG current status ******************/ + +/* return the current frame counter. Used by Linux kernel DRM */ +uint32_t dce120_timing_generator_get_vblank_counter( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_FRAME_COUNT, + tg110->offsets.crtc); + uint32_t field = get_reg_field_value( + value, CRTC0_CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +/* Get current H and V position */ +void dce120_timing_generator_get_crtc_position( + struct timing_generator *tg, + struct crtc_position *position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_POSITION, + tg110->offsets.crtc); + + position->horizontal_count = get_reg_field_value(value, + CRTC0_CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); + + position->vertical_count = get_reg_field_value(value, + CRTC0_CRTC_STATUS_POSITION, CRTC_VERT_COUNT); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_NOM_VERT_POSITION, + tg110->offsets.crtc); + + position->nominal_vcount = get_reg_field_value(value, + CRTC0_CRTC_NOM_VERT_POSITION, CRTC_VERT_COUNT_NOM); +} + +/* wait until TG is in beginning of vertical blank region */ +void dce120_timing_generator_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/* wait until TG is in beginning of active region */ +void dce120_timing_generator_wait_for_vactive(struct timing_generator *tg) +{ + while (dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/*********** Timing Generator Synchronization routines ****/ + +/* Setups Global Swap Lock group, TimingServer or TimingClient*/ +void dce120_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value_crtc_vtotal = + dm_read_reg_soc15(tg->ctx, + mmCRTC0_CRTC_V_TOTAL, + tg110->offsets.crtc); + /* Checkpoint relative to end of frame */ + uint32_t check_point = + get_reg_field_value(value_crtc_vtotal, + CRTC0_CRTC_V_TOTAL, + CRTC_V_TOTAL); + + + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_GSL_WINDOW, tg110->offsets.crtc, 0); + + CRTC_REG_UPDATE_N(DCP0_DCP_GSL_CONTROL, 6, + /* This pipe will belong to GSL Group zero. */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 1, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), gsl_params->gsl_master == tg->inst, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, + /* Keep signal low (pending high) during 6 lines. + * Also defines minimum interval before re-checking signal. */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, + /* DCP_GSL_PURPOSE_SURFACE_FLIP */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 1); + + CRTC_REG_SET_2( + CRTC0_CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM, check_point - FLIP_READY_BACK_LOOKUP, + CRTC_GSL_FORCE_DELAY, VFLIP_READY_DELAY); +} + +/* Clear all the register writes done by setup_global_swap_lock */ +void dce120_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Settig HW default values from reg specs */ + CRTC_REG_SET_N(DCP0_DCP_GSL_CONTROL, 6, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, + /* DCP_GSL_PURPOSE_SURFACE_FLIP */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 0); + + CRTC_REG_SET_2(CRTC0_CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM, 0, + CRTC_GSL_FORCE_DELAY, 0x2); /*TODO Why this value here ?*/ +} + +/* Reset slave controllers on master VSync */ +void dce120_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source) +{ + enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t rising_edge = 0; + uint32_t falling_edge = 0; + /* Setup trigger edge */ + uint32_t pol_value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_V_SYNC_A_CNTL, + tg110->offsets.crtc); + + /* Register spec has reversed definition: + * 0 for positive, 1 for negative */ + if (get_reg_field_value(pol_value, + CRTC0_CRTC_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL) == 0) { + rising_edge = 1; + } else { + falling_edge = 1; + } + + /* TODO What about other sources ?*/ + trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; + + CRTC_REG_UPDATE_N(CRTC0_CRTC_TRIGB_CNTL, 7, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_SOURCE_SELECT), trig_src_select, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_POLARITY_SELECT), TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_RISING_EDGE_DETECT_CNTL), rising_edge, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL), falling_edge, + /* send every signal */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FREQUENCY_SELECT), 0, + /* no delay */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_DELAY), 0, + /* clear trigger status */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_CLEAR), 1); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE, 2, + CRTC_FORCE_COUNT_NOW_TRIG_SEL, 1, + CRTC_FORCE_COUNT_NOW_CLEAR, 1); +} + +/* disabling trigger-reset */ +void dce120_timing_generator_disable_reset_trigger( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE, 0, + CRTC_FORCE_COUNT_NOW_CLEAR, 1); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT, TRIGGER_SOURCE_SELECT_LOGIC_ZERO, + CRTC_TRIGB_POLARITY_SELECT, TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + /* clear trigger status */ + CRTC_TRIGB_CLEAR, 1); + +} + +/* Checks whether CRTC triggered reset occurred */ +bool dce120_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + tg110->offsets.crtc); + + return get_reg_field_value(value, + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; +} + + +/******** Stuff to move to other virtual HW objects *****************/ +/* Move to enable accelerated mode */ +void dce120_timing_generator_disable_vga(struct timing_generator *tg) +{ + uint32_t offset = 0; + uint32_t value = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + switch (tg110->controller_id) { + case CONTROLLER_ID_D0: + offset = 0; + break; + case CONTROLLER_ID_D1: + offset = mmD2VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D2: + offset = mmD3VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D3: + offset = mmD4VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D4: + offset = mmD5VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D5: + offset = mmD6VGA_CONTROL - mmD1VGA_CONTROL; + break; + default: + break; + } + + value = dm_read_reg_soc15(tg->ctx, mmD1VGA_CONTROL, offset); + + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); + set_reg_field_value( + value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); + + dm_write_reg_soc15(tg->ctx, mmD1VGA_CONTROL, offset, value); +} +/* TODO: Should we move it to transform */ +/* Fully program CRTC timing in timing generator */ +void dce120_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t tmp1 = 0; + uint32_t tmp2 = 0; + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start = timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE( + CRTC0_CRTC_H_TOTAL, + CRTC_H_TOTAL, + timing->h_total - 1); + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL, + CRTC_V_TOTAL, + timing->v_total - 1); + + /* In case of V_TOTAL_CONTROL is on, make sure V_TOTAL_MAX and + * V_TOTAL_MIN are equal to V_TOTAL. + */ + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, + timing->v_total - 1); + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, + timing->v_total - 1); + + tmp1 = timing->h_total - + (h_sync_start + timing->h_border_left); + tmp2 = tmp1 + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END, tmp1, + CRTC_H_BLANK_START, tmp2); + + tmp1 = timing->v_total - (v_sync_start + timing->v_border_top); + tmp2 = tmp1 + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END, tmp1, + CRTC_V_BLANK_START, tmp2); +} + +/* TODO: Should we move it to opp? */ +/* Combine with below and move YUV/RGB color conversion to SW layer */ +void dce120_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, + CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, + CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); +} +/* Combine with above and move YUV/RGB color conversion to SW layer */ +void dce120_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + CRTC_REG_SET_3( + CRTC0_CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE, color->color_b_cb, + CRTC_OVERSCAN_COLOR_GREEN, color->color_g_y, + CRTC_OVERSCAN_COLOR_RED, color->color_r_cr); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_OVERSCAN_COLOR, + tg110->offsets.crtc); + + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLACK_COLOR, + tg110->offsets.crtc, + value); + + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_DATA_COLOR, + tg110->offsets.crtc, + value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ +} + +void dce120_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params) +{ + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + if (params != NULL && + params->vertical_total_max > 0 && + params->vertical_total_min > 0) { + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, params->vertical_total_min - 1); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, params->vertical_total_max - 1); + CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 6, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 1, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 1, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK_EN), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, + 0x180); + + } else { + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, 0); + CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 5, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, + 0); + } +} + +/** + ***************************************************************************** + * Function: dce120_timing_generator_get_position + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] position + ***************************************************************************** + */ +void dce120_timing_generator_get_position(struct timing_generator *tg, + struct crtc_position *position) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_POSITION, + tg110->offsets.crtc); + + position->horizontal_count = get_reg_field_value( + value, + CRTC0_CRTC_STATUS_POSITION, + CRTC_HORZ_COUNT); + + position->vertical_count = get_reg_field_value( + value, + CRTC0_CRTC_STATUS_POSITION, + CRTC_VERT_COUNT); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_NOM_VERT_POSITION, + tg110->offsets.crtc); + + position->nominal_vcount = get_reg_field_value( + value, + CRTC0_CRTC_NOM_VERT_POSITION, + CRTC_VERT_COUNT_NOM); +} + + +void dce120_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + struct crtc_position position; + + uint32_t v_blank_start_end = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_V_BLANK_START_END, + tg110->offsets.crtc); + + *v_blank_start = get_reg_field_value(v_blank_start_end, + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + *v_blank_end = get_reg_field_value(v_blank_start_end, + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + dce120_timing_generator_get_crtc_position( + tg, &position); + + *h_position = position.horizontal_count; + *v_position = position.vertical_count; +} + +void dce120_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t v_sync_width_and_b_porch = + timing->v_total - timing->v_addressable - + timing->v_border_bottom - timing->v_front_porch; + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_START_LINE_CONTROL, + tg110->offsets.crtc); + + set_reg_field_value( + value, + enable ? 0 : 1, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + + /* Program advanced line position acc.to the best case from fetching data perspective to hide MC latency + * and prefilling Line Buffer in V Blank (to 10 lines as LB can store max 10 lines) + */ + if (v_sync_width_and_b_porch > 10) + v_sync_width_and_b_porch = 10; + + set_reg_field_value( + value, + v_sync_width_and_b_porch, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + + dm_write_reg_soc15(tg->ctx, + mmCRTC0_CRTC_START_LINE_CONTROL, + tg110->offsets.crtc, + value); +} + +void dce120_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, + CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, + CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLACK_COLOR, + tg110->offsets.crtc); + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_DATA_COLOR, + tg110->offsets.crtc, + value); +} + +void dce120_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_SET_3( + CRTC0_CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE, overscan_color->color_b_cb, + CRTC_OVERSCAN_COLOR_GREEN, overscan_color->color_g_y, + CRTC_OVERSCAN_COLOR_RED, overscan_color->color_r_cr); +} + +void dce120_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce120_timing_generator_program_blanking(tg, timing); +} + +bool dce120_tg_is_blanked(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_CONTROL, + tg110->offsets.crtc); + + if (get_reg_field_value( + value, + CRTC0_CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC0_CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + return true; + + return false; +} + +void dce120_tg_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_SET( + CRTC0_CRTC_DOUBLE_BUFFER_CONTROL, + CRTC_BLANK_DATA_DOUBLE_BUFFER_EN, 0); + + if (enable_blanking) + CRTC_REG_SET(CRTC0_CRTC_BLANK_CONTROL, CRTC_BLANK_DATA_EN, 1); + else + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_BLANK_CONTROL, + tg110->offsets.crtc, 0); +} + +bool dce120_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +void dce120_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce120_timing_generator_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce120_timing_generator_wait_for_vactive(tg); + break; + + default: + break; + } +} + +void dce120_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce120_tg_program_blank_color(tg, blank_color); + + if (overscan_color != NULL) + dce120_tg_set_overscan_color(tg, overscan_color); +} + +static void dce120_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t value) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_2(CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, value, + CRTC_STATIC_SCREEN_FRAME_COUNT, 2); +} + +void dce120_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + enum test_pattern_color_format bit_depth; + enum test_pattern_dyn_range dyn_range; + enum test_pattern_mode mode; + /* color ramp generator mixes 16-bits color */ + uint32_t src_bpc = 16; + /* requested bpc */ + uint32_t dst_bpc; + uint32_t index; + /* RGB values of the color bars. + * Produce two RGB colors: RGB0 - white (all Fs) + * and RGB1 - black (all 0s) + * (three RGB components for two colors) + */ + uint16_t src_color[6] = {0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0000}; + /* dest color (converted to the specified color format) */ + uint16_t dst_color[6]; + uint32_t inc_base; + + /* translate to bit depth */ + switch (color_depth) { + case COLOR_DEPTH_666: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_6; + break; + case COLOR_DEPTH_888: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + case COLOR_DEPTH_101010: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_10; + break; + case COLOR_DEPTH_121212: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_12; + break; + default: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + } + + switch (test_pattern) { + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: + { + dyn_range = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ? + TEST_PATTERN_DYN_RANGE_CEA : + TEST_PATTERN_DYN_RANGE_VESA); + mode = TEST_PATTERN_MODE_COLORSQUARES_RGB; + + CRTC_REG_UPDATE_2(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_HRES, 6); + + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, dyn_range, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: + case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: + { + mode = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS ? + TEST_PATTERN_MODE_VERTICALBARS : + TEST_PATTERN_MODE_HORIZONTALBARS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* adjust color to the required colorFormat */ + for (index = 0; index < 6; index++) { + /* dst = 2^dstBpc * src / 2^srcBpc = src >> + * (srcBpc - dstBpc); + */ + dst_color[index] = + src_color[index] >> (src_bpc - dst_bpc); + /* CRTC_TEST_PATTERN_DATA has 16 bits, + * lowest 6 are hardwired to ZERO + * color bits should be left aligned aligned to MSB + * XXXXXXXXXX000000 for 10 bit, + * XXXXXXXX00000000 for 8 bit and XXXXXX0000000000 for 6 + */ + dst_color[index] <<= (16 - dst_bpc); + } + + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, 0); + + /* We have to write the mask before data, similar to pipeline. + * For example, for 8 bpc, if we want RGB0 to be magenta, + * and RGB1 to be cyan, + * we need to make 7 writes: + * MASK DATA + * 000001 00000000 00000000 set mask to R0 + * 000010 11111111 00000000 R0 255, 0xFF00, set mask to G0 + * 000100 00000000 00000000 G0 0, 0x0000, set mask to B0 + * 001000 11111111 00000000 B0 255, 0xFF00, set mask to R1 + * 010000 00000000 00000000 R1 0, 0x0000, set mask to G1 + * 100000 11111111 00000000 G1 255, 0xFF00, set mask to B1 + * 100000 11111111 00000000 B1 255, 0xFF00 + * + * we will make a loop of 6 in which we prepare the mask, + * then write, then prepare the color for next write. + * first iteration will write mask only, + * but each next iteration color prepared in + * previous iteration will be written within new mask, + * the last component will written separately, + * mask is not changing between 6th and 7th write + * and color will be prepared by last iteration + */ + + /* write color, color values mask in CRTC_TEST_PATTERN_MASK + * is B1, G1, R1, B0, G0, R0 + */ + value = 0; + for (index = 0; index < 6; index++) { + /* prepare color mask, first write PATTERN_DATA + * will have all zeros + */ + set_reg_field_value( + value, + (1 << index), + CRTC0_CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_MASK); + /* write color component */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + /* prepare next color component, + * will be written in the next iteration + */ + set_reg_field_value( + value, + dst_color[index], + CRTC0_CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_DATA); + } + /* write last color component, + * it's been already prepared in the loop + */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + + /* enable test pattern */ + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: + { + mode = (bit_depth == + TEST_PATTERN_COLOR_FORMAT_BPC_10 ? + TEST_PATTERN_MODE_DUALRAMP_RGB : + TEST_PATTERN_MODE_SINGLERAMP_RGB); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* increment for the first ramp for one color gradation + * 1 gradation for 6-bit color is 2^10 + * gradations in 16-bit color + */ + inc_base = (src_bpc - dst_bpc); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, 0, + CRTC_TEST_PATTERN_HRES, 6, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, 0, + CRTC_TEST_PATTERN_HRES, 8, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, inc_base + 2, + CRTC_TEST_PATTERN_HRES, 8, + CRTC_TEST_PATTERN_VRES, 5, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 384 << 6); + } + break; + default: + break; + } + + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, 0); + + /* enable test pattern */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, 0); + + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + { + value = 0; + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, value); + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, value); + } + break; + default: + break; + } +} + +static bool dce120_arm_vert_intr( + struct timing_generator *tg, + uint8_t width) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t v_blank_start, v_blank_end, h_position, v_position; + + tg->funcs->get_scanoutpos( + tg, + &v_blank_start, + &v_blank_end, + &h_position, + &v_position); + + if (v_blank_start == 0 || v_blank_end == 0) + return false; + + CRTC_REG_SET_2( + CRTC0_CRTC_VERTICAL_INTERRUPT0_POSITION, + CRTC_VERTICAL_INTERRUPT0_LINE_START, v_blank_start, + CRTC_VERTICAL_INTERRUPT0_LINE_END, v_blank_start + width); + + return true; +} + +static const struct timing_generator_funcs dce120_tg_funcs = { + .validate_timing = dce120_tg_validate_timing, + .program_timing = dce120_tg_program_timing, + .enable_crtc = dce120_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + /* used by enable_timing_synchronization. Not need for FPGA */ + .is_counter_moving = dce110_timing_generator_is_counter_moving, + /* never be called */ + .get_position = dce120_timing_generator_get_crtc_position, + .get_frame_count = dce120_timing_generator_get_vblank_counter, + .get_scanoutpos = dce120_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce120_timing_generator_set_early_control, + /* used by enable_timing_synchronization. Not need for FPGA */ + .wait_for_state = dce120_tg_wait_for_state, + .set_blank = dce120_tg_set_blank, + .is_blanked = dce120_tg_is_blanked, + /* never be called */ + .set_colors = dce120_tg_set_colors, + .set_overscan_blank_color = dce120_timing_generator_set_overscan_color_black, + .set_blank_color = dce120_timing_generator_program_blank_color, + .disable_vga = dce120_timing_generator_disable_vga, + .did_triggered_reset_occur = dce120_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = dce120_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce120_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce120_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = dce120_timing_generator_tear_down_global_swap_lock, + .enable_advanced_request = dce120_timing_generator_enable_advanced_request, + .set_drr = dce120_timing_generator_set_drr, + .set_static_screen_control = dce120_timing_generator_set_static_screen_control, + .set_test_pattern = dce120_timing_generator_set_test_pattern, + .arm_vert_intr = dce120_arm_vert_intr, +}; + + +void dce120_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + + tg110->offsets = *offsets; + + tg110->base.funcs = &dce120_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC0_CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC0_CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + /*//CRTC requires a minimum HBLANK = 32 pixels and o + * Minimum HSYNC = 8 pixels*/ + tg110->min_h_blank = 32; + /*DCE12_CRTC_Block_ARch.doc*/ + tg110->min_h_front_porch = 0; + tg110->min_h_back_porch = 0; + + tg110->min_h_sync_width = 8; + tg110->min_v_sync_width = 1; + tg110->min_v_blank = 3; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h new file mode 100644 index 000000000000..549d70b23e82 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h @@ -0,0 +1,40 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TIMING_GENERATOR_DCE120_H__ +#define __DC_TIMING_GENERATOR_DCE120_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" +#include "dce110/dce110_timing_generator.h" + + +void dce120_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +#endif /* __DC_TIMING_GENERATOR_DCE120_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce80/Makefile b/drivers/gpu/drm/amd/display/dc/dce80/Makefile new file mode 100644 index 000000000000..c1105895e5fa --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +DCE80 = dce80_timing_generator.o dce80_compressor.o dce80_hw_sequencer.o \ + dce80_resource.o + +AMD_DAL_DCE80 = $(addprefix $(AMDDALPATH)/dc/dce80/,$(DCE80)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE80) + + + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_compressor.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_compressor.c new file mode 100644 index 000000000000..951f2caba9b3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_compressor.c @@ -0,0 +1,834 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +#include "gmc/gmc_7_1_sh_mask.h" +#include "gmc/gmc_7_1_d.h" + +#include "include/logger_interface.h" +#include "dce80_compressor.h" + +#define DCP_REG(reg)\ + (reg + cp80->offsets.dcp_offset) +#define DMIF_REG(reg)\ + (reg + cp80->offsets.dmif_offset) + +static const struct dce80_compressor_reg_offsets reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG0_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG1_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG2_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG3_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG4_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = (mmDMIF_PG5_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +} +}; + +static const uint32_t dce8_one_lpt_channel_max_resolution = 2048 * 1200; + +enum fbc_idle_force { + /* Bit 0 - Display registers updated */ + FBC_IDLE_FORCE_DISPLAY_REGISTER_UPDATE = 0x00000001, + + /* Bit 2 - FBC_GRPH_COMP_EN register updated */ + FBC_IDLE_FORCE_GRPH_COMP_EN = 0x00000002, + /* Bit 3 - FBC_SRC_SEL register updated */ + FBC_IDLE_FORCE_SRC_SEL_CHANGE = 0x00000004, + /* Bit 4 - FBC_MIN_COMPRESSION register updated */ + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE = 0x00000008, + /* Bit 5 - FBC_ALPHA_COMP_EN register updated */ + FBC_IDLE_FORCE_ALPHA_COMP_EN = 0x00000010, + /* Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated */ + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN = 0x00000020, + /* Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated */ + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF = 0x00000040, + + /* Bit 24 - Memory write to region 0 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION0 = 0x01000000, + /* Bit 25 - Memory write to region 1 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION1 = 0x02000000, + /* Bit 26 - Memory write to region 2 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION2 = 0x04000000, + /* Bit 27 - Memory write to region 3 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION3 = 0x08000000, + + /* Bit 28 - Memory write from any client other than MCIF */ + FBC_IDLE_FORCE_MEMORY_WRITE_OTHER_THAN_MCIF = 0x10000000, + /* Bit 29 - CG statics screen signal is inactive */ + FBC_IDLE_FORCE_CG_STATIC_SCREEN_IS_INACTIVE = 0x20000000, +}; + +static uint32_t lpt_size_alignment(struct dce80_compressor *cp80) +{ + /*LPT_ALIGNMENT (in bytes) = ROW_SIZE * #BANKS * # DRAM CHANNELS. */ + return cp80->base.raw_size * cp80->base.banks_num * + cp80->base.dram_channels_num; +} + +static uint32_t lpt_memory_control_config(struct dce80_compressor *cp80, + uint32_t lpt_control) +{ + /*LPT MC Config */ + if (cp80->base.options.bits.LPT_MC_CONFIG == 1) { + /* POSSIBLE VALUES for LPT NUM_PIPES (DRAM CHANNELS): + * 00 - 1 CHANNEL + * 01 - 2 CHANNELS + * 02 - 4 OR 6 CHANNELS + * (Only for discrete GPU, N/A for CZ) + * 03 - 8 OR 12 CHANNELS + * (Only for discrete GPU, N/A for CZ) */ + switch (cp80->base.dram_channels_num) { + case 2: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + case 1: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + default: + dm_logger_write( + cp80->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT NUM_PIPES!!!", + __func__); + break; + } + + /* The mapping for LPT NUM_BANKS is in + * GRPH_CONTROL.GRPH_NUM_BANKS register field + * Specifies the number of memory banks for tiling + * purposes. Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: + * 00 - DCP_GRPH_NUM_BANKS_2BANK: ADDR_SURF_2_BANK + * 01 - DCP_GRPH_NUM_BANKS_4BANK: ADDR_SURF_4_BANK + * 02 - DCP_GRPH_NUM_BANKS_8BANK: ADDR_SURF_8_BANK + * 03 - DCP_GRPH_NUM_BANKS_16BANK: ADDR_SURF_16_BANK */ + switch (cp80->base.banks_num) { + case 16: + set_reg_field_value( + lpt_control, + 3, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 8: + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 4: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 2: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + default: + dm_logger_write( + cp80->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT NUM_BANKS!!!", + __func__); + break; + } + + /* The mapping is in DMIF_ADDR_CALC. + * ADDR_CONFIG_PIPE_INTERLEAVE_SIZE register field for + * Carrizo specifies the memory interleave per pipe. + * It effectively specifies the location of pipe bits in + * the memory address. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_PIPE_INTERLEAVE_256B: 256 byte + * interleave + * 01 - ADDR_CONFIG_PIPE_INTERLEAVE_512B: 512 byte + * interleave + */ + switch (cp80->base.channel_interleave_size) { + case 256: /*256B */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + case 512: /*512B */ + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + default: + dm_logger_write( + cp80->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT INTERLEAVE_SIZE!!!", + __func__); + break; + } + + /* The mapping for LOW_POWER_TILING_ROW_SIZE is in + * DMIF_ADDR_CALC.ADDR_CONFIG_ROW_SIZE register field + * for Carrizo. Specifies the size of dram row in bytes. + * This should match up with NOOFCOLS field in + * MC_ARB_RAMCFG (ROW_SIZE = 4 * 2 ^^ columns). + * This register DMIF_ADDR_CALC is not used by the + * hardware as it is only used for addrlib assertions. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_1KB_ROW: Treat 1KB as DRAM row + * boundary + * 01 - ADDR_CONFIG_2KB_ROW: Treat 2KB as DRAM row + * boundary + * 02 - ADDR_CONFIG_4KB_ROW: Treat 4KB as DRAM row + * boundary */ + switch (cp80->base.raw_size) { + case 4096: /*4 KB */ + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 2048: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 1024: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + default: + dm_logger_write( + cp80->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT ROW_SIZE!!!", + __func__); + break; + } + } else { + dm_logger_write( + cp80->base.ctx->logger, LOG_WARNING, + "%s: LPT MC Configuration is not provided", + __func__); + } + + return lpt_control; +} + +static bool is_source_bigger_than_epanel_size( + struct dce80_compressor *cp80, + uint32_t source_view_width, + uint32_t source_view_height) +{ + if (cp80->base.embedded_panel_h_size != 0 && + cp80->base.embedded_panel_v_size != 0 && + ((source_view_width * source_view_height) > + (cp80->base.embedded_panel_h_size * + cp80->base.embedded_panel_v_size))) + return true; + + return false; +} + +static uint32_t align_to_chunks_number_per_line( + struct dce80_compressor *cp80, + uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +static void wait_for_fbc_state_changed( + struct dce80_compressor *cp80, + bool enabled) +{ + uint8_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 10) { + value = dm_read_reg(cp80->base.ctx, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + udelay(10); + counter++; + } + + if (counter == 10) { + dm_logger_write( + cp80->base.ctx->logger, LOG_WARNING, + "%s: wait counter exceeded, changes to HW not applied", + __func__); + } +} + +void dce80_compressor_power_up_fbc(struct compressor *compressor) +{ + uint32_t value; + uint32_t addr; + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); + set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_MODE; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); + dm_write_reg(compressor->ctx, addr, value); + /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ + /* 1 ==> 4:1 */ + /* 2 ==> 8:1 */ + /* 0xF ==> 1:1 */ + set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); + dm_write_reg(compressor->ctx, addr, value); + compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; + + value = 0; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); + + value = 0xFFFFFF; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); +} + +void dce80_compressor_enable_fbc( + struct compressor *compressor, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + (compressor->options.bits.DUMMY_BACKEND == 0) && + (!dce80_compressor_is_fbc_enabled_in_hw(compressor, NULL)) && + (!is_source_bigger_than_epanel_size( + cp80, + params->source_view_width, + params->source_view_height))) { + + uint32_t addr; + uint32_t value; + + /* Before enabling FBC first need to enable LPT if applicable + * LPT state should always be changed (enable/disable) while FBC + * is disabled */ + if (compressor->options.bits.LPT_SUPPORT && (paths_num < 2) && + (params->source_view_width * + params->source_view_height <= + dce8_one_lpt_channel_max_resolution)) { + dce80_compressor_enable_lpt(compressor); + } + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value( + value, + params->inst, + FBC_CNTL, FBC_SRC_SEL); + dm_write_reg(compressor->ctx, addr, value); + + /* Keep track of enum controller_id FBC is attached to */ + compressor->is_enabled = true; + compressor->attached_inst = params->inst; + cp80->offsets = reg_offsets[params->inst]; + + /*Toggle it as there is bug in HW */ + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + wait_for_fbc_state_changed(cp80, true); + } +} + +void dce80_compressor_disable_fbc(struct compressor *compressor) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + dce80_compressor_is_fbc_enabled_in_hw(compressor, NULL)) { + uint32_t reg_data; + /* Turn off compression */ + reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL); + set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data); + + /* Reset enum controller_id to undefined */ + compressor->attached_inst = 0; + compressor->is_enabled = false; + + /* Whenever disabling FBC make sure LPT is disabled if LPT + * supported */ + if (compressor->options.bits.LPT_SUPPORT) + dce80_compressor_disable_lpt(compressor); + + wait_for_fbc_state_changed(cp80, false); + } +} + +bool dce80_compressor_is_fbc_enabled_in_hw( + struct compressor *compressor, + uint32_t *inst) +{ + /* Check the hardware register */ + uint32_t value; + + value = dm_read_reg(compressor->ctx, mmFBC_STATUS); + if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { + if (inst != NULL) + *inst = compressor->attached_inst; + return true; + } + + value = dm_read_reg(compressor->ctx, mmFBC_CNTL); + if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { + if (inst != NULL) + *inst = compressor->attached_inst; + return true; + } + + return false; +} + +bool dce80_compressor_is_lpt_enabled_in_hw(struct compressor *compressor) +{ + /* Check the hardware register */ + uint32_t value = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + return get_reg_field_value( + value, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); +} + +void dce80_compressor_program_compressed_surface_address_and_pitch( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + uint32_t value = 0; + uint32_t fbc_pitch = 0; + uint32_t compressed_surf_address_low_part = + compressor->compr_surface_address.addr.low_part; + + /* Clear content first. */ + dm_write_reg( + compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + 0); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), 0); + + if (compressor->options.bits.LPT_SUPPORT) { + uint32_t lpt_alignment = lpt_size_alignment(cp80); + + if (lpt_alignment != 0) { + compressed_surf_address_low_part = + ((compressed_surf_address_low_part + + (lpt_alignment - 1)) / lpt_alignment) + * lpt_alignment; + } + } + + /* Write address, HIGH has to be first. */ + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + compressor->compr_surface_address.addr.high_part); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), + compressed_surf_address_low_part); + + fbc_pitch = align_to_chunks_number_per_line( + cp80, + params->source_view_width); + + if (compressor->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) + fbc_pitch = fbc_pitch / 8; + else + dm_logger_write( + compressor->ctx->logger, LOG_WARNING, + "%s: Unexpected DCE8 compression ratio", + __func__); + + /* Clear content first. */ + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), 0); + + /* Write FBC Pitch. */ + set_reg_field_value( + value, + fbc_pitch, + GRPH_COMPRESS_PITCH, + GRPH_COMPRESS_PITCH); + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); + +} + +void dce80_compressor_disable_lpt(struct compressor *compressor) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t inx; + + /* Disable all pipes LPT Stutter */ + for (inx = 0; inx < 3; inx++) { + value = + dm_read_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 0, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), + value); + } + + /* Disable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); + + /* Clear selection of Channel(s) containing Compressed Surface */ + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0xFFFFFFFF, + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, mmGMCON_LPT_TARGET, value); +} + +void dce80_compressor_enable_lpt(struct compressor *compressor) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t value_control; + uint32_t channels; + + /* Enable LPT Stutter from Display pipe */ + value = dm_read_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 1, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), value); + + /* Selection of Channel(s) containing Compressed Surface: 0xfffffff + * will disable LPT. + * STCTRL_LPT_TARGETn corresponds to channel n. */ + addr = mmLOW_POWER_TILING_CONTROL; + value_control = dm_read_reg(compressor->ctx, addr); + channels = get_reg_field_value(value_control, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + channels + 1, /* not mentioned in programming guide, + but follow DCE8.1 */ + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, addr, value); + + /* Enable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); +} + +void dce80_compressor_program_lpt_control( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce80_compressor *cp80 = TO_DCE80_COMPRESSOR(compressor); + uint32_t rows_per_channel; + uint32_t lpt_alignment; + uint32_t source_view_width; + uint32_t source_view_height; + uint32_t lpt_control = 0; + + if (!compressor->options.bits.LPT_SUPPORT) + return; + + lpt_control = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + /* POSSIBLE VALUES for Low Power Tiling Mode: + * 00 - Use channel 0 + * 01 - Use Channel 0 and 1 + * 02 - Use Channel 0,1,2,3 + * 03 - reserved */ + switch (compressor->lpt_channels_num) { + /* case 2: + * Use Channel 0 & 1 / Not used for DCE 11 */ + case 1: + /*Use Channel 0 for LPT for DCE 11 */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + break; + default: + dm_logger_write( + compressor->ctx->logger, LOG_WARNING, + "%s: Invalid selected DRAM channels for LPT!!!", + __func__); + break; + } + + lpt_control = lpt_memory_control_config(cp80, lpt_control); + + /* Program LOW_POWER_TILING_ROWS_PER_CHAN field which depends on + * FBC compressed surface pitch. + * LOW_POWER_TILING_ROWS_PER_CHAN = Roundup ((Surface Height * + * Surface Pitch) / (Row Size * Number of Channels * + * Number of Banks)). */ + rows_per_channel = 0; + lpt_alignment = lpt_size_alignment(cp80); + source_view_width = + align_to_chunks_number_per_line( + cp80, + params->source_view_width); + source_view_height = (params->source_view_height + 1) & (~0x1); + + if (lpt_alignment != 0) { + rows_per_channel = source_view_width * source_view_height * 4; + rows_per_channel = + (rows_per_channel % lpt_alignment) ? + (rows_per_channel / lpt_alignment + 1) : + rows_per_channel / lpt_alignment; + } + + set_reg_field_value( + lpt_control, + rows_per_channel, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROWS_PER_CHAN); + + dm_write_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL, lpt_control); +} + +/* + * DCE 11 Frame Buffer Compression Implementation + */ + +void dce80_compressor_set_fbc_invalidation_triggers( + struct compressor *compressor, + uint32_t fbc_trigger) +{ + /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) + * for DCE 11 regions cannot be used - does not work with S/G + */ + uint32_t addr = mmFBC_CLIENT_REGION_MASK; + uint32_t value = dm_read_reg(compressor->ctx, addr); + + set_reg_field_value( + value, + 0, + FBC_CLIENT_REGION_MASK, + FBC_MEMORY_REGION_MASK); + dm_write_reg(compressor->ctx, addr, value); + + /* Setup events when to clear all CSM entries (effectively marking + * current compressed data invalid) + * For DCE 11 CSM metadata 11111 means - "Not Compressed" + * Used as the initial value of the metadata sent to the compressor + * after invalidation, to indicate that the compressor should attempt + * to compress all chunks on the current pass. Also used when the chunk + * is not successfully written to memory. + * When this CSM value is detected, FBC reads from the uncompressed + * buffer. Set events according to passed in value, these events are + * valid for DCE8: + * - bit 0 - display register updated + * - bit 28 - memory write from any client except from MCIF + * - bit 29 - CG static screen signal is inactive + * In addition, DCE8.1 also needs to set new DCE8.1 specific events + * that are used to trigger invalidation on certain register changes, + * for example enabling of Alpha Compression may trigger invalidation of + * FBC once bit is set. These events are as follows: + * - Bit 2 - FBC_GRPH_COMP_EN register updated + * - Bit 3 - FBC_SRC_SEL register updated + * - Bit 4 - FBC_MIN_COMPRESSION register updated + * - Bit 5 - FBC_ALPHA_COMP_EN register updated + * - Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated + * - Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated + */ + addr = mmFBC_IDLE_FORCE_CLEAR_MASK; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + fbc_trigger | + FBC_IDLE_FORCE_GRPH_COMP_EN | + FBC_IDLE_FORCE_SRC_SEL_CHANGE | + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE | + FBC_IDLE_FORCE_ALPHA_COMP_EN | + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN | + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF, + FBC_IDLE_FORCE_CLEAR_MASK, + FBC_IDLE_FORCE_CLEAR_MASK); + dm_write_reg(compressor->ctx, addr, value); +} + +void dce80_compressor_construct(struct dce80_compressor *compressor, + struct dc_context *ctx) +{ + struct dc_bios *bp = ctx->dc_bios; + struct embedded_panel_info panel_info; + + compressor->base.options.raw = 0; + compressor->base.options.bits.FBC_SUPPORT = true; + compressor->base.options.bits.LPT_SUPPORT = true; + /* For DCE 11 always use one DRAM channel for LPT */ + compressor->base.lpt_channels_num = 1; + compressor->base.options.bits.DUMMY_BACKEND = false; + + /* Check if this system has more than 1 DRAM channel; if only 1 then LPT + * should not be supported */ + if (compressor->base.memory_bus_width == 64) + compressor->base.options.bits.LPT_SUPPORT = false; + + compressor->base.options.bits.CLK_GATING_DISABLED = false; + + compressor->base.ctx = ctx; + compressor->base.embedded_panel_h_size = 0; + compressor->base.embedded_panel_v_size = 0; + compressor->base.memory_bus_width = ctx->asic_id.vram_width; + compressor->base.allocated_size = 0; + compressor->base.preferred_requested_size = 0; + compressor->base.min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; + compressor->base.banks_num = 0; + compressor->base.raw_size = 0; + compressor->base.channel_interleave_size = 0; + compressor->base.dram_channels_num = 0; + compressor->base.lpt_channels_num = 0; + compressor->base.attached_inst = 0; + compressor->base.is_enabled = false; + + if (BP_RESULT_OK == + bp->funcs->get_embedded_panel_info(bp, &panel_info)) { + compressor->base.embedded_panel_h_size = + panel_info.lcd_timing.horizontal_addressable; + compressor->base.embedded_panel_v_size = + panel_info.lcd_timing.vertical_addressable; + } +} + +struct compressor *dce80_compressor_create(struct dc_context *ctx) +{ + struct dce80_compressor *cp80 = + kzalloc(sizeof(struct dce80_compressor), GFP_KERNEL); + + if (!cp80) + return NULL; + + dce80_compressor_construct(cp80, ctx); + return &cp80->base; +} + +void dce80_compressor_destroy(struct compressor **compressor) +{ + kfree(TO_DCE80_COMPRESSOR(*compressor)); + *compressor = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_compressor.h b/drivers/gpu/drm/amd/display/dc/dce80/dce80_compressor.h new file mode 100644 index 000000000000..cca58b044402 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_compressor.h @@ -0,0 +1,78 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_COMPRESSOR_DCE80_H__ +#define __DC_COMPRESSOR_DCE80_H__ + +#include "../inc/compressor.h" + +#define TO_DCE80_COMPRESSOR(compressor)\ + container_of(compressor, struct dce80_compressor, base) + +struct dce80_compressor_reg_offsets { + uint32_t dcp_offset; + uint32_t dmif_offset; +}; + +struct dce80_compressor { + struct compressor base; + struct dce80_compressor_reg_offsets offsets; +}; + +struct compressor *dce80_compressor_create(struct dc_context *ctx); + +void dce80_compressor_construct(struct dce80_compressor *cp80, + struct dc_context *ctx); + +void dce80_compressor_destroy(struct compressor **cp); + +/* FBC RELATED */ +void dce80_compressor_power_up_fbc(struct compressor *cp); + +void dce80_compressor_enable_fbc(struct compressor *cp, uint32_t paths_num, + struct compr_addr_and_pitch_params *params); + +void dce80_compressor_disable_fbc(struct compressor *cp); + +void dce80_compressor_set_fbc_invalidation_triggers(struct compressor *cp, + uint32_t fbc_trigger); + +void dce80_compressor_program_compressed_surface_address_and_pitch( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce80_compressor_is_fbc_enabled_in_hw(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); + +/* LPT RELATED */ +void dce80_compressor_enable_lpt(struct compressor *cp); + +void dce80_compressor_disable_lpt(struct compressor *cp); + +void dce80_compressor_program_lpt_control(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce80_compressor_is_lpt_enabled_in_hw(struct compressor *cp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.c new file mode 100644 index 000000000000..ccfcf1c0eeb3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.c @@ -0,0 +1,117 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dc.h" +#include "core_types.h" +#include "dce80_hw_sequencer.h" + +#include "dce/dce_hwseq.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dce100/dce100_hw_sequencer.h" + +/* include DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +struct dce80_hw_seq_reg_offsets { + uint32_t crtc; +}; + +static const struct dce80_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +/******************************************************************************* + * Private definitions + ******************************************************************************/ + +/***************************PIPE_CONTROL***********************************/ + +static bool dce80_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (!(power_gating == PIPE_GATING_CONTROL_INIT && controller_id != 0)){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmMASTER_UPDATE_MODE, controller_id), + 0); + } + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +void dce80_hw_sequencer_construct(struct dc *dc) +{ + dce110_hw_sequencer_construct(dc); + + dc->hwss.enable_display_power_gating = dce80_enable_display_power_gating; + dc->hwss.pipe_control_lock = dce_pipe_control_lock; + dc->hwss.set_bandwidth = dce100_set_bandwidth; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.h new file mode 100644 index 000000000000..7a1b31def66f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.h @@ -0,0 +1,36 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HWSS_DCE80_H__ +#define __DC_HWSS_DCE80_H__ + +#include "core_types.h" + +struct dc; + +void dce80_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE80_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c new file mode 100644 index 000000000000..9c18efd3446f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c @@ -0,0 +1,1257 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dm_services.h" + +#include "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "irq/dce80/irq_service_dce80.h" +#include "dce110/dce110_timing_generator.h" +#include "dce110/dce110_resource.h" +#include "dce80/dce80_timing_generator.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_ipp.h" +#include "dce/dce_transform.h" +#include "dce/dce_opp.h" +#include "dce/dce_clocks.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_audio.h" +#include "dce/dce_hwseq.h" +#include "dce80/dce80_hw_sequencer.h" +#include "dce100/dce100_resource.h" + +#include "reg_helper.h" + +/* TODO remove this include */ + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_7_1_d.h" +#include "gmc/gmc_7_1_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL +#define mmDP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP0_DP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP1_DP_DPHY_INTERNAL_CTRL 0x1FDE +#define mmDP2_DP_DPHY_INTERNAL_CTRL 0x42DE +#define mmDP3_DP_DPHY_INTERNAL_CTRL 0x45DE +#define mmDP4_DP_DPHY_INTERNAL_CTRL 0x48DE +#define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4BDE +#define mmDP6_DP_DPHY_INTERNAL_CTRL 0x4EDE +#endif + + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x1CCE + #define mmDP0_DP_DPHY_FAST_TRAINING 0x1CCE + #define mmDP1_DP_DPHY_FAST_TRAINING 0x1FCE + #define mmDP2_DP_DPHY_FAST_TRAINING 0x42CE + #define mmDP3_DP_DPHY_FAST_TRAINING 0x45CE + #define mmDP4_DP_DPHY_FAST_TRAINING 0x48CE + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4BCE + #define mmDP6_DP_DPHY_FAST_TRAINING 0x4ECE +#endif + + +#ifndef mmHPD_DC_HPD_CONTROL + #define mmHPD_DC_HPD_CONTROL 0x189A + #define mmHPD0_DC_HPD_CONTROL 0x189A + #define mmHPD1_DC_HPD_CONTROL 0x18A2 + #define mmHPD2_DC_HPD_CONTROL 0x18AA + #define mmHPD3_DC_HPD_CONTROL 0x18B2 + #define mmHPD4_DC_HPD_CONTROL 0x18BA + #define mmHPD5_DC_HPD_CONTROL 0x18C2 +#endif + +#define DCE11_DIG_FE_CNTL 0x4a00 +#define DCE11_DIG_BE_CNTL 0x4a47 +#define DCE11_DP_SEC 0x4ac3 + +static const struct dce110_timing_generator_offsets dce80_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmGRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG3_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG4_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG5_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + + +static const struct dce_disp_clk_registers disp_clk_regs = { + CLK_COMMON_REG_LIST_DCE_BASE() +}; + +static const struct dce_disp_clk_shift disp_clk_shift = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_disp_clk_mask disp_clk_mask = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_COMMON_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE80(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE80(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE80(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE80_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST_DCE_BASE(id),\ + .AFMT_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5), + stream_enc_regs(6) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE80_100(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE80_100(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_80_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_80(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_80(_MASK) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_aduio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_80(id),\ +} + + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 3, +}; + +static const struct resource_caps res_cap_81 = { + .num_timing_generator = 4, + .num_audio = 7, + .num_stream_encoder = 7, + .num_pll = 3, +}; + +static const struct resource_caps res_cap_83 = { + .num_timing_generator = 2, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 2, +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x1918 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce80_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce80_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct output_pixel_processor *dce80_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static struct stream_encoder *dce80_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE8_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE8_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE8_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce80_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce80_stream_encoder_create, + .create_hwseq = dce80_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_DCE8_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE8_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE8_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + +static struct mem_input *dce80_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + dce_mi->wa.single_head_rdreq_dmif_limit = 2; + return &dce_mi->base; +} + +static void dce80_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce80_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->prescaler_on = false; + return &transform->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 297000, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_YCBCR_CAPABLE = true +}; + +struct link_encoder *dce80_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +struct clock_source *dce80_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce80_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static struct input_pixel_processor *dce80_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static void destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce80_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce80_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.dp_clock_source != NULL) + dce80_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.display_clock != NULL) + dce_disp_clk_destroy(&pool->base.display_clock); + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + dce110_resource_build_pipe_hw_param(pipe_ctx); + + resource_build_info_frame(pipe_ctx); + + return DC_OK; +} + +bool dce80_validate_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + /* TODO implement when needed but for now hardcode max value*/ + context->bw.dce.dispclk_khz = 681000; + context->bw.dce.yclk_khz = 250000 * MEMORY_TYPE_MULTIPLIER; + + return true; +} + +static bool dce80_validate_surface_sets( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 1) + return false; + + if (context->stream_status[i].plane_states[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + } + + return true; +} + +enum dc_status dce80_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce80_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +enum dc_status dce80_validate_guaranteed( + struct dc *dc, + struct dc_stream_state *dc_stream, + struct dc_state *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + context->streams[0] = dc_stream; + dc_stream_retain(context->streams[0]); + context->stream_count++; + + result = resource_map_pool_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = build_mapped_resource(dc, context, dc_stream); + + if (result == DC_OK) { + validate_guaranteed_copy_streams( + context, dc->caps.max_streams); + result = resource_build_scaling_params_for_context(dc, context); + } + + if (result == DC_OK) + result = dce80_validate_bandwidth(dc, context); + + return result; +} + +static void dce80_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce80_res_pool_funcs = { + .destroy = dce80_destroy_resource_pool, + .link_enc_create = dce80_link_encoder_create, + .validate_guaranteed = dce80_validate_guaranteed, + .validate_bandwidth = dce80_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce100_add_stream_to_ctx, + .validate_global = dce80_validate_global +}; + +static bool dce80_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_firmware_info info; + struct dc_bios *bp; + struct dm_pp_static_clock_info static_clk_info = {0}; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce80_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 128; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if ((bp->funcs->get_firmware_info(bp, &info) == BP_RESULT_OK) && + info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[2] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 3; + + } else { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.display_clock = dce_disp_clk_create(ctx, + &disp_clk_regs, + &disp_clk_shift, + &disp_clk_mask); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) + pool->base.display_clock->max_clks_state = + static_clk_info.max_clocks_state; + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce80_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce80_timing_generator_create( + ctx, i, &dce80_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce80_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce80_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce80_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce80_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce80_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + destruct(pool); + return false; +} + +struct resource_pool *dce80_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce80_construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} + +static bool dce81_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_firmware_info info; + struct dc_bios *bp; + struct dm_pp_static_clock_info static_clk_info = {0}; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap_81; + pool->base.funcs = &dce80_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap_81.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 128; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if ((bp->funcs->get_firmware_info(bp, &info) == BP_RESULT_OK) && + info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[2] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 3; + + } else { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.display_clock = dce_disp_clk_create(ctx, + &disp_clk_regs, + &disp_clk_shift, + &disp_clk_mask); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) + pool->base.display_clock->max_clks_state = + static_clk_info.max_clocks_state; + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce80_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce80_timing_generator_create( + ctx, i, &dce80_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce80_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce80_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce80_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce80_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce80_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + destruct(pool); + return false; +} + +struct resource_pool *dce81_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce81_construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} + +static bool dce83_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_firmware_info info; + struct dc_bios *bp; + struct dm_pp_static_clock_info static_clk_info = {0}; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap_83; + pool->base.funcs = &dce80_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap_83.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 128; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if ((bp->funcs->get_firmware_info(bp, &info) == BP_RESULT_OK) && + info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); + pool->base.clk_src_count = 2; + + } else { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); + pool->base.clk_src_count = 1; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.display_clock = dce_disp_clk_create(ctx, + &disp_clk_regs, + &disp_clk_shift, + &disp_clk_mask); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) + pool->base.display_clock->max_clks_state = + static_clk_info.max_clocks_state; + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce80_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce80_timing_generator_create( + ctx, i, &dce80_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce80_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce80_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce80_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce80_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce80_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + destruct(pool); + return false; +} + +struct resource_pool *dce83_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce83_construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.h b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.h new file mode 100644 index 000000000000..eff31ab83a39 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.h @@ -0,0 +1,47 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_RESOURCE_DCE80_H__ +#define __DC_RESOURCE_DCE80_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce80_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +struct resource_pool *dce81_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +struct resource_pool *dce83_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +#endif /* __DC_RESOURCE_DCE80_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c new file mode 100644 index 000000000000..265894851493 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c @@ -0,0 +1,239 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dc_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "../dce110/dce110_timing_generator.h" +#include "dce80_timing_generator.h" + +#include "timing_generator.h" + +enum black_color_format { + BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, /* used as index in array */ + BLACK_COLOR_FORMAT_RGB_LIMITED, + BLACK_COLOR_FORMAT_YUV_TV, + BLACK_COLOR_FORMAT_YUV_CV, + BLACK_COLOR_FORMAT_YUV_SUPER_AA, + + BLACK_COLOR_FORMAT_COUNT +}; + +static const struct dce110_timing_generator_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC1_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC2_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC3_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC4_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC5_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +} +}; + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) + +#define CRTC_REG(reg) (reg + tg110->offsets.crtc) +#define DCP_REG(reg) (reg + tg110->offsets.dcp) +#define DMIF_REG(reg) (reg + tg110->offsets.dmif) + +void program_pix_dur(struct timing_generator *tg, uint32_t pix_clk_khz) +{ + uint64_t pix_dur; + uint32_t addr = mmDMIF_PG0_DPG_PIPE_ARBITRATION_CONTROL1 + + DCE110TG_FROM_TG(tg)->offsets.dmif; + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (pix_clk_khz == 0) + return; + + pix_dur = 1000000000 / pix_clk_khz; + + set_reg_field_value( + value, + pix_dur, + DPG_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + + dm_write_reg(tg->ctx, addr, value); +} + +static void program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (!use_vbios) + program_pix_dur(tg, timing->pix_clk_khz); + + dce110_tg_program_timing(tg, timing, use_vbios); +} + +static const struct timing_generator_funcs dce80_tg_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = program_timing, + .enable_crtc = dce110_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + .is_counter_moving = dce110_timing_generator_is_counter_moving, + .get_position = dce110_timing_generator_get_position, + .get_frame_count = dce110_timing_generator_get_vblank_counter, + .get_scanoutpos = dce110_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce110_timing_generator_set_early_control, + .wait_for_state = dce110_tg_wait_for_state, + .set_blank = dce110_tg_set_blank, + .is_blanked = dce110_tg_is_blanked, + .set_colors = dce110_tg_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_program_blank_color, + .disable_vga = dce110_timing_generator_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_tear_down_global_swap_lock, + .set_drr = dce110_timing_generator_set_drr, + .set_static_screen_control = + dce110_timing_generator_set_static_screen_control, + .set_test_pattern = dce110_timing_generator_set_test_pattern, + .arm_vert_intr = dce110_arm_vert_intr, + + /* DCE8.0 overrides */ + .enable_advanced_request = + dce80_timing_generator_enable_advanced_request, +}; + +void dce80_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + tg110->offsets = *offsets; + tg110->derived_offsets = reg_offsets[instance]; + + tg110->base.funcs = &dce80_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; +} + +void dce80_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dm_write_reg(tg->ctx, addr, value); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.h new file mode 100644 index 000000000000..9cebb24c94c8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TIMING_GENERATOR_DCE80_H__ +#define __DC_TIMING_GENERATOR_DCE80_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" + +/* DCE8.0 implementation inherits from DCE11.0 */ +void dce80_timing_generator_construct( + struct dce110_timing_generator *tg, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +/******** HW programming ************/ +void dce80_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing); + +#endif /* __DC_TIMING_GENERATOR_DCE80_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/Makefile b/drivers/gpu/drm/amd/display/dc/dcn10/Makefile new file mode 100644 index 000000000000..ebeb88283a14 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for DCN. + +DCN10 = dcn10_resource.o dcn10_ipp.o dcn10_hw_sequencer.o \ + dcn10_dpp.o dcn10_opp.o dcn10_timing_generator.o \ + dcn10_hubp.o dcn10_mpc.o \ + dcn10_dpp_dscl.o dcn10_dpp_cm.o dcn10_cm_common.o + +AMD_DAL_DCN10 = $(addprefix $(AMDDALPATH)/dc/dcn10/,$(DCN10)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCN10) diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c new file mode 100644 index 000000000000..7f579cb19f4b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c @@ -0,0 +1,123 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "reg_helper.h" +#include "dcn10_dpp.h" + +#include "dcn10_cm_common.h" + +#define REG(reg) reg + +#define CTX \ + ctx + +#undef FN +#define FN(reg_name, field_name) \ + reg->shifts.field_name, reg->masks.field_name + +void cm_helper_program_color_matrices( + struct dc_context *ctx, + const uint16_t *regval, + const struct color_matrices_reg *reg) +{ + uint32_t cur_csc_reg; + unsigned int i = 0; + + for (cur_csc_reg = reg->csc_c11_c12; + cur_csc_reg <= reg->csc_c33_c34; + cur_csc_reg++) { + + const uint16_t *regval0 = &(regval[2 * i]); + const uint16_t *regval1 = &(regval[(2 * i) + 1]); + + REG_SET_2(cur_csc_reg, 0, + csc_c11, *regval0, + csc_c12, *regval1); + + i++; + } + +} + +void cm_helper_program_xfer_func( + struct dc_context *ctx, + const struct pwl_params *params, + const struct xfer_func_reg *reg) +{ + uint32_t reg_region_cur; + unsigned int i = 0; + + REG_SET_2(reg->start_cntl_b, 0, + exp_region_start, params->arr_points[0].custom_float_x, + exp_resion_start_segment, 0); + REG_SET_2(reg->start_cntl_g, 0, + exp_region_start, params->arr_points[0].custom_float_x, + exp_resion_start_segment, 0); + REG_SET_2(reg->start_cntl_r, 0, + exp_region_start, params->arr_points[0].custom_float_x, + exp_resion_start_segment, 0); + + REG_SET(reg->start_slope_cntl_b, 0, + field_region_linear_slope, params->arr_points[0].custom_float_slope); + REG_SET(reg->start_slope_cntl_g, 0, + field_region_linear_slope, params->arr_points[0].custom_float_slope); + REG_SET(reg->start_slope_cntl_r, 0, + field_region_linear_slope, params->arr_points[0].custom_float_slope); + + REG_SET(reg->start_end_cntl1_b, 0, + field_region_end, params->arr_points[1].custom_float_x); + REG_SET_2(reg->start_end_cntl2_b, 0, + field_region_end_slope, params->arr_points[1].custom_float_slope, + field_region_end_base, params->arr_points[1].custom_float_y); + + REG_SET(reg->start_end_cntl1_g, 0, + field_region_end, params->arr_points[1].custom_float_x); + REG_SET_2(reg->start_end_cntl2_g, 0, + field_region_end_slope, params->arr_points[1].custom_float_slope, + field_region_end_base, params->arr_points[1].custom_float_y); + + REG_SET(reg->start_end_cntl1_r, 0, + field_region_end, params->arr_points[1].custom_float_x); + REG_SET_2(reg->start_end_cntl2_r, 0, + field_region_end_slope, params->arr_points[1].custom_float_slope, + field_region_end_base, params->arr_points[1].custom_float_y); + + for (reg_region_cur = reg->region_start; + reg_region_cur <= reg->region_end; + reg_region_cur++) { + + const struct gamma_curve *curve0 = &(params->arr_curve_points[2 * i]); + const struct gamma_curve *curve1 = &(params->arr_curve_points[(2 * i) + 1]); + + REG_SET_4(reg_region_cur, 0, + exp_region0_lut_offset, curve0->offset, + exp_region0_num_segments, curve0->segments_num, + exp_region1_lut_offset, curve1->offset, + exp_region1_num_segments, curve1->segments_num); + + i++; + } + +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h new file mode 100644 index 000000000000..64836dcf21f2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h @@ -0,0 +1,99 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_DCN10_CM_COMMON_H__ +#define __DAL_DCN10_CM_COMMON_H__ + +#define TF_HELPER_REG_FIELD_LIST(type) \ + type exp_region0_lut_offset; \ + type exp_region0_num_segments; \ + type exp_region1_lut_offset; \ + type exp_region1_num_segments;\ + type field_region_end;\ + type field_region_end_slope;\ + type field_region_end_base;\ + type exp_region_start;\ + type exp_resion_start_segment;\ + type field_region_linear_slope + +#define TF_CM_REG_FIELD_LIST(type) \ + type csc_c11; \ + type csc_c12 + +struct xfer_func_shift { + TF_HELPER_REG_FIELD_LIST(uint8_t); +}; + +struct xfer_func_mask { + TF_HELPER_REG_FIELD_LIST(uint32_t); +}; + +struct xfer_func_reg { + struct xfer_func_shift shifts; + struct xfer_func_mask masks; + + uint32_t start_cntl_b; + uint32_t start_cntl_g; + uint32_t start_cntl_r; + uint32_t start_slope_cntl_b; + uint32_t start_slope_cntl_g; + uint32_t start_slope_cntl_r; + uint32_t start_end_cntl1_b; + uint32_t start_end_cntl2_b; + uint32_t start_end_cntl1_g; + uint32_t start_end_cntl2_g; + uint32_t start_end_cntl1_r; + uint32_t start_end_cntl2_r; + uint32_t region_start; + uint32_t region_end; +}; + +struct cm_color_matrix_shift { + TF_CM_REG_FIELD_LIST(uint8_t); +}; + +struct cm_color_matrix_mask { + TF_CM_REG_FIELD_LIST(uint32_t); +}; + +struct color_matrices_reg{ + struct cm_color_matrix_shift shifts; + struct cm_color_matrix_mask masks; + + uint32_t csc_c11_c12; + uint32_t csc_c33_c34; +}; + +void cm_helper_program_color_matrices( + struct dc_context *ctx, + const uint16_t *regval, + const struct color_matrices_reg *reg); + +void cm_helper_program_xfer_func( + struct dc_context *ctx, + const struct pwl_params *params, + const struct xfer_func_reg *reg); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c new file mode 100644 index 000000000000..74e7c82bdc76 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c @@ -0,0 +1,481 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "core_types.h" + +#include "reg_helper.h" +#include "dcn10_dpp.h" +#include "basics/conversion.h" + +#define NUM_PHASES 64 +#define HORZ_MAX_TAPS 8 +#define VERT_MAX_TAPS 8 + +#define BLACK_OFFSET_RGB_Y 0x0 +#define BLACK_OFFSET_CBCR 0x8000 + +#define REG(reg)\ + dpp->tf_regs->reg + +#define CTX \ + dpp->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + dpp->tf_shift->field_name, dpp->tf_mask->field_name + +enum pixel_format_description { + PIXEL_FORMAT_FIXED = 0, + PIXEL_FORMAT_FIXED16, + PIXEL_FORMAT_FLOAT + +}; + +enum dcn10_coef_filter_type_sel { + SCL_COEF_LUMA_VERT_FILTER = 0, + SCL_COEF_LUMA_HORZ_FILTER = 1, + SCL_COEF_CHROMA_VERT_FILTER = 2, + SCL_COEF_CHROMA_HORZ_FILTER = 3, + SCL_COEF_ALPHA_VERT_FILTER = 4, + SCL_COEF_ALPHA_HORZ_FILTER = 5 +}; + +enum dscl_autocal_mode { + AUTOCAL_MODE_OFF = 0, + + /* Autocal calculate the scaling ratio and initial phase and the + * DSCL_MODE_SEL must be set to 1 + */ + AUTOCAL_MODE_AUTOSCALE = 1, + /* Autocal perform auto centering without replication and the + * DSCL_MODE_SEL must be set to 0 + */ + AUTOCAL_MODE_AUTOCENTER = 2, + /* Autocal perform auto centering and auto replication and the + * DSCL_MODE_SEL must be set to 0 + */ + AUTOCAL_MODE_AUTOREPLICATE = 3 +}; + +enum dscl_mode_sel { + DSCL_MODE_SCALING_444_BYPASS = 0, + DSCL_MODE_SCALING_444_RGB_ENABLE = 1, + DSCL_MODE_SCALING_444_YCBCR_ENABLE = 2, + DSCL_MODE_SCALING_420_YCBCR_ENABLE = 3, + DSCL_MODE_SCALING_420_LUMA_BYPASS = 4, + DSCL_MODE_SCALING_420_CHROMA_BYPASS = 5, + DSCL_MODE_DSCL_BYPASS = 6 +}; + +enum gamut_remap_select { + GAMUT_REMAP_BYPASS = 0, + GAMUT_REMAP_COEFF, + GAMUT_REMAP_COMA_COEFF, + GAMUT_REMAP_COMB_COEFF +}; + +/* Program gamut remap in bypass mode */ +void dpp_set_gamut_remap_bypass(struct dcn10_dpp *dpp) +{ + REG_SET(CM_GAMUT_REMAP_CONTROL, 0, + CM_GAMUT_REMAP_MODE, 0); + /* Gamut remap in bypass */ +} + +#define IDENTITY_RATIO(ratio) (dal_fixed31_32_u2d19(ratio) == (1 << 19)) + + +bool dpp_get_optimal_number_of_taps( + struct dpp *dpp, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps) +{ + uint32_t pixel_width; + + if (scl_data->viewport.width > scl_data->recout.width) + pixel_width = scl_data->recout.width; + else + pixel_width = scl_data->viewport.width; + + /* TODO: add lb check */ + + /* No support for programming ratio of 4, drop to 3.99999.. */ + if (scl_data->ratios.horz.value == (4ll << 32)) + scl_data->ratios.horz.value--; + if (scl_data->ratios.vert.value == (4ll << 32)) + scl_data->ratios.vert.value--; + if (scl_data->ratios.horz_c.value == (4ll << 32)) + scl_data->ratios.horz_c.value--; + if (scl_data->ratios.vert_c.value == (4ll << 32)) + scl_data->ratios.vert_c.value--; + + /* Set default taps if none are provided */ + if (in_taps->h_taps == 0) + scl_data->taps.h_taps = 4; + else + scl_data->taps.h_taps = in_taps->h_taps; + if (in_taps->v_taps == 0) + scl_data->taps.v_taps = 4; + else + scl_data->taps.v_taps = in_taps->v_taps; + if (in_taps->v_taps_c == 0) + scl_data->taps.v_taps_c = 2; + else + scl_data->taps.v_taps_c = in_taps->v_taps_c; + if (in_taps->h_taps_c == 0) + scl_data->taps.h_taps_c = 2; + /* Only 1 and even h_taps_c are supported by hw */ + else if ((in_taps->h_taps_c % 2) != 0 && in_taps->h_taps_c != 1) + scl_data->taps.h_taps_c = in_taps->h_taps_c - 1; + else + scl_data->taps.h_taps_c = in_taps->h_taps_c; + + if (!dpp->ctx->dc->debug.always_scale) { + if (IDENTITY_RATIO(scl_data->ratios.horz)) + scl_data->taps.h_taps = 1; + if (IDENTITY_RATIO(scl_data->ratios.vert)) + scl_data->taps.v_taps = 1; + /* + * Spreadsheet doesn't handle taps_c is one properly, + * need to force Chroma to always be scaled to pass + * bandwidth validation. + */ + } + + return true; +} + +void dpp_reset(struct dpp *dpp_base) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + dpp->filter_h_c = NULL; + dpp->filter_v_c = NULL; + dpp->filter_h = NULL; + dpp->filter_v = NULL; + + /* set boundary mode to 0 */ + REG_SET(DSCL_CONTROL, 0, SCL_BOUNDARY_MODE, 0); +} + + + +static void dpp1_cm_set_regamma_pwl( + struct dpp *dpp_base, const struct pwl_params *params) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + dpp1_cm_power_on_regamma_lut(dpp_base, true); + dpp1_cm_configure_regamma_lut(dpp_base, dpp->is_write_to_ram_a_safe); + + if (dpp->is_write_to_ram_a_safe) + dpp1_cm_program_regamma_luta_settings(dpp_base, params); + else + dpp1_cm_program_regamma_lutb_settings(dpp_base, params); + + dpp1_cm_program_regamma_lut( + dpp_base, params->rgb_resulted, params->hw_points_num); +} + +static void dpp1_cm_set_regamma_mode( + struct dpp *dpp_base, + enum opp_regamma mode) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + uint32_t re_mode = 0; + uint32_t obuf_bypass = 0; /* need for pipe split */ + uint32_t obuf_hupscale = 0; + + switch (mode) { + case OPP_REGAMMA_BYPASS: + re_mode = 0; + break; + case OPP_REGAMMA_SRGB: + re_mode = 1; + break; + case OPP_REGAMMA_3_6: + re_mode = 2; + break; + case OPP_REGAMMA_USER: + re_mode = dpp->is_write_to_ram_a_safe ? 3 : 4; + dpp->is_write_to_ram_a_safe = !dpp->is_write_to_ram_a_safe; + break; + default: + break; + } + + REG_SET(CM_RGAM_CONTROL, 0, CM_RGAM_LUT_MODE, re_mode); + REG_UPDATE_2(OBUF_CONTROL, + OBUF_BYPASS, obuf_bypass, + OBUF_H_2X_UPSCALE_EN, obuf_hupscale); +} + +static void dpp1_setup_format_flags(enum surface_pixel_format input_format,\ + enum pixel_format_description *fmt) +{ + + if (input_format == SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F || + input_format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) + *fmt = PIXEL_FORMAT_FLOAT; + else if (input_format == SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616) + *fmt = PIXEL_FORMAT_FIXED16; + else + *fmt = PIXEL_FORMAT_FIXED; +} + +static void dpp1_set_degamma_format_float( + struct dpp *dpp_base, + bool is_float) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + if (is_float) { + REG_UPDATE(CM_IGAM_CONTROL, CM_IGAM_INPUT_FORMAT, 3); + REG_UPDATE(CM_IGAM_CONTROL, CM_IGAM_LUT_MODE, 1); + } else { + REG_UPDATE(CM_IGAM_CONTROL, CM_IGAM_INPUT_FORMAT, 2); + REG_UPDATE(CM_IGAM_CONTROL, CM_IGAM_LUT_MODE, 0); + } +} + +void dpp1_cnv_setup ( + struct dpp *dpp_base, + enum surface_pixel_format input_format, + enum expansion_mode mode) +{ + uint32_t pixel_format; + uint32_t alpha_en; + enum pixel_format_description fmt ; + enum dc_color_space color_space; + enum dcn10_input_csc_select select; + bool is_float; + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + bool force_disable_cursor = false; + + dpp1_setup_format_flags(input_format, &fmt); + alpha_en = 1; + pixel_format = 0; + color_space = COLOR_SPACE_SRGB; + select = INPUT_CSC_SELECT_BYPASS; + is_float = false; + + switch (fmt) { + case PIXEL_FORMAT_FIXED: + case PIXEL_FORMAT_FIXED16: + /*when output is float then FORMAT_CONTROL__OUTPUT_FP=1*/ + REG_SET_3(FORMAT_CONTROL, 0, + CNVC_BYPASS, 0, + FORMAT_EXPANSION_MODE, mode, + OUTPUT_FP, 0); + break; + case PIXEL_FORMAT_FLOAT: + REG_SET_3(FORMAT_CONTROL, 0, + CNVC_BYPASS, 0, + FORMAT_EXPANSION_MODE, mode, + OUTPUT_FP, 1); + is_float = true; + break; + default: + + break; + } + + dpp1_set_degamma_format_float(dpp_base, is_float); + + switch (input_format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + pixel_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + pixel_format = 3; + alpha_en = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + pixel_format = 8; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + pixel_format = 10; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + force_disable_cursor = false; + pixel_format = 65; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + force_disable_cursor = true; + pixel_format = 64; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + force_disable_cursor = true; + pixel_format = 67; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + force_disable_cursor = true; + pixel_format = 66; + color_space = COLOR_SPACE_YCBCR709; + select = INPUT_CSC_SELECT_ICSC; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + pixel_format = 22; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + pixel_format = 24; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + pixel_format = 25; + break; + default: + break; + } + REG_SET(CNVC_SURFACE_PIXEL_FORMAT, 0, + CNVC_SURFACE_PIXEL_FORMAT, pixel_format); + REG_UPDATE(FORMAT_CONTROL, FORMAT_CONTROL__ALPHA_EN, alpha_en); + + dpp1_program_input_csc(dpp_base, color_space, select); + + if (force_disable_cursor) { + REG_UPDATE(CURSOR_CONTROL, + CURSOR_ENABLE, 0); + REG_UPDATE(CURSOR0_CONTROL, + CUR0_ENABLE, 0); + } +} + +void dpp1_set_cursor_attributes( + struct dpp *dpp_base, + const struct dc_cursor_attributes *attr) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + enum dc_cursor_color_format color_format = attr->color_format; + + REG_UPDATE_2(CURSOR0_CONTROL, + CUR0_MODE, color_format, + CUR0_EXPANSION_MODE, 0); + + if (color_format == CURSOR_MODE_MONO) { + /* todo: clarify what to program these to */ + REG_UPDATE(CURSOR0_COLOR0, + CUR0_COLOR0, 0x00000000); + REG_UPDATE(CURSOR0_COLOR1, + CUR0_COLOR1, 0xFFFFFFFF); + } + + /* TODO: Fixed vs float */ + + REG_UPDATE_3(FORMAT_CONTROL, + CNVC_BYPASS, 0, + FORMAT_CONTROL__ALPHA_EN, 1, + FORMAT_EXPANSION_MODE, 0); +} + + +void dpp1_set_cursor_position( + struct dpp *dpp_base, + const struct dc_cursor_position *pos, + const struct dc_cursor_mi_param *param, + uint32_t width) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + int src_x_offset = pos->x - pos->x_hotspot - param->viewport_x_start; + uint32_t cur_en = pos->enable ? 1 : 0; + + if (src_x_offset >= (int)param->viewport_width) + cur_en = 0; /* not visible beyond right edge*/ + + if (src_x_offset + (int)width < 0) + cur_en = 0; /* not visible beyond left edge*/ + + REG_UPDATE(CURSOR0_CONTROL, + CUR0_ENABLE, cur_en); + +} + +static const struct dpp_funcs dcn10_dpp_funcs = { + .dpp_reset = dpp_reset, + .dpp_set_scaler = dpp1_dscl_set_scaler_manual_scale, + .dpp_get_optimal_number_of_taps = dpp_get_optimal_number_of_taps, + .dpp_set_gamut_remap = dpp1_cm_set_gamut_remap, + .opp_set_csc_adjustment = dpp1_cm_set_output_csc_adjustment, + .opp_set_csc_default = dpp1_cm_set_output_csc_default, + .opp_power_on_regamma_lut = dpp1_cm_power_on_regamma_lut, + .opp_program_regamma_lut = dpp1_cm_program_regamma_lut, + .opp_configure_regamma_lut = dpp1_cm_configure_regamma_lut, + .opp_program_regamma_lutb_settings = dpp1_cm_program_regamma_lutb_settings, + .opp_program_regamma_luta_settings = dpp1_cm_program_regamma_luta_settings, + .opp_program_regamma_pwl = dpp1_cm_set_regamma_pwl, + .opp_set_regamma_mode = dpp1_cm_set_regamma_mode, + .ipp_set_degamma = dpp1_set_degamma, + .ipp_program_input_lut = dpp1_program_input_lut, + .ipp_program_degamma_pwl = dpp1_set_degamma_pwl, + .ipp_setup = dpp1_cnv_setup, + .ipp_full_bypass = dpp1_full_bypass, + .set_cursor_attributes = dpp1_set_cursor_attributes, + .set_cursor_position = dpp1_set_cursor_position, +}; + +static struct dpp_caps dcn10_dpp_cap = { + .dscl_data_proc_format = DSCL_DATA_PRCESSING_FIXED_FORMAT, + .dscl_calc_lb_num_partitions = dpp1_dscl_calc_lb_num_partitions, +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void dpp1_construct( + struct dcn10_dpp *dpp, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_dpp_registers *tf_regs, + const struct dcn_dpp_shift *tf_shift, + const struct dcn_dpp_mask *tf_mask) +{ + dpp->base.ctx = ctx; + + dpp->base.inst = inst; + dpp->base.funcs = &dcn10_dpp_funcs; + dpp->base.caps = &dcn10_dpp_cap; + + dpp->tf_regs = tf_regs; + dpp->tf_shift = tf_shift; + dpp->tf_mask = tf_mask; + + dpp->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP; + + dpp->lb_bits_per_entry = LB_BITS_PER_ENTRY; + dpp->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x1404*/ +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h new file mode 100644 index 000000000000..a9782b1aba47 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h @@ -0,0 +1,1386 @@ +/* Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_DPP_DCN10_H__ +#define __DAL_DPP_DCN10_H__ + +#include "dpp.h" + +#define TO_DCN10_DPP(dpp)\ + container_of(dpp, struct dcn10_dpp, base) + +/* TODO: Use correct number of taps. Using polaris values for now */ +#define LB_TOTAL_NUMBER_OF_ENTRIES 5124 +#define LB_BITS_PER_ENTRY 144 + +#define TF_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +//Used to resolve corner case +#define TF2_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## _ ## field_name ## post_fix + +#define TF_REG_LIST_DCN(id) \ + SRI(CM_GAMUT_REMAP_CONTROL, CM, id),\ + SRI(CM_GAMUT_REMAP_C11_C12, CM, id),\ + SRI(CM_GAMUT_REMAP_C33_C34, CM, id),\ + SRI(DSCL_EXT_OVERSCAN_LEFT_RIGHT, DSCL, id), \ + SRI(DSCL_EXT_OVERSCAN_TOP_BOTTOM, DSCL, id), \ + SRI(OTG_H_BLANK, DSCL, id), \ + SRI(OTG_V_BLANK, DSCL, id), \ + SRI(SCL_MODE, DSCL, id), \ + SRI(LB_DATA_FORMAT, DSCL, id), \ + SRI(LB_MEMORY_CTRL, DSCL, id), \ + SRI(DSCL_AUTOCAL, DSCL, id), \ + SRI(SCL_BLACK_OFFSET, DSCL, id), \ + SRI(DSCL_CONTROL, DSCL, id), \ + SRI(SCL_TAP_CONTROL, DSCL, id), \ + SRI(SCL_COEF_RAM_TAP_SELECT, DSCL, id), \ + SRI(SCL_COEF_RAM_TAP_DATA, DSCL, id), \ + SRI(DSCL_2TAP_CONTROL, DSCL, id), \ + SRI(MPC_SIZE, DSCL, id), \ + SRI(SCL_HORZ_FILTER_SCALE_RATIO, DSCL, id), \ + SRI(SCL_VERT_FILTER_SCALE_RATIO, DSCL, id), \ + SRI(SCL_HORZ_FILTER_SCALE_RATIO_C, DSCL, id), \ + SRI(SCL_VERT_FILTER_SCALE_RATIO_C, DSCL, id), \ + SRI(SCL_HORZ_FILTER_INIT, DSCL, id), \ + SRI(SCL_HORZ_FILTER_INIT_C, DSCL, id), \ + SRI(SCL_VERT_FILTER_INIT, DSCL, id), \ + SRI(SCL_VERT_FILTER_INIT_BOT, DSCL, id), \ + SRI(SCL_VERT_FILTER_INIT_C, DSCL, id), \ + SRI(SCL_VERT_FILTER_INIT_BOT_C, DSCL, id), \ + SRI(RECOUT_START, DSCL, id), \ + SRI(RECOUT_SIZE, DSCL, id), \ + SRI(OBUF_CONTROL, DSCL, id), \ + SRI(CM_ICSC_CONTROL, CM, id), \ + SRI(CM_ICSC_C11_C12, CM, id), \ + SRI(CM_ICSC_C33_C34, CM, id), \ + SRI(CM_DGAM_RAMB_START_CNTL_B, CM, id), \ + SRI(CM_DGAM_RAMB_START_CNTL_G, CM, id), \ + SRI(CM_DGAM_RAMB_START_CNTL_R, CM, id), \ + SRI(CM_DGAM_RAMB_SLOPE_CNTL_B, CM, id), \ + SRI(CM_DGAM_RAMB_SLOPE_CNTL_G, CM, id), \ + SRI(CM_DGAM_RAMB_SLOPE_CNTL_R, CM, id), \ + SRI(CM_DGAM_RAMB_END_CNTL1_B, CM, id), \ + SRI(CM_DGAM_RAMB_END_CNTL2_B, CM, id), \ + SRI(CM_DGAM_RAMB_END_CNTL1_G, CM, id), \ + SRI(CM_DGAM_RAMB_END_CNTL2_G, CM, id), \ + SRI(CM_DGAM_RAMB_END_CNTL1_R, CM, id), \ + SRI(CM_DGAM_RAMB_END_CNTL2_R, CM, id), \ + SRI(CM_DGAM_RAMB_REGION_0_1, CM, id), \ + SRI(CM_DGAM_RAMB_REGION_14_15, CM, id), \ + SRI(CM_DGAM_RAMA_START_CNTL_B, CM, id), \ + SRI(CM_DGAM_RAMA_START_CNTL_G, CM, id), \ + SRI(CM_DGAM_RAMA_START_CNTL_R, CM, id), \ + SRI(CM_DGAM_RAMA_SLOPE_CNTL_B, CM, id), \ + SRI(CM_DGAM_RAMA_SLOPE_CNTL_G, CM, id), \ + SRI(CM_DGAM_RAMA_SLOPE_CNTL_R, CM, id), \ + SRI(CM_DGAM_RAMA_END_CNTL1_B, CM, id), \ + SRI(CM_DGAM_RAMA_END_CNTL2_B, CM, id), \ + SRI(CM_DGAM_RAMA_END_CNTL1_G, CM, id), \ + SRI(CM_DGAM_RAMA_END_CNTL2_G, CM, id), \ + SRI(CM_DGAM_RAMA_END_CNTL1_R, CM, id), \ + SRI(CM_DGAM_RAMA_END_CNTL2_R, CM, id), \ + SRI(CM_DGAM_RAMA_REGION_0_1, CM, id), \ + SRI(CM_DGAM_RAMA_REGION_14_15, CM, id), \ + SRI(CM_MEM_PWR_CTRL, CM, id), \ + SRI(CM_DGAM_LUT_WRITE_EN_MASK, CM, id), \ + SRI(CM_DGAM_LUT_INDEX, CM, id), \ + SRI(CM_DGAM_LUT_DATA, CM, id), \ + SRI(CM_CONTROL, CM, id), \ + SRI(CM_DGAM_CONTROL, CM, id), \ + SRI(FORMAT_CONTROL, CNVC_CFG, id), \ + SRI(CNVC_SURFACE_PIXEL_FORMAT, CNVC_CFG, id), \ + SRI(CURSOR0_CONTROL, CNVC_CUR, id), \ + SRI(CURSOR0_COLOR0, CNVC_CUR, id), \ + SRI(CURSOR0_COLOR1, CNVC_CUR, id) + + + +#define TF_REG_LIST_DCN10(id) \ + TF_REG_LIST_DCN(id), \ + SRI(CM_COMA_C11_C12, CM, id),\ + SRI(CM_COMA_C33_C34, CM, id),\ + SRI(CM_COMB_C11_C12, CM, id),\ + SRI(CM_COMB_C33_C34, CM, id),\ + SRI(CM_OCSC_CONTROL, CM, id), \ + SRI(CM_OCSC_C11_C12, CM, id), \ + SRI(CM_OCSC_C33_C34, CM, id), \ + SRI(CM_MEM_PWR_CTRL, CM, id), \ + SRI(CM_RGAM_LUT_DATA, CM, id), \ + SRI(CM_RGAM_LUT_WRITE_EN_MASK, CM, id),\ + SRI(CM_RGAM_LUT_INDEX, CM, id), \ + SRI(CM_RGAM_RAMB_START_CNTL_B, CM, id), \ + SRI(CM_RGAM_RAMB_START_CNTL_G, CM, id), \ + SRI(CM_RGAM_RAMB_START_CNTL_R, CM, id), \ + SRI(CM_RGAM_RAMB_SLOPE_CNTL_B, CM, id), \ + SRI(CM_RGAM_RAMB_SLOPE_CNTL_G, CM, id), \ + SRI(CM_RGAM_RAMB_SLOPE_CNTL_R, CM, id), \ + SRI(CM_RGAM_RAMB_END_CNTL1_B, CM, id), \ + SRI(CM_RGAM_RAMB_END_CNTL2_B, CM, id), \ + SRI(CM_RGAM_RAMB_END_CNTL1_G, CM, id), \ + SRI(CM_RGAM_RAMB_END_CNTL2_G, CM, id), \ + SRI(CM_RGAM_RAMB_END_CNTL1_R, CM, id), \ + SRI(CM_RGAM_RAMB_END_CNTL2_R, CM, id), \ + SRI(CM_RGAM_RAMB_REGION_0_1, CM, id), \ + SRI(CM_RGAM_RAMB_REGION_32_33, CM, id), \ + SRI(CM_RGAM_RAMA_START_CNTL_B, CM, id), \ + SRI(CM_RGAM_RAMA_START_CNTL_G, CM, id), \ + SRI(CM_RGAM_RAMA_START_CNTL_R, CM, id), \ + SRI(CM_RGAM_RAMA_SLOPE_CNTL_B, CM, id), \ + SRI(CM_RGAM_RAMA_SLOPE_CNTL_G, CM, id), \ + SRI(CM_RGAM_RAMA_SLOPE_CNTL_R, CM, id), \ + SRI(CM_RGAM_RAMA_END_CNTL1_B, CM, id), \ + SRI(CM_RGAM_RAMA_END_CNTL2_B, CM, id), \ + SRI(CM_RGAM_RAMA_END_CNTL1_G, CM, id), \ + SRI(CM_RGAM_RAMA_END_CNTL2_G, CM, id), \ + SRI(CM_RGAM_RAMA_END_CNTL1_R, CM, id), \ + SRI(CM_RGAM_RAMA_END_CNTL2_R, CM, id), \ + SRI(CM_RGAM_RAMA_REGION_0_1, CM, id), \ + SRI(CM_RGAM_RAMA_REGION_32_33, CM, id), \ + SRI(CM_RGAM_CONTROL, CM, id), \ + SRI(CM_IGAM_CONTROL, CM, id), \ + SRI(CM_IGAM_LUT_RW_CONTROL, CM, id), \ + SRI(CM_IGAM_LUT_RW_INDEX, CM, id), \ + SRI(CM_IGAM_LUT_SEQ_COLOR, CM, id), \ + SRI(CURSOR_CONTROL, CURSOR, id), \ + SRI(CM_CMOUT_CONTROL, CM, id) + + +#define TF_REG_LIST_SH_MASK_DCN(mask_sh)\ + TF_SF(CM0_CM_GAMUT_REMAP_CONTROL, CM_GAMUT_REMAP_MODE, mask_sh),\ + TF_SF(CM0_CM_GAMUT_REMAP_C11_C12, CM_GAMUT_REMAP_C11, mask_sh),\ + TF_SF(CM0_CM_GAMUT_REMAP_C11_C12, CM_GAMUT_REMAP_C12, mask_sh),\ + TF_SF(CM0_CM_GAMUT_REMAP_C33_C34, CM_GAMUT_REMAP_C33, mask_sh),\ + TF_SF(CM0_CM_GAMUT_REMAP_C33_C34, CM_GAMUT_REMAP_C34, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM, mask_sh),\ + TF_SF(DSCL0_DSCL_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP, mask_sh),\ + TF_SF(DSCL0_OTG_H_BLANK, OTG_H_BLANK_START, mask_sh),\ + TF_SF(DSCL0_OTG_H_BLANK, OTG_H_BLANK_END, mask_sh),\ + TF_SF(DSCL0_OTG_V_BLANK, OTG_V_BLANK_START, mask_sh),\ + TF_SF(DSCL0_OTG_V_BLANK, OTG_V_BLANK_END, mask_sh),\ + TF_SF(DSCL0_LB_DATA_FORMAT, INTERLEAVE_EN, mask_sh),\ + TF2_SF(DSCL0, LB_DATA_FORMAT__ALPHA_EN, mask_sh),\ + TF_SF(DSCL0_LB_MEMORY_CTRL, MEMORY_CONFIG, mask_sh),\ + TF_SF(DSCL0_LB_MEMORY_CTRL, LB_MAX_PARTITIONS, mask_sh),\ + TF_SF(DSCL0_DSCL_AUTOCAL, AUTOCAL_MODE, mask_sh),\ + TF_SF(DSCL0_DSCL_AUTOCAL, AUTOCAL_NUM_PIPE, mask_sh),\ + TF_SF(DSCL0_DSCL_AUTOCAL, AUTOCAL_PIPE_ID, mask_sh),\ + TF_SF(DSCL0_SCL_BLACK_OFFSET, SCL_BLACK_OFFSET_RGB_Y, mask_sh),\ + TF_SF(DSCL0_SCL_BLACK_OFFSET, SCL_BLACK_OFFSET_CBCR, mask_sh),\ + TF_SF(DSCL0_DSCL_CONTROL, SCL_BOUNDARY_MODE, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_V_NUM_TAPS, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_H_NUM_TAPS, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_V_NUM_TAPS_C, mask_sh),\ + TF_SF(DSCL0_SCL_TAP_CONTROL, SCL_H_NUM_TAPS_C, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_SELECT, SCL_COEF_RAM_TAP_PAIR_IDX, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_SELECT, SCL_COEF_RAM_PHASE, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_SELECT, SCL_COEF_RAM_FILTER_TYPE, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_EVEN_TAP_COEF, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_EVEN_TAP_COEF_EN, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_ODD_TAP_COEF, mask_sh),\ + TF_SF(DSCL0_SCL_COEF_RAM_TAP_DATA, SCL_COEF_RAM_ODD_TAP_COEF_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_H_2TAP_HARDCODE_COEF_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_H_2TAP_SHARP_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_H_2TAP_SHARP_FACTOR, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_V_2TAP_HARDCODE_COEF_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_V_2TAP_SHARP_EN, mask_sh),\ + TF_SF(DSCL0_DSCL_2TAP_CONTROL, SCL_V_2TAP_SHARP_FACTOR, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, SCL_COEF_RAM_SELECT, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, DSCL_MODE, mask_sh),\ + TF_SF(DSCL0_RECOUT_START, RECOUT_START_X, mask_sh),\ + TF_SF(DSCL0_RECOUT_START, RECOUT_START_Y, mask_sh),\ + TF_SF(DSCL0_RECOUT_SIZE, RECOUT_WIDTH, mask_sh),\ + TF_SF(DSCL0_RECOUT_SIZE, RECOUT_HEIGHT, mask_sh),\ + TF_SF(DSCL0_MPC_SIZE, MPC_WIDTH, mask_sh),\ + TF_SF(DSCL0_MPC_SIZE, MPC_HEIGHT, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_SCALE_RATIO, SCL_H_SCALE_RATIO, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_SCALE_RATIO, SCL_V_SCALE_RATIO, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_SCALE_RATIO_C, SCL_H_SCALE_RATIO_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_SCALE_RATIO_C, SCL_V_SCALE_RATIO_C, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_FRAC, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_INT, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT_C, SCL_H_INIT_FRAC_C, mask_sh),\ + TF_SF(DSCL0_SCL_HORZ_FILTER_INIT_C, SCL_H_INIT_INT_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_FRAC, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_INT, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_BOT, SCL_V_INIT_FRAC_BOT, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_BOT, SCL_V_INIT_INT_BOT, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_C, SCL_V_INIT_FRAC_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_C, SCL_V_INIT_INT_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_BOT_C, SCL_V_INIT_FRAC_BOT_C, mask_sh),\ + TF_SF(DSCL0_SCL_VERT_FILTER_INIT_BOT_C, SCL_V_INIT_INT_BOT_C, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, SCL_CHROMA_COEF_MODE, mask_sh),\ + TF_SF(DSCL0_SCL_MODE, SCL_COEF_RAM_SELECT_CURRENT, mask_sh), \ + TF_SF(DSCL0_OBUF_CONTROL, OBUF_BYPASS, mask_sh), \ + TF_SF(CM0_CM_ICSC_CONTROL, CM_ICSC_MODE, mask_sh), \ + TF_SF(CM0_CM_ICSC_C11_C12, CM_ICSC_C11, mask_sh), \ + TF_SF(CM0_CM_ICSC_C11_C12, CM_ICSC_C12, mask_sh), \ + TF_SF(CM0_CM_ICSC_C33_C34, CM_ICSC_C33, mask_sh), \ + TF_SF(CM0_CM_ICSC_C33_C34, CM_ICSC_C34, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_START_CNTL_B, CM_DGAM_RAMB_EXP_REGION_START_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_START_CNTL_B, CM_DGAM_RAMB_EXP_REGION_START_SEGMENT_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_START_CNTL_G, CM_DGAM_RAMB_EXP_REGION_START_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_START_CNTL_G, CM_DGAM_RAMB_EXP_REGION_START_SEGMENT_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_START_CNTL_R, CM_DGAM_RAMB_EXP_REGION_START_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_START_CNTL_R, CM_DGAM_RAMB_EXP_REGION_START_SEGMENT_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_SLOPE_CNTL_B, CM_DGAM_RAMB_EXP_REGION_LINEAR_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_SLOPE_CNTL_G, CM_DGAM_RAMB_EXP_REGION_LINEAR_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_SLOPE_CNTL_R, CM_DGAM_RAMB_EXP_REGION_LINEAR_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL1_B, CM_DGAM_RAMB_EXP_REGION_END_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL2_B, CM_DGAM_RAMB_EXP_REGION_END_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL2_B, CM_DGAM_RAMB_EXP_REGION_END_BASE_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL1_G, CM_DGAM_RAMB_EXP_REGION_END_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL2_G, CM_DGAM_RAMB_EXP_REGION_END_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL2_G, CM_DGAM_RAMB_EXP_REGION_END_BASE_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL1_R, CM_DGAM_RAMB_EXP_REGION_END_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL2_R, CM_DGAM_RAMB_EXP_REGION_END_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_END_CNTL2_R, CM_DGAM_RAMB_EXP_REGION_END_BASE_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_0_1, CM_DGAM_RAMB_EXP_REGION0_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_0_1, CM_DGAM_RAMB_EXP_REGION0_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_0_1, CM_DGAM_RAMB_EXP_REGION1_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_0_1, CM_DGAM_RAMB_EXP_REGION1_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_14_15, CM_DGAM_RAMB_EXP_REGION14_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_14_15, CM_DGAM_RAMB_EXP_REGION14_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_14_15, CM_DGAM_RAMB_EXP_REGION15_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMB_REGION_14_15, CM_DGAM_RAMB_EXP_REGION15_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_START_CNTL_B, CM_DGAM_RAMA_EXP_REGION_START_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_START_CNTL_B, CM_DGAM_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_START_CNTL_G, CM_DGAM_RAMA_EXP_REGION_START_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_START_CNTL_G, CM_DGAM_RAMA_EXP_REGION_START_SEGMENT_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_START_CNTL_R, CM_DGAM_RAMA_EXP_REGION_START_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_START_CNTL_R, CM_DGAM_RAMA_EXP_REGION_START_SEGMENT_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_SLOPE_CNTL_B, CM_DGAM_RAMA_EXP_REGION_LINEAR_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_SLOPE_CNTL_G, CM_DGAM_RAMA_EXP_REGION_LINEAR_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_SLOPE_CNTL_R, CM_DGAM_RAMA_EXP_REGION_LINEAR_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL1_B, CM_DGAM_RAMA_EXP_REGION_END_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL2_B, CM_DGAM_RAMA_EXP_REGION_END_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL2_B, CM_DGAM_RAMA_EXP_REGION_END_BASE_B, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL1_G, CM_DGAM_RAMA_EXP_REGION_END_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL2_G, CM_DGAM_RAMA_EXP_REGION_END_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL2_G, CM_DGAM_RAMA_EXP_REGION_END_BASE_G, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL1_R, CM_DGAM_RAMA_EXP_REGION_END_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL2_R, CM_DGAM_RAMA_EXP_REGION_END_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_END_CNTL2_R, CM_DGAM_RAMA_EXP_REGION_END_BASE_R, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_0_1, CM_DGAM_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_0_1, CM_DGAM_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_0_1, CM_DGAM_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_0_1, CM_DGAM_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_14_15, CM_DGAM_RAMA_EXP_REGION14_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_14_15, CM_DGAM_RAMA_EXP_REGION14_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_14_15, CM_DGAM_RAMA_EXP_REGION15_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_DGAM_RAMA_REGION_14_15, CM_DGAM_RAMA_EXP_REGION15_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_MEM_PWR_CTRL, SHARED_MEM_PWR_DIS, mask_sh), \ + TF_SF(CM0_CM_DGAM_LUT_WRITE_EN_MASK, CM_DGAM_LUT_WRITE_EN_MASK, mask_sh), \ + TF_SF(CM0_CM_DGAM_LUT_WRITE_EN_MASK, CM_DGAM_LUT_WRITE_SEL, mask_sh), \ + TF_SF(CM0_CM_DGAM_LUT_INDEX, CM_DGAM_LUT_INDEX, mask_sh), \ + TF_SF(CM0_CM_DGAM_LUT_DATA, CM_DGAM_LUT_DATA, mask_sh), \ + TF_SF(CM0_CM_DGAM_CONTROL, CM_DGAM_LUT_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, CNVC_BYPASS, mask_sh), \ + TF2_SF(CNVC_CFG0, FORMAT_CONTROL__ALPHA_EN, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, FORMAT_EXPANSION_MODE, mask_sh), \ + TF_SF(CNVC_CFG0_CNVC_SURFACE_PIXEL_FORMAT, CNVC_SURFACE_PIXEL_FORMAT, mask_sh), \ + TF_SF(CNVC_CUR0_CURSOR0_CONTROL, CUR0_MODE, mask_sh), \ + TF_SF(CNVC_CUR0_CURSOR0_CONTROL, CUR0_EXPANSION_MODE, mask_sh), \ + TF_SF(CNVC_CUR0_CURSOR0_CONTROL, CUR0_ENABLE, mask_sh), \ + TF_SF(CNVC_CUR0_CURSOR0_COLOR0, CUR0_COLOR0, mask_sh), \ + TF_SF(CNVC_CUR0_CURSOR0_COLOR1, CUR0_COLOR1, mask_sh) + +#define TF_REG_LIST_SH_MASK_DCN10(mask_sh)\ + TF_REG_LIST_SH_MASK_DCN(mask_sh),\ + TF_SF(DSCL0_LB_DATA_FORMAT, PIXEL_DEPTH, mask_sh),\ + TF_SF(DSCL0_LB_DATA_FORMAT, PIXEL_EXPAN_MODE, mask_sh),\ + TF_SF(DSCL0_LB_DATA_FORMAT, PIXEL_REDUCE_MODE, mask_sh),\ + TF_SF(DSCL0_LB_DATA_FORMAT, DYNAMIC_PIXEL_DEPTH, mask_sh),\ + TF_SF(DSCL0_LB_DATA_FORMAT, DITHER_EN, mask_sh),\ + TF_SF(CM0_CM_COMA_C11_C12, CM_COMA_C11, mask_sh),\ + TF_SF(CM0_CM_COMA_C11_C12, CM_COMA_C12, mask_sh),\ + TF_SF(CM0_CM_COMA_C33_C34, CM_COMA_C33, mask_sh),\ + TF_SF(CM0_CM_COMA_C33_C34, CM_COMA_C34, mask_sh),\ + TF_SF(CM0_CM_COMB_C11_C12, CM_COMB_C11, mask_sh),\ + TF_SF(CM0_CM_COMB_C11_C12, CM_COMB_C12, mask_sh),\ + TF_SF(CM0_CM_COMB_C33_C34, CM_COMB_C33, mask_sh),\ + TF_SF(CM0_CM_COMB_C33_C34, CM_COMB_C34, mask_sh),\ + TF_SF(CM0_CM_OCSC_CONTROL, CM_OCSC_MODE, mask_sh), \ + TF_SF(CM0_CM_OCSC_C11_C12, CM_OCSC_C11, mask_sh), \ + TF_SF(CM0_CM_OCSC_C11_C12, CM_OCSC_C12, mask_sh), \ + TF_SF(CM0_CM_OCSC_C33_C34, CM_OCSC_C33, mask_sh), \ + TF_SF(CM0_CM_OCSC_C33_C34, CM_OCSC_C34, mask_sh), \ + TF_SF(CM0_CM_MEM_PWR_CTRL, RGAM_MEM_PWR_FORCE, mask_sh), \ + TF_SF(CM0_CM_RGAM_LUT_DATA, CM_RGAM_LUT_DATA, mask_sh), \ + TF_SF(CM0_CM_RGAM_LUT_WRITE_EN_MASK, CM_RGAM_LUT_WRITE_EN_MASK, mask_sh), \ + TF_SF(CM0_CM_RGAM_LUT_WRITE_EN_MASK, CM_RGAM_LUT_WRITE_SEL, mask_sh), \ + TF_SF(CM0_CM_RGAM_LUT_INDEX, CM_RGAM_LUT_INDEX, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_START_CNTL_B, CM_RGAM_RAMB_EXP_REGION_START_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_START_CNTL_B, CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_START_CNTL_G, CM_RGAM_RAMB_EXP_REGION_START_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_START_CNTL_G, CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_START_CNTL_R, CM_RGAM_RAMB_EXP_REGION_START_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_START_CNTL_R, CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_SLOPE_CNTL_B, CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_SLOPE_CNTL_G, CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_SLOPE_CNTL_R, CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL1_B, CM_RGAM_RAMB_EXP_REGION_END_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL2_B, CM_RGAM_RAMB_EXP_REGION_END_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL2_B, CM_RGAM_RAMB_EXP_REGION_END_BASE_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL1_G, CM_RGAM_RAMB_EXP_REGION_END_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL2_G, CM_RGAM_RAMB_EXP_REGION_END_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL2_G, CM_RGAM_RAMB_EXP_REGION_END_BASE_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL1_R, CM_RGAM_RAMB_EXP_REGION_END_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL2_R, CM_RGAM_RAMB_EXP_REGION_END_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_END_CNTL2_R, CM_RGAM_RAMB_EXP_REGION_END_BASE_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_0_1, CM_RGAM_RAMB_EXP_REGION0_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_0_1, CM_RGAM_RAMB_EXP_REGION0_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_0_1, CM_RGAM_RAMB_EXP_REGION1_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_0_1, CM_RGAM_RAMB_EXP_REGION1_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_32_33, CM_RGAM_RAMB_EXP_REGION32_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_32_33, CM_RGAM_RAMB_EXP_REGION32_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_32_33, CM_RGAM_RAMB_EXP_REGION33_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMB_REGION_32_33, CM_RGAM_RAMB_EXP_REGION33_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_START_CNTL_B, CM_RGAM_RAMA_EXP_REGION_START_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_START_CNTL_B, CM_RGAM_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_START_CNTL_G, CM_RGAM_RAMA_EXP_REGION_START_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_START_CNTL_G, CM_RGAM_RAMA_EXP_REGION_START_SEGMENT_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_START_CNTL_R, CM_RGAM_RAMA_EXP_REGION_START_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_START_CNTL_R, CM_RGAM_RAMA_EXP_REGION_START_SEGMENT_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_SLOPE_CNTL_B, CM_RGAM_RAMA_EXP_REGION_LINEAR_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_SLOPE_CNTL_G, CM_RGAM_RAMA_EXP_REGION_LINEAR_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_SLOPE_CNTL_R, CM_RGAM_RAMA_EXP_REGION_LINEAR_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL1_B, CM_RGAM_RAMA_EXP_REGION_END_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL2_B, CM_RGAM_RAMA_EXP_REGION_END_SLOPE_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL2_B, CM_RGAM_RAMA_EXP_REGION_END_BASE_B, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL1_G, CM_RGAM_RAMA_EXP_REGION_END_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL2_G, CM_RGAM_RAMA_EXP_REGION_END_SLOPE_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL2_G, CM_RGAM_RAMA_EXP_REGION_END_BASE_G, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL1_R, CM_RGAM_RAMA_EXP_REGION_END_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL2_R, CM_RGAM_RAMA_EXP_REGION_END_SLOPE_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_END_CNTL2_R, CM_RGAM_RAMA_EXP_REGION_END_BASE_R, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_0_1, CM_RGAM_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_0_1, CM_RGAM_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_0_1, CM_RGAM_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_0_1, CM_RGAM_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_32_33, CM_RGAM_RAMA_EXP_REGION32_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_32_33, CM_RGAM_RAMA_EXP_REGION32_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_32_33, CM_RGAM_RAMA_EXP_REGION33_LUT_OFFSET, mask_sh), \ + TF_SF(CM0_CM_RGAM_RAMA_REGION_32_33, CM_RGAM_RAMA_EXP_REGION33_NUM_SEGMENTS, mask_sh), \ + TF_SF(CM0_CM_RGAM_CONTROL, CM_RGAM_LUT_MODE, mask_sh), \ + TF_SF(DSCL0_OBUF_CONTROL, OBUF_H_2X_UPSCALE_EN, mask_sh), \ + TF_SF(CM0_CM_IGAM_CONTROL, CM_IGAM_LUT_MODE, mask_sh), \ + TF_SF(CM0_CM_IGAM_CONTROL, CM_IGAM_LUT_FORMAT_R, mask_sh), \ + TF_SF(CM0_CM_IGAM_CONTROL, CM_IGAM_LUT_FORMAT_G, mask_sh), \ + TF_SF(CM0_CM_IGAM_CONTROL, CM_IGAM_LUT_FORMAT_B, mask_sh), \ + TF_SF(CM0_CM_IGAM_CONTROL, CM_IGAM_INPUT_FORMAT, mask_sh), \ + TF_SF(CM0_CM_IGAM_LUT_RW_CONTROL, CM_IGAM_DGAM_CONFIG_STATUS, mask_sh), \ + TF_SF(CM0_CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_HOST_EN, mask_sh), \ + TF_SF(CM0_CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_RW_MODE, mask_sh), \ + TF_SF(CM0_CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_SEL, mask_sh), \ + TF_SF(CM0_CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_WRITE_EN_MASK, mask_sh), \ + TF_SF(CM0_CM_IGAM_LUT_RW_INDEX, CM_IGAM_LUT_RW_INDEX, mask_sh), \ + TF_SF(CM0_CM_CONTROL, CM_BYPASS_EN, mask_sh), \ + TF_SF(CM0_CM_IGAM_LUT_SEQ_COLOR, CM_IGAM_LUT_SEQ_COLOR, mask_sh), \ + TF_SF(CNVC_CFG0_FORMAT_CONTROL, OUTPUT_FP, mask_sh), \ + TF_SF(CM0_CM_CMOUT_CONTROL, CM_CMOUT_ROUND_TRUNC_MODE, mask_sh), \ + TF_SF(CURSOR0_CURSOR_CONTROL, CURSOR_MODE, mask_sh), \ + TF_SF(CURSOR0_CURSOR_CONTROL, CURSOR_PITCH, mask_sh), \ + TF_SF(CURSOR0_CURSOR_CONTROL, CURSOR_LINES_PER_CHUNK, mask_sh), \ + TF_SF(CURSOR0_CURSOR_CONTROL, CURSOR_ENABLE, mask_sh) + +#define TF_REG_FIELD_LIST(type) \ + type EXT_OVERSCAN_LEFT; \ + type EXT_OVERSCAN_RIGHT; \ + type EXT_OVERSCAN_BOTTOM; \ + type EXT_OVERSCAN_TOP; \ + type OTG_H_BLANK_START; \ + type OTG_H_BLANK_END; \ + type OTG_V_BLANK_START; \ + type OTG_V_BLANK_END; \ + type PIXEL_DEPTH; \ + type PIXEL_EXPAN_MODE; \ + type PIXEL_REDUCE_MODE; \ + type DYNAMIC_PIXEL_DEPTH; \ + type DITHER_EN; \ + type INTERLEAVE_EN; \ + type LB_DATA_FORMAT__ALPHA_EN; \ + type MEMORY_CONFIG; \ + type LB_MAX_PARTITIONS; \ + type AUTOCAL_MODE; \ + type AUTOCAL_NUM_PIPE; \ + type AUTOCAL_PIPE_ID; \ + type SCL_BLACK_OFFSET_RGB_Y; \ + type SCL_BLACK_OFFSET_CBCR; \ + type SCL_BOUNDARY_MODE; \ + type SCL_V_NUM_TAPS; \ + type SCL_H_NUM_TAPS; \ + type SCL_V_NUM_TAPS_C; \ + type SCL_H_NUM_TAPS_C; \ + type SCL_COEF_RAM_TAP_PAIR_IDX; \ + type SCL_COEF_RAM_PHASE; \ + type SCL_COEF_RAM_FILTER_TYPE; \ + type SCL_COEF_RAM_EVEN_TAP_COEF; \ + type SCL_COEF_RAM_EVEN_TAP_COEF_EN; \ + type SCL_COEF_RAM_ODD_TAP_COEF; \ + type SCL_COEF_RAM_ODD_TAP_COEF_EN; \ + type SCL_H_2TAP_HARDCODE_COEF_EN; \ + type SCL_H_2TAP_SHARP_EN; \ + type SCL_H_2TAP_SHARP_FACTOR; \ + type SCL_V_2TAP_HARDCODE_COEF_EN; \ + type SCL_V_2TAP_SHARP_EN; \ + type SCL_V_2TAP_SHARP_FACTOR; \ + type SCL_COEF_RAM_SELECT; \ + type DSCL_MODE; \ + type RECOUT_START_X; \ + type RECOUT_START_Y; \ + type RECOUT_WIDTH; \ + type RECOUT_HEIGHT; \ + type MPC_WIDTH; \ + type MPC_HEIGHT; \ + type SCL_H_SCALE_RATIO; \ + type SCL_V_SCALE_RATIO; \ + type SCL_H_SCALE_RATIO_C; \ + type SCL_V_SCALE_RATIO_C; \ + type SCL_H_INIT_FRAC; \ + type SCL_H_INIT_INT; \ + type SCL_H_INIT_FRAC_C; \ + type SCL_H_INIT_INT_C; \ + type SCL_V_INIT_FRAC; \ + type SCL_V_INIT_INT; \ + type SCL_V_INIT_FRAC_BOT; \ + type SCL_V_INIT_INT_BOT; \ + type SCL_V_INIT_FRAC_C; \ + type SCL_V_INIT_INT_C; \ + type SCL_V_INIT_FRAC_BOT_C; \ + type SCL_V_INIT_INT_BOT_C; \ + type SCL_CHROMA_COEF_MODE; \ + type SCL_COEF_RAM_SELECT_CURRENT; \ + type CM_GAMUT_REMAP_MODE; \ + type CM_GAMUT_REMAP_C11; \ + type CM_GAMUT_REMAP_C12; \ + type CM_GAMUT_REMAP_C33; \ + type CM_GAMUT_REMAP_C34; \ + type CM_COMA_C11; \ + type CM_COMA_C12; \ + type CM_COMA_C33; \ + type CM_COMA_C34; \ + type CM_COMB_C11; \ + type CM_COMB_C12; \ + type CM_COMB_C33; \ + type CM_COMB_C34; \ + type CM_OCSC_MODE; \ + type CM_OCSC_C11; \ + type CM_OCSC_C12; \ + type CM_OCSC_C33; \ + type CM_OCSC_C34; \ + type RGAM_MEM_PWR_FORCE; \ + type CM_RGAM_LUT_DATA; \ + type CM_RGAM_LUT_WRITE_EN_MASK; \ + type CM_RGAM_LUT_WRITE_SEL; \ + type CM_RGAM_LUT_INDEX; \ + type CM_RGAM_RAMB_EXP_REGION_START_B; \ + type CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_B; \ + type CM_RGAM_RAMB_EXP_REGION_START_G; \ + type CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_G; \ + type CM_RGAM_RAMB_EXP_REGION_START_R; \ + type CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_R; \ + type CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_B; \ + type CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_G; \ + type CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_R; \ + type CM_RGAM_RAMB_EXP_REGION_END_B; \ + type CM_RGAM_RAMB_EXP_REGION_END_SLOPE_B; \ + type CM_RGAM_RAMB_EXP_REGION_END_BASE_B; \ + type CM_RGAM_RAMB_EXP_REGION_END_G; \ + type CM_RGAM_RAMB_EXP_REGION_END_SLOPE_G; \ + type CM_RGAM_RAMB_EXP_REGION_END_BASE_G; \ + type CM_RGAM_RAMB_EXP_REGION_END_R; \ + type CM_RGAM_RAMB_EXP_REGION_END_SLOPE_R; \ + type CM_RGAM_RAMB_EXP_REGION_END_BASE_R; \ + type CM_RGAM_RAMB_EXP_REGION0_LUT_OFFSET; \ + type CM_RGAM_RAMB_EXP_REGION0_NUM_SEGMENTS; \ + type CM_RGAM_RAMB_EXP_REGION1_LUT_OFFSET; \ + type CM_RGAM_RAMB_EXP_REGION1_NUM_SEGMENTS; \ + type CM_RGAM_RAMB_EXP_REGION32_LUT_OFFSET; \ + type CM_RGAM_RAMB_EXP_REGION32_NUM_SEGMENTS; \ + type CM_RGAM_RAMB_EXP_REGION33_LUT_OFFSET; \ + type CM_RGAM_RAMB_EXP_REGION33_NUM_SEGMENTS; \ + type CM_RGAM_RAMA_EXP_REGION_START_B; \ + type CM_RGAM_RAMA_EXP_REGION_START_SEGMENT_B; \ + type CM_RGAM_RAMA_EXP_REGION_START_G; \ + type CM_RGAM_RAMA_EXP_REGION_START_SEGMENT_G; \ + type CM_RGAM_RAMA_EXP_REGION_START_R; \ + type CM_RGAM_RAMA_EXP_REGION_START_SEGMENT_R; \ + type CM_RGAM_RAMA_EXP_REGION_LINEAR_SLOPE_B; \ + type CM_RGAM_RAMA_EXP_REGION_LINEAR_SLOPE_G; \ + type CM_RGAM_RAMA_EXP_REGION_LINEAR_SLOPE_R; \ + type CM_RGAM_RAMA_EXP_REGION_END_B; \ + type CM_RGAM_RAMA_EXP_REGION_END_SLOPE_B; \ + type CM_RGAM_RAMA_EXP_REGION_END_BASE_B; \ + type CM_RGAM_RAMA_EXP_REGION_END_G; \ + type CM_RGAM_RAMA_EXP_REGION_END_SLOPE_G; \ + type CM_RGAM_RAMA_EXP_REGION_END_BASE_G; \ + type CM_RGAM_RAMA_EXP_REGION_END_R; \ + type CM_RGAM_RAMA_EXP_REGION_END_SLOPE_R; \ + type CM_RGAM_RAMA_EXP_REGION_END_BASE_R; \ + type CM_RGAM_RAMA_EXP_REGION0_LUT_OFFSET; \ + type CM_RGAM_RAMA_EXP_REGION0_NUM_SEGMENTS; \ + type CM_RGAM_RAMA_EXP_REGION1_LUT_OFFSET; \ + type CM_RGAM_RAMA_EXP_REGION1_NUM_SEGMENTS; \ + type CM_RGAM_RAMA_EXP_REGION32_LUT_OFFSET; \ + type CM_RGAM_RAMA_EXP_REGION32_NUM_SEGMENTS; \ + type CM_RGAM_RAMA_EXP_REGION33_LUT_OFFSET; \ + type CM_RGAM_RAMA_EXP_REGION33_NUM_SEGMENTS; \ + type CM_RGAM_LUT_MODE; \ + type CM_CMOUT_ROUND_TRUNC_MODE; \ + type OBUF_BYPASS; \ + type OBUF_H_2X_UPSCALE_EN; \ + type CM_BLNDGAM_LUT_MODE; \ + type CM_BLNDGAM_RAMB_EXP_REGION_START_B; \ + type CM_BLNDGAM_RAMB_EXP_REGION_START_SEGMENT_B; \ + type CM_BLNDGAM_RAMB_EXP_REGION_START_G; \ + type CM_BLNDGAM_RAMB_EXP_REGION_START_SEGMENT_G; \ + type CM_BLNDGAM_RAMB_EXP_REGION_START_R; \ + type CM_BLNDGAM_RAMB_EXP_REGION_START_SEGMENT_R; \ + type CM_BLNDGAM_RAMB_EXP_REGION_LINEAR_SLOPE_B; \ + type CM_BLNDGAM_RAMB_EXP_REGION_LINEAR_SLOPE_G; \ + type CM_BLNDGAM_RAMB_EXP_REGION_LINEAR_SLOPE_R; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_B; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_SLOPE_B; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_BASE_B; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_G; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_SLOPE_G; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_BASE_G; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_R; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_SLOPE_R; \ + type CM_BLNDGAM_RAMB_EXP_REGION_END_BASE_R; \ + type CM_BLNDGAM_RAMB_EXP_REGION0_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION0_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION1_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION1_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION2_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION2_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION3_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION3_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION4_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION4_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION5_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION5_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION6_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION6_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION7_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION7_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION8_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION8_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION9_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION9_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION10_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION10_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION11_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION11_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION12_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION12_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION13_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION13_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION14_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION14_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION15_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION15_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION16_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION16_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION17_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION17_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION18_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION18_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION19_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION19_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION20_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION20_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION21_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION21_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION22_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION22_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION23_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION23_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION24_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION24_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION25_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION25_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION26_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION26_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION27_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION27_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION28_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION28_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION29_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION29_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION30_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION30_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION31_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION31_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION32_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION32_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMB_EXP_REGION33_LUT_OFFSET; \ + type CM_BLNDGAM_RAMB_EXP_REGION33_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION_START_B; \ + type CM_BLNDGAM_RAMA_EXP_REGION_START_SEGMENT_B; \ + type CM_BLNDGAM_RAMA_EXP_REGION_START_G; \ + type CM_BLNDGAM_RAMA_EXP_REGION_START_SEGMENT_G; \ + type CM_BLNDGAM_RAMA_EXP_REGION_START_R; \ + type CM_BLNDGAM_RAMA_EXP_REGION_START_SEGMENT_R; \ + type CM_BLNDGAM_RAMA_EXP_REGION_LINEAR_SLOPE_B; \ + type CM_BLNDGAM_RAMA_EXP_REGION_LINEAR_SLOPE_G; \ + type CM_BLNDGAM_RAMA_EXP_REGION_LINEAR_SLOPE_R; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_B; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_SLOPE_B; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_BASE_B; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_G; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_SLOPE_G; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_BASE_G; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_R; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_SLOPE_R; \ + type CM_BLNDGAM_RAMA_EXP_REGION_END_BASE_R; \ + type CM_BLNDGAM_RAMA_EXP_REGION0_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION0_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION1_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION1_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION2_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION2_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION3_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION3_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION4_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION4_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION5_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION5_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION6_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION6_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION7_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION7_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION8_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION8_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION9_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION9_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION10_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION10_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION11_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION11_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION12_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION12_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION13_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION13_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION14_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION14_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION15_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION15_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION16_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION16_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION17_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION17_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION18_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION18_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION19_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION19_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION20_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION20_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION21_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION21_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION22_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION22_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION23_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION23_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION24_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION24_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION25_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION25_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION26_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION26_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION27_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION27_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION28_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION28_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION29_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION29_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION30_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION30_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION31_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION31_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION32_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION32_NUM_SEGMENTS; \ + type CM_BLNDGAM_RAMA_EXP_REGION33_LUT_OFFSET; \ + type CM_BLNDGAM_RAMA_EXP_REGION33_NUM_SEGMENTS; \ + type CM_BLNDGAM_LUT_WRITE_EN_MASK; \ + type CM_BLNDGAM_LUT_WRITE_SEL; \ + type CM_BLNDGAM_LUT_INDEX; \ + type CM_BLNDGAM_LUT_DATA; \ + type CM_3DLUT_MODE; \ + type CM_3DLUT_SIZE; \ + type CM_3DLUT_INDEX; \ + type CM_3DLUT_DATA0; \ + type CM_3DLUT_DATA1; \ + type CM_3DLUT_DATA_30BIT; \ + type CM_3DLUT_WRITE_EN_MASK; \ + type CM_3DLUT_RAM_SEL; \ + type CM_3DLUT_30BIT_EN; \ + type CM_3DLUT_CONFIG_STATUS; \ + type CM_3DLUT_READ_SEL; \ + type CM_SHAPER_LUT_MODE; \ + type CM_SHAPER_RAMB_EXP_REGION_START_B; \ + type CM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B; \ + type CM_SHAPER_RAMB_EXP_REGION_START_G; \ + type CM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_G; \ + type CM_SHAPER_RAMB_EXP_REGION_START_R; \ + type CM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_R; \ + type CM_SHAPER_RAMB_EXP_REGION_END_B; \ + type CM_SHAPER_RAMB_EXP_REGION_END_BASE_B; \ + type CM_SHAPER_RAMB_EXP_REGION_END_G; \ + type CM_SHAPER_RAMB_EXP_REGION_END_BASE_G; \ + type CM_SHAPER_RAMB_EXP_REGION_END_R; \ + type CM_SHAPER_RAMB_EXP_REGION_END_BASE_R; \ + type CM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION2_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION2_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION3_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION3_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION4_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION4_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION5_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION5_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION6_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION6_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION7_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION7_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION8_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION8_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION9_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION9_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION10_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION10_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION11_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION11_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION12_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION12_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION13_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION13_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION14_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION14_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION15_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION15_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION16_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION16_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION17_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION17_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION18_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION18_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION19_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION19_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION20_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION20_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION21_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION21_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION22_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION22_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION23_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION23_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION24_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION24_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION25_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION25_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION26_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION26_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION27_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION27_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION28_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION28_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION29_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION29_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION30_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION30_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION31_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION31_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION32_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION32_NUM_SEGMENTS; \ + type CM_SHAPER_RAMB_EXP_REGION33_LUT_OFFSET; \ + type CM_SHAPER_RAMB_EXP_REGION33_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION_START_B; \ + type CM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B; \ + type CM_SHAPER_RAMA_EXP_REGION_START_G; \ + type CM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_G; \ + type CM_SHAPER_RAMA_EXP_REGION_START_R; \ + type CM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_R; \ + type CM_SHAPER_RAMA_EXP_REGION_END_B; \ + type CM_SHAPER_RAMA_EXP_REGION_END_BASE_B; \ + type CM_SHAPER_RAMA_EXP_REGION_END_G; \ + type CM_SHAPER_RAMA_EXP_REGION_END_BASE_G; \ + type CM_SHAPER_RAMA_EXP_REGION_END_R; \ + type CM_SHAPER_RAMA_EXP_REGION_END_BASE_R; \ + type CM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION2_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION2_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION3_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION3_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION4_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION4_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION5_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION5_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION6_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION6_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION7_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION7_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION8_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION8_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION9_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION9_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION10_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION10_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION11_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION11_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION12_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION12_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION13_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION13_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION14_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION14_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION15_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION15_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION16_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION16_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION17_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION17_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION18_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION18_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION19_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION19_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION20_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION20_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION21_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION21_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION22_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION22_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION23_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION23_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION24_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION24_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION25_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION25_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION26_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION26_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION27_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION27_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION28_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION28_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION29_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION29_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION30_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION30_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION31_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION31_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION32_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION32_NUM_SEGMENTS; \ + type CM_SHAPER_RAMA_EXP_REGION33_LUT_OFFSET; \ + type CM_SHAPER_RAMA_EXP_REGION33_NUM_SEGMENTS; \ + type CM_SHAPER_LUT_WRITE_EN_MASK; \ + type CM_SHAPER_LUT_WRITE_SEL; \ + type CM_SHAPER_LUT_INDEX; \ + type CM_SHAPER_LUT_DATA; \ + type CM_DGAM_CONFIG_STATUS; \ + type CM_ICSC_MODE; \ + type CM_ICSC_C11; \ + type CM_ICSC_C12; \ + type CM_ICSC_C33; \ + type CM_ICSC_C34; \ + type CM_DGAM_RAMB_EXP_REGION_START_B; \ + type CM_DGAM_RAMB_EXP_REGION_START_SEGMENT_B; \ + type CM_DGAM_RAMB_EXP_REGION_START_G; \ + type CM_DGAM_RAMB_EXP_REGION_START_SEGMENT_G; \ + type CM_DGAM_RAMB_EXP_REGION_START_R; \ + type CM_DGAM_RAMB_EXP_REGION_START_SEGMENT_R; \ + type CM_DGAM_RAMB_EXP_REGION_LINEAR_SLOPE_B; \ + type CM_DGAM_RAMB_EXP_REGION_LINEAR_SLOPE_G; \ + type CM_DGAM_RAMB_EXP_REGION_LINEAR_SLOPE_R; \ + type CM_DGAM_RAMB_EXP_REGION_END_B; \ + type CM_DGAM_RAMB_EXP_REGION_END_SLOPE_B; \ + type CM_DGAM_RAMB_EXP_REGION_END_BASE_B; \ + type CM_DGAM_RAMB_EXP_REGION_END_G; \ + type CM_DGAM_RAMB_EXP_REGION_END_SLOPE_G; \ + type CM_DGAM_RAMB_EXP_REGION_END_BASE_G; \ + type CM_DGAM_RAMB_EXP_REGION_END_R; \ + type CM_DGAM_RAMB_EXP_REGION_END_SLOPE_R; \ + type CM_DGAM_RAMB_EXP_REGION_END_BASE_R; \ + type CM_DGAM_RAMB_EXP_REGION0_LUT_OFFSET; \ + type CM_DGAM_RAMB_EXP_REGION0_NUM_SEGMENTS; \ + type CM_DGAM_RAMB_EXP_REGION1_LUT_OFFSET; \ + type CM_DGAM_RAMB_EXP_REGION1_NUM_SEGMENTS; \ + type CM_DGAM_RAMB_EXP_REGION14_LUT_OFFSET; \ + type CM_DGAM_RAMB_EXP_REGION14_NUM_SEGMENTS; \ + type CM_DGAM_RAMB_EXP_REGION15_LUT_OFFSET; \ + type CM_DGAM_RAMB_EXP_REGION15_NUM_SEGMENTS; \ + type CM_DGAM_RAMA_EXP_REGION_START_B; \ + type CM_DGAM_RAMA_EXP_REGION_START_SEGMENT_B; \ + type CM_DGAM_RAMA_EXP_REGION_START_G; \ + type CM_DGAM_RAMA_EXP_REGION_START_SEGMENT_G; \ + type CM_DGAM_RAMA_EXP_REGION_START_R; \ + type CM_DGAM_RAMA_EXP_REGION_START_SEGMENT_R; \ + type CM_DGAM_RAMA_EXP_REGION_LINEAR_SLOPE_B; \ + type CM_DGAM_RAMA_EXP_REGION_LINEAR_SLOPE_G; \ + type CM_DGAM_RAMA_EXP_REGION_LINEAR_SLOPE_R; \ + type CM_DGAM_RAMA_EXP_REGION_END_B; \ + type CM_DGAM_RAMA_EXP_REGION_END_SLOPE_B; \ + type CM_DGAM_RAMA_EXP_REGION_END_BASE_B; \ + type CM_DGAM_RAMA_EXP_REGION_END_G; \ + type CM_DGAM_RAMA_EXP_REGION_END_SLOPE_G; \ + type CM_DGAM_RAMA_EXP_REGION_END_BASE_G; \ + type CM_DGAM_RAMA_EXP_REGION_END_R; \ + type CM_DGAM_RAMA_EXP_REGION_END_SLOPE_R; \ + type CM_DGAM_RAMA_EXP_REGION_END_BASE_R; \ + type CM_DGAM_RAMA_EXP_REGION0_LUT_OFFSET; \ + type CM_DGAM_RAMA_EXP_REGION0_NUM_SEGMENTS; \ + type CM_DGAM_RAMA_EXP_REGION1_LUT_OFFSET; \ + type CM_DGAM_RAMA_EXP_REGION1_NUM_SEGMENTS; \ + type CM_DGAM_RAMA_EXP_REGION14_LUT_OFFSET; \ + type CM_DGAM_RAMA_EXP_REGION14_NUM_SEGMENTS; \ + type CM_DGAM_RAMA_EXP_REGION15_LUT_OFFSET; \ + type CM_DGAM_RAMA_EXP_REGION15_NUM_SEGMENTS; \ + type SHARED_MEM_PWR_DIS; \ + type CM_IGAM_LUT_FORMAT_R; \ + type CM_IGAM_LUT_FORMAT_G; \ + type CM_IGAM_LUT_FORMAT_B; \ + type CM_IGAM_LUT_HOST_EN; \ + type CM_IGAM_LUT_RW_MODE; \ + type CM_IGAM_LUT_WRITE_EN_MASK; \ + type CM_IGAM_LUT_SEL; \ + type CM_IGAM_LUT_SEQ_COLOR; \ + type CM_IGAM_DGAM_CONFIG_STATUS; \ + type CM_DGAM_LUT_WRITE_EN_MASK; \ + type CM_DGAM_LUT_WRITE_SEL; \ + type CM_DGAM_LUT_INDEX; \ + type CM_DGAM_LUT_DATA; \ + type CM_DGAM_LUT_MODE; \ + type CM_IGAM_LUT_MODE; \ + type CM_IGAM_INPUT_FORMAT; \ + type CM_IGAM_LUT_RW_INDEX; \ + type CM_BYPASS_EN; \ + type FORMAT_EXPANSION_MODE; \ + type CNVC_BYPASS; \ + type OUTPUT_FP; \ + type CNVC_SURFACE_PIXEL_FORMAT; \ + type CURSOR_MODE; \ + type CURSOR_PITCH; \ + type CURSOR_LINES_PER_CHUNK; \ + type CURSOR_ENABLE; \ + type CUR0_MODE; \ + type CUR0_EXPANSION_MODE; \ + type CUR0_ENABLE; \ + type CM_BYPASS; \ + type FORMAT_CONTROL__ALPHA_EN; \ + type CUR0_COLOR0; \ + type CUR0_COLOR1 + + + +struct dcn_dpp_shift { + TF_REG_FIELD_LIST(uint8_t); +}; + +struct dcn_dpp_mask { + TF_REG_FIELD_LIST(uint32_t); +}; + + + + +struct dcn_dpp_registers { + uint32_t DSCL_EXT_OVERSCAN_LEFT_RIGHT; + uint32_t DSCL_EXT_OVERSCAN_TOP_BOTTOM; + uint32_t OTG_H_BLANK; + uint32_t OTG_V_BLANK; + uint32_t SCL_MODE; + uint32_t LB_DATA_FORMAT; + uint32_t LB_MEMORY_CTRL; + uint32_t DSCL_AUTOCAL; + uint32_t SCL_BLACK_OFFSET; + uint32_t DSCL_CONTROL; + uint32_t SCL_TAP_CONTROL; + uint32_t SCL_COEF_RAM_TAP_SELECT; + uint32_t SCL_COEF_RAM_TAP_DATA; + uint32_t DSCL_2TAP_CONTROL; + uint32_t MPC_SIZE; + uint32_t SCL_HORZ_FILTER_SCALE_RATIO; + uint32_t SCL_VERT_FILTER_SCALE_RATIO; + uint32_t SCL_HORZ_FILTER_SCALE_RATIO_C; + uint32_t SCL_VERT_FILTER_SCALE_RATIO_C; + uint32_t SCL_HORZ_FILTER_INIT; + uint32_t SCL_HORZ_FILTER_INIT_C; + uint32_t SCL_VERT_FILTER_INIT; + uint32_t SCL_VERT_FILTER_INIT_BOT; + uint32_t SCL_VERT_FILTER_INIT_C; + uint32_t SCL_VERT_FILTER_INIT_BOT_C; + uint32_t RECOUT_START; + uint32_t RECOUT_SIZE; + uint32_t CM_GAMUT_REMAP_CONTROL; + uint32_t CM_GAMUT_REMAP_C11_C12; + uint32_t CM_GAMUT_REMAP_C33_C34; + uint32_t CM_COMA_C11_C12; + uint32_t CM_COMA_C33_C34; + uint32_t CM_COMB_C11_C12; + uint32_t CM_COMB_C33_C34; + uint32_t CM_OCSC_CONTROL; + uint32_t CM_OCSC_C11_C12; + uint32_t CM_OCSC_C33_C34; + uint32_t CM_MEM_PWR_CTRL; + uint32_t CM_RGAM_LUT_DATA; + uint32_t CM_RGAM_LUT_WRITE_EN_MASK; + uint32_t CM_RGAM_LUT_INDEX; + uint32_t CM_RGAM_RAMB_START_CNTL_B; + uint32_t CM_RGAM_RAMB_START_CNTL_G; + uint32_t CM_RGAM_RAMB_START_CNTL_R; + uint32_t CM_RGAM_RAMB_SLOPE_CNTL_B; + uint32_t CM_RGAM_RAMB_SLOPE_CNTL_G; + uint32_t CM_RGAM_RAMB_SLOPE_CNTL_R; + uint32_t CM_RGAM_RAMB_END_CNTL1_B; + uint32_t CM_RGAM_RAMB_END_CNTL2_B; + uint32_t CM_RGAM_RAMB_END_CNTL1_G; + uint32_t CM_RGAM_RAMB_END_CNTL2_G; + uint32_t CM_RGAM_RAMB_END_CNTL1_R; + uint32_t CM_RGAM_RAMB_END_CNTL2_R; + uint32_t CM_RGAM_RAMB_REGION_0_1; + uint32_t CM_RGAM_RAMB_REGION_32_33; + uint32_t CM_RGAM_RAMA_START_CNTL_B; + uint32_t CM_RGAM_RAMA_START_CNTL_G; + uint32_t CM_RGAM_RAMA_START_CNTL_R; + uint32_t CM_RGAM_RAMA_SLOPE_CNTL_B; + uint32_t CM_RGAM_RAMA_SLOPE_CNTL_G; + uint32_t CM_RGAM_RAMA_SLOPE_CNTL_R; + uint32_t CM_RGAM_RAMA_END_CNTL1_B; + uint32_t CM_RGAM_RAMA_END_CNTL2_B; + uint32_t CM_RGAM_RAMA_END_CNTL1_G; + uint32_t CM_RGAM_RAMA_END_CNTL2_G; + uint32_t CM_RGAM_RAMA_END_CNTL1_R; + uint32_t CM_RGAM_RAMA_END_CNTL2_R; + uint32_t CM_RGAM_RAMA_REGION_0_1; + uint32_t CM_RGAM_RAMA_REGION_32_33; + uint32_t CM_RGAM_CONTROL; + uint32_t CM_CMOUT_CONTROL; + uint32_t OBUF_CONTROL; + uint32_t CM_BLNDGAM_LUT_WRITE_EN_MASK; + uint32_t CM_BLNDGAM_CONTROL; + uint32_t CM_BLNDGAM_RAMB_START_CNTL_B; + uint32_t CM_BLNDGAM_RAMB_START_CNTL_G; + uint32_t CM_BLNDGAM_RAMB_START_CNTL_R; + uint32_t CM_BLNDGAM_RAMB_SLOPE_CNTL_B; + uint32_t CM_BLNDGAM_RAMB_SLOPE_CNTL_G; + uint32_t CM_BLNDGAM_RAMB_SLOPE_CNTL_R; + uint32_t CM_BLNDGAM_RAMB_END_CNTL1_B; + uint32_t CM_BLNDGAM_RAMB_END_CNTL2_B; + uint32_t CM_BLNDGAM_RAMB_END_CNTL1_G; + uint32_t CM_BLNDGAM_RAMB_END_CNTL2_G; + uint32_t CM_BLNDGAM_RAMB_END_CNTL1_R; + uint32_t CM_BLNDGAM_RAMB_END_CNTL2_R; + uint32_t CM_BLNDGAM_RAMB_REGION_0_1; + uint32_t CM_BLNDGAM_RAMB_REGION_2_3; + uint32_t CM_BLNDGAM_RAMB_REGION_4_5; + uint32_t CM_BLNDGAM_RAMB_REGION_6_7; + uint32_t CM_BLNDGAM_RAMB_REGION_8_9; + uint32_t CM_BLNDGAM_RAMB_REGION_10_11; + uint32_t CM_BLNDGAM_RAMB_REGION_12_13; + uint32_t CM_BLNDGAM_RAMB_REGION_14_15; + uint32_t CM_BLNDGAM_RAMB_REGION_16_17; + uint32_t CM_BLNDGAM_RAMB_REGION_18_19; + uint32_t CM_BLNDGAM_RAMB_REGION_20_21; + uint32_t CM_BLNDGAM_RAMB_REGION_22_23; + uint32_t CM_BLNDGAM_RAMB_REGION_24_25; + uint32_t CM_BLNDGAM_RAMB_REGION_26_27; + uint32_t CM_BLNDGAM_RAMB_REGION_28_29; + uint32_t CM_BLNDGAM_RAMB_REGION_30_31; + uint32_t CM_BLNDGAM_RAMB_REGION_32_33; + uint32_t CM_BLNDGAM_RAMA_START_CNTL_B; + uint32_t CM_BLNDGAM_RAMA_START_CNTL_G; + uint32_t CM_BLNDGAM_RAMA_START_CNTL_R; + uint32_t CM_BLNDGAM_RAMA_SLOPE_CNTL_B; + uint32_t CM_BLNDGAM_RAMA_SLOPE_CNTL_G; + uint32_t CM_BLNDGAM_RAMA_SLOPE_CNTL_R; + uint32_t CM_BLNDGAM_RAMA_END_CNTL1_B; + uint32_t CM_BLNDGAM_RAMA_END_CNTL2_B; + uint32_t CM_BLNDGAM_RAMA_END_CNTL1_G; + uint32_t CM_BLNDGAM_RAMA_END_CNTL2_G; + uint32_t CM_BLNDGAM_RAMA_END_CNTL1_R; + uint32_t CM_BLNDGAM_RAMA_END_CNTL2_R; + uint32_t CM_BLNDGAM_RAMA_REGION_0_1; + uint32_t CM_BLNDGAM_RAMA_REGION_2_3; + uint32_t CM_BLNDGAM_RAMA_REGION_4_5; + uint32_t CM_BLNDGAM_RAMA_REGION_6_7; + uint32_t CM_BLNDGAM_RAMA_REGION_8_9; + uint32_t CM_BLNDGAM_RAMA_REGION_10_11; + uint32_t CM_BLNDGAM_RAMA_REGION_12_13; + uint32_t CM_BLNDGAM_RAMA_REGION_14_15; + uint32_t CM_BLNDGAM_RAMA_REGION_16_17; + uint32_t CM_BLNDGAM_RAMA_REGION_18_19; + uint32_t CM_BLNDGAM_RAMA_REGION_20_21; + uint32_t CM_BLNDGAM_RAMA_REGION_22_23; + uint32_t CM_BLNDGAM_RAMA_REGION_24_25; + uint32_t CM_BLNDGAM_RAMA_REGION_26_27; + uint32_t CM_BLNDGAM_RAMA_REGION_28_29; + uint32_t CM_BLNDGAM_RAMA_REGION_30_31; + uint32_t CM_BLNDGAM_RAMA_REGION_32_33; + uint32_t CM_BLNDGAM_LUT_INDEX; + uint32_t CM_BLNDGAM_LUT_DATA; + uint32_t CM_3DLUT_MODE; + uint32_t CM_3DLUT_INDEX; + uint32_t CM_3DLUT_DATA; + uint32_t CM_3DLUT_DATA_30BIT; + uint32_t CM_3DLUT_READ_WRITE_CONTROL; + uint32_t CM_SHAPER_LUT_WRITE_EN_MASK; + uint32_t CM_SHAPER_CONTROL; + uint32_t CM_SHAPER_RAMB_START_CNTL_B; + uint32_t CM_SHAPER_RAMB_START_CNTL_G; + uint32_t CM_SHAPER_RAMB_START_CNTL_R; + uint32_t CM_SHAPER_RAMB_END_CNTL_B; + uint32_t CM_SHAPER_RAMB_END_CNTL_G; + uint32_t CM_SHAPER_RAMB_END_CNTL_R; + uint32_t CM_SHAPER_RAMB_REGION_0_1; + uint32_t CM_SHAPER_RAMB_REGION_2_3; + uint32_t CM_SHAPER_RAMB_REGION_4_5; + uint32_t CM_SHAPER_RAMB_REGION_6_7; + uint32_t CM_SHAPER_RAMB_REGION_8_9; + uint32_t CM_SHAPER_RAMB_REGION_10_11; + uint32_t CM_SHAPER_RAMB_REGION_12_13; + uint32_t CM_SHAPER_RAMB_REGION_14_15; + uint32_t CM_SHAPER_RAMB_REGION_16_17; + uint32_t CM_SHAPER_RAMB_REGION_18_19; + uint32_t CM_SHAPER_RAMB_REGION_20_21; + uint32_t CM_SHAPER_RAMB_REGION_22_23; + uint32_t CM_SHAPER_RAMB_REGION_24_25; + uint32_t CM_SHAPER_RAMB_REGION_26_27; + uint32_t CM_SHAPER_RAMB_REGION_28_29; + uint32_t CM_SHAPER_RAMB_REGION_30_31; + uint32_t CM_SHAPER_RAMB_REGION_32_33; + uint32_t CM_SHAPER_RAMA_START_CNTL_B; + uint32_t CM_SHAPER_RAMA_START_CNTL_G; + uint32_t CM_SHAPER_RAMA_START_CNTL_R; + uint32_t CM_SHAPER_RAMA_END_CNTL_B; + uint32_t CM_SHAPER_RAMA_END_CNTL_G; + uint32_t CM_SHAPER_RAMA_END_CNTL_R; + uint32_t CM_SHAPER_RAMA_REGION_0_1; + uint32_t CM_SHAPER_RAMA_REGION_2_3; + uint32_t CM_SHAPER_RAMA_REGION_4_5; + uint32_t CM_SHAPER_RAMA_REGION_6_7; + uint32_t CM_SHAPER_RAMA_REGION_8_9; + uint32_t CM_SHAPER_RAMA_REGION_10_11; + uint32_t CM_SHAPER_RAMA_REGION_12_13; + uint32_t CM_SHAPER_RAMA_REGION_14_15; + uint32_t CM_SHAPER_RAMA_REGION_16_17; + uint32_t CM_SHAPER_RAMA_REGION_18_19; + uint32_t CM_SHAPER_RAMA_REGION_20_21; + uint32_t CM_SHAPER_RAMA_REGION_22_23; + uint32_t CM_SHAPER_RAMA_REGION_24_25; + uint32_t CM_SHAPER_RAMA_REGION_26_27; + uint32_t CM_SHAPER_RAMA_REGION_28_29; + uint32_t CM_SHAPER_RAMA_REGION_30_31; + uint32_t CM_SHAPER_RAMA_REGION_32_33; + uint32_t CM_SHAPER_LUT_INDEX; + uint32_t CM_SHAPER_LUT_DATA; + uint32_t CM_ICSC_CONTROL; + uint32_t CM_ICSC_C11_C12; + uint32_t CM_ICSC_C33_C34; + uint32_t CM_DGAM_RAMB_START_CNTL_B; + uint32_t CM_DGAM_RAMB_START_CNTL_G; + uint32_t CM_DGAM_RAMB_START_CNTL_R; + uint32_t CM_DGAM_RAMB_SLOPE_CNTL_B; + uint32_t CM_DGAM_RAMB_SLOPE_CNTL_G; + uint32_t CM_DGAM_RAMB_SLOPE_CNTL_R; + uint32_t CM_DGAM_RAMB_END_CNTL1_B; + uint32_t CM_DGAM_RAMB_END_CNTL2_B; + uint32_t CM_DGAM_RAMB_END_CNTL1_G; + uint32_t CM_DGAM_RAMB_END_CNTL2_G; + uint32_t CM_DGAM_RAMB_END_CNTL1_R; + uint32_t CM_DGAM_RAMB_END_CNTL2_R; + uint32_t CM_DGAM_RAMB_REGION_0_1; + uint32_t CM_DGAM_RAMB_REGION_14_15; + uint32_t CM_DGAM_RAMA_START_CNTL_B; + uint32_t CM_DGAM_RAMA_START_CNTL_G; + uint32_t CM_DGAM_RAMA_START_CNTL_R; + uint32_t CM_DGAM_RAMA_SLOPE_CNTL_B; + uint32_t CM_DGAM_RAMA_SLOPE_CNTL_G; + uint32_t CM_DGAM_RAMA_SLOPE_CNTL_R; + uint32_t CM_DGAM_RAMA_END_CNTL1_B; + uint32_t CM_DGAM_RAMA_END_CNTL2_B; + uint32_t CM_DGAM_RAMA_END_CNTL1_G; + uint32_t CM_DGAM_RAMA_END_CNTL2_G; + uint32_t CM_DGAM_RAMA_END_CNTL1_R; + uint32_t CM_DGAM_RAMA_END_CNTL2_R; + uint32_t CM_DGAM_RAMA_REGION_0_1; + uint32_t CM_DGAM_RAMA_REGION_14_15; + uint32_t CM_DGAM_LUT_WRITE_EN_MASK; + uint32_t CM_DGAM_LUT_INDEX; + uint32_t CM_DGAM_LUT_DATA; + uint32_t CM_CONTROL; + uint32_t CM_DGAM_CONTROL; + uint32_t CM_IGAM_CONTROL; + uint32_t CM_IGAM_LUT_RW_CONTROL; + uint32_t CM_IGAM_LUT_RW_INDEX; + uint32_t CM_IGAM_LUT_SEQ_COLOR; + uint32_t FORMAT_CONTROL; + uint32_t CNVC_SURFACE_PIXEL_FORMAT; + uint32_t CURSOR_CONTROL; + uint32_t CURSOR0_CONTROL; + uint32_t CURSOR0_COLOR0; + uint32_t CURSOR0_COLOR1; +}; + +struct dcn10_dpp { + struct dpp base; + + const struct dcn_dpp_registers *tf_regs; + const struct dcn_dpp_shift *tf_shift; + const struct dcn_dpp_mask *tf_mask; + + const uint16_t *filter_v; + const uint16_t *filter_h; + const uint16_t *filter_v_c; + const uint16_t *filter_h_c; + int lb_pixel_depth_supported; + int lb_memory_size; + int lb_bits_per_entry; + bool is_write_to_ram_a_safe; +}; + +enum dcn10_input_csc_select { + INPUT_CSC_SELECT_BYPASS = 0, + INPUT_CSC_SELECT_ICSC, + INPUT_CSC_SELECT_COMA +}; + +bool dpp1_dscl_is_lb_conf_valid( + int ceil_vratio, + int num_partitions, + int vtaps); + +void dpp1_dscl_calc_lb_num_partitions( + const struct scaler_data *scl_data, + enum lb_memory_config lb_config, + int *num_part_y, + int *num_part_c); + +void dpp1_degamma_ram_select( + struct dpp *dpp_base, + bool use_ram_a); + +void dpp1_program_degamma_luta_settings( + struct dpp *dpp_base, + const struct pwl_params *params); + +void dpp1_program_degamma_lutb_settings( + struct dpp *dpp_base, + const struct pwl_params *params); + +void dpp1_program_degamma_lut( + struct dpp *dpp_base, + const struct pwl_result_data *rgb, + uint32_t num, + bool is_ram_a); + +void dpp1_power_on_degamma_lut( + struct dpp *dpp_base, + bool power_on); + +void dpp1_program_input_csc( + struct dpp *dpp_base, + enum dc_color_space color_space, + enum dcn10_input_csc_select select); + +void dpp1_program_input_lut( + struct dpp *dpp_base, + const struct dc_gamma *gamma); + +void dpp1_full_bypass(struct dpp *dpp_base); + +void dpp1_set_degamma( + struct dpp *dpp_base, + enum ipp_degamma_mode mode); + +void dpp1_set_degamma_pwl(struct dpp *dpp_base, + const struct pwl_params *params); + +bool dpp_get_optimal_number_of_taps( + struct dpp *dpp, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps); + +void dpp_reset(struct dpp *dpp_base); + +void dpp1_cm_program_regamma_lut( + struct dpp *dpp_base, + const struct pwl_result_data *rgb, + uint32_t num); + +void dpp1_cm_power_on_regamma_lut( + struct dpp *dpp_base, + bool power_on); + +void dpp1_cm_configure_regamma_lut( + struct dpp *dpp_base, + bool is_ram_a); + +/*program re gamma RAM A*/ +void dpp1_cm_program_regamma_luta_settings( + struct dpp *dpp_base, + const struct pwl_params *params); + +/*program re gamma RAM B*/ +void dpp1_cm_program_regamma_lutb_settings( + struct dpp *dpp_base, + const struct pwl_params *params); +void dpp1_cm_set_output_csc_adjustment( + struct dpp *dpp_base, + const struct out_csc_color_matrix *tbl_entry); + +void dpp1_cm_set_output_csc_default( + struct dpp *dpp_base, + const struct default_adjustment *default_adjust); + +void dpp1_cm_set_gamut_remap( + struct dpp *dpp, + const struct dpp_grph_csc_adjustment *adjust); + +void dpp1_dscl_set_scaler_manual_scale( + struct dpp *dpp_base, + const struct scaler_data *scl_data); + +void dpp1_cnv_setup ( + struct dpp *dpp_base, + enum surface_pixel_format input_format, + enum expansion_mode mode); + +void dpp1_full_bypass(struct dpp *dpp_base); + +void dpp1_construct(struct dcn10_dpp *dpp1, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_dpp_registers *tf_regs, + const struct dcn_dpp_shift *tf_shift, + const struct dcn_dpp_mask *tf_mask); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c new file mode 100644 index 000000000000..40627c244bf5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c @@ -0,0 +1,816 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "core_types.h" + +#include "reg_helper.h" +#include "dcn10_dpp.h" +#include "basics/conversion.h" +#include "dcn10_cm_common.h" + +#define NUM_PHASES 64 +#define HORZ_MAX_TAPS 8 +#define VERT_MAX_TAPS 8 + +#define BLACK_OFFSET_RGB_Y 0x0 +#define BLACK_OFFSET_CBCR 0x8000 + +#define REG(reg)\ + dpp->tf_regs->reg + +#define CTX \ + dpp->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + dpp->tf_shift->field_name, dpp->tf_mask->field_name + +struct dcn10_input_csc_matrix { + enum dc_color_space color_space; + uint16_t regval[12]; +}; + +enum dcn10_coef_filter_type_sel { + SCL_COEF_LUMA_VERT_FILTER = 0, + SCL_COEF_LUMA_HORZ_FILTER = 1, + SCL_COEF_CHROMA_VERT_FILTER = 2, + SCL_COEF_CHROMA_HORZ_FILTER = 3, + SCL_COEF_ALPHA_VERT_FILTER = 4, + SCL_COEF_ALPHA_HORZ_FILTER = 5 +}; + +enum dscl_autocal_mode { + AUTOCAL_MODE_OFF = 0, + + /* Autocal calculate the scaling ratio and initial phase and the + * DSCL_MODE_SEL must be set to 1 + */ + AUTOCAL_MODE_AUTOSCALE = 1, + /* Autocal perform auto centering without replication and the + * DSCL_MODE_SEL must be set to 0 + */ + AUTOCAL_MODE_AUTOCENTER = 2, + /* Autocal perform auto centering and auto replication and the + * DSCL_MODE_SEL must be set to 0 + */ + AUTOCAL_MODE_AUTOREPLICATE = 3 +}; + +enum dscl_mode_sel { + DSCL_MODE_SCALING_444_BYPASS = 0, + DSCL_MODE_SCALING_444_RGB_ENABLE = 1, + DSCL_MODE_SCALING_444_YCBCR_ENABLE = 2, + DSCL_MODE_SCALING_420_YCBCR_ENABLE = 3, + DSCL_MODE_SCALING_420_LUMA_BYPASS = 4, + DSCL_MODE_SCALING_420_CHROMA_BYPASS = 5, + DSCL_MODE_DSCL_BYPASS = 6 +}; + +enum gamut_remap_select { + GAMUT_REMAP_BYPASS = 0, + GAMUT_REMAP_COEFF, + GAMUT_REMAP_COMA_COEFF, + GAMUT_REMAP_COMB_COEFF +}; + +static const struct dcn10_input_csc_matrix dcn10_input_csc_matrix[] = { + {COLOR_SPACE_SRGB, + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_SRGB_LIMITED, + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_YCBCR601, + {0x2cdd, 0x2000, 0, 0xe991, 0xe926, 0x2000, 0xf4fd, 0x10ef, + 0, 0x2000, 0x38b4, 0xe3a6} }, + {COLOR_SPACE_YCBCR601_LIMITED, + {0x3353, 0x2568, 0, 0xe400, 0xe5dc, 0x2568, 0xf367, 0x1108, + 0, 0x2568, 0x40de, 0xdd3a} }, + {COLOR_SPACE_YCBCR709, + {0x3265, 0x2000, 0, 0xe6ce, 0xf105, 0x2000, 0xfa01, 0xa7d, 0, + 0x2000, 0x3b61, 0xe24f} }, + + {COLOR_SPACE_YCBCR709_LIMITED, + {0x39a6, 0x2568, 0, 0xe0d6, 0xeedd, 0x2568, 0xf925, 0x9a8, 0, + 0x2568, 0x43ee, 0xdbb2} } +}; + + + +static void program_gamut_remap( + struct dcn10_dpp *dpp, + const uint16_t *regval, + enum gamut_remap_select select) +{ + uint16_t selection = 0; + struct color_matrices_reg gam_regs; + + if (regval == NULL || select == GAMUT_REMAP_BYPASS) { + REG_SET(CM_GAMUT_REMAP_CONTROL, 0, + CM_GAMUT_REMAP_MODE, 0); + return; + } + switch (select) { + case GAMUT_REMAP_COEFF: + selection = 1; + break; + case GAMUT_REMAP_COMA_COEFF: + selection = 2; + break; + case GAMUT_REMAP_COMB_COEFF: + selection = 3; + break; + default: + break; + } + + gam_regs.shifts.csc_c11 = dpp->tf_shift->CM_GAMUT_REMAP_C11; + gam_regs.masks.csc_c11 = dpp->tf_mask->CM_GAMUT_REMAP_C11; + gam_regs.shifts.csc_c12 = dpp->tf_shift->CM_GAMUT_REMAP_C12; + gam_regs.masks.csc_c12 = dpp->tf_mask->CM_GAMUT_REMAP_C12; + + + if (select == GAMUT_REMAP_COEFF) { + gam_regs.csc_c11_c12 = REG(CM_GAMUT_REMAP_C11_C12); + gam_regs.csc_c33_c34 = REG(CM_GAMUT_REMAP_C33_C34); + + cm_helper_program_color_matrices( + dpp->base.ctx, + regval, + &gam_regs); + + } else if (select == GAMUT_REMAP_COMA_COEFF) { + + gam_regs.csc_c11_c12 = REG(CM_COMA_C11_C12); + gam_regs.csc_c33_c34 = REG(CM_COMA_C33_C34); + + cm_helper_program_color_matrices( + dpp->base.ctx, + regval, + &gam_regs); + + } else { + + gam_regs.csc_c11_c12 = REG(CM_COMB_C11_C12); + gam_regs.csc_c33_c34 = REG(CM_COMB_C33_C34); + + cm_helper_program_color_matrices( + dpp->base.ctx, + regval, + &gam_regs); + } + + REG_SET( + CM_GAMUT_REMAP_CONTROL, 0, + CM_GAMUT_REMAP_MODE, selection); + +} + +void dpp1_cm_set_gamut_remap( + struct dpp *dpp_base, + const struct dpp_grph_csc_adjustment *adjust) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW) + /* Bypass if type is bypass or hw */ + program_gamut_remap(dpp, NULL, GAMUT_REMAP_BYPASS); + else { + struct fixed31_32 arr_matrix[12]; + uint16_t arr_reg_val[12]; + + arr_matrix[0] = adjust->temperature_matrix[0]; + arr_matrix[1] = adjust->temperature_matrix[1]; + arr_matrix[2] = adjust->temperature_matrix[2]; + arr_matrix[3] = dal_fixed31_32_zero; + + arr_matrix[4] = adjust->temperature_matrix[3]; + arr_matrix[5] = adjust->temperature_matrix[4]; + arr_matrix[6] = adjust->temperature_matrix[5]; + arr_matrix[7] = dal_fixed31_32_zero; + + arr_matrix[8] = adjust->temperature_matrix[6]; + arr_matrix[9] = adjust->temperature_matrix[7]; + arr_matrix[10] = adjust->temperature_matrix[8]; + arr_matrix[11] = dal_fixed31_32_zero; + + convert_float_matrix( + arr_reg_val, arr_matrix, 12); + + program_gamut_remap(dpp, arr_reg_val, GAMUT_REMAP_COEFF); + } +} + +void dpp1_cm_set_output_csc_default( + struct dpp *dpp_base, + const struct default_adjustment *default_adjust) +{ + + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + uint32_t ocsc_mode = 0; + + if (default_adjust != NULL) { + switch (default_adjust->out_color_space) { + case COLOR_SPACE_SRGB: + case COLOR_SPACE_2020_RGB_FULLRANGE: + ocsc_mode = 0; + break; + case COLOR_SPACE_SRGB_LIMITED: + case COLOR_SPACE_2020_RGB_LIMITEDRANGE: + ocsc_mode = 1; + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + ocsc_mode = 2; + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + case COLOR_SPACE_2020_YCBCR: + ocsc_mode = 3; + break; + case COLOR_SPACE_UNKNOWN: + default: + break; + } + } + + REG_SET(CM_OCSC_CONTROL, 0, CM_OCSC_MODE, ocsc_mode); + +} + +static void dpp1_cm_get_reg_field( + struct dcn10_dpp *dpp, + struct xfer_func_reg *reg) +{ + reg->shifts.exp_region0_lut_offset = dpp->tf_shift->CM_RGAM_RAMA_EXP_REGION0_LUT_OFFSET; + reg->masks.exp_region0_lut_offset = dpp->tf_mask->CM_RGAM_RAMA_EXP_REGION0_LUT_OFFSET; + reg->shifts.exp_region0_num_segments = dpp->tf_shift->CM_RGAM_RAMA_EXP_REGION0_NUM_SEGMENTS; + reg->masks.exp_region0_num_segments = dpp->tf_mask->CM_RGAM_RAMA_EXP_REGION0_NUM_SEGMENTS; + reg->shifts.exp_region1_lut_offset = dpp->tf_shift->CM_RGAM_RAMA_EXP_REGION1_LUT_OFFSET; + reg->masks.exp_region1_lut_offset = dpp->tf_mask->CM_RGAM_RAMA_EXP_REGION1_LUT_OFFSET; + reg->shifts.exp_region1_num_segments = dpp->tf_shift->CM_RGAM_RAMA_EXP_REGION1_NUM_SEGMENTS; + reg->masks.exp_region1_num_segments = dpp->tf_mask->CM_RGAM_RAMA_EXP_REGION1_NUM_SEGMENTS; + + reg->shifts.field_region_end = dpp->tf_shift->CM_RGAM_RAMB_EXP_REGION_END_B; + reg->masks.field_region_end = dpp->tf_mask->CM_RGAM_RAMB_EXP_REGION_END_B; + reg->shifts.field_region_end_slope = dpp->tf_shift->CM_RGAM_RAMB_EXP_REGION_END_SLOPE_B; + reg->masks.field_region_end_slope = dpp->tf_mask->CM_RGAM_RAMB_EXP_REGION_END_SLOPE_B; + reg->shifts.field_region_end_base = dpp->tf_shift->CM_RGAM_RAMB_EXP_REGION_END_BASE_B; + reg->masks.field_region_end_base = dpp->tf_mask->CM_RGAM_RAMB_EXP_REGION_END_BASE_B; + reg->shifts.field_region_linear_slope = dpp->tf_shift->CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_B; + reg->masks.field_region_linear_slope = dpp->tf_mask->CM_RGAM_RAMB_EXP_REGION_LINEAR_SLOPE_B; + reg->shifts.exp_region_start = dpp->tf_shift->CM_RGAM_RAMB_EXP_REGION_START_B; + reg->masks.exp_region_start = dpp->tf_mask->CM_RGAM_RAMB_EXP_REGION_START_B; + reg->shifts.exp_resion_start_segment = dpp->tf_shift->CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_B; + reg->masks.exp_resion_start_segment = dpp->tf_mask->CM_RGAM_RAMB_EXP_REGION_START_SEGMENT_B; +} + +static void dpp1_cm_program_color_matrix( + struct dcn10_dpp *dpp, + const struct out_csc_color_matrix *tbl_entry) +{ + uint32_t mode; + struct color_matrices_reg gam_regs; + + REG_GET(CM_OCSC_CONTROL, CM_OCSC_MODE, &mode); + + if (tbl_entry == NULL) { + BREAK_TO_DEBUGGER(); + return; + } + + gam_regs.shifts.csc_c11 = dpp->tf_shift->CM_OCSC_C11; + gam_regs.masks.csc_c11 = dpp->tf_mask->CM_OCSC_C11; + gam_regs.shifts.csc_c12 = dpp->tf_shift->CM_OCSC_C12; + gam_regs.masks.csc_c12 = dpp->tf_mask->CM_OCSC_C12; + + if (mode == 4) { + + gam_regs.csc_c11_c12 = REG(CM_OCSC_C11_C12); + gam_regs.csc_c33_c34 = REG(CM_OCSC_C33_C34); + + cm_helper_program_color_matrices( + dpp->base.ctx, + tbl_entry->regval, + &gam_regs); + + } else { + + gam_regs.csc_c11_c12 = REG(CM_COMB_C11_C12); + gam_regs.csc_c33_c34 = REG(CM_COMB_C33_C34); + + cm_helper_program_color_matrices( + dpp->base.ctx, + tbl_entry->regval, + &gam_regs); + } +} + +void dpp1_cm_set_output_csc_adjustment( + struct dpp *dpp_base, + const struct out_csc_color_matrix *tbl_entry) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + //enum csc_color_mode config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + uint32_t ocsc_mode = 4; + + /** + *if (tbl_entry != NULL) { + * switch (tbl_entry->color_space) { + * case COLOR_SPACE_SRGB: + * case COLOR_SPACE_2020_RGB_FULLRANGE: + * ocsc_mode = 0; + * break; + * case COLOR_SPACE_SRGB_LIMITED: + * case COLOR_SPACE_2020_RGB_LIMITEDRANGE: + * ocsc_mode = 1; + * break; + * case COLOR_SPACE_YCBCR601: + * case COLOR_SPACE_YCBCR601_LIMITED: + * ocsc_mode = 2; + * break; + * case COLOR_SPACE_YCBCR709: + * case COLOR_SPACE_YCBCR709_LIMITED: + * case COLOR_SPACE_2020_YCBCR: + * ocsc_mode = 3; + * break; + * case COLOR_SPACE_UNKNOWN: + * default: + * break; + * } + *} + */ + + REG_SET(CM_OCSC_CONTROL, 0, CM_OCSC_MODE, ocsc_mode); + dpp1_cm_program_color_matrix(dpp, tbl_entry); +} + +void dpp1_cm_power_on_regamma_lut( + struct dpp *dpp_base, + bool power_on) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + REG_SET(CM_MEM_PWR_CTRL, 0, + RGAM_MEM_PWR_FORCE, power_on == true ? 0:1); + +} + +void dpp1_cm_program_regamma_lut( + struct dpp *dpp_base, + const struct pwl_result_data *rgb, + uint32_t num) +{ + uint32_t i; + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + for (i = 0 ; i < num; i++) { + REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].red_reg); + REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].green_reg); + REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].blue_reg); + + REG_SET(CM_RGAM_LUT_DATA, 0, + CM_RGAM_LUT_DATA, rgb[i].delta_red_reg); + REG_SET(CM_RGAM_LUT_DATA, 0, + CM_RGAM_LUT_DATA, rgb[i].delta_green_reg); + REG_SET(CM_RGAM_LUT_DATA, 0, + CM_RGAM_LUT_DATA, rgb[i].delta_blue_reg); + + } + +} + +void dpp1_cm_configure_regamma_lut( + struct dpp *dpp_base, + bool is_ram_a) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + REG_UPDATE(CM_RGAM_LUT_WRITE_EN_MASK, + CM_RGAM_LUT_WRITE_EN_MASK, 7); + REG_UPDATE(CM_RGAM_LUT_WRITE_EN_MASK, + CM_RGAM_LUT_WRITE_SEL, is_ram_a == true ? 0:1); + REG_SET(CM_RGAM_LUT_INDEX, 0, CM_RGAM_LUT_INDEX, 0); +} + +/*program re gamma RAM A*/ +void dpp1_cm_program_regamma_luta_settings( + struct dpp *dpp_base, + const struct pwl_params *params) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + struct xfer_func_reg gam_regs; + + dpp1_cm_get_reg_field(dpp, &gam_regs); + + gam_regs.start_cntl_b = REG(CM_RGAM_RAMA_START_CNTL_B); + gam_regs.start_cntl_g = REG(CM_RGAM_RAMA_START_CNTL_G); + gam_regs.start_cntl_r = REG(CM_RGAM_RAMA_START_CNTL_R); + gam_regs.start_slope_cntl_b = REG(CM_RGAM_RAMA_SLOPE_CNTL_B); + gam_regs.start_slope_cntl_g = REG(CM_RGAM_RAMA_SLOPE_CNTL_G); + gam_regs.start_slope_cntl_r = REG(CM_RGAM_RAMA_SLOPE_CNTL_R); + gam_regs.start_end_cntl1_b = REG(CM_RGAM_RAMA_END_CNTL1_B); + gam_regs.start_end_cntl2_b = REG(CM_RGAM_RAMA_END_CNTL2_B); + gam_regs.start_end_cntl1_g = REG(CM_RGAM_RAMA_END_CNTL1_G); + gam_regs.start_end_cntl2_g = REG(CM_RGAM_RAMA_END_CNTL2_G); + gam_regs.start_end_cntl1_r = REG(CM_RGAM_RAMA_END_CNTL1_R); + gam_regs.start_end_cntl2_r = REG(CM_RGAM_RAMA_END_CNTL2_R); + gam_regs.region_start = REG(CM_RGAM_RAMA_REGION_0_1); + gam_regs.region_end = REG(CM_RGAM_RAMA_REGION_32_33); + + cm_helper_program_xfer_func(dpp->base.ctx, params, &gam_regs); + +} + +/*program re gamma RAM B*/ +void dpp1_cm_program_regamma_lutb_settings( + struct dpp *dpp_base, + const struct pwl_params *params) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + struct xfer_func_reg gam_regs; + + dpp1_cm_get_reg_field(dpp, &gam_regs); + + gam_regs.start_cntl_b = REG(CM_RGAM_RAMB_START_CNTL_B); + gam_regs.start_cntl_g = REG(CM_RGAM_RAMB_START_CNTL_G); + gam_regs.start_cntl_r = REG(CM_RGAM_RAMB_START_CNTL_R); + gam_regs.start_slope_cntl_b = REG(CM_RGAM_RAMB_SLOPE_CNTL_B); + gam_regs.start_slope_cntl_g = REG(CM_RGAM_RAMB_SLOPE_CNTL_G); + gam_regs.start_slope_cntl_r = REG(CM_RGAM_RAMB_SLOPE_CNTL_R); + gam_regs.start_end_cntl1_b = REG(CM_RGAM_RAMB_END_CNTL1_B); + gam_regs.start_end_cntl2_b = REG(CM_RGAM_RAMB_END_CNTL2_B); + gam_regs.start_end_cntl1_g = REG(CM_RGAM_RAMB_END_CNTL1_G); + gam_regs.start_end_cntl2_g = REG(CM_RGAM_RAMB_END_CNTL2_G); + gam_regs.start_end_cntl1_r = REG(CM_RGAM_RAMB_END_CNTL1_R); + gam_regs.start_end_cntl2_r = REG(CM_RGAM_RAMB_END_CNTL2_R); + gam_regs.region_start = REG(CM_RGAM_RAMB_REGION_0_1); + gam_regs.region_end = REG(CM_RGAM_RAMB_REGION_32_33); + + cm_helper_program_xfer_func(dpp->base.ctx, params, &gam_regs); +} + +void dpp1_program_input_csc( + struct dpp *dpp_base, + enum dc_color_space color_space, + enum dcn10_input_csc_select select) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + int i; + int arr_size = sizeof(dcn10_input_csc_matrix)/sizeof(struct dcn10_input_csc_matrix); + const uint16_t *regval = NULL; + uint32_t selection = 1; + struct color_matrices_reg gam_regs; + + if (select == INPUT_CSC_SELECT_BYPASS) { + REG_SET(CM_ICSC_CONTROL, 0, CM_ICSC_MODE, 0); + return; + } + + for (i = 0; i < arr_size; i++) + if (dcn10_input_csc_matrix[i].color_space == color_space) { + regval = dcn10_input_csc_matrix[i].regval; + break; + } + + if (regval == NULL) { + BREAK_TO_DEBUGGER(); + return; + } + + if (select == INPUT_CSC_SELECT_COMA) + selection = 2; + REG_SET(CM_ICSC_CONTROL, 0, + CM_ICSC_MODE, selection); + + gam_regs.shifts.csc_c11 = dpp->tf_shift->CM_ICSC_C11; + gam_regs.masks.csc_c11 = dpp->tf_mask->CM_ICSC_C11; + gam_regs.shifts.csc_c12 = dpp->tf_shift->CM_ICSC_C12; + gam_regs.masks.csc_c12 = dpp->tf_mask->CM_ICSC_C12; + + + if (select == INPUT_CSC_SELECT_ICSC) { + + gam_regs.csc_c11_c12 = REG(CM_ICSC_C11_C12); + gam_regs.csc_c33_c34 = REG(CM_ICSC_C33_C34); + + cm_helper_program_color_matrices( + dpp->base.ctx, + regval, + &gam_regs); + } else { + + gam_regs.csc_c11_c12 = REG(CM_COMA_C11_C12); + gam_regs.csc_c33_c34 = REG(CM_COMA_C33_C34); + + cm_helper_program_color_matrices( + dpp->base.ctx, + regval, + &gam_regs); + } +} + +/*program de gamma RAM B*/ +void dpp1_program_degamma_lutb_settings( + struct dpp *dpp_base, + const struct pwl_params *params) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + struct xfer_func_reg gam_regs; + + dpp1_cm_get_reg_field(dpp, &gam_regs); + + gam_regs.start_cntl_b = REG(CM_DGAM_RAMB_START_CNTL_B); + gam_regs.start_cntl_g = REG(CM_DGAM_RAMB_START_CNTL_G); + gam_regs.start_cntl_r = REG(CM_DGAM_RAMB_START_CNTL_R); + gam_regs.start_slope_cntl_b = REG(CM_DGAM_RAMB_SLOPE_CNTL_B); + gam_regs.start_slope_cntl_g = REG(CM_DGAM_RAMB_SLOPE_CNTL_G); + gam_regs.start_slope_cntl_r = REG(CM_DGAM_RAMB_SLOPE_CNTL_R); + gam_regs.start_end_cntl1_b = REG(CM_DGAM_RAMB_END_CNTL1_B); + gam_regs.start_end_cntl2_b = REG(CM_DGAM_RAMB_END_CNTL2_B); + gam_regs.start_end_cntl1_g = REG(CM_DGAM_RAMB_END_CNTL1_G); + gam_regs.start_end_cntl2_g = REG(CM_DGAM_RAMB_END_CNTL2_G); + gam_regs.start_end_cntl1_r = REG(CM_DGAM_RAMB_END_CNTL1_R); + gam_regs.start_end_cntl2_r = REG(CM_DGAM_RAMB_END_CNTL2_R); + gam_regs.region_start = REG(CM_DGAM_RAMB_REGION_0_1); + gam_regs.region_end = REG(CM_DGAM_RAMB_REGION_14_15); + + + cm_helper_program_xfer_func(dpp->base.ctx, params, &gam_regs); +} + +/*program de gamma RAM A*/ +void dpp1_program_degamma_luta_settings( + struct dpp *dpp_base, + const struct pwl_params *params) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + struct xfer_func_reg gam_regs; + + dpp1_cm_get_reg_field(dpp, &gam_regs); + + gam_regs.start_cntl_b = REG(CM_DGAM_RAMA_START_CNTL_B); + gam_regs.start_cntl_g = REG(CM_DGAM_RAMA_START_CNTL_G); + gam_regs.start_cntl_r = REG(CM_DGAM_RAMA_START_CNTL_R); + gam_regs.start_slope_cntl_b = REG(CM_DGAM_RAMA_SLOPE_CNTL_B); + gam_regs.start_slope_cntl_g = REG(CM_DGAM_RAMA_SLOPE_CNTL_G); + gam_regs.start_slope_cntl_r = REG(CM_DGAM_RAMA_SLOPE_CNTL_R); + gam_regs.start_end_cntl1_b = REG(CM_DGAM_RAMA_END_CNTL1_B); + gam_regs.start_end_cntl2_b = REG(CM_DGAM_RAMA_END_CNTL2_B); + gam_regs.start_end_cntl1_g = REG(CM_DGAM_RAMA_END_CNTL1_G); + gam_regs.start_end_cntl2_g = REG(CM_DGAM_RAMA_END_CNTL2_G); + gam_regs.start_end_cntl1_r = REG(CM_DGAM_RAMA_END_CNTL1_R); + gam_regs.start_end_cntl2_r = REG(CM_DGAM_RAMA_END_CNTL2_R); + gam_regs.region_start = REG(CM_DGAM_RAMA_REGION_0_1); + gam_regs.region_end = REG(CM_DGAM_RAMA_REGION_14_15); + + cm_helper_program_xfer_func(dpp->base.ctx, params, &gam_regs); +} + +void dpp1_power_on_degamma_lut( + struct dpp *dpp_base, + bool power_on) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + REG_SET(CM_MEM_PWR_CTRL, 0, + SHARED_MEM_PWR_DIS, power_on == true ? 0:1); + +} + +static void dpp1_enable_cm_block( + struct dpp *dpp_base) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + REG_UPDATE(CM_CMOUT_CONTROL, CM_CMOUT_ROUND_TRUNC_MODE, 8); + REG_UPDATE(CM_CONTROL, CM_BYPASS_EN, 0); +} + +void dpp1_set_degamma( + struct dpp *dpp_base, + enum ipp_degamma_mode mode) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + dpp1_enable_cm_block(dpp_base); + + switch (mode) { + case IPP_DEGAMMA_MODE_BYPASS: + /* Setting de gamma bypass for now */ + REG_UPDATE(CM_DGAM_CONTROL, CM_DGAM_LUT_MODE, 0); + break; + case IPP_DEGAMMA_MODE_HW_sRGB: + REG_UPDATE(CM_DGAM_CONTROL, CM_DGAM_LUT_MODE, 1); + break; + case IPP_DEGAMMA_MODE_HW_xvYCC: + REG_UPDATE(CM_DGAM_CONTROL, CM_DGAM_LUT_MODE, 2); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} + +void dpp1_degamma_ram_select( + struct dpp *dpp_base, + bool use_ram_a) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + if (use_ram_a) + REG_UPDATE(CM_DGAM_CONTROL, CM_DGAM_LUT_MODE, 3); + else + REG_UPDATE(CM_DGAM_CONTROL, CM_DGAM_LUT_MODE, 4); + +} + +static bool dpp1_degamma_ram_inuse( + struct dpp *dpp_base, + bool *ram_a_inuse) +{ + bool ret = false; + uint32_t status_reg = 0; + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + REG_GET(CM_IGAM_LUT_RW_CONTROL, CM_IGAM_DGAM_CONFIG_STATUS, + &status_reg); + + if (status_reg == 9) { + *ram_a_inuse = true; + ret = true; + } else if (status_reg == 10) { + *ram_a_inuse = false; + ret = true; + } + return ret; +} + +void dpp1_program_degamma_lut( + struct dpp *dpp_base, + const struct pwl_result_data *rgb, + uint32_t num, + bool is_ram_a) +{ + uint32_t i; + + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + REG_UPDATE(CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_HOST_EN, 0); + REG_UPDATE(CM_DGAM_LUT_WRITE_EN_MASK, + CM_DGAM_LUT_WRITE_EN_MASK, 7); + REG_UPDATE(CM_DGAM_LUT_WRITE_EN_MASK, CM_DGAM_LUT_WRITE_SEL, + is_ram_a == true ? 0:1); + + REG_SET(CM_DGAM_LUT_INDEX, 0, CM_DGAM_LUT_INDEX, 0); + for (i = 0 ; i < num; i++) { + REG_SET(CM_DGAM_LUT_DATA, 0, CM_DGAM_LUT_DATA, rgb[i].red_reg); + REG_SET(CM_DGAM_LUT_DATA, 0, CM_DGAM_LUT_DATA, rgb[i].green_reg); + REG_SET(CM_DGAM_LUT_DATA, 0, CM_DGAM_LUT_DATA, rgb[i].blue_reg); + + REG_SET(CM_DGAM_LUT_DATA, 0, + CM_DGAM_LUT_DATA, rgb[i].delta_red_reg); + REG_SET(CM_DGAM_LUT_DATA, 0, + CM_DGAM_LUT_DATA, rgb[i].delta_green_reg); + REG_SET(CM_DGAM_LUT_DATA, 0, + CM_DGAM_LUT_DATA, rgb[i].delta_blue_reg); + } +} + +void dpp1_set_degamma_pwl(struct dpp *dpp_base, + const struct pwl_params *params) +{ + bool is_ram_a = true; + + dpp1_power_on_degamma_lut(dpp_base, true); + dpp1_enable_cm_block(dpp_base); + dpp1_degamma_ram_inuse(dpp_base, &is_ram_a); + if (is_ram_a == true) + dpp1_program_degamma_lutb_settings(dpp_base, params); + else + dpp1_program_degamma_luta_settings(dpp_base, params); + + dpp1_program_degamma_lut(dpp_base, params->rgb_resulted, + params->hw_points_num, !is_ram_a); + dpp1_degamma_ram_select(dpp_base, !is_ram_a); +} + +void dpp1_full_bypass(struct dpp *dpp_base) +{ + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + /* Input pixel format: ARGB8888 */ + REG_SET(CNVC_SURFACE_PIXEL_FORMAT, 0, + CNVC_SURFACE_PIXEL_FORMAT, 0x8); + + /* Zero expansion */ + REG_SET_3(FORMAT_CONTROL, 0, + CNVC_BYPASS, 0, + FORMAT_CONTROL__ALPHA_EN, 0, + FORMAT_EXPANSION_MODE, 0); + + /* COLOR_KEYER_CONTROL.COLOR_KEYER_EN = 0 this should be default */ + if (dpp->tf_mask->CM_BYPASS_EN) + REG_SET(CM_CONTROL, 0, CM_BYPASS_EN, 1); + + /* Setting degamma bypass for now */ + REG_SET(CM_DGAM_CONTROL, 0, CM_DGAM_LUT_MODE, 0); +} + +static bool dpp1_ingamma_ram_inuse(struct dpp *dpp_base, + bool *ram_a_inuse) +{ + bool in_use = false; + uint32_t status_reg = 0; + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + + REG_GET(CM_IGAM_LUT_RW_CONTROL, CM_IGAM_DGAM_CONFIG_STATUS, + &status_reg); + + // 1 => IGAM_RAMA, 3 => IGAM_RAMA & DGAM_ROMA, 4 => IGAM_RAMA & DGAM_ROMB + if (status_reg == 1 || status_reg == 3 || status_reg == 4) { + *ram_a_inuse = true; + in_use = true; + // 2 => IGAM_RAMB, 5 => IGAM_RAMB & DGAM_ROMA, 6 => IGAM_RAMB & DGAM_ROMB + } else if (status_reg == 2 || status_reg == 5 || status_reg == 6) { + *ram_a_inuse = false; + in_use = true; + } + return in_use; +} + +/* + * Input gamma LUT currently supports 256 values only. This means input color + * can have a maximum of 8 bits per channel (= 256 possible values) in order to + * have a one-to-one mapping with the LUT. Truncation will occur with color + * values greater than 8 bits. + * + * In the future, this function should support additional input gamma methods, + * such as piecewise linear mapping, and input gamma bypass. + */ +void dpp1_program_input_lut( + struct dpp *dpp_base, + const struct dc_gamma *gamma) +{ + int i; + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + bool rama_occupied = false; + uint32_t ram_num; + // Power on LUT memory. + REG_SET(CM_MEM_PWR_CTRL, 0, SHARED_MEM_PWR_DIS, 1); + dpp1_enable_cm_block(dpp_base); + // Determine whether to use RAM A or RAM B + dpp1_ingamma_ram_inuse(dpp_base, &rama_occupied); + if (!rama_occupied) + REG_UPDATE(CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_SEL, 0); + else + REG_UPDATE(CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_SEL, 1); + // RW mode is 256-entry LUT + REG_UPDATE(CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_RW_MODE, 0); + // IGAM Input format should be 8 bits per channel. + REG_UPDATE(CM_IGAM_CONTROL, CM_IGAM_INPUT_FORMAT, 0); + // Do not mask any R,G,B values + REG_UPDATE(CM_IGAM_LUT_RW_CONTROL, CM_IGAM_LUT_WRITE_EN_MASK, 7); + // LUT-256, unsigned, integer, new u0.12 format + REG_UPDATE_3( + CM_IGAM_CONTROL, + CM_IGAM_LUT_FORMAT_R, 3, + CM_IGAM_LUT_FORMAT_G, 3, + CM_IGAM_LUT_FORMAT_B, 3); + // Start at index 0 of IGAM LUT + REG_UPDATE(CM_IGAM_LUT_RW_INDEX, CM_IGAM_LUT_RW_INDEX, 0); + for (i = 0; i < gamma->num_entries; i++) { + REG_SET(CM_IGAM_LUT_SEQ_COLOR, 0, CM_IGAM_LUT_SEQ_COLOR, + dal_fixed31_32_round( + gamma->entries.red[i])); + REG_SET(CM_IGAM_LUT_SEQ_COLOR, 0, CM_IGAM_LUT_SEQ_COLOR, + dal_fixed31_32_round( + gamma->entries.green[i])); + REG_SET(CM_IGAM_LUT_SEQ_COLOR, 0, CM_IGAM_LUT_SEQ_COLOR, + dal_fixed31_32_round( + gamma->entries.blue[i])); + } + // Power off LUT memory + REG_SET(CM_MEM_PWR_CTRL, 0, SHARED_MEM_PWR_DIS, 0); + // Enable IGAM LUT on ram we just wrote to. 2 => RAMA, 3 => RAMB + REG_UPDATE(CM_IGAM_CONTROL, CM_IGAM_LUT_MODE, rama_occupied ? 3 : 2); + REG_GET(CM_IGAM_CONTROL, CM_IGAM_LUT_MODE, &ram_num); +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_dscl.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_dscl.c new file mode 100644 index 000000000000..cbad36410b32 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_dscl.c @@ -0,0 +1,702 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "core_types.h" + +#include "reg_helper.h" +#include "dcn10_dpp.h" +#include "basics/conversion.h" + + +#define NUM_PHASES 64 +#define HORZ_MAX_TAPS 8 +#define VERT_MAX_TAPS 8 + +#define BLACK_OFFSET_RGB_Y 0x0 +#define BLACK_OFFSET_CBCR 0x8000 + +#define REG(reg)\ + dpp->tf_regs->reg + +#define CTX \ + dpp->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + dpp->tf_shift->field_name, dpp->tf_mask->field_name + +enum dcn10_coef_filter_type_sel { + SCL_COEF_LUMA_VERT_FILTER = 0, + SCL_COEF_LUMA_HORZ_FILTER = 1, + SCL_COEF_CHROMA_VERT_FILTER = 2, + SCL_COEF_CHROMA_HORZ_FILTER = 3, + SCL_COEF_ALPHA_VERT_FILTER = 4, + SCL_COEF_ALPHA_HORZ_FILTER = 5 +}; + +enum dscl_autocal_mode { + AUTOCAL_MODE_OFF = 0, + + /* Autocal calculate the scaling ratio and initial phase and the + * DSCL_MODE_SEL must be set to 1 + */ + AUTOCAL_MODE_AUTOSCALE = 1, + /* Autocal perform auto centering without replication and the + * DSCL_MODE_SEL must be set to 0 + */ + AUTOCAL_MODE_AUTOCENTER = 2, + /* Autocal perform auto centering and auto replication and the + * DSCL_MODE_SEL must be set to 0 + */ + AUTOCAL_MODE_AUTOREPLICATE = 3 +}; + +enum dscl_mode_sel { + DSCL_MODE_SCALING_444_BYPASS = 0, + DSCL_MODE_SCALING_444_RGB_ENABLE = 1, + DSCL_MODE_SCALING_444_YCBCR_ENABLE = 2, + DSCL_MODE_SCALING_420_YCBCR_ENABLE = 3, + DSCL_MODE_SCALING_420_LUMA_BYPASS = 4, + DSCL_MODE_SCALING_420_CHROMA_BYPASS = 5, + DSCL_MODE_DSCL_BYPASS = 6 +}; + +static void dpp1_dscl_set_overscan( + struct dcn10_dpp *dpp, + const struct scaler_data *data) +{ + uint32_t left = data->recout.x; + uint32_t top = data->recout.y; + + int right = data->h_active - data->recout.x - data->recout.width; + int bottom = data->v_active - data->recout.y - data->recout.height; + + if (right < 0) { + BREAK_TO_DEBUGGER(); + right = 0; + } + if (bottom < 0) { + BREAK_TO_DEBUGGER(); + bottom = 0; + } + + REG_SET_2(DSCL_EXT_OVERSCAN_LEFT_RIGHT, 0, + EXT_OVERSCAN_LEFT, left, + EXT_OVERSCAN_RIGHT, right); + + REG_SET_2(DSCL_EXT_OVERSCAN_TOP_BOTTOM, 0, + EXT_OVERSCAN_BOTTOM, bottom, + EXT_OVERSCAN_TOP, top); +} + +static void dpp1_dscl_set_otg_blank( + struct dcn10_dpp *dpp, const struct scaler_data *data) +{ + uint32_t h_blank_start = data->h_active; + uint32_t h_blank_end = 0; + uint32_t v_blank_start = data->v_active; + uint32_t v_blank_end = 0; + + REG_SET_2(OTG_H_BLANK, 0, + OTG_H_BLANK_START, h_blank_start, + OTG_H_BLANK_END, h_blank_end); + + REG_SET_2(OTG_V_BLANK, 0, + OTG_V_BLANK_START, v_blank_start, + OTG_V_BLANK_END, v_blank_end); +} + +static int dpp1_dscl_get_pixel_depth_val(enum lb_pixel_depth depth) +{ + if (depth == LB_PIXEL_DEPTH_30BPP) + return 0; /* 10 bpc */ + else if (depth == LB_PIXEL_DEPTH_24BPP) + return 1; /* 8 bpc */ + else if (depth == LB_PIXEL_DEPTH_18BPP) + return 2; /* 6 bpc */ + else if (depth == LB_PIXEL_DEPTH_36BPP) + return 3; /* 12 bpc */ + else { + ASSERT(0); + return -1; /* Unsupported */ + } +} + +static bool dpp1_dscl_is_video_format(enum pixel_format format) +{ + if (format >= PIXEL_FORMAT_VIDEO_BEGIN + && format <= PIXEL_FORMAT_VIDEO_END) + return true; + else + return false; +} + +static bool dpp1_dscl_is_420_format(enum pixel_format format) +{ + if (format == PIXEL_FORMAT_420BPP8 || + format == PIXEL_FORMAT_420BPP10) + return true; + else + return false; +} + +static enum dscl_mode_sel dpp1_dscl_get_dscl_mode( + struct dpp *dpp_base, + const struct scaler_data *data, + bool dbg_always_scale) +{ + const long long one = dal_fixed31_32_one.value; + + if (dpp_base->caps->dscl_data_proc_format == DSCL_DATA_PRCESSING_FIXED_FORMAT) { + /* DSCL is processing data in fixed format */ + if (data->format == PIXEL_FORMAT_FP16) + return DSCL_MODE_DSCL_BYPASS; + } + + if (data->ratios.horz.value == one + && data->ratios.vert.value == one + && data->ratios.horz_c.value == one + && data->ratios.vert_c.value == one + && !dbg_always_scale) + return DSCL_MODE_SCALING_444_BYPASS; + + if (!dpp1_dscl_is_420_format(data->format)) { + if (dpp1_dscl_is_video_format(data->format)) + return DSCL_MODE_SCALING_444_YCBCR_ENABLE; + else + return DSCL_MODE_SCALING_444_RGB_ENABLE; + } + if (data->ratios.horz.value == one && data->ratios.vert.value == one) + return DSCL_MODE_SCALING_420_LUMA_BYPASS; + if (data->ratios.horz_c.value == one && data->ratios.vert_c.value == one) + return DSCL_MODE_SCALING_420_CHROMA_BYPASS; + + return DSCL_MODE_SCALING_420_YCBCR_ENABLE; +} + +static void dpp1_dscl_set_lb( + struct dcn10_dpp *dpp, + const struct line_buffer_params *lb_params, + enum lb_memory_config mem_size_config) +{ + /* LB */ + if (dpp->base.caps->dscl_data_proc_format == DSCL_DATA_PRCESSING_FIXED_FORMAT) { + /* DSCL caps: pixel data processed in fixed format */ + uint32_t pixel_depth = dpp1_dscl_get_pixel_depth_val(lb_params->depth); + uint32_t dyn_pix_depth = lb_params->dynamic_pixel_depth; + + REG_SET_7(LB_DATA_FORMAT, 0, + PIXEL_DEPTH, pixel_depth, /* Pixel depth stored in LB */ + PIXEL_EXPAN_MODE, lb_params->pixel_expan_mode, /* Pixel expansion mode */ + PIXEL_REDUCE_MODE, 1, /* Pixel reduction mode: Rounding */ + DYNAMIC_PIXEL_DEPTH, dyn_pix_depth, /* Dynamic expansion pixel depth */ + DITHER_EN, 0, /* Dithering enable: Disabled */ + INTERLEAVE_EN, lb_params->interleave_en, /* Interleave source enable */ + LB_DATA_FORMAT__ALPHA_EN, lb_params->alpha_en); /* Alpha enable */ + } + + REG_SET_2(LB_MEMORY_CTRL, 0, + MEMORY_CONFIG, mem_size_config, + LB_MAX_PARTITIONS, 63); +} + +static const uint16_t *dpp1_dscl_get_filter_coeffs_64p(int taps, struct fixed31_32 ratio) +{ + if (taps == 8) + return get_filter_8tap_64p(ratio); + else if (taps == 7) + return get_filter_7tap_64p(ratio); + else if (taps == 6) + return get_filter_6tap_64p(ratio); + else if (taps == 5) + return get_filter_5tap_64p(ratio); + else if (taps == 4) + return get_filter_4tap_64p(ratio); + else if (taps == 3) + return get_filter_3tap_64p(ratio); + else if (taps == 2) + return get_filter_2tap_64p(); + else if (taps == 1) + return NULL; + else { + /* should never happen, bug */ + BREAK_TO_DEBUGGER(); + return NULL; + } +} + +static void dpp1_dscl_set_scaler_filter( + struct dcn10_dpp *dpp, + uint32_t taps, + enum dcn10_coef_filter_type_sel filter_type, + const uint16_t *filter) +{ + const int tap_pairs = (taps + 1) / 2; + int phase; + int pair; + uint16_t odd_coef, even_coef; + + REG_SET_3(SCL_COEF_RAM_TAP_SELECT, 0, + SCL_COEF_RAM_TAP_PAIR_IDX, 0, + SCL_COEF_RAM_PHASE, 0, + SCL_COEF_RAM_FILTER_TYPE, filter_type); + + for (phase = 0; phase < (NUM_PHASES / 2 + 1); phase++) { + for (pair = 0; pair < tap_pairs; pair++) { + even_coef = filter[phase * taps + 2 * pair]; + if ((pair * 2 + 1) < taps) + odd_coef = filter[phase * taps + 2 * pair + 1]; + else + odd_coef = 0; + + REG_SET_4(SCL_COEF_RAM_TAP_DATA, 0, + /* Even tap coefficient (bits 1:0 fixed to 0) */ + SCL_COEF_RAM_EVEN_TAP_COEF, even_coef, + /* Write/read control for even coefficient */ + SCL_COEF_RAM_EVEN_TAP_COEF_EN, 1, + /* Odd tap coefficient (bits 1:0 fixed to 0) */ + SCL_COEF_RAM_ODD_TAP_COEF, odd_coef, + /* Write/read control for odd coefficient */ + SCL_COEF_RAM_ODD_TAP_COEF_EN, 1); + } + } + +} + +static void dpp1_dscl_set_scl_filter( + struct dcn10_dpp *dpp, + const struct scaler_data *scl_data, + bool chroma_coef_mode) +{ + bool h_2tap_hardcode_coef_en = false; + bool v_2tap_hardcode_coef_en = false; + bool h_2tap_sharp_en = false; + bool v_2tap_sharp_en = false; + uint32_t h_2tap_sharp_factor = scl_data->sharpness.horz; + uint32_t v_2tap_sharp_factor = scl_data->sharpness.vert; + bool coef_ram_current; + const uint16_t *filter_h = NULL; + const uint16_t *filter_v = NULL; + const uint16_t *filter_h_c = NULL; + const uint16_t *filter_v_c = NULL; + + h_2tap_hardcode_coef_en = scl_data->taps.h_taps < 3 + && scl_data->taps.h_taps_c < 3 + && (scl_data->taps.h_taps > 1 && scl_data->taps.h_taps_c > 1); + v_2tap_hardcode_coef_en = scl_data->taps.v_taps < 3 + && scl_data->taps.v_taps_c < 3 + && (scl_data->taps.v_taps > 1 && scl_data->taps.v_taps_c > 1); + + h_2tap_sharp_en = h_2tap_hardcode_coef_en && h_2tap_sharp_factor != 0; + v_2tap_sharp_en = v_2tap_hardcode_coef_en && v_2tap_sharp_factor != 0; + + REG_UPDATE_6(DSCL_2TAP_CONTROL, + SCL_H_2TAP_HARDCODE_COEF_EN, h_2tap_hardcode_coef_en, + SCL_H_2TAP_SHARP_EN, h_2tap_sharp_en, + SCL_H_2TAP_SHARP_FACTOR, h_2tap_sharp_factor, + SCL_V_2TAP_HARDCODE_COEF_EN, v_2tap_hardcode_coef_en, + SCL_V_2TAP_SHARP_EN, v_2tap_sharp_en, + SCL_V_2TAP_SHARP_FACTOR, v_2tap_sharp_factor); + + if (!v_2tap_hardcode_coef_en || !h_2tap_hardcode_coef_en) { + bool filter_updated = false; + + filter_h = dpp1_dscl_get_filter_coeffs_64p( + scl_data->taps.h_taps, scl_data->ratios.horz); + filter_v = dpp1_dscl_get_filter_coeffs_64p( + scl_data->taps.v_taps, scl_data->ratios.vert); + + filter_updated = (filter_h && (filter_h != dpp->filter_h)) + || (filter_v && (filter_v != dpp->filter_v)); + + if (chroma_coef_mode) { + filter_h_c = dpp1_dscl_get_filter_coeffs_64p( + scl_data->taps.h_taps_c, scl_data->ratios.horz_c); + filter_v_c = dpp1_dscl_get_filter_coeffs_64p( + scl_data->taps.v_taps_c, scl_data->ratios.vert_c); + filter_updated = filter_updated || (filter_h_c && (filter_h_c != dpp->filter_h_c)) + || (filter_v_c && (filter_v_c != dpp->filter_v_c)); + } + + if (filter_updated) { + uint32_t scl_mode = REG_READ(SCL_MODE); + + if (!h_2tap_hardcode_coef_en && filter_h) { + dpp1_dscl_set_scaler_filter( + dpp, scl_data->taps.h_taps, + SCL_COEF_LUMA_HORZ_FILTER, filter_h); + } + dpp->filter_h = filter_h; + if (!v_2tap_hardcode_coef_en && filter_v) { + dpp1_dscl_set_scaler_filter( + dpp, scl_data->taps.v_taps, + SCL_COEF_LUMA_VERT_FILTER, filter_v); + } + dpp->filter_v = filter_v; + if (chroma_coef_mode) { + if (!h_2tap_hardcode_coef_en && filter_h_c) { + dpp1_dscl_set_scaler_filter( + dpp, scl_data->taps.h_taps_c, + SCL_COEF_CHROMA_HORZ_FILTER, filter_h_c); + } + if (!v_2tap_hardcode_coef_en && filter_v_c) { + dpp1_dscl_set_scaler_filter( + dpp, scl_data->taps.v_taps_c, + SCL_COEF_CHROMA_VERT_FILTER, filter_v_c); + } + } + dpp->filter_h_c = filter_h_c; + dpp->filter_v_c = filter_v_c; + + coef_ram_current = get_reg_field_value_ex( + scl_mode, dpp->tf_mask->SCL_COEF_RAM_SELECT_CURRENT, + dpp->tf_shift->SCL_COEF_RAM_SELECT_CURRENT); + + /* Swap coefficient RAM and set chroma coefficient mode */ + REG_SET_2(SCL_MODE, scl_mode, + SCL_COEF_RAM_SELECT, !coef_ram_current, + SCL_CHROMA_COEF_MODE, chroma_coef_mode); + } + } +} + +static int dpp1_dscl_get_lb_depth_bpc(enum lb_pixel_depth depth) +{ + if (depth == LB_PIXEL_DEPTH_30BPP) + return 10; + else if (depth == LB_PIXEL_DEPTH_24BPP) + return 8; + else if (depth == LB_PIXEL_DEPTH_18BPP) + return 6; + else if (depth == LB_PIXEL_DEPTH_36BPP) + return 12; + else { + BREAK_TO_DEBUGGER(); + return -1; /* Unsupported */ + } +} + +void dpp1_dscl_calc_lb_num_partitions( + const struct scaler_data *scl_data, + enum lb_memory_config lb_config, + int *num_part_y, + int *num_part_c) +{ + int line_size = scl_data->viewport.width < scl_data->recout.width ? + scl_data->viewport.width : scl_data->recout.width; + int line_size_c = scl_data->viewport_c.width < scl_data->recout.width ? + scl_data->viewport_c.width : scl_data->recout.width; + int lb_bpc = dpp1_dscl_get_lb_depth_bpc(scl_data->lb_params.depth); + int memory_line_size_y = (line_size * lb_bpc + 71) / 72; /* +71 to ceil */ + int memory_line_size_c = (line_size_c * lb_bpc + 71) / 72; /* +71 to ceil */ + int memory_line_size_a = (line_size + 5) / 6; /* +5 to ceil */ + int lb_memory_size, lb_memory_size_c, lb_memory_size_a, num_partitions_a; + + if (lb_config == LB_MEMORY_CONFIG_1) { + lb_memory_size = 816; + lb_memory_size_c = 816; + lb_memory_size_a = 984; + } else if (lb_config == LB_MEMORY_CONFIG_2) { + lb_memory_size = 1088; + lb_memory_size_c = 1088; + lb_memory_size_a = 1312; + } else if (lb_config == LB_MEMORY_CONFIG_3) { + /* 420 mode: using 3rd mem from Y, Cr and Cb */ + lb_memory_size = 816 + 1088 + 848 + 848 + 848; + lb_memory_size_c = 816 + 1088; + lb_memory_size_a = 984 + 1312 + 456; + } else { + lb_memory_size = 816 + 1088 + 848; + lb_memory_size_c = 816 + 1088 + 848; + lb_memory_size_a = 984 + 1312 + 456; + } + *num_part_y = lb_memory_size / memory_line_size_y; + *num_part_c = lb_memory_size_c / memory_line_size_c; + num_partitions_a = lb_memory_size_a / memory_line_size_a; + + if (scl_data->lb_params.alpha_en + && (num_partitions_a < *num_part_y)) + *num_part_y = num_partitions_a; + + if (*num_part_y > 64) + *num_part_y = 64; + if (*num_part_c > 64) + *num_part_c = 64; + +} + +bool dpp1_dscl_is_lb_conf_valid(int ceil_vratio, int num_partitions, int vtaps) +{ + if (ceil_vratio > 2) + return vtaps <= (num_partitions - ceil_vratio + 2); + else + return vtaps <= num_partitions; +} + +/*find first match configuration which meets the min required lb size*/ +static enum lb_memory_config dpp1_dscl_find_lb_memory_config(struct dcn10_dpp *dpp, + const struct scaler_data *scl_data) +{ + int num_part_y, num_part_c; + int vtaps = scl_data->taps.v_taps; + int vtaps_c = scl_data->taps.v_taps_c; + int ceil_vratio = dal_fixed31_32_ceil(scl_data->ratios.vert); + int ceil_vratio_c = dal_fixed31_32_ceil(scl_data->ratios.vert_c); + enum lb_memory_config mem_cfg = LB_MEMORY_CONFIG_0; + + if (dpp->base.ctx->dc->debug.use_max_lb) + return mem_cfg; + + dpp->base.caps->dscl_calc_lb_num_partitions( + scl_data, LB_MEMORY_CONFIG_1, &num_part_y, &num_part_c); + + if (dpp1_dscl_is_lb_conf_valid(ceil_vratio, num_part_y, vtaps) + && dpp1_dscl_is_lb_conf_valid(ceil_vratio_c, num_part_c, vtaps_c)) + return LB_MEMORY_CONFIG_1; + + dpp->base.caps->dscl_calc_lb_num_partitions( + scl_data, LB_MEMORY_CONFIG_2, &num_part_y, &num_part_c); + + if (dpp1_dscl_is_lb_conf_valid(ceil_vratio, num_part_y, vtaps) + && dpp1_dscl_is_lb_conf_valid(ceil_vratio_c, num_part_c, vtaps_c)) + return LB_MEMORY_CONFIG_2; + + if (scl_data->format == PIXEL_FORMAT_420BPP8 + || scl_data->format == PIXEL_FORMAT_420BPP10) { + dpp->base.caps->dscl_calc_lb_num_partitions( + scl_data, LB_MEMORY_CONFIG_3, &num_part_y, &num_part_c); + + if (dpp1_dscl_is_lb_conf_valid(ceil_vratio, num_part_y, vtaps) + && dpp1_dscl_is_lb_conf_valid(ceil_vratio_c, num_part_c, vtaps_c)) + return LB_MEMORY_CONFIG_3; + } + + dpp->base.caps->dscl_calc_lb_num_partitions( + scl_data, LB_MEMORY_CONFIG_0, &num_part_y, &num_part_c); + + /*Ensure we can support the requested number of vtaps*/ + ASSERT(dpp1_dscl_is_lb_conf_valid(ceil_vratio, num_part_y, vtaps) + && dpp1_dscl_is_lb_conf_valid(ceil_vratio_c, num_part_c, vtaps_c)); + + return LB_MEMORY_CONFIG_0; +} + +void dpp1_dscl_set_scaler_auto_scale( + struct dpp *dpp_base, + const struct scaler_data *scl_data) +{ + enum lb_memory_config lb_config; + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + enum dscl_mode_sel dscl_mode = dpp1_dscl_get_dscl_mode( + dpp_base, scl_data, dpp_base->ctx->dc->debug.always_scale); + bool ycbcr = scl_data->format >= PIXEL_FORMAT_VIDEO_BEGIN + && scl_data->format <= PIXEL_FORMAT_VIDEO_END; + + dpp1_dscl_set_overscan(dpp, scl_data); + + dpp1_dscl_set_otg_blank(dpp, scl_data); + + REG_UPDATE(SCL_MODE, DSCL_MODE, dscl_mode); + + if (dscl_mode == DSCL_MODE_DSCL_BYPASS) + return; + + lb_config = dpp1_dscl_find_lb_memory_config(dpp, scl_data); + dpp1_dscl_set_lb(dpp, &scl_data->lb_params, lb_config); + + if (dscl_mode == DSCL_MODE_SCALING_444_BYPASS) + return; + + /* TODO: v_min */ + REG_SET_3(DSCL_AUTOCAL, 0, + AUTOCAL_MODE, AUTOCAL_MODE_AUTOSCALE, + AUTOCAL_NUM_PIPE, 0, + AUTOCAL_PIPE_ID, 0); + + /* Black offsets */ + if (ycbcr) + REG_SET_2(SCL_BLACK_OFFSET, 0, + SCL_BLACK_OFFSET_RGB_Y, BLACK_OFFSET_RGB_Y, + SCL_BLACK_OFFSET_CBCR, BLACK_OFFSET_CBCR); + else + + REG_SET_2(SCL_BLACK_OFFSET, 0, + SCL_BLACK_OFFSET_RGB_Y, BLACK_OFFSET_RGB_Y, + SCL_BLACK_OFFSET_CBCR, BLACK_OFFSET_RGB_Y); + + REG_SET_4(SCL_TAP_CONTROL, 0, + SCL_V_NUM_TAPS, scl_data->taps.v_taps - 1, + SCL_H_NUM_TAPS, scl_data->taps.h_taps - 1, + SCL_V_NUM_TAPS_C, scl_data->taps.v_taps_c - 1, + SCL_H_NUM_TAPS_C, scl_data->taps.h_taps_c - 1); + + dpp1_dscl_set_scl_filter(dpp, scl_data, ycbcr); +} + + +static void dpp1_dscl_set_manual_ratio_init( + struct dcn10_dpp *dpp, const struct scaler_data *data) +{ + uint32_t init_frac = 0; + uint32_t init_int = 0; + + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO, 0, + SCL_H_SCALE_RATIO, dal_fixed31_32_u2d19(data->ratios.horz) << 5); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO, 0, + SCL_V_SCALE_RATIO, dal_fixed31_32_u2d19(data->ratios.vert) << 5); + + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO_C, 0, + SCL_H_SCALE_RATIO_C, dal_fixed31_32_u2d19(data->ratios.horz_c) << 5); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO_C, 0, + SCL_V_SCALE_RATIO_C, dal_fixed31_32_u2d19(data->ratios.vert_c) << 5); + + /* + * 0.24 format for fraction, first five bits zeroed + */ + init_frac = dal_fixed31_32_u0d19(data->inits.h) << 5; + init_int = dal_fixed31_32_floor(data->inits.h); + REG_SET_2(SCL_HORZ_FILTER_INIT, 0, + SCL_H_INIT_FRAC, init_frac, + SCL_H_INIT_INT, init_int); + + init_frac = dal_fixed31_32_u0d19(data->inits.h_c) << 5; + init_int = dal_fixed31_32_floor(data->inits.h_c); + REG_SET_2(SCL_HORZ_FILTER_INIT_C, 0, + SCL_H_INIT_FRAC_C, init_frac, + SCL_H_INIT_INT_C, init_int); + + init_frac = dal_fixed31_32_u0d19(data->inits.v) << 5; + init_int = dal_fixed31_32_floor(data->inits.v); + REG_SET_2(SCL_VERT_FILTER_INIT, 0, + SCL_V_INIT_FRAC, init_frac, + SCL_V_INIT_INT, init_int); + + init_frac = dal_fixed31_32_u0d19(data->inits.v_bot) << 5; + init_int = dal_fixed31_32_floor(data->inits.v_bot); + REG_SET_2(SCL_VERT_FILTER_INIT_BOT, 0, + SCL_V_INIT_FRAC_BOT, init_frac, + SCL_V_INIT_INT_BOT, init_int); + + init_frac = dal_fixed31_32_u0d19(data->inits.v_c) << 5; + init_int = dal_fixed31_32_floor(data->inits.v_c); + REG_SET_2(SCL_VERT_FILTER_INIT_C, 0, + SCL_V_INIT_FRAC_C, init_frac, + SCL_V_INIT_INT_C, init_int); + + init_frac = dal_fixed31_32_u0d19(data->inits.v_c_bot) << 5; + init_int = dal_fixed31_32_floor(data->inits.v_c_bot); + REG_SET_2(SCL_VERT_FILTER_INIT_BOT_C, 0, + SCL_V_INIT_FRAC_BOT_C, init_frac, + SCL_V_INIT_INT_BOT_C, init_int); +} + + + +static void dpp1_dscl_set_recout( + struct dcn10_dpp *dpp, const struct rect *recout) +{ + REG_SET_2(RECOUT_START, 0, + /* First pixel of RECOUT */ + RECOUT_START_X, recout->x, + /* First line of RECOUT */ + RECOUT_START_Y, recout->y); + + REG_SET_2(RECOUT_SIZE, 0, + /* Number of RECOUT horizontal pixels */ + RECOUT_WIDTH, recout->width, + /* Number of RECOUT vertical lines */ + RECOUT_HEIGHT, recout->height + - dpp->base.ctx->dc->debug.surface_visual_confirm * 4 * + (dpp->base.inst + 1)); +} + +/* Main function to program scaler and line buffer in manual scaling mode */ +void dpp1_dscl_set_scaler_manual_scale( + struct dpp *dpp_base, + const struct scaler_data *scl_data) +{ + enum lb_memory_config lb_config; + struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + enum dscl_mode_sel dscl_mode = dpp1_dscl_get_dscl_mode( + dpp_base, scl_data, dpp_base->ctx->dc->debug.always_scale); + bool ycbcr = scl_data->format >= PIXEL_FORMAT_VIDEO_BEGIN + && scl_data->format <= PIXEL_FORMAT_VIDEO_END; + + /* Recout */ + dpp1_dscl_set_recout(dpp, &scl_data->recout); + + /* MPC Size */ + REG_SET_2(MPC_SIZE, 0, + /* Number of horizontal pixels of MPC */ + MPC_WIDTH, scl_data->h_active, + /* Number of vertical lines of MPC */ + MPC_HEIGHT, scl_data->v_active); + + /* SCL mode */ + REG_UPDATE(SCL_MODE, DSCL_MODE, dscl_mode); + + if (dscl_mode == DSCL_MODE_DSCL_BYPASS) + return; + + /* LB */ + lb_config = dpp1_dscl_find_lb_memory_config(dpp, scl_data); + dpp1_dscl_set_lb(dpp, &scl_data->lb_params, lb_config); + + if (dscl_mode == DSCL_MODE_SCALING_444_BYPASS) + return; + + /* Autocal off */ + REG_SET_3(DSCL_AUTOCAL, 0, + AUTOCAL_MODE, AUTOCAL_MODE_OFF, + AUTOCAL_NUM_PIPE, 0, + AUTOCAL_PIPE_ID, 0); + + /* Black offsets */ + if (ycbcr) + REG_SET_2(SCL_BLACK_OFFSET, 0, + SCL_BLACK_OFFSET_RGB_Y, BLACK_OFFSET_RGB_Y, + SCL_BLACK_OFFSET_CBCR, BLACK_OFFSET_CBCR); + else + + REG_SET_2(SCL_BLACK_OFFSET, 0, + SCL_BLACK_OFFSET_RGB_Y, BLACK_OFFSET_RGB_Y, + SCL_BLACK_OFFSET_CBCR, BLACK_OFFSET_RGB_Y); + + /* Manually calculate scale ratio and init values */ + dpp1_dscl_set_manual_ratio_init(dpp, scl_data); + + /* HTaps/VTaps */ + REG_SET_4(SCL_TAP_CONTROL, 0, + SCL_V_NUM_TAPS, scl_data->taps.v_taps - 1, + SCL_H_NUM_TAPS, scl_data->taps.h_taps - 1, + SCL_V_NUM_TAPS_C, scl_data->taps.v_taps_c - 1, + SCL_H_NUM_TAPS_C, scl_data->taps.h_taps_c - 1); + + dpp1_dscl_set_scl_filter(dpp, scl_data, ycbcr); +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c new file mode 100644 index 000000000000..b13dee64e0ce --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c @@ -0,0 +1,960 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" +#include "dce_calcs.h" +#include "reg_helper.h" +#include "basics/conversion.h" +#include "dcn10_hubp.h" + +#define REG(reg)\ + hubp1->mi_regs->reg + +#define CTX \ + hubp1->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + hubp1->mi_shift->field_name, hubp1->mi_mask->field_name + +void hubp1_set_blank(struct hubp *hubp, bool blank) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + uint32_t blank_en = blank ? 1 : 0; + + REG_UPDATE_2(DCHUBP_CNTL, + HUBP_BLANK_EN, blank_en, + HUBP_TTU_DISABLE, blank_en); + + if (blank) { + REG_WAIT(DCHUBP_CNTL, + HUBP_NO_OUTSTANDING_REQ, 1, + 1, 200); + hubp->mpcc_id = 0xf; + hubp->opp_id = 0xf; + } +} + +static void hubp1_set_hubp_blank_en(struct hubp *hubp, bool blank) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + uint32_t blank_en = blank ? 1 : 0; + + REG_UPDATE(DCHUBP_CNTL, HUBP_BLANK_EN, blank_en); +} + +static void hubp1_vready_workaround(struct hubp *hubp, + struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest) +{ + uint32_t value = 0; + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + + /* set HBUBREQ_DEBUG_DB[12] = 1 */ + value = REG_READ(HUBPREQ_DEBUG_DB); + + /* hack mode disable */ + value |= 0x100; + value &= ~0x1000; + + if ((pipe_dest->vstartup_start - 2*(pipe_dest->vready_offset+pipe_dest->vupdate_width + + pipe_dest->vupdate_offset) / pipe_dest->htotal) <= pipe_dest->vblank_end) { + /* if (eco_fix_needed(otg_global_sync_timing) + * set HBUBREQ_DEBUG_DB[12] = 1 */ + value |= 0x1000; + } + + REG_WRITE(HUBPREQ_DEBUG_DB, value); +} + +void hubp1_program_tiling( + struct dcn10_hubp *hubp1, + const union dc_tiling_info *info, + const enum surface_pixel_format pixel_format) +{ + REG_UPDATE_6(DCSURF_ADDR_CONFIG, + NUM_PIPES, log_2(info->gfx9.num_pipes), + NUM_BANKS, log_2(info->gfx9.num_banks), + PIPE_INTERLEAVE, info->gfx9.pipe_interleave, + NUM_SE, log_2(info->gfx9.num_shader_engines), + NUM_RB_PER_SE, log_2(info->gfx9.num_rb_per_se), + MAX_COMPRESSED_FRAGS, log_2(info->gfx9.max_compressed_frags)); + + REG_UPDATE_4(DCSURF_TILING_CONFIG, + SW_MODE, info->gfx9.swizzle, + META_LINEAR, info->gfx9.meta_linear, + RB_ALIGNED, info->gfx9.rb_aligned, + PIPE_ALIGNED, info->gfx9.pipe_aligned); +} + +void hubp1_program_size_and_rotation( + struct dcn10_hubp *hubp1, + enum dc_rotation_angle rotation, + enum surface_pixel_format format, + const union plane_size *plane_size, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror) +{ + uint32_t pitch, meta_pitch, pitch_c, meta_pitch_c, mirror; + + /* Program data and meta surface pitch (calculation from addrlib) + * 444 or 420 luma + */ + if (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + pitch = plane_size->video.luma_pitch - 1; + meta_pitch = dcc->video.meta_pitch_l - 1; + pitch_c = plane_size->video.chroma_pitch - 1; + meta_pitch_c = dcc->video.meta_pitch_c - 1; + } else { + pitch = plane_size->grph.surface_pitch - 1; + meta_pitch = dcc->grph.meta_pitch - 1; + pitch_c = 0; + meta_pitch_c = 0; + } + + if (!dcc->enable) { + meta_pitch = 0; + meta_pitch_c = 0; + } + + REG_UPDATE_2(DCSURF_SURFACE_PITCH, + PITCH, pitch, META_PITCH, meta_pitch); + + if (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + REG_UPDATE_2(DCSURF_SURFACE_PITCH_C, + PITCH_C, pitch_c, META_PITCH_C, meta_pitch_c); + + if (horizontal_mirror) + mirror = 1; + else + mirror = 0; + + + /* Program rotation angle and horz mirror - no mirror */ + if (rotation == ROTATION_ANGLE_0) + REG_UPDATE_2(DCSURF_SURFACE_CONFIG, + ROTATION_ANGLE, 0, + H_MIRROR_EN, mirror); + else if (rotation == ROTATION_ANGLE_90) + REG_UPDATE_2(DCSURF_SURFACE_CONFIG, + ROTATION_ANGLE, 1, + H_MIRROR_EN, mirror); + else if (rotation == ROTATION_ANGLE_180) + REG_UPDATE_2(DCSURF_SURFACE_CONFIG, + ROTATION_ANGLE, 2, + H_MIRROR_EN, mirror); + else if (rotation == ROTATION_ANGLE_270) + REG_UPDATE_2(DCSURF_SURFACE_CONFIG, + ROTATION_ANGLE, 3, + H_MIRROR_EN, mirror); +} + +void hubp1_program_pixel_format( + struct dcn10_hubp *hubp1, + enum surface_pixel_format format) +{ + uint32_t red_bar = 3; + uint32_t blue_bar = 2; + + /* swap for ABGR format */ + if (format == SURFACE_PIXEL_FORMAT_GRPH_ABGR8888 + || format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010 + || format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS + || format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) { + red_bar = 2; + blue_bar = 3; + } + + REG_UPDATE_2(HUBPRET_CONTROL, + CROSSBAR_SRC_CB_B, blue_bar, + CROSSBAR_SRC_CR_R, red_bar); + + /* Mapping is same as ipp programming (cnvc) */ + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 1); + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 3); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 8); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 10); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 22); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:/*we use crossbar already*/ + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 24); + break; + + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 65); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 64); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 67); + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: + REG_UPDATE(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, 66); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + /* don't see the need of program the xbar in DCN 1.0 */ +} + +bool hubp1_program_surface_flip_and_addr( + struct hubp *hubp, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + + /* program flip type */ + REG_SET(DCSURF_FLIP_CONTROL, 0, + SURFACE_FLIP_TYPE, flip_immediate); + + /* HW automatically latch rest of address register on write to + * DCSURF_PRIMARY_SURFACE_ADDRESS if SURFACE_UPDATE_LOCK is not used + * + * program high first and then the low addr, order matters! + */ + switch (address->type) { + case PLN_ADDR_TYPE_GRAPHICS: + /* DCN1.0 does not support const color + * TODO: program DCHUBBUB_RET_PATH_DCC_CFGx_0/1 + * base on address->grph.dcc_const_color + * x = 0, 2, 4, 6 for pipe 0, 1, 2, 3 for rgb and luma + * x = 1, 3, 5, 7 for pipe 0, 1, 2, 3 for chroma + */ + + if (address->grph.addr.quad_part == 0) + break; + + REG_UPDATE(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface); + + if (address->grph.meta_addr.quad_part != 0) { + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_META_SURFACE_ADDRESS_HIGH, + address->grph.meta_addr.high_part); + + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS, 0, + PRIMARY_META_SURFACE_ADDRESS, + address->grph.meta_addr.low_part); + } + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_SURFACE_ADDRESS_HIGH, + address->grph.addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS, 0, + PRIMARY_SURFACE_ADDRESS, + address->grph.addr.low_part); + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + if (address->video_progressive.luma_addr.quad_part == 0 + || address->video_progressive.chroma_addr.quad_part == 0) + break; + + REG_UPDATE(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface); + + if (address->video_progressive.luma_meta_addr.quad_part != 0) { + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C, 0, + PRIMARY_META_SURFACE_ADDRESS_HIGH_C, + address->video_progressive.chroma_meta_addr.high_part); + + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS_C, 0, + PRIMARY_META_SURFACE_ADDRESS_C, + address->video_progressive.chroma_meta_addr.low_part); + + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_META_SURFACE_ADDRESS_HIGH, + address->video_progressive.luma_meta_addr.high_part); + + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS, 0, + PRIMARY_META_SURFACE_ADDRESS, + address->video_progressive.luma_meta_addr.low_part); + } + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, 0, + PRIMARY_SURFACE_ADDRESS_HIGH_C, + address->video_progressive.chroma_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_C, 0, + PRIMARY_SURFACE_ADDRESS_C, + address->video_progressive.chroma_addr.low_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_SURFACE_ADDRESS_HIGH, + address->video_progressive.luma_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS, 0, + PRIMARY_SURFACE_ADDRESS, + address->video_progressive.luma_addr.low_part); + break; + case PLN_ADDR_TYPE_GRPH_STEREO: + if (address->grph_stereo.left_addr.quad_part == 0) + break; + if (address->grph_stereo.right_addr.quad_part == 0) + break; + + REG_UPDATE(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface); + + if (address->grph_stereo.right_meta_addr.quad_part != 0) { + + REG_SET(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH, 0, + SECONDARY_META_SURFACE_ADDRESS_HIGH, + address->grph_stereo.right_meta_addr.high_part); + + REG_SET(DCSURF_SECONDARY_META_SURFACE_ADDRESS, 0, + SECONDARY_META_SURFACE_ADDRESS, + address->grph_stereo.right_meta_addr.low_part); + } + if (address->grph_stereo.left_meta_addr.quad_part != 0) { + + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_META_SURFACE_ADDRESS_HIGH, + address->grph_stereo.left_meta_addr.high_part); + + REG_SET(DCSURF_PRIMARY_META_SURFACE_ADDRESS, 0, + PRIMARY_META_SURFACE_ADDRESS, + address->grph_stereo.left_meta_addr.low_part); + } + + REG_SET(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, 0, + SECONDARY_SURFACE_ADDRESS_HIGH, + address->grph_stereo.right_addr.high_part); + + REG_SET(DCSURF_SECONDARY_SURFACE_ADDRESS, 0, + SECONDARY_SURFACE_ADDRESS, + address->grph_stereo.right_addr.low_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + PRIMARY_SURFACE_ADDRESS_HIGH, + address->grph_stereo.left_addr.high_part); + + REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS, 0, + PRIMARY_SURFACE_ADDRESS, + address->grph_stereo.left_addr.low_part); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + hubp->request_address = *address; + + if (flip_immediate) + hubp->current_address = *address; + + return true; +} + +void hubp1_dcc_control(struct hubp *hubp, bool enable, + bool independent_64b_blks) +{ + uint32_t dcc_en = enable ? 1 : 0; + uint32_t dcc_ind_64b_blk = independent_64b_blks ? 1 : 0; + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + + REG_UPDATE_2(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_DCC_EN, dcc_en, + PRIMARY_SURFACE_DCC_IND_64B_BLK, dcc_ind_64b_blk); +} + +void hubp1_program_surface_config( + struct hubp *hubp, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + + hubp1_dcc_control(hubp, dcc->enable, dcc->grph.independent_64b_blks); + hubp1_program_tiling(hubp1, tiling_info, format); + hubp1_program_size_and_rotation( + hubp1, rotation, format, plane_size, dcc, horizontal_mirror); + hubp1_program_pixel_format(hubp1, format); +} + +void hubp1_program_requestor( + struct hubp *hubp, + struct _vcs_dpi_display_rq_regs_st *rq_regs) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + + REG_UPDATE(HUBPRET_CONTROL, + DET_BUF_PLANE1_BASE_ADDRESS, rq_regs->plane1_base_address); + REG_SET_4(DCN_EXPANSION_MODE, 0, + DRQ_EXPANSION_MODE, rq_regs->drq_expansion_mode, + PRQ_EXPANSION_MODE, rq_regs->prq_expansion_mode, + MRQ_EXPANSION_MODE, rq_regs->mrq_expansion_mode, + CRQ_EXPANSION_MODE, rq_regs->crq_expansion_mode); + REG_SET_8(DCHUBP_REQ_SIZE_CONFIG, 0, + CHUNK_SIZE, rq_regs->rq_regs_l.chunk_size, + MIN_CHUNK_SIZE, rq_regs->rq_regs_l.min_chunk_size, + META_CHUNK_SIZE, rq_regs->rq_regs_l.meta_chunk_size, + MIN_META_CHUNK_SIZE, rq_regs->rq_regs_l.min_meta_chunk_size, + DPTE_GROUP_SIZE, rq_regs->rq_regs_l.dpte_group_size, + MPTE_GROUP_SIZE, rq_regs->rq_regs_l.mpte_group_size, + SWATH_HEIGHT, rq_regs->rq_regs_l.swath_height, + PTE_ROW_HEIGHT_LINEAR, rq_regs->rq_regs_l.pte_row_height_linear); + REG_SET_8(DCHUBP_REQ_SIZE_CONFIG_C, 0, + CHUNK_SIZE_C, rq_regs->rq_regs_c.chunk_size, + MIN_CHUNK_SIZE_C, rq_regs->rq_regs_c.min_chunk_size, + META_CHUNK_SIZE_C, rq_regs->rq_regs_c.meta_chunk_size, + MIN_META_CHUNK_SIZE_C, rq_regs->rq_regs_c.min_meta_chunk_size, + DPTE_GROUP_SIZE_C, rq_regs->rq_regs_c.dpte_group_size, + MPTE_GROUP_SIZE_C, rq_regs->rq_regs_c.mpte_group_size, + SWATH_HEIGHT_C, rq_regs->rq_regs_c.swath_height, + PTE_ROW_HEIGHT_LINEAR_C, rq_regs->rq_regs_c.pte_row_height_linear); +} + + +void hubp1_program_deadline( + struct hubp *hubp, + struct _vcs_dpi_display_dlg_regs_st *dlg_attr, + struct _vcs_dpi_display_ttu_regs_st *ttu_attr) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + + /* DLG - Per hubp */ + REG_SET_2(BLANK_OFFSET_0, 0, + REFCYC_H_BLANK_END, dlg_attr->refcyc_h_blank_end, + DLG_V_BLANK_END, dlg_attr->dlg_vblank_end); + + REG_SET(BLANK_OFFSET_1, 0, + MIN_DST_Y_NEXT_START, dlg_attr->min_dst_y_next_start); + + REG_SET(DST_DIMENSIONS, 0, + REFCYC_PER_HTOTAL, dlg_attr->refcyc_per_htotal); + + REG_SET_2(DST_AFTER_SCALER, 0, + REFCYC_X_AFTER_SCALER, dlg_attr->refcyc_x_after_scaler, + DST_Y_AFTER_SCALER, dlg_attr->dst_y_after_scaler); + + if (REG(PREFETCH_SETTINS)) + REG_SET_2(PREFETCH_SETTINS, 0, + DST_Y_PREFETCH, dlg_attr->dst_y_prefetch, + VRATIO_PREFETCH, dlg_attr->vratio_prefetch); + else + REG_SET_2(PREFETCH_SETTINGS, 0, + DST_Y_PREFETCH, dlg_attr->dst_y_prefetch, + VRATIO_PREFETCH, dlg_attr->vratio_prefetch); + + REG_SET_2(VBLANK_PARAMETERS_0, 0, + DST_Y_PER_VM_VBLANK, dlg_attr->dst_y_per_vm_vblank, + DST_Y_PER_ROW_VBLANK, dlg_attr->dst_y_per_row_vblank); + + REG_SET(REF_FREQ_TO_PIX_FREQ, 0, + REF_FREQ_TO_PIX_FREQ, dlg_attr->ref_freq_to_pix_freq); + + /* DLG - Per luma/chroma */ + REG_SET(VBLANK_PARAMETERS_1, 0, + REFCYC_PER_PTE_GROUP_VBLANK_L, dlg_attr->refcyc_per_pte_group_vblank_l); + + REG_SET(VBLANK_PARAMETERS_3, 0, + REFCYC_PER_META_CHUNK_VBLANK_L, dlg_attr->refcyc_per_meta_chunk_vblank_l); + + REG_SET(NOM_PARAMETERS_0, 0, + DST_Y_PER_PTE_ROW_NOM_L, dlg_attr->dst_y_per_pte_row_nom_l); + + REG_SET(NOM_PARAMETERS_1, 0, + REFCYC_PER_PTE_GROUP_NOM_L, dlg_attr->refcyc_per_pte_group_nom_l); + + REG_SET(NOM_PARAMETERS_4, 0, + DST_Y_PER_META_ROW_NOM_L, dlg_attr->dst_y_per_meta_row_nom_l); + + REG_SET(NOM_PARAMETERS_5, 0, + REFCYC_PER_META_CHUNK_NOM_L, dlg_attr->refcyc_per_meta_chunk_nom_l); + + REG_SET_2(PER_LINE_DELIVERY_PRE, 0, + REFCYC_PER_LINE_DELIVERY_PRE_L, dlg_attr->refcyc_per_line_delivery_pre_l, + REFCYC_PER_LINE_DELIVERY_PRE_C, dlg_attr->refcyc_per_line_delivery_pre_c); + + REG_SET_2(PER_LINE_DELIVERY, 0, + REFCYC_PER_LINE_DELIVERY_L, dlg_attr->refcyc_per_line_delivery_l, + REFCYC_PER_LINE_DELIVERY_C, dlg_attr->refcyc_per_line_delivery_c); + + if (REG(PREFETCH_SETTINS_C)) + REG_SET(PREFETCH_SETTINS_C, 0, + VRATIO_PREFETCH_C, dlg_attr->vratio_prefetch_c); + else + REG_SET(PREFETCH_SETTINGS_C, 0, + VRATIO_PREFETCH_C, dlg_attr->vratio_prefetch_c); + + REG_SET(VBLANK_PARAMETERS_2, 0, + REFCYC_PER_PTE_GROUP_VBLANK_C, dlg_attr->refcyc_per_pte_group_vblank_c); + + REG_SET(VBLANK_PARAMETERS_4, 0, + REFCYC_PER_META_CHUNK_VBLANK_C, dlg_attr->refcyc_per_meta_chunk_vblank_c); + + REG_SET(NOM_PARAMETERS_2, 0, + DST_Y_PER_PTE_ROW_NOM_C, dlg_attr->dst_y_per_pte_row_nom_c); + + REG_SET(NOM_PARAMETERS_3, 0, + REFCYC_PER_PTE_GROUP_NOM_C, dlg_attr->refcyc_per_pte_group_nom_c); + + REG_SET(NOM_PARAMETERS_6, 0, + DST_Y_PER_META_ROW_NOM_C, dlg_attr->dst_y_per_meta_row_nom_c); + + REG_SET(NOM_PARAMETERS_7, 0, + REFCYC_PER_META_CHUNK_NOM_C, dlg_attr->refcyc_per_meta_chunk_nom_c); + + /* TTU - per hubp */ + REG_SET_2(DCN_TTU_QOS_WM, 0, + QoS_LEVEL_LOW_WM, ttu_attr->qos_level_low_wm, + QoS_LEVEL_HIGH_WM, ttu_attr->qos_level_high_wm); + + REG_SET_2(DCN_GLOBAL_TTU_CNTL, 0, + MIN_TTU_VBLANK, ttu_attr->min_ttu_vblank, + QoS_LEVEL_FLIP, ttu_attr->qos_level_flip); + + /* TTU - per luma/chroma */ + /* Assumed surf0 is luma and 1 is chroma */ + + REG_SET_3(DCN_SURF0_TTU_CNTL0, 0, + REFCYC_PER_REQ_DELIVERY, ttu_attr->refcyc_per_req_delivery_l, + QoS_LEVEL_FIXED, ttu_attr->qos_level_fixed_l, + QoS_RAMP_DISABLE, ttu_attr->qos_ramp_disable_l); + + REG_SET(DCN_SURF0_TTU_CNTL1, 0, + REFCYC_PER_REQ_DELIVERY_PRE, + ttu_attr->refcyc_per_req_delivery_pre_l); + + REG_SET_3(DCN_SURF1_TTU_CNTL0, 0, + REFCYC_PER_REQ_DELIVERY, ttu_attr->refcyc_per_req_delivery_c, + QoS_LEVEL_FIXED, ttu_attr->qos_level_fixed_c, + QoS_RAMP_DISABLE, ttu_attr->qos_ramp_disable_c); + + REG_SET(DCN_SURF1_TTU_CNTL1, 0, + REFCYC_PER_REQ_DELIVERY_PRE, + ttu_attr->refcyc_per_req_delivery_pre_c); +} + +static void hubp1_setup( + struct hubp *hubp, + struct _vcs_dpi_display_dlg_regs_st *dlg_attr, + struct _vcs_dpi_display_ttu_regs_st *ttu_attr, + struct _vcs_dpi_display_rq_regs_st *rq_regs, + struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest) +{ + /* otg is locked when this func is called. Register are double buffered. + * disable the requestors is not needed + */ + hubp1_program_requestor(hubp, rq_regs); + hubp1_program_deadline(hubp, dlg_attr, ttu_attr); + hubp1_vready_workaround(hubp, pipe_dest); +} + +bool hubp1_is_flip_pending(struct hubp *hubp) +{ + uint32_t flip_pending = 0; + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + struct dc_plane_address earliest_inuse_address; + + REG_GET(DCSURF_FLIP_CONTROL, + SURFACE_FLIP_PENDING, &flip_pending); + + REG_GET(DCSURF_SURFACE_EARLIEST_INUSE, + SURFACE_EARLIEST_INUSE_ADDRESS, &earliest_inuse_address.grph.addr.low_part); + + REG_GET(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, + SURFACE_EARLIEST_INUSE_ADDRESS_HIGH, &earliest_inuse_address.grph.addr.high_part); + + if (flip_pending) + return true; + + if (earliest_inuse_address.grph.addr.quad_part != hubp->request_address.grph.addr.quad_part) + return true; + + hubp->current_address = hubp->request_address; + return false; +} + +uint32_t aperture_default_system = 1; +uint32_t context0_default_system; /* = 0;*/ + +static void hubp1_set_vm_system_aperture_settings(struct hubp *hubp, + struct vm_system_aperture_param *apt) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + PHYSICAL_ADDRESS_LOC mc_vm_apt_default; + PHYSICAL_ADDRESS_LOC mc_vm_apt_low; + PHYSICAL_ADDRESS_LOC mc_vm_apt_high; + + mc_vm_apt_default.quad_part = apt->sys_default.quad_part >> 12; + mc_vm_apt_low.quad_part = apt->sys_low.quad_part >> 12; + mc_vm_apt_high.quad_part = apt->sys_high.quad_part >> 12; + + REG_SET_2(DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, 0, + MC_VM_SYSTEM_APERTURE_DEFAULT_SYSTEM, aperture_default_system, /* 1 = system physical memory */ + MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, mc_vm_apt_default.high_part); + REG_SET(DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, 0, + MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, mc_vm_apt_default.low_part); + + REG_SET(DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, 0, + MC_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, mc_vm_apt_low.high_part); + REG_SET(DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, 0, + MC_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, mc_vm_apt_low.low_part); + + REG_SET(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, 0, + MC_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, mc_vm_apt_high.high_part); + REG_SET(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, 0, + MC_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, mc_vm_apt_high.low_part); +} + +static void hubp1_set_vm_context0_settings(struct hubp *hubp, + const struct vm_context0_param *vm0) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + /* pte base */ + REG_SET(DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_MSB, 0, + VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_MSB, vm0->pte_base.high_part); + REG_SET(DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LSB, 0, + VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LSB, vm0->pte_base.low_part); + + /* pte start */ + REG_SET(DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_MSB, 0, + VM_CONTEXT0_PAGE_TABLE_START_ADDR_MSB, vm0->pte_start.high_part); + REG_SET(DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_LSB, 0, + VM_CONTEXT0_PAGE_TABLE_START_ADDR_LSB, vm0->pte_start.low_part); + + /* pte end */ + REG_SET(DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_MSB, 0, + VM_CONTEXT0_PAGE_TABLE_END_ADDR_MSB, vm0->pte_end.high_part); + REG_SET(DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_LSB, 0, + VM_CONTEXT0_PAGE_TABLE_END_ADDR_LSB, vm0->pte_end.low_part); + + /* fault handling */ + REG_SET_2(DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB, 0, + VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB, vm0->fault_default.high_part, + VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_SYSTEM, context0_default_system); + REG_SET(DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_LSB, 0, + VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_LSB, vm0->fault_default.low_part); + + /* control: enable VM PTE*/ + REG_SET_2(DCN_VM_MX_L1_TLB_CNTL, 0, + ENABLE_L1_TLB, 1, + SYSTEM_ACCESS_MODE, 3); +} + +void min_set_viewport( + struct hubp *hubp, + const struct rect *viewport, + const struct rect *viewport_c) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + + REG_SET_2(DCSURF_PRI_VIEWPORT_DIMENSION, 0, + PRI_VIEWPORT_WIDTH, viewport->width, + PRI_VIEWPORT_HEIGHT, viewport->height); + + REG_SET_2(DCSURF_PRI_VIEWPORT_START, 0, + PRI_VIEWPORT_X_START, viewport->x, + PRI_VIEWPORT_Y_START, viewport->y); + + /*for stereo*/ + REG_SET_2(DCSURF_SEC_VIEWPORT_DIMENSION, 0, + SEC_VIEWPORT_WIDTH, viewport->width, + SEC_VIEWPORT_HEIGHT, viewport->height); + + REG_SET_2(DCSURF_SEC_VIEWPORT_START, 0, + SEC_VIEWPORT_X_START, viewport->x, + SEC_VIEWPORT_Y_START, viewport->y); + + /* DC supports NV12 only at the moment */ + REG_SET_2(DCSURF_PRI_VIEWPORT_DIMENSION_C, 0, + PRI_VIEWPORT_WIDTH_C, viewport_c->width, + PRI_VIEWPORT_HEIGHT_C, viewport_c->height); + + REG_SET_2(DCSURF_PRI_VIEWPORT_START_C, 0, + PRI_VIEWPORT_X_START_C, viewport_c->x, + PRI_VIEWPORT_Y_START_C, viewport_c->y); +} + +void hubp1_read_state(struct dcn10_hubp *hubp1, + struct dcn_hubp_state *s) +{ + REG_GET(DCSURF_SURFACE_CONFIG, + SURFACE_PIXEL_FORMAT, &s->pixel_format); + + REG_GET(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, + SURFACE_EARLIEST_INUSE_ADDRESS_HIGH, &s->inuse_addr_hi); + + REG_GET_2(DCSURF_PRI_VIEWPORT_DIMENSION, + PRI_VIEWPORT_WIDTH, &s->viewport_width, + PRI_VIEWPORT_HEIGHT, &s->viewport_height); + + REG_GET_2(DCSURF_SURFACE_CONFIG, + ROTATION_ANGLE, &s->rotation_angle, + H_MIRROR_EN, &s->h_mirror_en); + + REG_GET(DCSURF_TILING_CONFIG, + SW_MODE, &s->sw_mode); + + REG_GET(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_DCC_EN, &s->dcc_en); + + REG_GET_3(DCHUBP_CNTL, + HUBP_BLANK_EN, &s->blank_en, + HUBP_TTU_DISABLE, &s->ttu_disable, + HUBP_UNDERFLOW_STATUS, &s->underflow_status); + + REG_GET(DCN_GLOBAL_TTU_CNTL, + MIN_TTU_VBLANK, &s->min_ttu_vblank); + + REG_GET_2(DCN_TTU_QOS_WM, + QoS_LEVEL_LOW_WM, &s->qos_level_low_wm, + QoS_LEVEL_HIGH_WM, &s->qos_level_high_wm); +} + +enum cursor_pitch { + CURSOR_PITCH_64_PIXELS = 0, + CURSOR_PITCH_128_PIXELS, + CURSOR_PITCH_256_PIXELS +}; + +enum cursor_lines_per_chunk { + CURSOR_LINE_PER_CHUNK_2 = 1, + CURSOR_LINE_PER_CHUNK_4, + CURSOR_LINE_PER_CHUNK_8, + CURSOR_LINE_PER_CHUNK_16 +}; + +static bool ippn10_cursor_program_control( + struct dcn10_hubp *hubp1, + bool pixel_data_invert, + enum dc_cursor_color_format color_format) +{ + if (REG(CURSOR_SETTINS)) + REG_SET_2(CURSOR_SETTINS, 0, + /* no shift of the cursor HDL schedule */ + CURSOR0_DST_Y_OFFSET, 0, + /* used to shift the cursor chunk request deadline */ + CURSOR0_CHUNK_HDL_ADJUST, 3); + else + REG_SET_2(CURSOR_SETTINGS, 0, + /* no shift of the cursor HDL schedule */ + CURSOR0_DST_Y_OFFSET, 0, + /* used to shift the cursor chunk request deadline */ + CURSOR0_CHUNK_HDL_ADJUST, 3); + + return true; +} + +static enum cursor_pitch ippn10_get_cursor_pitch( + unsigned int pitch) +{ + enum cursor_pitch hw_pitch; + + switch (pitch) { + case 64: + hw_pitch = CURSOR_PITCH_64_PIXELS; + break; + case 128: + hw_pitch = CURSOR_PITCH_128_PIXELS; + break; + case 256: + hw_pitch = CURSOR_PITCH_256_PIXELS; + break; + default: + DC_ERR("Invalid cursor pitch of %d. " + "Only 64/128/256 is supported on DCN.\n", pitch); + hw_pitch = CURSOR_PITCH_64_PIXELS; + break; + } + return hw_pitch; +} + +static enum cursor_lines_per_chunk ippn10_get_lines_per_chunk( + unsigned int cur_width, + enum dc_cursor_color_format format) +{ + enum cursor_lines_per_chunk line_per_chunk; + + if (format == CURSOR_MODE_MONO) + /* impl B. expansion in CUR Buffer reader */ + line_per_chunk = CURSOR_LINE_PER_CHUNK_16; + else if (cur_width <= 32) + line_per_chunk = CURSOR_LINE_PER_CHUNK_16; + else if (cur_width <= 64) + line_per_chunk = CURSOR_LINE_PER_CHUNK_8; + else if (cur_width <= 128) + line_per_chunk = CURSOR_LINE_PER_CHUNK_4; + else + line_per_chunk = CURSOR_LINE_PER_CHUNK_2; + + return line_per_chunk; +} + +void hubp1_cursor_set_attributes( + struct hubp *hubp, + const struct dc_cursor_attributes *attr) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + enum cursor_pitch hw_pitch = ippn10_get_cursor_pitch(attr->pitch); + enum cursor_lines_per_chunk lpc = ippn10_get_lines_per_chunk( + attr->width, attr->color_format); + + hubp->curs_attr = *attr; + + REG_UPDATE(CURSOR_SURFACE_ADDRESS_HIGH, + CURSOR_SURFACE_ADDRESS_HIGH, attr->address.high_part); + REG_UPDATE(CURSOR_SURFACE_ADDRESS, + CURSOR_SURFACE_ADDRESS, attr->address.low_part); + + REG_UPDATE_2(CURSOR_SIZE, + CURSOR_WIDTH, attr->width, + CURSOR_HEIGHT, attr->height); + REG_UPDATE_3(CURSOR_CONTROL, + CURSOR_MODE, attr->color_format, + CURSOR_PITCH, hw_pitch, + CURSOR_LINES_PER_CHUNK, lpc); + ippn10_cursor_program_control(hubp1, + attr->attribute_flags.bits.INVERT_PIXEL_DATA, + attr->color_format); +} + +void hubp1_cursor_set_position( + struct hubp *hubp, + const struct dc_cursor_position *pos, + const struct dc_cursor_mi_param *param) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + int src_x_offset = pos->x - pos->x_hotspot - param->viewport_x_start; + uint32_t cur_en = pos->enable ? 1 : 0; + uint32_t dst_x_offset = (src_x_offset >= 0) ? src_x_offset : 0; + + /* + * Guard aganst cursor_set_position() from being called with invalid + * attributes + * + * TODO: Look at combining cursor_set_position() and + * cursor_set_attributes() into cursor_update() + */ + if (hubp->curs_attr.address.quad_part == 0) + return; + + dst_x_offset *= param->ref_clk_khz; + dst_x_offset /= param->pixel_clk_khz; + + ASSERT(param->h_scale_ratio.value); + + if (param->h_scale_ratio.value) + dst_x_offset = dal_fixed31_32_floor(dal_fixed31_32_div( + dal_fixed31_32_from_int(dst_x_offset), + param->h_scale_ratio)); + + if (src_x_offset >= (int)param->viewport_width) + cur_en = 0; /* not visible beyond right edge*/ + + if (src_x_offset + (int)hubp->curs_attr.width < 0) + cur_en = 0; /* not visible beyond left edge*/ + + if (cur_en && REG_READ(CURSOR_SURFACE_ADDRESS) == 0) + hubp1_cursor_set_attributes(hubp, &hubp->curs_attr); + REG_UPDATE(CURSOR_CONTROL, + CURSOR_ENABLE, cur_en); + + REG_SET_2(CURSOR_POSITION, 0, + CURSOR_X_POSITION, pos->x, + CURSOR_Y_POSITION, pos->y); + + REG_SET_2(CURSOR_HOT_SPOT, 0, + CURSOR_HOT_SPOT_X, pos->x_hotspot, + CURSOR_HOT_SPOT_Y, pos->y_hotspot); + + REG_SET(CURSOR_DST_OFFSET, 0, + CURSOR_DST_X_OFFSET, dst_x_offset); + /* TODO Handle surface pixel formats other than 4:4:4 */ +} + +static struct hubp_funcs dcn10_hubp_funcs = { + .hubp_program_surface_flip_and_addr = + hubp1_program_surface_flip_and_addr, + .hubp_program_surface_config = + hubp1_program_surface_config, + .hubp_is_flip_pending = hubp1_is_flip_pending, + .hubp_setup = hubp1_setup, + .hubp_set_vm_system_aperture_settings = hubp1_set_vm_system_aperture_settings, + .hubp_set_vm_context0_settings = hubp1_set_vm_context0_settings, + .set_blank = hubp1_set_blank, + .dcc_control = hubp1_dcc_control, + .mem_program_viewport = min_set_viewport, + .set_hubp_blank_en = hubp1_set_hubp_blank_en, + .set_cursor_attributes = hubp1_cursor_set_attributes, + .set_cursor_position = hubp1_cursor_set_position, +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void dcn10_hubp_construct( + struct dcn10_hubp *hubp1, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_mi_registers *mi_regs, + const struct dcn_mi_shift *mi_shift, + const struct dcn_mi_mask *mi_mask) +{ + hubp1->base.funcs = &dcn10_hubp_funcs; + hubp1->base.ctx = ctx; + hubp1->mi_regs = mi_regs; + hubp1->mi_shift = mi_shift; + hubp1->mi_mask = mi_mask; + hubp1->base.inst = inst; + hubp1->base.opp_id = 0xf; + hubp1->base.mpcc_id = 0xf; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h new file mode 100644 index 000000000000..66db453c801b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h @@ -0,0 +1,683 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_MEM_INPUT_DCN10_H__ +#define __DC_MEM_INPUT_DCN10_H__ + +#include "hubp.h" + +#define TO_DCN10_HUBP(hubp)\ + container_of(hubp, struct dcn10_hubp, base) + +#define MI_REG_LIST_DCN(id)\ + SRI(DCHUBP_CNTL, HUBP, id),\ + SRI(HUBPREQ_DEBUG_DB, HUBP, id),\ + SRI(DCSURF_ADDR_CONFIG, HUBP, id),\ + SRI(DCSURF_TILING_CONFIG, HUBP, id),\ + SRI(DCSURF_SURFACE_PITCH, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_PITCH_C, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_CONFIG, HUBP, id),\ + SRI(DCSURF_FLIP_CONTROL, HUBPREQ, id),\ + SRI(DCSURF_PRI_VIEWPORT_DIMENSION, HUBP, id), \ + SRI(DCSURF_PRI_VIEWPORT_START, HUBP, id), \ + SRI(DCSURF_SEC_VIEWPORT_DIMENSION, HUBP, id), \ + SRI(DCSURF_SEC_VIEWPORT_START, HUBP, id), \ + SRI(DCSURF_PRI_VIEWPORT_DIMENSION_C, HUBP, id), \ + SRI(DCSURF_PRI_VIEWPORT_START_C, HUBP, id), \ + SRI(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id),\ + SRI(DCSURF_PRIMARY_SURFACE_ADDRESS, HUBPREQ, id),\ + SRI(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id),\ + SRI(DCSURF_SECONDARY_SURFACE_ADDRESS, HUBPREQ, id),\ + SRI(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id),\ + SRI(DCSURF_PRIMARY_META_SURFACE_ADDRESS, HUBPREQ, id),\ + SRI(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id),\ + SRI(DCSURF_SECONDARY_META_SURFACE_ADDRESS, HUBPREQ, id),\ + SRI(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id),\ + SRI(DCSURF_PRIMARY_SURFACE_ADDRESS_C, HUBPREQ, id),\ + SRI(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id),\ + SRI(DCSURF_PRIMARY_META_SURFACE_ADDRESS_C, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_INUSE, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_INUSE_HIGH, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_INUSE_C, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_INUSE_HIGH_C, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_EARLIEST_INUSE, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_EARLIEST_INUSE_C, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C, HUBPREQ, id),\ + SRI(DCSURF_SURFACE_CONTROL, HUBPREQ, id),\ + SRI(HUBPRET_CONTROL, HUBPRET, id),\ + SRI(DCN_EXPANSION_MODE, HUBPREQ, id),\ + SRI(DCHUBP_REQ_SIZE_CONFIG, HUBP, id),\ + SRI(DCHUBP_REQ_SIZE_CONFIG_C, HUBP, id),\ + SRI(BLANK_OFFSET_0, HUBPREQ, id),\ + SRI(BLANK_OFFSET_1, HUBPREQ, id),\ + SRI(DST_DIMENSIONS, HUBPREQ, id),\ + SRI(DST_AFTER_SCALER, HUBPREQ, id),\ + SRI(VBLANK_PARAMETERS_0, HUBPREQ, id),\ + SRI(REF_FREQ_TO_PIX_FREQ, HUBPREQ, id),\ + SRI(VBLANK_PARAMETERS_1, HUBPREQ, id),\ + SRI(VBLANK_PARAMETERS_3, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_0, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_1, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_4, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_5, HUBPREQ, id),\ + SRI(PER_LINE_DELIVERY_PRE, HUBPREQ, id),\ + SRI(PER_LINE_DELIVERY, HUBPREQ, id),\ + SRI(VBLANK_PARAMETERS_2, HUBPREQ, id),\ + SRI(VBLANK_PARAMETERS_4, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_2, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_3, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_6, HUBPREQ, id),\ + SRI(NOM_PARAMETERS_7, HUBPREQ, id),\ + SRI(DCN_TTU_QOS_WM, HUBPREQ, id),\ + SRI(DCN_GLOBAL_TTU_CNTL, HUBPREQ, id),\ + SRI(DCN_SURF0_TTU_CNTL0, HUBPREQ, id),\ + SRI(DCN_SURF0_TTU_CNTL1, HUBPREQ, id),\ + SRI(DCN_SURF1_TTU_CNTL0, HUBPREQ, id),\ + SRI(DCN_SURF1_TTU_CNTL1, HUBPREQ, id),\ + SRI(DCN_VM_MX_L1_TLB_CNTL, HUBPREQ, id) + +#define MI_REG_LIST_DCN10(id)\ + MI_REG_LIST_DCN(id),\ + SRI(PREFETCH_SETTINS, HUBPREQ, id),\ + SRI(PREFETCH_SETTINS_C, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_MSB, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LSB, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_MSB, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_LSB, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_MSB, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_LSB, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB, HUBPREQ, id),\ + SRI(DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_LSB, HUBPREQ, id),\ + SRI(DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, HUBPREQ, id),\ + SRI(DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, HUBPREQ, id),\ + SRI(DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, HUBPREQ, id),\ + SRI(DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, HUBPREQ, id),\ + SRI(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, HUBPREQ, id),\ + SRI(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, HUBPREQ, id),\ + SR(DCHUBBUB_SDPIF_FB_BASE),\ + SR(DCHUBBUB_SDPIF_FB_OFFSET),\ + SRI(CURSOR_SETTINS, HUBPREQ, id), \ + SRI(CURSOR_SURFACE_ADDRESS_HIGH, CURSOR, id), \ + SRI(CURSOR_SURFACE_ADDRESS, CURSOR, id), \ + SRI(CURSOR_SIZE, CURSOR, id), \ + SRI(CURSOR_CONTROL, CURSOR, id), \ + SRI(CURSOR_POSITION, CURSOR, id), \ + SRI(CURSOR_HOT_SPOT, CURSOR, id), \ + SRI(CURSOR_DST_OFFSET, CURSOR, id) + + + +struct dcn_mi_registers { + uint32_t DCHUBP_CNTL; + uint32_t HUBPREQ_DEBUG_DB; + uint32_t DCSURF_ADDR_CONFIG; + uint32_t DCSURF_TILING_CONFIG; + uint32_t DCSURF_SURFACE_PITCH; + uint32_t DCSURF_SURFACE_PITCH_C; + uint32_t DCSURF_SURFACE_CONFIG; + uint32_t DCSURF_FLIP_CONTROL; + uint32_t DCSURF_PRI_VIEWPORT_DIMENSION; + uint32_t DCSURF_PRI_VIEWPORT_START; + uint32_t DCSURF_SEC_VIEWPORT_DIMENSION; + uint32_t DCSURF_SEC_VIEWPORT_START; + uint32_t DCSURF_PRI_VIEWPORT_DIMENSION_C; + uint32_t DCSURF_PRI_VIEWPORT_START_C; + uint32_t DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH; + uint32_t DCSURF_PRIMARY_SURFACE_ADDRESS; + uint32_t DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH; + uint32_t DCSURF_SECONDARY_SURFACE_ADDRESS; + uint32_t DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH; + uint32_t DCSURF_PRIMARY_META_SURFACE_ADDRESS; + uint32_t DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH; + uint32_t DCSURF_SECONDARY_META_SURFACE_ADDRESS; + uint32_t DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C; + uint32_t DCSURF_PRIMARY_SURFACE_ADDRESS_C; + uint32_t DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C; + uint32_t DCSURF_PRIMARY_META_SURFACE_ADDRESS_C; + uint32_t DCSURF_SURFACE_INUSE; + uint32_t DCSURF_SURFACE_INUSE_HIGH; + uint32_t DCSURF_SURFACE_INUSE_C; + uint32_t DCSURF_SURFACE_INUSE_HIGH_C; + uint32_t DCSURF_SURFACE_EARLIEST_INUSE; + uint32_t DCSURF_SURFACE_EARLIEST_INUSE_HIGH; + uint32_t DCSURF_SURFACE_EARLIEST_INUSE_C; + uint32_t DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C; + uint32_t DCSURF_SURFACE_CONTROL; + uint32_t HUBPRET_CONTROL; + uint32_t DCN_EXPANSION_MODE; + uint32_t DCHUBP_REQ_SIZE_CONFIG; + uint32_t DCHUBP_REQ_SIZE_CONFIG_C; + uint32_t BLANK_OFFSET_0; + uint32_t BLANK_OFFSET_1; + uint32_t DST_DIMENSIONS; + uint32_t DST_AFTER_SCALER; + uint32_t PREFETCH_SETTINS; + uint32_t PREFETCH_SETTINGS; + uint32_t VBLANK_PARAMETERS_0; + uint32_t REF_FREQ_TO_PIX_FREQ; + uint32_t VBLANK_PARAMETERS_1; + uint32_t VBLANK_PARAMETERS_3; + uint32_t NOM_PARAMETERS_0; + uint32_t NOM_PARAMETERS_1; + uint32_t NOM_PARAMETERS_4; + uint32_t NOM_PARAMETERS_5; + uint32_t PER_LINE_DELIVERY_PRE; + uint32_t PER_LINE_DELIVERY; + uint32_t PREFETCH_SETTINS_C; + uint32_t PREFETCH_SETTINGS_C; + uint32_t VBLANK_PARAMETERS_2; + uint32_t VBLANK_PARAMETERS_4; + uint32_t NOM_PARAMETERS_2; + uint32_t NOM_PARAMETERS_3; + uint32_t NOM_PARAMETERS_6; + uint32_t NOM_PARAMETERS_7; + uint32_t DCN_TTU_QOS_WM; + uint32_t DCN_GLOBAL_TTU_CNTL; + uint32_t DCN_SURF0_TTU_CNTL0; + uint32_t DCN_SURF0_TTU_CNTL1; + uint32_t DCN_SURF1_TTU_CNTL0; + uint32_t DCN_SURF1_TTU_CNTL1; + uint32_t DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_MSB; + uint32_t DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LSB; + uint32_t DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_MSB; + uint32_t DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_LSB; + uint32_t DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_MSB; + uint32_t DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_LSB; + uint32_t DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB; + uint32_t DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_LSB; + uint32_t DCN_VM_MX_L1_TLB_CNTL; + uint32_t DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB; + uint32_t DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB; + uint32_t DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB; + uint32_t DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB; + uint32_t DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB; + uint32_t DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB; + uint32_t DCN_VM_SYSTEM_APERTURE_LOW_ADDR; + uint32_t DCN_VM_SYSTEM_APERTURE_HIGH_ADDR; + uint32_t DCHUBBUB_SDPIF_FB_BASE; + uint32_t DCHUBBUB_SDPIF_FB_OFFSET; + uint32_t DCN_VM_FB_LOCATION_TOP; + uint32_t DCN_VM_FB_LOCATION_BASE; + uint32_t DCN_VM_FB_OFFSET; + uint32_t DCN_VM_AGP_BASE; + uint32_t DCN_VM_AGP_BOT; + uint32_t DCN_VM_AGP_TOP; + uint32_t CURSOR_SETTINS; + uint32_t CURSOR_SETTINGS; + uint32_t CURSOR_SURFACE_ADDRESS_HIGH; + uint32_t CURSOR_SURFACE_ADDRESS; + uint32_t CURSOR_SIZE; + uint32_t CURSOR_CONTROL; + uint32_t CURSOR_POSITION; + uint32_t CURSOR_HOT_SPOT; + uint32_t CURSOR_DST_OFFSET; +}; + +#define MI_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define MI_MASK_SH_LIST_DCN(mask_sh)\ + MI_SF(HUBP0_DCHUBP_CNTL, HUBP_BLANK_EN, mask_sh),\ + MI_SF(HUBP0_DCHUBP_CNTL, HUBP_TTU_DISABLE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_CNTL, HUBP_UNDERFLOW_STATUS, mask_sh),\ + MI_SF(HUBP0_DCHUBP_CNTL, HUBP_NO_OUTSTANDING_REQ, mask_sh),\ + MI_SF(HUBP0_DCSURF_ADDR_CONFIG, NUM_PIPES, mask_sh),\ + MI_SF(HUBP0_DCSURF_ADDR_CONFIG, NUM_BANKS, mask_sh),\ + MI_SF(HUBP0_DCSURF_ADDR_CONFIG, PIPE_INTERLEAVE, mask_sh),\ + MI_SF(HUBP0_DCSURF_ADDR_CONFIG, NUM_SE, mask_sh),\ + MI_SF(HUBP0_DCSURF_ADDR_CONFIG, NUM_RB_PER_SE, mask_sh),\ + MI_SF(HUBP0_DCSURF_ADDR_CONFIG, MAX_COMPRESSED_FRAGS, mask_sh),\ + MI_SF(HUBP0_DCSURF_TILING_CONFIG, SW_MODE, mask_sh),\ + MI_SF(HUBP0_DCSURF_TILING_CONFIG, META_LINEAR, mask_sh),\ + MI_SF(HUBP0_DCSURF_TILING_CONFIG, RB_ALIGNED, mask_sh),\ + MI_SF(HUBP0_DCSURF_TILING_CONFIG, PIPE_ALIGNED, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_PITCH, PITCH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_PITCH, META_PITCH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_PITCH_C, PITCH_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_PITCH_C, META_PITCH_C, mask_sh),\ + MI_SF(HUBP0_DCSURF_SURFACE_CONFIG, ROTATION_ANGLE, mask_sh),\ + MI_SF(HUBP0_DCSURF_SURFACE_CONFIG, H_MIRROR_EN, mask_sh),\ + MI_SF(HUBP0_DCSURF_SURFACE_CONFIG, SURFACE_PIXEL_FORMAT, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_PENDING, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_UPDATE_LOCK, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION, PRI_VIEWPORT_WIDTH, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION, PRI_VIEWPORT_HEIGHT, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_START, PRI_VIEWPORT_X_START, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_START, PRI_VIEWPORT_Y_START, mask_sh),\ + MI_SF(HUBP0_DCSURF_SEC_VIEWPORT_DIMENSION, SEC_VIEWPORT_WIDTH, mask_sh),\ + MI_SF(HUBP0_DCSURF_SEC_VIEWPORT_DIMENSION, SEC_VIEWPORT_HEIGHT, mask_sh),\ + MI_SF(HUBP0_DCSURF_SEC_VIEWPORT_START, SEC_VIEWPORT_X_START, mask_sh),\ + MI_SF(HUBP0_DCSURF_SEC_VIEWPORT_START, SEC_VIEWPORT_Y_START, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION_C, PRI_VIEWPORT_WIDTH_C, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION_C, PRI_VIEWPORT_HEIGHT_C, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_START_C, PRI_VIEWPORT_X_START_C, mask_sh),\ + MI_SF(HUBP0_DCSURF_PRI_VIEWPORT_START_C, PRI_VIEWPORT_Y_START_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, PRIMARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS, PRIMARY_SURFACE_ADDRESS, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, SECONDARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SECONDARY_SURFACE_ADDRESS, SECONDARY_SURFACE_ADDRESS, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, PRIMARY_META_SURFACE_ADDRESS_HIGH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_META_SURFACE_ADDRESS, PRIMARY_META_SURFACE_ADDRESS, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH, SECONDARY_META_SURFACE_ADDRESS_HIGH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SECONDARY_META_SURFACE_ADDRESS, SECONDARY_META_SURFACE_ADDRESS, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, PRIMARY_SURFACE_ADDRESS_HIGH_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_SURFACE_ADDRESS_C, PRIMARY_SURFACE_ADDRESS_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C, PRIMARY_META_SURFACE_ADDRESS_HIGH_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_PRIMARY_META_SURFACE_ADDRESS_C, PRIMARY_META_SURFACE_ADDRESS_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_INUSE, SURFACE_INUSE_ADDRESS, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_INUSE_HIGH, SURFACE_INUSE_ADDRESS_HIGH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_INUSE_C, SURFACE_INUSE_ADDRESS_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_INUSE_HIGH_C, SURFACE_INUSE_ADDRESS_HIGH_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE, SURFACE_EARLIEST_INUSE_ADDRESS, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE_HIGH, SURFACE_EARLIEST_INUSE_ADDRESS_HIGH, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE_C, SURFACE_EARLIEST_INUSE_ADDRESS_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C, SURFACE_EARLIEST_INUSE_ADDRESS_HIGH_C, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_TMZ, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_DCC_EN, mask_sh),\ + MI_SF(HUBPREQ0_DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_DCC_IND_64B_BLK, mask_sh),\ + MI_SF(HUBPRET0_HUBPRET_CONTROL, DET_BUF_PLANE1_BASE_ADDRESS, mask_sh),\ + MI_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_CB_B, mask_sh),\ + MI_SF(HUBPRET0_HUBPRET_CONTROL, CROSSBAR_SRC_CR_R, mask_sh),\ + MI_SF(HUBPREQ0_DCN_EXPANSION_MODE, DRQ_EXPANSION_MODE, mask_sh),\ + MI_SF(HUBPREQ0_DCN_EXPANSION_MODE, PRQ_EXPANSION_MODE, mask_sh),\ + MI_SF(HUBPREQ0_DCN_EXPANSION_MODE, MRQ_EXPANSION_MODE, mask_sh),\ + MI_SF(HUBPREQ0_DCN_EXPANSION_MODE, CRQ_EXPANSION_MODE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, CHUNK_SIZE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, MIN_CHUNK_SIZE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, META_CHUNK_SIZE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, MIN_META_CHUNK_SIZE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, DPTE_GROUP_SIZE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, MPTE_GROUP_SIZE, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, SWATH_HEIGHT, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG, PTE_ROW_HEIGHT_LINEAR, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, CHUNK_SIZE_C, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, MIN_CHUNK_SIZE_C, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, META_CHUNK_SIZE_C, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, MIN_META_CHUNK_SIZE_C, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, DPTE_GROUP_SIZE_C, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, MPTE_GROUP_SIZE_C, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, SWATH_HEIGHT_C, mask_sh),\ + MI_SF(HUBP0_DCHUBP_REQ_SIZE_CONFIG_C, PTE_ROW_HEIGHT_LINEAR_C, mask_sh),\ + MI_SF(HUBPREQ0_BLANK_OFFSET_0, REFCYC_H_BLANK_END, mask_sh),\ + MI_SF(HUBPREQ0_BLANK_OFFSET_0, DLG_V_BLANK_END, mask_sh),\ + MI_SF(HUBPREQ0_BLANK_OFFSET_1, MIN_DST_Y_NEXT_START, mask_sh),\ + MI_SF(HUBPREQ0_DST_DIMENSIONS, REFCYC_PER_HTOTAL, mask_sh),\ + MI_SF(HUBPREQ0_DST_AFTER_SCALER, REFCYC_X_AFTER_SCALER, mask_sh),\ + MI_SF(HUBPREQ0_DST_AFTER_SCALER, DST_Y_AFTER_SCALER, mask_sh),\ + MI_SF(HUBPREQ0_VBLANK_PARAMETERS_0, DST_Y_PER_VM_VBLANK, mask_sh),\ + MI_SF(HUBPREQ0_VBLANK_PARAMETERS_0, DST_Y_PER_ROW_VBLANK, mask_sh),\ + MI_SF(HUBPREQ0_REF_FREQ_TO_PIX_FREQ, REF_FREQ_TO_PIX_FREQ, mask_sh),\ + MI_SF(HUBPREQ0_VBLANK_PARAMETERS_1, REFCYC_PER_PTE_GROUP_VBLANK_L, mask_sh),\ + MI_SF(HUBPREQ0_VBLANK_PARAMETERS_3, REFCYC_PER_META_CHUNK_VBLANK_L, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_0, DST_Y_PER_PTE_ROW_NOM_L, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_1, REFCYC_PER_PTE_GROUP_NOM_L, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_4, DST_Y_PER_META_ROW_NOM_L, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_5, REFCYC_PER_META_CHUNK_NOM_L, mask_sh),\ + MI_SF(HUBPREQ0_PER_LINE_DELIVERY_PRE, REFCYC_PER_LINE_DELIVERY_PRE_L, mask_sh),\ + MI_SF(HUBPREQ0_PER_LINE_DELIVERY_PRE, REFCYC_PER_LINE_DELIVERY_PRE_C, mask_sh),\ + MI_SF(HUBPREQ0_PER_LINE_DELIVERY, REFCYC_PER_LINE_DELIVERY_L, mask_sh),\ + MI_SF(HUBPREQ0_PER_LINE_DELIVERY, REFCYC_PER_LINE_DELIVERY_C, mask_sh),\ + MI_SF(HUBPREQ0_VBLANK_PARAMETERS_2, REFCYC_PER_PTE_GROUP_VBLANK_C, mask_sh),\ + MI_SF(HUBPREQ0_VBLANK_PARAMETERS_4, REFCYC_PER_META_CHUNK_VBLANK_C, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_2, DST_Y_PER_PTE_ROW_NOM_C, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_3, REFCYC_PER_PTE_GROUP_NOM_C, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_6, DST_Y_PER_META_ROW_NOM_C, mask_sh),\ + MI_SF(HUBPREQ0_NOM_PARAMETERS_7, REFCYC_PER_META_CHUNK_NOM_C, mask_sh),\ + MI_SF(HUBPREQ0_DCN_TTU_QOS_WM, QoS_LEVEL_LOW_WM, mask_sh),\ + MI_SF(HUBPREQ0_DCN_TTU_QOS_WM, QoS_LEVEL_HIGH_WM, mask_sh),\ + MI_SF(HUBPREQ0_DCN_GLOBAL_TTU_CNTL, MIN_TTU_VBLANK, mask_sh),\ + MI_SF(HUBPREQ0_DCN_GLOBAL_TTU_CNTL, QoS_LEVEL_FLIP, mask_sh),\ + MI_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL0, REFCYC_PER_REQ_DELIVERY, mask_sh),\ + MI_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL0, QoS_LEVEL_FIXED, mask_sh),\ + MI_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL0, QoS_RAMP_DISABLE, mask_sh),\ + MI_SF(HUBPREQ0_DCN_SURF0_TTU_CNTL1, REFCYC_PER_REQ_DELIVERY_PRE, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_MX_L1_TLB_CNTL, ENABLE_L1_TLB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_MX_L1_TLB_CNTL, SYSTEM_ACCESS_MODE, mask_sh) + +#define MI_MASK_SH_LIST_DCN10(mask_sh)\ + MI_MASK_SH_LIST_DCN(mask_sh),\ + MI_SF(HUBPREQ0_PREFETCH_SETTINS, DST_Y_PREFETCH, mask_sh),\ + MI_SF(HUBPREQ0_PREFETCH_SETTINS, VRATIO_PREFETCH, mask_sh),\ + MI_SF(HUBPREQ0_PREFETCH_SETTINS_C, VRATIO_PREFETCH_C, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_MSB, VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_MSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LSB, VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_MSB, VM_CONTEXT0_PAGE_TABLE_START_ADDR_MSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PAGE_TABLE_START_ADDR_LSB, VM_CONTEXT0_PAGE_TABLE_START_ADDR_LSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_MSB, VM_CONTEXT0_PAGE_TABLE_END_ADDR_MSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PAGE_TABLE_END_ADDR_LSB, VM_CONTEXT0_PAGE_TABLE_END_ADDR_LSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB, VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB, VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_SYSTEM, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_LSB, VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_LSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, MC_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, MC_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, MC_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, MC_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, mask_sh),\ + MI_SF(DCHUBBUB_SDPIF_FB_BASE, SDPIF_FB_BASE, mask_sh),\ + MI_SF(DCHUBBUB_SDPIF_FB_OFFSET, SDPIF_FB_OFFSET, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, MC_VM_SYSTEM_APERTURE_DEFAULT_SYSTEM, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, mask_sh),\ + MI_SF(HUBPREQ0_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, mask_sh),\ + MI_SF(HUBPREQ0_CURSOR_SETTINS, CURSOR0_DST_Y_OFFSET, mask_sh), \ + MI_SF(HUBPREQ0_CURSOR_SETTINS, CURSOR0_CHUNK_HDL_ADJUST, mask_sh), \ + MI_SF(CURSOR0_CURSOR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + MI_SF(CURSOR0_CURSOR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + MI_SF(CURSOR0_CURSOR_SIZE, CURSOR_WIDTH, mask_sh), \ + MI_SF(CURSOR0_CURSOR_SIZE, CURSOR_HEIGHT, mask_sh), \ + MI_SF(CURSOR0_CURSOR_CONTROL, CURSOR_MODE, mask_sh), \ + MI_SF(CURSOR0_CURSOR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + MI_SF(CURSOR0_CURSOR_CONTROL, CURSOR_PITCH, mask_sh), \ + MI_SF(CURSOR0_CURSOR_CONTROL, CURSOR_LINES_PER_CHUNK, mask_sh), \ + MI_SF(CURSOR0_CURSOR_CONTROL, CURSOR_ENABLE, mask_sh), \ + MI_SF(CURSOR0_CURSOR_POSITION, CURSOR_X_POSITION, mask_sh), \ + MI_SF(CURSOR0_CURSOR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + MI_SF(CURSOR0_CURSOR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + MI_SF(CURSOR0_CURSOR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + MI_SF(CURSOR0_CURSOR_DST_OFFSET, CURSOR_DST_X_OFFSET, mask_sh) + +#define DCN_MI_REG_FIELD_LIST(type) \ + type HUBP_BLANK_EN;\ + type HUBP_TTU_DISABLE;\ + type HUBP_NO_OUTSTANDING_REQ;\ + type HUBP_UNDERFLOW_STATUS;\ + type NUM_PIPES;\ + type NUM_BANKS;\ + type PIPE_INTERLEAVE;\ + type NUM_SE;\ + type NUM_RB_PER_SE;\ + type MAX_COMPRESSED_FRAGS;\ + type SW_MODE;\ + type META_LINEAR;\ + type RB_ALIGNED;\ + type PIPE_ALIGNED;\ + type PITCH;\ + type META_PITCH;\ + type PITCH_C;\ + type META_PITCH_C;\ + type ROTATION_ANGLE;\ + type H_MIRROR_EN;\ + type SURFACE_PIXEL_FORMAT;\ + type SURFACE_FLIP_TYPE;\ + type SURFACE_UPDATE_LOCK;\ + type SURFACE_FLIP_PENDING;\ + type PRI_VIEWPORT_WIDTH; \ + type PRI_VIEWPORT_HEIGHT; \ + type PRI_VIEWPORT_X_START; \ + type PRI_VIEWPORT_Y_START; \ + type SEC_VIEWPORT_WIDTH; \ + type SEC_VIEWPORT_HEIGHT; \ + type SEC_VIEWPORT_X_START; \ + type SEC_VIEWPORT_Y_START; \ + type PRI_VIEWPORT_WIDTH_C; \ + type PRI_VIEWPORT_HEIGHT_C; \ + type PRI_VIEWPORT_X_START_C; \ + type PRI_VIEWPORT_Y_START_C; \ + type PRIMARY_SURFACE_ADDRESS_HIGH;\ + type PRIMARY_SURFACE_ADDRESS;\ + type SECONDARY_SURFACE_ADDRESS_HIGH;\ + type SECONDARY_SURFACE_ADDRESS;\ + type PRIMARY_META_SURFACE_ADDRESS_HIGH;\ + type PRIMARY_META_SURFACE_ADDRESS;\ + type SECONDARY_META_SURFACE_ADDRESS_HIGH;\ + type SECONDARY_META_SURFACE_ADDRESS;\ + type PRIMARY_SURFACE_ADDRESS_HIGH_C;\ + type PRIMARY_SURFACE_ADDRESS_C;\ + type PRIMARY_META_SURFACE_ADDRESS_HIGH_C;\ + type PRIMARY_META_SURFACE_ADDRESS_C;\ + type SURFACE_INUSE_ADDRESS;\ + type SURFACE_INUSE_ADDRESS_HIGH;\ + type SURFACE_INUSE_ADDRESS_C;\ + type SURFACE_INUSE_ADDRESS_HIGH_C;\ + type SURFACE_EARLIEST_INUSE_ADDRESS;\ + type SURFACE_EARLIEST_INUSE_ADDRESS_HIGH;\ + type SURFACE_EARLIEST_INUSE_ADDRESS_C;\ + type SURFACE_EARLIEST_INUSE_ADDRESS_HIGH_C;\ + type PRIMARY_SURFACE_TMZ;\ + type PRIMARY_SURFACE_DCC_EN;\ + type PRIMARY_SURFACE_DCC_IND_64B_BLK;\ + type DET_BUF_PLANE1_BASE_ADDRESS;\ + type CROSSBAR_SRC_CB_B;\ + type CROSSBAR_SRC_CR_R;\ + type DRQ_EXPANSION_MODE;\ + type PRQ_EXPANSION_MODE;\ + type MRQ_EXPANSION_MODE;\ + type CRQ_EXPANSION_MODE;\ + type CHUNK_SIZE;\ + type MIN_CHUNK_SIZE;\ + type META_CHUNK_SIZE;\ + type MIN_META_CHUNK_SIZE;\ + type DPTE_GROUP_SIZE;\ + type MPTE_GROUP_SIZE;\ + type SWATH_HEIGHT;\ + type PTE_ROW_HEIGHT_LINEAR;\ + type CHUNK_SIZE_C;\ + type MIN_CHUNK_SIZE_C;\ + type META_CHUNK_SIZE_C;\ + type MIN_META_CHUNK_SIZE_C;\ + type DPTE_GROUP_SIZE_C;\ + type MPTE_GROUP_SIZE_C;\ + type SWATH_HEIGHT_C;\ + type PTE_ROW_HEIGHT_LINEAR_C;\ + type REFCYC_H_BLANK_END;\ + type DLG_V_BLANK_END;\ + type MIN_DST_Y_NEXT_START;\ + type REFCYC_PER_HTOTAL;\ + type REFCYC_X_AFTER_SCALER;\ + type DST_Y_AFTER_SCALER;\ + type DST_Y_PREFETCH;\ + type VRATIO_PREFETCH;\ + type DST_Y_PER_VM_VBLANK;\ + type DST_Y_PER_ROW_VBLANK;\ + type REF_FREQ_TO_PIX_FREQ;\ + type REFCYC_PER_PTE_GROUP_VBLANK_L;\ + type REFCYC_PER_META_CHUNK_VBLANK_L;\ + type DST_Y_PER_PTE_ROW_NOM_L;\ + type REFCYC_PER_PTE_GROUP_NOM_L;\ + type DST_Y_PER_META_ROW_NOM_L;\ + type REFCYC_PER_META_CHUNK_NOM_L;\ + type REFCYC_PER_LINE_DELIVERY_PRE_L;\ + type REFCYC_PER_LINE_DELIVERY_PRE_C;\ + type REFCYC_PER_LINE_DELIVERY_L;\ + type REFCYC_PER_LINE_DELIVERY_C;\ + type VRATIO_PREFETCH_C;\ + type REFCYC_PER_PTE_GROUP_VBLANK_C;\ + type REFCYC_PER_META_CHUNK_VBLANK_C;\ + type DST_Y_PER_PTE_ROW_NOM_C;\ + type REFCYC_PER_PTE_GROUP_NOM_C;\ + type DST_Y_PER_META_ROW_NOM_C;\ + type REFCYC_PER_META_CHUNK_NOM_C;\ + type QoS_LEVEL_LOW_WM;\ + type QoS_LEVEL_HIGH_WM;\ + type MIN_TTU_VBLANK;\ + type QoS_LEVEL_FLIP;\ + type REFCYC_PER_REQ_DELIVERY;\ + type QoS_LEVEL_FIXED;\ + type QoS_RAMP_DISABLE;\ + type REFCYC_PER_REQ_DELIVERY_PRE;\ + type VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_MSB;\ + type VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LSB;\ + type VM_CONTEXT0_PAGE_TABLE_START_ADDR_MSB;\ + type VM_CONTEXT0_PAGE_TABLE_START_ADDR_LSB;\ + type VM_CONTEXT0_PAGE_TABLE_END_ADDR_MSB;\ + type VM_CONTEXT0_PAGE_TABLE_END_ADDR_LSB;\ + type VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_MSB;\ + type VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_SYSTEM;\ + type VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR_LSB;\ + type ENABLE_L1_TLB;\ + type SYSTEM_ACCESS_MODE;\ + type MC_VM_SYSTEM_APERTURE_DEFAULT_SYSTEM;\ + type MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB;\ + type MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB;\ + type MC_VM_SYSTEM_APERTURE_LOW_ADDR_MSB;\ + type MC_VM_SYSTEM_APERTURE_LOW_ADDR_LSB;\ + type MC_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB;\ + type MC_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB;\ + type MC_VM_SYSTEM_APERTURE_LOW_ADDR;\ + type MC_VM_SYSTEM_APERTURE_HIGH_ADDR;\ + type SDPIF_FB_TOP;\ + type SDPIF_FB_BASE;\ + type SDPIF_FB_OFFSET;\ + type SDPIF_AGP_BASE;\ + type SDPIF_AGP_BOT;\ + type SDPIF_AGP_TOP;\ + type FB_TOP;\ + type FB_BASE;\ + type FB_OFFSET;\ + type AGP_BASE;\ + type AGP_BOT;\ + type AGP_TOP;\ + /* todo: get these from GVM instead of reading registers ourselves */\ + type PAGE_DIRECTORY_ENTRY_HI32;\ + type PAGE_DIRECTORY_ENTRY_LO32;\ + type LOGICAL_PAGE_NUMBER_HI4;\ + type LOGICAL_PAGE_NUMBER_LO32;\ + type PHYSICAL_PAGE_ADDR_HI4;\ + type PHYSICAL_PAGE_ADDR_LO32;\ + type PHYSICAL_PAGE_NUMBER_MSB;\ + type PHYSICAL_PAGE_NUMBER_LSB;\ + type LOGICAL_ADDR;\ + type CURSOR0_DST_Y_OFFSET; \ + type CURSOR0_CHUNK_HDL_ADJUST; \ + type CURSOR_SURFACE_ADDRESS_HIGH; \ + type CURSOR_SURFACE_ADDRESS; \ + type CURSOR_WIDTH; \ + type CURSOR_HEIGHT; \ + type CURSOR_MODE; \ + type CURSOR_2X_MAGNIFY; \ + type CURSOR_PITCH; \ + type CURSOR_LINES_PER_CHUNK; \ + type CURSOR_ENABLE; \ + type CURSOR_X_POSITION; \ + type CURSOR_Y_POSITION; \ + type CURSOR_HOT_SPOT_X; \ + type CURSOR_HOT_SPOT_Y; \ + type CURSOR_DST_X_OFFSET; \ + type OUTPUT_FP + +struct dcn_mi_shift { + DCN_MI_REG_FIELD_LIST(uint8_t); +}; + +struct dcn_mi_mask { + DCN_MI_REG_FIELD_LIST(uint32_t); +}; + +struct dcn10_hubp { + struct hubp base; + const struct dcn_mi_registers *mi_regs; + const struct dcn_mi_shift *mi_shift; + const struct dcn_mi_mask *mi_mask; +}; + +void hubp1_program_surface_config( + struct hubp *hubp, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror); + +void hubp1_program_deadline( + struct hubp *hubp, + struct _vcs_dpi_display_dlg_regs_st *dlg_attr, + struct _vcs_dpi_display_ttu_regs_st *ttu_attr); + +void hubp1_program_requestor( + struct hubp *hubp, + struct _vcs_dpi_display_rq_regs_st *rq_regs); + +void hubp1_program_pixel_format( + struct dcn10_hubp *hubp, + enum surface_pixel_format format); + +void hubp1_program_size_and_rotation( + struct dcn10_hubp *hubp, + enum dc_rotation_angle rotation, + enum surface_pixel_format format, + const union plane_size *plane_size, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror); + +void hubp1_program_tiling( + struct dcn10_hubp *hubp, + const union dc_tiling_info *info, + const enum surface_pixel_format pixel_format); + +void hubp1_dcc_control(struct hubp *hubp, + bool enable, + bool independent_64b_blks); + +bool hubp1_program_surface_flip_and_addr( + struct hubp *hubp, + const struct dc_plane_address *address, + bool flip_immediate); + +bool hubp1_is_flip_pending(struct hubp *hubp); + +void hubp1_cursor_set_attributes( + struct hubp *hubp, + const struct dc_cursor_attributes *attr); + +void hubp1_cursor_set_position( + struct hubp *hubp, + const struct dc_cursor_position *pos, + const struct dc_cursor_mi_param *param); + +void hubp1_set_blank(struct hubp *hubp, bool blank); + +void min_set_viewport(struct hubp *hubp, + const struct rect *viewport, + const struct rect *viewport_c); + +void dcn10_hubp_construct( + struct dcn10_hubp *hubp1, + struct dc_context *ctx, + uint32_t inst, + const struct dcn_mi_registers *mi_regs, + const struct dcn_mi_shift *mi_shift, + const struct dcn_mi_mask *mi_mask); + + +struct dcn_hubp_state { + uint32_t pixel_format; + uint32_t inuse_addr_hi; + uint32_t viewport_width; + uint32_t viewport_height; + uint32_t rotation_angle; + uint32_t h_mirror_en; + uint32_t sw_mode; + uint32_t dcc_en; + uint32_t blank_en; + uint32_t underflow_status; + uint32_t ttu_disable; + uint32_t min_ttu_vblank; + uint32_t qos_level_low_wm; + uint32_t qos_level_high_wm; +}; +void hubp1_read_state(struct dcn10_hubp *hubp1, + struct dcn_hubp_state *s); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c new file mode 100644 index 000000000000..961ad5c3b454 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -0,0 +1,2958 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "core_types.h" +#include "resource.h" +#include "custom_float.h" +#include "dcn10_hw_sequencer.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dce/dce_hwseq.h" +#include "abm.h" +#include "dcn10/dcn10_timing_generator.h" +#include "dcn10/dcn10_dpp.h" +#include "dcn10/dcn10_mpc.h" +#include "timing_generator.h" +#include "opp.h" +#include "ipp.h" +#include "mpc.h" +#include "reg_helper.h" +#include "custom_float.h" +#include "dcn10_hubp.h" + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +static void log_mpc_crc(struct dc *dc) +{ + struct dc_context *dc_ctx = dc->ctx; + struct dce_hwseq *hws = dc->hwseq; + + if (REG(MPC_CRC_RESULT_GB)) + DTN_INFO("MPC_CRC_RESULT_GB:%d MPC_CRC_RESULT_C:%d MPC_CRC_RESULT_AR:%d\n", + REG_READ(MPC_CRC_RESULT_GB), REG_READ(MPC_CRC_RESULT_C), REG_READ(MPC_CRC_RESULT_AR)); + if (REG(DPP_TOP0_DPP_CRC_VAL_B_A)) + DTN_INFO("DPP_TOP0_DPP_CRC_VAL_B_A:%d DPP_TOP0_DPP_CRC_VAL_R_G:%d\n", + REG_READ(DPP_TOP0_DPP_CRC_VAL_B_A), REG_READ(DPP_TOP0_DPP_CRC_VAL_R_G)); +} + +void print_microsec(struct dc_context *dc_ctx, uint32_t ref_cycle) +{ + static const uint32_t ref_clk_mhz = 48; + static const unsigned int frac = 10; + uint32_t us_x10 = (ref_cycle * frac) / ref_clk_mhz; + + DTN_INFO("%d.%d \t ", + us_x10 / frac, + us_x10 % frac); +} + +#define DTN_INFO_MICRO_SEC(ref_cycle) \ + print_microsec(dc_ctx, ref_cycle) + +struct dcn_hubbub_wm_set { + uint32_t wm_set; + uint32_t data_urgent; + uint32_t pte_meta_urgent; + uint32_t sr_enter; + uint32_t sr_exit; + uint32_t dram_clk_chanage; +}; + +struct dcn_hubbub_wm { + struct dcn_hubbub_wm_set sets[4]; +}; + +static void dcn10_hubbub_wm_read_state(struct dce_hwseq *hws, + struct dcn_hubbub_wm *wm) +{ + struct dcn_hubbub_wm_set *s; + + s = &wm->sets[0]; + s->wm_set = 0; + s->data_urgent = REG_READ(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A); + s->pte_meta_urgent = REG_READ(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_A); + s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A); + s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A); + s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A); + + s = &wm->sets[1]; + s->wm_set = 1; + s->data_urgent = REG_READ(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B); + s->pte_meta_urgent = REG_READ(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_B); + s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B); + s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B); + s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B); + + s = &wm->sets[2]; + s->wm_set = 2; + s->data_urgent = REG_READ(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C); + s->pte_meta_urgent = REG_READ(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_C); + s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C); + s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C); + s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C); + + s = &wm->sets[3]; + s->wm_set = 3; + s->data_urgent = REG_READ(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D); + s->pte_meta_urgent = REG_READ(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_D); + s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D); + s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D); + s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D); +} + +static void dcn10_log_hubbub_state(struct dc *dc) +{ + struct dc_context *dc_ctx = dc->ctx; + struct dcn_hubbub_wm wm; + int i; + + dcn10_hubbub_wm_read_state(dc->hwseq, &wm); + + DTN_INFO("HUBBUB WM: \t data_urgent \t pte_meta_urgent \t " + "sr_enter \t sr_exit \t dram_clk_change \n"); + + for (i = 0; i < 4; i++) { + struct dcn_hubbub_wm_set *s; + + s = &wm.sets[i]; + DTN_INFO("WM_Set[%d]:\t ", s->wm_set); + DTN_INFO_MICRO_SEC(s->data_urgent); + DTN_INFO_MICRO_SEC(s->pte_meta_urgent); + DTN_INFO_MICRO_SEC(s->sr_enter); + DTN_INFO_MICRO_SEC(s->sr_exit); + DTN_INFO_MICRO_SEC(s->dram_clk_chanage); + DTN_INFO("\n"); + } + + DTN_INFO("\n"); +} + +static void dcn10_log_hw_state(struct dc *dc) +{ + struct dc_context *dc_ctx = dc->ctx; + struct resource_pool *pool = dc->res_pool; + int i; + + DTN_INFO_BEGIN(); + + dcn10_log_hubbub_state(dc); + + DTN_INFO("HUBP:\t format \t addr_hi \t width \t height \t " + "rotation \t mirror \t sw_mode \t " + "dcc_en \t blank_en \t ttu_dis \t underflow \t " + "min_ttu_vblank \t qos_low_wm \t qos_high_wm \n"); + + for (i = 0; i < pool->pipe_count; i++) { + struct hubp *hubp = pool->hubps[i]; + struct dcn_hubp_state s; + + hubp1_read_state(TO_DCN10_HUBP(hubp), &s); + + DTN_INFO("[%d]:\t %xh \t %xh \t %d \t %d \t " + "%xh \t %xh \t %xh \t " + "%d \t %d \t %d \t %xh \t", + i, + s.pixel_format, + s.inuse_addr_hi, + s.viewport_width, + s.viewport_height, + s.rotation_angle, + s.h_mirror_en, + s.sw_mode, + s.dcc_en, + s.blank_en, + s.ttu_disable, + s.underflow_status); + DTN_INFO_MICRO_SEC(s.min_ttu_vblank); + DTN_INFO_MICRO_SEC(s.qos_level_low_wm); + DTN_INFO_MICRO_SEC(s.qos_level_high_wm); + DTN_INFO("\n"); + } + DTN_INFO("\n"); + + DTN_INFO("OTG:\t v_bs \t v_be \t v_ss \t v_se \t vpol \t vmax \t vmin \t " + "h_bs \t h_be \t h_ss \t h_se \t hpol \t htot \t vtot \t underflow\n"); + + for (i = 0; i < pool->res_cap->num_timing_generator; i++) { + struct timing_generator *tg = pool->timing_generators[i]; + struct dcn_otg_state s = {0}; + + tgn10_read_otg_state(DCN10TG_FROM_TG(tg), &s); + + //only print if OTG master is enabled + if ((s.otg_enabled & 1) == 0) + continue; + + DTN_INFO("[%d]:\t %d \t %d \t %d \t %d \t " + "%d \t %d \t %d \t %d \t %d \t %d \t " + "%d \t %d \t %d \t %d \t %d \t ", + i, + s.v_blank_start, + s.v_blank_end, + s.v_sync_a_start, + s.v_sync_a_end, + s.v_sync_a_pol, + s.v_total_max, + s.v_total_min, + s.h_blank_start, + s.h_blank_end, + s.h_sync_a_start, + s.h_sync_a_end, + s.h_sync_a_pol, + s.h_total, + s.v_total, + s.underflow_occurred_status); + DTN_INFO("\n"); + } + DTN_INFO("\n"); + + log_mpc_crc(dc); + + DTN_INFO_END(); +} + +static void verify_allow_pstate_change_high( + struct dce_hwseq *hws) +{ + /* pstate latency is ~20us so if we wait over 40us and pstate allow + * still not asserted, we are probably stuck and going to hang + * + * TODO: Figure out why it takes ~100us on linux + * pstate takes around ~100us on linux. Unknown currently as to + * why it takes that long on linux + */ + static unsigned int pstate_wait_timeout_us = 200; + static unsigned int pstate_wait_expected_timeout_us = 40; + static unsigned int max_sampled_pstate_wait_us; /* data collection */ + static bool forced_pstate_allow; /* help with revert wa */ + static bool should_log_hw_state; /* prevent hw state log by default */ + + unsigned int debug_index = 0x7; + unsigned int debug_data; + unsigned int i; + + if (forced_pstate_allow) { + /* we hacked to force pstate allow to prevent hang last time + * we verify_allow_pstate_change_high. so disable force + * here so we can check status + */ + REG_UPDATE_2(DCHUBBUB_ARB_DRAM_STATE_CNTL, + DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_VALUE, 0, + DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_ENABLE, 0); + forced_pstate_allow = false; + } + + /* description "3-0: Pipe0 cursor0 QOS + * 7-4: Pipe1 cursor0 QOS + * 11-8: Pipe2 cursor0 QOS + * 15-12: Pipe3 cursor0 QOS + * 16: Pipe0 Plane0 Allow Pstate Change + * 17: Pipe1 Plane0 Allow Pstate Change + * 18: Pipe2 Plane0 Allow Pstate Change + * 19: Pipe3 Plane0 Allow Pstate Change + * 20: Pipe0 Plane1 Allow Pstate Change + * 21: Pipe1 Plane1 Allow Pstate Change + * 22: Pipe2 Plane1 Allow Pstate Change + * 23: Pipe3 Plane1 Allow Pstate Change + * 24: Pipe0 cursor0 Allow Pstate Change + * 25: Pipe1 cursor0 Allow Pstate Change + * 26: Pipe2 cursor0 Allow Pstate Change + * 27: Pipe3 cursor0 Allow Pstate Change + * 28: WB0 Allow Pstate Change + * 29: WB1 Allow Pstate Change + * 30: Arbiter's allow_pstate_change + * 31: SOC pstate change request + */ + + REG_WRITE(DCHUBBUB_TEST_DEBUG_INDEX, debug_index); + + for (i = 0; i < pstate_wait_timeout_us; i++) { + debug_data = REG_READ(DCHUBBUB_TEST_DEBUG_DATA); + + if (debug_data & (1 << 30)) { + + if (i > pstate_wait_expected_timeout_us) + dm_logger_write(hws->ctx->logger, LOG_WARNING, + "pstate took longer than expected ~%dus\n", + i); + + return; + } + if (max_sampled_pstate_wait_us < i) + max_sampled_pstate_wait_us = i; + + udelay(1); + } + + /* force pstate allow to prevent system hang + * and break to debugger to investigate + */ + REG_UPDATE_2(DCHUBBUB_ARB_DRAM_STATE_CNTL, + DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_VALUE, 1, + DCHUBBUB_ARB_ALLOW_PSTATE_CHANGE_FORCE_ENABLE, 1); + forced_pstate_allow = true; + + if (should_log_hw_state) { + dcn10_log_hw_state(hws->ctx->dc); + } + + dm_logger_write(hws->ctx->logger, LOG_WARNING, + "pstate TEST_DEBUG_DATA: 0x%X\n", + debug_data); + BREAK_TO_DEBUGGER(); +} + +static void enable_dppclk( + struct dce_hwseq *hws, + uint8_t plane_id, + uint32_t requested_pix_clk, + bool dppclk_div) +{ + dm_logger_write(hws->ctx->logger, LOG_SURFACE, + "dppclk_rate_control for pipe %d programed to %d\n", + plane_id, + dppclk_div); + + if (hws->shifts->DPPCLK_RATE_CONTROL) + REG_UPDATE_2(DPP_CONTROL[plane_id], + DPPCLK_RATE_CONTROL, dppclk_div, + DPP_CLOCK_ENABLE, 1); + else + REG_UPDATE(DPP_CONTROL[plane_id], + DPP_CLOCK_ENABLE, 1); +} + +static void enable_power_gating_plane( + struct dce_hwseq *hws, + bool enable) +{ + bool force_on = 1; /* disable power gating */ + + if (enable) + force_on = 0; + + /* DCHUBP0/1/2/3 */ + REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, force_on); + REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, force_on); + REG_UPDATE(DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, force_on); + REG_UPDATE(DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, force_on); + + /* DPP0/1/2/3 */ + REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, force_on); + REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, force_on); + REG_UPDATE(DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, force_on); + REG_UPDATE(DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, force_on); +} + +static void disable_vga( + struct dce_hwseq *hws) +{ + REG_WRITE(D1VGA_CONTROL, 0); + REG_WRITE(D2VGA_CONTROL, 0); + REG_WRITE(D3VGA_CONTROL, 0); + REG_WRITE(D4VGA_CONTROL, 0); +} + +static void dpp_pg_control( + struct dce_hwseq *hws, + unsigned int dpp_inst, + bool power_on) +{ + uint32_t power_gate = power_on ? 0 : 1; + uint32_t pwr_status = power_on ? 0 : 2; + + if (hws->ctx->dc->debug.disable_dpp_power_gate) + return; + + switch (dpp_inst) { + case 0: /* DPP0 */ + REG_UPDATE(DOMAIN1_PG_CONFIG, + DOMAIN1_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN1_PG_STATUS, + DOMAIN1_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 1: /* DPP1 */ + REG_UPDATE(DOMAIN3_PG_CONFIG, + DOMAIN3_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN3_PG_STATUS, + DOMAIN3_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 2: /* DPP2 */ + REG_UPDATE(DOMAIN5_PG_CONFIG, + DOMAIN5_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN5_PG_STATUS, + DOMAIN5_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 3: /* DPP3 */ + REG_UPDATE(DOMAIN7_PG_CONFIG, + DOMAIN7_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN7_PG_STATUS, + DOMAIN7_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} + +static uint32_t convert_and_clamp( + uint32_t wm_ns, + uint32_t refclk_mhz, + uint32_t clamp_value) +{ + uint32_t ret_val = 0; + ret_val = wm_ns * refclk_mhz; + ret_val /= 1000; + + if (ret_val > clamp_value) + ret_val = clamp_value; + + return ret_val; +} + +static void program_watermarks( + struct dce_hwseq *hws, + struct dcn_watermark_set *watermarks, + unsigned int refclk_mhz) +{ + uint32_t force_en = hws->ctx->dc->debug.disable_stutter ? 1 : 0; + /* + * Need to clamp to max of the register values (i.e. no wrap) + * for dcn1, all wm registers are 21-bit wide + */ + uint32_t prog_wm_value; + + REG_UPDATE(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, + DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST, 0); + + /* Repeat for water mark set A, B, C and D. */ + /* clock state A */ + prog_wm_value = convert_and_clamp(watermarks->a.urgent_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, prog_wm_value); + + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "URGENCY_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->a.urgent_ns, prog_wm_value); + + prog_wm_value = convert_and_clamp(watermarks->a.pte_meta_urgent_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_A, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "PTE_META_URGENCY_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->a.pte_meta_urgent_ns, prog_wm_value); + + if (REG(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A)) { + prog_wm_value = convert_and_clamp( + watermarks->a.cstate_pstate.cstate_enter_plus_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_ENTER_EXIT_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->a.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value); + + + prog_wm_value = convert_and_clamp( + watermarks->a.cstate_pstate.cstate_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_EXIT_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->a.cstate_pstate.cstate_exit_ns, prog_wm_value); + } + + prog_wm_value = convert_and_clamp( + watermarks->a.cstate_pstate.pstate_change_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "DRAM_CLK_CHANGE_WATERMARK_A calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->a.cstate_pstate.pstate_change_ns, prog_wm_value); + + + /* clock state B */ + prog_wm_value = convert_and_clamp( + watermarks->b.urgent_ns, refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "URGENCY_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->b.urgent_ns, prog_wm_value); + + + prog_wm_value = convert_and_clamp( + watermarks->b.pte_meta_urgent_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_B, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "PTE_META_URGENCY_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->b.pte_meta_urgent_ns, prog_wm_value); + + + if (REG(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B)) { + prog_wm_value = convert_and_clamp( + watermarks->b.cstate_pstate.cstate_enter_plus_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_ENTER_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->b.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value); + + + prog_wm_value = convert_and_clamp( + watermarks->b.cstate_pstate.cstate_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_EXIT_WATERMARK_B calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->b.cstate_pstate.cstate_exit_ns, prog_wm_value); + } + + prog_wm_value = convert_and_clamp( + watermarks->b.cstate_pstate.pstate_change_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "DRAM_CLK_CHANGE_WATERMARK_B calculated =%d\n\n" + "HW register value = 0x%x\n", + watermarks->b.cstate_pstate.pstate_change_ns, prog_wm_value); + + /* clock state C */ + prog_wm_value = convert_and_clamp( + watermarks->c.urgent_ns, refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "URGENCY_WATERMARK_C calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->c.urgent_ns, prog_wm_value); + + + prog_wm_value = convert_and_clamp( + watermarks->c.pte_meta_urgent_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_C, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "PTE_META_URGENCY_WATERMARK_C calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->c.pte_meta_urgent_ns, prog_wm_value); + + + if (REG(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C)) { + prog_wm_value = convert_and_clamp( + watermarks->c.cstate_pstate.cstate_enter_plus_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_ENTER_WATERMARK_C calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->c.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value); + + + prog_wm_value = convert_and_clamp( + watermarks->c.cstate_pstate.cstate_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_EXIT_WATERMARK_C calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->c.cstate_pstate.cstate_exit_ns, prog_wm_value); + } + + prog_wm_value = convert_and_clamp( + watermarks->c.cstate_pstate.pstate_change_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "DRAM_CLK_CHANGE_WATERMARK_C calculated =%d\n\n" + "HW register value = 0x%x\n", + watermarks->c.cstate_pstate.pstate_change_ns, prog_wm_value); + + /* clock state D */ + prog_wm_value = convert_and_clamp( + watermarks->d.urgent_ns, refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "URGENCY_WATERMARK_D calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->d.urgent_ns, prog_wm_value); + + prog_wm_value = convert_and_clamp( + watermarks->d.pte_meta_urgent_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_PTE_META_URGENCY_WATERMARK_D, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "PTE_META_URGENCY_WATERMARK_D calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->d.pte_meta_urgent_ns, prog_wm_value); + + + if (REG(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D)) { + prog_wm_value = convert_and_clamp( + watermarks->d.cstate_pstate.cstate_enter_plus_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_ENTER_WATERMARK_D calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->d.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value); + + + prog_wm_value = convert_and_clamp( + watermarks->d.cstate_pstate.cstate_exit_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "SR_EXIT_WATERMARK_D calculated =%d\n" + "HW register value = 0x%x\n", + watermarks->d.cstate_pstate.cstate_exit_ns, prog_wm_value); + } + + + prog_wm_value = convert_and_clamp( + watermarks->d.cstate_pstate.pstate_change_ns, + refclk_mhz, 0x1fffff); + REG_WRITE(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, prog_wm_value); + dm_logger_write(hws->ctx->logger, LOG_BANDWIDTH_CALCS, + "DRAM_CLK_CHANGE_WATERMARK_D calculated =%d\n" + "HW register value = 0x%x\n\n", + watermarks->d.cstate_pstate.pstate_change_ns, prog_wm_value); + + REG_UPDATE(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, + DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST, 1); + + REG_UPDATE(DCHUBBUB_ARB_SAT_LEVEL, + DCHUBBUB_ARB_SAT_LEVEL, 60 * refclk_mhz); + REG_UPDATE(DCHUBBUB_ARB_DF_REQ_OUTSTAND, + DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 68); + + REG_UPDATE_2(DCHUBBUB_ARB_DRAM_STATE_CNTL, + DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_VALUE, 0, + DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE, force_en); + +#if 0 + REG_UPDATE_2(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, + DCHUBBUB_ARB_WATERMARK_CHANGE_DONE_INTERRUPT_DISABLE, 1, + DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST, 1); +#endif +} + + +static void dcn10_update_dchub( + struct dce_hwseq *hws, + struct dchub_init_data *dh_data) +{ + /* TODO: port code from dal2 */ + switch (dh_data->fb_mode) { + case FRAME_BUFFER_MODE_ZFB_ONLY: + /*For ZFB case need to put DCHUB FB BASE and TOP upside down to indicate ZFB mode*/ + REG_UPDATE(DCHUBBUB_SDPIF_FB_TOP, + SDPIF_FB_TOP, 0); + + REG_UPDATE(DCHUBBUB_SDPIF_FB_BASE, + SDPIF_FB_BASE, 0x0FFFF); + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_BASE, + SDPIF_AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_BOT, + SDPIF_AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_TOP, + SDPIF_AGP_TOP, (dh_data->zfb_mc_base_addr + + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_MIXED_ZFB_AND_LOCAL: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_BASE, + SDPIF_AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_BOT, + SDPIF_AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_TOP, + SDPIF_AGP_TOP, (dh_data->zfb_mc_base_addr + + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_LOCAL_ONLY: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + REG_UPDATE(DCHUBBUB_SDPIF_AGP_BASE, + SDPIF_AGP_BASE, 0); + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_BOT, + SDPIF_AGP_BOT, 0X03FFFF); + + REG_UPDATE(DCHUBBUB_SDPIF_AGP_TOP, + SDPIF_AGP_TOP, 0); + break; + default: + break; + } + + dh_data->dchub_initialzied = true; + dh_data->dchub_info_valid = false; +} + +static void hubp_pg_control( + struct dce_hwseq *hws, + unsigned int hubp_inst, + bool power_on) +{ + uint32_t power_gate = power_on ? 0 : 1; + uint32_t pwr_status = power_on ? 0 : 2; + + if (hws->ctx->dc->debug.disable_hubp_power_gate) + return; + + switch (hubp_inst) { + case 0: /* DCHUBP0 */ + REG_UPDATE(DOMAIN0_PG_CONFIG, + DOMAIN0_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN0_PG_STATUS, + DOMAIN0_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 1: /* DCHUBP1 */ + REG_UPDATE(DOMAIN2_PG_CONFIG, + DOMAIN2_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN2_PG_STATUS, + DOMAIN2_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 2: /* DCHUBP2 */ + REG_UPDATE(DOMAIN4_PG_CONFIG, + DOMAIN4_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN4_PG_STATUS, + DOMAIN4_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + case 3: /* DCHUBP3 */ + REG_UPDATE(DOMAIN6_PG_CONFIG, + DOMAIN6_POWER_GATE, power_gate); + + REG_WAIT(DOMAIN6_PG_STATUS, + DOMAIN6_PGFSM_PWR_STATUS, pwr_status, + 1, 1000); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} + +static void power_on_plane( + struct dce_hwseq *hws, + int plane_id) +{ + if (REG(DC_IP_REQUEST_CNTL)) { + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 1); + dpp_pg_control(hws, plane_id, true); + hubp_pg_control(hws, plane_id, true); + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 0); + dm_logger_write(hws->ctx->logger, LOG_DEBUG, + "Un-gated front end for pipe %d\n", plane_id); + } +} + +static void undo_DEGVIDCN10_253_wa(struct dc *dc) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubp *hubp = dc->res_pool->hubps[0]; + int pwr_status = 0; + + REG_GET(DOMAIN0_PG_STATUS, DOMAIN0_PGFSM_PWR_STATUS, &pwr_status); + /* Don't need to blank if hubp is power gated*/ + if (pwr_status == 2) + return; + + hubp->funcs->set_blank(hubp, true); + + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 1); + + hubp_pg_control(hws, 0, false); + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 0); +} + +static void apply_DEGVIDCN10_253_wa(struct dc *dc) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubp *hubp = dc->res_pool->hubps[0]; + + if (dc->debug.disable_stutter) + return; + + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 1); + + hubp_pg_control(hws, 0, true); + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 0); + + hubp->funcs->set_hubp_blank_en(hubp, false); +} + +static void bios_golden_init(struct dc *dc) +{ + struct dc_bios *bp = dc->ctx->dc_bios; + int i; + + /* initialize dcn global */ + bp->funcs->enable_disp_power_gating(bp, + CONTROLLER_ID_D0, ASIC_PIPE_INIT); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + /* initialize dcn per pipe */ + bp->funcs->enable_disp_power_gating(bp, + CONTROLLER_ID_D0 + i, ASIC_PIPE_DISABLE); + } +} + +static void dcn10_init_hw(struct dc *dc) +{ + int i; + struct abm *abm = dc->res_pool->abm; + struct dce_hwseq *hws = dc->hwseq; + + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + REG_WRITE(REFCLK_CNTL, 0); + REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1); + REG_WRITE(DIO_MEM_PWR_CTRL, 0); + + if (!dc->debug.disable_clock_gate) { + /* enable all DCN clock gating */ + REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); + + REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0); + + REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); + } + + enable_power_gating_plane(dc->hwseq, true); + return; + } + /* end of FPGA. Below if real ASIC */ + + bios_golden_init(dc); + + disable_vga(dc->hwseq); + + for (i = 0; i < dc->link_count; i++) { + /* Power up AND update implementation according to the + * required signal (which may be different from the + * default signal on connector). + */ + struct dc_link *link = dc->links[i]; + + link->link_enc->funcs->hw_init(link->link_enc); + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct dpp *dpp = dc->res_pool->dpps[i]; + struct timing_generator *tg = dc->res_pool->timing_generators[i]; + + dpp->funcs->dpp_reset(dpp); + dc->res_pool->mpc->funcs->remove( + dc->res_pool->mpc, &(dc->res_pool->opps[i]->mpc_tree), + dc->res_pool->opps[i]->inst, i); + + /* Blank controller using driver code instead of + * command table. + */ + tg->funcs->set_blank(tg, true); + hwss_wait_for_blank_complete(tg); + } + + for (i = 0; i < dc->res_pool->audio_count; i++) { + struct audio *audio = dc->res_pool->audios[i]; + + audio->funcs->hw_init(audio); + } + + if (abm != NULL) { + abm->funcs->init_backlight(abm); + abm->funcs->abm_init(abm); + } + + /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ + REG_WRITE(DIO_MEM_PWR_CTRL, 0); + + if (!dc->debug.disable_clock_gate) { + /* enable all DCN clock gating */ + REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); + + REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0); + + REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); + } + + enable_power_gating_plane(dc->hwseq, true); +} + +static enum dc_status dcn10_prog_pixclk_crtc_otg( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + enum dc_color_space color_space; + struct tg_color black_color = {0}; + bool enableStereo = stream->timing.timing_3d_format == TIMING_3D_FORMAT_NONE ? + false:true; + bool rightEyePolarity = stream->timing.flags.RIGHT_EYE_3D_POLARITY; + + + /* by upper caller loop, pipe0 is parent pipe and be called first. + * back end is set up by for pipe0. Other children pipe share back end + * with pipe 0. No program is needed. + */ + if (pipe_ctx->top_pipe != NULL) + return DC_OK; + + /* TODO check if timing_changed, disable stream if timing changed */ + + /* HW program guide assume display already disable + * by unplug sequence. OTG assume stop. + */ + pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, true); + + if (false == pipe_ctx->clock_source->funcs->program_pix_clk( + pipe_ctx->clock_source, + &pipe_ctx->stream_res.pix_clk_params, + &pipe_ctx->pll_settings)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + pipe_ctx->stream_res.tg->dlg_otg_param.vready_offset = pipe_ctx->pipe_dlg_param.vready_offset; + pipe_ctx->stream_res.tg->dlg_otg_param.vstartup_start = pipe_ctx->pipe_dlg_param.vstartup_start; + pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_offset = pipe_ctx->pipe_dlg_param.vupdate_offset; + pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_width = pipe_ctx->pipe_dlg_param.vupdate_width; + + pipe_ctx->stream_res.tg->dlg_otg_param.signal = pipe_ctx->stream->signal; + + pipe_ctx->stream_res.tg->funcs->program_timing( + pipe_ctx->stream_res.tg, + &stream->timing, + true); + + pipe_ctx->stream_res.opp->funcs->opp_set_stereo_polarity( + pipe_ctx->stream_res.opp, + enableStereo, + rightEyePolarity); + +#if 0 /* move to after enable_crtc */ + /* TODO: OPP FMT, ABM. etc. should be done here. */ + /* or FPGA now. instance 0 only. TODO: move to opp.c */ + + inst_offset = reg_offsets[pipe_ctx->stream_res.tg->inst].fmt; + + pipe_ctx->stream_res.opp->funcs->opp_program_fmt( + pipe_ctx->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); +#endif + /* program otg blank color */ + color_space = stream->output_color_space; + color_space_to_black_color(dc, color_space, &black_color); + pipe_ctx->stream_res.tg->funcs->set_blank_color( + pipe_ctx->stream_res.tg, + &black_color); + + pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true); + hwss_wait_for_blank_complete(pipe_ctx->stream_res.tg); + + /* VTG is within DCHUB command block. DCFCLK is always on */ + if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + /* TODO program crtc source select for non-virtual signal*/ + /* TODO program FMT */ + /* TODO setup link_enc */ + /* TODO set stream attributes */ + /* TODO program audio */ + /* TODO enable stream if timing changed */ + /* TODO unblank stream if DP */ + + return DC_OK; +} + +static void reset_back_end_for_pipe( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context) +{ + int i; + + if (pipe_ctx->stream_res.stream_enc == NULL) { + pipe_ctx->stream = NULL; + return; + } + + if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + /* DPMS may already disable */ + if (!pipe_ctx->stream->dpms_off) + core_link_disable_stream(pipe_ctx, FREE_ACQUIRED_RESOURCE); + } + + /* by upper caller loop, parent pipe: pipe0, will be reset last. + * back end share by all pipes and will be disable only when disable + * parent pipe. + */ + if (pipe_ctx->top_pipe == NULL) { + pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); + + pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) + if (&dc->current_state->res_ctx.pipe_ctx[i] == pipe_ctx) + break; + + if (i == dc->res_pool->pipe_count) + return; + + pipe_ctx->stream = NULL; + dm_logger_write(dc->ctx->logger, LOG_DEBUG, + "Reset back end for pipe %d, tg:%d\n", + pipe_ctx->pipe_idx, pipe_ctx->stream_res.tg->inst); +} + +/* trigger HW to start disconnect plane from stream on the next vsync */ +static void plane_atomic_disconnect(struct dc *dc, + int fe_idx) +{ + struct hubp *hubp = dc->res_pool->hubps[fe_idx]; + struct mpc *mpc = dc->res_pool->mpc; + int opp_id, z_idx; + int mpcc_id = -1; + + /* look at tree rather than mi here to know if we already reset */ + for (opp_id = 0; opp_id < dc->res_pool->pipe_count; opp_id++) { + struct output_pixel_processor *opp = dc->res_pool->opps[opp_id]; + + for (z_idx = 0; z_idx < opp->mpc_tree.num_pipes; z_idx++) { + if (opp->mpc_tree.dpp[z_idx] == fe_idx) { + mpcc_id = opp->mpc_tree.mpcc[z_idx]; + break; + } + } + if (mpcc_id != -1) + break; + } + /*Already reset*/ + if (opp_id == dc->res_pool->pipe_count) + return; + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + hubp->funcs->dcc_control(hubp, false, false); + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + + mpc->funcs->remove(mpc, &(dc->res_pool->opps[opp_id]->mpc_tree), + dc->res_pool->opps[opp_id]->inst, fe_idx); +} + +/* disable HW used by plane. + * note: cannot disable until disconnect is complete */ +static void plane_atomic_disable(struct dc *dc, + int fe_idx) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubp *hubp = dc->res_pool->hubps[fe_idx]; + struct mpc *mpc = dc->res_pool->mpc; + int opp_id = hubp->opp_id; + + if (opp_id == 0xf) + return; + + mpc->funcs->wait_for_idle(mpc, hubp->mpcc_id); + dc->res_pool->opps[hubp->opp_id]->mpcc_disconnect_pending[hubp->mpcc_id] = false; + /*dm_logger_write(dc->ctx->logger, LOG_ERROR, + "[debug_mpo: atomic disable finished on mpcc %d]\n", + fe_idx);*/ + + hubp->funcs->set_blank(hubp, true); + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + + REG_UPDATE(HUBP_CLK_CNTL[fe_idx], + HUBP_CLOCK_ENABLE, 0); + REG_UPDATE(DPP_CONTROL[fe_idx], + DPP_CLOCK_ENABLE, 0); + + if (dc->res_pool->opps[opp_id]->mpc_tree.num_pipes == 0) + REG_UPDATE(OPP_PIPE_CONTROL[opp_id], + OPP_PIPE_CLOCK_EN, 0); + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); +} + +/* + * kill power to plane hw + * note: cannot power down until plane is disable + */ +static void plane_atomic_power_down(struct dc *dc, int fe_idx) +{ + struct dce_hwseq *hws = dc->hwseq; + struct dpp *dpp = dc->res_pool->dpps[fe_idx]; + + if (REG(DC_IP_REQUEST_CNTL)) { + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 1); + dpp_pg_control(hws, fe_idx, false); + hubp_pg_control(hws, fe_idx, false); + dpp->funcs->dpp_reset(dpp); + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 0); + dm_logger_write(dc->ctx->logger, LOG_DEBUG, + "Power gated front end %d\n", fe_idx); + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + } +} + + +static void reset_front_end( + struct dc *dc, + int fe_idx) +{ + struct dce_hwseq *hws = dc->hwseq; + struct timing_generator *tg; + int opp_id = dc->res_pool->hubps[fe_idx]->opp_id; + + /*Already reset*/ + if (opp_id == 0xf) + return; + + tg = dc->res_pool->timing_generators[opp_id]; + tg->funcs->lock(tg); + + plane_atomic_disconnect(dc, fe_idx); + + REG_UPDATE(OTG_GLOBAL_SYNC_STATUS[tg->inst], VUPDATE_NO_LOCK_EVENT_CLEAR, 1); + tg->funcs->unlock(tg); + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(hws); + + if (tg->ctx->dce_environment != DCE_ENV_FPGA_MAXIMUS) + REG_WAIT(OTG_GLOBAL_SYNC_STATUS[tg->inst], + VUPDATE_NO_LOCK_EVENT_OCCURRED, 1, + 1, 100000); + + plane_atomic_disable(dc, fe_idx); + + dm_logger_write(dc->ctx->logger, LOG_DC, + "Reset front end %d\n", + fe_idx); +} + +static void dcn10_power_down_fe(struct dc *dc, int fe_idx) +{ + struct dce_hwseq *hws = dc->hwseq; + struct dpp *dpp = dc->res_pool->dpps[fe_idx]; + + reset_front_end(dc, fe_idx); + + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 1); + dpp_pg_control(hws, fe_idx, false); + hubp_pg_control(hws, fe_idx, false); + dpp->funcs->dpp_reset(dpp); + REG_SET(DC_IP_REQUEST_CNTL, 0, + IP_REQUEST_EN, 0); + dm_logger_write(dc->ctx->logger, LOG_DEBUG, + "Power gated front end %d\n", fe_idx); + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); +} + +static void reset_hw_ctx_wrap( + struct dc *dc, + struct dc_state *context) +{ + int i; + + /* Reset Front End*/ + /* Lock*/ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *cur_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + struct timing_generator *tg = cur_pipe_ctx->stream_res.tg; + + if (cur_pipe_ctx->stream) + tg->funcs->lock(tg); + } + /* Disconnect*/ + for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (!pipe_ctx->stream || + !pipe_ctx->plane_state || + pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { + + plane_atomic_disconnect(dc, i); + } + } + /* Unlock*/ + for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) { + struct pipe_ctx *cur_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + struct timing_generator *tg = cur_pipe_ctx->stream_res.tg; + + if (cur_pipe_ctx->stream) + tg->funcs->unlock(tg); + } + + /* Disable and Powerdown*/ + for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + /*if (!pipe_ctx_old->stream) + continue;*/ + + if (pipe_ctx->stream && pipe_ctx->plane_state + && !pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + continue; + + plane_atomic_disable(dc, i); + + if (!pipe_ctx->stream || !pipe_ctx->plane_state) + plane_atomic_power_down(dc, i); + } + + /* Reset Back End*/ + for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (!pipe_ctx_old->stream) + continue; + + if (pipe_ctx_old->top_pipe) + continue; + + if (!pipe_ctx->stream || + pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { + struct clock_source *old_clk = pipe_ctx_old->clock_source; + + reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state); + + if (old_clk) + old_clk->funcs->cs_power_down(old_clk); + } + } + +} + +static bool patch_address_for_sbs_tb_stereo( + struct pipe_ctx *pipe_ctx, PHYSICAL_ADDRESS_LOC *addr) +{ + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + bool sec_split = pipe_ctx->top_pipe && + pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state; + if (sec_split && plane_state->address.type == PLN_ADDR_TYPE_GRPH_STEREO && + (pipe_ctx->stream->timing.timing_3d_format == + TIMING_3D_FORMAT_SIDE_BY_SIDE || + pipe_ctx->stream->timing.timing_3d_format == + TIMING_3D_FORMAT_TOP_AND_BOTTOM)) { + *addr = plane_state->address.grph_stereo.left_addr; + plane_state->address.grph_stereo.left_addr = + plane_state->address.grph_stereo.right_addr; + return true; + } else { + if (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_NONE && + plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO) { + plane_state->address.type = PLN_ADDR_TYPE_GRPH_STEREO; + plane_state->address.grph_stereo.right_addr = + plane_state->address.grph_stereo.left_addr; + } + } + return false; +} + +static void toggle_watermark_change_req(struct dce_hwseq *hws) +{ + uint32_t watermark_change_req; + + REG_GET(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, + DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST, &watermark_change_req); + + if (watermark_change_req) + watermark_change_req = 0; + else + watermark_change_req = 1; + + REG_UPDATE(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL, + DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST, watermark_change_req); +} + +static void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + bool addr_patched = false; + PHYSICAL_ADDRESS_LOC addr; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + + if (plane_state == NULL) + return; + addr_patched = patch_address_for_sbs_tb_stereo(pipe_ctx, &addr); + pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( + pipe_ctx->plane_res.hubp, + &plane_state->address, + plane_state->flip_immediate); + plane_state->status.requested_address = plane_state->address; + if (addr_patched) + pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; +} + +static bool dcn10_set_input_transfer_func( + struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) +{ + struct dpp *dpp_base = pipe_ctx->plane_res.dpp; + const struct dc_transfer_func *tf = NULL; + bool result = true; + + if (dpp_base == NULL) + return false; + + if (plane_state->in_transfer_func) + tf = plane_state->in_transfer_func; + + if (plane_state->gamma_correction && dce_use_lut(plane_state)) + dpp_base->funcs->ipp_program_input_lut(dpp_base, + plane_state->gamma_correction); + + if (tf == NULL) + dpp_base->funcs->ipp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS); + else if (tf->type == TF_TYPE_PREDEFINED) { + switch (tf->tf) { + case TRANSFER_FUNCTION_SRGB: + dpp_base->funcs->ipp_set_degamma(dpp_base, + IPP_DEGAMMA_MODE_HW_sRGB); + break; + case TRANSFER_FUNCTION_BT709: + dpp_base->funcs->ipp_set_degamma(dpp_base, + IPP_DEGAMMA_MODE_HW_xvYCC); + break; + case TRANSFER_FUNCTION_LINEAR: + dpp_base->funcs->ipp_set_degamma(dpp_base, + IPP_DEGAMMA_MODE_BYPASS); + break; + case TRANSFER_FUNCTION_PQ: + result = false; + break; + default: + result = false; + break; + } + } else if (tf->type == TF_TYPE_BYPASS) { + dpp_base->funcs->ipp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS); + } else { + /*TF_TYPE_DISTRIBUTED_POINTS*/ + result = false; + } + + return result; +} +/*modify the method to handle rgb for arr_points*/ +static bool convert_to_custom_float( + struct pwl_result_data *rgb_resulted, + struct curve_points *arr_points, + uint32_t hw_points_num) +{ + struct custom_float_format fmt; + + struct pwl_result_data *rgb = rgb_resulted; + + uint32_t i = 0; + + fmt.exponenta_bits = 6; + fmt.mantissa_bits = 12; + fmt.sign = false; + + if (!convert_to_custom_float_format( + arr_points[0].x, + &fmt, + &arr_points[0].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[0].offset, + &fmt, + &arr_points[0].custom_float_offset)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[0].slope, + &fmt, + &arr_points[0].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 10; + fmt.sign = false; + + if (!convert_to_custom_float_format( + arr_points[1].x, + &fmt, + &arr_points[1].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[1].y, + &fmt, + &arr_points[1].custom_float_y)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + arr_points[1].slope, + &fmt, + &arr_points[1].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 12; + fmt.sign = true; + + while (i != hw_points_num) { + if (!convert_to_custom_float_format( + rgb->red, + &fmt, + &rgb->red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->green, + &fmt, + &rgb->green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->blue, + &fmt, + &rgb->blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_red, + &fmt, + &rgb->delta_red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_green, + &fmt, + &rgb->delta_green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_blue, + &fmt, + &rgb->delta_blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + ++rgb; + ++i; + } + + return true; +} +#define MAX_REGIONS_NUMBER 34 +#define MAX_LOW_POINT 25 +#define NUMBER_SEGMENTS 32 + +static bool dcn10_translate_regamma_to_hw_format(const struct dc_transfer_func + *output_tf, struct pwl_params *regamma_params) +{ + struct curve_points *arr_points; + struct pwl_result_data *rgb_resulted; + struct pwl_result_data *rgb; + struct pwl_result_data *rgb_plus_1; + struct fixed31_32 y_r; + struct fixed31_32 y_g; + struct fixed31_32 y_b; + struct fixed31_32 y1_min; + struct fixed31_32 y3_max; + + int32_t segment_start, segment_end; + int32_t i; + uint32_t j, k, seg_distr[MAX_REGIONS_NUMBER], increment, start_index, hw_points; + + if (output_tf == NULL || regamma_params == NULL || + output_tf->type == TF_TYPE_BYPASS) + return false; + + arr_points = regamma_params->arr_points; + rgb_resulted = regamma_params->rgb_resulted; + hw_points = 0; + + memset(regamma_params, 0, sizeof(struct pwl_params)); + memset(seg_distr, 0, sizeof(seg_distr)); + + if (output_tf->tf == TRANSFER_FUNCTION_PQ) { + /* 32 segments + * segments are from 2^-25 to 2^7 + */ + for (i = 0; i < 32 ; i++) + seg_distr[i] = 3; + + segment_start = -25; + segment_end = 7; + } else { + /* 10 segments + * segment is from 2^-10 to 2^0 + * There are less than 256 points, for optimization + */ + seg_distr[0] = 3; + seg_distr[1] = 4; + seg_distr[2] = 4; + seg_distr[3] = 4; + seg_distr[4] = 4; + seg_distr[5] = 4; + seg_distr[6] = 4; + seg_distr[7] = 4; + seg_distr[8] = 5; + seg_distr[9] = 5; + + segment_start = -10; + segment_end = 0; + } + + for (i = segment_end - segment_start; i < MAX_REGIONS_NUMBER ; i++) + seg_distr[i] = -1; + + for (k = 0; k < MAX_REGIONS_NUMBER; k++) { + if (seg_distr[k] != -1) + hw_points += (1 << seg_distr[k]); + } + + j = 0; + for (k = 0; k < (segment_end - segment_start); k++) { + increment = NUMBER_SEGMENTS / (1 << seg_distr[k]); + start_index = (segment_start + k + MAX_LOW_POINT) * NUMBER_SEGMENTS; + for (i = start_index; i < start_index + NUMBER_SEGMENTS; i += increment) { + if (j == hw_points - 1) + break; + rgb_resulted[j].red = output_tf->tf_pts.red[i]; + rgb_resulted[j].green = output_tf->tf_pts.green[i]; + rgb_resulted[j].blue = output_tf->tf_pts.blue[i]; + j++; + } + } + + /* last point */ + start_index = (segment_end + MAX_LOW_POINT) * NUMBER_SEGMENTS; + rgb_resulted[hw_points - 1].red = + output_tf->tf_pts.red[start_index]; + rgb_resulted[hw_points - 1].green = + output_tf->tf_pts.green[start_index]; + rgb_resulted[hw_points - 1].blue = + output_tf->tf_pts.blue[start_index]; + + arr_points[0].x = dal_fixed31_32_pow(dal_fixed31_32_from_int(2), + dal_fixed31_32_from_int(segment_start)); + arr_points[1].x = dal_fixed31_32_pow(dal_fixed31_32_from_int(2), + dal_fixed31_32_from_int(segment_end)); + arr_points[2].x = dal_fixed31_32_pow(dal_fixed31_32_from_int(2), + dal_fixed31_32_from_int(segment_end)); + + y_r = rgb_resulted[0].red; + y_g = rgb_resulted[0].green; + y_b = rgb_resulted[0].blue; + + y1_min = dal_fixed31_32_min(y_r, dal_fixed31_32_min(y_g, y_b)); + + arr_points[0].y = y1_min; + arr_points[0].slope = dal_fixed31_32_div( + arr_points[0].y, + arr_points[0].x); + y_r = rgb_resulted[hw_points - 1].red; + y_g = rgb_resulted[hw_points - 1].green; + y_b = rgb_resulted[hw_points - 1].blue; + + /* see comment above, m_arrPoints[1].y should be the Y value for the + * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1) + */ + y3_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); + + arr_points[1].y = y3_max; + arr_points[2].y = y3_max; + + arr_points[1].slope = dal_fixed31_32_zero; + arr_points[2].slope = dal_fixed31_32_zero; + + if (output_tf->tf == TRANSFER_FUNCTION_PQ) { + /* for PQ, we want to have a straight line from last HW X point, + * and the slope to be such that we hit 1.0 at 10000 nits. + */ + const struct fixed31_32 end_value = + dal_fixed31_32_from_int(125); + + arr_points[1].slope = dal_fixed31_32_div( + dal_fixed31_32_sub(dal_fixed31_32_one, arr_points[1].y), + dal_fixed31_32_sub(end_value, arr_points[1].x)); + arr_points[2].slope = dal_fixed31_32_div( + dal_fixed31_32_sub(dal_fixed31_32_one, arr_points[1].y), + dal_fixed31_32_sub(end_value, arr_points[1].x)); + } + + regamma_params->hw_points_num = hw_points; + + i = 1; + for (k = 0; k < MAX_REGIONS_NUMBER && i < MAX_REGIONS_NUMBER; k++) { + if (seg_distr[k] != -1) { + regamma_params->arr_curve_points[k].segments_num = + seg_distr[k]; + regamma_params->arr_curve_points[i].offset = + regamma_params->arr_curve_points[k]. + offset + (1 << seg_distr[k]); + } + i++; + } + + if (seg_distr[k] != -1) + regamma_params->arr_curve_points[k].segments_num = + seg_distr[k]; + + rgb = rgb_resulted; + rgb_plus_1 = rgb_resulted + 1; + + i = 1; + + while (i != hw_points + 1) { + if (dal_fixed31_32_lt(rgb_plus_1->red, rgb->red)) + rgb_plus_1->red = rgb->red; + if (dal_fixed31_32_lt(rgb_plus_1->green, rgb->green)) + rgb_plus_1->green = rgb->green; + if (dal_fixed31_32_lt(rgb_plus_1->blue, rgb->blue)) + rgb_plus_1->blue = rgb->blue; + + rgb->delta_red = dal_fixed31_32_sub( + rgb_plus_1->red, + rgb->red); + rgb->delta_green = dal_fixed31_32_sub( + rgb_plus_1->green, + rgb->green); + rgb->delta_blue = dal_fixed31_32_sub( + rgb_plus_1->blue, + rgb->blue); + + ++rgb_plus_1; + ++rgb; + ++i; + } + + convert_to_custom_float(rgb_resulted, arr_points, hw_points); + + return true; +} + +static bool dcn10_set_output_transfer_func( + struct pipe_ctx *pipe_ctx, + const struct dc_stream_state *stream) +{ + struct dpp *dpp = pipe_ctx->plane_res.dpp; + + if (dpp == NULL) + return false; + + dpp->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM; + + if (stream->out_transfer_func && + stream->out_transfer_func->type == + TF_TYPE_PREDEFINED && + stream->out_transfer_func->tf == + TRANSFER_FUNCTION_SRGB) { + dpp->funcs->opp_set_regamma_mode(dpp, OPP_REGAMMA_SRGB); + } else if (dcn10_translate_regamma_to_hw_format( + stream->out_transfer_func, &dpp->regamma_params)) { + dpp->funcs->opp_program_regamma_pwl(dpp, &dpp->regamma_params); + dpp->funcs->opp_set_regamma_mode(dpp, OPP_REGAMMA_USER); + } else { + dpp->funcs->opp_set_regamma_mode(dpp, OPP_REGAMMA_BYPASS); + } + + return true; +} + +static void dcn10_pipe_control_lock( + struct dc *dc, + struct pipe_ctx *pipe, + bool lock) +{ + struct hubp *hubp = NULL; + hubp = dc->res_pool->hubps[pipe->pipe_idx]; + /* use TG master update lock to lock everything on the TG + * therefore only top pipe need to lock + */ + if (pipe->top_pipe) + return; + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + + if (lock) + pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg); + else + pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg); + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); +} + +static bool wait_for_reset_trigger_to_occur( + struct dc_context *dc_ctx, + struct timing_generator *tg) +{ + bool rc = false; + + /* To avoid endless loop we wait at most + * frames_to_wait_on_triggered_reset frames for the reset to occur. */ + const uint32_t frames_to_wait_on_triggered_reset = 10; + int i; + + for (i = 0; i < frames_to_wait_on_triggered_reset; i++) { + + if (!tg->funcs->is_counter_moving(tg)) { + DC_ERROR("TG counter is not moving!\n"); + break; + } + + if (tg->funcs->did_triggered_reset_occur(tg)) { + rc = true; + /* usually occurs at i=1 */ + DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n", + i); + break; + } + + /* Wait for one frame. */ + tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE); + tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK); + } + + if (false == rc) + DC_ERROR("GSL: Timeout on reset trigger!\n"); + + return rc; +} + +static void dcn10_enable_timing_synchronization( + struct dc *dc, + int group_index, + int group_size, + struct pipe_ctx *grouped_pipes[]) +{ + struct dc_context *dc_ctx = dc->ctx; + int i; + + DC_SYNC_INFO("Setting up OTG reset trigger\n"); + + for (i = 1; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger( + grouped_pipes[i]->stream_res.tg, grouped_pipes[0]->stream_res.tg->inst); + + + DC_SYNC_INFO("Waiting for trigger\n"); + + /* Need to get only check 1 pipe for having reset as all the others are + * synchronized. Look at last pipe programmed to reset. + */ + wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[1]->stream_res.tg); + for (i = 1; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger( + grouped_pipes[i]->stream_res.tg); + + DC_SYNC_INFO("Sync complete\n"); +} + +static void print_rq_dlg_ttu( + struct dc *core_dc, + struct pipe_ctx *pipe_ctx) +{ + dm_logger_write(core_dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "\n============== DML TTU Output parameters [%d] ==============\n" + "qos_level_low_wm: %d, \n" + "qos_level_high_wm: %d, \n" + "min_ttu_vblank: %d, \n" + "qos_level_flip: %d, \n" + "refcyc_per_req_delivery_l: %d, \n" + "qos_level_fixed_l: %d, \n" + "qos_ramp_disable_l: %d, \n" + "refcyc_per_req_delivery_pre_l: %d, \n" + "refcyc_per_req_delivery_c: %d, \n" + "qos_level_fixed_c: %d, \n" + "qos_ramp_disable_c: %d, \n" + "refcyc_per_req_delivery_pre_c: %d\n" + "=============================================================\n", + pipe_ctx->pipe_idx, + pipe_ctx->ttu_regs.qos_level_low_wm, + pipe_ctx->ttu_regs.qos_level_high_wm, + pipe_ctx->ttu_regs.min_ttu_vblank, + pipe_ctx->ttu_regs.qos_level_flip, + pipe_ctx->ttu_regs.refcyc_per_req_delivery_l, + pipe_ctx->ttu_regs.qos_level_fixed_l, + pipe_ctx->ttu_regs.qos_ramp_disable_l, + pipe_ctx->ttu_regs.refcyc_per_req_delivery_pre_l, + pipe_ctx->ttu_regs.refcyc_per_req_delivery_c, + pipe_ctx->ttu_regs.qos_level_fixed_c, + pipe_ctx->ttu_regs.qos_ramp_disable_c, + pipe_ctx->ttu_regs.refcyc_per_req_delivery_pre_c + ); + + dm_logger_write(core_dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "\n============== DML DLG Output parameters [%d] ==============\n" + "refcyc_h_blank_end: %d, \n" + "dlg_vblank_end: %d, \n" + "min_dst_y_next_start: %d, \n" + "refcyc_per_htotal: %d, \n" + "refcyc_x_after_scaler: %d, \n" + "dst_y_after_scaler: %d, \n" + "dst_y_prefetch: %d, \n" + "dst_y_per_vm_vblank: %d, \n" + "dst_y_per_row_vblank: %d, \n" + "ref_freq_to_pix_freq: %d, \n" + "vratio_prefetch: %d, \n" + "refcyc_per_pte_group_vblank_l: %d, \n" + "refcyc_per_meta_chunk_vblank_l: %d, \n" + "dst_y_per_pte_row_nom_l: %d, \n" + "refcyc_per_pte_group_nom_l: %d, \n", + pipe_ctx->pipe_idx, + pipe_ctx->dlg_regs.refcyc_h_blank_end, + pipe_ctx->dlg_regs.dlg_vblank_end, + pipe_ctx->dlg_regs.min_dst_y_next_start, + pipe_ctx->dlg_regs.refcyc_per_htotal, + pipe_ctx->dlg_regs.refcyc_x_after_scaler, + pipe_ctx->dlg_regs.dst_y_after_scaler, + pipe_ctx->dlg_regs.dst_y_prefetch, + pipe_ctx->dlg_regs.dst_y_per_vm_vblank, + pipe_ctx->dlg_regs.dst_y_per_row_vblank, + pipe_ctx->dlg_regs.ref_freq_to_pix_freq, + pipe_ctx->dlg_regs.vratio_prefetch, + pipe_ctx->dlg_regs.refcyc_per_pte_group_vblank_l, + pipe_ctx->dlg_regs.refcyc_per_meta_chunk_vblank_l, + pipe_ctx->dlg_regs.dst_y_per_pte_row_nom_l, + pipe_ctx->dlg_regs.refcyc_per_pte_group_nom_l + ); + + dm_logger_write(core_dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "\ndst_y_per_meta_row_nom_l: %d, \n" + "refcyc_per_meta_chunk_nom_l: %d, \n" + "refcyc_per_line_delivery_pre_l: %d, \n" + "refcyc_per_line_delivery_l: %d, \n" + "vratio_prefetch_c: %d, \n" + "refcyc_per_pte_group_vblank_c: %d, \n" + "refcyc_per_meta_chunk_vblank_c: %d, \n" + "dst_y_per_pte_row_nom_c: %d, \n" + "refcyc_per_pte_group_nom_c: %d, \n" + "dst_y_per_meta_row_nom_c: %d, \n" + "refcyc_per_meta_chunk_nom_c: %d, \n" + "refcyc_per_line_delivery_pre_c: %d, \n" + "refcyc_per_line_delivery_c: %d \n" + "========================================================\n", + pipe_ctx->dlg_regs.dst_y_per_meta_row_nom_l, + pipe_ctx->dlg_regs.refcyc_per_meta_chunk_nom_l, + pipe_ctx->dlg_regs.refcyc_per_line_delivery_pre_l, + pipe_ctx->dlg_regs.refcyc_per_line_delivery_l, + pipe_ctx->dlg_regs.vratio_prefetch_c, + pipe_ctx->dlg_regs.refcyc_per_pte_group_vblank_c, + pipe_ctx->dlg_regs.refcyc_per_meta_chunk_vblank_c, + pipe_ctx->dlg_regs.dst_y_per_pte_row_nom_c, + pipe_ctx->dlg_regs.refcyc_per_pte_group_nom_c, + pipe_ctx->dlg_regs.dst_y_per_meta_row_nom_c, + pipe_ctx->dlg_regs.refcyc_per_meta_chunk_nom_c, + pipe_ctx->dlg_regs.refcyc_per_line_delivery_pre_c, + pipe_ctx->dlg_regs.refcyc_per_line_delivery_c + ); + + dm_logger_write(core_dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "\n============== DML RQ Output parameters [%d] ==============\n" + "chunk_size: %d \n" + "min_chunk_size: %d \n" + "meta_chunk_size: %d \n" + "min_meta_chunk_size: %d \n" + "dpte_group_size: %d \n" + "mpte_group_size: %d \n" + "swath_height: %d \n" + "pte_row_height_linear: %d \n" + "========================================================\n", + pipe_ctx->pipe_idx, + pipe_ctx->rq_regs.rq_regs_l.chunk_size, + pipe_ctx->rq_regs.rq_regs_l.min_chunk_size, + pipe_ctx->rq_regs.rq_regs_l.meta_chunk_size, + pipe_ctx->rq_regs.rq_regs_l.min_meta_chunk_size, + pipe_ctx->rq_regs.rq_regs_l.dpte_group_size, + pipe_ctx->rq_regs.rq_regs_l.mpte_group_size, + pipe_ctx->rq_regs.rq_regs_l.swath_height, + pipe_ctx->rq_regs.rq_regs_l.pte_row_height_linear + ); +} + +static void dcn10_power_on_fe( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context) +{ + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct dce_hwseq *hws = dc->hwseq; + + if (dc->debug.sanity_checks) { + verify_allow_pstate_change_high(dc->hwseq); + } + + power_on_plane(dc->hwseq, + pipe_ctx->pipe_idx); + + /* enable DCFCLK current DCHUB */ + REG_UPDATE(HUBP_CLK_CNTL[pipe_ctx->pipe_idx], + HUBP_CLOCK_ENABLE, 1); + + /* make sure OPP_PIPE_CLOCK_EN = 1 */ + REG_UPDATE(OPP_PIPE_CONTROL[pipe_ctx->stream_res.tg->inst], + OPP_PIPE_CLOCK_EN, 1); + /*TODO: REG_UPDATE(DENTIST_DISPCLK_CNTL, DENTIST_DPPCLK_WDIVIDER, 0x1f);*/ + + if (plane_state) { + dm_logger_write(dc->ctx->logger, LOG_DC, + "Pipe:%d 0x%x: addr hi:0x%x, " + "addr low:0x%x, " + "src: %d, %d, %d," + " %d; dst: %d, %d, %d, %d;\n", + pipe_ctx->pipe_idx, + plane_state, + plane_state->address.grph.addr.high_part, + plane_state->address.grph.addr.low_part, + plane_state->src_rect.x, + plane_state->src_rect.y, + plane_state->src_rect.width, + plane_state->src_rect.height, + plane_state->dst_rect.x, + plane_state->dst_rect.y, + plane_state->dst_rect.width, + plane_state->dst_rect.height); + + dm_logger_write(dc->ctx->logger, LOG_DC, + "Pipe %d: width, height, x, y format:%d\n" + "viewport:%d, %d, %d, %d\n" + "recout: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + plane_state->format, + pipe_ctx->plane_res.scl_data.viewport.width, + pipe_ctx->plane_res.scl_data.viewport.height, + pipe_ctx->plane_res.scl_data.viewport.x, + pipe_ctx->plane_res.scl_data.viewport.y, + pipe_ctx->plane_res.scl_data.recout.width, + pipe_ctx->plane_res.scl_data.recout.height, + pipe_ctx->plane_res.scl_data.recout.x, + pipe_ctx->plane_res.scl_data.recout.y); + print_rq_dlg_ttu(dc, pipe_ctx); + } + + if (dc->debug.sanity_checks) { + verify_allow_pstate_change_high(dc->hwseq); + } +} + +static void program_gamut_remap(struct pipe_ctx *pipe_ctx) +{ + struct dpp_grph_csc_adjustment adjust; + memset(&adjust, 0, sizeof(adjust)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + + if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + adjust.temperature_matrix[0] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[0]; + adjust.temperature_matrix[1] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[1]; + adjust.temperature_matrix[2] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[2]; + adjust.temperature_matrix[3] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[4]; + adjust.temperature_matrix[4] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[5]; + adjust.temperature_matrix[5] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[6]; + adjust.temperature_matrix[6] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[8]; + adjust.temperature_matrix[7] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[9]; + adjust.temperature_matrix[8] = + pipe_ctx->stream-> + gamut_remap_matrix.matrix[10]; + } + + pipe_ctx->plane_res.dpp->funcs->dpp_set_gamut_remap(pipe_ctx->plane_res.dpp, &adjust); +} + + +static void program_csc_matrix(struct pipe_ctx *pipe_ctx, + enum dc_color_space colorspace, + uint16_t *matrix) +{ + int i; + struct out_csc_color_matrix tbl_entry; + + if (pipe_ctx->stream->csc_color_matrix.enable_adjustment + == true) { + enum dc_color_space color_space = + pipe_ctx->stream->output_color_space; + + //uint16_t matrix[12]; + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = pipe_ctx->stream->csc_color_matrix.matrix[i]; + + tbl_entry.color_space = color_space; + //tbl_entry.regval = matrix; + pipe_ctx->plane_res.dpp->funcs->opp_set_csc_adjustment(pipe_ctx->plane_res.dpp, &tbl_entry); + } +} +static bool is_lower_pipe_tree_visible(struct pipe_ctx *pipe_ctx) +{ + if (pipe_ctx->plane_state->visible) + return true; + if (pipe_ctx->bottom_pipe && is_lower_pipe_tree_visible(pipe_ctx->bottom_pipe)) + return true; + return false; +} + +static bool is_upper_pipe_tree_visible(struct pipe_ctx *pipe_ctx) +{ + if (pipe_ctx->plane_state->visible) + return true; + if (pipe_ctx->top_pipe && is_upper_pipe_tree_visible(pipe_ctx->top_pipe)) + return true; + return false; +} + +static bool is_pipe_tree_visible(struct pipe_ctx *pipe_ctx) +{ + if (pipe_ctx->plane_state->visible) + return true; + if (pipe_ctx->top_pipe && is_upper_pipe_tree_visible(pipe_ctx->top_pipe)) + return true; + if (pipe_ctx->bottom_pipe && is_lower_pipe_tree_visible(pipe_ctx->bottom_pipe)) + return true; + return false; +} + +static bool is_rgb_cspace(enum dc_color_space output_color_space) +{ + switch (output_color_space) { + case COLOR_SPACE_SRGB: + case COLOR_SPACE_SRGB_LIMITED: + case COLOR_SPACE_2020_RGB_FULLRANGE: + case COLOR_SPACE_2020_RGB_LIMITEDRANGE: + case COLOR_SPACE_ADOBERGB: + return true; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR601_LIMITED: + case COLOR_SPACE_YCBCR709_LIMITED: + case COLOR_SPACE_2020_YCBCR: + return false; + default: + /* Add a case to switch */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +static void dcn10_get_surface_visual_confirm_color( + const struct pipe_ctx *pipe_ctx, + struct tg_color *color) +{ + uint32_t color_value = MAX_TG_COLOR_VALUE; + + switch (pipe_ctx->plane_res.scl_data.format) { + case PIXEL_FORMAT_ARGB8888: + /* set boarder color to red */ + color->color_r_cr = color_value; + break; + + case PIXEL_FORMAT_ARGB2101010: + /* set boarder color to blue */ + color->color_b_cb = color_value; + break; + case PIXEL_FORMAT_420BPP8: + /* set boarder color to green */ + color->color_g_y = color_value; + break; + case PIXEL_FORMAT_420BPP10: + /* set boarder color to yellow */ + color->color_g_y = color_value; + color->color_r_cr = color_value; + break; + case PIXEL_FORMAT_FP16: + /* set boarder color to white */ + color->color_r_cr = color_value; + color->color_b_cb = color_value; + color->color_g_y = color_value; + break; + default: + break; + } +} + +static void mmhub_read_vm_system_aperture_settings(struct dcn10_hubp *hubp1, + struct vm_system_aperture_param *apt, + struct dce_hwseq *hws) +{ + PHYSICAL_ADDRESS_LOC physical_page_number; + uint32_t logical_addr_low; + uint32_t logical_addr_high; + + REG_GET(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, + PHYSICAL_PAGE_NUMBER_MSB, &physical_page_number.high_part); + REG_GET(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, + PHYSICAL_PAGE_NUMBER_LSB, &physical_page_number.low_part); + + REG_GET(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + LOGICAL_ADDR, &logical_addr_low); + + REG_GET(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + LOGICAL_ADDR, &logical_addr_high); + + apt->sys_default.quad_part = physical_page_number.quad_part << 12; + apt->sys_low.quad_part = (int64_t)logical_addr_low << 18; + apt->sys_high.quad_part = (int64_t)logical_addr_high << 18; +} + +/* Temporary read settings, future will get values from kmd directly */ +static void mmhub_read_vm_context0_settings(struct dcn10_hubp *hubp1, + struct vm_context0_param *vm0, + struct dce_hwseq *hws) +{ + PHYSICAL_ADDRESS_LOC fb_base; + PHYSICAL_ADDRESS_LOC fb_offset; + uint32_t fb_base_value; + uint32_t fb_offset_value; + + REG_GET(DCHUBBUB_SDPIF_FB_BASE, SDPIF_FB_BASE, &fb_base_value); + REG_GET(DCHUBBUB_SDPIF_FB_OFFSET, SDPIF_FB_OFFSET, &fb_offset_value); + + REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32, + PAGE_DIRECTORY_ENTRY_HI32, &vm0->pte_base.high_part); + REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32, + PAGE_DIRECTORY_ENTRY_LO32, &vm0->pte_base.low_part); + + REG_GET(VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32, + LOGICAL_PAGE_NUMBER_HI4, &vm0->pte_start.high_part); + REG_GET(VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32, + LOGICAL_PAGE_NUMBER_LO32, &vm0->pte_start.low_part); + + REG_GET(VM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32, + LOGICAL_PAGE_NUMBER_HI4, &vm0->pte_end.high_part); + REG_GET(VM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32, + LOGICAL_PAGE_NUMBER_LO32, &vm0->pte_end.low_part); + + REG_GET(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32, + PHYSICAL_PAGE_ADDR_HI4, &vm0->fault_default.high_part); + REG_GET(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32, + PHYSICAL_PAGE_ADDR_LO32, &vm0->fault_default.low_part); + + /* + * The values in VM_CONTEXT0_PAGE_TABLE_BASE_ADDR is in UMA space. + * Therefore we need to do + * DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + * - DCHUBBUB_SDPIF_FB_OFFSET + DCHUBBUB_SDPIF_FB_BASE + */ + fb_base.quad_part = (uint64_t)fb_base_value << 24; + fb_offset.quad_part = (uint64_t)fb_offset_value << 24; + vm0->pte_base.quad_part += fb_base.quad_part; + vm0->pte_base.quad_part -= fb_offset.quad_part; +} + +static void dcn10_program_pte_vm(struct hubp *hubp, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation, + struct dce_hwseq *hws) +{ + struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + struct vm_system_aperture_param apt = { {{ 0 } } }; + struct vm_context0_param vm0 = { { { 0 } } }; + + + mmhub_read_vm_system_aperture_settings(hubp1, &apt, hws); + mmhub_read_vm_context0_settings(hubp1, &vm0, hws); + + hubp->funcs->hubp_set_vm_system_aperture_settings(hubp, &apt); + hubp->funcs->hubp_set_vm_context0_settings(hubp, &vm0); +} + +static void update_dchubp_dpp( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + struct dpp *dpp = pipe_ctx->plane_res.dpp; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + union plane_size size = plane_state->plane_size; + struct mpcc_cfg mpcc_cfg = {0}; + struct pipe_ctx *top_pipe; + bool per_pixel_alpha = plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe; + + /* TODO: proper fix once fpga works */ + /* depends on DML calculation, DPP clock value may change dynamically */ + enable_dppclk( + dc->hwseq, + pipe_ctx->pipe_idx, + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk, + context->bw.dcn.calc_clk.dppclk_div); + dc->current_state->bw.dcn.cur_clk.dppclk_div = + context->bw.dcn.calc_clk.dppclk_div; + context->bw.dcn.cur_clk.dppclk_div = context->bw.dcn.calc_clk.dppclk_div; + + /* TODO: Need input parameter to tell current DCHUB pipe tie to which OTG + * VTG is within DCHUBBUB which is commond block share by each pipe HUBP. + * VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG + */ + REG_UPDATE(DCHUBP_CNTL[pipe_ctx->pipe_idx], HUBP_VTG_SEL, pipe_ctx->stream_res.tg->inst); + + hubp->funcs->hubp_setup( + hubp, + &pipe_ctx->dlg_regs, + &pipe_ctx->ttu_regs, + &pipe_ctx->rq_regs, + &pipe_ctx->pipe_dlg_param); + + size.grph.surface_size = pipe_ctx->plane_res.scl_data.viewport; + + if (dc->config.gpu_vm_support) + dcn10_program_pte_vm( + pipe_ctx->plane_res.hubp, + plane_state->format, + &plane_state->tiling_info, + plane_state->rotation, + hws + ); + + dpp->funcs->ipp_setup(dpp, + plane_state->format, + EXPANSION_MODE_ZERO); + + mpcc_cfg.dpp_id = hubp->inst; + mpcc_cfg.opp_id = pipe_ctx->stream_res.opp->inst; + mpcc_cfg.tree_cfg = &(pipe_ctx->stream_res.opp->mpc_tree); + for (top_pipe = pipe_ctx->top_pipe; top_pipe; top_pipe = top_pipe->top_pipe) + mpcc_cfg.z_index++; + if (dc->debug.surface_visual_confirm) + dcn10_get_surface_visual_confirm_color( + pipe_ctx, &mpcc_cfg.black_color); + else + color_space_to_black_color( + dc, pipe_ctx->stream->output_color_space, + &mpcc_cfg.black_color); + mpcc_cfg.per_pixel_alpha = per_pixel_alpha; + /* DCN1.0 has output CM before MPC which seems to screw with + * pre-multiplied alpha. + */ + mpcc_cfg.pre_multiplied_alpha = is_rgb_cspace( + pipe_ctx->stream->output_color_space) + && per_pixel_alpha; + hubp->mpcc_id = dc->res_pool->mpc->funcs->add(dc->res_pool->mpc, &mpcc_cfg); + hubp->opp_id = mpcc_cfg.opp_id; + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = per_pixel_alpha; + pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP; + /* scaler configuration */ + pipe_ctx->plane_res.dpp->funcs->dpp_set_scaler( + pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data); + + hubp->funcs->mem_program_viewport(hubp, + &pipe_ctx->plane_res.scl_data.viewport, &pipe_ctx->plane_res.scl_data.viewport_c); + + /*gamut remap*/ + program_gamut_remap(pipe_ctx); + + program_csc_matrix(pipe_ctx, + pipe_ctx->stream->output_color_space, + pipe_ctx->stream->csc_color_matrix.matrix); + + hubp->funcs->hubp_program_surface_config( + hubp, + plane_state->format, + &plane_state->tiling_info, + &size, + plane_state->rotation, + &plane_state->dcc, + plane_state->horizontal_mirror); + + dc->hwss.update_plane_addr(dc, pipe_ctx); + + if (is_pipe_tree_visible(pipe_ctx)) + hubp->funcs->set_blank(hubp, false); +} + + +static void program_all_pipe_in_tree( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context) +{ + unsigned int ref_clk_mhz = dc->res_pool->ref_clock_inKhz/1000; + + if (pipe_ctx->top_pipe == NULL) { + + /* lock otg_master_update to process all pipes associated with + * this OTG. this is done only one time. + */ + /* watermark is for all pipes */ + program_watermarks(dc->hwseq, &context->bw.dcn.watermarks, ref_clk_mhz); + + if (dc->debug.sanity_checks) { + /* pstate stuck check after watermark update */ + verify_allow_pstate_change_high(dc->hwseq); + } + + pipe_ctx->stream_res.tg->funcs->lock(pipe_ctx->stream_res.tg); + + pipe_ctx->stream_res.tg->dlg_otg_param.vready_offset = pipe_ctx->pipe_dlg_param.vready_offset; + pipe_ctx->stream_res.tg->dlg_otg_param.vstartup_start = pipe_ctx->pipe_dlg_param.vstartup_start; + pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_offset = pipe_ctx->pipe_dlg_param.vupdate_offset; + pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_width = pipe_ctx->pipe_dlg_param.vupdate_width; + pipe_ctx->stream_res.tg->dlg_otg_param.signal = pipe_ctx->stream->signal; + + pipe_ctx->stream_res.tg->funcs->program_global_sync( + pipe_ctx->stream_res.tg); + pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, !is_pipe_tree_visible(pipe_ctx)); + } + + if (pipe_ctx->plane_state != NULL) { + struct dc_cursor_position position = { 0 }; + struct pipe_ctx *cur_pipe_ctx = + &dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx]; + + dcn10_power_on_fe(dc, pipe_ctx, context); + + /* temporary dcn1 wa: + * watermark update requires toggle after a/b/c/d sets are programmed + * if hubp is pg then wm value doesn't get properaged to hubp + * need to toggle after ungate to ensure wm gets to hubp. + * + * final solution: we need to get SMU to do the toggle as + * DCHUBBUB_ARB_WATERMARK_CHANGE_REQUEST is owned by SMU we should have + * both driver and fw accessing same register + */ + toggle_watermark_change_req(dc->hwseq); + + update_dchubp_dpp(dc, pipe_ctx, context); + + /* TODO: this is a hack w/a for switching from mpo to pipe split */ + dc_stream_set_cursor_position(pipe_ctx->stream, &position); + + dc_stream_set_cursor_attributes(pipe_ctx->stream, + &pipe_ctx->stream->cursor_attributes); + + if (cur_pipe_ctx->plane_state != pipe_ctx->plane_state) { + dc->hwss.set_input_transfer_func( + pipe_ctx, pipe_ctx->plane_state); + dc->hwss.set_output_transfer_func( + pipe_ctx, pipe_ctx->stream); + } + } + + if (dc->debug.sanity_checks) { + /* pstate stuck check after each pipe is programmed */ + verify_allow_pstate_change_high(dc->hwseq); + } + + if (pipe_ctx->bottom_pipe != NULL && pipe_ctx->bottom_pipe != pipe_ctx) + program_all_pipe_in_tree(dc, pipe_ctx->bottom_pipe, context); +} + +static void dcn10_pplib_apply_display_requirements( + struct dc *dc, + struct dc_state *context) +{ + struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; + + pp_display_cfg->all_displays_in_sync = false;/*todo*/ + pp_display_cfg->nb_pstate_switch_disable = false; + pp_display_cfg->min_engine_clock_khz = context->bw.dcn.cur_clk.dcfclk_khz; + pp_display_cfg->min_memory_clock_khz = context->bw.dcn.cur_clk.fclk_khz; + pp_display_cfg->min_engine_clock_deep_sleep_khz = context->bw.dcn.cur_clk.dcfclk_deep_sleep_khz; + pp_display_cfg->min_dcfc_deep_sleep_clock_khz = context->bw.dcn.cur_clk.dcfclk_deep_sleep_khz; + pp_display_cfg->avail_mclk_switch_time_us = + context->bw.dcn.cur_clk.dram_ccm_us > 0 ? context->bw.dcn.cur_clk.dram_ccm_us : 0; + pp_display_cfg->avail_mclk_switch_time_in_disp_active_us = + context->bw.dcn.cur_clk.min_active_dram_ccm_us > 0 ? context->bw.dcn.cur_clk.min_active_dram_ccm_us : 0; + pp_display_cfg->min_dcfclock_khz = context->bw.dcn.cur_clk.dcfclk_khz; + pp_display_cfg->disp_clk_khz = context->bw.dcn.cur_clk.dispclk_khz; + dce110_fill_display_configs(context, pp_display_cfg); + + if (memcmp(&dc->prev_display_config, pp_display_cfg, sizeof( + struct dm_pp_display_configuration)) != 0) + dm_pp_apply_display_requirements(dc->ctx, pp_display_cfg); + + dc->prev_display_config = *pp_display_cfg; +} + +static void optimize_shared_resources(struct dc *dc) +{ + if (dc->current_state->stream_count == 0) { + apply_DEGVIDCN10_253_wa(dc); + /* S0i2 message */ + dcn10_pplib_apply_display_requirements(dc, dc->current_state); + } + + if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) + dcn_bw_notify_pplib_of_wm_ranges(dc); +} + +static void ready_shared_resources(struct dc *dc, struct dc_state *context) +{ + if (dc->current_state->stream_count == 0 && + !dc->debug.disable_stutter) + undo_DEGVIDCN10_253_wa(dc); + + /* S0i2 message */ + if (dc->current_state->stream_count == 0 && + context->stream_count != 0) + dcn10_pplib_apply_display_requirements(dc, context); +} + +static void dcn10_apply_ctx_for_surface( + struct dc *dc, + const struct dc_stream_state *stream, + int num_planes, + struct dc_state *context) +{ + int i, be_idx; + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + + be_idx = -1; + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (stream == context->res_ctx.pipe_ctx[i].stream) { + be_idx = context->res_ctx.pipe_ctx[i].stream_res.tg->inst; + break; + } + } + + ASSERT(be_idx != -1); + + if (num_planes == 0) { + for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { + struct pipe_ctx *old_pipe_ctx = + &dc->current_state->res_ctx.pipe_ctx[i]; + + if (old_pipe_ctx->stream_res.tg && old_pipe_ctx->stream_res.tg->inst == be_idx) { + old_pipe_ctx->stream_res.tg->funcs->set_blank(old_pipe_ctx->stream_res.tg, true); + dcn10_power_down_fe(dc, old_pipe_ctx->pipe_idx); + } + } + return; + } + + /* reset unused mpcc */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *old_pipe_ctx = + &dc->current_state->res_ctx.pipe_ctx[i]; + + if (!pipe_ctx->plane_state && !old_pipe_ctx->plane_state) + continue; + + /* + * Powergate reused pipes that are not powergated + * fairly hacky right now, using opp_id as indicator + */ + + if (pipe_ctx->plane_state && !old_pipe_ctx->plane_state) { + if (pipe_ctx->plane_res.hubp->opp_id != 0xf && pipe_ctx->stream_res.tg->inst == be_idx) { + dcn10_power_down_fe(dc, pipe_ctx->pipe_idx); + /* + * power down fe will unlock when calling reset, need + * to lock it back here. Messy, need rework. + */ + pipe_ctx->stream_res.tg->funcs->lock(pipe_ctx->stream_res.tg); + } + } + + + if ((!pipe_ctx->plane_state && old_pipe_ctx->plane_state) + || (!pipe_ctx->stream && old_pipe_ctx->stream)) { + if (old_pipe_ctx->stream_res.tg->inst != be_idx) + continue; + + if (!old_pipe_ctx->top_pipe) { + ASSERT(0); + continue; + } + + /* reset mpc */ + dc->res_pool->mpc->funcs->remove( + dc->res_pool->mpc, + &(old_pipe_ctx->stream_res.opp->mpc_tree), + old_pipe_ctx->stream_res.opp->inst, + old_pipe_ctx->pipe_idx); + old_pipe_ctx->stream_res.opp->mpcc_disconnect_pending[old_pipe_ctx->plane_res.hubp->mpcc_id] = true; + + /*dm_logger_write(dc->ctx->logger, LOG_ERROR, + "[debug_mpo: apply_ctx disconnect pending on mpcc %d]\n", + old_pipe_ctx->mpcc->inst);*/ + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + + old_pipe_ctx->top_pipe = NULL; + old_pipe_ctx->bottom_pipe = NULL; + old_pipe_ctx->plane_state = NULL; + old_pipe_ctx->stream = NULL; + + dm_logger_write(dc->ctx->logger, LOG_DC, + "Reset mpcc for pipe %d\n", + old_pipe_ctx->pipe_idx); + } + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream != stream) + continue; + + /* looking for top pipe to program */ + if (!pipe_ctx->top_pipe) + program_all_pipe_in_tree(dc, pipe_ctx, context); + } + + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "\n============== Watermark parameters ==============\n" + "a.urgent_ns: %d \n" + "a.cstate_enter_plus_exit: %d \n" + "a.cstate_exit: %d \n" + "a.pstate_change: %d \n" + "a.pte_meta_urgent: %d \n" + "b.urgent_ns: %d \n" + "b.cstate_enter_plus_exit: %d \n" + "b.cstate_exit: %d \n" + "b.pstate_change: %d \n" + "b.pte_meta_urgent: %d \n", + context->bw.dcn.watermarks.a.urgent_ns, + context->bw.dcn.watermarks.a.cstate_pstate.cstate_enter_plus_exit_ns, + context->bw.dcn.watermarks.a.cstate_pstate.cstate_exit_ns, + context->bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns, + context->bw.dcn.watermarks.a.pte_meta_urgent_ns, + context->bw.dcn.watermarks.b.urgent_ns, + context->bw.dcn.watermarks.b.cstate_pstate.cstate_enter_plus_exit_ns, + context->bw.dcn.watermarks.b.cstate_pstate.cstate_exit_ns, + context->bw.dcn.watermarks.b.cstate_pstate.pstate_change_ns, + context->bw.dcn.watermarks.b.pte_meta_urgent_ns + ); + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "\nc.urgent_ns: %d \n" + "c.cstate_enter_plus_exit: %d \n" + "c.cstate_exit: %d \n" + "c.pstate_change: %d \n" + "c.pte_meta_urgent: %d \n" + "d.urgent_ns: %d \n" + "d.cstate_enter_plus_exit: %d \n" + "d.cstate_exit: %d \n" + "d.pstate_change: %d \n" + "d.pte_meta_urgent: %d \n" + "========================================================\n", + context->bw.dcn.watermarks.c.urgent_ns, + context->bw.dcn.watermarks.c.cstate_pstate.cstate_enter_plus_exit_ns, + context->bw.dcn.watermarks.c.cstate_pstate.cstate_exit_ns, + context->bw.dcn.watermarks.c.cstate_pstate.pstate_change_ns, + context->bw.dcn.watermarks.c.pte_meta_urgent_ns, + context->bw.dcn.watermarks.d.urgent_ns, + context->bw.dcn.watermarks.d.cstate_pstate.cstate_enter_plus_exit_ns, + context->bw.dcn.watermarks.d.cstate_pstate.cstate_exit_ns, + context->bw.dcn.watermarks.d.cstate_pstate.pstate_change_ns, + context->bw.dcn.watermarks.d.pte_meta_urgent_ns + ); + + if (dc->debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); +} + +static void dcn10_set_bandwidth( + struct dc *dc, + struct dc_state *context, + bool decrease_allowed) +{ + struct pp_smu_display_requirement_rv *smu_req_cur = + &dc->res_pool->pp_smu_req; + struct pp_smu_display_requirement_rv smu_req = *smu_req_cur; + struct pp_smu_funcs_rv *pp_smu = dc->res_pool->pp_smu; + + if (dc->debug.sanity_checks) { + verify_allow_pstate_change_high(dc->hwseq); + } + + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) + return; + + if (decrease_allowed || context->bw.dcn.calc_clk.dispclk_khz + > dc->current_state->bw.dcn.cur_clk.dispclk_khz) { + dc->res_pool->display_clock->funcs->set_clock( + dc->res_pool->display_clock, + context->bw.dcn.calc_clk.dispclk_khz); + dc->current_state->bw.dcn.cur_clk.dispclk_khz = + context->bw.dcn.calc_clk.dispclk_khz; + } + if (decrease_allowed || context->bw.dcn.calc_clk.dcfclk_khz + > dc->current_state->bw.dcn.cur_clk.dcfclk_khz) { + smu_req.hard_min_dcefclk_khz = + context->bw.dcn.calc_clk.dcfclk_khz; + } + if (decrease_allowed || context->bw.dcn.calc_clk.fclk_khz + > dc->current_state->bw.dcn.cur_clk.fclk_khz) { + smu_req.hard_min_fclk_khz = context->bw.dcn.calc_clk.fclk_khz; + } + if (decrease_allowed || context->bw.dcn.calc_clk.dcfclk_deep_sleep_khz + > dc->current_state->bw.dcn.cur_clk.dcfclk_deep_sleep_khz) { + dc->current_state->bw.dcn.calc_clk.dcfclk_deep_sleep_khz = + context->bw.dcn.calc_clk.dcfclk_deep_sleep_khz; + context->bw.dcn.cur_clk.dcfclk_deep_sleep_khz = + context->bw.dcn.calc_clk.dcfclk_deep_sleep_khz; + } + + smu_req.display_count = context->stream_count; + + if (pp_smu->set_display_requirement) + pp_smu->set_display_requirement(&pp_smu->pp_smu, &smu_req); + + *smu_req_cur = smu_req; + + /* Decrease in freq is increase in period so opposite comparison for dram_ccm */ + if (decrease_allowed || context->bw.dcn.calc_clk.dram_ccm_us + < dc->current_state->bw.dcn.cur_clk.dram_ccm_us) { + dc->current_state->bw.dcn.calc_clk.dram_ccm_us = + context->bw.dcn.calc_clk.dram_ccm_us; + context->bw.dcn.cur_clk.dram_ccm_us = + context->bw.dcn.calc_clk.dram_ccm_us; + } + if (decrease_allowed || context->bw.dcn.calc_clk.min_active_dram_ccm_us + < dc->current_state->bw.dcn.cur_clk.min_active_dram_ccm_us) { + dc->current_state->bw.dcn.calc_clk.min_active_dram_ccm_us = + context->bw.dcn.calc_clk.min_active_dram_ccm_us; + context->bw.dcn.cur_clk.min_active_dram_ccm_us = + context->bw.dcn.calc_clk.min_active_dram_ccm_us; + } + dcn10_pplib_apply_display_requirements(dc, context); + + if (dc->debug.sanity_checks) { + verify_allow_pstate_change_high(dc->hwseq); + } + + /* need to fix this function. not doing the right thing here */ +} + +static void set_drr(struct pipe_ctx **pipe_ctx, + int num_pipes, int vmin, int vmax) +{ + int i = 0; + struct drr_params params = {0}; + + params.vertical_total_max = vmax; + params.vertical_total_min = vmin; + + /* TODO: If multiple pipes are to be supported, you need + * some GSL stuff + */ + for (i = 0; i < num_pipes; i++) { + pipe_ctx[i]->stream_res.tg->funcs->set_drr(pipe_ctx[i]->stream_res.tg, ¶ms); + } +} + +static void get_position(struct pipe_ctx **pipe_ctx, + int num_pipes, + struct crtc_position *position) +{ + int i = 0; + + /* TODO: handle pipes > 1 + */ + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->stream_res.tg->funcs->get_position(pipe_ctx[i]->stream_res.tg, position); +} + +static void set_static_screen_control(struct pipe_ctx **pipe_ctx, + int num_pipes, const struct dc_static_screen_events *events) +{ + unsigned int i; + unsigned int value = 0; + + if (events->surface_update) + value |= 0x80; + if (events->cursor_update) + value |= 0x2; + + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->stream_res.tg->funcs-> + set_static_screen_control(pipe_ctx[i]->stream_res.tg, value); +} + +static void set_plane_config( + const struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct resource_context *res_ctx) +{ + /* TODO */ + program_gamut_remap(pipe_ctx); +} + +static void dcn10_config_stereo_parameters( + struct dc_stream_state *stream, struct crtc_stereo_flags *flags) +{ + enum view_3d_format view_format = stream->view_format; + enum dc_timing_3d_format timing_3d_format =\ + stream->timing.timing_3d_format; + bool non_stereo_timing = false; + + if (timing_3d_format == TIMING_3D_FORMAT_NONE || + timing_3d_format == TIMING_3D_FORMAT_SIDE_BY_SIDE || + timing_3d_format == TIMING_3D_FORMAT_TOP_AND_BOTTOM) + non_stereo_timing = true; + + if (non_stereo_timing == false && + view_format == VIEW_3D_FORMAT_FRAME_SEQUENTIAL) { + + flags->PROGRAM_STEREO = 1; + flags->PROGRAM_POLARITY = 1; + if (timing_3d_format == TIMING_3D_FORMAT_INBAND_FA || + timing_3d_format == TIMING_3D_FORMAT_DP_HDMI_INBAND_FA || + timing_3d_format == TIMING_3D_FORMAT_SIDEBAND_FA) { + enum display_dongle_type dongle = \ + stream->sink->link->ddc->dongle_type; + if (dongle == DISPLAY_DONGLE_DP_VGA_CONVERTER || + dongle == DISPLAY_DONGLE_DP_DVI_CONVERTER || + dongle == DISPLAY_DONGLE_DP_HDMI_CONVERTER) + flags->DISABLE_STEREO_DP_SYNC = 1; + } + flags->RIGHT_EYE_POLARITY =\ + stream->timing.flags.RIGHT_EYE_3D_POLARITY; + if (timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) + flags->FRAME_PACKED = 1; + } + + return; +} + +static void dcn10_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) +{ + struct crtc_stereo_flags flags = { 0 }; + struct dc_stream_state *stream = pipe_ctx->stream; + + dcn10_config_stereo_parameters(stream, &flags); + + pipe_ctx->stream_res.opp->funcs->opp_set_stereo_polarity( + pipe_ctx->stream_res.opp, + flags.PROGRAM_STEREO == 1 ? true:false, + stream->timing.flags.RIGHT_EYE_3D_POLARITY == 1 ? true:false); + + pipe_ctx->stream_res.tg->funcs->program_stereo( + pipe_ctx->stream_res.tg, + &stream->timing, + &flags); + + return; +} + +static void dcn10_wait_for_mpcc_disconnect( + struct dc *dc, + struct resource_pool *res_pool, + struct pipe_ctx *pipe_ctx) +{ + int i; + + if (dc->debug.sanity_checks) { + verify_allow_pstate_change_high(dc->hwseq); + } + + if (!pipe_ctx->stream_res.opp) + return; + + for (i = 0; i < MAX_PIPES; i++) { + if (pipe_ctx->stream_res.opp->mpcc_disconnect_pending[i]) { + res_pool->mpc->funcs->wait_for_idle(res_pool->mpc, i); + pipe_ctx->stream_res.opp->mpcc_disconnect_pending[i] = false; + res_pool->hubps[i]->funcs->set_blank(res_pool->hubps[i], true); + /*dm_logger_write(dc->ctx->logger, LOG_ERROR, + "[debug_mpo: wait_for_mpcc finished waiting on mpcc %d]\n", + i);*/ + } + } + + if (dc->debug.sanity_checks) { + verify_allow_pstate_change_high(dc->hwseq); + } + +} + +static bool dcn10_dummy_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + return true; +} + +void dcn10_update_pending_status(struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct timing_generator *tg = pipe_ctx->stream_res.tg; + + if (plane_state == NULL) + return; + + plane_state->status.is_flip_pending = + pipe_ctx->plane_res.hubp->funcs->hubp_is_flip_pending( + pipe_ctx->plane_res.hubp); + + plane_state->status.current_address = pipe_ctx->plane_res.hubp->current_address; + if (pipe_ctx->plane_res.hubp->current_address.type == PLN_ADDR_TYPE_GRPH_STEREO && + tg->funcs->is_stereo_left_eye) { + plane_state->status.is_right_eye = + !tg->funcs->is_stereo_left_eye(pipe_ctx->stream_res.tg); + } +} + + + +static const struct hw_sequencer_funcs dcn10_funcs = { + .program_gamut_remap = program_gamut_remap, + .program_csc_matrix = program_csc_matrix, + .init_hw = dcn10_init_hw, + .apply_ctx_to_hw = dce110_apply_ctx_to_hw, + .apply_ctx_for_surface = dcn10_apply_ctx_for_surface, + .set_plane_config = set_plane_config, + .update_plane_addr = dcn10_update_plane_addr, + .update_dchub = dcn10_update_dchub, + .update_pending_status = dcn10_update_pending_status, + .set_input_transfer_func = dcn10_set_input_transfer_func, + .set_output_transfer_func = dcn10_set_output_transfer_func, + .power_down = dce110_power_down, + .enable_accelerated_mode = dce110_enable_accelerated_mode, + .enable_timing_synchronization = dcn10_enable_timing_synchronization, + .update_info_frame = dce110_update_info_frame, + .enable_stream = dce110_enable_stream, + .disable_stream = dce110_disable_stream, + .unblank_stream = dce110_unblank_stream, + .enable_display_power_gating = dcn10_dummy_display_power_gating, + .power_down_front_end = dcn10_power_down_fe, + .power_on_front_end = dcn10_power_on_fe, + .pipe_control_lock = dcn10_pipe_control_lock, + .set_bandwidth = dcn10_set_bandwidth, + .reset_hw_ctx_wrap = reset_hw_ctx_wrap, + .prog_pixclk_crtc_otg = dcn10_prog_pixclk_crtc_otg, + .set_drr = set_drr, + .get_position = get_position, + .set_static_screen_control = set_static_screen_control, + .setup_stereo = dcn10_setup_stereo, + .set_avmute = dce110_set_avmute, + .log_hw_state = dcn10_log_hw_state, + .wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect, + .ready_shared_resources = ready_shared_resources, + .optimize_shared_resources = optimize_shared_resources, + .edp_backlight_control = hwss_edp_backlight_control, + .edp_power_control = hwss_edp_power_control +}; + + +void dcn10_hw_sequencer_construct(struct dc *dc) +{ + dc->hwss = dcn10_funcs; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h new file mode 100644 index 000000000000..ca53dc1cc19b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h @@ -0,0 +1,38 @@ +/* +* Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HWSS_DCN10_H__ +#define __DC_HWSS_DCN10_H__ + +#include "core_types.h" + +struct dc; + +void dcn10_hw_sequencer_construct(struct dc *dc); +extern void fill_display_configs( + const struct dc_state *context, + struct dm_pp_display_configuration *pp_display_cfg); + +#endif /* __DC_HWSS_DCN10_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_ipp.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_ipp.c new file mode 100644 index 000000000000..08db1e6b5166 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_ipp.c @@ -0,0 +1,70 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dcn10_ipp.h" +#include "reg_helper.h" + +#define REG(reg) \ + (ippn10->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + ippn10->ipp_shift->field_name, ippn10->ipp_mask->field_name + +#define CTX \ + ippn10->base.ctx + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static void dcn10_ipp_destroy(struct input_pixel_processor **ipp) +{ + kfree(TO_DCN10_IPP(*ipp)); + *ipp = NULL; +} + +static const struct ipp_funcs dcn10_ipp_funcs = { + .ipp_destroy = dcn10_ipp_destroy +}; + +void dcn10_ipp_construct( + struct dcn10_ipp *ippn10, + struct dc_context *ctx, + int inst, + const struct dcn10_ipp_registers *regs, + const struct dcn10_ipp_shift *ipp_shift, + const struct dcn10_ipp_mask *ipp_mask) +{ + ippn10->base.ctx = ctx; + ippn10->base.inst = inst; + ippn10->base.funcs = &dcn10_ipp_funcs; + + ippn10->regs = regs; + ippn10->ipp_shift = ipp_shift; + ippn10->ipp_mask = ipp_mask; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_ipp.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_ipp.h new file mode 100644 index 000000000000..d7b5bd20352a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_ipp.h @@ -0,0 +1,167 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _DCN10_IPP_H_ +#define _DCN10_IPP_H_ + +#include "ipp.h" + +#define TO_DCN10_IPP(ipp)\ + container_of(ipp, struct dcn10_ipp, base) + +#define IPP_REG_LIST_DCN(id) \ + SRI(FORMAT_CONTROL, CNVC_CFG, id), \ + SRI(DPP_CONTROL, DPP_TOP, id), \ + SRI(CNVC_SURFACE_PIXEL_FORMAT, CNVC_CFG, id), \ + SRI(CURSOR0_CONTROL, CNVC_CUR, id), \ + SRI(CURSOR0_COLOR0, CNVC_CUR, id), \ + SRI(CURSOR0_COLOR1, CNVC_CUR, id) + +#define IPP_REG_LIST_DCN10(id) \ + IPP_REG_LIST_DCN(id), \ + SRI(CURSOR_SETTINS, HUBPREQ, id), \ + SRI(CURSOR_SURFACE_ADDRESS_HIGH, CURSOR, id), \ + SRI(CURSOR_SURFACE_ADDRESS, CURSOR, id), \ + SRI(CURSOR_SIZE, CURSOR, id), \ + SRI(CURSOR_CONTROL, CURSOR, id), \ + SRI(CURSOR_POSITION, CURSOR, id), \ + SRI(CURSOR_HOT_SPOT, CURSOR, id), \ + SRI(CURSOR_DST_OFFSET, CURSOR, id) + +#define CURSOR0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY__SHIFT 0x4 +#define CURSOR0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY_MASK 0x00000010L +#define CURSOR1_CURSOR_CONTROL__CURSOR_2X_MAGNIFY__SHIFT 0x4 +#define CURSOR1_CURSOR_CONTROL__CURSOR_2X_MAGNIFY_MASK 0x00000010L +#define CURSOR2_CURSOR_CONTROL__CURSOR_2X_MAGNIFY__SHIFT 0x4 +#define CURSOR2_CURSOR_CONTROL__CURSOR_2X_MAGNIFY_MASK 0x00000010L +#define CURSOR3_CURSOR_CONTROL__CURSOR_2X_MAGNIFY__SHIFT 0x4 +#define CURSOR3_CURSOR_CONTROL__CURSOR_2X_MAGNIFY_MASK 0x00000010L + +#define IPP_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define IPP_MASK_SH_LIST_DCN(mask_sh) \ + IPP_SF(CNVC_CFG0_CNVC_SURFACE_PIXEL_FORMAT, CNVC_SURFACE_PIXEL_FORMAT, mask_sh), \ + IPP_SF(CNVC_CFG0_FORMAT_CONTROL, CNVC_BYPASS, mask_sh), \ + IPP_SF(CNVC_CFG0_FORMAT_CONTROL, ALPHA_EN, mask_sh), \ + IPP_SF(CNVC_CFG0_FORMAT_CONTROL, FORMAT_EXPANSION_MODE, mask_sh), \ + IPP_SF(CNVC_CUR0_CURSOR0_CONTROL, CUR0_MODE, mask_sh), \ + IPP_SF(CNVC_CUR0_CURSOR0_COLOR0, CUR0_COLOR0, mask_sh), \ + IPP_SF(CNVC_CUR0_CURSOR0_COLOR1, CUR0_COLOR1, mask_sh), \ + IPP_SF(CNVC_CUR0_CURSOR0_CONTROL, CUR0_EXPANSION_MODE, mask_sh), \ + IPP_SF(CNVC_CUR0_CURSOR0_CONTROL, CUR0_ENABLE, mask_sh) + +#define IPP_MASK_SH_LIST_DCN10(mask_sh) \ + IPP_MASK_SH_LIST_DCN(mask_sh),\ + IPP_SF(HUBPREQ0_CURSOR_SETTINS, CURSOR0_DST_Y_OFFSET, mask_sh), \ + IPP_SF(HUBPREQ0_CURSOR_SETTINS, CURSOR0_CHUNK_HDL_ADJUST, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_SIZE, CURSOR_WIDTH, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_SIZE, CURSOR_HEIGHT, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_CONTROL, CURSOR_MODE, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_CONTROL, CURSOR_PITCH, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_CONTROL, CURSOR_LINES_PER_CHUNK, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_CONTROL, CURSOR_ENABLE, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_POSITION, CURSOR_X_POSITION, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + IPP_SF(CURSOR0_CURSOR_DST_OFFSET, CURSOR_DST_X_OFFSET, mask_sh), \ + IPP_SF(CNVC_CFG0_FORMAT_CONTROL, OUTPUT_FP, mask_sh) + +#define IPP_DCN10_REG_FIELD_LIST(type) \ + type CNVC_SURFACE_PIXEL_FORMAT; \ + type CNVC_BYPASS; \ + type ALPHA_EN; \ + type FORMAT_EXPANSION_MODE; \ + type CURSOR0_DST_Y_OFFSET; \ + type CURSOR0_CHUNK_HDL_ADJUST; \ + type CUR0_MODE; \ + type CUR0_COLOR0; \ + type CUR0_COLOR1; \ + type CUR0_EXPANSION_MODE; \ + type CURSOR_SURFACE_ADDRESS_HIGH; \ + type CURSOR_SURFACE_ADDRESS; \ + type CURSOR_WIDTH; \ + type CURSOR_HEIGHT; \ + type CURSOR_MODE; \ + type CURSOR_2X_MAGNIFY; \ + type CURSOR_PITCH; \ + type CURSOR_LINES_PER_CHUNK; \ + type CURSOR_ENABLE; \ + type CUR0_ENABLE; \ + type CURSOR_X_POSITION; \ + type CURSOR_Y_POSITION; \ + type CURSOR_HOT_SPOT_X; \ + type CURSOR_HOT_SPOT_Y; \ + type CURSOR_DST_X_OFFSET; \ + type OUTPUT_FP + +struct dcn10_ipp_shift { + IPP_DCN10_REG_FIELD_LIST(uint8_t); +}; + +struct dcn10_ipp_mask { + IPP_DCN10_REG_FIELD_LIST(uint32_t); +}; + +struct dcn10_ipp_registers { + uint32_t DPP_CONTROL; + uint32_t CURSOR_SETTINS; + uint32_t CURSOR_SETTINGS; + uint32_t CNVC_SURFACE_PIXEL_FORMAT; + uint32_t CURSOR0_CONTROL; + uint32_t CURSOR0_COLOR0; + uint32_t CURSOR0_COLOR1; + uint32_t FORMAT_CONTROL; + uint32_t CURSOR_SURFACE_ADDRESS_HIGH; + uint32_t CURSOR_SURFACE_ADDRESS; + uint32_t CURSOR_SIZE; + uint32_t CURSOR_CONTROL; + uint32_t CURSOR_POSITION; + uint32_t CURSOR_HOT_SPOT; + uint32_t CURSOR_DST_OFFSET; +}; + +struct dcn10_ipp { + struct input_pixel_processor base; + + const struct dcn10_ipp_registers *regs; + const struct dcn10_ipp_shift *ipp_shift; + const struct dcn10_ipp_mask *ipp_mask; + + struct dc_cursor_attributes curs_attr; +}; + +void dcn10_ipp_construct(struct dcn10_ipp *ippn10, + struct dc_context *ctx, + int inst, + const struct dcn10_ipp_registers *regs, + const struct dcn10_ipp_shift *ipp_shift, + const struct dcn10_ipp_mask *ipp_mask); + +#endif /* _DCN10_IPP_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c new file mode 100644 index 000000000000..76573e1f5b01 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c @@ -0,0 +1,363 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "reg_helper.h" +#include "dcn10_mpc.h" +#include "dc.h" +#include "mem_input.h" + +#define REG(reg)\ + mpc10->mpc_regs->reg + +#define CTX \ + mpc10->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + mpc10->mpc_shift->field_name, mpc10->mpc_mask->field_name + +#define MODE_TOP_ONLY 1 +#define MODE_BLEND 3 +#define BLND_PP_ALPHA 0 +#define BLND_GLOBAL_ALPHA 2 + + +static void mpc10_set_bg_color( + struct dcn10_mpc *mpc10, + struct tg_color *bg_color, + int id) +{ + /* mpc color is 12 bit. tg_color is 10 bit */ + /* todo: might want to use 16 bit to represent color and have each + * hw block translate to correct color depth. + */ + uint32_t bg_r_cr = bg_color->color_r_cr << 2; + uint32_t bg_g_y = bg_color->color_g_y << 2; + uint32_t bg_b_cb = bg_color->color_b_cb << 2; + + REG_SET(MPCC_BG_R_CR[id], 0, + MPCC_BG_R_CR, bg_r_cr); + REG_SET(MPCC_BG_G_Y[id], 0, + MPCC_BG_G_Y, bg_g_y); + REG_SET(MPCC_BG_B_CB[id], 0, + MPCC_BG_B_CB, bg_b_cb); +} + +void mpc10_assert_idle_mpcc(struct mpc *mpc, int id) +{ + struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); + + ASSERT(!(mpc10->mpcc_in_use_mask & 1 << id)); + REG_WAIT(MPCC_STATUS[id], + MPCC_IDLE, 1, + 1, 100000); +} + +static int mpc10_get_idle_mpcc_id(struct dcn10_mpc *mpc10) +{ + int i; + int last_free_mpcc_id = -1; + + for (i = 0; i < mpc10->num_mpcc; i++) { + uint32_t is_idle = 0; + + if (mpc10->mpcc_in_use_mask & 1 << i) + continue; + + last_free_mpcc_id = i; + REG_GET(MPCC_STATUS[i], MPCC_IDLE, &is_idle); + if (is_idle) + return i; + } + + /* This assert should never trigger, we have mpcc leak if it does */ + ASSERT(last_free_mpcc_id != -1); + + mpc10_assert_idle_mpcc(&mpc10->base, last_free_mpcc_id); + return last_free_mpcc_id; +} + +static void mpc10_assert_mpcc_idle_before_connect(struct dcn10_mpc *mpc10, int id) +{ + unsigned int top_sel, mpc_busy, mpc_idle; + + REG_GET(MPCC_TOP_SEL[id], + MPCC_TOP_SEL, &top_sel); + + if (top_sel == 0xf) { + REG_GET_2(MPCC_STATUS[id], + MPCC_BUSY, &mpc_busy, + MPCC_IDLE, &mpc_idle); + + ASSERT(mpc_busy == 0); + ASSERT(mpc_idle == 1); + } +} + +void mpc10_mpcc_remove( + struct mpc *mpc, + struct mpc_tree_cfg *tree_cfg, + int opp_id, + int dpp_id) +{ + struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); + int mpcc_id, z_idx; + + /* find z_idx for the dpp to be removed */ + for (z_idx = 0; z_idx < tree_cfg->num_pipes; z_idx++) + if (tree_cfg->dpp[z_idx] == dpp_id) + break; + + if (z_idx == tree_cfg->num_pipes) { + /* In case of resume from S3/S4, remove mpcc from bios left over */ + REG_SET(MPCC_OPP_ID[dpp_id], 0, + MPCC_OPP_ID, 0xf); + REG_SET(MPCC_TOP_SEL[dpp_id], 0, + MPCC_TOP_SEL, 0xf); + REG_SET(MPCC_BOT_SEL[dpp_id], 0, + MPCC_BOT_SEL, 0xf); + return; + } + + mpcc_id = tree_cfg->mpcc[z_idx]; + + REG_SET(MPCC_OPP_ID[mpcc_id], 0, + MPCC_OPP_ID, 0xf); + REG_SET(MPCC_TOP_SEL[mpcc_id], 0, + MPCC_TOP_SEL, 0xf); + REG_SET(MPCC_BOT_SEL[mpcc_id], 0, + MPCC_BOT_SEL, 0xf); + + if (z_idx > 0) { + int top_mpcc_id = tree_cfg->mpcc[z_idx - 1]; + + if (z_idx + 1 < tree_cfg->num_pipes) + /* mpcc to be removed is in the middle of the tree */ + REG_SET(MPCC_BOT_SEL[top_mpcc_id], 0, + MPCC_BOT_SEL, tree_cfg->mpcc[z_idx + 1]); + else { + /* mpcc to be removed is at the bottom of the tree */ + REG_SET(MPCC_BOT_SEL[top_mpcc_id], 0, + MPCC_BOT_SEL, 0xf); + REG_UPDATE(MPCC_CONTROL[top_mpcc_id], + MPCC_MODE, MODE_TOP_ONLY); + } + } else if (tree_cfg->num_pipes > 1) + /* mpcc to be removed is at the top of the tree */ + REG_SET(MUX[opp_id], 0, + MPC_OUT_MUX, tree_cfg->mpcc[z_idx + 1]); + else + /* mpcc to be removed is the only one in the tree */ + REG_SET(MUX[opp_id], 0, MPC_OUT_MUX, 0xf); + + /* mark this mpcc as not in use */ + mpc10->mpcc_in_use_mask &= ~(1 << mpcc_id); + tree_cfg->num_pipes--; + for (; z_idx < tree_cfg->num_pipes; z_idx++) { + tree_cfg->dpp[z_idx] = tree_cfg->dpp[z_idx + 1]; + tree_cfg->mpcc[z_idx] = tree_cfg->mpcc[z_idx + 1]; + } + tree_cfg->dpp[tree_cfg->num_pipes] = 0xdeadbeef; + tree_cfg->mpcc[tree_cfg->num_pipes] = 0xdeadbeef; +} + +static void mpc10_add_to_tree_cfg( + struct mpc *mpc, + struct mpcc_cfg *cfg, + int mpcc_id) +{ + struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); + int mpcc_mode = MODE_TOP_ONLY; + int position = cfg->z_index; + struct mpc_tree_cfg *tree_cfg = cfg->tree_cfg; + int alpha_blnd_mode = cfg->per_pixel_alpha ? + BLND_PP_ALPHA : BLND_GLOBAL_ALPHA; + int z_idx; + + REG_SET(MPCC_OPP_ID[mpcc_id], 0, + MPCC_OPP_ID, cfg->opp_id); + + REG_SET(MPCC_TOP_SEL[mpcc_id], 0, + MPCC_TOP_SEL, cfg->dpp_id); + + if (position == 0) { + /* idle dpp/mpcc is added to the top layer of tree */ + + if (tree_cfg->num_pipes > 0) { + /* get instance of previous top mpcc */ + int prev_top_mpcc_id = tree_cfg->mpcc[0]; + + REG_SET(MPCC_BOT_SEL[mpcc_id], 0, + MPCC_BOT_SEL, prev_top_mpcc_id); + mpcc_mode = MODE_BLEND; + } + + /* opp will get new output. from new added mpcc */ + REG_SET(MUX[cfg->opp_id], 0, MPC_OUT_MUX, mpcc_id); + + } else if (position == tree_cfg->num_pipes) { + /* idle dpp/mpcc is added to the bottom layer of tree */ + + /* get instance of previous bottom mpcc, set to middle layer */ + int prev_bot_mpcc_id = tree_cfg->mpcc[tree_cfg->num_pipes - 1]; + + REG_SET(MPCC_BOT_SEL[prev_bot_mpcc_id], 0, + MPCC_BOT_SEL, mpcc_id); + REG_UPDATE(MPCC_CONTROL[prev_bot_mpcc_id], + MPCC_MODE, MODE_BLEND); + + /* mpcc_id become new bottom mpcc*/ + REG_SET(MPCC_BOT_SEL[mpcc_id], 0, + MPCC_BOT_SEL, 0xf); + + } else { + /* idle dpp/mpcc is added to middle of tree */ + int above_mpcc_id = tree_cfg->mpcc[position - 1]; + int below_mpcc_id = tree_cfg->mpcc[position]; + + /* mpcc above new mpcc_id has new bottom mux*/ + REG_SET(MPCC_BOT_SEL[above_mpcc_id], 0, + MPCC_BOT_SEL, mpcc_id); + REG_UPDATE(MPCC_CONTROL[above_mpcc_id], + MPCC_MODE, MODE_BLEND); + + /* mpcc_id bottom mux is from below mpcc*/ + REG_SET(MPCC_BOT_SEL[mpcc_id], 0, + MPCC_BOT_SEL, below_mpcc_id); + mpcc_mode = MODE_BLEND; + } + + REG_SET_4(MPCC_CONTROL[mpcc_id], 0xffffffff, + MPCC_MODE, mpcc_mode, + MPCC_ALPHA_BLND_MODE, alpha_blnd_mode, + MPCC_ALPHA_MULTIPLIED_MODE, cfg->pre_multiplied_alpha, + MPCC_BLND_ACTIVE_OVERLAP_ONLY, false); + + /* update mpc_tree_cfg with new mpcc */ + for (z_idx = tree_cfg->num_pipes; z_idx > position; z_idx--) { + tree_cfg->dpp[z_idx] = tree_cfg->dpp[z_idx - 1]; + tree_cfg->mpcc[z_idx] = tree_cfg->mpcc[z_idx - 1]; + } + tree_cfg->dpp[position] = cfg->dpp_id; + tree_cfg->mpcc[position] = mpcc_id; + tree_cfg->num_pipes++; +} + +int mpc10_mpcc_add(struct mpc *mpc, struct mpcc_cfg *cfg) +{ + struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); + int mpcc_id, z_idx; + + ASSERT(cfg->z_index < mpc10->num_mpcc); + + /* check in dpp already exists in mpc tree */ + for (z_idx = 0; z_idx < cfg->tree_cfg->num_pipes; z_idx++) + if (cfg->tree_cfg->dpp[z_idx] == cfg->dpp_id) + break; + if (z_idx == cfg->tree_cfg->num_pipes) { + ASSERT(cfg->z_index <= cfg->tree_cfg->num_pipes); + mpcc_id = mpc10_get_idle_mpcc_id(mpc10); + + /* + * TODO: remove hack + * Note: currently there is a bug in init_hw such that + * on resume from hibernate, BIOS sets up MPCC0, and + * we do mpcc_remove but the mpcc cannot go to idle + * after remove. This cause us to pick mpcc1 here, + * which causes a pstate hang for yet unknown reason. + */ + mpcc_id = cfg->dpp_id; + /* end hack*/ + + ASSERT(!(mpc10->mpcc_in_use_mask & 1 << mpcc_id)); + + if (mpc->ctx->dc->debug.sanity_checks) + mpc10_assert_mpcc_idle_before_connect(mpc10, mpcc_id); + } else { + ASSERT(cfg->z_index < cfg->tree_cfg->num_pipes); + mpcc_id = cfg->tree_cfg->mpcc[z_idx]; + mpc10_mpcc_remove(mpc, cfg->tree_cfg, cfg->opp_id, cfg->dpp_id); + } + + /* add dpp/mpcc pair to mpc_tree_cfg and update mpcc registers */ + mpc10_add_to_tree_cfg(mpc, cfg, mpcc_id); + + /* set background color */ + mpc10_set_bg_color(mpc10, &cfg->black_color, mpcc_id); + + /* mark this mpcc as in use */ + mpc10->mpcc_in_use_mask |= 1 << mpcc_id; + + return mpcc_id; +} + +void mpc10_update_blend_mode( + struct mpc *mpc, + struct mpcc_cfg *cfg) +{ + struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); + int mpcc_id, z_idx; + int alpha_blnd_mode = cfg->per_pixel_alpha ? + BLND_PP_ALPHA : BLND_GLOBAL_ALPHA; + + /* find z_idx for the dpp that requires blending mode update*/ + for (z_idx = 0; z_idx < cfg->tree_cfg->num_pipes; z_idx++) + if (cfg->tree_cfg->dpp[z_idx] == cfg->dpp_id) + break; + + ASSERT(z_idx < cfg->tree_cfg->num_pipes); + mpcc_id = cfg->tree_cfg->mpcc[z_idx]; + + REG_UPDATE_2(MPCC_CONTROL[mpcc_id], + MPCC_ALPHA_BLND_MODE, alpha_blnd_mode, + MPCC_ALPHA_MULTIPLIED_MODE, cfg->pre_multiplied_alpha); +} + +const struct mpc_funcs dcn10_mpc_funcs = { + .add = mpc10_mpcc_add, + .remove = mpc10_mpcc_remove, + .wait_for_idle = mpc10_assert_idle_mpcc, + .update_blend_mode = mpc10_update_blend_mode, +}; + +void dcn10_mpc_construct(struct dcn10_mpc *mpc10, + struct dc_context *ctx, + const struct dcn_mpc_registers *mpc_regs, + const struct dcn_mpc_shift *mpc_shift, + const struct dcn_mpc_mask *mpc_mask, + int num_mpcc) +{ + mpc10->base.ctx = ctx; + + mpc10->base.funcs = &dcn10_mpc_funcs; + + mpc10->mpc_regs = mpc_regs; + mpc10->mpc_shift = mpc_shift; + mpc10->mpc_mask = mpc_mask; + + mpc10->mpcc_in_use_mask = 0; + mpc10->num_mpcc = num_mpcc; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.h new file mode 100644 index 000000000000..683ce4aaa76e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.h @@ -0,0 +1,138 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_MPCC_DCN10_H__ +#define __DC_MPCC_DCN10_H__ + +#include "mpc.h" + +#define TO_DCN10_MPC(mpc_base) \ + container_of(mpc_base, struct dcn10_mpc, base) + +#define MAX_MPCC 6 +#define MAX_OPP 6 + +#define MPC_COMMON_REG_LIST_DCN1_0(inst) \ + SRII(MPCC_TOP_SEL, MPCC, inst),\ + SRII(MPCC_BOT_SEL, MPCC, inst),\ + SRII(MPCC_CONTROL, MPCC, inst),\ + SRII(MPCC_STATUS, MPCC, inst),\ + SRII(MPCC_OPP_ID, MPCC, inst),\ + SRII(MPCC_BG_G_Y, MPCC, inst),\ + SRII(MPCC_BG_R_CR, MPCC, inst),\ + SRII(MPCC_BG_B_CB, MPCC, inst),\ + SRII(MPCC_BG_B_CB, MPCC, inst) + +#define MPC_OUT_MUX_COMMON_REG_LIST_DCN1_0(inst) \ + SRII(MUX, MPC_OUT, inst) + +#define MPC_COMMON_REG_VARIABLE_LIST \ + uint32_t MPCC_TOP_SEL[MAX_MPCC]; \ + uint32_t MPCC_BOT_SEL[MAX_MPCC]; \ + uint32_t MPCC_CONTROL[MAX_MPCC]; \ + uint32_t MPCC_STATUS[MAX_MPCC]; \ + uint32_t MPCC_OPP_ID[MAX_MPCC]; \ + uint32_t MPCC_BG_G_Y[MAX_MPCC]; \ + uint32_t MPCC_BG_R_CR[MAX_MPCC]; \ + uint32_t MPCC_BG_B_CB[MAX_MPCC]; \ + uint32_t MUX[MAX_OPP]; + +#define MPC_COMMON_MASK_SH_LIST_DCN1_0(mask_sh)\ + SF(MPCC0_MPCC_TOP_SEL, MPCC_TOP_SEL, mask_sh),\ + SF(MPCC0_MPCC_BOT_SEL, MPCC_BOT_SEL, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_MODE, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_ALPHA_BLND_MODE, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_ALPHA_MULTIPLIED_MODE, mask_sh),\ + SF(MPCC0_MPCC_CONTROL, MPCC_BLND_ACTIVE_OVERLAP_ONLY, mask_sh),\ + SF(MPCC0_MPCC_STATUS, MPCC_IDLE, mask_sh),\ + SF(MPCC0_MPCC_STATUS, MPCC_BUSY, mask_sh),\ + SF(MPCC0_MPCC_OPP_ID, MPCC_OPP_ID, mask_sh),\ + SF(MPCC0_MPCC_BG_G_Y, MPCC_BG_G_Y, mask_sh),\ + SF(MPCC0_MPCC_BG_R_CR, MPCC_BG_R_CR, mask_sh),\ + SF(MPCC0_MPCC_BG_B_CB, MPCC_BG_B_CB, mask_sh),\ + SF(MPC_OUT0_MUX, MPC_OUT_MUX, mask_sh) + +#define MPC_REG_FIELD_LIST(type) \ + type MPCC_TOP_SEL;\ + type MPCC_BOT_SEL;\ + type MPCC_MODE;\ + type MPCC_ALPHA_BLND_MODE;\ + type MPCC_ALPHA_MULTIPLIED_MODE;\ + type MPCC_BLND_ACTIVE_OVERLAP_ONLY;\ + type MPCC_IDLE;\ + type MPCC_BUSY;\ + type MPCC_OPP_ID;\ + type MPCC_BG_G_Y;\ + type MPCC_BG_R_CR;\ + type MPCC_BG_B_CB;\ + type MPC_OUT_MUX; + +struct dcn_mpc_registers { + MPC_COMMON_REG_VARIABLE_LIST +}; + +struct dcn_mpc_shift { + MPC_REG_FIELD_LIST(uint8_t) +}; + +struct dcn_mpc_mask { + MPC_REG_FIELD_LIST(uint32_t) +}; + +struct dcn10_mpc { + struct mpc base; + + int mpcc_in_use_mask; + int num_mpcc; + const struct dcn_mpc_registers *mpc_regs; + const struct dcn_mpc_shift *mpc_shift; + const struct dcn_mpc_mask *mpc_mask; +}; + +void dcn10_mpc_construct(struct dcn10_mpc *mpcc10, + struct dc_context *ctx, + const struct dcn_mpc_registers *mpc_regs, + const struct dcn_mpc_shift *mpc_shift, + const struct dcn_mpc_mask *mpc_mask, + int num_mpcc); + +int mpc10_mpcc_add( + struct mpc *mpc, + struct mpcc_cfg *cfg); + +void mpc10_mpcc_remove( + struct mpc *mpc, + struct mpc_tree_cfg *tree_cfg, + int opp_id, + int dpp_id); + +void mpc10_assert_idle_mpcc( + struct mpc *mpc, + int id); + +void mpc10_update_blend_mode( + struct mpc *mpc, + struct mpcc_cfg *cfg); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.c new file mode 100644 index 000000000000..a136f70b7a3c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.c @@ -0,0 +1,351 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dcn10_opp.h" +#include "reg_helper.h" + +#define REG(reg) \ + (oppn10->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + oppn10->opp_shift->field_name, oppn10->opp_mask->field_name + +#define CTX \ + oppn10->base.ctx + + + +/************* FORMATTER ************/ + +/** + * set_truncation + * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp + * 2) enable truncation + * 3) HW remove 12bit FMT support for DCE11 power saving reason. + */ +static void set_truncation( + struct dcn10_opp *oppn10, + const struct bit_depth_reduction_params *params) +{ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, params->flags.TRUNCATE_ENABLED, + FMT_TRUNCATE_DEPTH, params->flags.TRUNCATE_DEPTH, + FMT_TRUNCATE_MODE, params->flags.TRUNCATE_MODE); +} + +static void set_spatial_dither( + struct dcn10_opp *oppn10, + const struct bit_depth_reduction_params *params) +{ + /*Disable spatial (random) dithering*/ + REG_UPDATE_7(FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_EN, 0, + FMT_SPATIAL_DITHER_MODE, 0, + FMT_SPATIAL_DITHER_DEPTH, 0, + FMT_TEMPORAL_DITHER_EN, 0, + FMT_HIGHPASS_RANDOM_ENABLE, 0, + FMT_FRAME_RANDOM_ENABLE, 0, + FMT_RGB_RANDOM_ENABLE, 0); + + + /* only use FRAME_COUNTER_MAX if frameRandom == 1*/ + if (params->flags.FRAME_RANDOM == 1) { + if (params->flags.SPATIAL_DITHER_DEPTH == 0 || params->flags.SPATIAL_DITHER_DEPTH == 1) { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 15, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 2); + } else if (params->flags.SPATIAL_DITHER_DEPTH == 2) { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 3, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 1); + } else { + return; + } + } else { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 0, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 0); + } + + /*Set seed for random values for + * spatial dithering for R,G,B channels*/ + + REG_SET(FMT_DITHER_RAND_R_SEED, 0, + FMT_RAND_R_SEED, params->r_seed_value); + + REG_SET(FMT_DITHER_RAND_G_SEED, 0, + FMT_RAND_G_SEED, params->g_seed_value); + + REG_SET(FMT_DITHER_RAND_B_SEED, 0, + FMT_RAND_B_SEED, params->b_seed_value); + + /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero + * offset for the R/Cr channel, lower 4LSB + * is forced to zeros. Typically set to 0 + * RGB and 0x80000 YCbCr. + */ + /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero + * offset for the G/Y channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB + * and 0x80000 YCbCr. + */ + /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero + * offset for the B/Cb channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB and + * 0x80000 YCbCr. + */ + + REG_UPDATE_6(FMT_BIT_DEPTH_CONTROL, + /*Enable spatial dithering*/ + FMT_SPATIAL_DITHER_EN, params->flags.SPATIAL_DITHER_ENABLED, + /* Set spatial dithering mode + * (default is Seed patterrn AAAA...) + */ + FMT_SPATIAL_DITHER_MODE, params->flags.SPATIAL_DITHER_MODE, + /*Set spatial dithering bit depth*/ + FMT_SPATIAL_DITHER_DEPTH, params->flags.SPATIAL_DITHER_DEPTH, + /*Disable High pass filter*/ + FMT_HIGHPASS_RANDOM_ENABLE, params->flags.HIGHPASS_RANDOM, + /*Reset only at startup*/ + FMT_FRAME_RANDOM_ENABLE, params->flags.FRAME_RANDOM, + /*Set RGB data dithered with x^28+x^3+1*/ + FMT_RGB_RANDOM_ENABLE, params->flags.RGB_RANDOM); +} + +static void oppn10_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params) +{ + struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); + + set_truncation(oppn10, params); + set_spatial_dither(oppn10, params); + /* TODO + * set_temporal_dither(oppn10, params); + */ +} + +/** + * set_pixel_encoding + * + * Set Pixel Encoding + * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly + * 1: YCbCr 4:2:2 + */ +static void set_pixel_encoding( + struct dcn10_opp *oppn10, + const struct clamping_and_pixel_encoding_params *params) +{ + switch (params->pixel_encoding) { + + case PIXEL_ENCODING_RGB: + case PIXEL_ENCODING_YCBCR444: + REG_UPDATE(FMT_CONTROL, FMT_PIXEL_ENCODING, 0); + break; + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(FMT_CONTROL, FMT_PIXEL_ENCODING, 1); + break; + case PIXEL_ENCODING_YCBCR420: + REG_UPDATE(FMT_CONTROL, FMT_PIXEL_ENCODING, 2); + break; + default: + break; + } +} + +/** + * Set Clamping + * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) + * 1 for 8 bpc + * 2 for 10 bpc + * 3 for 12 bpc + * 7 for programable + * 2) Enable clamp if Limited range requested + */ +static void opp_set_clamping( + struct dcn10_opp *oppn10, + const struct clamping_and_pixel_encoding_params *params) +{ + REG_UPDATE_2(FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN, 0, + FMT_CLAMP_COLOR_FORMAT, 0); + + switch (params->clamping_level) { + case CLAMPING_FULL_RANGE: + REG_UPDATE_2(FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 0); + break; + case CLAMPING_LIMITED_RANGE_8BPC: + REG_UPDATE_2(FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 1); + break; + case CLAMPING_LIMITED_RANGE_10BPC: + REG_UPDATE_2(FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 2); + + break; + case CLAMPING_LIMITED_RANGE_12BPC: + REG_UPDATE_2(FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 3); + break; + case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: + /* TODO */ + default: + break; + } + +} + +static void oppn10_set_dyn_expansion( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal) +{ + struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); + + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 0, + FMT_DYNAMIC_EXP_MODE, 0); + + /*00 - 10-bit -> 12-bit dynamic expansion*/ + /*01 - 8-bit -> 12-bit dynamic expansion*/ + if (signal == SIGNAL_TYPE_HDMI_TYPE_A || + signal == SIGNAL_TYPE_DISPLAY_PORT || + signal == SIGNAL_TYPE_DISPLAY_PORT_MST || + signal == SIGNAL_TYPE_VIRTUAL) { + switch (color_dpth) { + case COLOR_DEPTH_888: + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1, + FMT_DYNAMIC_EXP_MODE, 1); + break; + case COLOR_DEPTH_101010: + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1, + FMT_DYNAMIC_EXP_MODE, 0); + break; + case COLOR_DEPTH_121212: + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1,/*otherwise last two bits are zero*/ + FMT_DYNAMIC_EXP_MODE, 0); + break; + default: + break; + } + } +} + +static void opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params) +{ + struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); + + opp_set_clamping(oppn10, params); + set_pixel_encoding(oppn10, params); +} + +static void oppn10_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping) +{ + struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); + + if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) + REG_UPDATE(FMT_MAP420_MEMORY_CONTROL, FMT_MAP420MEM_PWR_FORCE, 0); + + /* dithering is affected by , hence should be + * programmed afterwards */ + oppn10_program_bit_depth_reduction( + opp, + fmt_bit_depth); + + opp_program_clamping_and_pixel_encoding( + opp, + clamping); + + return; +} + + + +static void oppn10_set_stereo_polarity( + struct output_pixel_processor *opp, + bool enable, bool rightEyePolarity) +{ + struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); + + REG_UPDATE(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, enable); +} + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static void dcn10_opp_destroy(struct output_pixel_processor **opp) +{ + kfree(TO_DCN10_OPP(*opp)); + *opp = NULL; +} + +static struct opp_funcs dcn10_opp_funcs = { + .opp_set_dyn_expansion = oppn10_set_dyn_expansion, + .opp_program_fmt = oppn10_program_fmt, + .opp_program_bit_depth_reduction = oppn10_program_bit_depth_reduction, + .opp_set_stereo_polarity = oppn10_set_stereo_polarity, + .opp_destroy = dcn10_opp_destroy +}; + +void dcn10_opp_construct(struct dcn10_opp *oppn10, + struct dc_context *ctx, + uint32_t inst, + const struct dcn10_opp_registers *regs, + const struct dcn10_opp_shift *opp_shift, + const struct dcn10_opp_mask *opp_mask) +{ + int i; + oppn10->base.ctx = ctx; + oppn10->base.inst = inst; + oppn10->base.funcs = &dcn10_opp_funcs; + + oppn10->base.mpc_tree.dpp[0] = inst; + oppn10->base.mpc_tree.mpcc[0] = inst; + oppn10->base.mpc_tree.num_pipes = 1; + for (i = 0; i < MAX_PIPES; i++) + oppn10->base.mpcc_disconnect_pending[i] = false; + + oppn10->regs = regs; + oppn10->opp_shift = opp_shift; + oppn10->opp_mask = opp_mask; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.h new file mode 100644 index 000000000000..790ce6014832 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.h @@ -0,0 +1,186 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_OPP_DCN10_H__ +#define __DC_OPP_DCN10_H__ + +#include "opp.h" + +#define TO_DCN10_OPP(opp)\ + container_of(opp, struct dcn10_opp, base) + +#define OPP_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define OPP_REG_LIST_DCN(id) \ + SRI(FMT_BIT_DEPTH_CONTROL, FMT, id), \ + SRI(FMT_CONTROL, FMT, id), \ + SRI(FMT_DITHER_RAND_R_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_G_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_B_SEED, FMT, id), \ + SRI(FMT_CLAMP_CNTL, FMT, id), \ + SRI(FMT_DYNAMIC_EXP_CNTL, FMT, id), \ + SRI(FMT_MAP420_MEMORY_CONTROL, FMT, id) + +#define OPP_REG_LIST_DCN10(id) \ + OPP_REG_LIST_DCN(id) + +#define OPP_MASK_SH_LIST_DCN(mask_sh) \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE, mask_sh), \ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_PIXEL_ENCODING, mask_sh), \ + OPP_SF(FMT0_FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh), \ + OPP_SF(FMT0_FMT_DITHER_RAND_R_SEED, FMT_RAND_R_SEED, mask_sh), \ + OPP_SF(FMT0_FMT_DITHER_RAND_G_SEED, FMT_RAND_G_SEED, mask_sh), \ + OPP_SF(FMT0_FMT_DITHER_RAND_B_SEED, FMT_RAND_B_SEED, mask_sh), \ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_DATA_EN, mask_sh), \ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_COLOR_FORMAT, mask_sh), \ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, mask_sh), \ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE, mask_sh), \ + OPP_SF(FMT0_FMT_MAP420_MEMORY_CONTROL, FMT_MAP420MEM_PWR_FORCE, mask_sh) + +#define OPP_MASK_SH_LIST_DCN10(mask_sh) \ + OPP_MASK_SH_LIST_DCN(mask_sh) + +#define OPP_DCN10_REG_FIELD_LIST(type) \ + type DPG_EN; \ + type DPG_MODE; \ + type DPG_VRES; \ + type DPG_HRES; \ + type DPG_COLOUR0_R_CR; \ + type DPG_COLOUR1_R_CR; \ + type DPG_COLOUR0_B_CB; \ + type DPG_COLOUR1_B_CB; \ + type DPG_COLOUR0_G_Y; \ + type DPG_COLOUR1_G_Y; \ + type CM_OCSC_C11; \ + type CM_OCSC_C12; \ + type CM_OCSC_C13; \ + type CM_OCSC_C14; \ + type CM_OCSC_C21; \ + type CM_OCSC_C22; \ + type CM_OCSC_C23; \ + type CM_OCSC_C24; \ + type CM_OCSC_C31; \ + type CM_OCSC_C32; \ + type CM_OCSC_C33; \ + type CM_OCSC_C34; \ + type CM_COMB_C11; \ + type CM_COMB_C12; \ + type CM_COMB_C13; \ + type CM_COMB_C14; \ + type CM_COMB_C21; \ + type CM_COMB_C22; \ + type CM_COMB_C23; \ + type CM_COMB_C24; \ + type CM_COMB_C31; \ + type CM_COMB_C32; \ + type CM_COMB_C33; \ + type CM_COMB_C34; \ + type FMT_TRUNCATE_EN; \ + type FMT_TRUNCATE_DEPTH; \ + type FMT_TRUNCATE_MODE; \ + type FMT_SPATIAL_DITHER_EN; \ + type FMT_SPATIAL_DITHER_MODE; \ + type FMT_SPATIAL_DITHER_DEPTH; \ + type FMT_TEMPORAL_DITHER_EN; \ + type FMT_HIGHPASS_RANDOM_ENABLE; \ + type FMT_FRAME_RANDOM_ENABLE; \ + type FMT_RGB_RANDOM_ENABLE; \ + type FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX; \ + type FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP; \ + type FMT_RAND_R_SEED; \ + type FMT_RAND_G_SEED; \ + type FMT_RAND_B_SEED; \ + type FMT_PIXEL_ENCODING; \ + type FMT_CLAMP_DATA_EN; \ + type FMT_CLAMP_COLOR_FORMAT; \ + type FMT_DYNAMIC_EXP_EN; \ + type FMT_DYNAMIC_EXP_MODE; \ + type FMT_MAP420MEM_PWR_FORCE; \ + type FMT_STEREOSYNC_OVERRIDE + +struct dcn10_opp_shift { + OPP_DCN10_REG_FIELD_LIST(uint8_t); +}; + +struct dcn10_opp_mask { + OPP_DCN10_REG_FIELD_LIST(uint32_t); +}; + +struct dcn10_opp_registers { + uint32_t DPG_CONTROL; + uint32_t DPG_COLOUR_B_CB; + uint32_t DPG_COLOUR_G_Y; + uint32_t DPG_COLOUR_R_CR; + uint32_t CM_OCSC_C11_C12; + uint32_t CM_OCSC_C13_C14; + uint32_t CM_OCSC_C21_C22; + uint32_t CM_OCSC_C23_C24; + uint32_t CM_OCSC_C31_C32; + uint32_t CM_OCSC_C33_C34; + uint32_t CM_COMB_C11_C12; + uint32_t CM_COMB_C13_C14; + uint32_t CM_COMB_C21_C22; + uint32_t CM_COMB_C23_C24; + uint32_t CM_COMB_C31_C32; + uint32_t CM_COMB_C33_C34; + uint32_t FMT_BIT_DEPTH_CONTROL; + uint32_t FMT_CONTROL; + uint32_t FMT_DITHER_RAND_R_SEED; + uint32_t FMT_DITHER_RAND_G_SEED; + uint32_t FMT_DITHER_RAND_B_SEED; + uint32_t FMT_CLAMP_CNTL; + uint32_t FMT_DYNAMIC_EXP_CNTL; + uint32_t FMT_MAP420_MEMORY_CONTROL; +}; + +struct dcn10_opp { + struct output_pixel_processor base; + + const struct dcn10_opp_registers *regs; + const struct dcn10_opp_shift *opp_shift; + const struct dcn10_opp_mask *opp_mask; + + bool is_write_to_ram_a_safe; +}; + +void dcn10_opp_construct(struct dcn10_opp *oppn10, + struct dc_context *ctx, + uint32_t inst, + const struct dcn10_opp_registers *regs, + const struct dcn10_opp_shift *opp_shift, + const struct dcn10_opp_mask *opp_mask); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c new file mode 100644 index 000000000000..4c4bd72d4e40 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c @@ -0,0 +1,1466 @@ +/* +* Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dc.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "dcn10/dcn10_resource.h" + +#include "dcn10/dcn10_ipp.h" +#include "dcn10/dcn10_mpc.h" +#include "irq/dcn10/irq_service_dcn10.h" +#include "dcn10/dcn10_dpp.h" +#include "dcn10/dcn10_timing_generator.h" +#include "dcn10/dcn10_hw_sequencer.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dcn10/dcn10_opp.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_clocks.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_audio.h" +#include "dce/dce_hwseq.h" +#include "../virtual/virtual_stream_encoder.h" +#include "dce110/dce110_resource.h" +#include "dce112/dce112_resource.h" +#include "dcn10_hubp.h" + +#include "vega10/soc15ip.h" + +#include "raven1/DCN/dcn_1_0_offset.h" +#include "raven1/DCN/dcn_1_0_sh_mask.h" + +#include "raven1/NBIO/nbio_7_0_offset.h" + +#include "raven1/MMHUB/mmhub_9_1_offset.h" +#include "raven1/MMHUB/mmhub_9_1_sh_mask.h" + +#include "reg_helper.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" + +#ifndef mmDP0_DP_DPHY_INTERNAL_CTRL + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x210f + #define mmDP0_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x220f + #define mmDP1_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x230f + #define mmDP2_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x240f + #define mmDP3_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x250f + #define mmDP4_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x260f + #define mmDP5_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x270f + #define mmDP6_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 +#endif + + +enum dcn10_clk_src_array_id { + DCN10_CLK_SRC_PLL0, + DCN10_CLK_SRC_PLL1, + DCN10_CLK_SRC_PLL2, + DCN10_CLK_SRC_PLL3, + DCN10_CLK_SRC_TOTAL +}; + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +/* DCN */ +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +#define BASE(seg) \ + BASE_INNER(seg) + +#define SR(reg_name)\ + .reg_name = BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +#define SRI(reg_name, block, id)\ + .reg_name = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + + +#define SRII(reg_name, block, id)\ + .reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +/* NBIO */ +#define NBIO_BASE_INNER(seg) \ + NBIF_BASE__INST0_SEG ## seg + +#define NBIO_BASE(seg) \ + NBIO_BASE_INNER(seg) + +#define NBIO_SR(reg_name)\ + .reg_name = NBIO_BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +/* MMHUB */ +#define MMHUB_BASE_INNER(seg) \ + MMHUB_BASE__INST0_SEG ## seg + +#define MMHUB_BASE(seg) \ + MMHUB_BASE_INNER(seg) + +#define MMHUB_SR(reg_name)\ + .reg_name = MMHUB_BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCN10_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCN10(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCN10(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCN10_REG_LIST(0) +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCN10(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCN10(_MASK) +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_DCN_REG_LIST(id),\ + .TMDS_CNTL = 0,\ + .AFMT_AVI_INFO0 = 0,\ + .AFMT_AVI_INFO1 = 0,\ + .AFMT_AVI_INFO2 = 0,\ + .AFMT_AVI_INFO3 = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCN10(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCN10(_MASK), + .AFMT_GENERIC0_UPDATE = 0, + .AFMT_GENERIC2_UPDATE = 0, + .DP_DYN_RANGE = 0, + .DP_YCBCR_RANGE = 0, + .HDMI_AVI_INFO_SEND = 0, + .HDMI_AVI_INFO_CONT = 0, + .HDMI_AVI_INFO_LINE = 0, + .DP_SEC_AVI_ENABLE = 0, + .AFMT_AVI_INFO_VERSION = 0 +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), +}; + +#define DCE120_AUD_COMMON_MASK_SH_LIST(mask_sh)\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh),\ + AUD_COMMON_MASK_SH_LIST_BASE(mask_sh) + +static const struct dce_audio_shift audio_shift = { + DCE120_AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_aduio_mask audio_mask = { + DCE120_AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCN10_REG_LIST(id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id) \ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_REG_LIST_DCN10(id),\ +} + +static const struct dcn10_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), +}; + +static const struct dcn10_ipp_shift ipp_shift = { + IPP_MASK_SH_LIST_DCN10(__SHIFT) +}; + +static const struct dcn10_ipp_mask ipp_mask = { + IPP_MASK_SH_LIST_DCN10(_MASK), +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_REG_LIST_DCN10(id),\ +} + +static const struct dcn10_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), +}; + +static const struct dcn10_opp_shift opp_shift = { + OPP_MASK_SH_LIST_DCN10(__SHIFT) +}; + +static const struct dcn10_opp_mask opp_mask = { + OPP_MASK_SH_LIST_DCN10(_MASK), +}; + +#define tf_regs(id)\ +[id] = {\ + TF_REG_LIST_DCN10(id),\ +} + +static const struct dcn_dpp_registers tf_regs[] = { + tf_regs(0), + tf_regs(1), + tf_regs(2), + tf_regs(3), +}; + +static const struct dcn_dpp_shift tf_shift = { + TF_REG_LIST_SH_MASK_DCN10(__SHIFT) +}; + +static const struct dcn_dpp_mask tf_mask = { + TF_REG_LIST_SH_MASK_DCN10(_MASK), +}; + +static const struct dcn_mpc_registers mpc_regs = { + MPC_COMMON_REG_LIST_DCN1_0(0), + MPC_COMMON_REG_LIST_DCN1_0(1), + MPC_COMMON_REG_LIST_DCN1_0(2), + MPC_COMMON_REG_LIST_DCN1_0(3), + MPC_OUT_MUX_COMMON_REG_LIST_DCN1_0(0), + MPC_OUT_MUX_COMMON_REG_LIST_DCN1_0(1), + MPC_OUT_MUX_COMMON_REG_LIST_DCN1_0(2), + MPC_OUT_MUX_COMMON_REG_LIST_DCN1_0(3) +}; + +static const struct dcn_mpc_shift mpc_shift = { + MPC_COMMON_MASK_SH_LIST_DCN1_0(__SHIFT) +}; + +static const struct dcn_mpc_mask mpc_mask = { + MPC_COMMON_MASK_SH_LIST_DCN1_0(_MASK), +}; + +#define tg_regs(id)\ +[id] = {TG_COMMON_REG_LIST_DCN1_0(id)} + +static const struct dcn_tg_registers tg_regs[] = { + tg_regs(0), + tg_regs(1), + tg_regs(2), + tg_regs(3), +}; + +static const struct dcn_tg_shift tg_shift = { + TG_COMMON_MASK_SH_LIST_DCN1_0(__SHIFT) +}; + +static const struct dcn_tg_mask tg_mask = { + TG_COMMON_MASK_SH_LIST_DCN1_0(_MASK) +}; + + +static const struct bios_registers bios_regs = { + NBIO_SR(BIOS_SCRATCH_6) +}; + +#define mi_regs(id)\ +[id] = {\ + MI_REG_LIST_DCN10(id)\ +} + + +static const struct dcn_mi_registers mi_regs[] = { + mi_regs(0), + mi_regs(1), + mi_regs(2), + mi_regs(3), +}; + +static const struct dcn_mi_shift mi_shift = { + MI_MASK_SH_LIST_DCN10(__SHIFT) +}; + +static const struct dcn_mi_mask mi_mask = { + MI_MASK_SH_LIST_DCN10(_MASK) +}; + +#define clk_src_regs(index, pllid)\ +[index] = {\ + CS_COMMON_REG_LIST_DCN1_0(index, pllid),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0, A), + clk_src_regs(1, B), + clk_src_regs(2, C), + clk_src_regs(3, D) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCN1_0(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCN1_0(_MASK) +}; + + +static const struct resource_caps res_cap = { + .num_timing_generator = 4, + .num_video_plane = 4, + .num_audio = 4, + .num_stream_encoder = 4, + .num_pll = 4, +}; + +static const struct dc_debug debug_defaults_drv = { + .sanity_checks = true, + .disable_dmcu = true, + .force_abm_enable = false, + .timing_trace = false, + .clock_trace = true, + + .min_disp_clk_khz = 300000, + + .disable_pplib_clock_request = true, + .disable_pplib_wm_range = false, + .pplib_wm_report_mode = WM_REPORT_DEFAULT, + .pipe_split_policy = MPC_SPLIT_AVOID_MULT_DISP, + .force_single_disp_pipe_split = true, + .disable_dcc = DCC_ENABLE, + .voltage_align_fclk = true, + .disable_stereo_support = true, + .vsr_support = true, + .performance_trace = false, +}; + +static const struct dc_debug debug_defaults_diags = { + .disable_dmcu = true, + .force_abm_enable = false, + .timing_trace = true, + .clock_trace = true, + .disable_stutter = true, + .disable_pplib_clock_request = true, + .disable_pplib_wm_range = true +}; + +static void dcn10_dpp_destroy(struct dpp **dpp) +{ + kfree(TO_DCN10_DPP(*dpp)); + *dpp = NULL; +} + +static struct dpp *dcn10_dpp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dcn10_dpp *dpp = + kzalloc(sizeof(struct dcn10_dpp), GFP_KERNEL); + + if (!dpp) + return NULL; + + dpp1_construct(dpp, ctx, inst, + &tf_regs[inst], &tf_shift, &tf_mask); + return &dpp->base; +} + +static struct input_pixel_processor *dcn10_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dcn10_ipp *ipp = + kzalloc(sizeof(struct dcn10_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dcn10_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + + +static struct output_pixel_processor *dcn10_opp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dcn10_opp *opp = + kzalloc(sizeof(struct dcn10_opp), GFP_KERNEL); + + if (!opp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dcn10_opp_construct(opp, ctx, inst, + &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static struct mpc *dcn10_mpc_create(struct dc_context *ctx) +{ + struct dcn10_mpc *mpc10 = kzalloc(sizeof(struct dcn10_mpc), + GFP_KERNEL); + + if (!mpc10) + return NULL; + + dcn10_mpc_construct(mpc10, ctx, + &mpc_regs, + &mpc_shift, + &mpc_mask, + 4); + + return &mpc10->base; +} + +static struct timing_generator *dcn10_timing_generator_create( + struct dc_context *ctx, + uint32_t instance) +{ + struct dcn10_timing_generator *tgn10 = + kzalloc(sizeof(struct dcn10_timing_generator), GFP_KERNEL); + + if (!tgn10) + return NULL; + + tgn10->base.inst = instance; + tgn10->base.ctx = ctx; + + tgn10->tg_regs = &tg_regs[instance]; + tgn10->tg_shift = &tg_shift; + tgn10->tg_mask = &tg_mask; + + dcn10_timing_generator_init(tgn10); + + return &tgn10->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .ycbcr420_supported = true, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true, + .flags.bits.IS_YCBCR_CAPABLE = true +}; + +struct link_encoder *dcn10_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + + return &enc110->base; +} + +struct clock_source *dcn10_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + generic_reg_get(ctx, mmDC_PINSTRAPS + BASE(mmDC_PINSTRAPS_BASE_IDX), + FN(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO), &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct stream_encoder *dcn10_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCN1_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCN1_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCN1_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dcn10_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dcn10_stream_encoder_create, + .create_hwseq = dcn10_hwseq_create, +}; + +static const struct resource_create_funcs res_create_maximus_funcs = { + .read_dce_straps = NULL, + .create_audio = NULL, + .create_stream_encoder = NULL, + .create_hwseq = dcn10_hwseq_create, +}; + +void dcn10_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static struct pp_smu_funcs_rv *dcn10_pp_smu_create(struct dc_context *ctx) +{ + struct pp_smu_funcs_rv *pp_smu = kzalloc(sizeof(*pp_smu), GFP_KERNEL); + + if (!pp_smu) + return pp_smu; + + dm_pp_get_funcs_rv(ctx, pp_smu); + return pp_smu; +} + +static void destruct(struct dcn10_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) { + /* TODO: free dcn version of stream encoder once implemented + * rather than using virtual stream encoder + */ + kfree(pool->base.stream_enc[i]); + pool->base.stream_enc[i] = NULL; + } + } + + if (pool->base.mpc != NULL) { + kfree(TO_DCN10_MPC(pool->base.mpc)); + pool->base.mpc = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + pool->base.opps[i]->funcs->opp_destroy(&pool->base.opps[i]); + + if (pool->base.dpps[i] != NULL) + dcn10_dpp_destroy(&pool->base.dpps[i]); + + if (pool->base.ipps[i] != NULL) + pool->base.ipps[i]->funcs->ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.hubps[i] != NULL) { + kfree(TO_DCN10_HUBP(pool->base.hubps[i])); + pool->base.hubps[i] = NULL; + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCN10TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) + kfree(pool->base.stream_enc[i]); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i]) + dce_aud_destroy(&pool->base.audios[i]); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dcn10_clock_source_destroy(&pool->base.clock_sources[i]); + pool->base.clock_sources[i] = NULL; + } + } + + if (pool->base.dp_clock_source != NULL) { + dcn10_clock_source_destroy(&pool->base.dp_clock_source); + pool->base.dp_clock_source = NULL; + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.display_clock != NULL) + dce_disp_clk_destroy(&pool->base.display_clock); + + kfree(pool->base.pp_smu); +} + +static struct hubp *dcn10_hubp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dcn10_hubp *hubp1 = + kzalloc(sizeof(struct dcn10_hubp), GFP_KERNEL); + + if (!hubp1) + return NULL; + + dcn10_hubp_construct(hubp1, ctx, inst, + &mi_regs[inst], &mi_shift, &mi_mask); + return &hubp1->base; +} + +static void get_pixel_clock_parameters( + const struct pipe_ctx *pipe_ctx, + struct pixel_clk_params *pixel_clk_params) +{ + const struct dc_stream_state *stream = pipe_ctx->stream; + pixel_clk_params->requested_pix_clk = stream->timing.pix_clk_khz; + pixel_clk_params->encoder_object_id = stream->sink->link->link_enc->id; + pixel_clk_params->signal_type = pipe_ctx->stream->signal; + pixel_clk_params->controller_id = pipe_ctx->pipe_idx + 1; + /* TODO: un-hardcode*/ + pixel_clk_params->requested_sym_clk = LINK_RATE_LOW * + LINK_RATE_REF_FREQ_IN_KHZ; + pixel_clk_params->flags.ENABLE_SS = 0; + pixel_clk_params->color_depth = + stream->timing.display_color_depth; + pixel_clk_params->flags.DISPLAY_BLANKED = 1; + pixel_clk_params->pixel_encoding = stream->timing.pixel_encoding; + + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) + pixel_clk_params->color_depth = COLOR_DEPTH_888; + + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + pixel_clk_params->requested_pix_clk /= 2; + +} + +static void build_clamping_params(struct dc_stream_state *stream) +{ + stream->clamping.clamping_level = CLAMPING_FULL_RANGE; + stream->clamping.c_depth = stream->timing.display_color_depth; + stream->clamping.pixel_encoding = stream->timing.pixel_encoding; +} + +static void build_pipe_hw_param(struct pipe_ctx *pipe_ctx) +{ + + get_pixel_clock_parameters(pipe_ctx, &pipe_ctx->stream_res.pix_clk_params); + + pipe_ctx->clock_source->funcs->get_pix_clk_dividers( + pipe_ctx->clock_source, + &pipe_ctx->stream_res.pix_clk_params, + &pipe_ctx->pll_settings); + + pipe_ctx->stream->clamping.pixel_encoding = pipe_ctx->stream->timing.pixel_encoding; + + resource_build_bit_depth_reduction_params(pipe_ctx->stream, + &pipe_ctx->stream->bit_depth_params); + build_clamping_params(pipe_ctx->stream); +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + /*TODO Seems unneeded anymore */ + /* if (old_context && resource_is_stream_unchanged(old_context, stream)) { + if (stream != NULL && old_context->streams[i] != NULL) { + todo: shouldn't have to copy missing parameter here + resource_build_bit_depth_reduction_params(stream, + &stream->bit_depth_params); + stream->clamping.pixel_encoding = + stream->timing.pixel_encoding; + + resource_build_bit_depth_reduction_params(stream, + &stream->bit_depth_params); + build_clamping_params(stream); + + continue; + } + } + */ + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + build_pipe_hw_param(pipe_ctx); + return DC_OK; +} + +enum dc_status dcn10_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + result = resource_map_pool_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = resource_map_phy_clock_resources(dc, new_ctx, dc_stream); + + + if (result == DC_OK) + result = build_mapped_resource(dc, new_ctx, dc_stream); + + return result; +} + +enum dc_status dcn10_validate_guaranteed( + struct dc *dc, + struct dc_stream_state *dc_stream, + struct dc_state *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + context->streams[0] = dc_stream; + dc_stream_retain(context->streams[0]); + context->stream_count++; + + result = resource_map_pool_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = resource_map_phy_clock_resources(dc, context, dc_stream); + + if (result == DC_OK) + result = build_mapped_resource(dc, context, dc_stream); + + if (result == DC_OK) { + validate_guaranteed_copy_streams( + context, dc->caps.max_streams); + result = resource_build_scaling_params_for_context(dc, context); + } + if (result == DC_OK && !dcn_validate_bandwidth(dc, context)) + return DC_FAIL_BANDWIDTH_VALIDATE; + + return result; +} + +static struct pipe_ctx *dcn10_acquire_idle_pipe_for_layer( + struct dc_state *context, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + struct resource_context *res_ctx = &context->res_ctx; + struct pipe_ctx *head_pipe = resource_get_head_pipe_for_stream(res_ctx, stream); + struct pipe_ctx *idle_pipe = find_idle_secondary_pipe(res_ctx, pool); + + if (!head_pipe) + ASSERT(0); + + if (!idle_pipe) + return false; + + idle_pipe->stream = head_pipe->stream; + idle_pipe->stream_res.tg = head_pipe->stream_res.tg; + idle_pipe->stream_res.opp = head_pipe->stream_res.opp; + + idle_pipe->plane_res.hubp = pool->hubps[idle_pipe->pipe_idx]; + idle_pipe->plane_res.ipp = pool->ipps[idle_pipe->pipe_idx]; + idle_pipe->plane_res.dpp = pool->dpps[idle_pipe->pipe_idx]; + + return idle_pipe; +} + +enum dcc_control { + dcc_control__256_256_xxx, + dcc_control__128_128_xxx, + dcc_control__256_64_64, +}; + +enum segment_order { + segment_order__na, + segment_order__contiguous, + segment_order__non_contiguous, +}; + +static bool dcc_support_pixel_format( + enum surface_pixel_format format, + unsigned int *bytes_per_element) +{ + /* DML: get_bytes_per_element */ + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + *bytes_per_element = 2; + return true; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + *bytes_per_element = 4; + return true; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + *bytes_per_element = 8; + return true; + default: + return false; + } +} + +static bool dcc_support_swizzle( + enum swizzle_mode_values swizzle, + unsigned int bytes_per_element, + enum segment_order *segment_order_horz, + enum segment_order *segment_order_vert) +{ + bool standard_swizzle = false; + bool display_swizzle = false; + + switch (swizzle) { + case DC_SW_4KB_S: + case DC_SW_64KB_S: + case DC_SW_VAR_S: + case DC_SW_4KB_S_X: + case DC_SW_64KB_S_X: + case DC_SW_VAR_S_X: + standard_swizzle = true; + break; + case DC_SW_4KB_D: + case DC_SW_64KB_D: + case DC_SW_VAR_D: + case DC_SW_4KB_D_X: + case DC_SW_64KB_D_X: + case DC_SW_VAR_D_X: + display_swizzle = true; + break; + default: + break; + } + + if (bytes_per_element == 1 && standard_swizzle) { + *segment_order_horz = segment_order__contiguous; + *segment_order_vert = segment_order__na; + return true; + } + if (bytes_per_element == 2 && standard_swizzle) { + *segment_order_horz = segment_order__non_contiguous; + *segment_order_vert = segment_order__contiguous; + return true; + } + if (bytes_per_element == 4 && standard_swizzle) { + *segment_order_horz = segment_order__non_contiguous; + *segment_order_vert = segment_order__contiguous; + return true; + } + if (bytes_per_element == 8 && standard_swizzle) { + *segment_order_horz = segment_order__na; + *segment_order_vert = segment_order__contiguous; + return true; + } + if (bytes_per_element == 8 && display_swizzle) { + *segment_order_horz = segment_order__contiguous; + *segment_order_vert = segment_order__non_contiguous; + return true; + } + + return false; +} + +static void get_blk256_size(unsigned int *blk256_width, unsigned int *blk256_height, + unsigned int bytes_per_element) +{ + /* copied from DML. might want to refactor DML to leverage from DML */ + /* DML : get_blk256_size */ + if (bytes_per_element == 1) { + *blk256_width = 16; + *blk256_height = 16; + } else if (bytes_per_element == 2) { + *blk256_width = 16; + *blk256_height = 8; + } else if (bytes_per_element == 4) { + *blk256_width = 8; + *blk256_height = 8; + } else if (bytes_per_element == 8) { + *blk256_width = 8; + *blk256_height = 4; + } +} + +static void det_request_size( + unsigned int height, + unsigned int width, + unsigned int bpe, + bool *req128_horz_wc, + bool *req128_vert_wc) +{ + unsigned int detile_buf_size = 164 * 1024; /* 164KB for DCN1.0 */ + + unsigned int blk256_height = 0; + unsigned int blk256_width = 0; + unsigned int swath_bytes_horz_wc, swath_bytes_vert_wc; + + get_blk256_size(&blk256_width, &blk256_height, bpe); + + swath_bytes_horz_wc = height * blk256_height * bpe; + swath_bytes_vert_wc = width * blk256_width * bpe; + + *req128_horz_wc = (2 * swath_bytes_horz_wc <= detile_buf_size) ? + false : /* full 256B request */ + true; /* half 128b request */ + + *req128_vert_wc = (2 * swath_bytes_vert_wc <= detile_buf_size) ? + false : /* full 256B request */ + true; /* half 128b request */ +} + +static bool get_dcc_compression_cap(const struct dc *dc, + const struct dc_dcc_surface_param *input, + struct dc_surface_dcc_cap *output) +{ + /* implement section 1.6.2.1 of DCN1_Programming_Guide.docx */ + enum dcc_control dcc_control; + unsigned int bpe; + enum segment_order segment_order_horz, segment_order_vert; + bool req128_horz_wc, req128_vert_wc; + + memset(output, 0, sizeof(*output)); + + if (dc->debug.disable_dcc == DCC_DISABLE) + return false; + + if (!dcc_support_pixel_format(input->format, + &bpe)) + return false; + + if (!dcc_support_swizzle(input->swizzle_mode, bpe, + &segment_order_horz, &segment_order_vert)) + return false; + + det_request_size(input->surface_size.height, input->surface_size.width, + bpe, &req128_horz_wc, &req128_vert_wc); + + if (!req128_horz_wc && !req128_vert_wc) { + dcc_control = dcc_control__256_256_xxx; + } else if (input->scan == SCAN_DIRECTION_HORIZONTAL) { + if (!req128_horz_wc) + dcc_control = dcc_control__256_256_xxx; + else if (segment_order_horz == segment_order__contiguous) + dcc_control = dcc_control__128_128_xxx; + else + dcc_control = dcc_control__256_64_64; + } else if (input->scan == SCAN_DIRECTION_VERTICAL) { + if (!req128_vert_wc) + dcc_control = dcc_control__256_256_xxx; + else if (segment_order_vert == segment_order__contiguous) + dcc_control = dcc_control__128_128_xxx; + else + dcc_control = dcc_control__256_64_64; + } else { + if ((req128_horz_wc && + segment_order_horz == segment_order__non_contiguous) || + (req128_vert_wc && + segment_order_vert == segment_order__non_contiguous)) + /* access_dir not known, must use most constraining */ + dcc_control = dcc_control__256_64_64; + else + /* reg128 is true for either horz and vert + * but segment_order is contiguous + */ + dcc_control = dcc_control__128_128_xxx; + } + + if (dc->debug.disable_dcc == DCC_HALF_REQ_DISALBE && + dcc_control != dcc_control__256_256_xxx) + return false; + + switch (dcc_control) { + case dcc_control__256_256_xxx: + output->grph.rgb.max_uncompressed_blk_size = 256; + output->grph.rgb.max_compressed_blk_size = 256; + output->grph.rgb.independent_64b_blks = false; + break; + case dcc_control__128_128_xxx: + output->grph.rgb.max_uncompressed_blk_size = 128; + output->grph.rgb.max_compressed_blk_size = 128; + output->grph.rgb.independent_64b_blks = false; + break; + case dcc_control__256_64_64: + output->grph.rgb.max_uncompressed_blk_size = 256; + output->grph.rgb.max_compressed_blk_size = 64; + output->grph.rgb.independent_64b_blks = true; + break; + } + + output->capable = true; + output->const_color_support = false; + + return true; +} + + +static void dcn10_destroy_resource_pool(struct resource_pool **pool) +{ + struct dcn10_resource_pool *dcn10_pool = TO_DCN10_RES_POOL(*pool); + + destruct(dcn10_pool); + kfree(dcn10_pool); + *pool = NULL; +} + +static enum dc_status dcn10_validate_plane(const struct dc_plane_state *plane_state, struct dc_caps *caps) +{ + if (plane_state->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN + && caps->max_video_width != 0 + && plane_state->src_rect.width > caps->max_video_width) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static struct dc_cap_funcs cap_funcs = { + .get_dcc_compression_cap = get_dcc_compression_cap +}; + +static struct resource_funcs dcn10_res_pool_funcs = { + .destroy = dcn10_destroy_resource_pool, + .link_enc_create = dcn10_link_encoder_create, + .validate_guaranteed = dcn10_validate_guaranteed, + .validate_bandwidth = dcn_validate_bandwidth, + .acquire_idle_pipe_for_layer = dcn10_acquire_idle_pipe_for_layer, + .validate_plane = dcn10_validate_plane, + .add_stream_to_ctx = dcn10_add_stream_to_ctx +}; + +static uint32_t read_pipe_fuses(struct dc_context *ctx) +{ + uint32_t value = dm_read_reg_soc15(ctx, mmCC_DC_PIPE_DIS, 0); + /* RV1 support max 4 pipes */ + value = value & 0xf; + return value; +} + +static bool construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dcn10_resource_pool *pool) +{ + int i; + int j; + struct dc_context *ctx = dc->ctx; + uint32_t pipe_fuses = read_pipe_fuses(ctx); + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dcn10_res_pool_funcs; + + /* + * TODO fill in from actual raven resource when we create + * more than virtual encoder + */ + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + + /* max pipe num for ASIC before check pipe fuses */ + pool->base.pipe_count = pool->base.res_cap->num_timing_generator; + + dc->caps.max_video_width = 3840; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 100; + dc->caps.max_cursor_size = 256; + + dc->caps.max_slave_planes = 1; + + if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV) + dc->debug = debug_defaults_drv; + else + dc->debug = debug_defaults_diags; + + /************************************************* + * Create resources * + *************************************************/ + + pool->base.clock_sources[DCN10_CLK_SRC_PLL0] = + dcn10_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[DCN10_CLK_SRC_PLL1] = + dcn10_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL1, + &clk_src_regs[1], false); + pool->base.clock_sources[DCN10_CLK_SRC_PLL2] = + dcn10_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL2, + &clk_src_regs[2], false); + pool->base.clock_sources[DCN10_CLK_SRC_PLL3] = + dcn10_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + + pool->base.clk_src_count = DCN10_CLK_SRC_TOTAL; + + pool->base.dp_clock_source = + dcn10_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_ID_DP_DTO, + /* todo: not reuse phy_pll registers */ + &clk_src_regs[0], true); + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto clock_source_create_fail; + } + } + + if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + pool->base.display_clock = dce120_disp_clk_create(ctx); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto disp_clk_create_fail; + } + } + + pool->base.dmcu = dcn10_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + dml_init_instance(&dc->dml, DML_PROJECT_RAVEN1); + memcpy(dc->dcn_ip, &dcn10_ip_defaults, sizeof(dcn10_ip_defaults)); + memcpy(dc->dcn_soc, &dcn10_soc_defaults, sizeof(dcn10_soc_defaults)); + + if (ASICREV_IS_RV1_F0(dc->ctx->asic_id.hw_internal_rev)) { + dc->dcn_soc->urgent_latency = 3; + dc->debug.disable_dmcu = true; + dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 = 41.60f; + } + + + dc->dcn_soc->number_of_channels = dc->ctx->asic_id.vram_width / ddr4_dram_width; + ASSERT(dc->dcn_soc->number_of_channels < 3); + if (dc->dcn_soc->number_of_channels == 0)/*old sbios bug*/ + dc->dcn_soc->number_of_channels = 2; + + if (dc->dcn_soc->number_of_channels == 1) { + dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 = 19.2f; + dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8 = 17.066f; + dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72 = 14.933f; + dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65 = 12.8f; + if (ASICREV_IS_RV1_F0(dc->ctx->asic_id.hw_internal_rev)) { + dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 = 20.80f; + } + } + + pool->base.pp_smu = dcn10_pp_smu_create(ctx); + + if (!dc->debug.disable_pplib_clock_request) + dcn_bw_update_from_pplib(dc); + dcn_bw_sync_calcs_and_dml(dc); + if (!dc->debug.disable_pplib_wm_range) { + dc->res_pool = &pool->base; + dcn_bw_notify_pplib_of_wm_ranges(dc); + } + + { + #if defined(CONFIG_DRM_AMD_DC_DCN1_0) + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dcn10_create(&init_data); + if (!pool->base.irqs) + goto irqs_create_fail; + #endif + } + + /* index to valid pipe resource */ + j = 0; + /* mem input -> ipp -> dpp -> opp -> TG */ + for (i = 0; i < pool->base.pipe_count; i++) { + /* if pipe is disabled, skip instance of HW pipe, + * i.e, skip ASIC register instance + */ + if ((pipe_fuses & (1 << i)) != 0) + continue; + + pool->base.hubps[j] = dcn10_hubp_create(ctx, i); + if (pool->base.hubps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto mi_create_fail; + } + + pool->base.ipps[j] = dcn10_ipp_create(ctx, i); + if (pool->base.ipps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto ipp_create_fail; + } + + pool->base.dpps[j] = dcn10_dpp_create(ctx, i); + if (pool->base.dpps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create dpp!\n"); + goto dpp_create_fail; + } + + pool->base.opps[j] = dcn10_opp_create(ctx, i); + if (pool->base.opps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto opp_create_fail; + } + + pool->base.timing_generators[j] = dcn10_timing_generator_create( + ctx, i); + if (pool->base.timing_generators[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto otg_create_fail; + } + /* check next valid pipe */ + j++; + } + + /* valid pipe num */ + pool->base.pipe_count = j; + + /* within dml lib, it is hard code to 4. If ASIC pipe is fused, + * the value may be changed + */ + dc->dml.ip.max_num_dpp = pool->base.pipe_count; + dc->dcn_ip->max_num_dpp = pool->base.pipe_count; + + pool->base.mpc = dcn10_mpc_create(ctx); + if (pool->base.mpc == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create mpc!\n"); + goto mpc_create_fail; + } + + if (!resource_construct(num_virtual_links, dc, &pool->base, + (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment) ? + &res_create_funcs : &res_create_maximus_funcs))) + goto res_create_fail; + + dcn10_hw_sequencer_construct(dc); + dc->caps.max_planes = pool->base.pipe_count; + + dc->cap_funcs = cap_funcs; + + return true; + +disp_clk_create_fail: +mpc_create_fail: +otg_create_fail: +opp_create_fail: +dpp_create_fail: +ipp_create_fail: +mi_create_fail: +irqs_create_fail: +res_create_fail: +clock_source_create_fail: + + destruct(pool); + + return false; +} + +struct resource_pool *dcn10_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dcn10_resource_pool *pool = + kzalloc(sizeof(struct dcn10_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.h new file mode 100644 index 000000000000..8f71225bc61b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.h @@ -0,0 +1,47 @@ +/* +* Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_RESOURCE_DCN10_H__ +#define __DC_RESOURCE_DCN10_H__ + +#include "core_types.h" + +#define TO_DCN10_RES_POOL(pool)\ + container_of(pool, struct dcn10_resource_pool, base) + +struct dc; +struct resource_pool; +struct _vcs_dpi_display_pipe_params_st; + +struct dcn10_resource_pool { + struct resource_pool base; +}; +struct resource_pool *dcn10_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + + +#endif /* __DC_RESOURCE_DCN10_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_timing_generator.c new file mode 100644 index 000000000000..c7333cdf1802 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_timing_generator.c @@ -0,0 +1,1203 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "reg_helper.h" +#include "dcn10_timing_generator.h" +#include "dc.h" + +#define REG(reg)\ + tgn10->tg_regs->reg + +#define CTX \ + tgn10->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + tgn10->tg_shift->field_name, tgn10->tg_mask->field_name + +#define STATIC_SCREEN_EVENT_MASK_RANGETIMING_DOUBLE_BUFFER_UPDATE_EN 0x100 + +/** +* apply_front_porch_workaround TODO FPGA still need? +* +* This is a workaround for a bug that has existed since R5xx and has not been +* fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. +*/ +static void tgn10_apply_front_porch_workaround( + struct timing_generator *tg, + struct dc_crtc_timing *timing) +{ + if (timing->flags.INTERLACE == 1) { + if (timing->v_front_porch < 2) + timing->v_front_porch = 2; + } else { + if (timing->v_front_porch < 1) + timing->v_front_porch = 1; + } +} + +static void tgn10_program_global_sync( + struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + if (tg->dlg_otg_param.vstartup_start == 0) { + BREAK_TO_DEBUGGER(); + return; + } + + REG_SET(OTG_VSTARTUP_PARAM, 0, + VSTARTUP_START, tg->dlg_otg_param.vstartup_start); + + REG_SET_2(OTG_VUPDATE_PARAM, 0, + VUPDATE_OFFSET, tg->dlg_otg_param.vupdate_offset, + VUPDATE_WIDTH, tg->dlg_otg_param.vupdate_width); + + REG_SET(OTG_VREADY_PARAM, 0, + VREADY_OFFSET, tg->dlg_otg_param.vready_offset); +} + +static void tgn10_disable_stereo(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_SET(OTG_STEREO_CONTROL, 0, + OTG_STEREO_EN, 0); + + REG_SET_3(OTG_3D_STRUCTURE_CONTROL, 0, + OTG_3D_STRUCTURE_EN, 0, + OTG_3D_STRUCTURE_V_UPDATE_MODE, 0, + OTG_3D_STRUCTURE_STEREO_SEL_OVR, 0); + + REG_UPDATE(OPPBUF_CONTROL, + OPPBUF_ACTIVE_WIDTH, 0); + REG_UPDATE(OPPBUF_3D_PARAMETERS_0, + OPPBUF_3D_VACT_SPACE1_SIZE, 0); +} + +/** + * program_timing_generator used by mode timing set + * Program CRTC Timing Registers - OTG_H_*, OTG_V_*, Pixel repetition. + * Including SYNC. Call BIOS command table to program Timings. + */ +static void tgn10_program_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *dc_crtc_timing, + bool use_vbios) +{ + struct dc_crtc_timing patched_crtc_timing; + uint32_t vesa_sync_start; + uint32_t asic_blank_end; + uint32_t asic_blank_start; + uint32_t v_total; + uint32_t v_sync_end; + uint32_t v_init, v_fp2; + uint32_t h_sync_polarity, v_sync_polarity; + uint32_t interlace_factor; + uint32_t start_point = 0; + uint32_t field_num = 0; + uint32_t h_div_2; + int32_t vertical_line_start; + + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + patched_crtc_timing = *dc_crtc_timing; + tgn10_apply_front_porch_workaround(tg, &patched_crtc_timing); + + /* Load horizontal timing */ + + /* CRTC_H_TOTAL = vesa.h_total - 1 */ + REG_SET(OTG_H_TOTAL, 0, + OTG_H_TOTAL, patched_crtc_timing.h_total - 1); + + /* h_sync_start = 0, h_sync_end = vesa.h_sync_width */ + REG_UPDATE_2(OTG_H_SYNC_A, + OTG_H_SYNC_A_START, 0, + OTG_H_SYNC_A_END, patched_crtc_timing.h_sync_width); + + /* asic_h_blank_end = HsyncWidth + HbackPorch = + * vesa. usHorizontalTotal - vesa. usHorizontalSyncStart - + * vesa.h_left_border + */ + vesa_sync_start = patched_crtc_timing.h_addressable + + patched_crtc_timing.h_border_right + + patched_crtc_timing.h_front_porch; + + asic_blank_end = patched_crtc_timing.h_total - + vesa_sync_start - + patched_crtc_timing.h_border_left; + + /* h_blank_start = v_blank_end + v_active */ + asic_blank_start = asic_blank_end + + patched_crtc_timing.h_border_left + + patched_crtc_timing.h_addressable + + patched_crtc_timing.h_border_right; + + REG_UPDATE_2(OTG_H_BLANK_START_END, + OTG_H_BLANK_START, asic_blank_start, + OTG_H_BLANK_END, asic_blank_end); + + /* h_sync polarity */ + h_sync_polarity = patched_crtc_timing.flags.HSYNC_POSITIVE_POLARITY ? + 0 : 1; + + REG_UPDATE(OTG_H_SYNC_A_CNTL, + OTG_H_SYNC_A_POL, h_sync_polarity); + + /* Load vertical timing */ + + /* CRTC_V_TOTAL = v_total - 1 */ + if (patched_crtc_timing.flags.INTERLACE) { + interlace_factor = 2; + v_total = 2 * patched_crtc_timing.v_total; + } else { + interlace_factor = 1; + v_total = patched_crtc_timing.v_total - 1; + } + REG_SET(OTG_V_TOTAL, 0, + OTG_V_TOTAL, v_total); + + /* In case of V_TOTAL_CONTROL is on, make sure OTG_V_TOTAL_MAX and + * OTG_V_TOTAL_MIN are equal to V_TOTAL. + */ + REG_SET(OTG_V_TOTAL_MAX, 0, + OTG_V_TOTAL_MAX, v_total); + REG_SET(OTG_V_TOTAL_MIN, 0, + OTG_V_TOTAL_MIN, v_total); + + /* v_sync_start = 0, v_sync_end = v_sync_width */ + v_sync_end = patched_crtc_timing.v_sync_width * interlace_factor; + + REG_UPDATE_2(OTG_V_SYNC_A, + OTG_V_SYNC_A_START, 0, + OTG_V_SYNC_A_END, v_sync_end); + + vesa_sync_start = patched_crtc_timing.v_addressable + + patched_crtc_timing.v_border_bottom + + patched_crtc_timing.v_front_porch; + + asic_blank_end = (patched_crtc_timing.v_total - + vesa_sync_start - + patched_crtc_timing.v_border_top) + * interlace_factor; + + /* v_blank_start = v_blank_end + v_active */ + asic_blank_start = asic_blank_end + + (patched_crtc_timing.v_border_top + + patched_crtc_timing.v_addressable + + patched_crtc_timing.v_border_bottom) + * interlace_factor; + + REG_UPDATE_2(OTG_V_BLANK_START_END, + OTG_V_BLANK_START, asic_blank_start, + OTG_V_BLANK_END, asic_blank_end); + + /* Use OTG_VERTICAL_INTERRUPT2 replace VUPDATE interrupt, + * program the reg for interrupt postition. + */ + vertical_line_start = asic_blank_end - tg->dlg_otg_param.vstartup_start + 1; + if (vertical_line_start < 0) { + ASSERT(0); + vertical_line_start = 0; + } + REG_SET(OTG_VERTICAL_INTERRUPT2_POSITION, 0, + OTG_VERTICAL_INTERRUPT2_LINE_START, vertical_line_start); + + /* v_sync polarity */ + v_sync_polarity = patched_crtc_timing.flags.VSYNC_POSITIVE_POLARITY ? + 0 : 1; + + REG_UPDATE(OTG_V_SYNC_A_CNTL, + OTG_V_SYNC_A_POL, v_sync_polarity); + + v_init = asic_blank_start; + if (tg->dlg_otg_param.signal == SIGNAL_TYPE_DISPLAY_PORT || + tg->dlg_otg_param.signal == SIGNAL_TYPE_DISPLAY_PORT_MST || + tg->dlg_otg_param.signal == SIGNAL_TYPE_EDP) { + start_point = 1; + if (patched_crtc_timing.flags.INTERLACE == 1) + field_num = 1; + } + v_fp2 = 0; + if (tg->dlg_otg_param.vstartup_start > asic_blank_end) + v_fp2 = tg->dlg_otg_param.vstartup_start > asic_blank_end; + + /* Interlace */ + if (patched_crtc_timing.flags.INTERLACE == 1) { + REG_UPDATE(OTG_INTERLACE_CONTROL, + OTG_INTERLACE_ENABLE, 1); + v_init = v_init / 2; + if ((tg->dlg_otg_param.vstartup_start/2)*2 > asic_blank_end) + v_fp2 = v_fp2 / 2; + } + else + REG_UPDATE(OTG_INTERLACE_CONTROL, + OTG_INTERLACE_ENABLE, 0); + + + /* VTG enable set to 0 first VInit */ + REG_UPDATE(CONTROL, + VTG0_ENABLE, 0); + + REG_UPDATE_2(CONTROL, + VTG0_FP2, v_fp2, + VTG0_VCOUNT_INIT, v_init); + + /* original code is using VTG offset to address OTG reg, seems wrong */ + REG_UPDATE_2(OTG_CONTROL, + OTG_START_POINT_CNTL, start_point, + OTG_FIELD_NUMBER_CNTL, field_num); + + tgn10_program_global_sync(tg); + + /* TODO + * patched_crtc_timing.flags.HORZ_COUNT_BY_TWO == 1 + * program_horz_count_by_2 + * for DVI 30bpp mode, 0 otherwise + * program_horz_count_by_2(tg, &patched_crtc_timing); + */ + + /* Enable stereo - only when we need to pack 3D frame. Other types + * of stereo handled in explicit call + */ + h_div_2 = (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) ? + 1 : 0; + + REG_UPDATE(OTG_H_TIMING_CNTL, + OTG_H_TIMING_DIV_BY2, h_div_2); + +} + +/** + * unblank_crtc + * Call ASIC Control Object to UnBlank CRTC. + */ +static void tgn10_unblank_crtc(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + uint32_t vertical_interrupt_enable = 0; + + REG_GET(OTG_VERTICAL_INTERRUPT2_CONTROL, + OTG_VERTICAL_INTERRUPT2_INT_ENABLE, &vertical_interrupt_enable); + + /* temporary work around for vertical interrupt, once vertical interrupt enabled, + * this check will be removed. + */ + if (vertical_interrupt_enable) + REG_UPDATE(OTG_DOUBLE_BUFFER_CONTROL, + OTG_BLANK_DATA_DOUBLE_BUFFER_EN, 1); + + REG_UPDATE_2(OTG_BLANK_CONTROL, + OTG_BLANK_DATA_EN, 0, + OTG_BLANK_DE_MODE, 0); +} + +/** + * blank_crtc + * Call ASIC Control Object to Blank CRTC. + */ + +static void tgn10_blank_crtc(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_UPDATE_2(OTG_BLANK_CONTROL, + OTG_BLANK_DATA_EN, 1, + OTG_BLANK_DE_MODE, 0); + + /* todo: why are we waiting for BLANK_DATA_EN? shouldn't we be waiting + * for status? + */ + REG_WAIT(OTG_BLANK_CONTROL, + OTG_BLANK_DATA_EN, 1, + 1, 100000); + + REG_UPDATE(OTG_DOUBLE_BUFFER_CONTROL, + OTG_BLANK_DATA_DOUBLE_BUFFER_EN, 0); +} + +static void tgn10_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + if (enable_blanking) + tgn10_blank_crtc(tg); + else + tgn10_unblank_crtc(tg); +} + +static bool tgn10_is_blanked(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + uint32_t blank_en; + uint32_t blank_state; + + REG_GET_2(OTG_BLANK_CONTROL, + OTG_BLANK_DATA_EN, &blank_en, + OTG_CURRENT_BLANK_STATE, &blank_state); + + return blank_en && blank_state; +} + +static void tgn10_enable_optc_clock(struct timing_generator *tg, bool enable) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + if (enable) { + REG_UPDATE_2(OPTC_INPUT_CLOCK_CONTROL, + OPTC_INPUT_CLK_EN, 1, + OPTC_INPUT_CLK_GATE_DIS, 1); + + REG_WAIT(OPTC_INPUT_CLOCK_CONTROL, + OPTC_INPUT_CLK_ON, 1, + 1, 1000); + + /* Enable clock */ + REG_UPDATE_2(OTG_CLOCK_CONTROL, + OTG_CLOCK_EN, 1, + OTG_CLOCK_GATE_DIS, 1); + REG_WAIT(OTG_CLOCK_CONTROL, + OTG_CLOCK_ON, 1, + 1, 1000); + } else { + REG_UPDATE_2(OTG_CLOCK_CONTROL, + OTG_CLOCK_GATE_DIS, 0, + OTG_CLOCK_EN, 0); + + if (tg->ctx->dce_environment != DCE_ENV_FPGA_MAXIMUS) + REG_WAIT(OTG_CLOCK_CONTROL, + OTG_CLOCK_ON, 0, + 1, 1000); + + REG_UPDATE_2(OPTC_INPUT_CLOCK_CONTROL, + OPTC_INPUT_CLK_GATE_DIS, 0, + OPTC_INPUT_CLK_EN, 0); + + if (tg->ctx->dce_environment != DCE_ENV_FPGA_MAXIMUS) + REG_WAIT(OPTC_INPUT_CLOCK_CONTROL, + OPTC_INPUT_CLK_ON, 0, + 1, 1000); + } +} + +/** + * Enable CRTC + * Enable CRTC - call ASIC Control Object to enable Timing generator. + */ +static bool tgn10_enable_crtc(struct timing_generator *tg) +{ + /* TODO FPGA wait for answer + * OTG_MASTER_UPDATE_MODE != CRTC_MASTER_UPDATE_MODE + * OTG_MASTER_UPDATE_LOCK != CRTC_MASTER_UPDATE_LOCK + */ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + /* opp instance for OTG. For DCN1.0, ODM is remoed. + * OPP and OPTC should 1:1 mapping + */ + REG_UPDATE(OPTC_DATA_SOURCE_SELECT, + OPTC_SRC_SEL, tg->inst); + + /* VTG enable first is for HW workaround */ + REG_UPDATE(CONTROL, + VTG0_ENABLE, 1); + + /* Enable CRTC */ + REG_UPDATE_2(OTG_CONTROL, + OTG_DISABLE_POINT_CNTL, 3, + OTG_MASTER_EN, 1); + + return true; +} + +/* disable_crtc - call ASIC Control Object to disable Timing generator. */ +static bool tgn10_disable_crtc(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + /* disable otg request until end of the first line + * in the vertical blank region + */ + REG_UPDATE_2(OTG_CONTROL, + OTG_DISABLE_POINT_CNTL, 3, + OTG_MASTER_EN, 0); + + REG_UPDATE(CONTROL, + VTG0_ENABLE, 0); + + /* CRTC disabled, so disable clock. */ + REG_WAIT(OTG_CLOCK_CONTROL, + OTG_BUSY, 0, + 1, 100000); + + return true; +} + + +static void tgn10_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_SET_3(OTG_BLACK_COLOR, 0, + OTG_BLACK_COLOR_B_CB, black_color->color_b_cb, + OTG_BLACK_COLOR_G_Y, black_color->color_g_y, + OTG_BLACK_COLOR_R_CR, black_color->color_r_cr); +} + +static bool tgn10_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t interlace_factor; + uint32_t v_blank; + uint32_t h_blank; + uint32_t min_v_blank; + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + ASSERT(timing != NULL); + + interlace_factor = timing->flags.INTERLACE ? 2 : 1; + v_blank = (timing->v_total - timing->v_addressable - + timing->v_border_top - timing->v_border_bottom) * + interlace_factor; + + h_blank = (timing->h_total - timing->h_addressable - + timing->h_border_right - + timing->h_border_left); + + if (timing->timing_3d_format != TIMING_3D_FORMAT_NONE && + timing->timing_3d_format != TIMING_3D_FORMAT_HW_FRAME_PACKING && + timing->timing_3d_format != TIMING_3D_FORMAT_TOP_AND_BOTTOM && + timing->timing_3d_format != TIMING_3D_FORMAT_SIDE_BY_SIDE && + timing->timing_3d_format != TIMING_3D_FORMAT_FRAME_ALTERNATE && + timing->timing_3d_format != TIMING_3D_FORMAT_INBAND_FA) + return false; + + if (timing->timing_3d_format != TIMING_3D_FORMAT_NONE && + tg->ctx->dc->debug.disable_stereo_support) + return false; + /* Temporarily blocking interlacing mode until it's supported */ + if (timing->flags.INTERLACE == 1) + return false; + + /* Check maximum number of pixels supported by Timing Generator + * (Currently will never fail, in order to fail needs display which + * needs more than 8192 horizontal and + * more than 8192 vertical total pixels) + */ + if (timing->h_total > tgn10->max_h_total || + timing->v_total > tgn10->max_v_total) + return false; + + + if (h_blank < tgn10->min_h_blank) + return false; + + if (timing->h_sync_width < tgn10->min_h_sync_width || + timing->v_sync_width < tgn10->min_v_sync_width) + return false; + + min_v_blank = timing->flags.INTERLACE?tgn10->min_v_blank_interlace:tgn10->min_v_blank; + + if (v_blank < min_v_blank) + return false; + + return true; + +} + +/* + * get_vblank_counter + * + * @brief + * Get counter for vertical blanks. use register CRTC_STATUS_FRAME_COUNT which + * holds the counter of frames. + * + * @param + * struct timing_generator *tg - [in] timing generator which controls the + * desired CRTC + * + * @return + * Counter of frames, which should equal to number of vblanks. + */ +static uint32_t tgn10_get_vblank_counter(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + uint32_t frame_count; + + REG_GET(OTG_STATUS_FRAME_COUNT, + OTG_FRAME_COUNT, &frame_count); + + return frame_count; +} + +static void tgn10_lock(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_SET(OTG_GLOBAL_CONTROL0, 0, + OTG_MASTER_UPDATE_LOCK_SEL, tg->inst); + REG_SET(OTG_MASTER_UPDATE_LOCK, 0, + OTG_MASTER_UPDATE_LOCK, 1); + + if (tg->ctx->dce_environment != DCE_ENV_FPGA_MAXIMUS) + REG_WAIT(OTG_MASTER_UPDATE_LOCK, + UPDATE_LOCK_STATUS, 1, + 1, 100); +} + +static void tgn10_unlock(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_SET(OTG_MASTER_UPDATE_LOCK, 0, + OTG_MASTER_UPDATE_LOCK, 0); + + /* why are we waiting here? */ + REG_WAIT(OTG_DOUBLE_BUFFER_CONTROL, + OTG_UPDATE_PENDING, 0, + 1, 100000); +} + +static void tgn10_get_position(struct timing_generator *tg, + struct crtc_position *position) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_GET_2(OTG_STATUS_POSITION, + OTG_HORZ_COUNT, &position->horizontal_count, + OTG_VERT_COUNT, &position->vertical_count); + + REG_GET(OTG_NOM_VERT_POSITION, + OTG_VERT_COUNT_NOM, &position->nominal_vcount); +} + +static bool tgn10_is_counter_moving(struct timing_generator *tg) +{ + struct crtc_position position1, position2; + + tg->funcs->get_position(tg, &position1); + tg->funcs->get_position(tg, &position2); + + if (position1.horizontal_count == position2.horizontal_count && + position1.vertical_count == position2.vertical_count) + return false; + else + return true; +} + +static bool tgn10_did_triggered_reset_occur( + struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + uint32_t occurred; + + REG_GET(OTG_FORCE_COUNT_NOW_CNTL, + OTG_FORCE_COUNT_NOW_OCCURRED, &occurred); + + return occurred != 0; +} + +static void tgn10_enable_reset_trigger(struct timing_generator *tg, int source_tg_inst) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + uint32_t falling_edge; + + REG_GET(OTG_V_SYNC_A_CNTL, + OTG_V_SYNC_A_POL, &falling_edge); + + if (falling_edge) + REG_SET_3(OTG_TRIGA_CNTL, 0, + /* vsync signal from selected OTG pipe based + * on OTG_TRIG_SOURCE_PIPE_SELECT setting + */ + OTG_TRIGA_SOURCE_SELECT, 20, + OTG_TRIGA_SOURCE_PIPE_SELECT, source_tg_inst, + /* always detect falling edge */ + OTG_TRIGA_FALLING_EDGE_DETECT_CNTL, 1); + else + REG_SET_3(OTG_TRIGA_CNTL, 0, + /* vsync signal from selected OTG pipe based + * on OTG_TRIG_SOURCE_PIPE_SELECT setting + */ + OTG_TRIGA_SOURCE_SELECT, 20, + OTG_TRIGA_SOURCE_PIPE_SELECT, source_tg_inst, + /* always detect rising edge */ + OTG_TRIGA_RISING_EDGE_DETECT_CNTL, 1); + + REG_SET(OTG_FORCE_COUNT_NOW_CNTL, 0, + /* force H count to H_TOTAL and V count to V_TOTAL in + * progressive mode and V_TOTAL-1 in interlaced mode + */ + OTG_FORCE_COUNT_NOW_MODE, 2); +} + +static void tgn10_disable_reset_trigger(struct timing_generator *tg) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_WRITE(OTG_TRIGA_CNTL, 0); + + REG_SET(OTG_FORCE_COUNT_NOW_CNTL, 0, + OTG_FORCE_COUNT_NOW_CLEAR, 1); +} + +static void tgn10_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + switch (state) { + case CRTC_STATE_VBLANK: + REG_WAIT(OTG_STATUS, + OTG_V_BLANK, 1, + 1, 100000); /* 1 vupdate at 10hz */ + break; + + case CRTC_STATE_VACTIVE: + REG_WAIT(OTG_STATUS, + OTG_V_ACTIVE_DISP, 1, + 1, 100000); /* 1 vupdate at 10hz */ + break; + + default: + break; + } +} + +static void tgn10_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + /* asic design change, do not need this control + * empty for share caller logic + */ +} + + +static void tgn10_set_static_screen_control( + struct timing_generator *tg, + uint32_t value) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + /* Bit 8 is no longer applicable in RV for PSR case, + * set bit 8 to 0 if given + */ + if ((value & STATIC_SCREEN_EVENT_MASK_RANGETIMING_DOUBLE_BUFFER_UPDATE_EN) + != 0) + value = value & + ~STATIC_SCREEN_EVENT_MASK_RANGETIMING_DOUBLE_BUFFER_UPDATE_EN; + + REG_SET_2(OTG_STATIC_SCREEN_CONTROL, 0, + OTG_STATIC_SCREEN_EVENT_MASK, value, + OTG_STATIC_SCREEN_FRAME_COUNT, 2); +} + + +/** + ***************************************************************************** + * Function: set_drr + * + * @brief + * Program dynamic refresh rate registers m_OTGx_OTG_V_TOTAL_*. + * + ***************************************************************************** + */ +static void tgn10_set_drr( + struct timing_generator *tg, + const struct drr_params *params) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + if (params != NULL && + params->vertical_total_max > 0 && + params->vertical_total_min > 0) { + + REG_SET(OTG_V_TOTAL_MAX, 0, + OTG_V_TOTAL_MAX, params->vertical_total_max - 1); + + REG_SET(OTG_V_TOTAL_MIN, 0, + OTG_V_TOTAL_MIN, params->vertical_total_min - 1); + + REG_UPDATE_5(OTG_V_TOTAL_CONTROL, + OTG_V_TOTAL_MIN_SEL, 1, + OTG_V_TOTAL_MAX_SEL, 1, + OTG_FORCE_LOCK_ON_EVENT, 0, + OTG_SET_V_TOTAL_MIN_MASK_EN, 0, + OTG_SET_V_TOTAL_MIN_MASK, 0); + } else { + REG_SET(OTG_V_TOTAL_MIN, 0, + OTG_V_TOTAL_MIN, 0); + + REG_SET(OTG_V_TOTAL_MAX, 0, + OTG_V_TOTAL_MAX, 0); + + REG_UPDATE_4(OTG_V_TOTAL_CONTROL, + OTG_SET_V_TOTAL_MIN_MASK, 0, + OTG_V_TOTAL_MIN_SEL, 0, + OTG_V_TOTAL_MAX_SEL, 0, + OTG_FORCE_LOCK_ON_EVENT, 0); + } +} + +static void tgn10_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + enum test_pattern_color_format bit_depth; + enum test_pattern_dyn_range dyn_range; + enum test_pattern_mode mode; + uint32_t pattern_mask; + uint32_t pattern_data; + /* color ramp generator mixes 16-bits color */ + uint32_t src_bpc = 16; + /* requested bpc */ + uint32_t dst_bpc; + uint32_t index; + /* RGB values of the color bars. + * Produce two RGB colors: RGB0 - white (all Fs) + * and RGB1 - black (all 0s) + * (three RGB components for two colors) + */ + uint16_t src_color[6] = {0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0000}; + /* dest color (converted to the specified color format) */ + uint16_t dst_color[6]; + uint32_t inc_base; + + /* translate to bit depth */ + switch (color_depth) { + case COLOR_DEPTH_666: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_6; + break; + case COLOR_DEPTH_888: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + case COLOR_DEPTH_101010: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_10; + break; + case COLOR_DEPTH_121212: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_12; + break; + default: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + } + + switch (test_pattern) { + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: + { + dyn_range = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ? + TEST_PATTERN_DYN_RANGE_CEA : + TEST_PATTERN_DYN_RANGE_VESA); + mode = TEST_PATTERN_MODE_COLORSQUARES_RGB; + + REG_UPDATE_2(OTG_TEST_PATTERN_PARAMETERS, + OTG_TEST_PATTERN_VRES, 6, + OTG_TEST_PATTERN_HRES, 6); + + REG_UPDATE_4(OTG_TEST_PATTERN_CONTROL, + OTG_TEST_PATTERN_EN, 1, + OTG_TEST_PATTERN_MODE, mode, + OTG_TEST_PATTERN_DYNAMIC_RANGE, dyn_range, + OTG_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: + case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: + { + mode = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS ? + TEST_PATTERN_MODE_VERTICALBARS : + TEST_PATTERN_MODE_HORIZONTALBARS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* adjust color to the required colorFormat */ + for (index = 0; index < 6; index++) { + /* dst = 2^dstBpc * src / 2^srcBpc = src >> + * (srcBpc - dstBpc); + */ + dst_color[index] = + src_color[index] >> (src_bpc - dst_bpc); + /* CRTC_TEST_PATTERN_DATA has 16 bits, + * lowest 6 are hardwired to ZERO + * color bits should be left aligned aligned to MSB + * XXXXXXXXXX000000 for 10 bit, + * XXXXXXXX00000000 for 8 bit and XXXXXX0000000000 for 6 + */ + dst_color[index] <<= (16 - dst_bpc); + } + + REG_WRITE(OTG_TEST_PATTERN_PARAMETERS, 0); + + /* We have to write the mask before data, similar to pipeline. + * For example, for 8 bpc, if we want RGB0 to be magenta, + * and RGB1 to be cyan, + * we need to make 7 writes: + * MASK DATA + * 000001 00000000 00000000 set mask to R0 + * 000010 11111111 00000000 R0 255, 0xFF00, set mask to G0 + * 000100 00000000 00000000 G0 0, 0x0000, set mask to B0 + * 001000 11111111 00000000 B0 255, 0xFF00, set mask to R1 + * 010000 00000000 00000000 R1 0, 0x0000, set mask to G1 + * 100000 11111111 00000000 G1 255, 0xFF00, set mask to B1 + * 100000 11111111 00000000 B1 255, 0xFF00 + * + * we will make a loop of 6 in which we prepare the mask, + * then write, then prepare the color for next write. + * first iteration will write mask only, + * but each next iteration color prepared in + * previous iteration will be written within new mask, + * the last component will written separately, + * mask is not changing between 6th and 7th write + * and color will be prepared by last iteration + */ + + /* write color, color values mask in CRTC_TEST_PATTERN_MASK + * is B1, G1, R1, B0, G0, R0 + */ + pattern_data = 0; + for (index = 0; index < 6; index++) { + /* prepare color mask, first write PATTERN_DATA + * will have all zeros + */ + pattern_mask = (1 << index); + + /* write color component */ + REG_SET_2(OTG_TEST_PATTERN_COLOR, 0, + OTG_TEST_PATTERN_MASK, pattern_mask, + OTG_TEST_PATTERN_DATA, pattern_data); + + /* prepare next color component, + * will be written in the next iteration + */ + pattern_data = dst_color[index]; + } + /* write last color component, + * it's been already prepared in the loop + */ + REG_SET_2(OTG_TEST_PATTERN_COLOR, 0, + OTG_TEST_PATTERN_MASK, pattern_mask, + OTG_TEST_PATTERN_DATA, pattern_data); + + /* enable test pattern */ + REG_UPDATE_4(OTG_TEST_PATTERN_CONTROL, + OTG_TEST_PATTERN_EN, 1, + OTG_TEST_PATTERN_MODE, mode, + OTG_TEST_PATTERN_DYNAMIC_RANGE, 0, + OTG_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: + { + mode = (bit_depth == + TEST_PATTERN_COLOR_FORMAT_BPC_10 ? + TEST_PATTERN_MODE_DUALRAMP_RGB : + TEST_PATTERN_MODE_SINGLERAMP_RGB); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* increment for the first ramp for one color gradation + * 1 gradation for 6-bit color is 2^10 + * gradations in 16-bit color + */ + inc_base = (src_bpc - dst_bpc); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + { + REG_UPDATE_5(OTG_TEST_PATTERN_PARAMETERS, + OTG_TEST_PATTERN_INC0, inc_base, + OTG_TEST_PATTERN_INC1, 0, + OTG_TEST_PATTERN_HRES, 6, + OTG_TEST_PATTERN_VRES, 6, + OTG_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + { + REG_UPDATE_5(OTG_TEST_PATTERN_PARAMETERS, + OTG_TEST_PATTERN_INC0, inc_base, + OTG_TEST_PATTERN_INC1, 0, + OTG_TEST_PATTERN_HRES, 8, + OTG_TEST_PATTERN_VRES, 6, + OTG_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + { + REG_UPDATE_5(OTG_TEST_PATTERN_PARAMETERS, + OTG_TEST_PATTERN_INC0, inc_base, + OTG_TEST_PATTERN_INC1, inc_base + 2, + OTG_TEST_PATTERN_HRES, 8, + OTG_TEST_PATTERN_VRES, 5, + OTG_TEST_PATTERN_RAMP0_OFFSET, 384 << 6); + } + break; + default: + break; + } + + REG_WRITE(OTG_TEST_PATTERN_COLOR, 0); + + /* enable test pattern */ + REG_WRITE(OTG_TEST_PATTERN_CONTROL, 0); + + REG_SET_4(OTG_TEST_PATTERN_CONTROL, 0, + OTG_TEST_PATTERN_EN, 1, + OTG_TEST_PATTERN_MODE, mode, + OTG_TEST_PATTERN_DYNAMIC_RANGE, 0, + OTG_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + { + REG_WRITE(OTG_TEST_PATTERN_CONTROL, 0); + REG_WRITE(OTG_TEST_PATTERN_COLOR, 0); + REG_WRITE(OTG_TEST_PATTERN_PARAMETERS, 0); + } + break; + default: + break; + + } +} + +static void tgn10_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + struct crtc_position position; + + REG_GET_2(OTG_V_BLANK_START_END, + OTG_V_BLANK_START, v_blank_start, + OTG_V_BLANK_END, v_blank_end); + + tgn10_get_position(tg, &position); + + *h_position = position.horizontal_count; + *v_position = position.vertical_count; +} + + + +static void tgn10_enable_stereo(struct timing_generator *tg, + const struct dc_crtc_timing *timing, struct crtc_stereo_flags *flags) +{ + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + uint32_t active_width = timing->h_addressable; + uint32_t space1_size = timing->v_total - timing->v_addressable; + + if (flags) { + uint32_t stereo_en; + stereo_en = flags->FRAME_PACKED == 0 ? 1 : 0; + + if (flags->PROGRAM_STEREO) + REG_UPDATE_3(OTG_STEREO_CONTROL, + OTG_STEREO_EN, stereo_en, + OTG_STEREO_SYNC_OUTPUT_LINE_NUM, 0, + OTG_STEREO_SYNC_OUTPUT_POLARITY, 0); + + if (flags->PROGRAM_POLARITY) + REG_UPDATE(OTG_STEREO_CONTROL, + OTG_STEREO_EYE_FLAG_POLARITY, + flags->RIGHT_EYE_POLARITY == 0 ? 0 : 1); + + if (flags->DISABLE_STEREO_DP_SYNC) + REG_UPDATE(OTG_STEREO_CONTROL, + OTG_DISABLE_STEREOSYNC_OUTPUT_FOR_DP, 1); + + if (flags->PROGRAM_STEREO) + REG_UPDATE_3(OTG_3D_STRUCTURE_CONTROL, + OTG_3D_STRUCTURE_EN, flags->FRAME_PACKED, + OTG_3D_STRUCTURE_V_UPDATE_MODE, flags->FRAME_PACKED, + OTG_3D_STRUCTURE_STEREO_SEL_OVR, flags->FRAME_PACKED); + + } + + REG_UPDATE(OPPBUF_CONTROL, + OPPBUF_ACTIVE_WIDTH, active_width); + + REG_UPDATE(OPPBUF_3D_PARAMETERS_0, + OPPBUF_3D_VACT_SPACE1_SIZE, space1_size); +} + +static void tgn10_program_stereo(struct timing_generator *tg, + const struct dc_crtc_timing *timing, struct crtc_stereo_flags *flags) +{ + if (flags->PROGRAM_STEREO) + tgn10_enable_stereo(tg, timing, flags); + else + tgn10_disable_stereo(tg); +} + + +static bool tgn10_is_stereo_left_eye(struct timing_generator *tg) +{ + bool ret = false; + uint32_t left_eye = 0; + struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); + + REG_GET(OTG_STEREO_STATUS, + OTG_STEREO_CURRENT_EYE, &left_eye); + if (left_eye == 1) + ret = true; + else + ret = false; + + return ret; +} + +void tgn10_read_otg_state(struct dcn10_timing_generator *tgn10, + struct dcn_otg_state *s) +{ + REG_GET(OTG_CONTROL, + OTG_MASTER_EN, &s->otg_enabled); + + REG_GET_2(OTG_V_BLANK_START_END, + OTG_V_BLANK_START, &s->v_blank_start, + OTG_V_BLANK_END, &s->v_blank_end); + + REG_GET(OTG_V_SYNC_A_CNTL, + OTG_V_SYNC_A_POL, &s->v_sync_a_pol); + + REG_GET(OTG_V_TOTAL, + OTG_V_TOTAL, &s->v_total); + + REG_GET(OTG_V_TOTAL_MAX, + OTG_V_TOTAL_MAX, &s->v_total_max); + + REG_GET(OTG_V_TOTAL_MIN, + OTG_V_TOTAL_MIN, &s->v_total_min); + + REG_GET_2(OTG_V_SYNC_A, + OTG_V_SYNC_A_START, &s->v_sync_a_start, + OTG_V_SYNC_A_END, &s->v_sync_a_end); + + REG_GET_2(OTG_H_BLANK_START_END, + OTG_H_BLANK_START, &s->h_blank_start, + OTG_H_BLANK_END, &s->h_blank_end); + + REG_GET_2(OTG_H_SYNC_A, + OTG_H_SYNC_A_START, &s->h_sync_a_start, + OTG_H_SYNC_A_END, &s->h_sync_a_end); + + REG_GET(OTG_H_SYNC_A_CNTL, + OTG_H_SYNC_A_POL, &s->h_sync_a_pol); + + REG_GET(OTG_H_TOTAL, + OTG_H_TOTAL, &s->h_total); + + REG_GET(OPTC_INPUT_GLOBAL_CONTROL, + OPTC_UNDERFLOW_OCCURRED_STATUS, &s->underflow_occurred_status); +} + + +static const struct timing_generator_funcs dcn10_tg_funcs = { + .validate_timing = tgn10_validate_timing, + .program_timing = tgn10_program_timing, + .program_global_sync = tgn10_program_global_sync, + .enable_crtc = tgn10_enable_crtc, + .disable_crtc = tgn10_disable_crtc, + /* used by enable_timing_synchronization. Not need for FPGA */ + .is_counter_moving = tgn10_is_counter_moving, + .get_position = tgn10_get_position, + .get_frame_count = tgn10_get_vblank_counter, + .get_scanoutpos = tgn10_get_crtc_scanoutpos, + .set_early_control = tgn10_set_early_control, + /* used by enable_timing_synchronization. Not need for FPGA */ + .wait_for_state = tgn10_wait_for_state, + .set_blank = tgn10_set_blank, + .is_blanked = tgn10_is_blanked, + .set_blank_color = tgn10_program_blank_color, + .did_triggered_reset_occur = tgn10_did_triggered_reset_occur, + .enable_reset_trigger = tgn10_enable_reset_trigger, + .disable_reset_trigger = tgn10_disable_reset_trigger, + .lock = tgn10_lock, + .unlock = tgn10_unlock, + .enable_optc_clock = tgn10_enable_optc_clock, + .set_drr = tgn10_set_drr, + .set_static_screen_control = tgn10_set_static_screen_control, + .set_test_pattern = tgn10_set_test_pattern, + .program_stereo = tgn10_program_stereo, + .is_stereo_left_eye = tgn10_is_stereo_left_eye +}; + +void dcn10_timing_generator_init(struct dcn10_timing_generator *tgn10) +{ + tgn10->base.funcs = &dcn10_tg_funcs; + + tgn10->max_h_total = tgn10->tg_mask->OTG_H_TOTAL + 1; + tgn10->max_v_total = tgn10->tg_mask->OTG_V_TOTAL + 1; + + tgn10->min_h_blank = 32; + tgn10->min_v_blank = 3; + tgn10->min_v_blank_interlace = 5; + tgn10->min_h_sync_width = 8; + tgn10->min_v_sync_width = 1; +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_timing_generator.h new file mode 100644 index 000000000000..7d4818d7aa31 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_timing_generator.h @@ -0,0 +1,374 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TIMING_GENERATOR_DCN10_H__ +#define __DC_TIMING_GENERATOR_DCN10_H__ + +#include "timing_generator.h" + +#define DCN10TG_FROM_TG(tg)\ + container_of(tg, struct dcn10_timing_generator, base) + +#define TG_COMMON_REG_LIST_DCN(inst) \ + SRI(OTG_VSTARTUP_PARAM, OTG, inst),\ + SRI(OTG_VUPDATE_PARAM, OTG, inst),\ + SRI(OTG_VREADY_PARAM, OTG, inst),\ + SRI(OTG_BLANK_CONTROL, OTG, inst),\ + SRI(OTG_MASTER_UPDATE_LOCK, OTG, inst),\ + SRI(OTG_GLOBAL_CONTROL0, OTG, inst),\ + SRI(OTG_DOUBLE_BUFFER_CONTROL, OTG, inst),\ + SRI(OTG_H_TOTAL, OTG, inst),\ + SRI(OTG_H_BLANK_START_END, OTG, inst),\ + SRI(OTG_H_SYNC_A, OTG, inst),\ + SRI(OTG_H_SYNC_A_CNTL, OTG, inst),\ + SRI(OTG_H_TIMING_CNTL, OTG, inst),\ + SRI(OTG_V_TOTAL, OTG, inst),\ + SRI(OTG_V_BLANK_START_END, OTG, inst),\ + SRI(OTG_V_SYNC_A, OTG, inst),\ + SRI(OTG_V_SYNC_A_CNTL, OTG, inst),\ + SRI(OTG_INTERLACE_CONTROL, OTG, inst),\ + SRI(OTG_CONTROL, OTG, inst),\ + SRI(OTG_STEREO_CONTROL, OTG, inst),\ + SRI(OTG_3D_STRUCTURE_CONTROL, OTG, inst),\ + SRI(OTG_STEREO_STATUS, OTG, inst),\ + SRI(OTG_V_TOTAL_MAX, OTG, inst),\ + SRI(OTG_V_TOTAL_MIN, OTG, inst),\ + SRI(OTG_V_TOTAL_CONTROL, OTG, inst),\ + SRI(OTG_TRIGA_CNTL, OTG, inst),\ + SRI(OTG_FORCE_COUNT_NOW_CNTL, OTG, inst),\ + SRI(OTG_STATIC_SCREEN_CONTROL, OTG, inst),\ + SRI(OTG_STATUS_FRAME_COUNT, OTG, inst),\ + SRI(OTG_STATUS, OTG, inst),\ + SRI(OTG_STATUS_POSITION, OTG, inst),\ + SRI(OTG_NOM_VERT_POSITION, OTG, inst),\ + SRI(OTG_BLACK_COLOR, OTG, inst),\ + SRI(OTG_CLOCK_CONTROL, OTG, inst),\ + SRI(OTG_VERTICAL_INTERRUPT2_CONTROL, OTG, inst),\ + SRI(OTG_VERTICAL_INTERRUPT2_POSITION, OTG, inst),\ + SRI(OPTC_INPUT_CLOCK_CONTROL, ODM, inst),\ + SRI(OPTC_DATA_SOURCE_SELECT, ODM, inst),\ + SRI(OPTC_INPUT_GLOBAL_CONTROL, ODM, inst),\ + SRI(OPPBUF_CONTROL, OPPBUF, inst),\ + SRI(OPPBUF_3D_PARAMETERS_0, OPPBUF, inst),\ + SRI(CONTROL, VTG, inst) + +#define TG_COMMON_REG_LIST_DCN1_0(inst) \ + TG_COMMON_REG_LIST_DCN(inst),\ + SRI(OTG_TEST_PATTERN_PARAMETERS, OTG, inst),\ + SRI(OTG_TEST_PATTERN_CONTROL, OTG, inst),\ + SRI(OTG_TEST_PATTERN_COLOR, OTG, inst) + + +struct dcn_tg_registers { + uint32_t OTG_VSTARTUP_PARAM; + uint32_t OTG_VUPDATE_PARAM; + uint32_t OTG_VREADY_PARAM; + uint32_t OTG_BLANK_CONTROL; + uint32_t OTG_MASTER_UPDATE_LOCK; + uint32_t OTG_GLOBAL_CONTROL0; + uint32_t OTG_DOUBLE_BUFFER_CONTROL; + uint32_t OTG_H_TOTAL; + uint32_t OTG_H_BLANK_START_END; + uint32_t OTG_H_SYNC_A; + uint32_t OTG_H_SYNC_A_CNTL; + uint32_t OTG_H_TIMING_CNTL; + uint32_t OTG_V_TOTAL; + uint32_t OTG_V_BLANK_START_END; + uint32_t OTG_V_SYNC_A; + uint32_t OTG_V_SYNC_A_CNTL; + uint32_t OTG_INTERLACE_CONTROL; + uint32_t OTG_CONTROL; + uint32_t OTG_STEREO_CONTROL; + uint32_t OTG_3D_STRUCTURE_CONTROL; + uint32_t OTG_STEREO_STATUS; + uint32_t OTG_V_TOTAL_MAX; + uint32_t OTG_V_TOTAL_MIN; + uint32_t OTG_V_TOTAL_CONTROL; + uint32_t OTG_TRIGA_CNTL; + uint32_t OTG_FORCE_COUNT_NOW_CNTL; + uint32_t OTG_STATIC_SCREEN_CONTROL; + uint32_t OTG_STATUS_FRAME_COUNT; + uint32_t OTG_STATUS; + uint32_t OTG_STATUS_POSITION; + uint32_t OTG_NOM_VERT_POSITION; + uint32_t OTG_BLACK_COLOR; + uint32_t OTG_TEST_PATTERN_PARAMETERS; + uint32_t OTG_TEST_PATTERN_CONTROL; + uint32_t OTG_TEST_PATTERN_COLOR; + uint32_t OTG_CLOCK_CONTROL; + uint32_t OTG_VERTICAL_INTERRUPT2_CONTROL; + uint32_t OTG_VERTICAL_INTERRUPT2_POSITION; + uint32_t OPTC_INPUT_CLOCK_CONTROL; + uint32_t OPTC_DATA_SOURCE_SELECT; + uint32_t OPTC_INPUT_GLOBAL_CONTROL; + uint32_t OPPBUF_CONTROL; + uint32_t OPPBUF_3D_PARAMETERS_0; + uint32_t CONTROL; +}; + +#define TG_COMMON_MASK_SH_LIST_DCN(mask_sh)\ + SF(OTG0_OTG_VSTARTUP_PARAM, VSTARTUP_START, mask_sh),\ + SF(OTG0_OTG_VUPDATE_PARAM, VUPDATE_OFFSET, mask_sh),\ + SF(OTG0_OTG_VUPDATE_PARAM, VUPDATE_WIDTH, mask_sh),\ + SF(OTG0_OTG_VREADY_PARAM, VREADY_OFFSET, mask_sh),\ + SF(OTG0_OTG_BLANK_CONTROL, OTG_BLANK_DATA_EN, mask_sh),\ + SF(OTG0_OTG_BLANK_CONTROL, OTG_BLANK_DE_MODE, mask_sh),\ + SF(OTG0_OTG_BLANK_CONTROL, OTG_CURRENT_BLANK_STATE, mask_sh),\ + SF(OTG0_OTG_MASTER_UPDATE_LOCK, OTG_MASTER_UPDATE_LOCK, mask_sh),\ + SF(OTG0_OTG_MASTER_UPDATE_LOCK, UPDATE_LOCK_STATUS, mask_sh),\ + SF(OTG0_OTG_GLOBAL_CONTROL0, OTG_MASTER_UPDATE_LOCK_SEL, mask_sh),\ + SF(OTG0_OTG_DOUBLE_BUFFER_CONTROL, OTG_UPDATE_PENDING, mask_sh),\ + SF(OTG0_OTG_DOUBLE_BUFFER_CONTROL, OTG_BLANK_DATA_DOUBLE_BUFFER_EN, mask_sh),\ + SF(OTG0_OTG_H_TOTAL, OTG_H_TOTAL, mask_sh),\ + SF(OTG0_OTG_H_BLANK_START_END, OTG_H_BLANK_START, mask_sh),\ + SF(OTG0_OTG_H_BLANK_START_END, OTG_H_BLANK_END, mask_sh),\ + SF(OTG0_OTG_H_SYNC_A, OTG_H_SYNC_A_START, mask_sh),\ + SF(OTG0_OTG_H_SYNC_A, OTG_H_SYNC_A_END, mask_sh),\ + SF(OTG0_OTG_H_SYNC_A_CNTL, OTG_H_SYNC_A_POL, mask_sh),\ + SF(OTG0_OTG_H_TIMING_CNTL, OTG_H_TIMING_DIV_BY2, mask_sh),\ + SF(OTG0_OTG_V_TOTAL, OTG_V_TOTAL, mask_sh),\ + SF(OTG0_OTG_V_BLANK_START_END, OTG_V_BLANK_START, mask_sh),\ + SF(OTG0_OTG_V_BLANK_START_END, OTG_V_BLANK_END, mask_sh),\ + SF(OTG0_OTG_V_SYNC_A, OTG_V_SYNC_A_START, mask_sh),\ + SF(OTG0_OTG_V_SYNC_A, OTG_V_SYNC_A_END, mask_sh),\ + SF(OTG0_OTG_V_SYNC_A_CNTL, OTG_V_SYNC_A_POL, mask_sh),\ + SF(OTG0_OTG_INTERLACE_CONTROL, OTG_INTERLACE_ENABLE, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_MASTER_EN, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_START_POINT_CNTL, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_DISABLE_POINT_CNTL, mask_sh),\ + SF(OTG0_OTG_CONTROL, OTG_FIELD_NUMBER_CNTL, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_EN, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_LINE_NUM, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_POLARITY, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_EYE_FLAG_POLARITY, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_DISABLE_STEREOSYNC_OUTPUT_FOR_DP, mask_sh),\ + SF(OTG0_OTG_STEREO_CONTROL, OTG_DISABLE_STEREOSYNC_OUTPUT_FOR_DP, mask_sh),\ + SF(OTG0_OTG_STEREO_STATUS, OTG_STEREO_CURRENT_EYE, mask_sh),\ + SF(OTG0_OTG_3D_STRUCTURE_CONTROL, OTG_3D_STRUCTURE_EN, mask_sh),\ + SF(OTG0_OTG_3D_STRUCTURE_CONTROL, OTG_3D_STRUCTURE_V_UPDATE_MODE, mask_sh),\ + SF(OTG0_OTG_3D_STRUCTURE_CONTROL, OTG_3D_STRUCTURE_STEREO_SEL_OVR, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_MAX, OTG_V_TOTAL_MAX, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_MIN, OTG_V_TOTAL_MIN, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_V_TOTAL_MIN_SEL, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_V_TOTAL_MAX_SEL, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_FORCE_LOCK_ON_EVENT, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_SET_V_TOTAL_MIN_MASK_EN, mask_sh),\ + SF(OTG0_OTG_V_TOTAL_CONTROL, OTG_SET_V_TOTAL_MIN_MASK, mask_sh),\ + SF(OTG0_OTG_FORCE_COUNT_NOW_CNTL, OTG_FORCE_COUNT_NOW_CLEAR, mask_sh),\ + SF(OTG0_OTG_FORCE_COUNT_NOW_CNTL, OTG_FORCE_COUNT_NOW_MODE, mask_sh),\ + SF(OTG0_OTG_FORCE_COUNT_NOW_CNTL, OTG_FORCE_COUNT_NOW_OCCURRED, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_SOURCE_SELECT, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_SOURCE_PIPE_SELECT, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_RISING_EDGE_DETECT_CNTL, mask_sh),\ + SF(OTG0_OTG_TRIGA_CNTL, OTG_TRIGA_FALLING_EDGE_DETECT_CNTL, mask_sh),\ + SF(OTG0_OTG_STATIC_SCREEN_CONTROL, OTG_STATIC_SCREEN_EVENT_MASK, mask_sh),\ + SF(OTG0_OTG_STATIC_SCREEN_CONTROL, OTG_STATIC_SCREEN_FRAME_COUNT, mask_sh),\ + SF(OTG0_OTG_STATUS_FRAME_COUNT, OTG_FRAME_COUNT, mask_sh),\ + SF(OTG0_OTG_STATUS, OTG_V_BLANK, mask_sh),\ + SF(OTG0_OTG_STATUS, OTG_V_ACTIVE_DISP, mask_sh),\ + SF(OTG0_OTG_STATUS_POSITION, OTG_HORZ_COUNT, mask_sh),\ + SF(OTG0_OTG_STATUS_POSITION, OTG_VERT_COUNT, mask_sh),\ + SF(OTG0_OTG_NOM_VERT_POSITION, OTG_VERT_COUNT_NOM, mask_sh),\ + SF(OTG0_OTG_BLACK_COLOR, OTG_BLACK_COLOR_B_CB, mask_sh),\ + SF(OTG0_OTG_BLACK_COLOR, OTG_BLACK_COLOR_G_Y, mask_sh),\ + SF(OTG0_OTG_BLACK_COLOR, OTG_BLACK_COLOR_R_CR, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_BUSY, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_CLOCK_EN, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_CLOCK_ON, mask_sh),\ + SF(OTG0_OTG_CLOCK_CONTROL, OTG_CLOCK_GATE_DIS, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT2_CONTROL, OTG_VERTICAL_INTERRUPT2_INT_ENABLE, mask_sh),\ + SF(OTG0_OTG_VERTICAL_INTERRUPT2_POSITION, OTG_VERTICAL_INTERRUPT2_LINE_START, mask_sh),\ + SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_EN, mask_sh),\ + SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_ON, mask_sh),\ + SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_GATE_DIS, mask_sh),\ + SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_OCCURRED_STATUS, mask_sh),\ + SF(OPPBUF0_OPPBUF_CONTROL, OPPBUF_ACTIVE_WIDTH, mask_sh),\ + SF(OPPBUF0_OPPBUF_3D_PARAMETERS_0, OPPBUF_3D_VACT_SPACE1_SIZE, mask_sh),\ + SF(VTG0_CONTROL, VTG0_ENABLE, mask_sh),\ + SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\ + SF(VTG0_CONTROL, VTG0_VCOUNT_INIT, mask_sh) + +#define TG_COMMON_MASK_SH_LIST_DCN1_0(mask_sh)\ + TG_COMMON_MASK_SH_LIST_DCN(mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_PARAMETERS, OTG_TEST_PATTERN_INC0, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_PARAMETERS, OTG_TEST_PATTERN_INC1, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_PARAMETERS, OTG_TEST_PATTERN_VRES, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_PARAMETERS, OTG_TEST_PATTERN_HRES, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_PARAMETERS, OTG_TEST_PATTERN_RAMP0_OFFSET, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_CONTROL, OTG_TEST_PATTERN_EN, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_CONTROL, OTG_TEST_PATTERN_MODE, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_CONTROL, OTG_TEST_PATTERN_DYNAMIC_RANGE, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_CONTROL, OTG_TEST_PATTERN_COLOR_FORMAT, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_COLOR, OTG_TEST_PATTERN_MASK, mask_sh),\ + SF(OTG0_OTG_TEST_PATTERN_COLOR, OTG_TEST_PATTERN_DATA, mask_sh),\ + SF(ODM0_OPTC_DATA_SOURCE_SELECT, OPTC_SRC_SEL, mask_sh) + +#define TG_REG_FIELD_LIST(type) \ + type VSTARTUP_START;\ + type VUPDATE_OFFSET;\ + type VUPDATE_WIDTH;\ + type VREADY_OFFSET;\ + type OTG_BLANK_DATA_EN;\ + type OTG_BLANK_DE_MODE;\ + type OTG_CURRENT_BLANK_STATE;\ + type OTG_MASTER_UPDATE_LOCK;\ + type UPDATE_LOCK_STATUS;\ + type OTG_UPDATE_PENDING;\ + type OTG_MASTER_UPDATE_LOCK_SEL;\ + type OTG_BLANK_DATA_DOUBLE_BUFFER_EN;\ + type OTG_H_TOTAL;\ + type OTG_H_BLANK_START;\ + type OTG_H_BLANK_END;\ + type OTG_H_SYNC_A_START;\ + type OTG_H_SYNC_A_END;\ + type OTG_H_SYNC_A_POL;\ + type OTG_H_TIMING_DIV_BY2;\ + type OTG_V_TOTAL;\ + type OTG_V_BLANK_START;\ + type OTG_V_BLANK_END;\ + type OTG_V_SYNC_A_START;\ + type OTG_V_SYNC_A_END;\ + type OTG_V_SYNC_A_POL;\ + type OTG_INTERLACE_ENABLE;\ + type OTG_MASTER_EN;\ + type OTG_START_POINT_CNTL;\ + type OTG_DISABLE_POINT_CNTL;\ + type OTG_FIELD_NUMBER_CNTL;\ + type OTG_STEREO_EN;\ + type OTG_STEREO_SYNC_OUTPUT_LINE_NUM;\ + type OTG_STEREO_SYNC_OUTPUT_POLARITY;\ + type OTG_STEREO_EYE_FLAG_POLARITY;\ + type OTG_STEREO_CURRENT_EYE;\ + type OTG_DISABLE_STEREOSYNC_OUTPUT_FOR_DP;\ + type OTG_3D_STRUCTURE_EN;\ + type OTG_3D_STRUCTURE_V_UPDATE_MODE;\ + type OTG_3D_STRUCTURE_STEREO_SEL_OVR;\ + type OTG_V_TOTAL_MAX;\ + type OTG_V_TOTAL_MIN;\ + type OTG_V_TOTAL_MIN_SEL;\ + type OTG_V_TOTAL_MAX_SEL;\ + type OTG_FORCE_LOCK_ON_EVENT;\ + type OTG_SET_V_TOTAL_MIN_MASK_EN;\ + type OTG_SET_V_TOTAL_MIN_MASK;\ + type OTG_FORCE_COUNT_NOW_CLEAR;\ + type OTG_FORCE_COUNT_NOW_MODE;\ + type OTG_FORCE_COUNT_NOW_OCCURRED;\ + type OTG_TRIGA_SOURCE_SELECT;\ + type OTG_TRIGA_SOURCE_PIPE_SELECT;\ + type OTG_TRIGA_RISING_EDGE_DETECT_CNTL;\ + type OTG_TRIGA_FALLING_EDGE_DETECT_CNTL;\ + type OTG_STATIC_SCREEN_EVENT_MASK;\ + type OTG_STATIC_SCREEN_FRAME_COUNT;\ + type OTG_FRAME_COUNT;\ + type OTG_V_BLANK;\ + type OTG_V_ACTIVE_DISP;\ + type OTG_HORZ_COUNT;\ + type OTG_VERT_COUNT;\ + type OTG_VERT_COUNT_NOM;\ + type OTG_BLACK_COLOR_B_CB;\ + type OTG_BLACK_COLOR_G_Y;\ + type OTG_BLACK_COLOR_R_CR;\ + type OTG_TEST_PATTERN_INC0;\ + type OTG_TEST_PATTERN_INC1;\ + type OTG_TEST_PATTERN_VRES;\ + type OTG_TEST_PATTERN_HRES;\ + type OTG_TEST_PATTERN_RAMP0_OFFSET;\ + type OTG_TEST_PATTERN_EN;\ + type OTG_TEST_PATTERN_MODE;\ + type OTG_TEST_PATTERN_DYNAMIC_RANGE;\ + type OTG_TEST_PATTERN_COLOR_FORMAT;\ + type OTG_TEST_PATTERN_MASK;\ + type OTG_TEST_PATTERN_DATA;\ + type OTG_BUSY;\ + type OTG_CLOCK_EN;\ + type OTG_CLOCK_ON;\ + type OTG_CLOCK_GATE_DIS;\ + type OTG_VERTICAL_INTERRUPT2_INT_ENABLE;\ + type OTG_VERTICAL_INTERRUPT2_LINE_START;\ + type OPTC_INPUT_CLK_EN;\ + type OPTC_INPUT_CLK_ON;\ + type OPTC_INPUT_CLK_GATE_DIS;\ + type OPTC_SRC_SEL;\ + type OPTC_SEG0_SRC_SEL;\ + type OPTC_UNDERFLOW_OCCURRED_STATUS;\ + type OPPBUF_ACTIVE_WIDTH;\ + type OPPBUF_3D_VACT_SPACE1_SIZE;\ + type VTG0_ENABLE;\ + type VTG0_FP2;\ + type VTG0_VCOUNT_INIT; + +struct dcn_tg_shift { + TG_REG_FIELD_LIST(uint8_t) +}; + +struct dcn_tg_mask { + TG_REG_FIELD_LIST(uint32_t) +}; + +struct dcn10_timing_generator { + struct timing_generator base; + + const struct dcn_tg_registers *tg_regs; + const struct dcn_tg_shift *tg_shift; + const struct dcn_tg_mask *tg_mask; + + enum controller_id controller_id; + + uint32_t max_h_total; + uint32_t max_v_total; + + uint32_t min_h_blank; + + uint32_t min_h_sync_width; + uint32_t min_v_sync_width; + uint32_t min_v_blank; + uint32_t min_v_blank_interlace; +}; + +void dcn10_timing_generator_init(struct dcn10_timing_generator *tg); + +struct dcn_otg_state { + uint32_t v_blank_start; + uint32_t v_blank_end; + uint32_t v_sync_a_pol; + uint32_t v_total; + uint32_t v_total_max; + uint32_t v_total_min; + uint32_t v_sync_a_start; + uint32_t v_sync_a_end; + uint32_t h_blank_start; + uint32_t h_blank_end; + uint32_t h_sync_a_start; + uint32_t h_sync_a_end; + uint32_t h_sync_a_pol; + uint32_t h_total; + uint32_t underflow_occurred_status; + uint32_t otg_enabled; +}; + +void tgn10_read_otg_state(struct dcn10_timing_generator *tgn10, + struct dcn_otg_state *s); + +#endif /* __DC_TIMING_GENERATOR_DCN10_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h new file mode 100644 index 000000000000..ab88f07772a3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h @@ -0,0 +1,105 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/** + * This file defines helper functions provided by the Display Manager to + * Display Core. + */ +#ifndef __DM_HELPERS__ +#define __DM_HELPERS__ + +#include "dc_types.h" +#include "dc.h" + +struct dp_mst_stream_allocation_table; + +enum dc_edid_status dm_helpers_parse_edid_caps( + struct dc_context *ctx, + const struct dc_edid *edid, + struct dc_edid_caps *edid_caps); + +/* + * Writes payload allocation table in immediate downstream device. + */ +bool dm_helpers_dp_mst_write_payload_allocation_table( + struct dc_context *ctx, + const struct dc_stream_state *stream, + struct dp_mst_stream_allocation_table *proposed_table, + bool enable); + +/* + * Polls for ACT (allocation change trigger) handled and + */ +bool dm_helpers_dp_mst_poll_for_allocation_change_trigger( + struct dc_context *ctx, + const struct dc_stream_state *stream); +/* + * Sends ALLOCATE_PAYLOAD message. + */ +bool dm_helpers_dp_mst_send_payload_allocation( + struct dc_context *ctx, + const struct dc_stream_state *stream, + bool enable); + +bool dm_helpers_dp_mst_start_top_mgr( + struct dc_context *ctx, + const struct dc_link *link, + bool boot); + +void dm_helpers_dp_mst_stop_top_mgr( + struct dc_context *ctx, + const struct dc_link *link); +/** + * OS specific aux read callback. + */ +bool dm_helpers_dp_read_dpcd( + struct dc_context *ctx, + const struct dc_link *link, + uint32_t address, + uint8_t *data, + uint32_t size); + +/** + * OS specific aux write callback. + */ +bool dm_helpers_dp_write_dpcd( + struct dc_context *ctx, + const struct dc_link *link, + uint32_t address, + const uint8_t *data, + uint32_t size); + +bool dm_helpers_submit_i2c( + struct dc_context *ctx, + const struct dc_link *link, + struct i2c_command *cmd); + +enum dc_edid_status dm_helpers_read_local_edid( + struct dc_context *ctx, + struct dc_link *link, + struct dc_sink *sink); + + +#endif /* __DM_HELPERS__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dm_pp_smu.h b/drivers/gpu/drm/amd/display/dc/dm_pp_smu.h new file mode 100644 index 000000000000..bbfa83252fc1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dm_pp_smu.h @@ -0,0 +1,131 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DM_PP_SMU_IF__H +#define DM_PP_SMU_IF__H + +/* + * interface to PPLIB/SMU to setup clocks and pstate requirements on SoC + */ + + +struct pp_smu { + struct dc_context *ctx; +}; + +enum wm_set_id { + WM_A, + WM_B, + WM_C, + WM_D, + WM_COUNT, +}; + +struct pp_smu_wm_set_range { + enum wm_set_id wm_inst; + uint32_t min_fill_clk_khz; + uint32_t max_fill_clk_khz; + uint32_t min_drain_clk_khz; + uint32_t max_drain_clk_khz; +}; + +struct pp_smu_wm_range_sets { + uint32_t num_reader_wm_sets; + struct pp_smu_wm_set_range reader_wm_sets[WM_COUNT]; + + uint32_t num_writer_wm_sets; + struct pp_smu_wm_set_range writer_wm_sets[WM_COUNT]; +}; + +struct pp_smu_display_requirement_rv { + /* PPSMC_MSG_SetDisplayCount: count + * 0 triggers S0i2 optimization + */ + unsigned int display_count; + + /* PPSMC_MSG_SetHardMinFclkByFreq: khz + * FCLK will vary with DPM, but never below requested hard min + */ + unsigned int hard_min_fclk_khz; + + /* PPSMC_MSG_SetHardMinDcefclkByFreq: khz + * fixed clock at requested freq, either from FCH bypass or DFS + */ + unsigned int hard_min_dcefclk_khz; + + /* PPSMC_MSG_SetMinDeepSleepDcefclk: mhz + * when DF is in cstate, dcf clock is further divided down + * to just above given frequency + */ + unsigned int min_deep_sleep_dcefclk_mhz; +}; + +struct pp_smu_funcs_rv { + struct pp_smu pp_smu; + + void (*set_display_requirement)(struct pp_smu *pp, + struct pp_smu_display_requirement_rv *req); + + /* which SMU message? are reader and writer WM separate SMU msg? */ + void (*set_wm_ranges)(struct pp_smu *pp, + struct pp_smu_wm_range_sets *ranges); + +}; + +#if 0 +struct pp_smu_funcs_rv { + + /* PPSMC_MSG_SetDisplayCount + * 0 triggers S0i2 optimization + */ + void (*set_display_count)(struct pp_smu *pp, int count); + + /* PPSMC_MSG_SetHardMinFclkByFreq + * FCLK will vary with DPM, but never below requested hard min + */ + void (*set_hard_min_fclk_by_freq)(struct pp_smu *pp, int khz); + + /* PPSMC_MSG_SetHardMinDcefclkByFreq + * fixed clock at requested freq, either from FCH bypass or DFS + */ + void (*set_hard_min_dcefclk_by_freq)(struct pp_smu *pp, int khz); + + /* PPSMC_MSG_SetMinDeepSleepDcefclk + * when DF is in cstate, dcf clock is further divided down + * to just above given frequency + */ + void (*set_min_deep_sleep_dcefclk)(struct pp_smu *pp, int mhz); + + /* todo: aesthetic + * watermark range table + */ + + /* todo: functional/feature + * PPSMC_MSG_SetHardMinSocclkByFreq: required to support DWB + */ +}; +#endif + +#endif /* DM_PP_SMU_IF__H */ diff --git a/drivers/gpu/drm/amd/display/dc/dm_services.h b/drivers/gpu/drm/amd/display/dc/dm_services.h new file mode 100644 index 000000000000..d4917037ac42 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dm_services.h @@ -0,0 +1,387 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/** + * This file defines external dependencies of Display Core. + */ + +#ifndef __DM_SERVICES_H__ + +#define __DM_SERVICES_H__ + +/* TODO: remove when DC is complete. */ +#include "dm_services_types.h" +#include "logger_interface.h" +#include "link_service_types.h" + +#undef DEPRECATED + +irq_handler_idx dm_register_interrupt( + struct dc_context *ctx, + struct dc_interrupt_params *int_params, + interrupt_handler ih, + void *handler_args); + + +/* + * + * GPU registers access + * + */ + +/* enable for debugging new code, this adds 50k to the driver size. */ +/* #define DM_CHECK_ADDR_0 */ + +#define dm_read_reg(ctx, address) \ + dm_read_reg_func(ctx, address, __func__) + +static inline uint32_t dm_read_reg_func( + const struct dc_context *ctx, + uint32_t address, + const char *func_name) +{ + uint32_t value; +#ifdef DM_CHECK_ADDR_0 + if (address == 0) { + DC_ERR("invalid register read; address = 0\n"); + return 0; + } +#endif + value = cgs_read_register(ctx->cgs_device, address); + + return value; +} + +#define dm_write_reg(ctx, address, value) \ + dm_write_reg_func(ctx, address, value, __func__) + +static inline void dm_write_reg_func( + const struct dc_context *ctx, + uint32_t address, + uint32_t value, + const char *func_name) +{ +#ifdef DM_CHECK_ADDR_0 + if (address == 0) { + DC_ERR("invalid register write. address = 0"); + return; + } +#endif + cgs_write_register(ctx->cgs_device, address, value); +} + +static inline uint32_t dm_read_index_reg( + const struct dc_context *ctx, + enum cgs_ind_reg addr_space, + uint32_t index) +{ + return cgs_read_ind_register(ctx->cgs_device, addr_space, index); +} + +static inline void dm_write_index_reg( + const struct dc_context *ctx, + enum cgs_ind_reg addr_space, + uint32_t index, + uint32_t value) +{ + cgs_write_ind_register(ctx->cgs_device, addr_space, index, value); +} + +static inline uint32_t get_reg_field_value_ex( + uint32_t reg_value, + uint32_t mask, + uint8_t shift) +{ + return (mask & reg_value) >> shift; +} + +#define get_reg_field_value(reg_value, reg_name, reg_field)\ + get_reg_field_value_ex(\ + (reg_value),\ + reg_name ## __ ## reg_field ## _MASK,\ + reg_name ## __ ## reg_field ## __SHIFT) + +static inline uint32_t set_reg_field_value_ex( + uint32_t reg_value, + uint32_t value, + uint32_t mask, + uint8_t shift) +{ + ASSERT(mask != 0); + return (reg_value & ~mask) | (mask & (value << shift)); +} + +#define set_reg_field_value(reg_value, value, reg_name, reg_field)\ + (reg_value) = set_reg_field_value_ex(\ + (reg_value),\ + (value),\ + reg_name ## __ ## reg_field ## _MASK,\ + reg_name ## __ ## reg_field ## __SHIFT) + +uint32_t generic_reg_update_ex(const struct dc_context *ctx, + uint32_t addr, uint32_t reg_val, int n, + uint8_t shift1, uint32_t mask1, uint32_t field_value1, ...); + +#define FD(reg_field) reg_field ## __SHIFT, \ + reg_field ## _MASK + +/* + * return number of poll before condition is met + * return 0 if condition is not meet after specified time out tries + */ +unsigned int generic_reg_wait(const struct dc_context *ctx, + uint32_t addr, uint32_t mask, uint32_t shift, uint32_t condition_value, + unsigned int delay_between_poll_us, unsigned int time_out_num_tries, + const char *func_name, int line); + + +/* These macros need to be used with soc15 registers in order to retrieve + * the actual offset. + */ +#define dm_write_reg_soc15(ctx, reg, inst_offset, value) \ + dm_write_reg_func(ctx, reg + DCE_BASE.instance[0].segment[reg##_BASE_IDX] + inst_offset, value, __func__) + +#define dm_read_reg_soc15(ctx, reg, inst_offset) \ + dm_read_reg_func(ctx, reg + DCE_BASE.instance[0].segment[reg##_BASE_IDX] + inst_offset, __func__) + +#define generic_reg_update_soc15(ctx, inst_offset, reg_name, n, ...)\ + generic_reg_update_ex(ctx, DCE_BASE.instance[0].segment[mm##reg_name##_BASE_IDX] + mm##reg_name + inst_offset, \ + dm_read_reg_func(ctx, mm##reg_name + DCE_BASE.instance[0].segment[mm##reg_name##_BASE_IDX] + inst_offset, __func__), \ + n, __VA_ARGS__) + +#define generic_reg_set_soc15(ctx, inst_offset, reg_name, n, ...)\ + generic_reg_update_ex(ctx, DCE_BASE.instance[0].segment[mm##reg_name##_BASE_IDX] + mm##reg_name + inst_offset, 0, \ + n, __VA_ARGS__) + +#define get_reg_field_value_soc15(reg_value, block, reg_num, reg_name, reg_field)\ + get_reg_field_value_ex(\ + (reg_value),\ + block ## reg_num ## _ ## reg_name ## __ ## reg_field ## _MASK,\ + block ## reg_num ## _ ## reg_name ## __ ## reg_field ## __SHIFT) + +#define set_reg_field_value_soc15(reg_value, value, block, reg_num, reg_name, reg_field)\ + (reg_value) = set_reg_field_value_ex(\ + (reg_value),\ + (value),\ + block ## reg_num ## _ ## reg_name ## __ ## reg_field ## _MASK,\ + block ## reg_num ## _ ## reg_name ## __ ## reg_field ## __SHIFT) + +/************************************** + * Power Play (PP) interfaces + **************************************/ + +/* DAL calls this function to notify PP about clocks it needs for the Mode Set. + * This is done *before* it changes DCE clock. + * + * If required clock is higher than current, then PP will increase the voltage. + * + * If required clock is lower than current, then PP will defer reduction of + * voltage until the call to dc_service_pp_post_dce_clock_change(). + * + * \input - Contains clocks needed for Mode Set. + * + * \output - Contains clocks adjusted by PP which DAL should use for Mode Set. + * Valid only if function returns zero. + * + * \returns true - call is successful + * false - call failed + */ +bool dm_pp_pre_dce_clock_change( + struct dc_context *ctx, + struct dm_pp_gpu_clock_range *requested_state, + struct dm_pp_gpu_clock_range *actual_state); + +/* The returned clocks range are 'static' system clocks which will be used for + * mode validation purposes. + * + * \returns true - call is successful + * false - call failed + */ +bool dc_service_get_system_clocks_range( + const struct dc_context *ctx, + struct dm_pp_gpu_clock_range *sys_clks); + +/* Gets valid clocks levels from pplib + * + * input: clk_type - display clk / sclk / mem clk + * + * output: array of valid clock levels for given type in ascending order, + * with invalid levels filtered out + * + */ +bool dm_pp_get_clock_levels_by_type( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels *clk_level_info); + +bool dm_pp_get_clock_levels_by_type_with_latency( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels_with_latency *clk_level_info); + +bool dm_pp_get_clock_levels_by_type_with_voltage( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels_with_voltage *clk_level_info); + +bool dm_pp_notify_wm_clock_changes( + const struct dc_context *ctx, + struct dm_pp_wm_sets_with_clock_ranges *wm_with_clock_ranges); + +void dm_pp_get_funcs_rv(struct dc_context *ctx, + struct pp_smu_funcs_rv *funcs); + +/* DAL calls this function to notify PP about completion of Mode Set. + * For PP it means that current DCE clocks are those which were returned + * by dc_service_pp_pre_dce_clock_change(), in the 'output' parameter. + * + * If the clocks are higher than before, then PP does nothing. + * + * If the clocks are lower than before, then PP reduces the voltage. + * + * \returns true - call is successful + * false - call failed + */ +bool dm_pp_apply_display_requirements( + const struct dc_context *ctx, + const struct dm_pp_display_configuration *pp_display_cfg); + +bool dm_pp_apply_power_level_change_request( + const struct dc_context *ctx, + struct dm_pp_power_level_change_request *level_change_req); + +bool dm_pp_apply_clock_for_voltage_request( + const struct dc_context *ctx, + struct dm_pp_clock_for_voltage_req *clock_for_voltage_req); + +bool dm_pp_get_static_clocks( + const struct dc_context *ctx, + struct dm_pp_static_clock_info *static_clk_info); + +/****** end of PP interfaces ******/ + +struct persistent_data_flag { + bool save_per_link; + bool save_per_edid; +}; + +/* Call to write data in registry editor for persistent data storage. + * + * \inputs sink - identify edid/link for registry folder creation + * module name - identify folders for registry + * key name - identify keys within folders for registry + * params - value to write in defined folder/key + * size - size of the input params + * flag - determine whether to save by link or edid + * + * \returns true - call is successful + * false - call failed + * + * sink module key + * ----------------------------------------------------------------------------- + * NULL NULL NULL - failure + * NULL NULL - - create key with param value + * under base folder + * NULL - NULL - create module folder under base folder + * - NULL NULL - failure + * NULL - - - create key under module folder + * with no edid/link identification + * - NULL - - create key with param value + * under base folder + * - - NULL - create module folder under base folder + * - - - - create key under module folder + * with edid/link identification + */ +bool dm_write_persistent_data(struct dc_context *ctx, + const struct dc_sink *sink, + const char *module_name, + const char *key_name, + void *params, + unsigned int size, + struct persistent_data_flag *flag); + + +/* Call to read data in registry editor for persistent data storage. + * + * \inputs sink - identify edid/link for registry folder creation + * module name - identify folders for registry + * key name - identify keys within folders for registry + * size - size of the output params + * flag - determine whether it was save by link or edid + * + * \returns params - value read from defined folder/key + * true - call is successful + * false - call failed + * + * sink module key + * ----------------------------------------------------------------------------- + * NULL NULL NULL - failure + * NULL NULL - - read key under base folder + * NULL - NULL - failure + * - NULL NULL - failure + * NULL - - - read key under module folder + * with no edid/link identification + * - NULL - - read key under base folder + * - - NULL - failure + * - - - - read key under module folder + * with edid/link identification + */ +bool dm_read_persistent_data(struct dc_context *ctx, + const struct dc_sink *sink, + const char *module_name, + const char *key_name, + void *params, + unsigned int size, + struct persistent_data_flag *flag); + +bool dm_query_extended_brightness_caps + (struct dc_context *ctx, enum dm_acpi_display_type display, + struct dm_acpi_atif_backlight_caps *pCaps); + +bool dm_dmcu_set_pipe(struct dc_context *ctx, unsigned int controller_id); + +/* + * + * print-out services + * + */ +#define dm_log_to_buffer(buffer, size, fmt, args)\ + vsnprintf(buffer, size, fmt, args) + +unsigned long long dm_get_timestamp(struct dc_context *ctx); + +/* + * Debug and verification hooks + */ +bool dm_helpers_dc_conn_log( + struct dc_context *ctx, + struct log_entry *entry, + enum dc_log_type event); + +void dm_dtn_log_begin(struct dc_context *ctx); +void dm_dtn_log_append_v(struct dc_context *ctx, const char *msg, ...); +void dm_dtn_log_end(struct dc_context *ctx); + +#endif /* __DM_SERVICES_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dm_services_types.h b/drivers/gpu/drm/amd/display/dc/dm_services_types.h new file mode 100644 index 000000000000..fa26cf488b3c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dm_services_types.h @@ -0,0 +1,282 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DM_SERVICES_TYPES_H__ +#define __DM_SERVICES_TYPES_H__ + +#include "os_types.h" +#include "dc_types.h" + +#include "dm_pp_smu.h" + +struct dm_pp_clock_range { + int min_khz; + int max_khz; +}; + +enum dm_pp_clocks_state { + DM_PP_CLOCKS_STATE_INVALID, + DM_PP_CLOCKS_STATE_ULTRA_LOW, + DM_PP_CLOCKS_STATE_LOW, + DM_PP_CLOCKS_STATE_NOMINAL, + DM_PP_CLOCKS_STATE_PERFORMANCE, + + /* Starting from DCE11, Max 8 levels of DPM state supported. */ + DM_PP_CLOCKS_DPM_STATE_LEVEL_INVALID = DM_PP_CLOCKS_STATE_INVALID, + DM_PP_CLOCKS_DPM_STATE_LEVEL_0, + DM_PP_CLOCKS_DPM_STATE_LEVEL_1, + DM_PP_CLOCKS_DPM_STATE_LEVEL_2, + /* to be backward compatible */ + DM_PP_CLOCKS_DPM_STATE_LEVEL_3, + DM_PP_CLOCKS_DPM_STATE_LEVEL_4, + DM_PP_CLOCKS_DPM_STATE_LEVEL_5, + DM_PP_CLOCKS_DPM_STATE_LEVEL_6, + DM_PP_CLOCKS_DPM_STATE_LEVEL_7, + + DM_PP_CLOCKS_MAX_STATES +}; + +struct dm_pp_gpu_clock_range { + enum dm_pp_clocks_state clock_state; + struct dm_pp_clock_range sclk; + struct dm_pp_clock_range mclk; + struct dm_pp_clock_range eclk; + struct dm_pp_clock_range dclk; +}; + +enum dm_pp_clock_type { + DM_PP_CLOCK_TYPE_DISPLAY_CLK = 1, + DM_PP_CLOCK_TYPE_ENGINE_CLK, /* System clock */ + DM_PP_CLOCK_TYPE_MEMORY_CLK, + DM_PP_CLOCK_TYPE_DCFCLK, + DM_PP_CLOCK_TYPE_DCEFCLK, + DM_PP_CLOCK_TYPE_SOCCLK, + DM_PP_CLOCK_TYPE_PIXELCLK, + DM_PP_CLOCK_TYPE_DISPLAYPHYCLK, + DM_PP_CLOCK_TYPE_DPPCLK, + DM_PP_CLOCK_TYPE_FCLK, +}; + +#define DC_DECODE_PP_CLOCK_TYPE(clk_type) \ + (clk_type) == DM_PP_CLOCK_TYPE_DISPLAY_CLK ? "Display" : \ + (clk_type) == DM_PP_CLOCK_TYPE_ENGINE_CLK ? "Engine" : \ + (clk_type) == DM_PP_CLOCK_TYPE_MEMORY_CLK ? "Memory" : "Invalid" + +#define DM_PP_MAX_CLOCK_LEVELS 8 + +struct dm_pp_clock_levels { + uint32_t num_levels; + uint32_t clocks_in_khz[DM_PP_MAX_CLOCK_LEVELS]; +}; + +struct dm_pp_clock_with_latency { + uint32_t clocks_in_khz; + uint32_t latency_in_us; +}; + +struct dm_pp_clock_levels_with_latency { + uint32_t num_levels; + struct dm_pp_clock_with_latency data[DM_PP_MAX_CLOCK_LEVELS]; +}; + +struct dm_pp_clock_with_voltage { + uint32_t clocks_in_khz; + uint32_t voltage_in_mv; +}; + +struct dm_pp_clock_levels_with_voltage { + uint32_t num_levels; + struct dm_pp_clock_with_voltage data[DM_PP_MAX_CLOCK_LEVELS]; +}; + +struct dm_pp_single_disp_config { + enum signal_type signal; + uint8_t transmitter; + uint8_t ddi_channel_mapping; + uint8_t pipe_idx; + uint32_t src_height; + uint32_t src_width; + uint32_t v_refresh; + uint32_t sym_clock; /* HDMI only */ + struct dc_link_settings link_settings; /* DP only */ +}; + +#define MAX_WM_SETS 4 + +enum dm_pp_wm_set_id { + WM_SET_A = 0, + WM_SET_B, + WM_SET_C, + WM_SET_D, + WM_SET_INVALID = 0xffff, +}; + +struct dm_pp_clock_range_for_wm_set { + enum dm_pp_wm_set_id wm_set_id; + uint32_t wm_min_eng_clk_in_khz; + uint32_t wm_max_eng_clk_in_khz; + uint32_t wm_min_memg_clk_in_khz; + uint32_t wm_max_mem_clk_in_khz; +}; + +struct dm_pp_wm_sets_with_clock_ranges { + uint32_t num_wm_sets; + struct dm_pp_clock_range_for_wm_set wm_clk_ranges[MAX_WM_SETS]; +}; + +struct dm_pp_clock_range_for_dmif_wm_set_soc15 { + enum dm_pp_wm_set_id wm_set_id; + uint32_t wm_min_dcfclk_clk_in_khz; + uint32_t wm_max_dcfclk_clk_in_khz; + uint32_t wm_min_memg_clk_in_khz; + uint32_t wm_max_mem_clk_in_khz; +}; + +struct dm_pp_clock_range_for_mcif_wm_set_soc15 { + enum dm_pp_wm_set_id wm_set_id; + uint32_t wm_min_socclk_clk_in_khz; + uint32_t wm_max_socclk_clk_in_khz; + uint32_t wm_min_memg_clk_in_khz; + uint32_t wm_max_mem_clk_in_khz; +}; + +struct dm_pp_wm_sets_with_clock_ranges_soc15 { + uint32_t num_wm_dmif_sets; + uint32_t num_wm_mcif_sets; + struct dm_pp_clock_range_for_dmif_wm_set_soc15 + wm_dmif_clocks_ranges[MAX_WM_SETS]; + struct dm_pp_clock_range_for_mcif_wm_set_soc15 + wm_mcif_clocks_ranges[MAX_WM_SETS]; +}; + +#define MAX_DISPLAY_CONFIGS 6 + +struct dm_pp_display_configuration { + bool nb_pstate_switch_disable;/* controls NB PState switch */ + bool cpu_cc6_disable; /* controls CPU CState switch ( on or off) */ + bool cpu_pstate_disable; + uint32_t cpu_pstate_separation_time; + + uint32_t min_memory_clock_khz; + uint32_t min_engine_clock_khz; + uint32_t min_engine_clock_deep_sleep_khz; + + uint32_t avail_mclk_switch_time_us; + uint32_t avail_mclk_switch_time_in_disp_active_us; + uint32_t min_dcfclock_khz; + uint32_t min_dcfc_deep_sleep_clock_khz; + + uint32_t disp_clk_khz; + + bool all_displays_in_sync; + + uint8_t display_count; + struct dm_pp_single_disp_config disp_configs[MAX_DISPLAY_CONFIGS]; + + /*Controller Index of primary display - used in MCLK SMC switching hang + * SW Workaround*/ + uint8_t crtc_index; + /*htotal*1000/pixelclk - used in MCLK SMC switching hang SW Workaround*/ + uint32_t line_time_in_us; +}; + +struct dm_bl_data_point { + /* Brightness level in percentage */ + uint8_t luminance; + /* Brightness level as effective value in range 0-255, + * corresponding to above percentage + */ + uint8_t signalLevel; +}; + +/* Total size of the structure should not exceed 256 bytes */ +struct dm_acpi_atif_backlight_caps { + + + uint16_t size; /* Bytes 0-1 (2 bytes) */ + uint16_t flags; /* Byted 2-3 (2 bytes) */ + uint8_t errorCode; /* Byte 4 */ + uint8_t acLevelPercentage; /* Byte 5 */ + uint8_t dcLevelPercentage; /* Byte 6 */ + uint8_t minInputSignal; /* Byte 7 */ + uint8_t maxInputSignal; /* Byte 8 */ + uint8_t numOfDataPoints; /* Byte 9 */ + struct dm_bl_data_point dataPoints[99]; /* Bytes 10-207 (198 bytes)*/ +}; + +enum dm_acpi_display_type { + AcpiDisplayType_LCD1 = 0, + AcpiDisplayType_CRT1 = 1, + AcpiDisplayType_DFP1 = 3, + AcpiDisplayType_CRT2 = 4, + AcpiDisplayType_LCD2 = 5, + AcpiDisplayType_DFP2 = 7, + AcpiDisplayType_DFP3 = 9, + AcpiDisplayType_DFP4 = 10, + AcpiDisplayType_DFP5 = 11, + AcpiDisplayType_DFP6 = 12 +}; + +enum dm_pp_power_level { + DM_PP_POWER_LEVEL_INVALID, + DM_PP_POWER_LEVEL_ULTRA_LOW, + DM_PP_POWER_LEVEL_LOW, + DM_PP_POWER_LEVEL_NOMINAL, + DM_PP_POWER_LEVEL_PERFORMANCE, + + DM_PP_POWER_LEVEL_0 = DM_PP_POWER_LEVEL_ULTRA_LOW, + DM_PP_POWER_LEVEL_1 = DM_PP_POWER_LEVEL_LOW, + DM_PP_POWER_LEVEL_2 = DM_PP_POWER_LEVEL_NOMINAL, + DM_PP_POWER_LEVEL_3 = DM_PP_POWER_LEVEL_PERFORMANCE, + DM_PP_POWER_LEVEL_4 = DM_PP_CLOCKS_DPM_STATE_LEVEL_3 + 1, + DM_PP_POWER_LEVEL_5 = DM_PP_CLOCKS_DPM_STATE_LEVEL_4 + 1, + DM_PP_POWER_LEVEL_6 = DM_PP_CLOCKS_DPM_STATE_LEVEL_5 + 1, + DM_PP_POWER_LEVEL_7 = DM_PP_CLOCKS_DPM_STATE_LEVEL_6 + 1, +}; + +struct dm_pp_power_level_change_request { + enum dm_pp_power_level power_level; +}; + +struct dm_pp_clock_for_voltage_req { + enum dm_pp_clock_type clk_type; + uint32_t clocks_in_khz; +}; + +struct dm_pp_static_clock_info { + uint32_t max_sclk_khz; + uint32_t max_mclk_khz; + + /* max possible display block clocks state */ + enum dm_pp_clocks_state max_clocks_state; +}; + +struct dtn_min_clk_info { + uint32_t disp_clk_khz; + uint32_t min_engine_clock_khz; + uint32_t min_memory_clock_khz; +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/Makefile b/drivers/gpu/drm/amd/display/dc/dml/Makefile new file mode 100644 index 000000000000..87bab8e8139f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the 'utils' sub-component of DAL. +# It provides the general basic services required by other DAL +# subcomponents. + +CFLAGS_display_mode_vba.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_display_mode_lib.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_display_pipe_clocks.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_display_rq_dlg_calc.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_dml1_display_rq_dlg_calc.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_display_rq_dlg_helpers.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_soc_bounding_box.o := -mhard-float -msse -mpreferred-stack-boundary=4 +CFLAGS_dml_common_defs.o := -mhard-float -msse -mpreferred-stack-boundary=4 + + +DML = display_mode_lib.o display_rq_dlg_calc.o \ + display_rq_dlg_helpers.o dml1_display_rq_dlg_calc.o \ + soc_bounding_box.o dml_common_defs.o display_mode_vba.o + +AMD_DAL_DML = $(addprefix $(AMDDALPATH)/dc/dml/,$(DML)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DML) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dc_features.h b/drivers/gpu/drm/amd/display/dc/dml/dc_features.h new file mode 100644 index 000000000000..ea4cde952f4f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dc_features.h @@ -0,0 +1,559 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DC_FEATURES_H__ +#define __DC_FEATURES_H__ + +// local features +#define DC__PRESENT 1 +#define DC__PRESENT__1 1 +#define DC__NUM_DPP 4 +#define DC__VOLTAGE_STATES 7 +#define DC__NUM_DPP__4 1 +#define DC__NUM_DPP__0_PRESENT 1 +#define DC__NUM_DPP__1_PRESENT 1 +#define DC__NUM_DPP__2_PRESENT 1 +#define DC__NUM_DPP__3_PRESENT 1 +#define DC__NUM_DPP__MAX 8 +#define DC__NUM_DPP__MAX__8 1 +#define DC__PIPE_10BIT 0 +#define DC__PIPE_10BIT__0 1 +#define DC__PIPE_10BIT__MAX 1 +#define DC__PIPE_10BIT__MAX__1 1 +#define DC__NUM_OPP 4 +#define DC__NUM_OPP__4 1 +#define DC__NUM_OPP__0_PRESENT 1 +#define DC__NUM_OPP__1_PRESENT 1 +#define DC__NUM_OPP__2_PRESENT 1 +#define DC__NUM_OPP__3_PRESENT 1 +#define DC__NUM_OPP__MAX 6 +#define DC__NUM_OPP__MAX__6 1 +#define DC__NUM_DSC 0 +#define DC__NUM_DSC__0 1 +#define DC__NUM_DSC__MAX 6 +#define DC__NUM_DSC__MAX__6 1 +#define DC__NUM_ABM 1 +#define DC__NUM_ABM__1 1 +#define DC__NUM_ABM__0_PRESENT 1 +#define DC__NUM_ABM__MAX 2 +#define DC__NUM_ABM__MAX__2 1 +#define DC__ODM_PRESENT 0 +#define DC__ODM_PRESENT__0 1 +#define DC__NUM_OTG 4 +#define DC__NUM_OTG__4 1 +#define DC__NUM_OTG__0_PRESENT 1 +#define DC__NUM_OTG__1_PRESENT 1 +#define DC__NUM_OTG__2_PRESENT 1 +#define DC__NUM_OTG__3_PRESENT 1 +#define DC__NUM_OTG__MAX 6 +#define DC__NUM_OTG__MAX__6 1 +#define DC__NUM_DWB 2 +#define DC__NUM_DWB__2 1 +#define DC__NUM_DWB__0_PRESENT 1 +#define DC__NUM_DWB__1_PRESENT 1 +#define DC__NUM_DWB__MAX 2 +#define DC__NUM_DWB__MAX__2 1 +#define DC__NUM_DIG 4 +#define DC__NUM_DIG__4 1 +#define DC__NUM_DIG__0_PRESENT 1 +#define DC__NUM_DIG__1_PRESENT 1 +#define DC__NUM_DIG__2_PRESENT 1 +#define DC__NUM_DIG__3_PRESENT 1 +#define DC__NUM_DIG__MAX 6 +#define DC__NUM_DIG__MAX__6 1 +#define DC__NUM_AUX 4 +#define DC__NUM_AUX__4 1 +#define DC__NUM_AUX__0_PRESENT 1 +#define DC__NUM_AUX__1_PRESENT 1 +#define DC__NUM_AUX__2_PRESENT 1 +#define DC__NUM_AUX__3_PRESENT 1 +#define DC__NUM_AUX__MAX 6 +#define DC__NUM_AUX__MAX__6 1 +#define DC__NUM_AUDIO_STREAMS 4 +#define DC__NUM_AUDIO_STREAMS__4 1 +#define DC__NUM_AUDIO_STREAMS__0_PRESENT 1 +#define DC__NUM_AUDIO_STREAMS__1_PRESENT 1 +#define DC__NUM_AUDIO_STREAMS__2_PRESENT 1 +#define DC__NUM_AUDIO_STREAMS__3_PRESENT 1 +#define DC__NUM_AUDIO_STREAMS__MAX 8 +#define DC__NUM_AUDIO_STREAMS__MAX__8 1 +#define DC__NUM_AUDIO_ENDPOINTS 6 +#define DC__NUM_AUDIO_ENDPOINTS__6 1 +#define DC__NUM_AUDIO_ENDPOINTS__0_PRESENT 1 +#define DC__NUM_AUDIO_ENDPOINTS__1_PRESENT 1 +#define DC__NUM_AUDIO_ENDPOINTS__2_PRESENT 1 +#define DC__NUM_AUDIO_ENDPOINTS__3_PRESENT 1 +#define DC__NUM_AUDIO_ENDPOINTS__4_PRESENT 1 +#define DC__NUM_AUDIO_ENDPOINTS__5_PRESENT 1 +#define DC__NUM_AUDIO_ENDPOINTS__MAX 8 +#define DC__NUM_AUDIO_ENDPOINTS__MAX__8 1 +#define DC__NUM_AUDIO_INPUT_STREAMS 0 +#define DC__NUM_AUDIO_INPUT_STREAMS__0 1 +#define DC__NUM_AUDIO_INPUT_STREAMS__MAX 8 +#define DC__NUM_AUDIO_INPUT_STREAMS__MAX__8 1 +#define DC__NUM_AUDIO_INPUT_ENDPOINTS 0 +#define DC__NUM_AUDIO_INPUT_ENDPOINTS__0 1 +#define DC__NUM_AUDIO_INPUT_ENDPOINTS__MAX 8 +#define DC__NUM_AUDIO_INPUT_ENDPOINTS__MAX__8 1 +#define DC__NUM_CURSOR 1 +#define DC__NUM_CURSOR__1 1 +#define DC__NUM_CURSOR__0_PRESENT 1 +#define DC__NUM_CURSOR__MAX 2 +#define DC__NUM_CURSOR__MAX__2 1 +#define DC__DIGITAL_BYPASS_PRESENT 0 +#define DC__DIGITAL_BYPASS_PRESENT__0 1 +#define DC__HCID_HWMAJVER 1 +#define DC__HCID_HWMAJVER__1 1 +#define DC__HCID_HWMINVER 0 +#define DC__HCID_HWMINVER__0 1 +#define DC__HCID_HWREV 0 +#define DC__HCID_HWREV__0 1 +#define DC__ROMSTRAP_PRESENT 0 +#define DC__ROMSTRAP_PRESENT__0 1 +#define DC__NUM_RBBMIF_DECODES 30 +#define DC__NUM_RBBMIF_DECODES__30 1 +#define DC__NUM_DBG_REGS 36 +#define DC__NUM_DBG_REGS__36 1 +#define DC__NUM_PIPES_UNDERLAY 0 +#define DC__NUM_PIPES_UNDERLAY__0 1 +#define DC__NUM_PIPES_UNDERLAY__MAX 2 +#define DC__NUM_PIPES_UNDERLAY__MAX__2 1 +#define DC__NUM_VCE_ENGINE 1 +#define DC__NUM_VCE_ENGINE__1 1 +#define DC__NUM_VCE_ENGINE__0_PRESENT 1 +#define DC__NUM_VCE_ENGINE__MAX 2 +#define DC__NUM_VCE_ENGINE__MAX__2 1 +#define DC__OTG_EXTERNAL_SYNC_PRESENT 0 +#define DC__OTG_EXTERNAL_SYNC_PRESENT__0 1 +#define DC__OTG_CRC_PRESENT 1 +#define DC__OTG_CRC_PRESENT__1 1 +#define DC__VIP_PRESENT 0 +#define DC__VIP_PRESENT__0 1 +#define DC__DTMTEST_PRESENT 0 +#define DC__DTMTEST_PRESENT__0 1 +#define DC__POWER_GATE_PRESENT 1 +#define DC__POWER_GATE_PRESENT__1 1 +#define DC__MEM_PG 1 +#define DC__MEM_PG__1 1 +#define DC__FMT_SRC_SEL_PRESENT 0 +#define DC__FMT_SRC_SEL_PRESENT__0 1 +#define DC__DIG_FEATURES__HDMI_PRESENT 1 +#define DC__DIG_FEATURES__HDMI_PRESENT__1 1 +#define DC__DIG_FEATURES__DP_PRESENT 1 +#define DC__DIG_FEATURES__DP_PRESENT__1 1 +#define DC__DIG_FEATURES__DP_MST_PRESENT 1 +#define DC__DIG_FEATURES__DP_MST_PRESENT__1 1 +#define DC__DIG_LP_FEATURES__HDMI_PRESENT 0 +#define DC__DIG_LP_FEATURES__HDMI_PRESENT__0 1 +#define DC__DIG_LP_FEATURES__DP_PRESENT 1 +#define DC__DIG_LP_FEATURES__DP_PRESENT__1 1 +#define DC__DIG_LP_FEATURES__DP_MST_PRESENT 0 +#define DC__DIG_LP_FEATURES__DP_MST_PRESENT__0 1 +#define DC__DIG_RESYNC_FIFO_SIZE 14 +#define DC__DIG_RESYNC_FIFO_SIZE__14 1 +#define DC__DIG_RESYNC_FIFO_SIZE__0_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__1_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__2_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__3_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__4_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__5_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__6_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__7_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__8_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__9_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__10_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__11_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__12_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__13_PRESENT 1 +#define DC__DIG_RESYNC_FIFO_SIZE__MAX 16 +#define DC__DIG_RESYNC_FIFO_SIZE__MAX__16 1 +#define DC__DAC_RESYNC_FIFO_SIZE 12 +#define DC__DAC_RESYNC_FIFO_SIZE__12 1 +#define DC__DAC_RESYNC_FIFO_SIZE__0_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__1_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__2_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__3_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__4_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__5_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__6_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__7_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__8_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__9_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__10_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__11_PRESENT 1 +#define DC__DAC_RESYNC_FIFO_SIZE__MAX 16 +#define DC__DAC_RESYNC_FIFO_SIZE__MAX__16 1 +#define DC__DVO_RESYNC_FIFO_SIZE 12 +#define DC__DVO_RESYNC_FIFO_SIZE__12 1 +#define DC__DVO_RESYNC_FIFO_SIZE__0_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__1_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__2_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__3_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__4_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__5_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__6_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__7_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__8_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__9_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__10_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__11_PRESENT 1 +#define DC__DVO_RESYNC_FIFO_SIZE__MAX 16 +#define DC__DVO_RESYNC_FIFO_SIZE__MAX__16 1 +#define DC__MEM_CDC_PRESENT 1 +#define DC__MEM_CDC_PRESENT__1 1 +#define DC__NUM_HPD 4 +#define DC__NUM_HPD__4 1 +#define DC__NUM_HPD__0_PRESENT 1 +#define DC__NUM_HPD__1_PRESENT 1 +#define DC__NUM_HPD__2_PRESENT 1 +#define DC__NUM_HPD__3_PRESENT 1 +#define DC__NUM_HPD__MAX 6 +#define DC__NUM_HPD__MAX__6 1 +#define DC__NUM_DDC_PAIRS 4 +#define DC__NUM_DDC_PAIRS__4 1 +#define DC__NUM_DDC_PAIRS__0_PRESENT 1 +#define DC__NUM_DDC_PAIRS__1_PRESENT 1 +#define DC__NUM_DDC_PAIRS__2_PRESENT 1 +#define DC__NUM_DDC_PAIRS__3_PRESENT 1 +#define DC__NUM_DDC_PAIRS__MAX 6 +#define DC__NUM_DDC_PAIRS__MAX__6 1 +#define DC__NUM_AUDIO_PLL 0 +#define DC__NUM_AUDIO_PLL__0 1 +#define DC__NUM_AUDIO_PLL__MAX 2 +#define DC__NUM_AUDIO_PLL__MAX__2 1 +#define DC__NUM_PIXEL_PLL 1 +#define DC__NUM_PIXEL_PLL__1 1 +#define DC__NUM_PIXEL_PLL__0_PRESENT 1 +#define DC__NUM_PIXEL_PLL__MAX 4 +#define DC__NUM_PIXEL_PLL__MAX__4 1 +#define DC__NUM_CASCADED_PLL 0 +#define DC__NUM_CASCADED_PLL__0 1 +#define DC__NUM_CASCADED_PLL__MAX 3 +#define DC__NUM_CASCADED_PLL__MAX__3 1 +#define DC__PIXCLK_FROM_PHYPLL 1 +#define DC__PIXCLK_FROM_PHYPLL__1 1 +#define DC__NB_STUTTER_MODE_PRESENT 0 +#define DC__NB_STUTTER_MODE_PRESENT__0 1 +#define DC__I2S0_AND_SPDIF0_PRESENT 0 +#define DC__I2S0_AND_SPDIF0_PRESENT__0 1 +#define DC__I2S1_PRESENT 0 +#define DC__I2S1_PRESENT__0 1 +#define DC__SPDIF1_PRESENT 0 +#define DC__SPDIF1_PRESENT__0 1 +#define DC__DSI_PRESENT 0 +#define DC__DSI_PRESENT__0 1 +#define DC__DACA_PRESENT 0 +#define DC__DACA_PRESENT__0 1 +#define DC__DACB_PRESENT 0 +#define DC__DACB_PRESENT__0 1 +#define DC__NUM_PIPES 4 +#define DC__NUM_PIPES__4 1 +#define DC__NUM_PIPES__0_PRESENT 1 +#define DC__NUM_PIPES__1_PRESENT 1 +#define DC__NUM_PIPES__2_PRESENT 1 +#define DC__NUM_PIPES__3_PRESENT 1 +#define DC__NUM_PIPES__MAX 6 +#define DC__NUM_PIPES__MAX__6 1 +#define DC__NUM_DIG_LP 0 +#define DC__NUM_DIG_LP__0 1 +#define DC__NUM_DIG_LP__MAX 2 +#define DC__NUM_DIG_LP__MAX__2 1 +#define DC__DPDEBUG_PRESENT 0 +#define DC__DPDEBUG_PRESENT__0 1 +#define DC__DISPLAY_WB_PRESENT 1 +#define DC__DISPLAY_WB_PRESENT__1 1 +#define DC__NUM_CWB 0 +#define DC__NUM_CWB__0 1 +#define DC__NUM_CWB__MAX 2 +#define DC__NUM_CWB__MAX__2 1 +#define DC__MVP_PRESENT 0 +#define DC__MVP_PRESENT__0 1 +#define DC__DVO_PRESENT 0 +#define DC__DVO_PRESENT__0 1 +#define DC__ABM_PRESENT 0 +#define DC__ABM_PRESENT__0 1 +#define DC__BPHYC_PLL_PRESENT 0 +#define DC__BPHYC_PLL_PRESENT__0 1 +#define DC__BPHYC_UNIPHY_PRESENT 0 +#define DC__BPHYC_UNIPHY_PRESENT__0 1 +#define DC__PHY_BROADCAST_PRESENT 0 +#define DC__PHY_BROADCAST_PRESENT__0 1 +#define DC__NUM_OF_DCRX_SD 0 +#define DC__NUM_OF_DCRX_SD__0 1 +#define DC__DVO_17BIT_MAPPING 0 +#define DC__DVO_17BIT_MAPPING__0 1 +#define DC__AVSYNC_PRESENT 0 +#define DC__AVSYNC_PRESENT__0 1 +#define DC__NUM_OF_DCRX_PORTS 0 +#define DC__NUM_OF_DCRX_PORTS__0 1 +#define DC__NUM_OF_DCRX_PORTS__MAX 1 +#define DC__NUM_OF_DCRX_PORTS__MAX__1 1 +#define DC__NUM_PHY 4 +#define DC__NUM_PHY__4 1 +#define DC__NUM_PHY__0_PRESENT 1 +#define DC__NUM_PHY__1_PRESENT 1 +#define DC__NUM_PHY__2_PRESENT 1 +#define DC__NUM_PHY__3_PRESENT 1 +#define DC__NUM_PHY__MAX 7 +#define DC__NUM_PHY__MAX__7 1 +#define DC__NUM_PHY_LP 0 +#define DC__NUM_PHY_LP__0 1 +#define DC__NUM_PHY_LP__MAX 2 +#define DC__NUM_PHY_LP__MAX__2 1 +#define DC__SYNC_CELL vid_sync_gf14lpp +#define DC__SYNC_CELL__VID_SYNC_GF14LPP 1 +#define DC__USE_NEW_VSS 1 +#define DC__USE_NEW_VSS__1 1 +#define DC__SYNC_CELL_DISPCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_DISPCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_DVOCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_DVOCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_PIXCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_PIXCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_SYMCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_SYMCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_DPPCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_DPPCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_DPREFCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_DPREFCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_REFCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_REFCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_PCIE_REFCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_PCIE_REFCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_MVPCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_MVPCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_SCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_SCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_DCEFCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_DCEFCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_AMCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_AMCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_DSICLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_DSICLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_BYTECLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_BYTECLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_ESCCLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_ESCCLK_NUM_LATCHES__6 1 +#define DC__SYNC_CELL_DB_CLK_NUM_LATCHES 6 +#define DC__SYNC_CELL_DB_CLK_NUM_LATCHES__6 1 +#define UNIPHYA_PRESENT 1 +#define UNIPHYA_PRESENT__1 1 +#define DC__UNIPHYA_PRESENT 1 +#define DC__UNIPHYA_PRESENT__1 1 +#define UNIPHYB_PRESENT 1 +#define UNIPHYB_PRESENT__1 1 +#define DC__UNIPHYB_PRESENT 1 +#define DC__UNIPHYB_PRESENT__1 1 +#define UNIPHYC_PRESENT 1 +#define UNIPHYC_PRESENT__1 1 +#define DC__UNIPHYC_PRESENT 1 +#define DC__UNIPHYC_PRESENT__1 1 +#define UNIPHYD_PRESENT 1 +#define UNIPHYD_PRESENT__1 1 +#define DC__UNIPHYD_PRESENT 1 +#define DC__UNIPHYD_PRESENT__1 1 +#define UNIPHYE_PRESENT 0 +#define UNIPHYE_PRESENT__0 1 +#define DC__UNIPHYE_PRESENT 0 +#define DC__UNIPHYE_PRESENT__0 1 +#define UNIPHYF_PRESENT 0 +#define UNIPHYF_PRESENT__0 1 +#define DC__UNIPHYF_PRESENT 0 +#define DC__UNIPHYF_PRESENT__0 1 +#define UNIPHYG_PRESENT 0 +#define UNIPHYG_PRESENT__0 1 +#define DC__UNIPHYG_PRESENT 0 +#define DC__UNIPHYG_PRESENT__0 1 +#define DC__TMDS_LINK tmds_link_dual +#define DC__TMDS_LINK__TMDS_LINK_DUAL 1 +#define DC__WBSCL_PIXBW 8 +#define DC__WBSCL_PIXBW__8 1 +#define DC__DWB_CSC_PRESENT 0 +#define DC__DWB_CSC_PRESENT__0 1 +#define DC__DWB_LUMA_SCL_PRESENT 0 +#define DC__DWB_LUMA_SCL_PRESENT__0 1 +#define DC__DENTIST_INTERFACE_PRESENT 1 +#define DC__DENTIST_INTERFACE_PRESENT__1 1 +#define DC__GENERICA_PRESENT 1 +#define DC__GENERICA_PRESENT__1 1 +#define DC__GENERICB_PRESENT 1 +#define DC__GENERICB_PRESENT__1 1 +#define DC__GENERICC_PRESENT 0 +#define DC__GENERICC_PRESENT__0 1 +#define DC__GENERICD_PRESENT 0 +#define DC__GENERICD_PRESENT__0 1 +#define DC__GENERICE_PRESENT 0 +#define DC__GENERICE_PRESENT__0 1 +#define DC__GENERICF_PRESENT 0 +#define DC__GENERICF_PRESENT__0 1 +#define DC__GENERICG_PRESENT 0 +#define DC__GENERICG_PRESENT__0 1 +#define DC__UNIPHY_VOLTAGE_MODE 1 +#define DC__UNIPHY_VOLTAGE_MODE__1 1 +#define DC__BLON_TYPE dedicated +#define DC__BLON_TYPE__DEDICATED 1 +#define DC__UNIPHY_STAGGER_CH_PRESENT 1 +#define DC__UNIPHY_STAGGER_CH_PRESENT__1 1 +#define DC__XDMA_PRESENT 0 +#define DC__XDMA_PRESENT__0 1 +#define XDMA__PRESENT 0 +#define XDMA__PRESENT__0 1 +#define DC__DP_MEM_PG 0 +#define DC__DP_MEM_PG__0 1 +#define DP__MEM_PG 0 +#define DP__MEM_PG__0 1 +#define DC__AFMT_MEM_PG 0 +#define DC__AFMT_MEM_PG__0 1 +#define AFMT__MEM_PG 0 +#define AFMT__MEM_PG__0 1 +#define DC__HDMI_MEM_PG 0 +#define DC__HDMI_MEM_PG__0 1 +#define HDMI__MEM_PG 0 +#define HDMI__MEM_PG__0 1 +#define DC__I2C_MEM_PG 0 +#define DC__I2C_MEM_PG__0 1 +#define I2C__MEM_PG 0 +#define I2C__MEM_PG__0 1 +#define DC__DSCL_MEM_PG 0 +#define DC__DSCL_MEM_PG__0 1 +#define DSCL__MEM_PG 0 +#define DSCL__MEM_PG__0 1 +#define DC__CM_MEM_PG 0 +#define DC__CM_MEM_PG__0 1 +#define CM__MEM_PG 0 +#define CM__MEM_PG__0 1 +#define DC__OBUF_MEM_PG 0 +#define DC__OBUF_MEM_PG__0 1 +#define OBUF__MEM_PG 0 +#define OBUF__MEM_PG__0 1 +#define DC__WBIF_MEM_PG 1 +#define DC__WBIF_MEM_PG__1 1 +#define WBIF__MEM_PG 1 +#define WBIF__MEM_PG__1 1 +#define DC__VGA_MEM_PG 0 +#define DC__VGA_MEM_PG__0 1 +#define VGA__MEM_PG 0 +#define VGA__MEM_PG__0 1 +#define DC__FMT_MEM_PG 0 +#define DC__FMT_MEM_PG__0 1 +#define FMT__MEM_PG 0 +#define FMT__MEM_PG__0 1 +#define DC__ODM_MEM_PG 0 +#define DC__ODM_MEM_PG__0 1 +#define ODM__MEM_PG 0 +#define ODM__MEM_PG__0 1 +#define DC__DSI_MEM_PG 0 +#define DC__DSI_MEM_PG__0 1 +#define DSI__MEM_PG 0 +#define DSI__MEM_PG__0 1 +#define DC__AZ_MEM_PG 1 +#define DC__AZ_MEM_PG__1 1 +#define AZ__MEM_PG 1 +#define AZ__MEM_PG__1 1 +#define DC__WBSCL_MEM1P1024X64QS_MEM_PG 1 +#define DC__WBSCL_MEM1P1024X64QS_MEM_PG__1 1 +#define WBSCL_MEM1P1024X64QS__MEM_PG 1 +#define WBSCL_MEM1P1024X64QS__MEM_PG__1 1 +#define DC__WBSCL_MEM1P528X64QS_MEM_PG 1 +#define DC__WBSCL_MEM1P528X64QS_MEM_PG__1 1 +#define WBSCL_MEM1P528X64QS__MEM_PG 1 +#define WBSCL_MEM1P528X64QS__MEM_PG__1 1 +#define DC__DMCU_MEM1P1024X32BQS_MEM_PG 1 +#define DC__DMCU_MEM1P1024X32BQS_MEM_PG__1 1 +#define DMCU_MEM1P1024X32BQS__MEM_PG 1 +#define DMCU_MEM1P1024X32BQS__MEM_PG__1 1 +#define DC__HUBBUB_SDP_TAG_INT_MEM_PG 0 +#define DC__HUBBUB_SDP_TAG_INT_MEM_PG__0 1 +#define HUBBUB_SDP_TAG_INT__MEM_PG 0 +#define HUBBUB_SDP_TAG_INT__MEM_PG__0 1 +#define DC__HUBBUB_SDP_TAG_EXT_MEM_PG 0 +#define DC__HUBBUB_SDP_TAG_EXT_MEM_PG__0 1 +#define HUBBUB_SDP_TAG_EXT__MEM_PG 0 +#define HUBBUB_SDP_TAG_EXT__MEM_PG__0 1 +#define DC__HUBBUB_RET_ZERO_MEM_PG 0 +#define DC__HUBBUB_RET_ZERO_MEM_PG__0 1 +#define HUBBUB_RET_ZERO__MEM_PG 0 +#define HUBBUB_RET_ZERO__MEM_PG__0 1 +#define DC__HUBBUB_RET_ROB_MEM_PG 0 +#define DC__HUBBUB_RET_ROB_MEM_PG__0 1 +#define HUBBUB_RET_ROB__MEM_PG 0 +#define HUBBUB_RET_ROB__MEM_PG__0 1 +#define DC__HUBPRET_CUR_ROB_MEM_PG 0 +#define DC__HUBPRET_CUR_ROB_MEM_PG__0 1 +#define HUBPRET_CUR_ROB__MEM_PG 0 +#define HUBPRET_CUR_ROB__MEM_PG__0 1 +#define DC__HUBPRET_CUR_CDC_MEM_PG 0 +#define DC__HUBPRET_CUR_CDC_MEM_PG__0 1 +#define HUBPRET_CUR_CDC__MEM_PG 0 +#define HUBPRET_CUR_CDC__MEM_PG__0 1 +#define DC__HUBPREQ_MPTE_MEM_PG 0 +#define DC__HUBPREQ_MPTE_MEM_PG__0 1 +#define HUBPREQ_MPTE__MEM_PG 0 +#define HUBPREQ_MPTE__MEM_PG__0 1 +#define DC__HUBPREQ_META_MEM_PG 0 +#define DC__HUBPREQ_META_MEM_PG__0 1 +#define HUBPREQ_META__MEM_PG 0 +#define HUBPREQ_META__MEM_PG__0 1 +#define DC__HUBPREQ_DPTE_MEM_PG 0 +#define DC__HUBPREQ_DPTE_MEM_PG__0 1 +#define HUBPREQ_DPTE__MEM_PG 0 +#define HUBPREQ_DPTE__MEM_PG__0 1 +#define DC__HUBPRET_DET_MEM_PG 0 +#define DC__HUBPRET_DET_MEM_PG__0 1 +#define HUBPRET_DET__MEM_PG 0 +#define HUBPRET_DET__MEM_PG__0 1 +#define DC__HUBPRET_PIX_CDC_MEM_PG 0 +#define DC__HUBPRET_PIX_CDC_MEM_PG__0 1 +#define HUBPRET_PIX_CDC__MEM_PG 0 +#define HUBPRET_PIX_CDC__MEM_PG__0 1 +#define DC__TOP_BLKS__DCCG 1 +#define DC__TOP_BLKS__DCHUBBUB 1 +#define DC__TOP_BLKS__DCHUBP 1 +#define DC__TOP_BLKS__HDA 1 +#define DC__TOP_BLKS__DIO 1 +#define DC__TOP_BLKS__DCIO 1 +#define DC__TOP_BLKS__DMU 1 +#define DC__TOP_BLKS__DPP 1 +#define DC__TOP_BLKS__MPC 1 +#define DC__TOP_BLKS__OPP 1 +#define DC__TOP_BLKS__OPTC 1 +#define DC__TOP_BLKS__MMHUBBUB 1 +#define DC__TOP_BLKS__WB 1 +#define DC__TOP_BLKS__MAX 13 +#define DC__TOP_BLKS__MAX__13 1 +#define DC__DCHUBP_DPP_SF_PIXEL_CREDITS 9 +#define DC__DCHUBP_DPP_SF_PIXEL_CREDITS__9 1 +#define DC__DPP_MPC_SF_PIXEL_CREDITS 9 +#define DC__DPP_MPC_SF_PIXEL_CREDITS__9 1 +#define DC__MPC_OPP_SF_PIXEL_CREDITS 8 +#define DC__MPC_OPP_SF_PIXEL_CREDITS__8 1 +#define DC__OPP_OPTC_SF_PIXEL_CREDITS 8 +#define DC__OPP_OPTC_SF_PIXEL_CREDITS__8 1 +#define DC__SFR_SFT_ROUND_TRIP_DELAY 5 +#define DC__SFR_SFT_ROUND_TRIP_DELAY__5 1 +#define DC__REPEATER_PROJECT_MAX 8 +#define DC__REPEATER_PROJECT_MAX__8 1 +#define DC__SURFACE_422_CAPABLE 0 +#define DC__SURFACE_422_CAPABLE__0 1 +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h new file mode 100644 index 000000000000..b1ad3553f900 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h @@ -0,0 +1,111 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DISPLAY_MODE_ENUMS_H__ +#define __DISPLAY_MODE_ENUMS_H__ + +enum output_encoder_class { + dm_dp = 0, dm_hdmi = 1, dm_wb = 2, dm_edp +}; +enum output_format_class { + dm_444 = 0, dm_420 = 1, dm_n422, dm_s422 +}; +enum source_format_class { + dm_444_16 = 0, + dm_444_32 = 1, + dm_444_64 = 2, + dm_420_8 = 3, + dm_420_10 = 4, + dm_422_8 = 5, + dm_422_10 = 6, + dm_444_8 = 7, + dm_mono_8, + dm_mono_16 +}; +enum output_bpc_class { + dm_out_6 = 0, dm_out_8 = 1, dm_out_10 = 2, dm_out_12 = 3, dm_out_16 = 4 +}; +enum scan_direction_class { + dm_horz = 0, dm_vert = 1 +}; +enum dm_swizzle_mode { + dm_sw_linear = 0, + dm_sw_256b_s = 1, + dm_sw_256b_d = 2, + dm_sw_SPARE_0 = 3, + dm_sw_SPARE_1 = 4, + dm_sw_4kb_s = 5, + dm_sw_4kb_d = 6, + dm_sw_SPARE_2 = 7, + dm_sw_SPARE_3 = 8, + dm_sw_64kb_s = 9, + dm_sw_64kb_d = 10, + dm_sw_SPARE_4 = 11, + dm_sw_SPARE_5 = 12, + dm_sw_var_s = 13, + dm_sw_var_d = 14, + dm_sw_SPARE_6 = 15, + dm_sw_SPARE_7 = 16, + dm_sw_64kb_s_t = 17, + dm_sw_64kb_d_t = 18, + dm_sw_SPARE_10 = 19, + dm_sw_SPARE_11 = 20, + dm_sw_4kb_s_x = 21, + dm_sw_4kb_d_x = 22, + dm_sw_SPARE_12 = 23, + dm_sw_SPARE_13 = 24, + dm_sw_64kb_s_x = 25, + dm_sw_64kb_d_x = 26, + dm_sw_SPARE_14 = 27, + dm_sw_SPARE_15 = 28, + dm_sw_var_s_x = 29, + dm_sw_var_d_x = 30, + dm_sw_64kb_r_x, + dm_sw_gfx7_2d_thin_lvp, + dm_sw_gfx7_2d_thin_gl +}; +enum lb_depth { + dm_lb_10 = 0, dm_lb_8 = 1, dm_lb_6 = 2, dm_lb_12 = 3, dm_lb_16 +}; +enum voltage_state { + dm_vmin = 0, dm_vmid = 1, dm_vnom = 2, dm_vmax = 3 +}; +enum source_macro_tile_size { + dm_4k_tile = 0, dm_64k_tile = 1, dm_256k_tile = 2 +}; +enum cursor_bpp { + dm_cur_2bit = 0, dm_cur_32bit = 1, dm_cur_64bit = 2 +}; +enum clock_change_support { + dm_dram_clock_change_uninitialized = 0, + dm_dram_clock_change_vactive, + dm_dram_clock_change_vblank, + dm_dram_clock_change_unsupported +}; + +enum output_standard { + dm_std_uninitialized = 0, dm_std_cvtr2, dm_std_cvt +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c b/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c new file mode 100644 index 000000000000..4c31fa54af39 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c @@ -0,0 +1,138 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "display_mode_lib.h" +#include "dc_features.h" + +static void set_soc_bounding_box(struct _vcs_dpi_soc_bounding_box_st *soc, enum dml_project project) +{ + if (project == DML_PROJECT_RAVEN1) { + soc->sr_exit_time_us = 9.0; + soc->sr_enter_plus_exit_time_us = 11.0; + soc->urgent_latency_us = 4.0; + soc->writeback_latency_us = 12.0; + soc->ideal_dram_bw_after_urgent_percent = 80.0; + soc->max_request_size_bytes = 256; + + soc->vmin.dcfclk_mhz = 300.0; + soc->vmin.dispclk_mhz = 608.0; + soc->vmin.dppclk_mhz = 435.0; + soc->vmin.dram_bw_per_chan_gbps = 12.8; + soc->vmin.phyclk_mhz = 540.0; + soc->vmin.socclk_mhz = 208.0; + + soc->vmid.dcfclk_mhz = 600.0; + soc->vmid.dispclk_mhz = 661.0; + soc->vmid.dppclk_mhz = 661.0; + soc->vmid.dram_bw_per_chan_gbps = 12.8; + soc->vmid.phyclk_mhz = 540.0; + soc->vmid.socclk_mhz = 208.0; + + soc->vnom.dcfclk_mhz = 600.0; + soc->vnom.dispclk_mhz = 661.0; + soc->vnom.dppclk_mhz = 661.0; + soc->vnom.dram_bw_per_chan_gbps = 38.4; + soc->vnom.phyclk_mhz = 810; + soc->vnom.socclk_mhz = 208.0; + + soc->vmax.dcfclk_mhz = 600.0; + soc->vmax.dispclk_mhz = 1086.0; + soc->vmax.dppclk_mhz = 661.0; + soc->vmax.dram_bw_per_chan_gbps = 38.4; + soc->vmax.phyclk_mhz = 810.0; + soc->vmax.socclk_mhz = 208.0; + + soc->downspread_percent = 0.5; + soc->dram_page_open_time_ns = 50.0; + soc->dram_rw_turnaround_time_ns = 17.5; + soc->dram_return_buffer_per_channel_bytes = 8192; + soc->round_trip_ping_latency_dcfclk_cycles = 128; + soc->urgent_out_of_order_return_per_channel_bytes = 256; + soc->channel_interleave_bytes = 256; + soc->num_banks = 8; + soc->num_chans = 2; + soc->vmm_page_size_bytes = 4096; + soc->dram_clock_change_latency_us = 17.0; + soc->writeback_dram_clock_change_latency_us = 23.0; + soc->return_bus_width_bytes = 64; + } else { + BREAK_TO_DEBUGGER(); /* Invalid Project Specified */ + } +} + +static void set_ip_params(struct _vcs_dpi_ip_params_st *ip, enum dml_project project) +{ + if (project == DML_PROJECT_RAVEN1) { + ip->rob_buffer_size_kbytes = 64; + ip->det_buffer_size_kbytes = 164; + ip->dpte_buffer_size_in_pte_reqs = 42; + ip->dpp_output_buffer_pixels = 2560; + ip->opp_output_buffer_lines = 1; + ip->pixel_chunk_size_kbytes = 8; + ip->pte_enable = 1; + ip->pte_chunk_size_kbytes = 2; + ip->meta_chunk_size_kbytes = 2; + ip->writeback_chunk_size_kbytes = 2; + ip->line_buffer_size_bits = 589824; + ip->max_line_buffer_lines = 12; + ip->IsLineBufferBppFixed = 0; + ip->LineBufferFixedBpp = -1; + ip->writeback_luma_buffer_size_kbytes = 12; + ip->writeback_chroma_buffer_size_kbytes = 8; + ip->max_num_dpp = 4; + ip->max_num_wb = 2; + ip->max_dchub_pscl_bw_pix_per_clk = 4; + ip->max_pscl_lb_bw_pix_per_clk = 2; + ip->max_lb_vscl_bw_pix_per_clk = 4; + ip->max_vscl_hscl_bw_pix_per_clk = 4; + ip->max_hscl_ratio = 4; + ip->max_vscl_ratio = 4; + ip->hscl_mults = 4; + ip->vscl_mults = 4; + ip->max_hscl_taps = 8; + ip->max_vscl_taps = 8; + ip->dispclk_ramp_margin_percent = 1; + ip->underscan_factor = 1.10; + ip->min_vblank_lines = 14; + ip->dppclk_delay_subtotal = 90; + ip->dispclk_delay_subtotal = 42; + ip->dcfclk_cstate_latency = 10; + ip->max_inter_dcn_tile_repeaters = 8; + ip->can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one = 0; + ip->bug_forcing_LC_req_same_size_fixed = 0; + } else { + BREAK_TO_DEBUGGER(); /* Invalid Project Specified */ + } +} + +void dml_init_instance(struct display_mode_lib *lib, enum dml_project project) +{ + if (lib->project != project) { + set_soc_bounding_box(&lib->soc, project); + set_ip_params(&lib->ip, project); + lib->project = project; + } +} + diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.h new file mode 100644 index 000000000000..26f4f2a3d90d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.h @@ -0,0 +1,50 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DISPLAY_MODE_LIB_H__ +#define __DISPLAY_MODE_LIB_H__ + + +#include "dml_common_defs.h" +#include "soc_bounding_box.h" +#include "display_mode_vba.h" +#include "display_rq_dlg_calc.h" +#include "dml1_display_rq_dlg_calc.h" + +enum dml_project { + DML_PROJECT_UNDEFINED, + DML_PROJECT_RAVEN1 +}; + +struct display_mode_lib { + struct _vcs_dpi_ip_params_st ip; + struct _vcs_dpi_soc_bounding_box_st soc; + enum dml_project project; + struct vba_vars_st vba; + struct dal_logger *logger; +}; + +void dml_init_instance(struct display_mode_lib *lib, enum dml_project project); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h new file mode 100644 index 000000000000..baf182177736 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h @@ -0,0 +1,557 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DISPLAY_MODE_STRUCTS_H__ +#define __DISPLAY_MODE_STRUCTS_H__ + +typedef struct _vcs_dpi_voltage_scaling_st voltage_scaling_st; +typedef struct _vcs_dpi_soc_bounding_box_st soc_bounding_box_st; +typedef struct _vcs_dpi_ip_params_st ip_params_st; +typedef struct _vcs_dpi_display_pipe_source_params_st display_pipe_source_params_st; +typedef struct _vcs_dpi_display_output_params_st display_output_params_st; +typedef struct _vcs_dpi_display_bandwidth_st display_bandwidth_st; +typedef struct _vcs_dpi_scaler_ratio_depth_st scaler_ratio_depth_st; +typedef struct _vcs_dpi_scaler_taps_st scaler_taps_st; +typedef struct _vcs_dpi_display_pipe_dest_params_st display_pipe_dest_params_st; +typedef struct _vcs_dpi_display_pipe_params_st display_pipe_params_st; +typedef struct _vcs_dpi_display_clocks_and_cfg_st display_clocks_and_cfg_st; +typedef struct _vcs_dpi_display_e2e_pipe_params_st display_e2e_pipe_params_st; +typedef struct _vcs_dpi_dchub_buffer_sizing_st dchub_buffer_sizing_st; +typedef struct _vcs_dpi_watermarks_perf_st watermarks_perf_st; +typedef struct _vcs_dpi_cstate_pstate_watermarks_st cstate_pstate_watermarks_st; +typedef struct _vcs_dpi_wm_calc_pipe_params_st wm_calc_pipe_params_st; +typedef struct _vcs_dpi_vratio_pre_st vratio_pre_st; +typedef struct _vcs_dpi_display_data_rq_misc_params_st display_data_rq_misc_params_st; +typedef struct _vcs_dpi_display_data_rq_sizing_params_st display_data_rq_sizing_params_st; +typedef struct _vcs_dpi_display_data_rq_dlg_params_st display_data_rq_dlg_params_st; +typedef struct _vcs_dpi_display_cur_rq_dlg_params_st display_cur_rq_dlg_params_st; +typedef struct _vcs_dpi_display_rq_dlg_params_st display_rq_dlg_params_st; +typedef struct _vcs_dpi_display_rq_sizing_params_st display_rq_sizing_params_st; +typedef struct _vcs_dpi_display_rq_misc_params_st display_rq_misc_params_st; +typedef struct _vcs_dpi_display_rq_params_st display_rq_params_st; +typedef struct _vcs_dpi_display_dlg_regs_st display_dlg_regs_st; +typedef struct _vcs_dpi_display_ttu_regs_st display_ttu_regs_st; +typedef struct _vcs_dpi_display_data_rq_regs_st display_data_rq_regs_st; +typedef struct _vcs_dpi_display_rq_regs_st display_rq_regs_st; +typedef struct _vcs_dpi_display_dlg_sys_params_st display_dlg_sys_params_st; +typedef struct _vcs_dpi_display_dlg_prefetch_param_st display_dlg_prefetch_param_st; +typedef struct _vcs_dpi_display_pipe_clock_st display_pipe_clock_st; +typedef struct _vcs_dpi_display_arb_params_st display_arb_params_st; + +struct _vcs_dpi_voltage_scaling_st { + int state; + double dscclk_mhz; + double dcfclk_mhz; + double socclk_mhz; + double dram_speed_mhz; + double fabricclk_mhz; + double dispclk_mhz; + double dram_bw_per_chan_gbps; + double phyclk_mhz; + double dppclk_mhz; +}; + +struct _vcs_dpi_soc_bounding_box_st { + double sr_exit_time_us; + double sr_enter_plus_exit_time_us; + double urgent_latency_us; + double writeback_latency_us; + double ideal_dram_bw_after_urgent_percent; + unsigned int max_request_size_bytes; + struct _vcs_dpi_voltage_scaling_st vmin; + struct _vcs_dpi_voltage_scaling_st vmid; + struct _vcs_dpi_voltage_scaling_st vnom; + struct _vcs_dpi_voltage_scaling_st vmax; + double downspread_percent; + double dram_page_open_time_ns; + double dram_rw_turnaround_time_ns; + double dram_return_buffer_per_channel_bytes; + double dram_channel_width_bytes; + double fabric_datapath_to_dcn_data_return_bytes; + double dcn_downspread_percent; + double dispclk_dppclk_vco_speed_mhz; + double dfs_vco_period_ps; + unsigned int round_trip_ping_latency_dcfclk_cycles; + unsigned int urgent_out_of_order_return_per_channel_bytes; + unsigned int channel_interleave_bytes; + unsigned int num_banks; + unsigned int num_chans; + unsigned int vmm_page_size_bytes; + double dram_clock_change_latency_us; + double writeback_dram_clock_change_latency_us; + unsigned int return_bus_width_bytes; + unsigned int voltage_override; + double xfc_bus_transport_time_us; + double xfc_xbuf_latency_tolerance_us; + struct _vcs_dpi_voltage_scaling_st clock_limits[7]; +}; + +struct _vcs_dpi_ip_params_st { + unsigned int max_inter_dcn_tile_repeaters; + unsigned int num_dsc; + unsigned int odm_capable; + unsigned int rob_buffer_size_kbytes; + unsigned int det_buffer_size_kbytes; + unsigned int dpte_buffer_size_in_pte_reqs; + unsigned int pde_proc_buffer_size_64k_reqs; + unsigned int dpp_output_buffer_pixels; + unsigned int opp_output_buffer_lines; + unsigned int pixel_chunk_size_kbytes; + unsigned char pte_enable; + unsigned int pte_chunk_size_kbytes; + unsigned int meta_chunk_size_kbytes; + unsigned int writeback_chunk_size_kbytes; + unsigned int line_buffer_size_bits; + unsigned int max_line_buffer_lines; + unsigned int writeback_luma_buffer_size_kbytes; + unsigned int writeback_chroma_buffer_size_kbytes; + unsigned int writeback_chroma_line_buffer_width_pixels; + unsigned int max_page_table_levels; + unsigned int max_num_dpp; + unsigned int max_num_otg; + unsigned int cursor_chunk_size; + unsigned int cursor_buffer_size; + unsigned int max_num_wb; + unsigned int max_dchub_pscl_bw_pix_per_clk; + unsigned int max_pscl_lb_bw_pix_per_clk; + unsigned int max_lb_vscl_bw_pix_per_clk; + unsigned int max_vscl_hscl_bw_pix_per_clk; + double max_hscl_ratio; + double max_vscl_ratio; + unsigned int hscl_mults; + unsigned int vscl_mults; + unsigned int max_hscl_taps; + unsigned int max_vscl_taps; + unsigned int xfc_supported; + unsigned int ptoi_supported; + unsigned int xfc_fill_constant_bytes; + double dispclk_ramp_margin_percent; + double xfc_fill_bw_overhead_percent; + double underscan_factor; + unsigned int min_vblank_lines; + unsigned int dppclk_delay_subtotal; + unsigned int dispclk_delay_subtotal; + unsigned int dcfclk_cstate_latency; + unsigned int dppclk_delay_scl; + unsigned int dppclk_delay_scl_lb_only; + unsigned int dppclk_delay_cnvc_formatter; + unsigned int dppclk_delay_cnvc_cursor; + unsigned int is_line_buffer_bpp_fixed; + unsigned int line_buffer_fixed_bpp; + unsigned int dcc_supported; + + unsigned int IsLineBufferBppFixed; + unsigned int LineBufferFixedBpp; + unsigned int can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one; + unsigned int bug_forcing_LC_req_same_size_fixed; +}; + +struct _vcs_dpi_display_xfc_params_st { + double xfc_tslv_vready_offset_us; + double xfc_tslv_vupdate_width_us; + double xfc_tslv_vupdate_offset_us; + int xfc_slv_chunk_size_bytes; +}; + +struct _vcs_dpi_display_pipe_source_params_st { + int source_format; + unsigned char dcc; + unsigned int dcc_override; + unsigned int dcc_rate; + unsigned char dcc_use_global; + unsigned char vm; + unsigned char vm_levels_force_en; + unsigned int vm_levels_force; + int source_scan; + int sw_mode; + int macro_tile_size; + unsigned char is_display_sw; + unsigned int viewport_width; + unsigned int viewport_height; + unsigned int viewport_y_y; + unsigned int viewport_y_c; + unsigned int viewport_width_c; + unsigned int viewport_height_c; + unsigned int data_pitch; + unsigned int data_pitch_c; + unsigned int meta_pitch; + unsigned int meta_pitch_c; + unsigned int cur0_src_width; + int cur0_bpp; + unsigned int cur1_src_width; + int cur1_bpp; + int num_cursors; + unsigned char is_hsplit; + unsigned char dynamic_metadata_enable; + unsigned int dynamic_metadata_lines_before_active; + unsigned int dynamic_metadata_xmit_bytes; + unsigned int hsplit_grp; + unsigned char xfc_enable; + unsigned char xfc_slave; + struct _vcs_dpi_display_xfc_params_st xfc_params; +}; +struct writeback_st { + int wb_src_height; + int wb_dst_width; + int wb_dst_height; + int wb_pixel_format; + int wb_htaps_luma; + int wb_vtaps_luma; + int wb_htaps_chroma; + int wb_vtaps_chroma; + int wb_hratio; + int wb_vratio; +}; + +struct _vcs_dpi_display_output_params_st { + int dp_lanes; + int output_bpp; + int dsc_enable; + int wb_enable; + int output_bpc; + int output_type; + int output_format; + int output_standard; + int dsc_slices; + struct writeback_st wb; +}; + +struct _vcs_dpi_display_bandwidth_st { + double total_bw_consumed_gbps; + double guaranteed_urgent_return_bw_gbps; +}; + +struct _vcs_dpi_scaler_ratio_depth_st { + double hscl_ratio; + double vscl_ratio; + double hscl_ratio_c; + double vscl_ratio_c; + double vinit; + double vinit_c; + double vinit_bot; + double vinit_bot_c; + int lb_depth; + int scl_enable; +}; + +struct _vcs_dpi_scaler_taps_st { + unsigned int htaps; + unsigned int vtaps; + unsigned int htaps_c; + unsigned int vtaps_c; +}; + +struct _vcs_dpi_display_pipe_dest_params_st { + unsigned int recout_width; + unsigned int recout_height; + unsigned int full_recout_width; + unsigned int full_recout_height; + unsigned int hblank_start; + unsigned int hblank_end; + unsigned int vblank_start; + unsigned int vblank_end; + unsigned int htotal; + unsigned int vtotal; + unsigned int vactive; + unsigned int hactive; + unsigned int vstartup_start; + unsigned int vupdate_offset; + unsigned int vupdate_width; + unsigned int vready_offset; + unsigned char interlaced; + unsigned char underscan; + double pixel_rate_mhz; + unsigned char synchronized_vblank_all_planes; + unsigned char otg_inst; + unsigned char odm_split_cnt; + unsigned char odm_combine; +}; + +struct _vcs_dpi_display_pipe_params_st { + display_pipe_source_params_st src; + display_pipe_dest_params_st dest; + scaler_ratio_depth_st scale_ratio_depth; + scaler_taps_st scale_taps; +}; + +struct _vcs_dpi_display_clocks_and_cfg_st { + int voltage; + double dppclk_mhz; + double refclk_mhz; + double dispclk_mhz; + double dcfclk_mhz; + double socclk_mhz; +}; + +struct _vcs_dpi_display_e2e_pipe_params_st { + display_pipe_params_st pipe; + display_output_params_st dout; + display_clocks_and_cfg_st clks_cfg; +}; + +struct _vcs_dpi_dchub_buffer_sizing_st { + unsigned int swath_width_y; + unsigned int swath_height_y; + unsigned int swath_height_c; + unsigned int detail_buffer_size_y; +}; + +struct _vcs_dpi_watermarks_perf_st { + double stutter_eff_in_active_region_percent; + double urgent_latency_supported_us; + double non_urgent_latency_supported_us; + double dram_clock_change_margin_us; + double dram_access_eff_percent; +}; + +struct _vcs_dpi_cstate_pstate_watermarks_st { + double cstate_exit_us; + double cstate_enter_plus_exit_us; + double pstate_change_us; +}; + +struct _vcs_dpi_wm_calc_pipe_params_st { + unsigned int num_dpp; + int voltage; + int output_type; + double dcfclk_mhz; + double socclk_mhz; + double dppclk_mhz; + double pixclk_mhz; + unsigned char interlace_en; + unsigned char pte_enable; + unsigned char dcc_enable; + double dcc_rate; + double bytes_per_pixel_c; + double bytes_per_pixel_y; + unsigned int swath_width_y; + unsigned int swath_height_y; + unsigned int swath_height_c; + unsigned int det_buffer_size_y; + double h_ratio; + double v_ratio; + unsigned int h_taps; + unsigned int h_total; + unsigned int v_total; + unsigned int v_active; + unsigned int e2e_index; + double display_pipe_line_delivery_time; + double read_bw; + unsigned int lines_in_det_y; + unsigned int lines_in_det_y_rounded_down_to_swath; + double full_det_buffering_time; + double dcfclk_deepsleep_mhz_per_plane; +}; + +struct _vcs_dpi_vratio_pre_st { + double vratio_pre_l; + double vratio_pre_c; +}; + +struct _vcs_dpi_display_data_rq_misc_params_st { + unsigned int full_swath_bytes; + unsigned int stored_swath_bytes; + unsigned int blk256_height; + unsigned int blk256_width; + unsigned int req_height; + unsigned int req_width; +}; + +struct _vcs_dpi_display_data_rq_sizing_params_st { + unsigned int chunk_bytes; + unsigned int min_chunk_bytes; + unsigned int meta_chunk_bytes; + unsigned int min_meta_chunk_bytes; + unsigned int mpte_group_bytes; + unsigned int dpte_group_bytes; +}; + +struct _vcs_dpi_display_data_rq_dlg_params_st { + unsigned int swath_width_ub; + unsigned int swath_height; + unsigned int req_per_swath_ub; + unsigned int meta_pte_bytes_per_frame_ub; + unsigned int dpte_req_per_row_ub; + unsigned int dpte_groups_per_row_ub; + unsigned int dpte_row_height; + unsigned int dpte_bytes_per_row_ub; + unsigned int meta_chunks_per_row_ub; + unsigned int meta_req_per_row_ub; + unsigned int meta_row_height; + unsigned int meta_bytes_per_row_ub; +}; + +struct _vcs_dpi_display_cur_rq_dlg_params_st { + unsigned char enable; + unsigned int swath_height; + unsigned int req_per_line; +}; + +struct _vcs_dpi_display_rq_dlg_params_st { + display_data_rq_dlg_params_st rq_l; + display_data_rq_dlg_params_st rq_c; + display_cur_rq_dlg_params_st rq_cur0; +}; + +struct _vcs_dpi_display_rq_sizing_params_st { + display_data_rq_sizing_params_st rq_l; + display_data_rq_sizing_params_st rq_c; +}; + +struct _vcs_dpi_display_rq_misc_params_st { + display_data_rq_misc_params_st rq_l; + display_data_rq_misc_params_st rq_c; +}; + +struct _vcs_dpi_display_rq_params_st { + unsigned char yuv420; + unsigned char yuv420_10bpc; + display_rq_misc_params_st misc; + display_rq_sizing_params_st sizing; + display_rq_dlg_params_st dlg; +}; + +struct _vcs_dpi_display_dlg_regs_st { + unsigned int refcyc_h_blank_end; + unsigned int dlg_vblank_end; + unsigned int min_dst_y_next_start; + unsigned int refcyc_per_htotal; + unsigned int refcyc_x_after_scaler; + unsigned int dst_y_after_scaler; + unsigned int dst_y_prefetch; + unsigned int dst_y_per_vm_vblank; + unsigned int dst_y_per_row_vblank; + unsigned int dst_y_per_vm_flip; + unsigned int dst_y_per_row_flip; + unsigned int ref_freq_to_pix_freq; + unsigned int vratio_prefetch; + unsigned int vratio_prefetch_c; + unsigned int refcyc_per_pte_group_vblank_l; + unsigned int refcyc_per_pte_group_vblank_c; + unsigned int refcyc_per_meta_chunk_vblank_l; + unsigned int refcyc_per_meta_chunk_vblank_c; + unsigned int refcyc_per_pte_group_flip_l; + unsigned int refcyc_per_pte_group_flip_c; + unsigned int refcyc_per_meta_chunk_flip_l; + unsigned int refcyc_per_meta_chunk_flip_c; + unsigned int dst_y_per_pte_row_nom_l; + unsigned int dst_y_per_pte_row_nom_c; + unsigned int refcyc_per_pte_group_nom_l; + unsigned int refcyc_per_pte_group_nom_c; + unsigned int dst_y_per_meta_row_nom_l; + unsigned int dst_y_per_meta_row_nom_c; + unsigned int refcyc_per_meta_chunk_nom_l; + unsigned int refcyc_per_meta_chunk_nom_c; + unsigned int refcyc_per_line_delivery_pre_l; + unsigned int refcyc_per_line_delivery_pre_c; + unsigned int refcyc_per_line_delivery_l; + unsigned int refcyc_per_line_delivery_c; + unsigned int chunk_hdl_adjust_cur0; + unsigned int chunk_hdl_adjust_cur1; + unsigned int vready_after_vcount0; + unsigned int dst_y_offset_cur0; + unsigned int dst_y_offset_cur1; + unsigned int xfc_reg_transfer_delay; + unsigned int xfc_reg_precharge_delay; + unsigned int xfc_reg_remote_surface_flip_latency; + unsigned int xfc_reg_prefetch_margin; + unsigned int dst_y_delta_drq_limit; +}; + +struct _vcs_dpi_display_ttu_regs_st { + unsigned int qos_level_low_wm; + unsigned int qos_level_high_wm; + unsigned int min_ttu_vblank; + unsigned int qos_level_flip; + unsigned int refcyc_per_req_delivery_l; + unsigned int refcyc_per_req_delivery_c; + unsigned int refcyc_per_req_delivery_cur0; + unsigned int refcyc_per_req_delivery_cur1; + unsigned int refcyc_per_req_delivery_pre_l; + unsigned int refcyc_per_req_delivery_pre_c; + unsigned int refcyc_per_req_delivery_pre_cur0; + unsigned int refcyc_per_req_delivery_pre_cur1; + unsigned int qos_level_fixed_l; + unsigned int qos_level_fixed_c; + unsigned int qos_level_fixed_cur0; + unsigned int qos_level_fixed_cur1; + unsigned int qos_ramp_disable_l; + unsigned int qos_ramp_disable_c; + unsigned int qos_ramp_disable_cur0; + unsigned int qos_ramp_disable_cur1; +}; + +struct _vcs_dpi_display_data_rq_regs_st { + unsigned int chunk_size; + unsigned int min_chunk_size; + unsigned int meta_chunk_size; + unsigned int min_meta_chunk_size; + unsigned int dpte_group_size; + unsigned int mpte_group_size; + unsigned int swath_height; + unsigned int pte_row_height_linear; +}; + +struct _vcs_dpi_display_rq_regs_st { + display_data_rq_regs_st rq_regs_l; + display_data_rq_regs_st rq_regs_c; + unsigned int drq_expansion_mode; + unsigned int prq_expansion_mode; + unsigned int mrq_expansion_mode; + unsigned int crq_expansion_mode; + unsigned int plane1_base_address; +}; + +struct _vcs_dpi_display_dlg_sys_params_st { + double t_mclk_wm_us; + double t_urg_wm_us; + double t_sr_wm_us; + double t_extra_us; + double mem_trip_us; + double t_srx_delay_us; + double deepsleep_dcfclk_mhz; + double total_flip_bw; + unsigned int total_flip_bytes; +}; + +struct _vcs_dpi_display_dlg_prefetch_param_st { + double prefetch_bw; + unsigned int flip_bytes; +}; + +struct _vcs_dpi_display_pipe_clock_st { + double dcfclk_mhz; + double dispclk_mhz; + double socclk_mhz; + double dscclk_mhz[6]; + double dppclk_mhz[6]; +}; + +struct _vcs_dpi_display_arb_params_st { + int max_req_outstanding; + int min_req_outstanding; + int sat_level_us; +}; + +#endif /*__DISPLAY_MODE_STRUCTS_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.c b/drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.c new file mode 100644 index 000000000000..ea661ee44674 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.c @@ -0,0 +1,6124 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "display_mode_lib.h" +#include "display_mode_vba.h" + +#include "dml_inline_defs.h" + +static const unsigned int NumberOfStates = DC__VOLTAGE_STATES; + +static void fetch_socbb_params(struct display_mode_lib *mode_lib); +static void fetch_ip_params(struct display_mode_lib *mode_lib); +static void fetch_pipe_params(struct display_mode_lib *mode_lib); +static void recalculate_params( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes); +static void recalculate(struct display_mode_lib *mode_lib); +static double adjust_ReturnBW( + struct display_mode_lib *mode_lib, + double ReturnBW, + bool DCCEnabledAnyPlane, + double ReturnBandwidthToDCN); +static void ModeSupportAndSystemConfiguration(struct display_mode_lib *mode_lib); +static void DisplayPipeConfiguration(struct display_mode_lib *mode_lib); +static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation( + struct display_mode_lib *mode_lib); +static unsigned int dscceComputeDelay( + unsigned int bpc, + double bpp, + unsigned int sliceWidth, + unsigned int numSlices, + enum output_format_class pixelFormat); +static unsigned int dscComputeDelay(enum output_format_class pixelFormat); +// Super monster function with some 45 argument +static bool CalculatePrefetchSchedule( + struct display_mode_lib *mode_lib, + double DPPCLK, + double DISPCLK, + double PixelClock, + double DCFClkDeepSleep, + unsigned int DSCDelay, + unsigned int DPPPerPlane, + bool ScalerEnabled, + unsigned int NumberOfCursors, + double DPPCLKDelaySubtotal, + double DPPCLKDelaySCL, + double DPPCLKDelaySCLLBOnly, + double DPPCLKDelayCNVCFormater, + double DPPCLKDelayCNVCCursor, + double DISPCLKDelaySubtotal, + unsigned int ScalerRecoutWidth, + enum output_format_class OutputFormat, + unsigned int VBlank, + unsigned int HTotal, + unsigned int MaxInterDCNTileRepeaters, + unsigned int VStartup, + unsigned int PageTableLevels, + bool VirtualMemoryEnable, + bool DynamicMetadataEnable, + unsigned int DynamicMetadataLinesBeforeActiveRequired, + unsigned int DynamicMetadataTransmittedBytes, + bool DCCEnable, + double UrgentLatency, + double UrgentExtraLatency, + double TCalc, + unsigned int PDEAndMetaPTEBytesFrame, + unsigned int MetaRowByte, + unsigned int PixelPTEBytesPerRow, + double PrefetchSourceLinesY, + unsigned int SwathWidthY, + double BytePerPixelDETY, + double VInitPreFillY, + unsigned int MaxNumSwathY, + double PrefetchSourceLinesC, + double BytePerPixelDETC, + double VInitPreFillC, + unsigned int MaxNumSwathC, + unsigned int SwathHeightY, + unsigned int SwathHeightC, + double TWait, + bool XFCEnabled, + double XFCRemoteSurfaceFlipDelay, + bool InterlaceEnable, + bool ProgressiveToInterlaceUnitInOPP, + double *DSTXAfterScaler, + double *DSTYAfterScaler, + double *DestinationLinesForPrefetch, + double *PrefetchBandwidth, + double *DestinationLinesToRequestVMInVBlank, + double *DestinationLinesToRequestRowInVBlank, + double *VRatioPrefetchY, + double *VRatioPrefetchC, + double *RequiredPrefetchPixDataBW, + unsigned int *VStartupRequiredWhenNotEnoughTimeForDynamicMetadata, + double *Tno_bw, + unsigned int *VUpdateOffsetPix, + unsigned int *VUpdateWidthPix, + unsigned int *VReadyOffsetPix); +static double RoundToDFSGranularityUp(double Clock, double VCOSpeed); +static double RoundToDFSGranularityDown(double Clock, double VCOSpeed); +static double CalculatePrefetchSourceLines( + struct display_mode_lib *mode_lib, + double VRatio, + double vtaps, + bool Interlace, + bool ProgressiveToInterlaceUnitInOPP, + unsigned int SwathHeight, + unsigned int ViewportYStart, + double *VInitPreFill, + unsigned int *MaxNumSwath); +static unsigned int CalculateVMAndRowBytes( + struct display_mode_lib *mode_lib, + bool DCCEnable, + unsigned int BlockHeight256Bytes, + unsigned int BlockWidth256Bytes, + enum source_format_class SourcePixelFormat, + unsigned int SurfaceTiling, + unsigned int BytePerPixel, + enum scan_direction_class ScanDirection, + unsigned int ViewportWidth, + unsigned int ViewportHeight, + unsigned int SwathWidthY, + bool VirtualMemoryEnable, + unsigned int VMMPageSize, + unsigned int PTEBufferSizeInRequests, + unsigned int PDEProcessingBufIn64KBReqs, + unsigned int Pitch, + unsigned int DCCMetaPitch, + unsigned int *MacroTileWidth, + unsigned int *MetaRowByte, + unsigned int *PixelPTEBytesPerRow, + bool *PTEBufferSizeNotExceeded, + unsigned int *dpte_row_height, + unsigned int *meta_row_height); +static double CalculateTWait( + unsigned int PrefetchMode, + double DRAMClockChangeLatency, + double UrgentLatency, + double SREnterPlusExitTime); +static double CalculateRemoteSurfaceFlipDelay( + struct display_mode_lib *mode_lib, + double VRatio, + double SwathWidth, + double Bpp, + double LineTime, + double XFCTSlvVupdateOffset, + double XFCTSlvVupdateWidth, + double XFCTSlvVreadyOffset, + double XFCXBUFLatencyTolerance, + double XFCFillBWOverhead, + double XFCSlvChunkSize, + double XFCBusTransportTime, + double TCalc, + double TWait, + double *SrcActiveDrainRate, + double *TInitXFill, + double *TslvChk); +static double CalculateWriteBackDISPCLK( + enum source_format_class WritebackPixelFormat, + double PixelClock, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackLumaHTaps, + unsigned int WritebackLumaVTaps, + unsigned int WritebackChromaHTaps, + unsigned int WritebackChromaVTaps, + double WritebackDestinationWidth, + unsigned int HTotal, + unsigned int WritebackChromaLineBufferWidth); +static void CalculateActiveRowBandwidth( + bool VirtualMemoryEnable, + enum source_format_class SourcePixelFormat, + double VRatio, + bool DCCEnable, + double LineTime, + unsigned int MetaRowByteLuma, + unsigned int MetaRowByteChroma, + unsigned int meta_row_height_luma, + unsigned int meta_row_height_chroma, + unsigned int PixelPTEBytesPerRowLuma, + unsigned int PixelPTEBytesPerRowChroma, + unsigned int dpte_row_height_luma, + unsigned int dpte_row_height_chroma, + double *meta_row_bw, + double *dpte_row_bw, + double *qual_row_bw); +static void CalculateFlipSchedule( + struct display_mode_lib *mode_lib, + double UrgentExtraLatency, + double UrgentLatency, + unsigned int MaxPageTableLevels, + bool VirtualMemoryEnable, + double BandwidthAvailableForImmediateFlip, + unsigned int TotImmediateFlipBytes, + enum source_format_class SourcePixelFormat, + unsigned int ImmediateFlipBytes, + double LineTime, + double Tno_bw, + double VRatio, + double PDEAndMetaPTEBytesFrame, + unsigned int MetaRowByte, + unsigned int PixelPTEBytesPerRow, + bool DCCEnable, + unsigned int dpte_row_height, + unsigned int meta_row_height, + double qual_row_bw, + double *DestinationLinesToRequestVMInImmediateFlip, + double *DestinationLinesToRequestRowInImmediateFlip, + double *final_flip_bw, + bool *ImmediateFlipSupportedForPipe); +static double CalculateWriteBackDelay( + enum source_format_class WritebackPixelFormat, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackLumaHTaps, + unsigned int WritebackLumaVTaps, + unsigned int WritebackChromaHTaps, + unsigned int WritebackChromaVTaps, + unsigned int WritebackDestinationWidth); +static void PixelClockAdjustmentForProgressiveToInterlaceUnit(struct display_mode_lib *mode_lib); +static unsigned int CursorBppEnumToBits(enum cursor_bpp ebpp); +static void ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_lib); + +void set_prefetch_mode( + struct display_mode_lib *mode_lib, + bool cstate_en, + bool pstate_en, + bool ignore_viewport_pos, + bool immediate_flip_support) +{ + unsigned int prefetch_mode; + + if (cstate_en && pstate_en) + prefetch_mode = 0; + else if (cstate_en) + prefetch_mode = 1; + else + prefetch_mode = 2; + if (prefetch_mode != mode_lib->vba.PrefetchMode + || ignore_viewport_pos != mode_lib->vba.IgnoreViewportPositioning + || immediate_flip_support != mode_lib->vba.ImmediateFlipSupport) { + DTRACE( + " Prefetch mode has changed from %i to %i. Recalculating.", + prefetch_mode, + mode_lib->vba.PrefetchMode); + mode_lib->vba.PrefetchMode = prefetch_mode; + mode_lib->vba.IgnoreViewportPositioning = ignore_viewport_pos; + mode_lib->vba.ImmediateFlipSupport = immediate_flip_support; + recalculate(mode_lib); + } +} + +unsigned int dml_get_voltage_level( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes) +{ + bool need_recalculate = memcmp(&mode_lib->soc, &mode_lib->vba.soc, sizeof(mode_lib->vba.soc)) != 0 + || memcmp(&mode_lib->ip, &mode_lib->vba.ip, sizeof(mode_lib->vba.ip)) != 0 + || num_pipes != mode_lib->vba.cache_num_pipes + || memcmp(pipes, mode_lib->vba.cache_pipes, + sizeof(display_e2e_pipe_params_st) * num_pipes) != 0; + + mode_lib->vba.soc = mode_lib->soc; + mode_lib->vba.ip = mode_lib->ip; + memcpy(mode_lib->vba.cache_pipes, pipes, sizeof(*pipes) * num_pipes); + mode_lib->vba.cache_num_pipes = num_pipes; + + if (need_recalculate && pipes[0].clks_cfg.dppclk_mhz != 0) + recalculate(mode_lib); + else { + fetch_socbb_params(mode_lib); + fetch_ip_params(mode_lib); + fetch_pipe_params(mode_lib); + } + ModeSupportAndSystemConfigurationFull(mode_lib); + + return mode_lib->vba.VoltageLevel; +} + +#define dml_get_attr_func(attr, var) double get_##attr(struct display_mode_lib *mode_lib, const display_e2e_pipe_params_st *pipes, unsigned int num_pipes) \ +{ \ + recalculate_params(mode_lib, pipes, num_pipes); \ + return var; \ +} + +dml_get_attr_func(clk_dcf_deepsleep, mode_lib->vba.DCFClkDeepSleep); +dml_get_attr_func(wm_urgent, mode_lib->vba.UrgentWatermark); +dml_get_attr_func(wm_memory_trip, mode_lib->vba.MemoryTripWatermark); +dml_get_attr_func(wm_writeback_urgent, mode_lib->vba.WritebackUrgentWatermark); +dml_get_attr_func(wm_stutter_exit, mode_lib->vba.StutterExitWatermark); +dml_get_attr_func(wm_stutter_enter_exit, mode_lib->vba.StutterEnterPlusExitWatermark); +dml_get_attr_func(wm_dram_clock_change, mode_lib->vba.DRAMClockChangeWatermark); +dml_get_attr_func(wm_writeback_dram_clock_change, mode_lib->vba.WritebackDRAMClockChangeWatermark); +dml_get_attr_func(wm_xfc_underflow, mode_lib->vba.UrgentWatermark); // xfc_underflow maps to urgent +dml_get_attr_func(stutter_efficiency, mode_lib->vba.StutterEfficiency); +dml_get_attr_func(stutter_efficiency_no_vblank, mode_lib->vba.StutterEfficiencyNotIncludingVBlank); +dml_get_attr_func(urgent_latency, mode_lib->vba.MinUrgentLatencySupportUs); +dml_get_attr_func(urgent_extra_latency, mode_lib->vba.UrgentExtraLatency); +dml_get_attr_func(nonurgent_latency, mode_lib->vba.NonUrgentLatencyTolerance); +dml_get_attr_func( + dram_clock_change_latency, + mode_lib->vba.MinActiveDRAMClockChangeLatencySupported); +dml_get_attr_func(dispclk_calculated, mode_lib->vba.DISPCLK_calculated); +dml_get_attr_func(total_data_read_bw, mode_lib->vba.TotalDataReadBandwidth); +dml_get_attr_func(return_bw, mode_lib->vba.ReturnBW); +dml_get_attr_func(tcalc, mode_lib->vba.TCalc); + +#define dml_get_pipe_attr_func(attr, var) double get_##attr(struct display_mode_lib *mode_lib, const display_e2e_pipe_params_st *pipes, unsigned int num_pipes, unsigned int which_pipe) \ +{\ + unsigned int which_plane; \ + recalculate_params(mode_lib, pipes, num_pipes); \ + which_plane = mode_lib->vba.pipe_plane[which_pipe]; \ + return var[which_plane]; \ +} + +dml_get_pipe_attr_func(dsc_delay, mode_lib->vba.DSCDelay); +dml_get_pipe_attr_func(dppclk_calculated, mode_lib->vba.DPPCLK_calculated); +dml_get_pipe_attr_func(dscclk_calculated, mode_lib->vba.DSCCLK_calculated); +dml_get_pipe_attr_func(min_ttu_vblank, mode_lib->vba.MinTTUVBlank); +dml_get_pipe_attr_func(vratio_prefetch_l, mode_lib->vba.VRatioPrefetchY); +dml_get_pipe_attr_func(vratio_prefetch_c, mode_lib->vba.VRatioPrefetchC); +dml_get_pipe_attr_func(dst_x_after_scaler, mode_lib->vba.DSTXAfterScaler); +dml_get_pipe_attr_func(dst_y_after_scaler, mode_lib->vba.DSTYAfterScaler); +dml_get_pipe_attr_func(dst_y_per_vm_vblank, mode_lib->vba.DestinationLinesToRequestVMInVBlank); +dml_get_pipe_attr_func(dst_y_per_row_vblank, mode_lib->vba.DestinationLinesToRequestRowInVBlank); +dml_get_pipe_attr_func(dst_y_prefetch, mode_lib->vba.DestinationLinesForPrefetch); +dml_get_pipe_attr_func(dst_y_per_vm_flip, mode_lib->vba.DestinationLinesToRequestVMInImmediateFlip); +dml_get_pipe_attr_func( + dst_y_per_row_flip, + mode_lib->vba.DestinationLinesToRequestRowInImmediateFlip); + +dml_get_pipe_attr_func(xfc_transfer_delay, mode_lib->vba.XFCTransferDelay); +dml_get_pipe_attr_func(xfc_precharge_delay, mode_lib->vba.XFCPrechargeDelay); +dml_get_pipe_attr_func(xfc_remote_surface_flip_latency, mode_lib->vba.XFCRemoteSurfaceFlipLatency); +dml_get_pipe_attr_func(xfc_prefetch_margin, mode_lib->vba.XFCPrefetchMargin); + +unsigned int get_vstartup_calculated( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes, + unsigned int which_pipe) +{ + unsigned int which_plane; + + recalculate_params(mode_lib, pipes, num_pipes); + which_plane = mode_lib->vba.pipe_plane[which_pipe]; + return mode_lib->vba.VStartup[which_plane]; +} + +double get_total_immediate_flip_bytes( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes) +{ + recalculate_params(mode_lib, pipes, num_pipes); + return mode_lib->vba.TotImmediateFlipBytes; +} + +double get_total_immediate_flip_bw( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes) +{ + recalculate_params(mode_lib, pipes, num_pipes); + return mode_lib->vba.ImmediateFlipBW; +} + +double get_total_prefetch_bw( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes) +{ + unsigned int k; + double total_prefetch_bw = 0.0; + + recalculate_params(mode_lib, pipes, num_pipes); + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) + total_prefetch_bw += mode_lib->vba.PrefetchBandwidth[k]; + return total_prefetch_bw; +} + +static void fetch_socbb_params(struct display_mode_lib *mode_lib) +{ + soc_bounding_box_st *soc = &mode_lib->vba.soc; + unsigned int i; + + // SOC Bounding Box Parameters + mode_lib->vba.ReturnBusWidth = soc->return_bus_width_bytes; + mode_lib->vba.NumberOfChannels = soc->num_chans; + mode_lib->vba.PercentOfIdealDRAMAndFabricBWReceivedAfterUrgLatency = + soc->ideal_dram_bw_after_urgent_percent; // there's always that one bastard variable that's so long it throws everything out of alignment! + mode_lib->vba.UrgentLatency = soc->urgent_latency_us; + mode_lib->vba.RoundTripPingLatencyCycles = soc->round_trip_ping_latency_dcfclk_cycles; + mode_lib->vba.UrgentOutOfOrderReturnPerChannel = + soc->urgent_out_of_order_return_per_channel_bytes; + mode_lib->vba.WritebackLatency = soc->writeback_latency_us; + mode_lib->vba.SRExitTime = soc->sr_exit_time_us; + mode_lib->vba.SREnterPlusExitTime = soc->sr_enter_plus_exit_time_us; + mode_lib->vba.DRAMClockChangeLatency = soc->dram_clock_change_latency_us; + mode_lib->vba.Downspreading = soc->downspread_percent; + mode_lib->vba.DRAMChannelWidth = soc->dram_channel_width_bytes; // new! + mode_lib->vba.FabricDatapathToDCNDataReturn = soc->fabric_datapath_to_dcn_data_return_bytes; // new! + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading = soc->dcn_downspread_percent; // new + mode_lib->vba.DISPCLKDPPCLKVCOSpeed = soc->dispclk_dppclk_vco_speed_mhz; // new + mode_lib->vba.VMMPageSize = soc->vmm_page_size_bytes; + // Set the voltage scaling clocks as the defaults. Most of these will + // be set to different values by the test + for (i = 0; i < DC__VOLTAGE_STATES; i++) + if (soc->clock_limits[i].state == mode_lib->vba.VoltageLevel) + break; + + mode_lib->vba.DCFCLK = soc->clock_limits[i].dcfclk_mhz; + mode_lib->vba.SOCCLK = soc->clock_limits[i].socclk_mhz; + mode_lib->vba.DRAMSpeed = soc->clock_limits[i].dram_speed_mhz; + mode_lib->vba.FabricClock = soc->clock_limits[i].fabricclk_mhz; + + mode_lib->vba.XFCBusTransportTime = soc->xfc_bus_transport_time_us; + mode_lib->vba.XFCXBUFLatencyTolerance = soc->xfc_xbuf_latency_tolerance_us; + + mode_lib->vba.SupportGFX7CompatibleTilingIn32bppAnd64bpp = false; + mode_lib->vba.MaxHSCLRatio = 4; + mode_lib->vba.MaxVSCLRatio = 4; + mode_lib->vba.MaxNumWriteback = 0; /*TODO*/ + mode_lib->vba.WritebackLumaAndChromaScalingSupported = true; + mode_lib->vba.Cursor64BppSupport = true; + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.DCFCLKPerState[i] = soc->clock_limits[i].dcfclk_mhz; + mode_lib->vba.FabricClockPerState[i] = soc->clock_limits[i].fabricclk_mhz; + mode_lib->vba.SOCCLKPerState[i] = soc->clock_limits[i].socclk_mhz; + mode_lib->vba.PHYCLKPerState[i] = soc->clock_limits[i].phyclk_mhz; + mode_lib->vba.MaxDppclk[i] = soc->clock_limits[i].dppclk_mhz; + mode_lib->vba.MaxDSCCLK[i] = soc->clock_limits[i].dscclk_mhz; + mode_lib->vba.DRAMSpeedPerState[i] = soc->clock_limits[i].dram_speed_mhz; + mode_lib->vba.MaxDispclk[i] = soc->clock_limits[i].dispclk_mhz; + } +} + +static void fetch_ip_params(struct display_mode_lib *mode_lib) +{ + ip_params_st *ip = &mode_lib->vba.ip; + + // IP Parameters + mode_lib->vba.MaxNumDPP = ip->max_num_dpp; + mode_lib->vba.MaxNumOTG = ip->max_num_otg; + mode_lib->vba.CursorChunkSize = ip->cursor_chunk_size; + mode_lib->vba.CursorBufferSize = ip->cursor_buffer_size; + + mode_lib->vba.MaxDCHUBToPSCLThroughput = ip->max_dchub_pscl_bw_pix_per_clk; + mode_lib->vba.MaxPSCLToLBThroughput = ip->max_pscl_lb_bw_pix_per_clk; + mode_lib->vba.ROBBufferSizeInKByte = ip->rob_buffer_size_kbytes; + mode_lib->vba.DETBufferSizeInKByte = ip->det_buffer_size_kbytes; + mode_lib->vba.PixelChunkSizeInKByte = ip->pixel_chunk_size_kbytes; + mode_lib->vba.MetaChunkSize = ip->meta_chunk_size_kbytes; + mode_lib->vba.PTEChunkSize = ip->pte_chunk_size_kbytes; + mode_lib->vba.WritebackChunkSize = ip->writeback_chunk_size_kbytes; + mode_lib->vba.LineBufferSize = ip->line_buffer_size_bits; + mode_lib->vba.MaxLineBufferLines = ip->max_line_buffer_lines; + mode_lib->vba.PTEBufferSizeInRequests = ip->dpte_buffer_size_in_pte_reqs; + mode_lib->vba.DPPOutputBufferPixels = ip->dpp_output_buffer_pixels; + mode_lib->vba.OPPOutputBufferLines = ip->opp_output_buffer_lines; + mode_lib->vba.WritebackInterfaceLumaBufferSize = ip->writeback_luma_buffer_size_kbytes; + mode_lib->vba.WritebackInterfaceChromaBufferSize = ip->writeback_chroma_buffer_size_kbytes; + mode_lib->vba.WritebackChromaLineBufferWidth = + ip->writeback_chroma_line_buffer_width_pixels; + mode_lib->vba.MaxPageTableLevels = ip->max_page_table_levels; + mode_lib->vba.MaxInterDCNTileRepeaters = ip->max_inter_dcn_tile_repeaters; + mode_lib->vba.NumberOfDSC = ip->num_dsc; + mode_lib->vba.ODMCapability = ip->odm_capable; + mode_lib->vba.DISPCLKRampingMargin = ip->dispclk_ramp_margin_percent; + + mode_lib->vba.XFCSupported = ip->xfc_supported; + mode_lib->vba.XFCFillBWOverhead = ip->xfc_fill_bw_overhead_percent; + mode_lib->vba.XFCFillConstant = ip->xfc_fill_constant_bytes; + mode_lib->vba.DPPCLKDelaySubtotal = ip->dppclk_delay_subtotal; + mode_lib->vba.DPPCLKDelaySCL = ip->dppclk_delay_scl; + mode_lib->vba.DPPCLKDelaySCLLBOnly = ip->dppclk_delay_scl_lb_only; + mode_lib->vba.DPPCLKDelayCNVCFormater = ip->dppclk_delay_cnvc_formatter; + mode_lib->vba.DPPCLKDelayCNVCCursor = ip->dppclk_delay_cnvc_cursor; + mode_lib->vba.DISPCLKDelaySubtotal = ip->dispclk_delay_subtotal; + + mode_lib->vba.ProgressiveToInterlaceUnitInOPP = ip->ptoi_supported; + + mode_lib->vba.PDEProcessingBufIn64KBReqs = ip->pde_proc_buffer_size_64k_reqs; +} + +static void fetch_pipe_params(struct display_mode_lib *mode_lib) +{ + display_e2e_pipe_params_st *pipes = mode_lib->vba.cache_pipes; + ip_params_st *ip = &mode_lib->vba.ip; + + unsigned int OTGInstPlane[DC__NUM_DPP__MAX]; + unsigned int j, k; + bool PlaneVisited[DC__NUM_DPP__MAX]; + bool visited[DC__NUM_DPP__MAX]; + + // Convert Pipes to Planes + for (k = 0; k < mode_lib->vba.cache_num_pipes; ++k) + visited[k] = false; + + mode_lib->vba.NumberOfActivePlanes = 0; + for (j = 0; j < mode_lib->vba.cache_num_pipes; ++j) { + display_pipe_source_params_st *src = &pipes[j].pipe.src; + display_pipe_dest_params_st *dst = &pipes[j].pipe.dest; + scaler_ratio_depth_st *scl = &pipes[j].pipe.scale_ratio_depth; + scaler_taps_st *taps = &pipes[j].pipe.scale_taps; + display_output_params_st *dout = &pipes[j].dout; + display_clocks_and_cfg_st *clks = &pipes[j].clks_cfg; + + if (visited[j]) + continue; + visited[j] = true; + + mode_lib->vba.pipe_plane[j] = mode_lib->vba.NumberOfActivePlanes; + + mode_lib->vba.DPPPerPlane[mode_lib->vba.NumberOfActivePlanes] = 1; + mode_lib->vba.SourceScan[mode_lib->vba.NumberOfActivePlanes] = + (enum scan_direction_class) (src->source_scan); + mode_lib->vba.ViewportWidth[mode_lib->vba.NumberOfActivePlanes] = + src->viewport_width; + mode_lib->vba.ViewportHeight[mode_lib->vba.NumberOfActivePlanes] = + src->viewport_height; + mode_lib->vba.ViewportYStartY[mode_lib->vba.NumberOfActivePlanes] = + src->viewport_y_y; + mode_lib->vba.ViewportYStartC[mode_lib->vba.NumberOfActivePlanes] = + src->viewport_y_c; + mode_lib->vba.PitchY[mode_lib->vba.NumberOfActivePlanes] = src->data_pitch; + mode_lib->vba.PitchC[mode_lib->vba.NumberOfActivePlanes] = src->data_pitch_c; + mode_lib->vba.DCCMetaPitchY[mode_lib->vba.NumberOfActivePlanes] = src->meta_pitch; + mode_lib->vba.HRatio[mode_lib->vba.NumberOfActivePlanes] = scl->hscl_ratio; + mode_lib->vba.VRatio[mode_lib->vba.NumberOfActivePlanes] = scl->vscl_ratio; + mode_lib->vba.ScalerEnabled[mode_lib->vba.NumberOfActivePlanes] = scl->scl_enable; + mode_lib->vba.Interlace[mode_lib->vba.NumberOfActivePlanes] = dst->interlaced; + if (mode_lib->vba.Interlace[mode_lib->vba.NumberOfActivePlanes]) + mode_lib->vba.VRatio[mode_lib->vba.NumberOfActivePlanes] *= 2.0; + mode_lib->vba.htaps[mode_lib->vba.NumberOfActivePlanes] = taps->htaps; + mode_lib->vba.vtaps[mode_lib->vba.NumberOfActivePlanes] = taps->vtaps; + mode_lib->vba.HTAPsChroma[mode_lib->vba.NumberOfActivePlanes] = taps->htaps_c; + mode_lib->vba.VTAPsChroma[mode_lib->vba.NumberOfActivePlanes] = taps->vtaps_c; + mode_lib->vba.HTotal[mode_lib->vba.NumberOfActivePlanes] = dst->htotal; + mode_lib->vba.VTotal[mode_lib->vba.NumberOfActivePlanes] = dst->vtotal; + mode_lib->vba.DCCEnable[mode_lib->vba.NumberOfActivePlanes] = + src->dcc_use_global ? + ip->dcc_supported : src->dcc && ip->dcc_supported; + mode_lib->vba.DCCRate[mode_lib->vba.NumberOfActivePlanes] = src->dcc_rate; + mode_lib->vba.SourcePixelFormat[mode_lib->vba.NumberOfActivePlanes] = + (enum source_format_class) (src->source_format); + mode_lib->vba.HActive[mode_lib->vba.NumberOfActivePlanes] = dst->hactive; + mode_lib->vba.VActive[mode_lib->vba.NumberOfActivePlanes] = dst->vactive; + mode_lib->vba.SurfaceTiling[mode_lib->vba.NumberOfActivePlanes] = + (enum dm_swizzle_mode) (src->sw_mode); + mode_lib->vba.ScalerRecoutWidth[mode_lib->vba.NumberOfActivePlanes] = + dst->recout_width; // TODO: or should this be full_recout_width???...maybe only when in hsplit mode? + mode_lib->vba.ODMCombineEnabled[mode_lib->vba.NumberOfActivePlanes] = + dst->odm_combine; + mode_lib->vba.OutputFormat[mode_lib->vba.NumberOfActivePlanes] = + (enum output_format_class) (dout->output_format); + mode_lib->vba.Output[mode_lib->vba.NumberOfActivePlanes] = + (enum output_encoder_class) (dout->output_type); + mode_lib->vba.OutputBpp[mode_lib->vba.NumberOfActivePlanes] = dout->output_bpp; + mode_lib->vba.OutputLinkDPLanes[mode_lib->vba.NumberOfActivePlanes] = + dout->dp_lanes; + mode_lib->vba.DSCEnabled[mode_lib->vba.NumberOfActivePlanes] = dout->dsc_enable; + mode_lib->vba.NumberOfDSCSlices[mode_lib->vba.NumberOfActivePlanes] = + dout->dsc_slices; + mode_lib->vba.DSCInputBitPerComponent[mode_lib->vba.NumberOfActivePlanes] = + dout->output_bpc == 0 ? 12 : dout->output_bpc; + mode_lib->vba.WritebackEnable[mode_lib->vba.NumberOfActivePlanes] = dout->wb_enable; + mode_lib->vba.WritebackSourceHeight[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_src_height; + mode_lib->vba.WritebackDestinationWidth[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_dst_width; + mode_lib->vba.WritebackDestinationHeight[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_dst_height; + mode_lib->vba.WritebackPixelFormat[mode_lib->vba.NumberOfActivePlanes] = + (enum source_format_class) (dout->wb.wb_pixel_format); + mode_lib->vba.WritebackLumaHTaps[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_htaps_luma; + mode_lib->vba.WritebackLumaVTaps[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_vtaps_luma; + mode_lib->vba.WritebackChromaHTaps[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_htaps_chroma; + mode_lib->vba.WritebackChromaVTaps[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_vtaps_chroma; + mode_lib->vba.WritebackHRatio[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_hratio; + mode_lib->vba.WritebackVRatio[mode_lib->vba.NumberOfActivePlanes] = + dout->wb.wb_vratio; + + mode_lib->vba.DynamicMetadataEnable[mode_lib->vba.NumberOfActivePlanes] = + src->dynamic_metadata_enable; + mode_lib->vba.DynamicMetadataLinesBeforeActiveRequired[mode_lib->vba.NumberOfActivePlanes] = + src->dynamic_metadata_lines_before_active; + mode_lib->vba.DynamicMetadataTransmittedBytes[mode_lib->vba.NumberOfActivePlanes] = + src->dynamic_metadata_xmit_bytes; + + mode_lib->vba.XFCEnabled[mode_lib->vba.NumberOfActivePlanes] = src->xfc_enable + && ip->xfc_supported; + mode_lib->vba.XFCSlvChunkSize = src->xfc_params.xfc_slv_chunk_size_bytes; + mode_lib->vba.XFCTSlvVupdateOffset = src->xfc_params.xfc_tslv_vupdate_offset_us; + mode_lib->vba.XFCTSlvVupdateWidth = src->xfc_params.xfc_tslv_vupdate_width_us; + mode_lib->vba.XFCTSlvVreadyOffset = src->xfc_params.xfc_tslv_vready_offset_us; + mode_lib->vba.PixelClock[mode_lib->vba.NumberOfActivePlanes] = dst->pixel_rate_mhz; + mode_lib->vba.DPPCLK[mode_lib->vba.NumberOfActivePlanes] = clks->dppclk_mhz; + if (ip->is_line_buffer_bpp_fixed) + mode_lib->vba.LBBitPerPixel[mode_lib->vba.NumberOfActivePlanes] = + ip->line_buffer_fixed_bpp; + else { + unsigned int lb_depth; + + switch (scl->lb_depth) { + case dm_lb_6: + lb_depth = 18; + break; + case dm_lb_8: + lb_depth = 24; + break; + case dm_lb_10: + lb_depth = 30; + break; + case dm_lb_12: + lb_depth = 36; + break; + case dm_lb_16: + lb_depth = 48; + break; + default: + lb_depth = 36; + } + mode_lib->vba.LBBitPerPixel[mode_lib->vba.NumberOfActivePlanes] = lb_depth; + } + mode_lib->vba.NumberOfCursors[mode_lib->vba.NumberOfActivePlanes] = 0; + // The DML spreadsheet assumes that the two cursors utilize the same amount of bandwidth. We'll + // calculate things a little more accurately + for (k = 0; k < DC__NUM_CURSOR__MAX; ++k) { + switch (k) { + case 0: + mode_lib->vba.CursorBPP[mode_lib->vba.NumberOfActivePlanes][0] = + CursorBppEnumToBits( + (enum cursor_bpp) (src->cur0_bpp)); + mode_lib->vba.CursorWidth[mode_lib->vba.NumberOfActivePlanes][0] = + src->cur0_src_width; + if (src->cur0_src_width > 0) + mode_lib->vba.NumberOfCursors[mode_lib->vba.NumberOfActivePlanes]++; + break; + case 1: + mode_lib->vba.CursorBPP[mode_lib->vba.NumberOfActivePlanes][1] = + CursorBppEnumToBits( + (enum cursor_bpp) (src->cur1_bpp)); + mode_lib->vba.CursorWidth[mode_lib->vba.NumberOfActivePlanes][1] = + src->cur1_src_width; + if (src->cur1_src_width > 0) + mode_lib->vba.NumberOfCursors[mode_lib->vba.NumberOfActivePlanes]++; + break; + default: + dml_print( + "ERROR: Number of cursors specified exceeds supported maximum\n") + ; + } + } + + OTGInstPlane[mode_lib->vba.NumberOfActivePlanes] = dst->otg_inst; + + if (dst->odm_combine && !src->is_hsplit) + dml_print( + "ERROR: ODM Combine is specified but is_hsplit has not be specified for pipe %i\n", + j); + + if (src->is_hsplit) { + for (k = j + 1; k < mode_lib->vba.cache_num_pipes; ++k) { + display_pipe_source_params_st *src_k = &pipes[k].pipe.src; + display_output_params_st *dout_k = &pipes[k].dout; + + if (src_k->is_hsplit && !visited[k] + && src->hsplit_grp == src_k->hsplit_grp) { + mode_lib->vba.pipe_plane[k] = + mode_lib->vba.NumberOfActivePlanes; + mode_lib->vba.DPPPerPlane[mode_lib->vba.NumberOfActivePlanes]++; + if (mode_lib->vba.SourceScan[mode_lib->vba.NumberOfActivePlanes] + == dm_horz) + mode_lib->vba.ViewportWidth[mode_lib->vba.NumberOfActivePlanes] += + src_k->viewport_width; + else + mode_lib->vba.ViewportHeight[mode_lib->vba.NumberOfActivePlanes] += + src_k->viewport_height; + + mode_lib->vba.NumberOfDSCSlices[mode_lib->vba.NumberOfActivePlanes] += + dout_k->dsc_slices; + visited[k] = true; + } + } + } + + mode_lib->vba.NumberOfActivePlanes++; + } + + // handle overlays through dml_ml->vba.BlendingAndTiming + // dml_ml->vba.BlendingAndTiming tells you which instance to look at to get timing, the so called 'master' + + for (j = 0; j < mode_lib->vba.NumberOfActivePlanes; ++j) + PlaneVisited[j] = false; + + for (j = 0; j < mode_lib->vba.NumberOfActivePlanes; ++j) { + for (k = j + 1; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (!PlaneVisited[k] && OTGInstPlane[j] == OTGInstPlane[k]) { + // doesn't matter, so choose the smaller one + mode_lib->vba.BlendingAndTiming[j] = j; + PlaneVisited[j] = true; + mode_lib->vba.BlendingAndTiming[k] = j; + PlaneVisited[k] = true; + } + } + + if (!PlaneVisited[j]) { + mode_lib->vba.BlendingAndTiming[j] = j; + PlaneVisited[j] = true; + } + } + + // TODO: dml_ml->vba.ODMCombineEnabled => 2 * dml_ml->vba.DPPPerPlane...actually maybe not since all pipes are specified + // Do we want the dscclk to automatically be halved? Guess not since the value is specified + + mode_lib->vba.SynchronizedVBlank = pipes[0].pipe.dest.synchronized_vblank_all_planes; + for (k = 1; k < mode_lib->vba.cache_num_pipes; ++k) + ASSERT(mode_lib->vba.SynchronizedVBlank == pipes[k].pipe.dest.synchronized_vblank_all_planes); + + mode_lib->vba.VirtualMemoryEnable = false; + mode_lib->vba.OverridePageTableLevels = 0; + + for (k = 0; k < mode_lib->vba.cache_num_pipes; ++k) { + mode_lib->vba.VirtualMemoryEnable = mode_lib->vba.VirtualMemoryEnable + || !!pipes[k].pipe.src.vm; + mode_lib->vba.OverridePageTableLevels = + (pipes[k].pipe.src.vm_levels_force_en + && mode_lib->vba.OverridePageTableLevels + < pipes[k].pipe.src.vm_levels_force) ? + pipes[k].pipe.src.vm_levels_force : + mode_lib->vba.OverridePageTableLevels; + } + + if (mode_lib->vba.OverridePageTableLevels) + mode_lib->vba.MaxPageTableLevels = mode_lib->vba.OverridePageTableLevels; + + mode_lib->vba.VirtualMemoryEnable = mode_lib->vba.VirtualMemoryEnable && !!ip->pte_enable; + + mode_lib->vba.FabricAndDRAMBandwidth = dml_min( + mode_lib->vba.DRAMSpeed * mode_lib->vba.NumberOfChannels + * mode_lib->vba.DRAMChannelWidth, + mode_lib->vba.FabricClock * mode_lib->vba.FabricDatapathToDCNDataReturn) + / 1000.0; + + // TODO: Must be consistent across all pipes + // DCCProgrammingAssumesScanDirectionUnknown = src.dcc_scan_dir_unknown; +} + +static void recalculate(struct display_mode_lib *mode_lib) +{ + ModeSupportAndSystemConfiguration(mode_lib); + PixelClockAdjustmentForProgressiveToInterlaceUnit(mode_lib); + DisplayPipeConfiguration(mode_lib); + DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation(mode_lib); +} + +// in wm mode we pull the parameters needed from the display_e2e_pipe_params_st structs +// rather than working them out as in recalculate_ms +static void recalculate_params( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes) +{ + // This is only safe to use memcmp because there are non-POD types in struct display_mode_lib + if (memcmp(&mode_lib->soc, &mode_lib->vba.soc, sizeof(mode_lib->vba.soc)) != 0 + || memcmp(&mode_lib->ip, &mode_lib->vba.ip, sizeof(mode_lib->vba.ip)) != 0 + || num_pipes != mode_lib->vba.cache_num_pipes + || memcmp( + pipes, + mode_lib->vba.cache_pipes, + sizeof(display_e2e_pipe_params_st) * num_pipes) != 0) { + mode_lib->vba.soc = mode_lib->soc; + mode_lib->vba.ip = mode_lib->ip; + memcpy(mode_lib->vba.cache_pipes, pipes, sizeof(*pipes) * num_pipes); + mode_lib->vba.cache_num_pipes = num_pipes; + recalculate(mode_lib); + } +} + +static void ModeSupportAndSystemConfiguration(struct display_mode_lib *mode_lib) +{ + soc_bounding_box_st *soc = &mode_lib->vba.soc; + unsigned int i, k; + unsigned int total_pipes = 0; + + mode_lib->vba.VoltageLevel = mode_lib->vba.cache_pipes[0].clks_cfg.voltage; + for (i = 1; i < mode_lib->vba.cache_num_pipes; ++i) + ASSERT(mode_lib->vba.VoltageLevel == -1 || mode_lib->vba.VoltageLevel == mode_lib->vba.cache_pipes[i].clks_cfg.voltage); + + mode_lib->vba.DCFCLK = mode_lib->vba.cache_pipes[0].clks_cfg.dcfclk_mhz; + mode_lib->vba.SOCCLK = mode_lib->vba.cache_pipes[0].clks_cfg.socclk_mhz; + + if (mode_lib->vba.cache_pipes[0].clks_cfg.dispclk_mhz > 0.0) + mode_lib->vba.DISPCLK = mode_lib->vba.cache_pipes[0].clks_cfg.dispclk_mhz; + else + mode_lib->vba.DISPCLK = soc->clock_limits[mode_lib->vba.VoltageLevel].dispclk_mhz; + + fetch_socbb_params(mode_lib); + fetch_ip_params(mode_lib); + fetch_pipe_params(mode_lib); + + // Total Available Pipes Support Check + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) + total_pipes += mode_lib->vba.DPPPerPlane[k]; + ASSERT(total_pipes <= DC__NUM_DPP__MAX); +} + +static double adjust_ReturnBW( + struct display_mode_lib *mode_lib, + double ReturnBW, + bool DCCEnabledAnyPlane, + double ReturnBandwidthToDCN) +{ + double CriticalCompression; + + if (DCCEnabledAnyPlane + && ReturnBandwidthToDCN + > mode_lib->vba.DCFCLK * mode_lib->vba.ReturnBusWidth / 4.0) + ReturnBW = + dml_min( + ReturnBW, + ReturnBandwidthToDCN * 4 + * (1.0 + - mode_lib->vba.UrgentLatency + / ((mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024 + / ReturnBandwidthToDCN + - mode_lib->vba.DCFCLK + * mode_lib->vba.ReturnBusWidth + / 4) + + mode_lib->vba.UrgentLatency)); + + CriticalCompression = 2.0 * mode_lib->vba.ReturnBusWidth * mode_lib->vba.DCFCLK + * mode_lib->vba.UrgentLatency + / (ReturnBandwidthToDCN * mode_lib->vba.UrgentLatency + + (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024); + + if (DCCEnabledAnyPlane && CriticalCompression > 1.0 && CriticalCompression < 4.0) + ReturnBW = + dml_min( + ReturnBW, + 4.0 * ReturnBandwidthToDCN + * (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024 + * mode_lib->vba.ReturnBusWidth + * mode_lib->vba.DCFCLK + * mode_lib->vba.UrgentLatency + / dml_pow( + (ReturnBandwidthToDCN + * mode_lib->vba.UrgentLatency + + (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024), + 2)); + + return ReturnBW; +} + +static unsigned int dscceComputeDelay( + unsigned int bpc, + double bpp, + unsigned int sliceWidth, + unsigned int numSlices, + enum output_format_class pixelFormat) +{ + // valid bpc = source bits per component in the set of {8, 10, 12} + // valid bpp = increments of 1/16 of a bit + // min = 6/7/8 in N420/N422/444, respectively + // max = such that compression is 1:1 + //valid sliceWidth = number of pixels per slice line, must be less than or equal to 5184/numSlices (or 4096/numSlices in 420 mode) + //valid numSlices = number of slices in the horiziontal direction per DSC engine in the set of {1, 2, 3, 4} + //valid pixelFormat = pixel/color format in the set of {:N444_RGB, :S422, :N422, :N420} + + // fixed value + unsigned int rcModelSize = 8192; + + // N422/N420 operate at 2 pixels per clock + unsigned int pixelsPerClock, lstall, D, initalXmitDelay, w, s, ix, wx, p, l0, a, ax, l, + Delay, pixels; + + if (pixelFormat == dm_n422 || pixelFormat == dm_420) + pixelsPerClock = 2; + // #all other modes operate at 1 pixel per clock + else + pixelsPerClock = 1; + + //initial transmit delay as per PPS + initalXmitDelay = dml_round(rcModelSize / 2.0 / bpp / pixelsPerClock); + + //compute ssm delay + if (bpc == 8) + D = 81; + else if (bpc == 10) + D = 89; + else + D = 113; + + //divide by pixel per cycle to compute slice width as seen by DSC + w = sliceWidth / pixelsPerClock; + + //422 mode has an additional cycle of delay + if (pixelFormat == dm_s422) + s = 1; + else + s = 0; + + //main calculation for the dscce + ix = initalXmitDelay + 45; + wx = (w + 2) / 3; + p = 3 * wx - w; + l0 = ix / w; + a = ix + p * l0; + ax = (a + 2) / 3 + D + 6 + 1; + l = (ax + wx - 1) / wx; + if ((ix % w) == 0 && p != 0) + lstall = 1; + else + lstall = 0; + Delay = l * wx * (numSlices - 1) + ax + s + lstall + 22; + + //dsc processes 3 pixel containers per cycle and a container can contain 1 or 2 pixels + pixels = Delay * 3 * pixelsPerClock; + return pixels; +} + +static unsigned int dscComputeDelay(enum output_format_class pixelFormat) +{ + unsigned int Delay = 0; + + if (pixelFormat == dm_420) { + // sfr + Delay = Delay + 2; + // dsccif + Delay = Delay + 0; + // dscc - input deserializer + Delay = Delay + 3; + // dscc gets pixels every other cycle + Delay = Delay + 2; + // dscc - input cdc fifo + Delay = Delay + 12; + // dscc gets pixels every other cycle + Delay = Delay + 13; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output cdc fifo + Delay = Delay + 7; + // dscc gets pixels every other cycle + Delay = Delay + 3; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output serializer + Delay = Delay + 1; + // sft + Delay = Delay + 1; + } else if (pixelFormat == dm_n422) { + // sfr + Delay = Delay + 2; + // dsccif + Delay = Delay + 1; + // dscc - input deserializer + Delay = Delay + 5; + // dscc - input cdc fifo + Delay = Delay + 25; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output cdc fifo + Delay = Delay + 10; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output serializer + Delay = Delay + 1; + // sft + Delay = Delay + 1; + } else { + // sfr + Delay = Delay + 2; + // dsccif + Delay = Delay + 0; + // dscc - input deserializer + Delay = Delay + 3; + // dscc - input cdc fifo + Delay = Delay + 12; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output cdc fifo + Delay = Delay + 7; + // dscc - output serializer + Delay = Delay + 1; + // dscc - cdc uncertainty + Delay = Delay + 2; + // sft + Delay = Delay + 1; + } + + return Delay; +} + +static bool CalculatePrefetchSchedule( + struct display_mode_lib *mode_lib, + double DPPCLK, + double DISPCLK, + double PixelClock, + double DCFClkDeepSleep, + unsigned int DSCDelay, + unsigned int DPPPerPlane, + bool ScalerEnabled, + unsigned int NumberOfCursors, + double DPPCLKDelaySubtotal, + double DPPCLKDelaySCL, + double DPPCLKDelaySCLLBOnly, + double DPPCLKDelayCNVCFormater, + double DPPCLKDelayCNVCCursor, + double DISPCLKDelaySubtotal, + unsigned int ScalerRecoutWidth, + enum output_format_class OutputFormat, + unsigned int VBlank, + unsigned int HTotal, + unsigned int MaxInterDCNTileRepeaters, + unsigned int VStartup, + unsigned int PageTableLevels, + bool VirtualMemoryEnable, + bool DynamicMetadataEnable, + unsigned int DynamicMetadataLinesBeforeActiveRequired, + unsigned int DynamicMetadataTransmittedBytes, + bool DCCEnable, + double UrgentLatency, + double UrgentExtraLatency, + double TCalc, + unsigned int PDEAndMetaPTEBytesFrame, + unsigned int MetaRowByte, + unsigned int PixelPTEBytesPerRow, + double PrefetchSourceLinesY, + unsigned int SwathWidthY, + double BytePerPixelDETY, + double VInitPreFillY, + unsigned int MaxNumSwathY, + double PrefetchSourceLinesC, + double BytePerPixelDETC, + double VInitPreFillC, + unsigned int MaxNumSwathC, + unsigned int SwathHeightY, + unsigned int SwathHeightC, + double TWait, + bool XFCEnabled, + double XFCRemoteSurfaceFlipDelay, + bool InterlaceEnable, + bool ProgressiveToInterlaceUnitInOPP, + double *DSTXAfterScaler, + double *DSTYAfterScaler, + double *DestinationLinesForPrefetch, + double *PrefetchBandwidth, + double *DestinationLinesToRequestVMInVBlank, + double *DestinationLinesToRequestRowInVBlank, + double *VRatioPrefetchY, + double *VRatioPrefetchC, + double *RequiredPrefetchPixDataBW, + unsigned int *VStartupRequiredWhenNotEnoughTimeForDynamicMetadata, + double *Tno_bw, + unsigned int *VUpdateOffsetPix, + unsigned int *VUpdateWidthPix, + unsigned int *VReadyOffsetPix) +{ + bool MyError = false; + unsigned int DPPCycles, DISPCLKCycles; + double DSTTotalPixelsAfterScaler, TotalRepeaterDelayTime; + double Tdm, LineTime, Tsetup; + double dst_y_prefetch_equ; + double Tsw_oto; + double prefetch_bw_oto; + double Tvm_oto; + double Tr0_oto; + double Tpre_oto; + double dst_y_prefetch_oto; + double TimeForFetchingMetaPTE = 0; + double TimeForFetchingRowInVBlank = 0; + double LinesToRequestPrefetchPixelData = 0; + + if (ScalerEnabled) + DPPCycles = DPPCLKDelaySubtotal + DPPCLKDelaySCL; + else + DPPCycles = DPPCLKDelaySubtotal + DPPCLKDelaySCLLBOnly; + + DPPCycles = DPPCycles + DPPCLKDelayCNVCFormater + NumberOfCursors * DPPCLKDelayCNVCCursor; + + DISPCLKCycles = DISPCLKDelaySubtotal; + + if (DPPCLK == 0.0 || DISPCLK == 0.0) + return true; + + *DSTXAfterScaler = DPPCycles * PixelClock / DPPCLK + DISPCLKCycles * PixelClock / DISPCLK + + DSCDelay; + + if (DPPPerPlane > 1) + *DSTXAfterScaler = *DSTXAfterScaler + ScalerRecoutWidth; + + if (OutputFormat == dm_420 || (InterlaceEnable && ProgressiveToInterlaceUnitInOPP)) + *DSTYAfterScaler = 1; + else + *DSTYAfterScaler = 0; + + DSTTotalPixelsAfterScaler = ((double) (*DSTYAfterScaler * HTotal)) + *DSTXAfterScaler; + *DSTYAfterScaler = dml_floor(DSTTotalPixelsAfterScaler / HTotal, 1); + *DSTXAfterScaler = DSTTotalPixelsAfterScaler - ((double) (*DSTYAfterScaler * HTotal)); + + *VUpdateOffsetPix = dml_ceil(HTotal / 4.0, 1); + TotalRepeaterDelayTime = MaxInterDCNTileRepeaters * (2.0 / DPPCLK + 3.0 / DISPCLK); + *VUpdateWidthPix = (14.0 / DCFClkDeepSleep + 12.0 / DPPCLK + TotalRepeaterDelayTime) + * PixelClock; + + *VReadyOffsetPix = dml_max( + 150.0 / DPPCLK, + TotalRepeaterDelayTime + 20.0 / DCFClkDeepSleep + 10.0 / DPPCLK) + * PixelClock; + + Tsetup = (double) (*VUpdateOffsetPix + *VUpdateWidthPix + *VReadyOffsetPix) / PixelClock; + + LineTime = (double) HTotal / PixelClock; + + if (DynamicMetadataEnable) { + double Tdmbf, Tdmec, Tdmsks; + + Tdm = dml_max(0.0, UrgentExtraLatency - TCalc); + Tdmbf = DynamicMetadataTransmittedBytes / 4.0 / DISPCLK; + Tdmec = LineTime; + if (DynamicMetadataLinesBeforeActiveRequired == 0) + Tdmsks = VBlank * LineTime / 2.0; + else + Tdmsks = DynamicMetadataLinesBeforeActiveRequired * LineTime; + if (InterlaceEnable && !ProgressiveToInterlaceUnitInOPP) + Tdmsks = Tdmsks / 2; + if (VStartup * LineTime + < Tsetup + TWait + UrgentExtraLatency + Tdmbf + Tdmec + Tdmsks) { + MyError = true; + *VStartupRequiredWhenNotEnoughTimeForDynamicMetadata = (Tsetup + TWait + + UrgentExtraLatency + Tdmbf + Tdmec + Tdmsks) / LineTime; + } else + *VStartupRequiredWhenNotEnoughTimeForDynamicMetadata = 0.0; + } else + Tdm = 0; + + if (VirtualMemoryEnable) { + if (PageTableLevels == 4) + *Tno_bw = UrgentExtraLatency + UrgentLatency; + else if (PageTableLevels == 3) + *Tno_bw = UrgentExtraLatency; + else + *Tno_bw = 0; + } else if (DCCEnable) + *Tno_bw = LineTime; + else + *Tno_bw = LineTime / 4; + + dst_y_prefetch_equ = VStartup - dml_max(TCalc + TWait, XFCRemoteSurfaceFlipDelay) / LineTime + - (Tsetup + Tdm) / LineTime + - (*DSTYAfterScaler + *DSTXAfterScaler / HTotal); + + Tsw_oto = dml_max(PrefetchSourceLinesY, PrefetchSourceLinesC) * LineTime; + + prefetch_bw_oto = (MetaRowByte + PixelPTEBytesPerRow + + PrefetchSourceLinesY * SwathWidthY * dml_ceil(BytePerPixelDETY, 1) + + PrefetchSourceLinesC * SwathWidthY / 2 * dml_ceil(BytePerPixelDETC, 2)) + / Tsw_oto; + + if (VirtualMemoryEnable == true) { + Tvm_oto = + dml_max( + *Tno_bw + PDEAndMetaPTEBytesFrame / prefetch_bw_oto, + dml_max( + UrgentExtraLatency + + UrgentLatency + * (PageTableLevels + - 1), + LineTime / 4.0)); + } else + Tvm_oto = LineTime / 4.0; + + if ((VirtualMemoryEnable == true || DCCEnable == true)) { + Tr0_oto = dml_max( + (MetaRowByte + PixelPTEBytesPerRow) / prefetch_bw_oto, + dml_max(UrgentLatency, dml_max(LineTime - Tvm_oto, LineTime / 4))); + } else + Tr0_oto = LineTime - Tvm_oto; + + Tpre_oto = Tvm_oto + Tr0_oto + Tsw_oto; + + dst_y_prefetch_oto = Tpre_oto / LineTime; + + if (dst_y_prefetch_oto < dst_y_prefetch_equ) + *DestinationLinesForPrefetch = dst_y_prefetch_oto; + else + *DestinationLinesForPrefetch = dst_y_prefetch_equ; + + *DestinationLinesForPrefetch = dml_floor(4.0 * (*DestinationLinesForPrefetch + 0.125), 1) + / 4; + + dml_print("DML: VStartup: %d\n", VStartup); + dml_print("DML: TCalc: %f\n", TCalc); + dml_print("DML: TWait: %f\n", TWait); + dml_print("DML: XFCRemoteSurfaceFlipDelay: %f\n", XFCRemoteSurfaceFlipDelay); + dml_print("DML: LineTime: %f\n", LineTime); + dml_print("DML: Tsetup: %f\n", Tsetup); + dml_print("DML: Tdm: %f\n", Tdm); + dml_print("DML: DSTYAfterScaler: %f\n", *DSTYAfterScaler); + dml_print("DML: DSTXAfterScaler: %f\n", *DSTXAfterScaler); + dml_print("DML: HTotal: %d\n", HTotal); + + *PrefetchBandwidth = 0; + *DestinationLinesToRequestVMInVBlank = 0; + *DestinationLinesToRequestRowInVBlank = 0; + *VRatioPrefetchY = 0; + *VRatioPrefetchC = 0; + *RequiredPrefetchPixDataBW = 0; + if (*DestinationLinesForPrefetch > 1) { + *PrefetchBandwidth = (PDEAndMetaPTEBytesFrame + 2 * MetaRowByte + + 2 * PixelPTEBytesPerRow + + PrefetchSourceLinesY * SwathWidthY * dml_ceil(BytePerPixelDETY, 1) + + PrefetchSourceLinesC * SwathWidthY / 2 + * dml_ceil(BytePerPixelDETC, 2)) + / (*DestinationLinesForPrefetch * LineTime - *Tno_bw); + if (VirtualMemoryEnable) { + TimeForFetchingMetaPTE = + dml_max( + *Tno_bw + + (double) PDEAndMetaPTEBytesFrame + / *PrefetchBandwidth, + dml_max( + UrgentExtraLatency + + UrgentLatency + * (PageTableLevels + - 1), + LineTime / 4)); + } else { + if (NumberOfCursors > 0 || XFCEnabled) + TimeForFetchingMetaPTE = LineTime / 4; + else + TimeForFetchingMetaPTE = 0.0; + } + + if ((VirtualMemoryEnable == true || DCCEnable == true)) { + TimeForFetchingRowInVBlank = + dml_max( + (MetaRowByte + PixelPTEBytesPerRow) + / *PrefetchBandwidth, + dml_max( + UrgentLatency, + dml_max( + LineTime + - TimeForFetchingMetaPTE, + LineTime + / 4.0))); + } else { + if (NumberOfCursors > 0 || XFCEnabled) + TimeForFetchingRowInVBlank = LineTime - TimeForFetchingMetaPTE; + else + TimeForFetchingRowInVBlank = 0.0; + } + + *DestinationLinesToRequestVMInVBlank = dml_floor( + 4.0 * (TimeForFetchingMetaPTE / LineTime + 0.125), + 1) / 4.0; + + *DestinationLinesToRequestRowInVBlank = dml_floor( + 4.0 * (TimeForFetchingRowInVBlank / LineTime + 0.125), + 1) / 4.0; + + LinesToRequestPrefetchPixelData = + *DestinationLinesForPrefetch + - ((NumberOfCursors > 0 || VirtualMemoryEnable + || DCCEnable) ? + (*DestinationLinesToRequestVMInVBlank + + *DestinationLinesToRequestRowInVBlank) : + 0.0); + + if (LinesToRequestPrefetchPixelData > 0) { + + *VRatioPrefetchY = (double) PrefetchSourceLinesY + / LinesToRequestPrefetchPixelData; + *VRatioPrefetchY = dml_max(*VRatioPrefetchY, 1.0); + if ((SwathHeightY > 4) && (VInitPreFillY > 3)) { + if (LinesToRequestPrefetchPixelData > (VInitPreFillY - 3.0) / 2.0) { + *VRatioPrefetchY = + dml_max( + (double) PrefetchSourceLinesY + / LinesToRequestPrefetchPixelData, + (double) MaxNumSwathY + * SwathHeightY + / (LinesToRequestPrefetchPixelData + - (VInitPreFillY + - 3.0) + / 2.0)); + *VRatioPrefetchY = dml_max(*VRatioPrefetchY, 1.0); + } else { + MyError = true; + *VRatioPrefetchY = 0; + } + } + + *VRatioPrefetchC = (double) PrefetchSourceLinesC + / LinesToRequestPrefetchPixelData; + *VRatioPrefetchC = dml_max(*VRatioPrefetchC, 1.0); + + if ((SwathHeightC > 4)) { + if (LinesToRequestPrefetchPixelData > (VInitPreFillC - 3.0) / 2.0) { + *VRatioPrefetchC = + dml_max( + *VRatioPrefetchC, + (double) MaxNumSwathC + * SwathHeightC + / (LinesToRequestPrefetchPixelData + - (VInitPreFillC + - 3.0) + / 2.0)); + *VRatioPrefetchC = dml_max(*VRatioPrefetchC, 1.0); + } else { + MyError = true; + *VRatioPrefetchC = 0; + } + } + + *RequiredPrefetchPixDataBW = + DPPPerPlane + * ((double) PrefetchSourceLinesY + / LinesToRequestPrefetchPixelData + * dml_ceil( + BytePerPixelDETY, + 1) + + (double) PrefetchSourceLinesC + / LinesToRequestPrefetchPixelData + * dml_ceil( + BytePerPixelDETC, + 2) + / 2) + * SwathWidthY / LineTime; + } else { + MyError = true; + *VRatioPrefetchY = 0; + *VRatioPrefetchC = 0; + *RequiredPrefetchPixDataBW = 0; + } + + } else { + MyError = true; + } + + if (MyError) { + *PrefetchBandwidth = 0; + TimeForFetchingMetaPTE = 0; + TimeForFetchingRowInVBlank = 0; + *DestinationLinesToRequestVMInVBlank = 0; + *DestinationLinesToRequestRowInVBlank = 0; + *DestinationLinesForPrefetch = 0; + LinesToRequestPrefetchPixelData = 0; + *VRatioPrefetchY = 0; + *VRatioPrefetchC = 0; + *RequiredPrefetchPixDataBW = 0; + } + + return MyError; +} + +static double RoundToDFSGranularityUp(double Clock, double VCOSpeed) +{ + return VCOSpeed * 4 / dml_floor(VCOSpeed * 4 / Clock, 1); +} + +static double RoundToDFSGranularityDown(double Clock, double VCOSpeed) +{ + return VCOSpeed * 4 / dml_ceil(VCOSpeed * 4 / Clock, 1); +} + +static double CalculatePrefetchSourceLines( + struct display_mode_lib *mode_lib, + double VRatio, + double vtaps, + bool Interlace, + bool ProgressiveToInterlaceUnitInOPP, + unsigned int SwathHeight, + unsigned int ViewportYStart, + double *VInitPreFill, + unsigned int *MaxNumSwath) +{ + unsigned int MaxPartialSwath; + + if (ProgressiveToInterlaceUnitInOPP) + *VInitPreFill = dml_floor((VRatio + vtaps + 1) / 2.0, 1); + else + *VInitPreFill = dml_floor((VRatio + vtaps + 1 + Interlace * 0.5 * VRatio) / 2.0, 1); + + if (!mode_lib->vba.IgnoreViewportPositioning) { + + *MaxNumSwath = dml_ceil((*VInitPreFill - 1.0) / SwathHeight, 1) + 1.0; + + if (*VInitPreFill > 1.0) + MaxPartialSwath = (unsigned int) (*VInitPreFill - 2) % SwathHeight; + else + MaxPartialSwath = (unsigned int) (*VInitPreFill + SwathHeight - 2) + % SwathHeight; + MaxPartialSwath = dml_max(1U, MaxPartialSwath); + + } else { + + if (ViewportYStart != 0) + dml_print( + "WARNING DML: using viewport y position of 0 even though actual viewport y position is non-zero in prefetch source lines calculation\n"); + + *MaxNumSwath = dml_ceil(*VInitPreFill / SwathHeight, 1); + + if (*VInitPreFill > 1.0) + MaxPartialSwath = (unsigned int) (*VInitPreFill - 1) % SwathHeight; + else + MaxPartialSwath = (unsigned int) (*VInitPreFill + SwathHeight - 1) + % SwathHeight; + } + + return *MaxNumSwath * SwathHeight + MaxPartialSwath; +} + +static unsigned int CalculateVMAndRowBytes( + struct display_mode_lib *mode_lib, + bool DCCEnable, + unsigned int BlockHeight256Bytes, + unsigned int BlockWidth256Bytes, + enum source_format_class SourcePixelFormat, + unsigned int SurfaceTiling, + unsigned int BytePerPixel, + enum scan_direction_class ScanDirection, + unsigned int ViewportWidth, + unsigned int ViewportHeight, + unsigned int SwathWidth, + bool VirtualMemoryEnable, + unsigned int VMMPageSize, + unsigned int PTEBufferSizeInRequests, + unsigned int PDEProcessingBufIn64KBReqs, + unsigned int Pitch, + unsigned int DCCMetaPitch, + unsigned int *MacroTileWidth, + unsigned int *MetaRowByte, + unsigned int *PixelPTEBytesPerRow, + bool *PTEBufferSizeNotExceeded, + unsigned int *dpte_row_height, + unsigned int *meta_row_height) +{ + unsigned int MetaRequestHeight; + unsigned int MetaRequestWidth; + unsigned int MetaSurfWidth; + unsigned int MetaSurfHeight; + unsigned int MPDEBytesFrame; + unsigned int MetaPTEBytesFrame; + unsigned int DCCMetaSurfaceBytes; + + unsigned int MacroTileSizeBytes; + unsigned int MacroTileHeight; + unsigned int DPDE0BytesFrame; + unsigned int ExtraDPDEBytesFrame; + unsigned int PDEAndMetaPTEBytesFrame; + + if (DCCEnable == true) { + MetaRequestHeight = 8 * BlockHeight256Bytes; + MetaRequestWidth = 8 * BlockWidth256Bytes; + if (ScanDirection == dm_horz) { + *meta_row_height = MetaRequestHeight; + MetaSurfWidth = dml_ceil((double) SwathWidth - 1, MetaRequestWidth) + + MetaRequestWidth; + *MetaRowByte = MetaSurfWidth * MetaRequestHeight * BytePerPixel / 256.0; + } else { + *meta_row_height = MetaRequestWidth; + MetaSurfHeight = dml_ceil((double) SwathWidth - 1, MetaRequestHeight) + + MetaRequestHeight; + *MetaRowByte = MetaSurfHeight * MetaRequestWidth * BytePerPixel / 256.0; + } + if (ScanDirection == dm_horz) { + DCCMetaSurfaceBytes = DCCMetaPitch + * (dml_ceil(ViewportHeight - 1, 64 * BlockHeight256Bytes) + + 64 * BlockHeight256Bytes) * BytePerPixel + / 256; + } else { + DCCMetaSurfaceBytes = DCCMetaPitch + * (dml_ceil( + (double) ViewportHeight - 1, + 64 * BlockHeight256Bytes) + + 64 * BlockHeight256Bytes) * BytePerPixel + / 256; + } + if (VirtualMemoryEnable == true) { + MetaPTEBytesFrame = (dml_ceil( + (double) (DCCMetaSurfaceBytes - VMMPageSize) + / (8 * VMMPageSize), + 1) + 1) * 64; + MPDEBytesFrame = 128 * (mode_lib->vba.MaxPageTableLevels - 1); + } else { + MetaPTEBytesFrame = 0; + MPDEBytesFrame = 0; + } + } else { + MetaPTEBytesFrame = 0; + MPDEBytesFrame = 0; + *MetaRowByte = 0; + } + + if (SurfaceTiling == dm_sw_linear) { + MacroTileSizeBytes = 256; + MacroTileHeight = 1; + } else if (SurfaceTiling == dm_sw_4kb_s || SurfaceTiling == dm_sw_4kb_s_x + || SurfaceTiling == dm_sw_4kb_d || SurfaceTiling == dm_sw_4kb_d_x) { + MacroTileSizeBytes = 4096; + MacroTileHeight = 4 * BlockHeight256Bytes; + } else if (SurfaceTiling == dm_sw_64kb_s || SurfaceTiling == dm_sw_64kb_s_t + || SurfaceTiling == dm_sw_64kb_s_x || SurfaceTiling == dm_sw_64kb_d + || SurfaceTiling == dm_sw_64kb_d_t || SurfaceTiling == dm_sw_64kb_d_x + || SurfaceTiling == dm_sw_64kb_r_x) { + MacroTileSizeBytes = 65536; + MacroTileHeight = 16 * BlockHeight256Bytes; + } else { + MacroTileSizeBytes = 262144; + MacroTileHeight = 32 * BlockHeight256Bytes; + } + *MacroTileWidth = MacroTileSizeBytes / BytePerPixel / MacroTileHeight; + + if (VirtualMemoryEnable == true && mode_lib->vba.MaxPageTableLevels > 1) { + if (ScanDirection == dm_horz) { + DPDE0BytesFrame = + 64 + * (dml_ceil( + ((Pitch + * (dml_ceil( + ViewportHeight + - 1, + MacroTileHeight) + + MacroTileHeight) + * BytePerPixel) + - MacroTileSizeBytes) + / (8 + * 2097152), + 1) + 1); + } else { + DPDE0BytesFrame = + 64 + * (dml_ceil( + ((Pitch + * (dml_ceil( + (double) SwathWidth + - 1, + MacroTileHeight) + + MacroTileHeight) + * BytePerPixel) + - MacroTileSizeBytes) + / (8 + * 2097152), + 1) + 1); + } + ExtraDPDEBytesFrame = 128 * (mode_lib->vba.MaxPageTableLevels - 2); + } else { + DPDE0BytesFrame = 0; + ExtraDPDEBytesFrame = 0; + } + + PDEAndMetaPTEBytesFrame = MetaPTEBytesFrame + MPDEBytesFrame + DPDE0BytesFrame + + ExtraDPDEBytesFrame; + + if (VirtualMemoryEnable == true) { + unsigned int PTERequestSize; + unsigned int PixelPTEReqHeight; + unsigned int PixelPTEReqWidth; + double FractionOfPTEReturnDrop; + unsigned int EffectivePDEProcessingBufIn64KBReqs; + + if (SurfaceTiling == dm_sw_linear) { + PixelPTEReqHeight = 1; + PixelPTEReqWidth = 8.0 * VMMPageSize / BytePerPixel; + PTERequestSize = 64; + FractionOfPTEReturnDrop = 0; + } else if (MacroTileSizeBytes == 4096) { + PixelPTEReqHeight = MacroTileHeight; + PixelPTEReqWidth = 8 * *MacroTileWidth; + PTERequestSize = 64; + if (ScanDirection == dm_horz) + FractionOfPTEReturnDrop = 0; + else + FractionOfPTEReturnDrop = 7 / 8; + } else if (VMMPageSize == 4096 && MacroTileSizeBytes > 4096) { + PixelPTEReqHeight = 16 * BlockHeight256Bytes; + PixelPTEReqWidth = 16 * BlockWidth256Bytes; + PTERequestSize = 128; + FractionOfPTEReturnDrop = 0; + } else { + PixelPTEReqHeight = MacroTileHeight; + PixelPTEReqWidth = 8 * *MacroTileWidth; + PTERequestSize = 64; + FractionOfPTEReturnDrop = 0; + } + + if (SourcePixelFormat == dm_420_8 || SourcePixelFormat == dm_420_10) + EffectivePDEProcessingBufIn64KBReqs = PDEProcessingBufIn64KBReqs / 2; + else + EffectivePDEProcessingBufIn64KBReqs = PDEProcessingBufIn64KBReqs; + + if (SurfaceTiling == dm_sw_linear) { + *dpte_row_height = + dml_min( + 128, + 1 + << (unsigned int) dml_floor( + dml_log2( + dml_min( + (double) PTEBufferSizeInRequests + * PixelPTEReqWidth, + EffectivePDEProcessingBufIn64KBReqs + * 65536.0 + / BytePerPixel) + / Pitch), + 1)); + *PixelPTEBytesPerRow = PTERequestSize + * (dml_ceil( + (double) (Pitch * *dpte_row_height - 1) + / PixelPTEReqWidth, + 1) + 1); + } else if (ScanDirection == dm_horz) { + *dpte_row_height = PixelPTEReqHeight; + *PixelPTEBytesPerRow = PTERequestSize + * (dml_ceil(((double) SwathWidth - 1) / PixelPTEReqWidth, 1) + + 1); + } else { + *dpte_row_height = dml_min(PixelPTEReqWidth, *MacroTileWidth); + *PixelPTEBytesPerRow = PTERequestSize + * (dml_ceil( + ((double) SwathWidth - 1) + / PixelPTEReqHeight, + 1) + 1); + } + if (*PixelPTEBytesPerRow * (1 - FractionOfPTEReturnDrop) + <= 64 * PTEBufferSizeInRequests) { + *PTEBufferSizeNotExceeded = true; + } else { + *PTEBufferSizeNotExceeded = false; + } + } else { + *PixelPTEBytesPerRow = 0; + *PTEBufferSizeNotExceeded = true; + } + + return PDEAndMetaPTEBytesFrame; +} + +static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation( + struct display_mode_lib *mode_lib) +{ + unsigned int j, k; + + mode_lib->vba.WritebackDISPCLK = 0.0; + mode_lib->vba.DISPCLKWithRamping = 0; + mode_lib->vba.DISPCLKWithoutRamping = 0; + mode_lib->vba.GlobalDPPCLK = 0.0; + + // dml_ml->vba.DISPCLK and dml_ml->vba.DPPCLK Calculation + // + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.WritebackEnable[k]) { + mode_lib->vba.WritebackDISPCLK = + dml_max( + mode_lib->vba.WritebackDISPCLK, + CalculateWriteBackDISPCLK( + mode_lib->vba.WritebackPixelFormat[k], + mode_lib->vba.PixelClock[k], + mode_lib->vba.WritebackHRatio[k], + mode_lib->vba.WritebackVRatio[k], + mode_lib->vba.WritebackLumaHTaps[k], + mode_lib->vba.WritebackLumaVTaps[k], + mode_lib->vba.WritebackChromaHTaps[k], + mode_lib->vba.WritebackChromaVTaps[k], + mode_lib->vba.WritebackDestinationWidth[k], + mode_lib->vba.HTotal[k], + mode_lib->vba.WritebackChromaLineBufferWidth)); + } + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.HRatio[k] > 1) { + mode_lib->vba.PSCL_THROUGHPUT_LUMA[k] = dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput + * mode_lib->vba.HRatio[k] + / dml_ceil( + mode_lib->vba.htaps[k] + / 6.0, + 1)); + } else { + mode_lib->vba.PSCL_THROUGHPUT_LUMA[k] = dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput); + } + + mode_lib->vba.DPPCLKUsingSingleDPPLuma = + mode_lib->vba.PixelClock[k] + * dml_max( + mode_lib->vba.vtaps[k] / 6.0 + * dml_min( + 1.0, + mode_lib->vba.HRatio[k]), + dml_max( + mode_lib->vba.HRatio[k] + * mode_lib->vba.VRatio[k] + / mode_lib->vba.PSCL_THROUGHPUT_LUMA[k], + 1.0)); + + if ((mode_lib->vba.htaps[k] > 6 || mode_lib->vba.vtaps[k] > 6) + && mode_lib->vba.DPPCLKUsingSingleDPPLuma + < 2 * mode_lib->vba.PixelClock[k]) { + mode_lib->vba.DPPCLKUsingSingleDPPLuma = 2 * mode_lib->vba.PixelClock[k]; + } + + if ((mode_lib->vba.SourcePixelFormat[k] != dm_420_8 + && mode_lib->vba.SourcePixelFormat[k] != dm_420_10)) { + mode_lib->vba.PSCL_THROUGHPUT_CHROMA[k] = 0.0; + mode_lib->vba.DPPCLKUsingSingleDPP[k] = + mode_lib->vba.DPPCLKUsingSingleDPPLuma; + } else { + if (mode_lib->vba.HRatio[k] > 1) { + mode_lib->vba.PSCL_THROUGHPUT_CHROMA[k] = + dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput + * mode_lib->vba.HRatio[k] + / 2 + / dml_ceil( + mode_lib->vba.HTAPsChroma[k] + / 6.0, + 1.0)); + } else { + mode_lib->vba.PSCL_THROUGHPUT_CHROMA[k] = dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput); + } + mode_lib->vba.DPPCLKUsingSingleDPPChroma = + mode_lib->vba.PixelClock[k] + * dml_max( + mode_lib->vba.VTAPsChroma[k] + / 6.0 + * dml_min( + 1.0, + mode_lib->vba.HRatio[k] + / 2), + dml_max( + mode_lib->vba.HRatio[k] + * mode_lib->vba.VRatio[k] + / 4 + / mode_lib->vba.PSCL_THROUGHPUT_CHROMA[k], + 1.0)); + + if ((mode_lib->vba.HTAPsChroma[k] > 6 || mode_lib->vba.VTAPsChroma[k] > 6) + && mode_lib->vba.DPPCLKUsingSingleDPPChroma + < 2 * mode_lib->vba.PixelClock[k]) { + mode_lib->vba.DPPCLKUsingSingleDPPChroma = 2 + * mode_lib->vba.PixelClock[k]; + } + + mode_lib->vba.DPPCLKUsingSingleDPP[k] = dml_max( + mode_lib->vba.DPPCLKUsingSingleDPPLuma, + mode_lib->vba.DPPCLKUsingSingleDPPChroma); + } + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.BlendingAndTiming[k] != k) + continue; + if (mode_lib->vba.ODMCombineEnabled[k]) { + mode_lib->vba.DISPCLKWithRamping = + dml_max( + mode_lib->vba.DISPCLKWithRamping, + mode_lib->vba.PixelClock[k] / 2 + * (1 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100) + * (1 + + mode_lib->vba.DISPCLKRampingMargin + / 100)); + mode_lib->vba.DISPCLKWithoutRamping = + dml_max( + mode_lib->vba.DISPCLKWithoutRamping, + mode_lib->vba.PixelClock[k] / 2 + * (1 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100)); + } else if (!mode_lib->vba.ODMCombineEnabled[k]) { + mode_lib->vba.DISPCLKWithRamping = + dml_max( + mode_lib->vba.DISPCLKWithRamping, + mode_lib->vba.PixelClock[k] + * (1 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100) + * (1 + + mode_lib->vba.DISPCLKRampingMargin + / 100)); + mode_lib->vba.DISPCLKWithoutRamping = + dml_max( + mode_lib->vba.DISPCLKWithoutRamping, + mode_lib->vba.PixelClock[k] + * (1 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100)); + } + } + + mode_lib->vba.DISPCLKWithRamping = dml_max( + mode_lib->vba.DISPCLKWithRamping, + mode_lib->vba.WritebackDISPCLK); + mode_lib->vba.DISPCLKWithoutRamping = dml_max( + mode_lib->vba.DISPCLKWithoutRamping, + mode_lib->vba.WritebackDISPCLK); + + ASSERT(mode_lib->vba.DISPCLKDPPCLKVCOSpeed != 0); + mode_lib->vba.DISPCLKWithRampingRoundedToDFSGranularity = RoundToDFSGranularityUp( + mode_lib->vba.DISPCLKWithRamping, + mode_lib->vba.DISPCLKDPPCLKVCOSpeed); + mode_lib->vba.DISPCLKWithoutRampingRoundedToDFSGranularity = RoundToDFSGranularityUp( + mode_lib->vba.DISPCLKWithoutRamping, + mode_lib->vba.DISPCLKDPPCLKVCOSpeed); + mode_lib->vba.MaxDispclkRoundedToDFSGranularity = RoundToDFSGranularityDown( + mode_lib->vba.soc.clock_limits[NumberOfStates - 1].dispclk_mhz, + mode_lib->vba.DISPCLKDPPCLKVCOSpeed); + if (mode_lib->vba.DISPCLKWithoutRampingRoundedToDFSGranularity + > mode_lib->vba.MaxDispclkRoundedToDFSGranularity) { + mode_lib->vba.DISPCLK_calculated = + mode_lib->vba.DISPCLKWithoutRampingRoundedToDFSGranularity; + } else if (mode_lib->vba.DISPCLKWithRampingRoundedToDFSGranularity + > mode_lib->vba.MaxDispclkRoundedToDFSGranularity) { + mode_lib->vba.DISPCLK_calculated = mode_lib->vba.MaxDispclkRoundedToDFSGranularity; + } else { + mode_lib->vba.DISPCLK_calculated = + mode_lib->vba.DISPCLKWithRampingRoundedToDFSGranularity; + } + DTRACE(" dispclk_mhz (calculated) = %f", mode_lib->vba.DISPCLK_calculated); + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.DPPCLK_calculated[k] = mode_lib->vba.DPPCLKUsingSingleDPP[k] + / mode_lib->vba.DPPPerPlane[k] + * (1 + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading / 100); + mode_lib->vba.GlobalDPPCLK = dml_max( + mode_lib->vba.GlobalDPPCLK, + mode_lib->vba.DPPCLK_calculated[k]); + } + mode_lib->vba.GlobalDPPCLK = RoundToDFSGranularityUp( + mode_lib->vba.GlobalDPPCLK, + mode_lib->vba.DISPCLKDPPCLKVCOSpeed); + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.DPPCLK_calculated[k] = mode_lib->vba.GlobalDPPCLK / 255 + * dml_ceil( + mode_lib->vba.DPPCLK_calculated[k] * 255 + / mode_lib->vba.GlobalDPPCLK, + 1); + DTRACE(" dppclk_mhz[%i] (calculated) = %f", k, mode_lib->vba.DPPCLK_calculated[k]); + } + + // Urgent Watermark + mode_lib->vba.DCCEnabledAnyPlane = false; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) + if (mode_lib->vba.DCCEnable[k]) + mode_lib->vba.DCCEnabledAnyPlane = true; + + mode_lib->vba.ReturnBandwidthToDCN = dml_min( + mode_lib->vba.ReturnBusWidth * mode_lib->vba.DCFCLK, + mode_lib->vba.FabricAndDRAMBandwidth * 1000) + * mode_lib->vba.PercentOfIdealDRAMAndFabricBWReceivedAfterUrgLatency / 100; + + mode_lib->vba.ReturnBW = mode_lib->vba.ReturnBandwidthToDCN; + mode_lib->vba.ReturnBW = adjust_ReturnBW( + mode_lib, + mode_lib->vba.ReturnBW, + mode_lib->vba.DCCEnabledAnyPlane, + mode_lib->vba.ReturnBandwidthToDCN); + + // Let's do this calculation again?? + mode_lib->vba.ReturnBandwidthToDCN = dml_min( + mode_lib->vba.ReturnBusWidth * mode_lib->vba.DCFCLK, + mode_lib->vba.FabricAndDRAMBandwidth * 1000); + mode_lib->vba.ReturnBW = adjust_ReturnBW( + mode_lib, + mode_lib->vba.ReturnBW, + mode_lib->vba.DCCEnabledAnyPlane, + mode_lib->vba.ReturnBandwidthToDCN); + + DTRACE(" dcfclk_mhz = %f", mode_lib->vba.DCFCLK); + DTRACE(" return_bw_to_dcn = %f", mode_lib->vba.ReturnBandwidthToDCN); + DTRACE(" return_bus_bw = %f", mode_lib->vba.ReturnBW); + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + bool MainPlaneDoesODMCombine = false; + + if (mode_lib->vba.SourceScan[k] == dm_horz) + mode_lib->vba.SwathWidthSingleDPPY[k] = mode_lib->vba.ViewportWidth[k]; + else + mode_lib->vba.SwathWidthSingleDPPY[k] = mode_lib->vba.ViewportHeight[k]; + + if (mode_lib->vba.ODMCombineEnabled[k] == true) + MainPlaneDoesODMCombine = true; + for (j = 0; j < mode_lib->vba.NumberOfActivePlanes; ++j) + if (mode_lib->vba.BlendingAndTiming[k] == j + && mode_lib->vba.ODMCombineEnabled[j] == true) + MainPlaneDoesODMCombine = true; + + if (MainPlaneDoesODMCombine == true) + mode_lib->vba.SwathWidthY[k] = dml_min( + (double) mode_lib->vba.SwathWidthSingleDPPY[k], + dml_round( + mode_lib->vba.HActive[k] / 2.0 + * mode_lib->vba.HRatio[k])); + else + mode_lib->vba.SwathWidthY[k] = mode_lib->vba.SwathWidthSingleDPPY[k] + / mode_lib->vba.DPPPerPlane[k]; + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.SourcePixelFormat[k] == dm_444_64) { + mode_lib->vba.BytePerPixelDETY[k] = 8; + mode_lib->vba.BytePerPixelDETC[k] = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_32) { + mode_lib->vba.BytePerPixelDETY[k] = 4; + mode_lib->vba.BytePerPixelDETC[k] = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_16) { + mode_lib->vba.BytePerPixelDETY[k] = 2; + mode_lib->vba.BytePerPixelDETC[k] = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_8) { + mode_lib->vba.BytePerPixelDETY[k] = 1; + mode_lib->vba.BytePerPixelDETC[k] = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_8) { + mode_lib->vba.BytePerPixelDETY[k] = 1; + mode_lib->vba.BytePerPixelDETC[k] = 2; + } else { // dm_420_10 + mode_lib->vba.BytePerPixelDETY[k] = 4.0 / 3.0; + mode_lib->vba.BytePerPixelDETC[k] = 8.0 / 3.0; + } + } + + mode_lib->vba.TotalDataReadBandwidth = 0.0; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.ReadBandwidthPlaneLuma[k] = mode_lib->vba.SwathWidthSingleDPPY[k] + * dml_ceil(mode_lib->vba.BytePerPixelDETY[k], 1) + / (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]) + * mode_lib->vba.VRatio[k]; + mode_lib->vba.ReadBandwidthPlaneChroma[k] = mode_lib->vba.SwathWidthSingleDPPY[k] + / 2 * dml_ceil(mode_lib->vba.BytePerPixelDETC[k], 2) + / (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]) + * mode_lib->vba.VRatio[k] / 2; + DTRACE( + " read_bw[%i] = %fBps", + k, + mode_lib->vba.ReadBandwidthPlaneLuma[k] + + mode_lib->vba.ReadBandwidthPlaneChroma[k]); + mode_lib->vba.TotalDataReadBandwidth += mode_lib->vba.ReadBandwidthPlaneLuma[k] + + mode_lib->vba.ReadBandwidthPlaneChroma[k]; + } + + mode_lib->vba.TotalDCCActiveDPP = 0; + mode_lib->vba.TotalActiveDPP = 0; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.TotalActiveDPP = mode_lib->vba.TotalActiveDPP + + mode_lib->vba.DPPPerPlane[k]; + if (mode_lib->vba.DCCEnable[k]) + mode_lib->vba.TotalDCCActiveDPP = mode_lib->vba.TotalDCCActiveDPP + + mode_lib->vba.DPPPerPlane[k]; + } + + mode_lib->vba.UrgentRoundTripAndOutOfOrderLatency = + (mode_lib->vba.RoundTripPingLatencyCycles + 32) / mode_lib->vba.DCFCLK + + mode_lib->vba.UrgentOutOfOrderReturnPerChannel + * mode_lib->vba.NumberOfChannels + / mode_lib->vba.ReturnBW; + + mode_lib->vba.LastPixelOfLineExtraWatermark = 0; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + double DataFabricLineDeliveryTimeLuma, DataFabricLineDeliveryTimeChroma; + + if (mode_lib->vba.VRatio[k] <= 1.0) + mode_lib->vba.DisplayPipeLineDeliveryTimeLuma[k] = + (double) mode_lib->vba.SwathWidthY[k] + * mode_lib->vba.DPPPerPlane[k] + / mode_lib->vba.HRatio[k] + / mode_lib->vba.PixelClock[k]; + else + mode_lib->vba.DisplayPipeLineDeliveryTimeLuma[k] = + (double) mode_lib->vba.SwathWidthY[k] + / mode_lib->vba.PSCL_THROUGHPUT_LUMA[k] + / mode_lib->vba.DPPCLK[k]; + + DataFabricLineDeliveryTimeLuma = mode_lib->vba.SwathWidthSingleDPPY[k] + * mode_lib->vba.SwathHeightY[k] + * dml_ceil(mode_lib->vba.BytePerPixelDETY[k], 1) + / (mode_lib->vba.ReturnBW * mode_lib->vba.ReadBandwidthPlaneLuma[k] + / mode_lib->vba.TotalDataReadBandwidth); + mode_lib->vba.LastPixelOfLineExtraWatermark = dml_max( + mode_lib->vba.LastPixelOfLineExtraWatermark, + DataFabricLineDeliveryTimeLuma + - mode_lib->vba.DisplayPipeLineDeliveryTimeLuma[k]); + + if (mode_lib->vba.BytePerPixelDETC[k] == 0) + mode_lib->vba.DisplayPipeLineDeliveryTimeChroma[k] = 0.0; + else if (mode_lib->vba.VRatio[k] / 2.0 <= 1.0) + mode_lib->vba.DisplayPipeLineDeliveryTimeChroma[k] = + mode_lib->vba.SwathWidthY[k] / 2.0 + * mode_lib->vba.DPPPerPlane[k] + / (mode_lib->vba.HRatio[k] / 2.0) + / mode_lib->vba.PixelClock[k]; + else + mode_lib->vba.DisplayPipeLineDeliveryTimeChroma[k] = + mode_lib->vba.SwathWidthY[k] / 2.0 + / mode_lib->vba.PSCL_THROUGHPUT_CHROMA[k] + / mode_lib->vba.DPPCLK[k]; + + DataFabricLineDeliveryTimeChroma = mode_lib->vba.SwathWidthSingleDPPY[k] / 2.0 + * mode_lib->vba.SwathHeightC[k] + * dml_ceil(mode_lib->vba.BytePerPixelDETC[k], 2) + / (mode_lib->vba.ReturnBW + * mode_lib->vba.ReadBandwidthPlaneChroma[k] + / mode_lib->vba.TotalDataReadBandwidth); + mode_lib->vba.LastPixelOfLineExtraWatermark = + dml_max( + mode_lib->vba.LastPixelOfLineExtraWatermark, + DataFabricLineDeliveryTimeChroma + - mode_lib->vba.DisplayPipeLineDeliveryTimeChroma[k]); + } + + mode_lib->vba.UrgentExtraLatency = mode_lib->vba.UrgentRoundTripAndOutOfOrderLatency + + (mode_lib->vba.TotalActiveDPP * mode_lib->vba.PixelChunkSizeInKByte + + mode_lib->vba.TotalDCCActiveDPP + * mode_lib->vba.MetaChunkSize) * 1024.0 + / mode_lib->vba.ReturnBW; + + if (mode_lib->vba.VirtualMemoryEnable) + mode_lib->vba.UrgentExtraLatency += mode_lib->vba.TotalActiveDPP + * mode_lib->vba.PTEChunkSize * 1024.0 / mode_lib->vba.ReturnBW; + + mode_lib->vba.UrgentWatermark = mode_lib->vba.UrgentLatency + + mode_lib->vba.LastPixelOfLineExtraWatermark + + mode_lib->vba.UrgentExtraLatency; + + DTRACE(" urgent_extra_latency = %fus", mode_lib->vba.UrgentExtraLatency); + DTRACE(" wm_urgent = %fus", mode_lib->vba.UrgentWatermark); + + mode_lib->vba.MemoryTripWatermark = mode_lib->vba.UrgentLatency; + + mode_lib->vba.TotalActiveWriteback = 0; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.WritebackEnable[k]) + mode_lib->vba.TotalActiveWriteback = mode_lib->vba.TotalActiveWriteback + 1; + } + + if (mode_lib->vba.TotalActiveWriteback <= 1) + mode_lib->vba.WritebackUrgentWatermark = mode_lib->vba.WritebackLatency; + else + mode_lib->vba.WritebackUrgentWatermark = mode_lib->vba.WritebackLatency + + mode_lib->vba.WritebackChunkSize * 1024.0 / 32 + / mode_lib->vba.SOCCLK; + + DTRACE(" wm_wb_urgent = %fus", mode_lib->vba.WritebackUrgentWatermark); + + // NB P-State/DRAM Clock Change Watermark + mode_lib->vba.DRAMClockChangeWatermark = mode_lib->vba.DRAMClockChangeLatency + + mode_lib->vba.UrgentWatermark; + + DTRACE(" wm_pstate_change = %fus", mode_lib->vba.DRAMClockChangeWatermark); + + DTRACE(" calculating wb pstate watermark"); + DTRACE(" total wb outputs %d", mode_lib->vba.TotalActiveWriteback); + DTRACE(" socclk frequency %f Mhz", mode_lib->vba.SOCCLK); + + if (mode_lib->vba.TotalActiveWriteback <= 1) + mode_lib->vba.WritebackDRAMClockChangeWatermark = + mode_lib->vba.DRAMClockChangeLatency + + mode_lib->vba.WritebackLatency; + else + mode_lib->vba.WritebackDRAMClockChangeWatermark = + mode_lib->vba.DRAMClockChangeLatency + + mode_lib->vba.WritebackLatency + + mode_lib->vba.WritebackChunkSize * 1024.0 / 32 + / mode_lib->vba.SOCCLK; + + DTRACE(" wm_wb_pstate %fus", mode_lib->vba.WritebackDRAMClockChangeWatermark); + + // Stutter Efficiency + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.LinesInDETY[k] = mode_lib->vba.DETBufferSizeY[k] + / mode_lib->vba.BytePerPixelDETY[k] / mode_lib->vba.SwathWidthY[k]; + mode_lib->vba.LinesInDETYRoundedDownToSwath[k] = dml_floor( + mode_lib->vba.LinesInDETY[k], + mode_lib->vba.SwathHeightY[k]); + mode_lib->vba.FullDETBufferingTimeY[k] = + mode_lib->vba.LinesInDETYRoundedDownToSwath[k] + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + / mode_lib->vba.VRatio[k]; + if (mode_lib->vba.BytePerPixelDETC[k] > 0) { + mode_lib->vba.LinesInDETC[k] = mode_lib->vba.DETBufferSizeC[k] + / mode_lib->vba.BytePerPixelDETC[k] + / (mode_lib->vba.SwathWidthY[k] / 2); + mode_lib->vba.LinesInDETCRoundedDownToSwath[k] = dml_floor( + mode_lib->vba.LinesInDETC[k], + mode_lib->vba.SwathHeightC[k]); + mode_lib->vba.FullDETBufferingTimeC[k] = + mode_lib->vba.LinesInDETCRoundedDownToSwath[k] + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + / (mode_lib->vba.VRatio[k] / 2); + } else { + mode_lib->vba.LinesInDETC[k] = 0; + mode_lib->vba.LinesInDETCRoundedDownToSwath[k] = 0; + mode_lib->vba.FullDETBufferingTimeC[k] = 999999; + } + } + + mode_lib->vba.MinFullDETBufferingTime = 999999.0; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.FullDETBufferingTimeY[k] + < mode_lib->vba.MinFullDETBufferingTime) { + mode_lib->vba.MinFullDETBufferingTime = + mode_lib->vba.FullDETBufferingTimeY[k]; + mode_lib->vba.FrameTimeForMinFullDETBufferingTime = + (double) mode_lib->vba.VTotal[k] * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]; + } + if (mode_lib->vba.FullDETBufferingTimeC[k] + < mode_lib->vba.MinFullDETBufferingTime) { + mode_lib->vba.MinFullDETBufferingTime = + mode_lib->vba.FullDETBufferingTimeC[k]; + mode_lib->vba.FrameTimeForMinFullDETBufferingTime = + (double) mode_lib->vba.VTotal[k] * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]; + } + } + + mode_lib->vba.AverageReadBandwidthGBytePerSecond = 0.0; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.DCCEnable[k]) { + mode_lib->vba.AverageReadBandwidthGBytePerSecond = + mode_lib->vba.AverageReadBandwidthGBytePerSecond + + mode_lib->vba.ReadBandwidthPlaneLuma[k] + / mode_lib->vba.DCCRate[k] + / 1000 + + mode_lib->vba.ReadBandwidthPlaneChroma[k] + / mode_lib->vba.DCCRate[k] + / 1000; + } else { + mode_lib->vba.AverageReadBandwidthGBytePerSecond = + mode_lib->vba.AverageReadBandwidthGBytePerSecond + + mode_lib->vba.ReadBandwidthPlaneLuma[k] + / 1000 + + mode_lib->vba.ReadBandwidthPlaneChroma[k] + / 1000; + } + if (mode_lib->vba.DCCEnable[k]) { + mode_lib->vba.AverageReadBandwidthGBytePerSecond = + mode_lib->vba.AverageReadBandwidthGBytePerSecond + + mode_lib->vba.ReadBandwidthPlaneLuma[k] + / 1000 / 256 + + mode_lib->vba.ReadBandwidthPlaneChroma[k] + / 1000 / 256; + } + if (mode_lib->vba.VirtualMemoryEnable) { + mode_lib->vba.AverageReadBandwidthGBytePerSecond = + mode_lib->vba.AverageReadBandwidthGBytePerSecond + + mode_lib->vba.ReadBandwidthPlaneLuma[k] + / 1000 / 512 + + mode_lib->vba.ReadBandwidthPlaneChroma[k] + / 1000 / 512; + } + } + + mode_lib->vba.PartOfBurstThatFitsInROB = + dml_min( + mode_lib->vba.MinFullDETBufferingTime + * mode_lib->vba.TotalDataReadBandwidth, + mode_lib->vba.ROBBufferSizeInKByte * 1024 + * mode_lib->vba.TotalDataReadBandwidth + / (mode_lib->vba.AverageReadBandwidthGBytePerSecond + * 1000)); + mode_lib->vba.StutterBurstTime = mode_lib->vba.PartOfBurstThatFitsInROB + * (mode_lib->vba.AverageReadBandwidthGBytePerSecond * 1000) + / mode_lib->vba.TotalDataReadBandwidth / mode_lib->vba.ReturnBW + + (mode_lib->vba.MinFullDETBufferingTime + * mode_lib->vba.TotalDataReadBandwidth + - mode_lib->vba.PartOfBurstThatFitsInROB) + / (mode_lib->vba.DCFCLK * 64); + if (mode_lib->vba.TotalActiveWriteback == 0) { + mode_lib->vba.StutterEfficiencyNotIncludingVBlank = (1 + - (mode_lib->vba.SRExitTime + mode_lib->vba.StutterBurstTime) + / mode_lib->vba.MinFullDETBufferingTime) * 100; + } else { + mode_lib->vba.StutterEfficiencyNotIncludingVBlank = 0; + } + + mode_lib->vba.SmallestVBlank = 999999; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.SynchronizedVBlank || mode_lib->vba.NumberOfActivePlanes == 1) { + mode_lib->vba.VBlankTime = (double) (mode_lib->vba.VTotal[k] + - mode_lib->vba.VActive[k]) * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]; + } else { + mode_lib->vba.VBlankTime = 0; + } + mode_lib->vba.SmallestVBlank = dml_min( + mode_lib->vba.SmallestVBlank, + mode_lib->vba.VBlankTime); + } + + mode_lib->vba.StutterEfficiency = (mode_lib->vba.StutterEfficiencyNotIncludingVBlank / 100 + * (mode_lib->vba.FrameTimeForMinFullDETBufferingTime + - mode_lib->vba.SmallestVBlank) + + mode_lib->vba.SmallestVBlank) + / mode_lib->vba.FrameTimeForMinFullDETBufferingTime * 100; + + // dml_ml->vba.DCFCLK Deep Sleep + mode_lib->vba.DCFClkDeepSleep = 8.0; + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; k++) { + if (mode_lib->vba.BytePerPixelDETC[k] > 0) { + mode_lib->vba.DCFCLKDeepSleepPerPlane = + dml_max( + 1.1 * mode_lib->vba.SwathWidthY[k] + * dml_ceil( + mode_lib->vba.BytePerPixelDETY[k], + 1) / 32 + / mode_lib->vba.DisplayPipeLineDeliveryTimeLuma[k], + 1.1 * mode_lib->vba.SwathWidthY[k] / 2.0 + * dml_ceil( + mode_lib->vba.BytePerPixelDETC[k], + 2) / 32 + / mode_lib->vba.DisplayPipeLineDeliveryTimeChroma[k]); + } else + mode_lib->vba.DCFCLKDeepSleepPerPlane = 1.1 * mode_lib->vba.SwathWidthY[k] + * dml_ceil(mode_lib->vba.BytePerPixelDETY[k], 1) / 64.0 + / mode_lib->vba.DisplayPipeLineDeliveryTimeLuma[k]; + mode_lib->vba.DCFCLKDeepSleepPerPlane = dml_max( + mode_lib->vba.DCFCLKDeepSleepPerPlane, + mode_lib->vba.PixelClock[k] / 16.0); + mode_lib->vba.DCFClkDeepSleep = dml_max( + mode_lib->vba.DCFClkDeepSleep, + mode_lib->vba.DCFCLKDeepSleepPerPlane); + + DTRACE( + " dcfclk_deepsleep_per_plane[%i] = %fMHz", + k, + mode_lib->vba.DCFCLKDeepSleepPerPlane); + } + + DTRACE(" dcfclk_deepsleep_mhz = %fMHz", mode_lib->vba.DCFClkDeepSleep); + + // Stutter Watermark + mode_lib->vba.StutterExitWatermark = mode_lib->vba.SRExitTime + + mode_lib->vba.LastPixelOfLineExtraWatermark + + mode_lib->vba.UrgentExtraLatency + 10 / mode_lib->vba.DCFClkDeepSleep; + mode_lib->vba.StutterEnterPlusExitWatermark = mode_lib->vba.SREnterPlusExitTime + + mode_lib->vba.LastPixelOfLineExtraWatermark + + mode_lib->vba.UrgentExtraLatency; + + DTRACE(" wm_cstate_exit = %fus", mode_lib->vba.StutterExitWatermark); + DTRACE(" wm_cstate_enter_exit = %fus", mode_lib->vba.StutterEnterPlusExitWatermark); + + // Urgent Latency Supported + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.EffectiveDETPlusLBLinesLuma = + dml_floor( + mode_lib->vba.LinesInDETY[k] + + dml_min( + mode_lib->vba.LinesInDETY[k] + * mode_lib->vba.DPPCLK[k] + * mode_lib->vba.BytePerPixelDETY[k] + * mode_lib->vba.PSCL_THROUGHPUT_LUMA[k] + / (mode_lib->vba.ReturnBW + / mode_lib->vba.DPPPerPlane[k]), + (double) mode_lib->vba.EffectiveLBLatencyHidingSourceLinesLuma), + mode_lib->vba.SwathHeightY[k]); + + mode_lib->vba.UrgentLatencySupportUsLuma = mode_lib->vba.EffectiveDETPlusLBLinesLuma + * (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]) + / mode_lib->vba.VRatio[k] + - mode_lib->vba.EffectiveDETPlusLBLinesLuma + * mode_lib->vba.SwathWidthY[k] + * mode_lib->vba.BytePerPixelDETY[k] + / (mode_lib->vba.ReturnBW + / mode_lib->vba.DPPPerPlane[k]); + + if (mode_lib->vba.BytePerPixelDETC[k] > 0) { + mode_lib->vba.EffectiveDETPlusLBLinesChroma = + dml_floor( + mode_lib->vba.LinesInDETC[k] + + dml_min( + mode_lib->vba.LinesInDETC[k] + * mode_lib->vba.DPPCLK[k] + * mode_lib->vba.BytePerPixelDETC[k] + * mode_lib->vba.PSCL_THROUGHPUT_CHROMA[k] + / (mode_lib->vba.ReturnBW + / mode_lib->vba.DPPPerPlane[k]), + (double) mode_lib->vba.EffectiveLBLatencyHidingSourceLinesChroma), + mode_lib->vba.SwathHeightC[k]); + mode_lib->vba.UrgentLatencySupportUsChroma = + mode_lib->vba.EffectiveDETPlusLBLinesChroma + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + / (mode_lib->vba.VRatio[k] / 2) + - mode_lib->vba.EffectiveDETPlusLBLinesChroma + * (mode_lib->vba.SwathWidthY[k] + / 2) + * mode_lib->vba.BytePerPixelDETC[k] + / (mode_lib->vba.ReturnBW + / mode_lib->vba.DPPPerPlane[k]); + mode_lib->vba.UrgentLatencySupportUs[k] = dml_min( + mode_lib->vba.UrgentLatencySupportUsLuma, + mode_lib->vba.UrgentLatencySupportUsChroma); + } else { + mode_lib->vba.UrgentLatencySupportUs[k] = + mode_lib->vba.UrgentLatencySupportUsLuma; + } + } + + mode_lib->vba.MinUrgentLatencySupportUs = 999999; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.MinUrgentLatencySupportUs = dml_min( + mode_lib->vba.MinUrgentLatencySupportUs, + mode_lib->vba.UrgentLatencySupportUs[k]); + } + + // Non-Urgent Latency Tolerance + mode_lib->vba.NonUrgentLatencyTolerance = mode_lib->vba.MinUrgentLatencySupportUs + - mode_lib->vba.UrgentWatermark; + + // DSCCLK + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if ((mode_lib->vba.BlendingAndTiming[k] != k) || !mode_lib->vba.DSCEnabled[k]) { + mode_lib->vba.DSCCLK_calculated[k] = 0.0; + } else { + if (mode_lib->vba.OutputFormat[k] == dm_420 + || mode_lib->vba.OutputFormat[k] == dm_n422) + mode_lib->vba.DSCFormatFactor = 2; + else + mode_lib->vba.DSCFormatFactor = 1; + if (mode_lib->vba.ODMCombineEnabled[k]) + mode_lib->vba.DSCCLK_calculated[k] = + mode_lib->vba.PixelClockBackEnd[k] / 6 + / mode_lib->vba.DSCFormatFactor + / (1 + - mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100); + else + mode_lib->vba.DSCCLK_calculated[k] = + mode_lib->vba.PixelClockBackEnd[k] / 3 + / mode_lib->vba.DSCFormatFactor + / (1 + - mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100); + } + } + + // DSC Delay + // TODO + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + double bpp = mode_lib->vba.OutputBpp[k]; + unsigned int slices = mode_lib->vba.NumberOfDSCSlices[k]; + + if (mode_lib->vba.DSCEnabled[k] && bpp != 0) { + if (!mode_lib->vba.ODMCombineEnabled[k]) { + mode_lib->vba.DSCDelay[k] = + dscceComputeDelay( + mode_lib->vba.DSCInputBitPerComponent[k], + bpp, + dml_ceil( + (double) mode_lib->vba.HActive[k] + / mode_lib->vba.NumberOfDSCSlices[k], + 1), + slices, + mode_lib->vba.OutputFormat[k]) + + dscComputeDelay( + mode_lib->vba.OutputFormat[k]); + } else { + mode_lib->vba.DSCDelay[k] = + 2 + * (dscceComputeDelay( + mode_lib->vba.DSCInputBitPerComponent[k], + bpp, + dml_ceil( + (double) mode_lib->vba.HActive[k] + / mode_lib->vba.NumberOfDSCSlices[k], + 1), + slices / 2.0, + mode_lib->vba.OutputFormat[k]) + + dscComputeDelay( + mode_lib->vba.OutputFormat[k])); + } + mode_lib->vba.DSCDelay[k] = mode_lib->vba.DSCDelay[k] + * mode_lib->vba.PixelClock[k] + / mode_lib->vba.PixelClockBackEnd[k]; + } else { + mode_lib->vba.DSCDelay[k] = 0; + } + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) + for (j = 0; j < mode_lib->vba.NumberOfActivePlanes; ++j) // NumberOfPlanes + if (j != k && mode_lib->vba.BlendingAndTiming[k] == j + && mode_lib->vba.DSCEnabled[j]) + mode_lib->vba.DSCDelay[k] = mode_lib->vba.DSCDelay[j]; + + // Prefetch + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + unsigned int PDEAndMetaPTEBytesFrameY; + unsigned int PixelPTEBytesPerRowY; + unsigned int MetaRowByteY; + unsigned int MetaRowByteC; + unsigned int PDEAndMetaPTEBytesFrameC; + unsigned int PixelPTEBytesPerRowC; + + Calculate256BBlockSizes( + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.SurfaceTiling[k], + dml_ceil(mode_lib->vba.BytePerPixelDETY[k], 1), + dml_ceil(mode_lib->vba.BytePerPixelDETC[k], 2), + &mode_lib->vba.BlockHeight256BytesY[k], + &mode_lib->vba.BlockHeight256BytesC[k], + &mode_lib->vba.BlockWidth256BytesY[k], + &mode_lib->vba.BlockWidth256BytesC[k]); + PDEAndMetaPTEBytesFrameY = CalculateVMAndRowBytes( + mode_lib, + mode_lib->vba.DCCEnable[k], + mode_lib->vba.BlockHeight256BytesY[k], + mode_lib->vba.BlockWidth256BytesY[k], + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.SurfaceTiling[k], + dml_ceil(mode_lib->vba.BytePerPixelDETY[k], 1), + mode_lib->vba.SourceScan[k], + mode_lib->vba.ViewportWidth[k], + mode_lib->vba.ViewportHeight[k], + mode_lib->vba.SwathWidthY[k], + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.VMMPageSize, + mode_lib->vba.PTEBufferSizeInRequests, + mode_lib->vba.PDEProcessingBufIn64KBReqs, + mode_lib->vba.PitchY[k], + mode_lib->vba.DCCMetaPitchY[k], + &mode_lib->vba.MacroTileWidthY[k], + &MetaRowByteY, + &PixelPTEBytesPerRowY, + &mode_lib->vba.PTEBufferSizeNotExceeded[mode_lib->vba.VoltageLevel], + &mode_lib->vba.dpte_row_height[k], + &mode_lib->vba.meta_row_height[k]); + mode_lib->vba.PrefetchSourceLinesY[k] = CalculatePrefetchSourceLines( + mode_lib, + mode_lib->vba.VRatio[k], + mode_lib->vba.vtaps[k], + mode_lib->vba.Interlace[k], + mode_lib->vba.ProgressiveToInterlaceUnitInOPP, + mode_lib->vba.SwathHeightY[k], + mode_lib->vba.ViewportYStartY[k], + &mode_lib->vba.VInitPreFillY[k], + &mode_lib->vba.MaxNumSwathY[k]); + + if ((mode_lib->vba.SourcePixelFormat[k] != dm_444_64 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_32 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_8)) { + PDEAndMetaPTEBytesFrameC = + CalculateVMAndRowBytes( + mode_lib, + mode_lib->vba.DCCEnable[k], + mode_lib->vba.BlockHeight256BytesC[k], + mode_lib->vba.BlockWidth256BytesC[k], + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.SurfaceTiling[k], + dml_ceil( + mode_lib->vba.BytePerPixelDETC[k], + 2), + mode_lib->vba.SourceScan[k], + mode_lib->vba.ViewportWidth[k] / 2, + mode_lib->vba.ViewportHeight[k] / 2, + mode_lib->vba.SwathWidthY[k] / 2, + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.VMMPageSize, + mode_lib->vba.PTEBufferSizeInRequests, + mode_lib->vba.PDEProcessingBufIn64KBReqs, + mode_lib->vba.PitchC[k], + 0, + &mode_lib->vba.MacroTileWidthC[k], + &MetaRowByteC, + &PixelPTEBytesPerRowC, + &mode_lib->vba.PTEBufferSizeNotExceeded[mode_lib->vba.VoltageLevel], + &mode_lib->vba.dpte_row_height_chroma[k], + &mode_lib->vba.meta_row_height_chroma[k]); + mode_lib->vba.PrefetchSourceLinesC[k] = CalculatePrefetchSourceLines( + mode_lib, + mode_lib->vba.VRatio[k] / 2, + mode_lib->vba.VTAPsChroma[k], + mode_lib->vba.Interlace[k], + mode_lib->vba.ProgressiveToInterlaceUnitInOPP, + mode_lib->vba.SwathHeightC[k], + mode_lib->vba.ViewportYStartC[k], + &mode_lib->vba.VInitPreFillC[k], + &mode_lib->vba.MaxNumSwathC[k]); + } else { + PixelPTEBytesPerRowC = 0; + PDEAndMetaPTEBytesFrameC = 0; + MetaRowByteC = 0; + mode_lib->vba.MaxNumSwathC[k] = 0; + mode_lib->vba.PrefetchSourceLinesC[k] = 0; + } + + mode_lib->vba.PixelPTEBytesPerRow[k] = PixelPTEBytesPerRowY + PixelPTEBytesPerRowC; + mode_lib->vba.PDEAndMetaPTEBytesFrame[k] = PDEAndMetaPTEBytesFrameY + + PDEAndMetaPTEBytesFrameC; + mode_lib->vba.MetaRowByte[k] = MetaRowByteY + MetaRowByteC; + + CalculateActiveRowBandwidth( + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.VRatio[k], + mode_lib->vba.DCCEnable[k], + mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k], + MetaRowByteY, + MetaRowByteC, + mode_lib->vba.meta_row_height[k], + mode_lib->vba.meta_row_height_chroma[k], + PixelPTEBytesPerRowY, + PixelPTEBytesPerRowC, + mode_lib->vba.dpte_row_height[k], + mode_lib->vba.dpte_row_height_chroma[k], + &mode_lib->vba.meta_row_bw[k], + &mode_lib->vba.dpte_row_bw[k], + &mode_lib->vba.qual_row_bw[k]); + } + + mode_lib->vba.TCalc = 24.0 / mode_lib->vba.DCFClkDeepSleep; + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.BlendingAndTiming[k] == k) { + if (mode_lib->vba.WritebackEnable[k] == true) { + mode_lib->vba.WritebackDelay[mode_lib->vba.VoltageLevel][k] = + mode_lib->vba.WritebackLatency + + CalculateWriteBackDelay( + mode_lib->vba.WritebackPixelFormat[k], + mode_lib->vba.WritebackHRatio[k], + mode_lib->vba.WritebackVRatio[k], + mode_lib->vba.WritebackLumaHTaps[k], + mode_lib->vba.WritebackLumaVTaps[k], + mode_lib->vba.WritebackChromaHTaps[k], + mode_lib->vba.WritebackChromaVTaps[k], + mode_lib->vba.WritebackDestinationWidth[k]) + / mode_lib->vba.DISPCLK; + } else + mode_lib->vba.WritebackDelay[mode_lib->vba.VoltageLevel][k] = 0; + for (j = 0; j < mode_lib->vba.NumberOfActivePlanes; ++j) { + if (mode_lib->vba.BlendingAndTiming[j] == k + && mode_lib->vba.WritebackEnable[j] == true) { + mode_lib->vba.WritebackDelay[mode_lib->vba.VoltageLevel][k] = + dml_max( + mode_lib->vba.WritebackDelay[mode_lib->vba.VoltageLevel][k], + mode_lib->vba.WritebackLatency + + CalculateWriteBackDelay( + mode_lib->vba.WritebackPixelFormat[j], + mode_lib->vba.WritebackHRatio[j], + mode_lib->vba.WritebackVRatio[j], + mode_lib->vba.WritebackLumaHTaps[j], + mode_lib->vba.WritebackLumaVTaps[j], + mode_lib->vba.WritebackChromaHTaps[j], + mode_lib->vba.WritebackChromaVTaps[j], + mode_lib->vba.WritebackDestinationWidth[j]) + / mode_lib->vba.DISPCLK); + } + } + } + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) + for (j = 0; j < mode_lib->vba.NumberOfActivePlanes; ++j) + if (mode_lib->vba.BlendingAndTiming[k] == j) + mode_lib->vba.WritebackDelay[mode_lib->vba.VoltageLevel][k] = + mode_lib->vba.WritebackDelay[mode_lib->vba.VoltageLevel][j]; + + mode_lib->vba.VStartupLines = 13; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.MaxVStartupLines[k] = + mode_lib->vba.VTotal[k] - mode_lib->vba.VActive[k] + - dml_max( + 1.0, + dml_ceil( + mode_lib->vba.WritebackDelay[mode_lib->vba.VoltageLevel][k] + / (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]), + 1)); + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) + mode_lib->vba.MaximumMaxVStartupLines = dml_max( + mode_lib->vba.MaximumMaxVStartupLines, + mode_lib->vba.MaxVStartupLines[k]); + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.cursor_bw[k] = 0.0; + for (j = 0; j < mode_lib->vba.NumberOfCursors[k]; ++j) + mode_lib->vba.cursor_bw[k] += mode_lib->vba.CursorWidth[k][j] + * mode_lib->vba.CursorBPP[k][j] / 8.0 + / (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]) + * mode_lib->vba.VRatio[k]; + } + + do { + double MaxTotalRDBandwidth = 0; + bool DestinationLineTimesForPrefetchLessThan2 = false; + bool VRatioPrefetchMoreThan4 = false; + bool prefetch_vm_bw_valid = true; + bool prefetch_row_bw_valid = true; + double TWait = CalculateTWait( + mode_lib->vba.PrefetchMode, + mode_lib->vba.DRAMClockChangeLatency, + mode_lib->vba.UrgentLatency, + mode_lib->vba.SREnterPlusExitTime); + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.XFCEnabled[k] == true) { + mode_lib->vba.XFCRemoteSurfaceFlipDelay = + CalculateRemoteSurfaceFlipDelay( + mode_lib, + mode_lib->vba.VRatio[k], + mode_lib->vba.SwathWidthY[k], + dml_ceil( + mode_lib->vba.BytePerPixelDETY[k], + 1), + mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k], + mode_lib->vba.XFCTSlvVupdateOffset, + mode_lib->vba.XFCTSlvVupdateWidth, + mode_lib->vba.XFCTSlvVreadyOffset, + mode_lib->vba.XFCXBUFLatencyTolerance, + mode_lib->vba.XFCFillBWOverhead, + mode_lib->vba.XFCSlvChunkSize, + mode_lib->vba.XFCBusTransportTime, + mode_lib->vba.TCalc, + TWait, + &mode_lib->vba.SrcActiveDrainRate, + &mode_lib->vba.TInitXFill, + &mode_lib->vba.TslvChk); + } else { + mode_lib->vba.XFCRemoteSurfaceFlipDelay = 0; + } + mode_lib->vba.ErrorResult[k] = + CalculatePrefetchSchedule( + mode_lib, + mode_lib->vba.DPPCLK[k], + mode_lib->vba.DISPCLK, + mode_lib->vba.PixelClock[k], + mode_lib->vba.DCFClkDeepSleep, + mode_lib->vba.DSCDelay[k], + mode_lib->vba.DPPPerPlane[k], + mode_lib->vba.ScalerEnabled[k], + mode_lib->vba.NumberOfCursors[k], + mode_lib->vba.DPPCLKDelaySubtotal, + mode_lib->vba.DPPCLKDelaySCL, + mode_lib->vba.DPPCLKDelaySCLLBOnly, + mode_lib->vba.DPPCLKDelayCNVCFormater, + mode_lib->vba.DPPCLKDelayCNVCCursor, + mode_lib->vba.DISPCLKDelaySubtotal, + (unsigned int) (mode_lib->vba.SwathWidthY[k] + / mode_lib->vba.HRatio[k]), + mode_lib->vba.OutputFormat[k], + mode_lib->vba.VTotal[k] + - mode_lib->vba.VActive[k], + mode_lib->vba.HTotal[k], + mode_lib->vba.MaxInterDCNTileRepeaters, + dml_min( + mode_lib->vba.VStartupLines, + mode_lib->vba.MaxVStartupLines[k]), + mode_lib->vba.MaxPageTableLevels, + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.DynamicMetadataEnable[k], + mode_lib->vba.DynamicMetadataLinesBeforeActiveRequired[k], + mode_lib->vba.DynamicMetadataTransmittedBytes[k], + mode_lib->vba.DCCEnable[k], + mode_lib->vba.UrgentLatency, + mode_lib->vba.UrgentExtraLatency, + mode_lib->vba.TCalc, + mode_lib->vba.PDEAndMetaPTEBytesFrame[k], + mode_lib->vba.MetaRowByte[k], + mode_lib->vba.PixelPTEBytesPerRow[k], + mode_lib->vba.PrefetchSourceLinesY[k], + mode_lib->vba.SwathWidthY[k], + mode_lib->vba.BytePerPixelDETY[k], + mode_lib->vba.VInitPreFillY[k], + mode_lib->vba.MaxNumSwathY[k], + mode_lib->vba.PrefetchSourceLinesC[k], + mode_lib->vba.BytePerPixelDETC[k], + mode_lib->vba.VInitPreFillC[k], + mode_lib->vba.MaxNumSwathC[k], + mode_lib->vba.SwathHeightY[k], + mode_lib->vba.SwathHeightC[k], + TWait, + mode_lib->vba.XFCEnabled[k], + mode_lib->vba.XFCRemoteSurfaceFlipDelay, + mode_lib->vba.Interlace[k], + mode_lib->vba.ProgressiveToInterlaceUnitInOPP, + &mode_lib->vba.DSTXAfterScaler[k], + &mode_lib->vba.DSTYAfterScaler[k], + &mode_lib->vba.DestinationLinesForPrefetch[k], + &mode_lib->vba.PrefetchBandwidth[k], + &mode_lib->vba.DestinationLinesToRequestVMInVBlank[k], + &mode_lib->vba.DestinationLinesToRequestRowInVBlank[k], + &mode_lib->vba.VRatioPrefetchY[k], + &mode_lib->vba.VRatioPrefetchC[k], + &mode_lib->vba.RequiredPrefetchPixDataBW[k], + &mode_lib->vba.VStartupRequiredWhenNotEnoughTimeForDynamicMetadata, + &mode_lib->vba.Tno_bw[k], + &mode_lib->vba.VUpdateOffsetPix[k], + &mode_lib->vba.VUpdateWidthPix[k], + &mode_lib->vba.VReadyOffsetPix[k]); + if (mode_lib->vba.BlendingAndTiming[k] == k) { + mode_lib->vba.VStartup[k] = dml_min( + mode_lib->vba.VStartupLines, + mode_lib->vba.MaxVStartupLines[k]); + if (mode_lib->vba.VStartupRequiredWhenNotEnoughTimeForDynamicMetadata + != 0) { + mode_lib->vba.VStartup[k] = + mode_lib->vba.VStartupRequiredWhenNotEnoughTimeForDynamicMetadata; + } + } else { + mode_lib->vba.VStartup[k] = + dml_min( + mode_lib->vba.VStartupLines, + mode_lib->vba.MaxVStartupLines[mode_lib->vba.BlendingAndTiming[k]]); + } + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + + if (mode_lib->vba.PDEAndMetaPTEBytesFrame[k] == 0) + mode_lib->vba.prefetch_vm_bw[k] = 0; + else if (mode_lib->vba.DestinationLinesToRequestVMInVBlank[k] > 0) { + mode_lib->vba.prefetch_vm_bw[k] = + (double) mode_lib->vba.PDEAndMetaPTEBytesFrame[k] + / (mode_lib->vba.DestinationLinesToRequestVMInVBlank[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + } else { + mode_lib->vba.prefetch_vm_bw[k] = 0; + prefetch_vm_bw_valid = false; + } + if (mode_lib->vba.MetaRowByte[k] + mode_lib->vba.PixelPTEBytesPerRow[k] + == 0) + mode_lib->vba.prefetch_row_bw[k] = 0; + else if (mode_lib->vba.DestinationLinesToRequestRowInVBlank[k] > 0) { + mode_lib->vba.prefetch_row_bw[k] = + (double) (mode_lib->vba.MetaRowByte[k] + + mode_lib->vba.PixelPTEBytesPerRow[k]) + / (mode_lib->vba.DestinationLinesToRequestRowInVBlank[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + } else { + mode_lib->vba.prefetch_row_bw[k] = 0; + prefetch_row_bw_valid = false; + } + + MaxTotalRDBandwidth = + MaxTotalRDBandwidth + mode_lib->vba.cursor_bw[k] + + dml_max( + mode_lib->vba.prefetch_vm_bw[k], + dml_max( + mode_lib->vba.prefetch_row_bw[k], + dml_max( + mode_lib->vba.ReadBandwidthPlaneLuma[k] + + mode_lib->vba.ReadBandwidthPlaneChroma[k], + mode_lib->vba.RequiredPrefetchPixDataBW[k]) + + mode_lib->vba.meta_row_bw[k] + + mode_lib->vba.dpte_row_bw[k])); + + if (mode_lib->vba.DestinationLinesForPrefetch[k] < 2) + DestinationLineTimesForPrefetchLessThan2 = true; + if (mode_lib->vba.VRatioPrefetchY[k] > 4 + || mode_lib->vba.VRatioPrefetchC[k] > 4) + VRatioPrefetchMoreThan4 = true; + } + + if (MaxTotalRDBandwidth <= mode_lib->vba.ReturnBW && prefetch_vm_bw_valid + && prefetch_row_bw_valid && !VRatioPrefetchMoreThan4 + && !DestinationLineTimesForPrefetchLessThan2) + mode_lib->vba.PrefetchModeSupported = true; + else { + mode_lib->vba.PrefetchModeSupported = false; + dml_print( + "DML: CalculatePrefetchSchedule ***failed***. Bandwidth violation. Results are NOT valid\n"); + } + + if (mode_lib->vba.PrefetchModeSupported == true) { + double final_flip_bw[DC__NUM_DPP__MAX]; + unsigned int ImmediateFlipBytes[DC__NUM_DPP__MAX]; + double total_dcn_read_bw_with_flip = 0; + + mode_lib->vba.BandwidthAvailableForImmediateFlip = mode_lib->vba.ReturnBW; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.BandwidthAvailableForImmediateFlip = + mode_lib->vba.BandwidthAvailableForImmediateFlip + - mode_lib->vba.cursor_bw[k] + - dml_max( + mode_lib->vba.ReadBandwidthPlaneLuma[k] + + mode_lib->vba.ReadBandwidthPlaneChroma[k] + + mode_lib->vba.qual_row_bw[k], + mode_lib->vba.PrefetchBandwidth[k]); + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + ImmediateFlipBytes[k] = 0; + if ((mode_lib->vba.SourcePixelFormat[k] != dm_420_8 + && mode_lib->vba.SourcePixelFormat[k] != dm_420_10)) { + ImmediateFlipBytes[k] = + mode_lib->vba.PDEAndMetaPTEBytesFrame[k] + + mode_lib->vba.MetaRowByte[k] + + mode_lib->vba.PixelPTEBytesPerRow[k]; + } + } + mode_lib->vba.TotImmediateFlipBytes = 0; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if ((mode_lib->vba.SourcePixelFormat[k] != dm_420_8 + && mode_lib->vba.SourcePixelFormat[k] != dm_420_10)) { + mode_lib->vba.TotImmediateFlipBytes = + mode_lib->vba.TotImmediateFlipBytes + + ImmediateFlipBytes[k]; + } + } + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + CalculateFlipSchedule( + mode_lib, + mode_lib->vba.UrgentExtraLatency, + mode_lib->vba.UrgentLatency, + mode_lib->vba.MaxPageTableLevels, + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.BandwidthAvailableForImmediateFlip, + mode_lib->vba.TotImmediateFlipBytes, + mode_lib->vba.SourcePixelFormat[k], + ImmediateFlipBytes[k], + mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k], + mode_lib->vba.VRatio[k], + mode_lib->vba.Tno_bw[k], + mode_lib->vba.PDEAndMetaPTEBytesFrame[k], + mode_lib->vba.MetaRowByte[k], + mode_lib->vba.PixelPTEBytesPerRow[k], + mode_lib->vba.DCCEnable[k], + mode_lib->vba.dpte_row_height[k], + mode_lib->vba.meta_row_height[k], + mode_lib->vba.qual_row_bw[k], + &mode_lib->vba.DestinationLinesToRequestVMInImmediateFlip[k], + &mode_lib->vba.DestinationLinesToRequestRowInImmediateFlip[k], + &final_flip_bw[k], + &mode_lib->vba.ImmediateFlipSupportedForPipe[k]); + } + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + total_dcn_read_bw_with_flip = + total_dcn_read_bw_with_flip + + mode_lib->vba.cursor_bw[k] + + dml_max( + mode_lib->vba.prefetch_vm_bw[k], + dml_max( + mode_lib->vba.prefetch_row_bw[k], + final_flip_bw[k] + + dml_max( + mode_lib->vba.ReadBandwidthPlaneLuma[k] + + mode_lib->vba.ReadBandwidthPlaneChroma[k], + mode_lib->vba.RequiredPrefetchPixDataBW[k]))); + } + mode_lib->vba.ImmediateFlipSupported = true; + if (total_dcn_read_bw_with_flip > mode_lib->vba.ReturnBW) { + mode_lib->vba.ImmediateFlipSupported = false; + } + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.ImmediateFlipSupportedForPipe[k] == false) { + mode_lib->vba.ImmediateFlipSupported = false; + } + } + } else { + mode_lib->vba.ImmediateFlipSupported = false; + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.ErrorResult[k]) { + mode_lib->vba.PrefetchModeSupported = false; + dml_print( + "DML: CalculatePrefetchSchedule ***failed***. Prefetch schedule violation. Results are NOT valid\n"); + } + } + + mode_lib->vba.VStartupLines = mode_lib->vba.VStartupLines + 1; + } while (!((mode_lib->vba.PrefetchModeSupported + && (!mode_lib->vba.ImmediateFlipSupport + || mode_lib->vba.ImmediateFlipSupported)) + || mode_lib->vba.MaximumMaxVStartupLines < mode_lib->vba.VStartupLines)); + + //Display Pipeline Delivery Time in Prefetch + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.VRatioPrefetchY[k] <= 1) { + mode_lib->vba.DisplayPipeLineDeliveryTimeLumaPrefetch[k] = + mode_lib->vba.SwathWidthY[k] * mode_lib->vba.DPPPerPlane[k] + / mode_lib->vba.HRatio[k] + / mode_lib->vba.PixelClock[k]; + } else { + mode_lib->vba.DisplayPipeLineDeliveryTimeLumaPrefetch[k] = + mode_lib->vba.SwathWidthY[k] + / mode_lib->vba.PSCL_THROUGHPUT_LUMA[k] + / mode_lib->vba.DPPCLK[k]; + } + if (mode_lib->vba.BytePerPixelDETC[k] == 0) { + mode_lib->vba.DisplayPipeLineDeliveryTimeChromaPrefetch[k] = 0; + } else { + if (mode_lib->vba.VRatioPrefetchC[k] <= 1) { + mode_lib->vba.DisplayPipeLineDeliveryTimeChromaPrefetch[k] = + mode_lib->vba.SwathWidthY[k] + * mode_lib->vba.DPPPerPlane[k] + / mode_lib->vba.HRatio[k] + / mode_lib->vba.PixelClock[k]; + } else { + mode_lib->vba.DisplayPipeLineDeliveryTimeChromaPrefetch[k] = + mode_lib->vba.SwathWidthY[k] + / mode_lib->vba.PSCL_THROUGHPUT_LUMA[k] + / mode_lib->vba.DPPCLK[k]; + } + } + } + + // Min TTUVBlank + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.PrefetchMode == 0) { + mode_lib->vba.AllowDRAMClockChangeDuringVBlank[k] = true; + mode_lib->vba.AllowDRAMSelfRefreshDuringVBlank[k] = true; + mode_lib->vba.MinTTUVBlank[k] = dml_max( + mode_lib->vba.DRAMClockChangeWatermark, + dml_max( + mode_lib->vba.StutterEnterPlusExitWatermark, + mode_lib->vba.UrgentWatermark)); + } else if (mode_lib->vba.PrefetchMode == 1) { + mode_lib->vba.AllowDRAMClockChangeDuringVBlank[k] = false; + mode_lib->vba.AllowDRAMSelfRefreshDuringVBlank[k] = true; + mode_lib->vba.MinTTUVBlank[k] = dml_max( + mode_lib->vba.StutterEnterPlusExitWatermark, + mode_lib->vba.UrgentWatermark); + } else { + mode_lib->vba.AllowDRAMClockChangeDuringVBlank[k] = false; + mode_lib->vba.AllowDRAMSelfRefreshDuringVBlank[k] = false; + mode_lib->vba.MinTTUVBlank[k] = mode_lib->vba.UrgentWatermark; + } + if (!mode_lib->vba.DynamicMetadataEnable[k]) + mode_lib->vba.MinTTUVBlank[k] = mode_lib->vba.TCalc + + mode_lib->vba.MinTTUVBlank[k]; + } + + // DCC Configuration + mode_lib->vba.ActiveDPPs = 0; + // NB P-State/DRAM Clock Change Support + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.ActiveDPPs = mode_lib->vba.ActiveDPPs + mode_lib->vba.DPPPerPlane[k]; + } + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + double EffectiveLBLatencyHidingY; + double EffectiveLBLatencyHidingC; + double DPPOutputBufferLinesY; + double DPPOutputBufferLinesC; + double DPPOPPBufferingY; + double MaxDETBufferingTimeY; + double ActiveDRAMClockChangeLatencyMarginY; + + mode_lib->vba.LBLatencyHidingSourceLinesY = + dml_min( + mode_lib->vba.MaxLineBufferLines, + (unsigned int) dml_floor( + (double) mode_lib->vba.LineBufferSize + / mode_lib->vba.LBBitPerPixel[k] + / (mode_lib->vba.SwathWidthY[k] + / dml_max( + mode_lib->vba.HRatio[k], + 1.0)), + 1)) - (mode_lib->vba.vtaps[k] - 1); + + mode_lib->vba.LBLatencyHidingSourceLinesC = + dml_min( + mode_lib->vba.MaxLineBufferLines, + (unsigned int) dml_floor( + (double) mode_lib->vba.LineBufferSize + / mode_lib->vba.LBBitPerPixel[k] + / (mode_lib->vba.SwathWidthY[k] + / 2.0 + / dml_max( + mode_lib->vba.HRatio[k] + / 2, + 1.0)), + 1)) + - (mode_lib->vba.VTAPsChroma[k] - 1); + + EffectiveLBLatencyHidingY = mode_lib->vba.LBLatencyHidingSourceLinesY + / mode_lib->vba.VRatio[k] + * (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]); + + EffectiveLBLatencyHidingC = mode_lib->vba.LBLatencyHidingSourceLinesC + / (mode_lib->vba.VRatio[k] / 2) + * (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]); + + if (mode_lib->vba.SwathWidthY[k] > 2 * mode_lib->vba.DPPOutputBufferPixels) { + DPPOutputBufferLinesY = mode_lib->vba.DPPOutputBufferPixels + / mode_lib->vba.SwathWidthY[k]; + } else if (mode_lib->vba.SwathWidthY[k] > mode_lib->vba.DPPOutputBufferPixels) { + DPPOutputBufferLinesY = 0.5; + } else { + DPPOutputBufferLinesY = 1; + } + + if (mode_lib->vba.SwathWidthY[k] / 2 > 2 * mode_lib->vba.DPPOutputBufferPixels) { + DPPOutputBufferLinesC = mode_lib->vba.DPPOutputBufferPixels + / (mode_lib->vba.SwathWidthY[k] / 2); + } else if (mode_lib->vba.SwathWidthY[k] / 2 > mode_lib->vba.DPPOutputBufferPixels) { + DPPOutputBufferLinesC = 0.5; + } else { + DPPOutputBufferLinesC = 1; + } + + DPPOPPBufferingY = (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]) + * (DPPOutputBufferLinesY + mode_lib->vba.OPPOutputBufferLines); + MaxDETBufferingTimeY = mode_lib->vba.FullDETBufferingTimeY[k] + + (mode_lib->vba.LinesInDETY[k] + - mode_lib->vba.LinesInDETYRoundedDownToSwath[k]) + / mode_lib->vba.SwathHeightY[k] + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + + ActiveDRAMClockChangeLatencyMarginY = DPPOPPBufferingY + EffectiveLBLatencyHidingY + + MaxDETBufferingTimeY - mode_lib->vba.DRAMClockChangeWatermark; + + if (mode_lib->vba.ActiveDPPs > 1) { + ActiveDRAMClockChangeLatencyMarginY = + ActiveDRAMClockChangeLatencyMarginY + - (1 - 1 / (mode_lib->vba.ActiveDPPs - 1)) + * mode_lib->vba.SwathHeightY[k] + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + } + + if (mode_lib->vba.BytePerPixelDETC[k] > 0) { + double DPPOPPBufferingC = (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + * (DPPOutputBufferLinesC + + mode_lib->vba.OPPOutputBufferLines); + double MaxDETBufferingTimeC = + mode_lib->vba.FullDETBufferingTimeC[k] + + (mode_lib->vba.LinesInDETC[k] + - mode_lib->vba.LinesInDETCRoundedDownToSwath[k]) + / mode_lib->vba.SwathHeightC[k] + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + double ActiveDRAMClockChangeLatencyMarginC = DPPOPPBufferingC + + EffectiveLBLatencyHidingC + MaxDETBufferingTimeC + - mode_lib->vba.DRAMClockChangeWatermark; + + if (mode_lib->vba.ActiveDPPs > 1) { + ActiveDRAMClockChangeLatencyMarginC = + ActiveDRAMClockChangeLatencyMarginC + - (1 + - 1 + / (mode_lib->vba.ActiveDPPs + - 1)) + * mode_lib->vba.SwathHeightC[k] + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + } + mode_lib->vba.ActiveDRAMClockChangeLatencyMargin[k] = dml_min( + ActiveDRAMClockChangeLatencyMarginY, + ActiveDRAMClockChangeLatencyMarginC); + } else { + mode_lib->vba.ActiveDRAMClockChangeLatencyMargin[k] = + ActiveDRAMClockChangeLatencyMarginY; + } + + if (mode_lib->vba.WritebackEnable[k]) { + double WritebackDRAMClockChangeLatencyMargin; + + if (mode_lib->vba.WritebackPixelFormat[k] == dm_444_32) { + WritebackDRAMClockChangeLatencyMargin = + (double) (mode_lib->vba.WritebackInterfaceLumaBufferSize + + mode_lib->vba.WritebackInterfaceChromaBufferSize) + / (mode_lib->vba.WritebackDestinationWidth[k] + * mode_lib->vba.WritebackDestinationHeight[k] + / (mode_lib->vba.WritebackSourceHeight[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + * 4) + - mode_lib->vba.WritebackDRAMClockChangeWatermark; + } else if (mode_lib->vba.WritebackPixelFormat[k] == dm_420_10) { + WritebackDRAMClockChangeLatencyMargin = + dml_min( + (double) mode_lib->vba.WritebackInterfaceLumaBufferSize + * 8.0 / 10, + 2.0 + * mode_lib->vba.WritebackInterfaceChromaBufferSize + * 8 / 10) + / (mode_lib->vba.WritebackDestinationWidth[k] + * mode_lib->vba.WritebackDestinationHeight[k] + / (mode_lib->vba.WritebackSourceHeight[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k])) + - mode_lib->vba.WritebackDRAMClockChangeWatermark; + } else { + WritebackDRAMClockChangeLatencyMargin = + dml_min( + (double) mode_lib->vba.WritebackInterfaceLumaBufferSize, + 2.0 + * mode_lib->vba.WritebackInterfaceChromaBufferSize) + / (mode_lib->vba.WritebackDestinationWidth[k] + * mode_lib->vba.WritebackDestinationHeight[k] + / (mode_lib->vba.WritebackSourceHeight[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k])) + - mode_lib->vba.WritebackDRAMClockChangeWatermark; + } + mode_lib->vba.ActiveDRAMClockChangeLatencyMargin[k] = dml_min( + mode_lib->vba.ActiveDRAMClockChangeLatencyMargin[k], + WritebackDRAMClockChangeLatencyMargin); + } + } + + mode_lib->vba.MinActiveDRAMClockChangeMargin = 999999; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.ActiveDRAMClockChangeLatencyMargin[k] + < mode_lib->vba.MinActiveDRAMClockChangeMargin) { + mode_lib->vba.MinActiveDRAMClockChangeMargin = + mode_lib->vba.ActiveDRAMClockChangeLatencyMargin[k]; + } + } + + mode_lib->vba.MinActiveDRAMClockChangeLatencySupported = + mode_lib->vba.MinActiveDRAMClockChangeMargin + + mode_lib->vba.DRAMClockChangeLatency; + + if (mode_lib->vba.MinActiveDRAMClockChangeMargin > 0) { + mode_lib->vba.DRAMClockChangeSupport = dm_dram_clock_change_vactive; + } else { + if (mode_lib->vba.SynchronizedVBlank || mode_lib->vba.NumberOfActivePlanes == 1) { + mode_lib->vba.DRAMClockChangeSupport = dm_dram_clock_change_vblank; + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (!mode_lib->vba.AllowDRAMClockChangeDuringVBlank[k]) { + mode_lib->vba.DRAMClockChangeSupport = + dm_dram_clock_change_unsupported; + } + } + } else { + mode_lib->vba.DRAMClockChangeSupport = dm_dram_clock_change_unsupported; + } + } + + //XFC Parameters: + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + if (mode_lib->vba.XFCEnabled[k] == true) { + double TWait; + + mode_lib->vba.XFCSlaveVUpdateOffset[k] = mode_lib->vba.XFCTSlvVupdateOffset; + mode_lib->vba.XFCSlaveVupdateWidth[k] = mode_lib->vba.XFCTSlvVupdateWidth; + mode_lib->vba.XFCSlaveVReadyOffset[k] = mode_lib->vba.XFCTSlvVreadyOffset; + TWait = CalculateTWait( + mode_lib->vba.PrefetchMode, + mode_lib->vba.DRAMClockChangeLatency, + mode_lib->vba.UrgentLatency, + mode_lib->vba.SREnterPlusExitTime); + mode_lib->vba.XFCRemoteSurfaceFlipDelay = CalculateRemoteSurfaceFlipDelay( + mode_lib, + mode_lib->vba.VRatio[k], + mode_lib->vba.SwathWidthY[k], + dml_ceil(mode_lib->vba.BytePerPixelDETY[k], 1), + mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k], + mode_lib->vba.XFCTSlvVupdateOffset, + mode_lib->vba.XFCTSlvVupdateWidth, + mode_lib->vba.XFCTSlvVreadyOffset, + mode_lib->vba.XFCXBUFLatencyTolerance, + mode_lib->vba.XFCFillBWOverhead, + mode_lib->vba.XFCSlvChunkSize, + mode_lib->vba.XFCBusTransportTime, + mode_lib->vba.TCalc, + TWait, + &mode_lib->vba.SrcActiveDrainRate, + &mode_lib->vba.TInitXFill, + &mode_lib->vba.TslvChk); + mode_lib->vba.XFCRemoteSurfaceFlipLatency[k] = + dml_floor( + mode_lib->vba.XFCRemoteSurfaceFlipDelay + / (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]), + 1); + mode_lib->vba.XFCTransferDelay[k] = + dml_ceil( + mode_lib->vba.XFCBusTransportTime + / (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]), + 1); + mode_lib->vba.XFCPrechargeDelay[k] = + dml_ceil( + (mode_lib->vba.XFCBusTransportTime + + mode_lib->vba.TInitXFill + + mode_lib->vba.TslvChk) + / (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]), + 1); + mode_lib->vba.InitFillLevel = mode_lib->vba.XFCXBUFLatencyTolerance + * mode_lib->vba.SrcActiveDrainRate; + mode_lib->vba.FinalFillMargin = + (mode_lib->vba.DestinationLinesToRequestVMInVBlank[k] + + mode_lib->vba.DestinationLinesToRequestRowInVBlank[k]) + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k] + * mode_lib->vba.SrcActiveDrainRate + + mode_lib->vba.XFCFillConstant; + mode_lib->vba.FinalFillLevel = mode_lib->vba.XFCRemoteSurfaceFlipDelay + * mode_lib->vba.SrcActiveDrainRate + + mode_lib->vba.FinalFillMargin; + mode_lib->vba.RemainingFillLevel = dml_max( + 0.0, + mode_lib->vba.FinalFillLevel - mode_lib->vba.InitFillLevel); + mode_lib->vba.TFinalxFill = mode_lib->vba.RemainingFillLevel + / (mode_lib->vba.SrcActiveDrainRate + * mode_lib->vba.XFCFillBWOverhead / 100); + mode_lib->vba.XFCPrefetchMargin[k] = + mode_lib->vba.XFCRemoteSurfaceFlipDelay + + mode_lib->vba.TFinalxFill + + (mode_lib->vba.DestinationLinesToRequestVMInVBlank[k] + + mode_lib->vba.DestinationLinesToRequestRowInVBlank[k]) + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]; + } else { + mode_lib->vba.XFCSlaveVUpdateOffset[k] = 0; + mode_lib->vba.XFCSlaveVupdateWidth[k] = 0; + mode_lib->vba.XFCSlaveVReadyOffset[k] = 0; + mode_lib->vba.XFCRemoteSurfaceFlipLatency[k] = 0; + mode_lib->vba.XFCPrechargeDelay[k] = 0; + mode_lib->vba.XFCTransferDelay[k] = 0; + mode_lib->vba.XFCPrefetchMargin[k] = 0; + } + } +} + +static void DisplayPipeConfiguration(struct display_mode_lib *mode_lib) +{ + double BytePerPixDETY; + double BytePerPixDETC; + double Read256BytesBlockHeightY; + double Read256BytesBlockHeightC; + double Read256BytesBlockWidthY; + double Read256BytesBlockWidthC; + double MaximumSwathHeightY; + double MaximumSwathHeightC; + double MinimumSwathHeightY; + double MinimumSwathHeightC; + double SwathWidth; + double SwathWidthGranularityY; + double SwathWidthGranularityC; + double RoundedUpMaxSwathSizeBytesY; + double RoundedUpMaxSwathSizeBytesC; + unsigned int j, k; + + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + bool MainPlaneDoesODMCombine = false; + + if (mode_lib->vba.SourcePixelFormat[k] == dm_444_64) { + BytePerPixDETY = 8; + BytePerPixDETC = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_32) { + BytePerPixDETY = 4; + BytePerPixDETC = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_16) { + BytePerPixDETY = 2; + BytePerPixDETC = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_8) { + BytePerPixDETY = 1; + BytePerPixDETC = 0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_8) { + BytePerPixDETY = 1; + BytePerPixDETC = 2; + } else { + BytePerPixDETY = 4.0 / 3.0; + BytePerPixDETC = 8.0 / 3.0; + } + + if ((mode_lib->vba.SourcePixelFormat[k] == dm_444_64 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_32 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_16 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_8)) { + if (mode_lib->vba.SurfaceTiling[k] == dm_sw_linear) { + Read256BytesBlockHeightY = 1; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_64) { + Read256BytesBlockHeightY = 4; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_32 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_16) { + Read256BytesBlockHeightY = 8; + } else { + Read256BytesBlockHeightY = 16; + } + Read256BytesBlockWidthY = 256 / dml_ceil(BytePerPixDETY, 1) + / Read256BytesBlockHeightY; + Read256BytesBlockHeightC = 0; + Read256BytesBlockWidthC = 0; + } else { + if (mode_lib->vba.SurfaceTiling[k] == dm_sw_linear) { + Read256BytesBlockHeightY = 1; + Read256BytesBlockHeightC = 1; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_8) { + Read256BytesBlockHeightY = 16; + Read256BytesBlockHeightC = 8; + } else { + Read256BytesBlockHeightY = 8; + Read256BytesBlockHeightC = 8; + } + Read256BytesBlockWidthY = 256 / dml_ceil(BytePerPixDETY, 1) + / Read256BytesBlockHeightY; + Read256BytesBlockWidthC = 256 / dml_ceil(BytePerPixDETC, 2) + / Read256BytesBlockHeightC; + } + + if (mode_lib->vba.SourceScan[k] == dm_horz) { + MaximumSwathHeightY = Read256BytesBlockHeightY; + MaximumSwathHeightC = Read256BytesBlockHeightC; + } else { + MaximumSwathHeightY = Read256BytesBlockWidthY; + MaximumSwathHeightC = Read256BytesBlockWidthC; + } + + if ((mode_lib->vba.SourcePixelFormat[k] == dm_444_64 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_32 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_16 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_8)) { + if (mode_lib->vba.SurfaceTiling[k] == dm_sw_linear + || (mode_lib->vba.SourcePixelFormat[k] == dm_444_64 + && (mode_lib->vba.SurfaceTiling[k] + == dm_sw_4kb_s + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_4kb_s_x + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_64kb_s + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_64kb_s_t + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_64kb_s_x + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_var_s + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_var_s_x) + && mode_lib->vba.SourceScan[k] == dm_horz)) { + MinimumSwathHeightY = MaximumSwathHeightY; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_8 + && mode_lib->vba.SourceScan[k] != dm_horz) { + MinimumSwathHeightY = MaximumSwathHeightY; + } else { + MinimumSwathHeightY = MaximumSwathHeightY / 2.0; + } + MinimumSwathHeightC = MaximumSwathHeightC; + } else { + if (mode_lib->vba.SurfaceTiling[k] == dm_sw_linear) { + MinimumSwathHeightY = MaximumSwathHeightY; + MinimumSwathHeightC = MaximumSwathHeightC; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_8 + && mode_lib->vba.SourceScan[k] == dm_horz) { + MinimumSwathHeightY = MaximumSwathHeightY / 2.0; + MinimumSwathHeightC = MaximumSwathHeightC; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_10 + && mode_lib->vba.SourceScan[k] == dm_horz) { + MinimumSwathHeightC = MaximumSwathHeightC / 2.0; + MinimumSwathHeightY = MaximumSwathHeightY; + } else { + MinimumSwathHeightY = MaximumSwathHeightY; + MinimumSwathHeightC = MaximumSwathHeightC; + } + } + + if (mode_lib->vba.SourceScan[k] == dm_horz) { + SwathWidth = mode_lib->vba.ViewportWidth[k]; + } else { + SwathWidth = mode_lib->vba.ViewportHeight[k]; + } + + if (mode_lib->vba.ODMCombineEnabled[k] == true) { + MainPlaneDoesODMCombine = true; + } + for (j = 0; j < mode_lib->vba.NumberOfActivePlanes; ++j) { + if (mode_lib->vba.BlendingAndTiming[k] == j + && mode_lib->vba.ODMCombineEnabled[j] == true) { + MainPlaneDoesODMCombine = true; + } + } + + if (MainPlaneDoesODMCombine == true) { + SwathWidth = dml_min( + SwathWidth, + mode_lib->vba.HActive[k] / 2.0 * mode_lib->vba.HRatio[k]); + } else { + SwathWidth = SwathWidth / mode_lib->vba.DPPPerPlane[k]; + } + + SwathWidthGranularityY = 256 / dml_ceil(BytePerPixDETY, 1) / MaximumSwathHeightY; + RoundedUpMaxSwathSizeBytesY = (dml_ceil( + (double) (SwathWidth - 1), + SwathWidthGranularityY) + SwathWidthGranularityY) * BytePerPixDETY + * MaximumSwathHeightY; + if (mode_lib->vba.SourcePixelFormat[k] == dm_420_10) { + RoundedUpMaxSwathSizeBytesY = dml_ceil(RoundedUpMaxSwathSizeBytesY, 256) + + 256; + } + if (MaximumSwathHeightC > 0) { + SwathWidthGranularityC = 256.0 / dml_ceil(BytePerPixDETC, 2) + / MaximumSwathHeightC; + RoundedUpMaxSwathSizeBytesC = (dml_ceil( + (double) (SwathWidth / 2.0 - 1), + SwathWidthGranularityC) + SwathWidthGranularityC) + * BytePerPixDETC * MaximumSwathHeightC; + if (mode_lib->vba.SourcePixelFormat[k] == dm_420_10) { + RoundedUpMaxSwathSizeBytesC = dml_ceil( + RoundedUpMaxSwathSizeBytesC, + 256) + 256; + } + } else + RoundedUpMaxSwathSizeBytesC = 0.0; + + if (RoundedUpMaxSwathSizeBytesY + RoundedUpMaxSwathSizeBytesC + <= mode_lib->vba.DETBufferSizeInKByte * 1024.0 / 2.0) { + mode_lib->vba.SwathHeightY[k] = MaximumSwathHeightY; + mode_lib->vba.SwathHeightC[k] = MaximumSwathHeightC; + } else { + mode_lib->vba.SwathHeightY[k] = MinimumSwathHeightY; + mode_lib->vba.SwathHeightC[k] = MinimumSwathHeightC; + } + + if (mode_lib->vba.SwathHeightC[k] == 0) { + mode_lib->vba.DETBufferSizeY[k] = mode_lib->vba.DETBufferSizeInKByte * 1024; + mode_lib->vba.DETBufferSizeC[k] = 0; + } else if (mode_lib->vba.SwathHeightY[k] <= mode_lib->vba.SwathHeightC[k]) { + mode_lib->vba.DETBufferSizeY[k] = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 / 2; + mode_lib->vba.DETBufferSizeC[k] = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 / 2; + } else { + mode_lib->vba.DETBufferSizeY[k] = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 * 2 / 3; + mode_lib->vba.DETBufferSizeC[k] = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 / 3; + } + } +} + +bool Calculate256BBlockSizes( + enum source_format_class SourcePixelFormat, + enum dm_swizzle_mode SurfaceTiling, + unsigned int BytePerPixelY, + unsigned int BytePerPixelC, + unsigned int *BlockHeight256BytesY, + unsigned int *BlockHeight256BytesC, + unsigned int *BlockWidth256BytesY, + unsigned int *BlockWidth256BytesC) +{ + if ((SourcePixelFormat == dm_444_64 || SourcePixelFormat == dm_444_32 + || SourcePixelFormat == dm_444_16 + || SourcePixelFormat == dm_444_8)) { + if (SurfaceTiling == dm_sw_linear) { + *BlockHeight256BytesY = 1; + } else if (SourcePixelFormat == dm_444_64) { + *BlockHeight256BytesY = 4; + } else if (SourcePixelFormat == dm_444_8) { + *BlockHeight256BytesY = 16; + } else { + *BlockHeight256BytesY = 8; + } + *BlockWidth256BytesY = 256 / BytePerPixelY / *BlockHeight256BytesY; + *BlockHeight256BytesC = 0; + *BlockWidth256BytesC = 0; + } else { + if (SurfaceTiling == dm_sw_linear) { + *BlockHeight256BytesY = 1; + *BlockHeight256BytesC = 1; + } else if (SourcePixelFormat == dm_420_8) { + *BlockHeight256BytesY = 16; + *BlockHeight256BytesC = 8; + } else { + *BlockHeight256BytesY = 8; + *BlockHeight256BytesC = 8; + } + *BlockWidth256BytesY = 256 / BytePerPixelY / *BlockHeight256BytesY; + *BlockWidth256BytesC = 256 / BytePerPixelC / *BlockHeight256BytesC; + } + return true; +} + +static double CalculateTWait( + unsigned int PrefetchMode, + double DRAMClockChangeLatency, + double UrgentLatency, + double SREnterPlusExitTime) +{ + if (PrefetchMode == 0) { + return dml_max( + DRAMClockChangeLatency + UrgentLatency, + dml_max(SREnterPlusExitTime, UrgentLatency)); + } else if (PrefetchMode == 1) { + return dml_max(SREnterPlusExitTime, UrgentLatency); + } else { + return UrgentLatency; + } +} + +static double CalculateRemoteSurfaceFlipDelay( + struct display_mode_lib *mode_lib, + double VRatio, + double SwathWidth, + double Bpp, + double LineTime, + double XFCTSlvVupdateOffset, + double XFCTSlvVupdateWidth, + double XFCTSlvVreadyOffset, + double XFCXBUFLatencyTolerance, + double XFCFillBWOverhead, + double XFCSlvChunkSize, + double XFCBusTransportTime, + double TCalc, + double TWait, + double *SrcActiveDrainRate, + double *TInitXFill, + double *TslvChk) +{ + double TSlvSetup, AvgfillRate, result; + + *SrcActiveDrainRate = VRatio * SwathWidth * Bpp / LineTime; + TSlvSetup = XFCTSlvVupdateOffset + XFCTSlvVupdateWidth + XFCTSlvVreadyOffset; + *TInitXFill = XFCXBUFLatencyTolerance / (1 + XFCFillBWOverhead / 100); + AvgfillRate = *SrcActiveDrainRate * (1 + XFCFillBWOverhead / 100); + *TslvChk = XFCSlvChunkSize / AvgfillRate; + dml_print( + "DML::CalculateRemoteSurfaceFlipDelay: SrcActiveDrainRate: %f\n", + *SrcActiveDrainRate); + dml_print("DML::CalculateRemoteSurfaceFlipDelay: TSlvSetup: %f\n", TSlvSetup); + dml_print("DML::CalculateRemoteSurfaceFlipDelay: TInitXFill: %f\n", *TInitXFill); + dml_print("DML::CalculateRemoteSurfaceFlipDelay: AvgfillRate: %f\n", AvgfillRate); + dml_print("DML::CalculateRemoteSurfaceFlipDelay: TslvChk: %f\n", *TslvChk); + result = 2 * XFCBusTransportTime + TSlvSetup + TCalc + TWait + *TslvChk + *TInitXFill; // TODO: This doesn't seem to match programming guide + dml_print("DML::CalculateRemoteSurfaceFlipDelay: RemoteSurfaceFlipDelay: %f\n", result); + return result; +} + +static double CalculateWriteBackDISPCLK( + enum source_format_class WritebackPixelFormat, + double PixelClock, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackLumaHTaps, + unsigned int WritebackLumaVTaps, + unsigned int WritebackChromaHTaps, + unsigned int WritebackChromaVTaps, + double WritebackDestinationWidth, + unsigned int HTotal, + unsigned int WritebackChromaLineBufferWidth) +{ + double CalculateWriteBackDISPCLK = + 1.01 * PixelClock + * dml_max( + dml_ceil(WritebackLumaHTaps / 4.0, 1) + / WritebackHRatio, + dml_max( + (WritebackLumaVTaps + * dml_ceil( + 1.0 + / WritebackVRatio, + 1) + * dml_ceil( + WritebackDestinationWidth + / 4.0, + 1) + + dml_ceil( + WritebackDestinationWidth + / 4.0, + 1)) + / (double) HTotal + + dml_ceil( + 1.0 + / WritebackVRatio, + 1) + * (dml_ceil( + WritebackLumaVTaps + / 4.0, + 1) + + 4.0) + / (double) HTotal, + dml_ceil( + 1.0 + / WritebackVRatio, + 1) + * WritebackDestinationWidth + / (double) HTotal)); + if (WritebackPixelFormat != dm_444_32) { + CalculateWriteBackDISPCLK = + dml_max( + CalculateWriteBackDISPCLK, + 1.01 * PixelClock + * dml_max( + dml_ceil( + WritebackChromaHTaps + / 2.0, + 1) + / (2 + * WritebackHRatio), + dml_max( + (WritebackChromaVTaps + * dml_ceil( + 1 + / (2 + * WritebackVRatio), + 1) + * dml_ceil( + WritebackDestinationWidth + / 2.0 + / 2.0, + 1) + + dml_ceil( + WritebackDestinationWidth + / 2.0 + / WritebackChromaLineBufferWidth, + 1)) + / HTotal + + dml_ceil( + 1 + / (2 + * WritebackVRatio), + 1) + * (dml_ceil( + WritebackChromaVTaps + / 4.0, + 1) + + 4) + / HTotal, + dml_ceil( + 1.0 + / (2 + * WritebackVRatio), + 1) + * WritebackDestinationWidth + / 2.0 + / HTotal))); + } + return CalculateWriteBackDISPCLK; +} + +static double CalculateWriteBackDelay( + enum source_format_class WritebackPixelFormat, + double WritebackHRatio, + double WritebackVRatio, + unsigned int WritebackLumaHTaps, + unsigned int WritebackLumaVTaps, + unsigned int WritebackChromaHTaps, + unsigned int WritebackChromaVTaps, + unsigned int WritebackDestinationWidth) +{ + double CalculateWriteBackDelay = + dml_max( + dml_ceil(WritebackLumaHTaps / 4.0, 1) / WritebackHRatio, + WritebackLumaVTaps * dml_ceil(1.0 / WritebackVRatio, 1) + * dml_ceil( + WritebackDestinationWidth + / 4.0, + 1) + + dml_ceil(1.0 / WritebackVRatio, 1) + * (dml_ceil( + WritebackLumaVTaps + / 4.0, + 1) + 4)); + + if (WritebackPixelFormat != dm_444_32) { + CalculateWriteBackDelay = + dml_max( + CalculateWriteBackDelay, + dml_max( + dml_ceil( + WritebackChromaHTaps + / 2.0, + 1) + / (2 + * WritebackHRatio), + WritebackChromaVTaps + * dml_ceil( + 1 + / (2 + * WritebackVRatio), + 1) + * dml_ceil( + WritebackDestinationWidth + / 2.0 + / 2.0, + 1) + + dml_ceil( + 1 + / (2 + * WritebackVRatio), + 1) + * (dml_ceil( + WritebackChromaVTaps + / 4.0, + 1) + + 4))); + } + return CalculateWriteBackDelay; +} + +static void CalculateActiveRowBandwidth( + bool VirtualMemoryEnable, + enum source_format_class SourcePixelFormat, + double VRatio, + bool DCCEnable, + double LineTime, + unsigned int MetaRowByteLuma, + unsigned int MetaRowByteChroma, + unsigned int meta_row_height_luma, + unsigned int meta_row_height_chroma, + unsigned int PixelPTEBytesPerRowLuma, + unsigned int PixelPTEBytesPerRowChroma, + unsigned int dpte_row_height_luma, + unsigned int dpte_row_height_chroma, + double *meta_row_bw, + double *dpte_row_bw, + double *qual_row_bw) +{ + if (DCCEnable != true) { + *meta_row_bw = 0; + } else if (SourcePixelFormat == dm_420_8 || SourcePixelFormat == dm_420_10) { + *meta_row_bw = VRatio * MetaRowByteLuma / (meta_row_height_luma * LineTime) + + VRatio / 2 * MetaRowByteChroma + / (meta_row_height_chroma * LineTime); + } else { + *meta_row_bw = VRatio * MetaRowByteLuma / (meta_row_height_luma * LineTime); + } + + if (VirtualMemoryEnable != true) { + *dpte_row_bw = 0; + } else if (SourcePixelFormat == dm_420_8 || SourcePixelFormat == dm_420_10) { + *dpte_row_bw = VRatio * PixelPTEBytesPerRowLuma / (dpte_row_height_luma * LineTime) + + VRatio / 2 * PixelPTEBytesPerRowChroma + / (dpte_row_height_chroma * LineTime); + } else { + *dpte_row_bw = VRatio * PixelPTEBytesPerRowLuma / (dpte_row_height_luma * LineTime); + } + + if ((SourcePixelFormat == dm_420_8 || SourcePixelFormat == dm_420_10)) { + *qual_row_bw = *meta_row_bw + *dpte_row_bw; + } else { + *qual_row_bw = 0; + } +} + +static void CalculateFlipSchedule( + struct display_mode_lib *mode_lib, + double UrgentExtraLatency, + double UrgentLatency, + unsigned int MaxPageTableLevels, + bool VirtualMemoryEnable, + double BandwidthAvailableForImmediateFlip, + unsigned int TotImmediateFlipBytes, + enum source_format_class SourcePixelFormat, + unsigned int ImmediateFlipBytes, + double LineTime, + double Tno_bw, + double VRatio, + double PDEAndMetaPTEBytesFrame, + unsigned int MetaRowByte, + unsigned int PixelPTEBytesPerRow, + bool DCCEnable, + unsigned int dpte_row_height, + unsigned int meta_row_height, + double qual_row_bw, + double *DestinationLinesToRequestVMInImmediateFlip, + double *DestinationLinesToRequestRowInImmediateFlip, + double *final_flip_bw, + bool *ImmediateFlipSupportedForPipe) +{ + double min_row_time = 0.0; + + if (SourcePixelFormat == dm_420_8 || SourcePixelFormat == dm_420_10) { + *DestinationLinesToRequestVMInImmediateFlip = 0.0; + *DestinationLinesToRequestRowInImmediateFlip = 0.0; + *final_flip_bw = qual_row_bw; + *ImmediateFlipSupportedForPipe = true; + } else { + double TimeForFetchingMetaPTEImmediateFlip; + double TimeForFetchingRowInVBlankImmediateFlip; + + if (VirtualMemoryEnable == true) { + mode_lib->vba.ImmediateFlipBW = BandwidthAvailableForImmediateFlip + * ImmediateFlipBytes / TotImmediateFlipBytes; + TimeForFetchingMetaPTEImmediateFlip = + dml_max( + Tno_bw + + PDEAndMetaPTEBytesFrame + / mode_lib->vba.ImmediateFlipBW, + dml_max( + UrgentExtraLatency + + UrgentLatency + * (MaxPageTableLevels + - 1), + LineTime / 4.0)); + } else { + TimeForFetchingMetaPTEImmediateFlip = 0; + } + + *DestinationLinesToRequestVMInImmediateFlip = dml_floor( + 4.0 * (TimeForFetchingMetaPTEImmediateFlip / LineTime + 0.125), + 1) / 4.0; + + if ((VirtualMemoryEnable == true || DCCEnable == true)) { + mode_lib->vba.ImmediateFlipBW = BandwidthAvailableForImmediateFlip + * ImmediateFlipBytes / TotImmediateFlipBytes; + TimeForFetchingRowInVBlankImmediateFlip = dml_max( + (MetaRowByte + PixelPTEBytesPerRow) + / mode_lib->vba.ImmediateFlipBW, + dml_max(UrgentLatency, LineTime / 4.0)); + } else { + TimeForFetchingRowInVBlankImmediateFlip = 0; + } + + *DestinationLinesToRequestRowInImmediateFlip = dml_floor( + 4.0 * (TimeForFetchingRowInVBlankImmediateFlip / LineTime + 0.125), + 1) / 4.0; + + if (VirtualMemoryEnable == true) { + *final_flip_bw = + dml_max( + PDEAndMetaPTEBytesFrame + / (*DestinationLinesToRequestVMInImmediateFlip + * LineTime), + (MetaRowByte + PixelPTEBytesPerRow) + / (TimeForFetchingRowInVBlankImmediateFlip + * LineTime)); + } else if (MetaRowByte + PixelPTEBytesPerRow > 0) { + *final_flip_bw = (MetaRowByte + PixelPTEBytesPerRow) + / (TimeForFetchingRowInVBlankImmediateFlip * LineTime); + } else { + *final_flip_bw = 0; + } + + if (VirtualMemoryEnable && !DCCEnable) + min_row_time = dpte_row_height * LineTime / VRatio; + else if (!VirtualMemoryEnable && DCCEnable) + min_row_time = meta_row_height * LineTime / VRatio; + else + min_row_time = dml_min(dpte_row_height, meta_row_height) * LineTime + / VRatio; + + if (*DestinationLinesToRequestVMInImmediateFlip >= 8 + || *DestinationLinesToRequestRowInImmediateFlip >= 16 + || TimeForFetchingMetaPTEImmediateFlip + + 2 * TimeForFetchingRowInVBlankImmediateFlip + > min_row_time) + *ImmediateFlipSupportedForPipe = false; + else + *ImmediateFlipSupportedForPipe = true; + } +} + +static void PixelClockAdjustmentForProgressiveToInterlaceUnit(struct display_mode_lib *mode_lib) +{ + unsigned int k; + + //Progressive To dml_ml->vba.Interlace Unit Effect + for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) { + mode_lib->vba.PixelClockBackEnd[k] = mode_lib->vba.PixelClock[k]; + if (mode_lib->vba.Interlace[k] == 1 + && mode_lib->vba.ProgressiveToInterlaceUnitInOPP == true) { + mode_lib->vba.PixelClock[k] = 2 * mode_lib->vba.PixelClock[k]; + } + } +} + +static unsigned int CursorBppEnumToBits(enum cursor_bpp ebpp) +{ + switch (ebpp) { + case dm_cur_2bit: + return 2; + case dm_cur_32bit: + return 32; + case dm_cur_64bit: + return 64; + default: + return 0; + } +} + +static unsigned int TruncToValidBPP( + double DecimalBPP, + bool DSCEnabled, + enum output_encoder_class Output, + enum output_format_class Format, + unsigned int DSCInputBitPerComponent) +{ + if (Output == dm_hdmi) { + if (Format == dm_420) { + if (DecimalBPP >= 18) + return 18; + else if (DecimalBPP >= 15) + return 15; + else if (DecimalBPP >= 12) + return 12; + else + return 0; + } else if (Format == dm_444) { + if (DecimalBPP >= 36) + return 36; + else if (DecimalBPP >= 30) + return 30; + else if (DecimalBPP >= 24) + return 24; + else + return 0; + } else { + if (DecimalBPP / 1.5 >= 24) + return 24; + else if (DecimalBPP / 1.5 >= 20) + return 20; + else if (DecimalBPP / 1.5 >= 16) + return 16; + else + return 0; + } + } else { + if (DSCEnabled) { + if (Format == dm_420) { + if (DecimalBPP < 6) + return 0; + else if (DecimalBPP >= 1.5 * DSCInputBitPerComponent - 1 / 16) + return 1.5 * DSCInputBitPerComponent - 1 / 16; + else + return dml_floor(16 * DecimalBPP, 1) / 16; + } else if (Format == dm_n422) { + if (DecimalBPP < 7) + return 0; + else if (DecimalBPP >= 2 * DSCInputBitPerComponent - 1 / 16) + return 2 * DSCInputBitPerComponent - 1 / 16; + else + return dml_floor(16 * DecimalBPP, 1) / 16; + } else { + if (DecimalBPP < 8) + return 0; + else if (DecimalBPP >= 3 * DSCInputBitPerComponent - 1 / 16) + return 3 * DSCInputBitPerComponent - 1 / 16; + else + return dml_floor(16 * DecimalBPP, 1) / 16; + } + } else if (Format == dm_420) { + if (DecimalBPP >= 18) + return 18; + else if (DecimalBPP >= 15) + return 15; + else if (DecimalBPP >= 12) + return 12; + else + return 0; + } else if (Format == dm_s422 || Format == dm_n422) { + if (DecimalBPP >= 24) + return 24; + else if (DecimalBPP >= 20) + return 20; + else if (DecimalBPP >= 16) + return 16; + else + return 0; + } else { + if (DecimalBPP >= 36) + return 36; + else if (DecimalBPP >= 30) + return 30; + else if (DecimalBPP >= 24) + return 24; + else + return 0; + } + } +} + +static void ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_lib) +{ + int i; + unsigned int j, k; + /*MODE SUPPORT, VOLTAGE STATE AND SOC CONFIGURATION*/ + + /*Scale Ratio, taps Support Check*/ + + mode_lib->vba.ScaleRatioAndTapsSupport = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.ScalerEnabled[k] == false + && ((mode_lib->vba.SourcePixelFormat[k] != dm_444_64 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_32 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_8) + || mode_lib->vba.HRatio[k] != 1.0 + || mode_lib->vba.htaps[k] != 1.0 + || mode_lib->vba.VRatio[k] != 1.0 + || mode_lib->vba.vtaps[k] != 1.0)) { + mode_lib->vba.ScaleRatioAndTapsSupport = false; + } else if (mode_lib->vba.vtaps[k] < 1.0 || mode_lib->vba.vtaps[k] > 8.0 + || mode_lib->vba.htaps[k] < 1.0 || mode_lib->vba.htaps[k] > 8.0 + || (mode_lib->vba.htaps[k] > 1.0 + && (mode_lib->vba.htaps[k] % 2) == 1) + || mode_lib->vba.HRatio[k] > mode_lib->vba.MaxHSCLRatio + || mode_lib->vba.VRatio[k] > mode_lib->vba.MaxVSCLRatio + || mode_lib->vba.HRatio[k] > mode_lib->vba.htaps[k] + || mode_lib->vba.VRatio[k] > mode_lib->vba.vtaps[k] + || (mode_lib->vba.SourcePixelFormat[k] != dm_444_64 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_32 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_8 + && (mode_lib->vba.HRatio[k] / 2.0 + > mode_lib->vba.HTAPsChroma[k] + || mode_lib->vba.VRatio[k] / 2.0 + > mode_lib->vba.VTAPsChroma[k]))) { + mode_lib->vba.ScaleRatioAndTapsSupport = false; + } + } + /*Source Format, Pixel Format and Scan Support Check*/ + + mode_lib->vba.SourceFormatPixelAndScanSupport = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if ((mode_lib->vba.SurfaceTiling[k] == dm_sw_linear + && mode_lib->vba.SourceScan[k] != dm_horz) + || ((mode_lib->vba.SurfaceTiling[k] == dm_sw_4kb_d + || mode_lib->vba.SurfaceTiling[k] == dm_sw_4kb_d_x + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_d + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_d_t + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_d_x + || mode_lib->vba.SurfaceTiling[k] == dm_sw_var_d + || mode_lib->vba.SurfaceTiling[k] == dm_sw_var_d_x) + && mode_lib->vba.SourcePixelFormat[k] != dm_444_64) + || (mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_r_x + && (mode_lib->vba.SourcePixelFormat[k] == dm_mono_8 + || mode_lib->vba.SourcePixelFormat[k] + == dm_420_8 + || mode_lib->vba.SourcePixelFormat[k] + == dm_420_10)) + || (((mode_lib->vba.SurfaceTiling[k] + == dm_sw_gfx7_2d_thin_gl + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_gfx7_2d_thin_lvp) + && !((mode_lib->vba.SourcePixelFormat[k] + == dm_444_64 + || mode_lib->vba.SourcePixelFormat[k] + == dm_444_32) + && mode_lib->vba.SourceScan[k] + == dm_horz + && mode_lib->vba.SupportGFX7CompatibleTilingIn32bppAnd64bpp + == true + && mode_lib->vba.DCCEnable[k] + == false)) + || (mode_lib->vba.DCCEnable[k] == true + && (mode_lib->vba.SurfaceTiling[k] + == dm_sw_linear + || mode_lib->vba.SourcePixelFormat[k] + == dm_420_8 + || mode_lib->vba.SourcePixelFormat[k] + == dm_420_10)))) { + mode_lib->vba.SourceFormatPixelAndScanSupport = false; + } + } + /*Bandwidth Support Check*/ + + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.SourceScan[k] == dm_horz) { + mode_lib->vba.SwathWidthYSingleDPP[k] = mode_lib->vba.ViewportWidth[k]; + } else { + mode_lib->vba.SwathWidthYSingleDPP[k] = mode_lib->vba.ViewportHeight[k]; + } + if (mode_lib->vba.SourcePixelFormat[k] == dm_444_64) { + mode_lib->vba.BytePerPixelInDETY[k] = 8.0; + mode_lib->vba.BytePerPixelInDETC[k] = 0.0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_32) { + mode_lib->vba.BytePerPixelInDETY[k] = 4.0; + mode_lib->vba.BytePerPixelInDETC[k] = 0.0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_444_16 + || mode_lib->vba.SourcePixelFormat[k] == dm_mono_16) { + mode_lib->vba.BytePerPixelInDETY[k] = 2.0; + mode_lib->vba.BytePerPixelInDETC[k] = 0.0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_mono_8) { + mode_lib->vba.BytePerPixelInDETY[k] = 1.0; + mode_lib->vba.BytePerPixelInDETC[k] = 0.0; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_8) { + mode_lib->vba.BytePerPixelInDETY[k] = 1.0; + mode_lib->vba.BytePerPixelInDETC[k] = 2.0; + } else { + mode_lib->vba.BytePerPixelInDETY[k] = 4.0 / 3; + mode_lib->vba.BytePerPixelInDETC[k] = 8.0 / 3; + } + } + mode_lib->vba.TotalReadBandwidthConsumedGBytePerSecond = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.ReadBandwidth[k] = mode_lib->vba.SwathWidthYSingleDPP[k] + * (dml_ceil(mode_lib->vba.BytePerPixelInDETY[k], 1.0) + * mode_lib->vba.VRatio[k] + + dml_ceil(mode_lib->vba.BytePerPixelInDETC[k], 2.0) + / 2.0 * mode_lib->vba.VRatio[k] / 2) + / (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]); + if (mode_lib->vba.DCCEnable[k] == true) { + mode_lib->vba.ReadBandwidth[k] = mode_lib->vba.ReadBandwidth[k] + * (1 + 1 / 256); + } + if (mode_lib->vba.VirtualMemoryEnable == true + && mode_lib->vba.SourceScan[k] != dm_horz + && (mode_lib->vba.SurfaceTiling[k] == dm_sw_4kb_s + || mode_lib->vba.SurfaceTiling[k] == dm_sw_4kb_s_x + || mode_lib->vba.SurfaceTiling[k] == dm_sw_4kb_d + || mode_lib->vba.SurfaceTiling[k] == dm_sw_4kb_d_x)) { + mode_lib->vba.ReadBandwidth[k] = mode_lib->vba.ReadBandwidth[k] + * (1 + 1 / 64); + } else if (mode_lib->vba.VirtualMemoryEnable == true + && mode_lib->vba.SourceScan[k] == dm_horz + && (mode_lib->vba.SourcePixelFormat[k] == dm_444_64 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_32) + && (mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_s + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_s_t + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_s_x + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_d + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_d_t + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_d_x + || mode_lib->vba.SurfaceTiling[k] == dm_sw_64kb_r_x)) { + mode_lib->vba.ReadBandwidth[k] = mode_lib->vba.ReadBandwidth[k] + * (1 + 1 / 256); + } else if (mode_lib->vba.VirtualMemoryEnable == true) { + mode_lib->vba.ReadBandwidth[k] = mode_lib->vba.ReadBandwidth[k] + * (1 + 1 / 512); + } + mode_lib->vba.TotalReadBandwidthConsumedGBytePerSecond = + mode_lib->vba.TotalReadBandwidthConsumedGBytePerSecond + + mode_lib->vba.ReadBandwidth[k] / 1000.0; + } + mode_lib->vba.TotalWriteBandwidthConsumedGBytePerSecond = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.WritebackEnable[k] == true + && mode_lib->vba.WritebackPixelFormat[k] == dm_444_32) { + mode_lib->vba.WriteBandwidth[k] = mode_lib->vba.WritebackDestinationWidth[k] + * mode_lib->vba.WritebackDestinationHeight[k] + / (mode_lib->vba.WritebackSourceHeight[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) * 4.0; + } else if (mode_lib->vba.WritebackEnable[k] == true + && mode_lib->vba.WritebackPixelFormat[k] == dm_420_10) { + mode_lib->vba.WriteBandwidth[k] = mode_lib->vba.WritebackDestinationWidth[k] + * mode_lib->vba.WritebackDestinationHeight[k] + / (mode_lib->vba.WritebackSourceHeight[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) * 3.0; + } else if (mode_lib->vba.WritebackEnable[k] == true) { + mode_lib->vba.WriteBandwidth[k] = mode_lib->vba.WritebackDestinationWidth[k] + * mode_lib->vba.WritebackDestinationHeight[k] + / (mode_lib->vba.WritebackSourceHeight[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) * 1.5; + } else { + mode_lib->vba.WriteBandwidth[k] = 0.0; + } + mode_lib->vba.TotalWriteBandwidthConsumedGBytePerSecond = + mode_lib->vba.TotalWriteBandwidthConsumedGBytePerSecond + + mode_lib->vba.WriteBandwidth[k] / 1000.0; + } + mode_lib->vba.TotalBandwidthConsumedGBytePerSecond = + mode_lib->vba.TotalReadBandwidthConsumedGBytePerSecond + + mode_lib->vba.TotalWriteBandwidthConsumedGBytePerSecond; + mode_lib->vba.DCCEnabledInAnyPlane = false; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.DCCEnable[k] == true) { + mode_lib->vba.DCCEnabledInAnyPlane = true; + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.FabricAndDRAMBandwidthPerState[i] = dml_min( + mode_lib->vba.DRAMSpeedPerState[i] * mode_lib->vba.NumberOfChannels + * mode_lib->vba.DRAMChannelWidth, + mode_lib->vba.FabricClockPerState[i] + * mode_lib->vba.FabricDatapathToDCNDataReturn) + / 1000; + mode_lib->vba.ReturnBWToDCNPerState = dml_min( + mode_lib->vba.ReturnBusWidth * mode_lib->vba.DCFCLKPerState[i], + mode_lib->vba.FabricAndDRAMBandwidthPerState[i] * 1000.0) + * mode_lib->vba.PercentOfIdealDRAMAndFabricBWReceivedAfterUrgLatency + / 100; + mode_lib->vba.ReturnBWPerState[i] = mode_lib->vba.ReturnBWToDCNPerState; + if (mode_lib->vba.DCCEnabledInAnyPlane == true + && mode_lib->vba.ReturnBWToDCNPerState + > mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.ReturnBusWidth + / 4.0) { + mode_lib->vba.ReturnBWPerState[i] = + dml_min( + mode_lib->vba.ReturnBWPerState[i], + mode_lib->vba.ReturnBWToDCNPerState * 4.0 + * (1.0 + - mode_lib->vba.UrgentLatency + / ((mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0 + / (mode_lib->vba.ReturnBWToDCNPerState + - mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.ReturnBusWidth + / 4.0) + + mode_lib->vba.UrgentLatency))); + } + mode_lib->vba.CriticalPoint = + 2.0 * mode_lib->vba.ReturnBusWidth * mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.UrgentLatency + / (mode_lib->vba.ReturnBWToDCNPerState + * mode_lib->vba.UrgentLatency + + (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0); + if (mode_lib->vba.DCCEnabledInAnyPlane == true && mode_lib->vba.CriticalPoint > 1.0 + && mode_lib->vba.CriticalPoint < 4.0) { + mode_lib->vba.ReturnBWPerState[i] = + dml_min( + mode_lib->vba.ReturnBWPerState[i], + dml_pow( + 4.0 + * mode_lib->vba.ReturnBWToDCNPerState + * (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0 + * mode_lib->vba.ReturnBusWidth + * mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.UrgentLatency + / (mode_lib->vba.ReturnBWToDCNPerState + * mode_lib->vba.UrgentLatency + + (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0), + 2)); + } + mode_lib->vba.ReturnBWToDCNPerState = dml_min( + mode_lib->vba.ReturnBusWidth * mode_lib->vba.DCFCLKPerState[i], + mode_lib->vba.FabricAndDRAMBandwidthPerState[i] * 1000.0); + if (mode_lib->vba.DCCEnabledInAnyPlane == true + && mode_lib->vba.ReturnBWToDCNPerState + > mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.ReturnBusWidth + / 4.0) { + mode_lib->vba.ReturnBWPerState[i] = + dml_min( + mode_lib->vba.ReturnBWPerState[i], + mode_lib->vba.ReturnBWToDCNPerState * 4.0 + * (1.0 + - mode_lib->vba.UrgentLatency + / ((mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0 + / (mode_lib->vba.ReturnBWToDCNPerState + - mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.ReturnBusWidth + / 4.0) + + mode_lib->vba.UrgentLatency))); + } + mode_lib->vba.CriticalPoint = + 2.0 * mode_lib->vba.ReturnBusWidth * mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.UrgentLatency + / (mode_lib->vba.ReturnBWToDCNPerState + * mode_lib->vba.UrgentLatency + + (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0); + if (mode_lib->vba.DCCEnabledInAnyPlane == true && mode_lib->vba.CriticalPoint > 1.0 + && mode_lib->vba.CriticalPoint < 4.0) { + mode_lib->vba.ReturnBWPerState[i] = + dml_min( + mode_lib->vba.ReturnBWPerState[i], + dml_pow( + 4.0 + * mode_lib->vba.ReturnBWToDCNPerState + * (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0 + * mode_lib->vba.ReturnBusWidth + * mode_lib->vba.DCFCLKPerState[i] + * mode_lib->vba.UrgentLatency + / (mode_lib->vba.ReturnBWToDCNPerState + * mode_lib->vba.UrgentLatency + + (mode_lib->vba.ROBBufferSizeInKByte + - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0), + 2)); + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + if ((mode_lib->vba.TotalReadBandwidthConsumedGBytePerSecond * 1000.0 + <= mode_lib->vba.ReturnBWPerState[i]) + && (mode_lib->vba.TotalBandwidthConsumedGBytePerSecond * 1000.0 + <= mode_lib->vba.FabricAndDRAMBandwidthPerState[i] + * 1000.0 + * mode_lib->vba.PercentOfIdealDRAMAndFabricBWReceivedAfterUrgLatency + / 100.0)) { + mode_lib->vba.BandwidthSupport[i] = true; + } else { + mode_lib->vba.BandwidthSupport[i] = false; + } + } + /*Writeback Latency support check*/ + + mode_lib->vba.WritebackLatencySupport = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.WritebackEnable[k] == true) { + if (mode_lib->vba.WritebackPixelFormat[k] == dm_444_32) { + if (mode_lib->vba.WriteBandwidth[k] + > (mode_lib->vba.WritebackInterfaceLumaBufferSize + + mode_lib->vba.WritebackInterfaceChromaBufferSize) + / mode_lib->vba.WritebackLatency) { + mode_lib->vba.WritebackLatencySupport = false; + } + } else { + if (mode_lib->vba.WriteBandwidth[k] + > 1.5 + * dml_min( + mode_lib->vba.WritebackInterfaceLumaBufferSize, + 2.0 + * mode_lib->vba.WritebackInterfaceChromaBufferSize) + / mode_lib->vba.WritebackLatency) { + mode_lib->vba.WritebackLatencySupport = false; + } + } + } + } + /*Re-ordering Buffer Support Check*/ + + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.UrgentRoundTripAndOutOfOrderLatencyPerState[i] = + (mode_lib->vba.RoundTripPingLatencyCycles + 32.0) + / mode_lib->vba.DCFCLKPerState[i] + + mode_lib->vba.UrgentOutOfOrderReturnPerChannel + * mode_lib->vba.NumberOfChannels + / mode_lib->vba.ReturnBWPerState[i]; + if ((mode_lib->vba.ROBBufferSizeInKByte - mode_lib->vba.PixelChunkSizeInKByte) + * 1024.0 / mode_lib->vba.ReturnBWPerState[i] + > mode_lib->vba.UrgentRoundTripAndOutOfOrderLatencyPerState[i]) { + mode_lib->vba.ROBSupport[i] = true; + } else { + mode_lib->vba.ROBSupport[i] = false; + } + } + /*Writeback Mode Support Check*/ + + mode_lib->vba.TotalNumberOfActiveWriteback = 0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.WritebackEnable[k] == true) { + mode_lib->vba.TotalNumberOfActiveWriteback = + mode_lib->vba.TotalNumberOfActiveWriteback + 1; + } + } + mode_lib->vba.WritebackModeSupport = true; + if (mode_lib->vba.TotalNumberOfActiveWriteback > mode_lib->vba.MaxNumWriteback) { + mode_lib->vba.WritebackModeSupport = false; + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.WritebackEnable[k] == true + && mode_lib->vba.Writeback10bpc420Supported != true + && mode_lib->vba.WritebackPixelFormat[k] == dm_420_10) { + mode_lib->vba.WritebackModeSupport = false; + } + } + /*Writeback Scale Ratio and Taps Support Check*/ + + mode_lib->vba.WritebackScaleRatioAndTapsSupport = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.WritebackEnable[k] == true) { + if (mode_lib->vba.WritebackLumaAndChromaScalingSupported == false + && (mode_lib->vba.WritebackHRatio[k] != 1.0 + || mode_lib->vba.WritebackVRatio[k] != 1.0)) { + mode_lib->vba.WritebackScaleRatioAndTapsSupport = false; + } + if (mode_lib->vba.WritebackHRatio[k] > mode_lib->vba.WritebackMaxHSCLRatio + || mode_lib->vba.WritebackVRatio[k] + > mode_lib->vba.WritebackMaxVSCLRatio + || mode_lib->vba.WritebackHRatio[k] + < mode_lib->vba.WritebackMinHSCLRatio + || mode_lib->vba.WritebackVRatio[k] + < mode_lib->vba.WritebackMinVSCLRatio + || mode_lib->vba.WritebackLumaHTaps[k] + > mode_lib->vba.WritebackMaxHSCLTaps + || mode_lib->vba.WritebackLumaVTaps[k] + > mode_lib->vba.WritebackMaxVSCLTaps + || mode_lib->vba.WritebackHRatio[k] + > mode_lib->vba.WritebackLumaHTaps[k] + || mode_lib->vba.WritebackVRatio[k] + > mode_lib->vba.WritebackLumaVTaps[k] + || (mode_lib->vba.WritebackLumaHTaps[k] > 2.0 + && ((mode_lib->vba.WritebackLumaHTaps[k] % 2) + == 1)) + || (mode_lib->vba.WritebackPixelFormat[k] != dm_444_32 + && (mode_lib->vba.WritebackChromaHTaps[k] + > mode_lib->vba.WritebackMaxHSCLTaps + || mode_lib->vba.WritebackChromaVTaps[k] + > mode_lib->vba.WritebackMaxVSCLTaps + || 2.0 + * mode_lib->vba.WritebackHRatio[k] + > mode_lib->vba.WritebackChromaHTaps[k] + || 2.0 + * mode_lib->vba.WritebackVRatio[k] + > mode_lib->vba.WritebackChromaVTaps[k] + || (mode_lib->vba.WritebackChromaHTaps[k] > 2.0 + && ((mode_lib->vba.WritebackChromaHTaps[k] % 2) == 1))))) { + mode_lib->vba.WritebackScaleRatioAndTapsSupport = false; + } + if (mode_lib->vba.WritebackVRatio[k] < 1.0) { + mode_lib->vba.WritebackLumaVExtra = + dml_max(1.0 - 2.0 / dml_ceil(1.0 / mode_lib->vba.WritebackVRatio[k], 1.0), 0.0); + } else { + mode_lib->vba.WritebackLumaVExtra = -1; + } + if ((mode_lib->vba.WritebackPixelFormat[k] == dm_444_32 + && mode_lib->vba.WritebackLumaVTaps[k] + > (mode_lib->vba.WritebackLineBufferLumaBufferSize + + mode_lib->vba.WritebackLineBufferChromaBufferSize) + / 3.0 + / mode_lib->vba.WritebackDestinationWidth[k] + - mode_lib->vba.WritebackLumaVExtra) + || (mode_lib->vba.WritebackPixelFormat[k] == dm_420_8 + && mode_lib->vba.WritebackLumaVTaps[k] + > mode_lib->vba.WritebackLineBufferLumaBufferSize + / mode_lib->vba.WritebackDestinationWidth[k] + - mode_lib->vba.WritebackLumaVExtra) + || (mode_lib->vba.WritebackPixelFormat[k] == dm_420_10 + && mode_lib->vba.WritebackLumaVTaps[k] + > mode_lib->vba.WritebackLineBufferLumaBufferSize + * 8.0 / 10.0 + / mode_lib->vba.WritebackDestinationWidth[k] + - mode_lib->vba.WritebackLumaVExtra)) { + mode_lib->vba.WritebackScaleRatioAndTapsSupport = false; + } + if (2.0 * mode_lib->vba.WritebackVRatio[k] < 1) { + mode_lib->vba.WritebackChromaVExtra = 0.0; + } else { + mode_lib->vba.WritebackChromaVExtra = -1; + } + if ((mode_lib->vba.WritebackPixelFormat[k] == dm_420_8 + && mode_lib->vba.WritebackChromaVTaps[k] + > mode_lib->vba.WritebackLineBufferChromaBufferSize + / mode_lib->vba.WritebackDestinationWidth[k] + - mode_lib->vba.WritebackChromaVExtra) + || (mode_lib->vba.WritebackPixelFormat[k] == dm_420_10 + && mode_lib->vba.WritebackChromaVTaps[k] + > mode_lib->vba.WritebackLineBufferChromaBufferSize + * 8.0 / 10.0 + / mode_lib->vba.WritebackDestinationWidth[k] + - mode_lib->vba.WritebackChromaVExtra)) { + mode_lib->vba.WritebackScaleRatioAndTapsSupport = false; + } + } + } + /*Maximum DISPCLK/DPPCLK Support check*/ + + mode_lib->vba.WritebackRequiredDISPCLK = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.WritebackEnable[k] == true) { + mode_lib->vba.WritebackRequiredDISPCLK = + dml_max( + mode_lib->vba.WritebackRequiredDISPCLK, + CalculateWriteBackDISPCLK( + mode_lib->vba.WritebackPixelFormat[k], + mode_lib->vba.PixelClock[k], + mode_lib->vba.WritebackHRatio[k], + mode_lib->vba.WritebackVRatio[k], + mode_lib->vba.WritebackLumaHTaps[k], + mode_lib->vba.WritebackLumaVTaps[k], + mode_lib->vba.WritebackChromaHTaps[k], + mode_lib->vba.WritebackChromaVTaps[k], + mode_lib->vba.WritebackDestinationWidth[k], + mode_lib->vba.HTotal[k], + mode_lib->vba.WritebackChromaLineBufferWidth)); + } + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.HRatio[k] > 1.0) { + mode_lib->vba.PSCL_FACTOR[k] = dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput + * mode_lib->vba.HRatio[k] + / dml_ceil( + mode_lib->vba.htaps[k] + / 6.0, + 1.0)); + } else { + mode_lib->vba.PSCL_FACTOR[k] = dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput); + } + if (mode_lib->vba.BytePerPixelInDETC[k] == 0.0) { + mode_lib->vba.PSCL_FACTOR_CHROMA[k] = 0.0; + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] = + mode_lib->vba.PixelClock[k] + * dml_max3( + mode_lib->vba.vtaps[k] / 6.0 + * dml_min( + 1.0, + mode_lib->vba.HRatio[k]), + mode_lib->vba.HRatio[k] + * mode_lib->vba.VRatio[k] + / mode_lib->vba.PSCL_FACTOR[k], + 1.0); + if ((mode_lib->vba.htaps[k] > 6.0 || mode_lib->vba.vtaps[k] > 6.0) + && mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + < 2.0 * mode_lib->vba.PixelClock[k]) { + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] = 2.0 + * mode_lib->vba.PixelClock[k]; + } + } else { + if (mode_lib->vba.HRatio[k] / 2.0 > 1.0) { + mode_lib->vba.PSCL_FACTOR_CHROMA[k] = + dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput + * mode_lib->vba.HRatio[k] + / 2.0 + / dml_ceil( + mode_lib->vba.HTAPsChroma[k] + / 6.0, + 1.0)); + } else { + mode_lib->vba.PSCL_FACTOR_CHROMA[k] = dml_min( + mode_lib->vba.MaxDCHUBToPSCLThroughput, + mode_lib->vba.MaxPSCLToLBThroughput); + } + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] = + mode_lib->vba.PixelClock[k] + * dml_max5( + mode_lib->vba.vtaps[k] / 6.0 + * dml_min( + 1.0, + mode_lib->vba.HRatio[k]), + mode_lib->vba.HRatio[k] + * mode_lib->vba.VRatio[k] + / mode_lib->vba.PSCL_FACTOR[k], + mode_lib->vba.VTAPsChroma[k] + / 6.0 + * dml_min( + 1.0, + mode_lib->vba.HRatio[k] + / 2.0), + mode_lib->vba.HRatio[k] + * mode_lib->vba.VRatio[k] + / 4.0 + / mode_lib->vba.PSCL_FACTOR_CHROMA[k], + 1.0); + if ((mode_lib->vba.htaps[k] > 6.0 || mode_lib->vba.vtaps[k] > 6.0 + || mode_lib->vba.HTAPsChroma[k] > 6.0 + || mode_lib->vba.VTAPsChroma[k] > 6.0) + && mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + < 2.0 * mode_lib->vba.PixelClock[k]) { + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] = 2.0 + * mode_lib->vba.PixelClock[k]; + } + } + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + Calculate256BBlockSizes( + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.SurfaceTiling[k], + dml_ceil(mode_lib->vba.BytePerPixelInDETY[k], 1.0), + dml_ceil(mode_lib->vba.BytePerPixelInDETC[k], 2.0), + &mode_lib->vba.Read256BlockHeightY[k], + &mode_lib->vba.Read256BlockHeightC[k], + &mode_lib->vba.Read256BlockWidthY[k], + &mode_lib->vba.Read256BlockWidthC[k]); + if (mode_lib->vba.SourceScan[k] == dm_horz) { + mode_lib->vba.MaxSwathHeightY[k] = mode_lib->vba.Read256BlockHeightY[k]; + mode_lib->vba.MaxSwathHeightC[k] = mode_lib->vba.Read256BlockHeightC[k]; + } else { + mode_lib->vba.MaxSwathHeightY[k] = mode_lib->vba.Read256BlockWidthY[k]; + mode_lib->vba.MaxSwathHeightC[k] = mode_lib->vba.Read256BlockWidthC[k]; + } + if ((mode_lib->vba.SourcePixelFormat[k] == dm_444_64 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_32 + || mode_lib->vba.SourcePixelFormat[k] == dm_444_16 + || mode_lib->vba.SourcePixelFormat[k] == dm_mono_16 + || mode_lib->vba.SourcePixelFormat[k] == dm_mono_8)) { + if (mode_lib->vba.SurfaceTiling[k] == dm_sw_linear + || (mode_lib->vba.SourcePixelFormat[k] == dm_444_64 + && (mode_lib->vba.SurfaceTiling[k] + == dm_sw_4kb_s + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_4kb_s_x + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_64kb_s + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_64kb_s_t + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_64kb_s_x + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_var_s + || mode_lib->vba.SurfaceTiling[k] + == dm_sw_var_s_x) + && mode_lib->vba.SourceScan[k] == dm_horz)) { + mode_lib->vba.MinSwathHeightY[k] = mode_lib->vba.MaxSwathHeightY[k]; + } else { + mode_lib->vba.MinSwathHeightY[k] = mode_lib->vba.MaxSwathHeightY[k] + / 2.0; + } + mode_lib->vba.MinSwathHeightC[k] = mode_lib->vba.MaxSwathHeightC[k]; + } else { + if (mode_lib->vba.SurfaceTiling[k] == dm_sw_linear) { + mode_lib->vba.MinSwathHeightY[k] = mode_lib->vba.MaxSwathHeightY[k]; + mode_lib->vba.MinSwathHeightC[k] = mode_lib->vba.MaxSwathHeightC[k]; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_8 + && mode_lib->vba.SourceScan[k] == dm_horz) { + mode_lib->vba.MinSwathHeightY[k] = mode_lib->vba.MaxSwathHeightY[k] + / 2.0; + mode_lib->vba.MinSwathHeightC[k] = mode_lib->vba.MaxSwathHeightC[k]; + } else if (mode_lib->vba.SourcePixelFormat[k] == dm_420_10 + && mode_lib->vba.SourceScan[k] == dm_horz) { + mode_lib->vba.MinSwathHeightC[k] = mode_lib->vba.MaxSwathHeightC[k] + / 2.0; + mode_lib->vba.MinSwathHeightY[k] = mode_lib->vba.MaxSwathHeightY[k]; + } else { + mode_lib->vba.MinSwathHeightY[k] = mode_lib->vba.MaxSwathHeightY[k]; + mode_lib->vba.MinSwathHeightC[k] = mode_lib->vba.MaxSwathHeightC[k]; + } + } + if (mode_lib->vba.SurfaceTiling[k] == dm_sw_linear) { + mode_lib->vba.MaximumSwathWidthSupport = 8192.0; + } else { + mode_lib->vba.MaximumSwathWidthSupport = 5120.0; + } + mode_lib->vba.MaximumSwathWidthInDETBuffer = + dml_min( + mode_lib->vba.MaximumSwathWidthSupport, + mode_lib->vba.DETBufferSizeInKByte * 1024.0 / 2.0 + / (mode_lib->vba.BytePerPixelInDETY[k] + * mode_lib->vba.MinSwathHeightY[k] + + mode_lib->vba.BytePerPixelInDETC[k] + / 2.0 + * mode_lib->vba.MinSwathHeightC[k])); + if (mode_lib->vba.BytePerPixelInDETC[k] == 0.0) { + mode_lib->vba.MaximumSwathWidthInLineBuffer = + mode_lib->vba.LineBufferSize + * dml_max(mode_lib->vba.HRatio[k], 1.0) + / mode_lib->vba.LBBitPerPixel[k] + / (mode_lib->vba.vtaps[k] + + dml_max( + dml_ceil( + mode_lib->vba.VRatio[k], + 1.0) + - 2, + 0.0)); + } else { + mode_lib->vba.MaximumSwathWidthInLineBuffer = + dml_min( + mode_lib->vba.LineBufferSize + * dml_max( + mode_lib->vba.HRatio[k], + 1.0) + / mode_lib->vba.LBBitPerPixel[k] + / (mode_lib->vba.vtaps[k] + + dml_max( + dml_ceil( + mode_lib->vba.VRatio[k], + 1.0) + - 2, + 0.0)), + 2.0 * mode_lib->vba.LineBufferSize + * dml_max( + mode_lib->vba.HRatio[k] + / 2.0, + 1.0) + / mode_lib->vba.LBBitPerPixel[k] + / (mode_lib->vba.VTAPsChroma[k] + + dml_max( + dml_ceil( + mode_lib->vba.VRatio[k] + / 2.0, + 1.0) + - 2, + 0.0))); + } + mode_lib->vba.MaximumSwathWidth[k] = dml_min( + mode_lib->vba.MaximumSwathWidthInDETBuffer, + mode_lib->vba.MaximumSwathWidthInLineBuffer); + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.MaxDispclkRoundedDownToDFSGranularity = RoundToDFSGranularityDown( + mode_lib->vba.MaxDispclk[i], + mode_lib->vba.DISPCLKDPPCLKVCOSpeed); + mode_lib->vba.MaxDppclkRoundedDownToDFSGranularity = RoundToDFSGranularityDown( + mode_lib->vba.MaxDppclk[i], + mode_lib->vba.DISPCLKDPPCLKVCOSpeed); + mode_lib->vba.RequiredDISPCLK[i] = 0.0; + mode_lib->vba.DISPCLK_DPPCLK_Support[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine = + mode_lib->vba.PixelClock[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + * (1.0 + + mode_lib->vba.DISPCLKRampingMargin + / 100.0); + if (mode_lib->vba.ODMCapability == true + && mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine + > mode_lib->vba.MaxDispclkRoundedDownToDFSGranularity) { + mode_lib->vba.ODMCombineEnablePerState[i][k] = true; + mode_lib->vba.PlaneRequiredDISPCLK = + mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine + / 2.0; + } else { + mode_lib->vba.ODMCombineEnablePerState[i][k] = false; + mode_lib->vba.PlaneRequiredDISPCLK = + mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine; + } + if (mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + <= mode_lib->vba.MaxDppclkRoundedDownToDFSGranularity + && mode_lib->vba.SwathWidthYSingleDPP[k] + <= mode_lib->vba.MaximumSwathWidth[k] + && mode_lib->vba.ODMCombineEnablePerState[i][k] == false) { + mode_lib->vba.NoOfDPP[i][k] = 1; + mode_lib->vba.RequiredDPPCLK[i][k] = + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0); + } else { + mode_lib->vba.NoOfDPP[i][k] = 2; + mode_lib->vba.RequiredDPPCLK[i][k] = + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + / 2.0; + } + mode_lib->vba.RequiredDISPCLK[i] = dml_max( + mode_lib->vba.RequiredDISPCLK[i], + mode_lib->vba.PlaneRequiredDISPCLK); + if ((mode_lib->vba.MinDPPCLKUsingSingleDPP[k] / mode_lib->vba.NoOfDPP[i][k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + > mode_lib->vba.MaxDppclkRoundedDownToDFSGranularity) + || (mode_lib->vba.PlaneRequiredDISPCLK + > mode_lib->vba.MaxDispclkRoundedDownToDFSGranularity)) { + mode_lib->vba.DISPCLK_DPPCLK_Support[i] = false; + } + } + mode_lib->vba.TotalNumberOfActiveDPP[i] = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.TotalNumberOfActiveDPP[i] = + mode_lib->vba.TotalNumberOfActiveDPP[i] + + mode_lib->vba.NoOfDPP[i][k]; + } + if ((mode_lib->vba.MaxDispclk[i] == mode_lib->vba.MaxDispclk[DC__VOLTAGE_STATES] + && mode_lib->vba.MaxDppclk[i] + == mode_lib->vba.MaxDppclk[DC__VOLTAGE_STATES]) + && (mode_lib->vba.TotalNumberOfActiveDPP[i] + > mode_lib->vba.MaxNumDPP + || mode_lib->vba.DISPCLK_DPPCLK_Support[i] == false)) { + mode_lib->vba.RequiredDISPCLK[i] = 0.0; + mode_lib->vba.DISPCLK_DPPCLK_Support[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine = + mode_lib->vba.PixelClock[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0); + if (mode_lib->vba.ODMCapability == true + && mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine + > mode_lib->vba.MaxDispclkRoundedDownToDFSGranularity) { + mode_lib->vba.ODMCombineEnablePerState[i][k] = true; + mode_lib->vba.PlaneRequiredDISPCLK = + mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine + / 2.0; + } else { + mode_lib->vba.ODMCombineEnablePerState[i][k] = false; + mode_lib->vba.PlaneRequiredDISPCLK = + mode_lib->vba.PlaneRequiredDISPCLKWithoutODMCombine; + } + if (mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + <= mode_lib->vba.MaxDppclkRoundedDownToDFSGranularity + && mode_lib->vba.SwathWidthYSingleDPP[k] + <= mode_lib->vba.MaximumSwathWidth[k] + && mode_lib->vba.ODMCombineEnablePerState[i][k] + == false) { + mode_lib->vba.NoOfDPP[i][k] = 1; + mode_lib->vba.RequiredDPPCLK[i][k] = + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0); + } else { + mode_lib->vba.NoOfDPP[i][k] = 2; + mode_lib->vba.RequiredDPPCLK[i][k] = + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + / 2.0; + } + mode_lib->vba.RequiredDISPCLK[i] = dml_max( + mode_lib->vba.RequiredDISPCLK[i], + mode_lib->vba.PlaneRequiredDISPCLK); + if ((mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + / mode_lib->vba.NoOfDPP[i][k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + > mode_lib->vba.MaxDppclkRoundedDownToDFSGranularity) + || (mode_lib->vba.PlaneRequiredDISPCLK + > mode_lib->vba.MaxDispclkRoundedDownToDFSGranularity)) { + mode_lib->vba.DISPCLK_DPPCLK_Support[i] = false; + } + } + mode_lib->vba.TotalNumberOfActiveDPP[i] = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.TotalNumberOfActiveDPP[i] = + mode_lib->vba.TotalNumberOfActiveDPP[i] + + mode_lib->vba.NoOfDPP[i][k]; + } + } + if (mode_lib->vba.TotalNumberOfActiveDPP[i] > mode_lib->vba.MaxNumDPP) { + mode_lib->vba.RequiredDISPCLK[i] = 0.0; + mode_lib->vba.DISPCLK_DPPCLK_Support[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.ODMCombineEnablePerState[i][k] = false; + if (mode_lib->vba.SwathWidthYSingleDPP[k] + <= mode_lib->vba.MaximumSwathWidth[k]) { + mode_lib->vba.NoOfDPP[i][k] = 1; + mode_lib->vba.RequiredDPPCLK[i][k] = + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0); + } else { + mode_lib->vba.NoOfDPP[i][k] = 2; + mode_lib->vba.RequiredDPPCLK[i][k] = + mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + / 2.0; + } + if (!(mode_lib->vba.MaxDispclk[i] + == mode_lib->vba.MaxDispclk[DC__VOLTAGE_STATES] + && mode_lib->vba.MaxDppclk[i] + == mode_lib->vba.MaxDppclk[DC__VOLTAGE_STATES])) { + mode_lib->vba.PlaneRequiredDISPCLK = + mode_lib->vba.PixelClock[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + * (1.0 + + mode_lib->vba.DISPCLKRampingMargin + / 100.0); + } else { + mode_lib->vba.PlaneRequiredDISPCLK = + mode_lib->vba.PixelClock[k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0); + } + mode_lib->vba.RequiredDISPCLK[i] = dml_max( + mode_lib->vba.RequiredDISPCLK[i], + mode_lib->vba.PlaneRequiredDISPCLK); + if ((mode_lib->vba.MinDPPCLKUsingSingleDPP[k] + / mode_lib->vba.NoOfDPP[i][k] + * (1.0 + + mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + > mode_lib->vba.MaxDppclkRoundedDownToDFSGranularity) + || (mode_lib->vba.PlaneRequiredDISPCLK + > mode_lib->vba.MaxDispclkRoundedDownToDFSGranularity)) { + mode_lib->vba.DISPCLK_DPPCLK_Support[i] = false; + } + } + mode_lib->vba.TotalNumberOfActiveDPP[i] = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.TotalNumberOfActiveDPP[i] = + mode_lib->vba.TotalNumberOfActiveDPP[i] + + mode_lib->vba.NoOfDPP[i][k]; + } + } + mode_lib->vba.RequiredDISPCLK[i] = dml_max( + mode_lib->vba.RequiredDISPCLK[i], + mode_lib->vba.WritebackRequiredDISPCLK); + if (mode_lib->vba.MaxDispclkRoundedDownToDFSGranularity + < mode_lib->vba.WritebackRequiredDISPCLK) { + mode_lib->vba.DISPCLK_DPPCLK_Support[i] = false; + } + } + /*Viewport Size Check*/ + + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.ViewportSizeSupport[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.ODMCombineEnablePerState[i][k] == true) { + if (dml_min( + mode_lib->vba.SwathWidthYSingleDPP[k], + dml_round( + mode_lib->vba.HActive[k] / 2.0 + * mode_lib->vba.HRatio[k])) + > mode_lib->vba.MaximumSwathWidth[k]) { + mode_lib->vba.ViewportSizeSupport[i] = false; + } + } else { + if (mode_lib->vba.SwathWidthYSingleDPP[k] / 2.0 + > mode_lib->vba.MaximumSwathWidth[k]) { + mode_lib->vba.ViewportSizeSupport[i] = false; + } + } + } + } + /*Total Available Pipes Support Check*/ + + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + if (mode_lib->vba.TotalNumberOfActiveDPP[i] <= mode_lib->vba.MaxNumDPP) { + mode_lib->vba.TotalAvailablePipesSupport[i] = true; + } else { + mode_lib->vba.TotalAvailablePipesSupport[i] = false; + } + } + /*Total Available OTG Support Check*/ + + mode_lib->vba.TotalNumberOfActiveOTG = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.BlendingAndTiming[k] == k) { + mode_lib->vba.TotalNumberOfActiveOTG = mode_lib->vba.TotalNumberOfActiveOTG + + 1.0; + } + } + if (mode_lib->vba.TotalNumberOfActiveOTG <= mode_lib->vba.MaxNumOTG) { + mode_lib->vba.NumberOfOTGSupport = true; + } else { + mode_lib->vba.NumberOfOTGSupport = false; + } + /*Display IO and DSC Support Check*/ + + mode_lib->vba.NonsupportedDSCInputBPC = false; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (!(mode_lib->vba.DSCInputBitPerComponent[k] == 12.0 + || mode_lib->vba.DSCInputBitPerComponent[k] == 10.0 + || mode_lib->vba.DSCInputBitPerComponent[k] == 8.0)) { + mode_lib->vba.NonsupportedDSCInputBPC = true; + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.RequiresDSC[i][k] = 0; + mode_lib->vba.RequiresFEC[i][k] = 0; + if (mode_lib->vba.BlendingAndTiming[k] == k) { + if (mode_lib->vba.Output[k] == dm_hdmi) { + mode_lib->vba.RequiresDSC[i][k] = 0; + mode_lib->vba.RequiresFEC[i][k] = 0; + mode_lib->vba.OutputBppPerState[i][k] = + TruncToValidBPP( + dml_min( + 600.0, + mode_lib->vba.PHYCLKPerState[i]) + / mode_lib->vba.PixelClockBackEnd[k] + * 24, + false, + mode_lib->vba.Output[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.DSCInputBitPerComponent[k]); + } else if (mode_lib->vba.Output[k] == dm_dp + || mode_lib->vba.Output[k] == dm_edp) { + if (mode_lib->vba.Output[k] == dm_edp) { + mode_lib->vba.EffectiveFECOverhead = 0.0; + } else { + mode_lib->vba.EffectiveFECOverhead = + mode_lib->vba.FECOverhead; + } + if (mode_lib->vba.PHYCLKPerState[i] >= 270.0) { + mode_lib->vba.Outbpp = + TruncToValidBPP( + (1.0 + - mode_lib->vba.Downspreading + / 100.0) + * 270.0 + * mode_lib->vba.OutputLinkDPLanes[k] + / mode_lib->vba.PixelClockBackEnd[k] + * 8.0, + false, + mode_lib->vba.Output[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.DSCInputBitPerComponent[k]); + mode_lib->vba.OutbppDSC = + TruncToValidBPP( + (1.0 + - mode_lib->vba.Downspreading + / 100.0) + * (1.0 + - mode_lib->vba.EffectiveFECOverhead + / 100.0) + * 270.0 + * mode_lib->vba.OutputLinkDPLanes[k] + / mode_lib->vba.PixelClockBackEnd[k] + * 8.0, + true, + mode_lib->vba.Output[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.DSCInputBitPerComponent[k]); + if (mode_lib->vba.DSCEnabled[k] == true) { + mode_lib->vba.RequiresDSC[i][k] = true; + if (mode_lib->vba.Output[k] == dm_dp) { + mode_lib->vba.RequiresFEC[i][k] = + true; + } else { + mode_lib->vba.RequiresFEC[i][k] = + false; + } + mode_lib->vba.Outbpp = + mode_lib->vba.OutbppDSC; + } else { + mode_lib->vba.RequiresDSC[i][k] = false; + mode_lib->vba.RequiresFEC[i][k] = false; + } + mode_lib->vba.OutputBppPerState[i][k] = + mode_lib->vba.Outbpp; + } + if (mode_lib->vba.Outbpp == 0) { + mode_lib->vba.Outbpp = + TruncToValidBPP( + (1.0 + - mode_lib->vba.Downspreading + / 100.0) + * 540.0 + * mode_lib->vba.OutputLinkDPLanes[k] + / mode_lib->vba.PixelClockBackEnd[k] + * 8.0, + false, + mode_lib->vba.Output[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.DSCInputBitPerComponent[k]); + mode_lib->vba.OutbppDSC = + TruncToValidBPP( + (1.0 + - mode_lib->vba.Downspreading + / 100.0) + * (1.0 + - mode_lib->vba.EffectiveFECOverhead + / 100.0) + * 540.0 + * mode_lib->vba.OutputLinkDPLanes[k] + / mode_lib->vba.PixelClockBackEnd[k] + * 8.0, + true, + mode_lib->vba.Output[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.DSCInputBitPerComponent[k]); + if (mode_lib->vba.DSCEnabled[k] == true) { + mode_lib->vba.RequiresDSC[i][k] = true; + if (mode_lib->vba.Output[k] == dm_dp) { + mode_lib->vba.RequiresFEC[i][k] = + true; + } else { + mode_lib->vba.RequiresFEC[i][k] = + false; + } + mode_lib->vba.Outbpp = + mode_lib->vba.OutbppDSC; + } else { + mode_lib->vba.RequiresDSC[i][k] = false; + mode_lib->vba.RequiresFEC[i][k] = false; + } + mode_lib->vba.OutputBppPerState[i][k] = + mode_lib->vba.Outbpp; + } + if (mode_lib->vba.Outbpp == 0 + && mode_lib->vba.PHYCLKPerState[i] + >= 810.0) { + mode_lib->vba.Outbpp = + TruncToValidBPP( + (1.0 + - mode_lib->vba.Downspreading + / 100.0) + * 810.0 + * mode_lib->vba.OutputLinkDPLanes[k] + / mode_lib->vba.PixelClockBackEnd[k] + * 8.0, + false, + mode_lib->vba.Output[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.DSCInputBitPerComponent[k]); + mode_lib->vba.OutbppDSC = + TruncToValidBPP( + (1.0 + - mode_lib->vba.Downspreading + / 100.0) + * (1.0 + - mode_lib->vba.EffectiveFECOverhead + / 100.0) + * 810.0 + * mode_lib->vba.OutputLinkDPLanes[k] + / mode_lib->vba.PixelClockBackEnd[k] + * 8.0, + true, + mode_lib->vba.Output[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.DSCInputBitPerComponent[k]); + if (mode_lib->vba.DSCEnabled[k] == true + || mode_lib->vba.Outbpp == 0) { + mode_lib->vba.RequiresDSC[i][k] = true; + if (mode_lib->vba.Output[k] == dm_dp) { + mode_lib->vba.RequiresFEC[i][k] = + true; + } else { + mode_lib->vba.RequiresFEC[i][k] = + false; + } + mode_lib->vba.Outbpp = + mode_lib->vba.OutbppDSC; + } else { + mode_lib->vba.RequiresDSC[i][k] = false; + mode_lib->vba.RequiresFEC[i][k] = false; + } + mode_lib->vba.OutputBppPerState[i][k] = + mode_lib->vba.Outbpp; + } + } + } else { + mode_lib->vba.OutputBppPerState[i][k] = 0; + } + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.DIOSupport[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.OutputBppPerState[i][k] == 0 + || (mode_lib->vba.OutputFormat[k] == dm_420 + && mode_lib->vba.ProgressiveToInterlaceUnitInOPP + == true)) { + mode_lib->vba.DIOSupport[i] = false; + } + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.DSCCLKRequiredMoreThanSupported[i] = false; + if (mode_lib->vba.BlendingAndTiming[k] == k) { + if ((mode_lib->vba.Output[k] == dm_dp + || mode_lib->vba.Output[k] == dm_edp)) { + if (mode_lib->vba.OutputFormat[k] == dm_420 + || mode_lib->vba.OutputFormat[k] + == dm_n422) { + mode_lib->vba.DSCFormatFactor = 2; + } else { + mode_lib->vba.DSCFormatFactor = 1; + } + if (mode_lib->vba.RequiresDSC[i][k] == true) { + if (mode_lib->vba.ODMCombineEnablePerState[i][k] + == true) { + if (mode_lib->vba.PixelClockBackEnd[k] / 6.0 + / mode_lib->vba.DSCFormatFactor + > (1.0 + - mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + * mode_lib->vba.MaxDSCCLK[i]) { + mode_lib->vba.DSCCLKRequiredMoreThanSupported[i] = + true; + } + } else { + if (mode_lib->vba.PixelClockBackEnd[k] / 3.0 + / mode_lib->vba.DSCFormatFactor + > (1.0 + - mode_lib->vba.DISPCLKDPPCLKDSCCLKDownSpreading + / 100.0) + * mode_lib->vba.MaxDSCCLK[i]) { + mode_lib->vba.DSCCLKRequiredMoreThanSupported[i] = + true; + } + } + } + } + } + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.NotEnoughDSCUnits[i] = false; + mode_lib->vba.TotalDSCUnitsRequired = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.RequiresDSC[i][k] == true) { + if (mode_lib->vba.ODMCombineEnablePerState[i][k] == true) { + mode_lib->vba.TotalDSCUnitsRequired = + mode_lib->vba.TotalDSCUnitsRequired + 2.0; + } else { + mode_lib->vba.TotalDSCUnitsRequired = + mode_lib->vba.TotalDSCUnitsRequired + 1.0; + } + } + } + if (mode_lib->vba.TotalDSCUnitsRequired > mode_lib->vba.NumberOfDSC) { + mode_lib->vba.NotEnoughDSCUnits[i] = true; + } + } + /*DSC Delay per state*/ + + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.BlendingAndTiming[k] != k) { + mode_lib->vba.slices = 0; + } else if (mode_lib->vba.RequiresDSC[i][k] == 0 + || mode_lib->vba.RequiresDSC[i][k] == false) { + mode_lib->vba.slices = 0; + } else if (mode_lib->vba.PixelClockBackEnd[k] > 3200.0) { + mode_lib->vba.slices = dml_ceil( + mode_lib->vba.PixelClockBackEnd[k] / 400.0, + 4.0); + } else if (mode_lib->vba.PixelClockBackEnd[k] > 1360.0) { + mode_lib->vba.slices = 8.0; + } else if (mode_lib->vba.PixelClockBackEnd[k] > 680.0) { + mode_lib->vba.slices = 4.0; + } else if (mode_lib->vba.PixelClockBackEnd[k] > 340.0) { + mode_lib->vba.slices = 2.0; + } else { + mode_lib->vba.slices = 1.0; + } + if (mode_lib->vba.OutputBppPerState[i][k] == 0 + || mode_lib->vba.OutputBppPerState[i][k] == 0) { + mode_lib->vba.bpp = 0.0; + } else { + mode_lib->vba.bpp = mode_lib->vba.OutputBppPerState[i][k]; + } + if (mode_lib->vba.RequiresDSC[i][k] == true && mode_lib->vba.bpp != 0.0) { + if (mode_lib->vba.ODMCombineEnablePerState[i][k] == false) { + mode_lib->vba.DSCDelayPerState[i][k] = + dscceComputeDelay( + mode_lib->vba.DSCInputBitPerComponent[k], + mode_lib->vba.bpp, + dml_ceil( + mode_lib->vba.HActive[k] + / mode_lib->vba.slices, + 1.0), + mode_lib->vba.slices, + mode_lib->vba.OutputFormat[k]) + + dscComputeDelay( + mode_lib->vba.OutputFormat[k]); + } else { + mode_lib->vba.DSCDelayPerState[i][k] = + 2.0 + * (dscceComputeDelay( + mode_lib->vba.DSCInputBitPerComponent[k], + mode_lib->vba.bpp, + dml_ceil( + mode_lib->vba.HActive[k] + / mode_lib->vba.slices, + 1.0), + mode_lib->vba.slices + / 2, + mode_lib->vba.OutputFormat[k]) + + dscComputeDelay( + mode_lib->vba.OutputFormat[k])); + } + mode_lib->vba.DSCDelayPerState[i][k] = + mode_lib->vba.DSCDelayPerState[i][k] + * mode_lib->vba.PixelClock[k] + / mode_lib->vba.PixelClockBackEnd[k]; + } else { + mode_lib->vba.DSCDelayPerState[i][k] = 0.0; + } + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + for (j = 0; j <= mode_lib->vba.NumberOfActivePlanes - 1; j++) { + if (mode_lib->vba.BlendingAndTiming[k] == j + && mode_lib->vba.RequiresDSC[i][j] == true) { + mode_lib->vba.DSCDelayPerState[i][k] = + mode_lib->vba.DSCDelayPerState[i][j]; + } + } + } + } + /*Urgent Latency Support Check*/ + + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + if (mode_lib->vba.ODMCombineEnablePerState[i][k] == true) { + mode_lib->vba.SwathWidthYPerState[i][k] = + dml_min( + mode_lib->vba.SwathWidthYSingleDPP[k], + dml_round( + mode_lib->vba.HActive[k] + / 2.0 + * mode_lib->vba.HRatio[k])); + } else { + mode_lib->vba.SwathWidthYPerState[i][k] = + mode_lib->vba.SwathWidthYSingleDPP[k] + / mode_lib->vba.NoOfDPP[i][k]; + } + mode_lib->vba.SwathWidthGranularityY = 256.0 + / dml_ceil(mode_lib->vba.BytePerPixelInDETY[k], 1.0) + / mode_lib->vba.MaxSwathHeightY[k]; + mode_lib->vba.RoundedUpMaxSwathSizeBytesY = (dml_ceil( + mode_lib->vba.SwathWidthYPerState[i][k] - 1.0, + mode_lib->vba.SwathWidthGranularityY) + + mode_lib->vba.SwathWidthGranularityY) + * mode_lib->vba.BytePerPixelInDETY[k] + * mode_lib->vba.MaxSwathHeightY[k]; + if (mode_lib->vba.SourcePixelFormat[k] == dm_420_10) { + mode_lib->vba.RoundedUpMaxSwathSizeBytesY = dml_ceil( + mode_lib->vba.RoundedUpMaxSwathSizeBytesY, + 256.0) + 256; + } + if (mode_lib->vba.MaxSwathHeightC[k] > 0.0) { + mode_lib->vba.SwathWidthGranularityC = 256.0 + / dml_ceil(mode_lib->vba.BytePerPixelInDETC[k], 2.0) + / mode_lib->vba.MaxSwathHeightC[k]; + mode_lib->vba.RoundedUpMaxSwathSizeBytesC = (dml_ceil( + mode_lib->vba.SwathWidthYPerState[i][k] / 2.0 - 1.0, + mode_lib->vba.SwathWidthGranularityC) + + mode_lib->vba.SwathWidthGranularityC) + * mode_lib->vba.BytePerPixelInDETC[k] + * mode_lib->vba.MaxSwathHeightC[k]; + if (mode_lib->vba.SourcePixelFormat[k] == dm_420_10) { + mode_lib->vba.RoundedUpMaxSwathSizeBytesC = dml_ceil( + mode_lib->vba.RoundedUpMaxSwathSizeBytesC, + 256.0) + 256; + } + } else { + mode_lib->vba.RoundedUpMaxSwathSizeBytesC = 0.0; + } + if (mode_lib->vba.RoundedUpMaxSwathSizeBytesY + + mode_lib->vba.RoundedUpMaxSwathSizeBytesC + <= mode_lib->vba.DETBufferSizeInKByte * 1024.0 / 2.0) { + mode_lib->vba.SwathHeightYPerState[i][k] = + mode_lib->vba.MaxSwathHeightY[k]; + mode_lib->vba.SwathHeightCPerState[i][k] = + mode_lib->vba.MaxSwathHeightC[k]; + } else { + mode_lib->vba.SwathHeightYPerState[i][k] = + mode_lib->vba.MinSwathHeightY[k]; + mode_lib->vba.SwathHeightCPerState[i][k] = + mode_lib->vba.MinSwathHeightC[k]; + } + if (mode_lib->vba.BytePerPixelInDETC[k] == 0.0) { + mode_lib->vba.LinesInDETLuma = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 / mode_lib->vba.BytePerPixelInDETY[k] + / mode_lib->vba.SwathWidthYPerState[i][k]; + mode_lib->vba.LinesInDETChroma = 0.0; + } else if (mode_lib->vba.SwathHeightYPerState[i][k] + <= mode_lib->vba.SwathHeightCPerState[i][k]) { + mode_lib->vba.LinesInDETLuma = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 / 2.0 / mode_lib->vba.BytePerPixelInDETY[k] + / mode_lib->vba.SwathWidthYPerState[i][k]; + mode_lib->vba.LinesInDETChroma = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 / 2.0 / mode_lib->vba.BytePerPixelInDETC[k] + / (mode_lib->vba.SwathWidthYPerState[i][k] / 2.0); + } else { + mode_lib->vba.LinesInDETLuma = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 * 2.0 / 3.0 + / mode_lib->vba.BytePerPixelInDETY[k] + / mode_lib->vba.SwathWidthYPerState[i][k]; + mode_lib->vba.LinesInDETChroma = mode_lib->vba.DETBufferSizeInKByte + * 1024.0 / 3.0 / mode_lib->vba.BytePerPixelInDETY[k] + / (mode_lib->vba.SwathWidthYPerState[i][k] / 2.0); + } + mode_lib->vba.EffectiveLBLatencyHidingSourceLinesLuma = + dml_min( + mode_lib->vba.MaxLineBufferLines, + dml_floor( + mode_lib->vba.LineBufferSize + / mode_lib->vba.LBBitPerPixel[k] + / (mode_lib->vba.SwathWidthYPerState[i][k] + / dml_max( + mode_lib->vba.HRatio[k], + 1.0)), + 1.0)) + - (mode_lib->vba.vtaps[k] - 1.0); + mode_lib->vba.EffectiveLBLatencyHidingSourceLinesChroma = + dml_min( + mode_lib->vba.MaxLineBufferLines, + dml_floor( + mode_lib->vba.LineBufferSize + / mode_lib->vba.LBBitPerPixel[k] + / (mode_lib->vba.SwathWidthYPerState[i][k] + / 2.0 + / dml_max( + mode_lib->vba.HRatio[k] + / 2.0, + 1.0)), + 1.0)) + - (mode_lib->vba.VTAPsChroma[k] - 1.0); + mode_lib->vba.EffectiveDETLBLinesLuma = + dml_floor( + mode_lib->vba.LinesInDETLuma + + dml_min( + mode_lib->vba.LinesInDETLuma + * mode_lib->vba.RequiredDISPCLK[i] + * mode_lib->vba.BytePerPixelInDETY[k] + * mode_lib->vba.PSCL_FACTOR[k] + / mode_lib->vba.ReturnBWPerState[i], + mode_lib->vba.EffectiveLBLatencyHidingSourceLinesLuma), + mode_lib->vba.SwathHeightYPerState[i][k]); + mode_lib->vba.EffectiveDETLBLinesChroma = + dml_floor( + mode_lib->vba.LinesInDETChroma + + dml_min( + mode_lib->vba.LinesInDETChroma + * mode_lib->vba.RequiredDISPCLK[i] + * mode_lib->vba.BytePerPixelInDETC[k] + * mode_lib->vba.PSCL_FACTOR_CHROMA[k] + / mode_lib->vba.ReturnBWPerState[i], + mode_lib->vba.EffectiveLBLatencyHidingSourceLinesChroma), + mode_lib->vba.SwathHeightCPerState[i][k]); + if (mode_lib->vba.BytePerPixelInDETC[k] == 0.0) { + mode_lib->vba.UrgentLatencySupportUsPerState[i][k] = + mode_lib->vba.EffectiveDETLBLinesLuma + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + / mode_lib->vba.VRatio[k] + - mode_lib->vba.EffectiveDETLBLinesLuma + * mode_lib->vba.SwathWidthYPerState[i][k] + * dml_ceil( + mode_lib->vba.BytePerPixelInDETY[k], + 1.0) + / (mode_lib->vba.ReturnBWPerState[i] + / mode_lib->vba.NoOfDPP[i][k]); + } else { + mode_lib->vba.UrgentLatencySupportUsPerState[i][k] = + dml_min( + mode_lib->vba.EffectiveDETLBLinesLuma + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + / mode_lib->vba.VRatio[k] + - mode_lib->vba.EffectiveDETLBLinesLuma + * mode_lib->vba.SwathWidthYPerState[i][k] + * dml_ceil( + mode_lib->vba.BytePerPixelInDETY[k], + 1.0) + / (mode_lib->vba.ReturnBWPerState[i] + / mode_lib->vba.NoOfDPP[i][k]), + mode_lib->vba.EffectiveDETLBLinesChroma + * (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]) + / (mode_lib->vba.VRatio[k] + / 2.0) + - mode_lib->vba.EffectiveDETLBLinesChroma + * mode_lib->vba.SwathWidthYPerState[i][k] + / 2.0 + * dml_ceil( + mode_lib->vba.BytePerPixelInDETC[k], + 2.0) + / (mode_lib->vba.ReturnBWPerState[i] + / mode_lib->vba.NoOfDPP[i][k])); + } + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.UrgentLatencySupport[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.UrgentLatencySupportUsPerState[i][k] + < mode_lib->vba.UrgentLatency / 1.0) { + mode_lib->vba.UrgentLatencySupport[i] = false; + } + } + } + /*Prefetch Check*/ + + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.TotalNumberOfDCCActiveDPP[i] = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.DCCEnable[k] == true) { + mode_lib->vba.TotalNumberOfDCCActiveDPP[i] = + mode_lib->vba.TotalNumberOfDCCActiveDPP[i] + + mode_lib->vba.NoOfDPP[i][k]; + } + } + } + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.ProjectedDCFCLKDeepSleep = 8.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.ProjectedDCFCLKDeepSleep = dml_max( + mode_lib->vba.ProjectedDCFCLKDeepSleep, + mode_lib->vba.PixelClock[k] / 16.0); + if (mode_lib->vba.BytePerPixelInDETC[k] == 0.0) { + if (mode_lib->vba.VRatio[k] <= 1.0) { + mode_lib->vba.ProjectedDCFCLKDeepSleep = + dml_max( + mode_lib->vba.ProjectedDCFCLKDeepSleep, + 1.1 + * dml_ceil( + mode_lib->vba.BytePerPixelInDETY[k], + 1.0) + / 64.0 + * mode_lib->vba.HRatio[k] + * mode_lib->vba.PixelClock[k] + / mode_lib->vba.NoOfDPP[i][k]); + } else { + mode_lib->vba.ProjectedDCFCLKDeepSleep = + dml_max( + mode_lib->vba.ProjectedDCFCLKDeepSleep, + 1.1 + * dml_ceil( + mode_lib->vba.BytePerPixelInDETY[k], + 1.0) + / 64.0 + * mode_lib->vba.PSCL_FACTOR[k] + * mode_lib->vba.RequiredDPPCLK[i][k]); + } + } else { + if (mode_lib->vba.VRatio[k] <= 1.0) { + mode_lib->vba.ProjectedDCFCLKDeepSleep = + dml_max( + mode_lib->vba.ProjectedDCFCLKDeepSleep, + 1.1 + * dml_ceil( + mode_lib->vba.BytePerPixelInDETY[k], + 1.0) + / 32.0 + * mode_lib->vba.HRatio[k] + * mode_lib->vba.PixelClock[k] + / mode_lib->vba.NoOfDPP[i][k]); + } else { + mode_lib->vba.ProjectedDCFCLKDeepSleep = + dml_max( + mode_lib->vba.ProjectedDCFCLKDeepSleep, + 1.1 + * dml_ceil( + mode_lib->vba.BytePerPixelInDETY[k], + 1.0) + / 32.0 + * mode_lib->vba.PSCL_FACTOR[k] + * mode_lib->vba.RequiredDPPCLK[i][k]); + } + if (mode_lib->vba.VRatio[k] / 2.0 <= 1.0) { + mode_lib->vba.ProjectedDCFCLKDeepSleep = + dml_max( + mode_lib->vba.ProjectedDCFCLKDeepSleep, + 1.1 + * dml_ceil( + mode_lib->vba.BytePerPixelInDETC[k], + 2.0) + / 32.0 + * mode_lib->vba.HRatio[k] + / 2.0 + * mode_lib->vba.PixelClock[k] + / mode_lib->vba.NoOfDPP[i][k]); + } else { + mode_lib->vba.ProjectedDCFCLKDeepSleep = + dml_max( + mode_lib->vba.ProjectedDCFCLKDeepSleep, + 1.1 + * dml_ceil( + mode_lib->vba.BytePerPixelInDETC[k], + 2.0) + / 32.0 + * mode_lib->vba.PSCL_FACTOR_CHROMA[k] + * mode_lib->vba.RequiredDPPCLK[i][k]); + } + } + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.PDEAndMetaPTEBytesPerFrameY = CalculateVMAndRowBytes( + mode_lib, + mode_lib->vba.DCCEnable[k], + mode_lib->vba.Read256BlockHeightY[k], + mode_lib->vba.Read256BlockWidthY[k], + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.SurfaceTiling[k], + dml_ceil(mode_lib->vba.BytePerPixelInDETY[k], 1.0), + mode_lib->vba.SourceScan[k], + mode_lib->vba.ViewportWidth[k], + mode_lib->vba.ViewportHeight[k], + mode_lib->vba.SwathWidthYPerState[i][k], + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.VMMPageSize, + mode_lib->vba.PTEBufferSizeInRequests, + mode_lib->vba.PDEProcessingBufIn64KBReqs, + mode_lib->vba.PitchY[k], + mode_lib->vba.DCCMetaPitchY[k], + &mode_lib->vba.MacroTileWidthY[k], + &mode_lib->vba.MetaRowBytesY, + &mode_lib->vba.DPTEBytesPerRowY, + &mode_lib->vba.PTEBufferSizeNotExceededY[i][k], + &mode_lib->vba.dpte_row_height[k], + &mode_lib->vba.meta_row_height[k]); + mode_lib->vba.PrefetchLinesY[k] = CalculatePrefetchSourceLines( + mode_lib, + mode_lib->vba.VRatio[k], + mode_lib->vba.vtaps[k], + mode_lib->vba.Interlace[k], + mode_lib->vba.ProgressiveToInterlaceUnitInOPP, + mode_lib->vba.SwathHeightYPerState[i][k], + mode_lib->vba.ViewportYStartY[k], + &mode_lib->vba.PrefillY[k], + &mode_lib->vba.MaxNumSwY[k]); + if ((mode_lib->vba.SourcePixelFormat[k] != dm_444_64 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_32 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_8)) { + mode_lib->vba.PDEAndMetaPTEBytesPerFrameC = CalculateVMAndRowBytes( + mode_lib, + mode_lib->vba.DCCEnable[k], + mode_lib->vba.Read256BlockHeightY[k], + mode_lib->vba.Read256BlockWidthY[k], + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.SurfaceTiling[k], + dml_ceil(mode_lib->vba.BytePerPixelInDETC[k], 2.0), + mode_lib->vba.SourceScan[k], + mode_lib->vba.ViewportWidth[k] / 2.0, + mode_lib->vba.ViewportHeight[k] / 2.0, + mode_lib->vba.SwathWidthYPerState[i][k] / 2.0, + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.VMMPageSize, + mode_lib->vba.PTEBufferSizeInRequests, + mode_lib->vba.PDEProcessingBufIn64KBReqs, + mode_lib->vba.PitchC[k], + 0.0, + &mode_lib->vba.MacroTileWidthC[k], + &mode_lib->vba.MetaRowBytesC, + &mode_lib->vba.DPTEBytesPerRowC, + &mode_lib->vba.PTEBufferSizeNotExceededC[i][k], + &mode_lib->vba.dpte_row_height_chroma[k], + &mode_lib->vba.meta_row_height_chroma[k]); + mode_lib->vba.PrefetchLinesC[k] = CalculatePrefetchSourceLines( + mode_lib, + mode_lib->vba.VRatio[k] / 2.0, + mode_lib->vba.VTAPsChroma[k], + mode_lib->vba.Interlace[k], + mode_lib->vba.ProgressiveToInterlaceUnitInOPP, + mode_lib->vba.SwathHeightCPerState[i][k], + mode_lib->vba.ViewportYStartC[k], + &mode_lib->vba.PrefillC[k], + &mode_lib->vba.MaxNumSwC[k]); + } else { + mode_lib->vba.PDEAndMetaPTEBytesPerFrameC = 0.0; + mode_lib->vba.MetaRowBytesC = 0.0; + mode_lib->vba.DPTEBytesPerRowC = 0.0; + mode_lib->vba.PrefetchLinesC[k] = 0.0; + mode_lib->vba.PTEBufferSizeNotExceededC[i][k] = true; + } + mode_lib->vba.PDEAndMetaPTEBytesPerFrame[k] = + mode_lib->vba.PDEAndMetaPTEBytesPerFrameY + + mode_lib->vba.PDEAndMetaPTEBytesPerFrameC; + mode_lib->vba.MetaRowBytes[k] = mode_lib->vba.MetaRowBytesY + + mode_lib->vba.MetaRowBytesC; + mode_lib->vba.DPTEBytesPerRow[k] = mode_lib->vba.DPTEBytesPerRowY + + mode_lib->vba.DPTEBytesPerRowC; + } + mode_lib->vba.ExtraLatency = + mode_lib->vba.UrgentRoundTripAndOutOfOrderLatencyPerState[i] + + (mode_lib->vba.TotalNumberOfActiveDPP[i] + * mode_lib->vba.PixelChunkSizeInKByte + + mode_lib->vba.TotalNumberOfDCCActiveDPP[i] + * mode_lib->vba.MetaChunkSize) + * 1024.0 + / mode_lib->vba.ReturnBWPerState[i]; + if (mode_lib->vba.VirtualMemoryEnable == true) { + mode_lib->vba.ExtraLatency = mode_lib->vba.ExtraLatency + + mode_lib->vba.TotalNumberOfActiveDPP[i] + * mode_lib->vba.PTEChunkSize * 1024.0 + / mode_lib->vba.ReturnBWPerState[i]; + } + mode_lib->vba.TimeCalc = 24.0 / mode_lib->vba.ProjectedDCFCLKDeepSleep; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.BlendingAndTiming[k] == k) { + if (mode_lib->vba.WritebackEnable[k] == true) { + mode_lib->vba.WritebackDelay[i][k] = + mode_lib->vba.WritebackLatency + + CalculateWriteBackDelay( + mode_lib->vba.WritebackPixelFormat[k], + mode_lib->vba.WritebackHRatio[k], + mode_lib->vba.WritebackVRatio[k], + mode_lib->vba.WritebackLumaHTaps[k], + mode_lib->vba.WritebackLumaVTaps[k], + mode_lib->vba.WritebackChromaHTaps[k], + mode_lib->vba.WritebackChromaVTaps[k], + mode_lib->vba.WritebackDestinationWidth[k]) + / mode_lib->vba.RequiredDISPCLK[i]; + } else { + mode_lib->vba.WritebackDelay[i][k] = 0.0; + } + for (j = 0; j <= mode_lib->vba.NumberOfActivePlanes - 1; j++) { + if (mode_lib->vba.BlendingAndTiming[j] == k + && mode_lib->vba.WritebackEnable[j] + == true) { + mode_lib->vba.WritebackDelay[i][k] = + dml_max( + mode_lib->vba.WritebackDelay[i][k], + mode_lib->vba.WritebackLatency + + CalculateWriteBackDelay( + mode_lib->vba.WritebackPixelFormat[j], + mode_lib->vba.WritebackHRatio[j], + mode_lib->vba.WritebackVRatio[j], + mode_lib->vba.WritebackLumaHTaps[j], + mode_lib->vba.WritebackLumaVTaps[j], + mode_lib->vba.WritebackChromaHTaps[j], + mode_lib->vba.WritebackChromaVTaps[j], + mode_lib->vba.WritebackDestinationWidth[j]) + / mode_lib->vba.RequiredDISPCLK[i]); + } + } + } + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + for (j = 0; j <= mode_lib->vba.NumberOfActivePlanes - 1; j++) { + if (mode_lib->vba.BlendingAndTiming[k] == j) { + mode_lib->vba.WritebackDelay[i][k] = + mode_lib->vba.WritebackDelay[i][j]; + } + } + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.MaximumVStartup[k] = + mode_lib->vba.VTotal[k] - mode_lib->vba.VActive[k] + - dml_max( + 1.0, + dml_ceil( + mode_lib->vba.WritebackDelay[i][k] + / (mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]), + 1.0)); + } + mode_lib->vba.TWait = CalculateTWait( + mode_lib->vba.PrefetchMode, + mode_lib->vba.DRAMClockChangeLatency, + mode_lib->vba.UrgentLatency, + mode_lib->vba.SREnterPlusExitTime); + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.XFCEnabled[k] == true) { + mode_lib->vba.XFCRemoteSurfaceFlipDelay = + CalculateRemoteSurfaceFlipDelay( + mode_lib, + mode_lib->vba.VRatio[k], + mode_lib->vba.SwathWidthYPerState[i][k], + dml_ceil( + mode_lib->vba.BytePerPixelInDETY[k], + 1.0), + mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k], + mode_lib->vba.XFCTSlvVupdateOffset, + mode_lib->vba.XFCTSlvVupdateWidth, + mode_lib->vba.XFCTSlvVreadyOffset, + mode_lib->vba.XFCXBUFLatencyTolerance, + mode_lib->vba.XFCFillBWOverhead, + mode_lib->vba.XFCSlvChunkSize, + mode_lib->vba.XFCBusTransportTime, + mode_lib->vba.TimeCalc, + mode_lib->vba.TWait, + &mode_lib->vba.SrcActiveDrainRate, + &mode_lib->vba.TInitXFill, + &mode_lib->vba.TslvChk); + } else { + mode_lib->vba.XFCRemoteSurfaceFlipDelay = 0.0; + } + mode_lib->vba.IsErrorResult[i][k] = + CalculatePrefetchSchedule( + mode_lib, + mode_lib->vba.RequiredDPPCLK[i][k], + mode_lib->vba.RequiredDISPCLK[i], + mode_lib->vba.PixelClock[k], + mode_lib->vba.ProjectedDCFCLKDeepSleep, + mode_lib->vba.DSCDelayPerState[i][k], + mode_lib->vba.NoOfDPP[i][k], + mode_lib->vba.ScalerEnabled[k], + mode_lib->vba.NumberOfCursors[k], + mode_lib->vba.DPPCLKDelaySubtotal, + mode_lib->vba.DPPCLKDelaySCL, + mode_lib->vba.DPPCLKDelaySCLLBOnly, + mode_lib->vba.DPPCLKDelayCNVCFormater, + mode_lib->vba.DPPCLKDelayCNVCCursor, + mode_lib->vba.DISPCLKDelaySubtotal, + mode_lib->vba.SwathWidthYPerState[i][k] + / mode_lib->vba.HRatio[k], + mode_lib->vba.OutputFormat[k], + mode_lib->vba.VTotal[k] + - mode_lib->vba.VActive[k], + mode_lib->vba.HTotal[k], + mode_lib->vba.MaxInterDCNTileRepeaters, + mode_lib->vba.MaximumVStartup[k], + mode_lib->vba.MaxPageTableLevels, + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.DynamicMetadataEnable[k], + mode_lib->vba.DynamicMetadataLinesBeforeActiveRequired[k], + mode_lib->vba.DynamicMetadataTransmittedBytes[k], + mode_lib->vba.DCCEnable[k], + mode_lib->vba.UrgentLatency, + mode_lib->vba.ExtraLatency, + mode_lib->vba.TimeCalc, + mode_lib->vba.PDEAndMetaPTEBytesPerFrame[k], + mode_lib->vba.MetaRowBytes[k], + mode_lib->vba.DPTEBytesPerRow[k], + mode_lib->vba.PrefetchLinesY[k], + mode_lib->vba.SwathWidthYPerState[i][k], + mode_lib->vba.BytePerPixelInDETY[k], + mode_lib->vba.PrefillY[k], + mode_lib->vba.MaxNumSwY[k], + mode_lib->vba.PrefetchLinesC[k], + mode_lib->vba.BytePerPixelInDETC[k], + mode_lib->vba.PrefillC[k], + mode_lib->vba.MaxNumSwC[k], + mode_lib->vba.SwathHeightYPerState[i][k], + mode_lib->vba.SwathHeightCPerState[i][k], + mode_lib->vba.TWait, + mode_lib->vba.XFCEnabled[k], + mode_lib->vba.XFCRemoteSurfaceFlipDelay, + mode_lib->vba.Interlace[k], + mode_lib->vba.ProgressiveToInterlaceUnitInOPP, + mode_lib->vba.DSTXAfterScaler, + mode_lib->vba.DSTYAfterScaler, + &mode_lib->vba.LineTimesForPrefetch[k], + &mode_lib->vba.PrefetchBW[k], + &mode_lib->vba.LinesForMetaPTE[k], + &mode_lib->vba.LinesForMetaAndDPTERow[k], + &mode_lib->vba.VRatioPreY[i][k], + &mode_lib->vba.VRatioPreC[i][k], + &mode_lib->vba.RequiredPrefetchPixelDataBW[i][k], + &mode_lib->vba.VStartupRequiredWhenNotEnoughTimeForDynamicMetadata, + &mode_lib->vba.Tno_bw[k], + &mode_lib->vba.VUpdateOffsetPix[k], + &mode_lib->vba.VUpdateWidthPix[k], + &mode_lib->vba.VReadyOffsetPix[k]); + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.cursor_bw[k] = mode_lib->vba.NumberOfCursors[k] + * mode_lib->vba.CursorWidth[k][0] + * mode_lib->vba.CursorBPP[k][0] / 8.0 + / (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]) + * mode_lib->vba.VRatio[k]; + } + mode_lib->vba.MaximumReadBandwidthWithPrefetch = 0.0; + mode_lib->vba.prefetch_vm_bw_valid = true; + mode_lib->vba.prefetch_row_bw_valid = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.PDEAndMetaPTEBytesPerFrame[k] == 0.0) { + mode_lib->vba.prefetch_vm_bw[k] = 0.0; + } else if (mode_lib->vba.LinesForMetaPTE[k] > 0.0) { + mode_lib->vba.prefetch_vm_bw[k] = + mode_lib->vba.PDEAndMetaPTEBytesPerFrame[k] + / (mode_lib->vba.LinesForMetaPTE[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + } else { + mode_lib->vba.prefetch_vm_bw[k] = 0.0; + mode_lib->vba.prefetch_vm_bw_valid = false; + } + if (mode_lib->vba.MetaRowBytes[k] + mode_lib->vba.DPTEBytesPerRow[k] + == 0.0) { + mode_lib->vba.prefetch_row_bw[k] = 0.0; + } else if (mode_lib->vba.LinesForMetaAndDPTERow[k] > 0.0) { + mode_lib->vba.prefetch_row_bw[k] = (mode_lib->vba.MetaRowBytes[k] + + mode_lib->vba.DPTEBytesPerRow[k]) + / (mode_lib->vba.LinesForMetaAndDPTERow[k] + * mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k]); + } else { + mode_lib->vba.prefetch_row_bw[k] = 0.0; + mode_lib->vba.prefetch_row_bw_valid = false; + } + mode_lib->vba.MaximumReadBandwidthWithPrefetch = + mode_lib->vba.MaximumReadBandwidthWithPrefetch + + mode_lib->vba.cursor_bw[k] + + dml_max4( + mode_lib->vba.prefetch_vm_bw[k], + mode_lib->vba.prefetch_row_bw[k], + mode_lib->vba.ReadBandwidth[k], + mode_lib->vba.RequiredPrefetchPixelDataBW[i][k]); + } + mode_lib->vba.PrefetchSupported[i] = true; + if (mode_lib->vba.MaximumReadBandwidthWithPrefetch + > mode_lib->vba.ReturnBWPerState[i] + || mode_lib->vba.prefetch_vm_bw_valid == false + || mode_lib->vba.prefetch_row_bw_valid == false) { + mode_lib->vba.PrefetchSupported[i] = false; + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.LineTimesForPrefetch[k] < 2.0 + || mode_lib->vba.LinesForMetaPTE[k] >= 8.0 + || mode_lib->vba.LinesForMetaAndDPTERow[k] >= 16.0 + || mode_lib->vba.IsErrorResult[i][k] == true) { + mode_lib->vba.PrefetchSupported[i] = false; + } + } + mode_lib->vba.VRatioInPrefetchSupported[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.VRatioPreY[i][k] > 4.0 + || mode_lib->vba.VRatioPreC[i][k] > 4.0 + || mode_lib->vba.IsErrorResult[i][k] == true) { + mode_lib->vba.VRatioInPrefetchSupported[i] = false; + } + } + if (mode_lib->vba.PrefetchSupported[i] == true + && mode_lib->vba.VRatioInPrefetchSupported[i] == true) { + mode_lib->vba.BandwidthAvailableForImmediateFlip = + mode_lib->vba.ReturnBWPerState[i]; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.BandwidthAvailableForImmediateFlip = + mode_lib->vba.BandwidthAvailableForImmediateFlip + - mode_lib->vba.cursor_bw[k] + - dml_max( + mode_lib->vba.ReadBandwidth[k], + mode_lib->vba.PrefetchBW[k]); + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.ImmediateFlipBytes[k] = 0.0; + if ((mode_lib->vba.SourcePixelFormat[k] != dm_420_8 + && mode_lib->vba.SourcePixelFormat[k] != dm_420_10)) { + mode_lib->vba.ImmediateFlipBytes[k] = + mode_lib->vba.PDEAndMetaPTEBytesPerFrame[k] + + mode_lib->vba.MetaRowBytes[k] + + mode_lib->vba.DPTEBytesPerRow[k]; + } + } + mode_lib->vba.TotImmediateFlipBytes = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if ((mode_lib->vba.SourcePixelFormat[k] != dm_420_8 + && mode_lib->vba.SourcePixelFormat[k] != dm_420_10)) { + mode_lib->vba.TotImmediateFlipBytes = + mode_lib->vba.TotImmediateFlipBytes + + mode_lib->vba.ImmediateFlipBytes[k]; + } + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + CalculateFlipSchedule( + mode_lib, + mode_lib->vba.ExtraLatency, + mode_lib->vba.UrgentLatency, + mode_lib->vba.MaxPageTableLevels, + mode_lib->vba.VirtualMemoryEnable, + mode_lib->vba.BandwidthAvailableForImmediateFlip, + mode_lib->vba.TotImmediateFlipBytes, + mode_lib->vba.SourcePixelFormat[k], + mode_lib->vba.ImmediateFlipBytes[k], + mode_lib->vba.HTotal[k] + / mode_lib->vba.PixelClock[k], + mode_lib->vba.VRatio[k], + mode_lib->vba.Tno_bw[k], + mode_lib->vba.PDEAndMetaPTEBytesPerFrame[k], + mode_lib->vba.MetaRowBytes[k], + mode_lib->vba.DPTEBytesPerRow[k], + mode_lib->vba.DCCEnable[k], + mode_lib->vba.dpte_row_height[k], + mode_lib->vba.meta_row_height[k], + mode_lib->vba.qual_row_bw[k], + &mode_lib->vba.DestinationLinesToRequestVMInImmediateFlip[k], + &mode_lib->vba.DestinationLinesToRequestRowInImmediateFlip[k], + &mode_lib->vba.final_flip_bw[k], + &mode_lib->vba.ImmediateFlipSupportedForPipe[k]); + } + mode_lib->vba.total_dcn_read_bw_with_flip = 0.0; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.total_dcn_read_bw_with_flip = + mode_lib->vba.total_dcn_read_bw_with_flip + + mode_lib->vba.cursor_bw[k] + + dml_max3( + mode_lib->vba.prefetch_vm_bw[k], + mode_lib->vba.prefetch_row_bw[k], + mode_lib->vba.final_flip_bw[k] + + dml_max( + mode_lib->vba.ReadBandwidth[k], + mode_lib->vba.RequiredPrefetchPixelDataBW[i][k])); + } + mode_lib->vba.ImmediateFlipSupportedForState[i] = true; + if (mode_lib->vba.total_dcn_read_bw_with_flip + > mode_lib->vba.ReturnBWPerState[i]) { + mode_lib->vba.ImmediateFlipSupportedForState[i] = false; + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.ImmediateFlipSupportedForPipe[k] == false) { + mode_lib->vba.ImmediateFlipSupportedForState[i] = false; + } + } + } else { + mode_lib->vba.ImmediateFlipSupportedForState[i] = false; + } + } + /*PTE Buffer Size Check*/ + + for (i = 0; i <= DC__VOLTAGE_STATES; i++) { + mode_lib->vba.PTEBufferSizeNotExceeded[i] = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.PTEBufferSizeNotExceededY[i][k] == false + || mode_lib->vba.PTEBufferSizeNotExceededC[i][k] == false) { + mode_lib->vba.PTEBufferSizeNotExceeded[i] = false; + } + } + } + /*Cursor Support Check*/ + + mode_lib->vba.CursorSupport = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.CursorWidth[k][0] > 0.0) { + if (dml_floor( + dml_floor( + mode_lib->vba.CursorBufferSize + - mode_lib->vba.CursorChunkSize, + mode_lib->vba.CursorChunkSize) * 1024.0 + / (mode_lib->vba.CursorWidth[k][0] + * mode_lib->vba.CursorBPP[k][0] + / 8.0), + 1.0) + * (mode_lib->vba.HTotal[k] / mode_lib->vba.PixelClock[k]) + / mode_lib->vba.VRatio[k] < mode_lib->vba.UrgentLatency + || (mode_lib->vba.CursorBPP[k][0] == 64.0 + && mode_lib->vba.Cursor64BppSupport == false)) { + mode_lib->vba.CursorSupport = false; + } + } + } + /*Valid Pitch Check*/ + + mode_lib->vba.PitchSupport = true; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.AlignedYPitch[k] = dml_ceil( + dml_max(mode_lib->vba.PitchY[k], mode_lib->vba.ViewportWidth[k]), + mode_lib->vba.MacroTileWidthY[k]); + if (mode_lib->vba.AlignedYPitch[k] > mode_lib->vba.PitchY[k]) { + mode_lib->vba.PitchSupport = false; + } + if (mode_lib->vba.DCCEnable[k] == true) { + mode_lib->vba.AlignedDCCMetaPitch[k] = dml_ceil( + dml_max( + mode_lib->vba.DCCMetaPitchY[k], + mode_lib->vba.ViewportWidth[k]), + 64.0 * mode_lib->vba.Read256BlockWidthY[k]); + } else { + mode_lib->vba.AlignedDCCMetaPitch[k] = mode_lib->vba.DCCMetaPitchY[k]; + } + if (mode_lib->vba.AlignedDCCMetaPitch[k] > mode_lib->vba.DCCMetaPitchY[k]) { + mode_lib->vba.PitchSupport = false; + } + if (mode_lib->vba.SourcePixelFormat[k] != dm_444_64 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_32 + && mode_lib->vba.SourcePixelFormat[k] != dm_444_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_16 + && mode_lib->vba.SourcePixelFormat[k] != dm_mono_8) { + mode_lib->vba.AlignedCPitch[k] = dml_ceil( + dml_max( + mode_lib->vba.PitchC[k], + mode_lib->vba.ViewportWidth[k] / 2.0), + mode_lib->vba.MacroTileWidthC[k]); + } else { + mode_lib->vba.AlignedCPitch[k] = mode_lib->vba.PitchC[k]; + } + if (mode_lib->vba.AlignedCPitch[k] > mode_lib->vba.PitchC[k]) { + mode_lib->vba.PitchSupport = false; + } + } + /*Mode Support, Voltage State and SOC Configuration*/ + + for (i = DC__VOLTAGE_STATES; i >= 0; i--) { + if (mode_lib->vba.ScaleRatioAndTapsSupport == true + && mode_lib->vba.SourceFormatPixelAndScanSupport == true + && mode_lib->vba.ViewportSizeSupport[i] == true + && mode_lib->vba.BandwidthSupport[i] == true + && mode_lib->vba.DIOSupport[i] == true + && mode_lib->vba.NotEnoughDSCUnits[i] == false + && mode_lib->vba.DSCCLKRequiredMoreThanSupported[i] == false + && mode_lib->vba.UrgentLatencySupport[i] == true + && mode_lib->vba.ROBSupport[i] == true + && mode_lib->vba.DISPCLK_DPPCLK_Support[i] == true + && mode_lib->vba.TotalAvailablePipesSupport[i] == true + && mode_lib->vba.NumberOfOTGSupport == true + && mode_lib->vba.WritebackModeSupport == true + && mode_lib->vba.WritebackLatencySupport == true + && mode_lib->vba.WritebackScaleRatioAndTapsSupport == true + && mode_lib->vba.CursorSupport == true + && mode_lib->vba.PitchSupport == true + && mode_lib->vba.PrefetchSupported[i] == true + && mode_lib->vba.VRatioInPrefetchSupported[i] == true + && mode_lib->vba.PTEBufferSizeNotExceeded[i] == true + && mode_lib->vba.NonsupportedDSCInputBPC == false) { + mode_lib->vba.ModeSupport[i] = true; + } else { + mode_lib->vba.ModeSupport[i] = false; + } + } + for (i = DC__VOLTAGE_STATES; i >= 0; i--) { + if (i == DC__VOLTAGE_STATES || mode_lib->vba.ModeSupport[i] == true) { + mode_lib->vba.VoltageLevel = i; + } + } + mode_lib->vba.DCFCLK = mode_lib->vba.DCFCLKPerState[mode_lib->vba.VoltageLevel]; + mode_lib->vba.DRAMSpeed = mode_lib->vba.DRAMSpeedPerState[mode_lib->vba.VoltageLevel]; + mode_lib->vba.FabricClock = mode_lib->vba.FabricClockPerState[mode_lib->vba.VoltageLevel]; + mode_lib->vba.SOCCLK = mode_lib->vba.SOCCLKPerState[mode_lib->vba.VoltageLevel]; + mode_lib->vba.FabricAndDRAMBandwidth = + mode_lib->vba.FabricAndDRAMBandwidthPerState[mode_lib->vba.VoltageLevel]; + mode_lib->vba.ImmediateFlipSupport = + mode_lib->vba.ImmediateFlipSupportedForState[mode_lib->vba.VoltageLevel]; + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + mode_lib->vba.DPPPerPlane[k] = mode_lib->vba.NoOfDPP[mode_lib->vba.VoltageLevel][k]; + } + for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { + if (mode_lib->vba.BlendingAndTiming[k] == k) { + mode_lib->vba.ODMCombineEnabled[k] = + mode_lib->vba.ODMCombineEnablePerState[mode_lib->vba.VoltageLevel][k]; + } else { + mode_lib->vba.ODMCombineEnabled[k] = 0; + } + mode_lib->vba.DSCEnabled[k] = + mode_lib->vba.RequiresDSC[mode_lib->vba.VoltageLevel][k]; + mode_lib->vba.OutputBpp[k] = + mode_lib->vba.OutputBppPerState[mode_lib->vba.VoltageLevel][k]; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.h new file mode 100644 index 000000000000..4112409cd974 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.h @@ -0,0 +1,598 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DML2_DISPLAY_MODE_VBA_H__ +#define __DML2_DISPLAY_MODE_VBA_H__ + +#include "dml_common_defs.h" + +struct display_mode_lib; + +void set_prefetch_mode(struct display_mode_lib *mode_lib, + bool cstate_en, + bool pstate_en, + bool ignore_viewport_pos, + bool immediate_flip_support); + +#define dml_get_attr_decl(attr) double get_##attr(struct display_mode_lib *mode_lib, const display_e2e_pipe_params_st *pipes, unsigned int num_pipes) + +dml_get_attr_decl(clk_dcf_deepsleep); +dml_get_attr_decl(wm_urgent); +dml_get_attr_decl(wm_memory_trip); +dml_get_attr_decl(wm_writeback_urgent); +dml_get_attr_decl(wm_stutter_exit); +dml_get_attr_decl(wm_stutter_enter_exit); +dml_get_attr_decl(wm_dram_clock_change); +dml_get_attr_decl(wm_writeback_dram_clock_change); +dml_get_attr_decl(wm_xfc_underflow); +dml_get_attr_decl(stutter_efficiency_no_vblank); +dml_get_attr_decl(stutter_efficiency); +dml_get_attr_decl(urgent_latency); +dml_get_attr_decl(urgent_extra_latency); +dml_get_attr_decl(nonurgent_latency); +dml_get_attr_decl(dram_clock_change_latency); +dml_get_attr_decl(dispclk_calculated); +dml_get_attr_decl(total_data_read_bw); +dml_get_attr_decl(return_bw); +dml_get_attr_decl(tcalc); + +#define dml_get_pipe_attr_decl(attr) double get_##attr(struct display_mode_lib *mode_lib, const display_e2e_pipe_params_st *pipes, unsigned int num_pipes, unsigned int which_pipe) + +dml_get_pipe_attr_decl(dsc_delay); +dml_get_pipe_attr_decl(dppclk_calculated); +dml_get_pipe_attr_decl(dscclk_calculated); +dml_get_pipe_attr_decl(min_ttu_vblank); +dml_get_pipe_attr_decl(vratio_prefetch_l); +dml_get_pipe_attr_decl(vratio_prefetch_c); +dml_get_pipe_attr_decl(dst_x_after_scaler); +dml_get_pipe_attr_decl(dst_y_after_scaler); +dml_get_pipe_attr_decl(dst_y_per_vm_vblank); +dml_get_pipe_attr_decl(dst_y_per_row_vblank); +dml_get_pipe_attr_decl(dst_y_prefetch); +dml_get_pipe_attr_decl(dst_y_per_vm_flip); +dml_get_pipe_attr_decl(dst_y_per_row_flip); +dml_get_pipe_attr_decl(xfc_transfer_delay); +dml_get_pipe_attr_decl(xfc_precharge_delay); +dml_get_pipe_attr_decl(xfc_remote_surface_flip_latency); +dml_get_pipe_attr_decl(xfc_prefetch_margin); + +unsigned int get_vstartup_calculated( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes, + unsigned int which_pipe); + +double get_total_immediate_flip_bytes( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes); +double get_total_immediate_flip_bw( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes); +double get_total_prefetch_bw( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes); + +unsigned int dml_get_voltage_level( + struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *pipes, + unsigned int num_pipes); + +bool Calculate256BBlockSizes( + enum source_format_class SourcePixelFormat, + enum dm_swizzle_mode SurfaceTiling, + unsigned int BytePerPixelY, + unsigned int BytePerPixelC, + unsigned int *BlockHeight256BytesY, + unsigned int *BlockHeight256BytesC, + unsigned int *BlockWidth256BytesY, + unsigned int *BlockWidth256BytesC); + + +struct vba_vars_st { + ip_params_st ip; + soc_bounding_box_st soc; + + unsigned int MaximumMaxVStartupLines; + double cursor_bw[DC__NUM_DPP__MAX]; + double meta_row_bw[DC__NUM_DPP__MAX]; + double dpte_row_bw[DC__NUM_DPP__MAX]; + double qual_row_bw[DC__NUM_DPP__MAX]; + double WritebackDISPCLK; + double PSCL_THROUGHPUT_LUMA[DC__NUM_DPP__MAX]; + double PSCL_THROUGHPUT_CHROMA[DC__NUM_DPP__MAX]; + double DPPCLKUsingSingleDPPLuma; + double DPPCLKUsingSingleDPPChroma; + double DPPCLKUsingSingleDPP[DC__NUM_DPP__MAX]; + double DISPCLKWithRamping; + double DISPCLKWithoutRamping; + double GlobalDPPCLK; + double DISPCLKWithRampingRoundedToDFSGranularity; + double DISPCLKWithoutRampingRoundedToDFSGranularity; + double MaxDispclkRoundedToDFSGranularity; + bool DCCEnabledAnyPlane; + double ReturnBandwidthToDCN; + unsigned int SwathWidthY[DC__NUM_DPP__MAX]; + unsigned int SwathWidthSingleDPPY[DC__NUM_DPP__MAX]; + double BytePerPixelDETY[DC__NUM_DPP__MAX]; + double BytePerPixelDETC[DC__NUM_DPP__MAX]; + double ReadBandwidthPlaneLuma[DC__NUM_DPP__MAX]; + double ReadBandwidthPlaneChroma[DC__NUM_DPP__MAX]; + unsigned int TotalActiveDPP; + unsigned int TotalDCCActiveDPP; + double UrgentRoundTripAndOutOfOrderLatency; + double DisplayPipeLineDeliveryTimeLuma[DC__NUM_DPP__MAX]; // WM + double DisplayPipeLineDeliveryTimeChroma[DC__NUM_DPP__MAX]; // WM + double LinesInDETY[DC__NUM_DPP__MAX]; // WM + double LinesInDETC[DC__NUM_DPP__MAX]; // WM + unsigned int LinesInDETYRoundedDownToSwath[DC__NUM_DPP__MAX]; // WM + unsigned int LinesInDETCRoundedDownToSwath[DC__NUM_DPP__MAX]; // WM + double FullDETBufferingTimeY[DC__NUM_DPP__MAX]; // WM + double FullDETBufferingTimeC[DC__NUM_DPP__MAX]; // WM + double MinFullDETBufferingTime; + double FrameTimeForMinFullDETBufferingTime; + double AverageReadBandwidthGBytePerSecond; + double PartOfBurstThatFitsInROB; + double StutterBurstTime; + //unsigned int NextPrefetchMode; + double VBlankTime; + double SmallestVBlank; + double DCFCLKDeepSleepPerPlane; + double EffectiveDETPlusLBLinesLuma; + double EffectiveDETPlusLBLinesChroma; + double UrgentLatencySupportUsLuma; + double UrgentLatencySupportUsChroma; + double UrgentLatencySupportUs[DC__NUM_DPP__MAX]; + unsigned int DSCFormatFactor; + unsigned int BlockHeight256BytesY[DC__NUM_DPP__MAX]; + unsigned int BlockHeight256BytesC[DC__NUM_DPP__MAX]; + unsigned int BlockWidth256BytesY[DC__NUM_DPP__MAX]; + unsigned int BlockWidth256BytesC[DC__NUM_DPP__MAX]; + double VInitPreFillY[DC__NUM_DPP__MAX]; + double VInitPreFillC[DC__NUM_DPP__MAX]; + unsigned int MaxNumSwathY[DC__NUM_DPP__MAX]; + unsigned int MaxNumSwathC[DC__NUM_DPP__MAX]; + double PrefetchSourceLinesY[DC__NUM_DPP__MAX]; + double PrefetchSourceLinesC[DC__NUM_DPP__MAX]; + double PixelPTEBytesPerRow[DC__NUM_DPP__MAX]; + double MetaRowByte[DC__NUM_DPP__MAX]; + unsigned int dpte_row_height[DC__NUM_DPP__MAX]; + unsigned int dpte_row_height_chroma[DC__NUM_DPP__MAX]; + unsigned int meta_row_height[DC__NUM_DPP__MAX]; + unsigned int meta_row_height_chroma[DC__NUM_DPP__MAX]; + + unsigned int MacroTileWidthY[DC__NUM_DPP__MAX]; + unsigned int MacroTileWidthC[DC__NUM_DPP__MAX]; + unsigned int MaxVStartupLines[DC__NUM_DPP__MAX]; + double WritebackDelay[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + bool PrefetchModeSupported; + bool AllowDRAMClockChangeDuringVBlank[DC__NUM_DPP__MAX]; + bool AllowDRAMSelfRefreshDuringVBlank[DC__NUM_DPP__MAX]; + double RequiredPrefetchPixDataBW[DC__NUM_DPP__MAX]; + double XFCRemoteSurfaceFlipDelay; + double TInitXFill; + double TslvChk; + double SrcActiveDrainRate; + double Tno_bw[DC__NUM_DPP__MAX]; + bool ImmediateFlipSupported; + + double prefetch_vm_bw[DC__NUM_DPP__MAX]; + double prefetch_row_bw[DC__NUM_DPP__MAX]; + bool ImmediateFlipSupportedForPipe[DC__NUM_DPP__MAX]; + unsigned int VStartupLines; + double DisplayPipeLineDeliveryTimeLumaPrefetch[DC__NUM_DPP__MAX]; + double DisplayPipeLineDeliveryTimeChromaPrefetch[DC__NUM_DPP__MAX]; + unsigned int ActiveDPPs; + unsigned int LBLatencyHidingSourceLinesY; + unsigned int LBLatencyHidingSourceLinesC; + double ActiveDRAMClockChangeLatencyMargin[DC__NUM_DPP__MAX]; + double MinActiveDRAMClockChangeMargin; + double XFCSlaveVUpdateOffset[DC__NUM_DPP__MAX]; + double XFCSlaveVupdateWidth[DC__NUM_DPP__MAX]; + double XFCSlaveVReadyOffset[DC__NUM_DPP__MAX]; + double InitFillLevel; + double FinalFillMargin; + double FinalFillLevel; + double RemainingFillLevel; + double TFinalxFill; + + + // + // SOC Bounding Box Parameters + // + double SRExitTime; + double SREnterPlusExitTime; + double UrgentLatency; + double WritebackLatency; + double PercentOfIdealDRAMAndFabricBWReceivedAfterUrgLatency; + double NumberOfChannels; + double DRAMChannelWidth; + double FabricDatapathToDCNDataReturn; + double ReturnBusWidth; + double Downspreading; + double DISPCLKDPPCLKDSCCLKDownSpreading; + double DISPCLKDPPCLKVCOSpeed; + double RoundTripPingLatencyCycles; + double UrgentOutOfOrderReturnPerChannel; + unsigned int VMMPageSize; + double DRAMClockChangeLatency; + double XFCBusTransportTime; + double XFCXBUFLatencyTolerance; + + // + // IP Parameters + // + unsigned int ROBBufferSizeInKByte; + double DETBufferSizeInKByte; + unsigned int DPPOutputBufferPixels; + unsigned int OPPOutputBufferLines; + unsigned int PixelChunkSizeInKByte; + double ReturnBW; + bool VirtualMemoryEnable; + unsigned int MaxPageTableLevels; + unsigned int OverridePageTableLevels; + unsigned int PTEChunkSize; + unsigned int MetaChunkSize; + unsigned int WritebackChunkSize; + bool ODMCapability; + unsigned int NumberOfDSC; + unsigned int LineBufferSize; + unsigned int MaxLineBufferLines; + unsigned int WritebackInterfaceLumaBufferSize; + unsigned int WritebackInterfaceChromaBufferSize; + unsigned int WritebackChromaLineBufferWidth; + double MaxDCHUBToPSCLThroughput; + double MaxPSCLToLBThroughput; + unsigned int PTEBufferSizeInRequests; + double DISPCLKRampingMargin; + unsigned int MaxInterDCNTileRepeaters; + bool XFCSupported; + double XFCSlvChunkSize; + double XFCFillBWOverhead; + double XFCFillConstant; + double XFCTSlvVupdateOffset; + double XFCTSlvVupdateWidth; + double XFCTSlvVreadyOffset; + double DPPCLKDelaySubtotal; + double DPPCLKDelaySCL; + double DPPCLKDelaySCLLBOnly; + double DPPCLKDelayCNVCFormater; + double DPPCLKDelayCNVCCursor; + double DISPCLKDelaySubtotal; + bool ProgressiveToInterlaceUnitInOPP; + unsigned int PDEProcessingBufIn64KBReqs; + + // Pipe/Plane Parameters + int VoltageLevel; + double FabricAndDRAMBandwidth; + double FabricClock; + double DRAMSpeed; + double DISPCLK; + double SOCCLK; + double DCFCLK; + + unsigned int NumberOfActivePlanes; + unsigned int ViewportWidth[DC__NUM_DPP__MAX]; + unsigned int ViewportHeight[DC__NUM_DPP__MAX]; + unsigned int ViewportYStartY[DC__NUM_DPP__MAX]; + unsigned int ViewportYStartC[DC__NUM_DPP__MAX]; + unsigned int PitchY[DC__NUM_DPP__MAX]; + unsigned int PitchC[DC__NUM_DPP__MAX]; + double HRatio[DC__NUM_DPP__MAX]; + double VRatio[DC__NUM_DPP__MAX]; + unsigned int htaps[DC__NUM_DPP__MAX]; + unsigned int vtaps[DC__NUM_DPP__MAX]; + unsigned int HTAPsChroma[DC__NUM_DPP__MAX]; + unsigned int VTAPsChroma[DC__NUM_DPP__MAX]; + unsigned int HTotal[DC__NUM_DPP__MAX]; + unsigned int VTotal[DC__NUM_DPP__MAX]; + unsigned int DPPPerPlane[DC__NUM_DPP__MAX]; + double PixelClock[DC__NUM_DPP__MAX]; + double PixelClockBackEnd[DC__NUM_DPP__MAX]; + double DPPCLK[DC__NUM_DPP__MAX]; + bool DCCEnable[DC__NUM_DPP__MAX]; + unsigned int DCCMetaPitchY[DC__NUM_DPP__MAX]; + enum scan_direction_class SourceScan[DC__NUM_DPP__MAX]; + enum source_format_class SourcePixelFormat[DC__NUM_DPP__MAX]; + bool WritebackEnable[DC__NUM_DPP__MAX]; + double WritebackDestinationWidth[DC__NUM_DPP__MAX]; + double WritebackDestinationHeight[DC__NUM_DPP__MAX]; + double WritebackSourceHeight[DC__NUM_DPP__MAX]; + enum source_format_class WritebackPixelFormat[DC__NUM_DPP__MAX]; + unsigned int WritebackLumaHTaps[DC__NUM_DPP__MAX]; + unsigned int WritebackLumaVTaps[DC__NUM_DPP__MAX]; + unsigned int WritebackChromaHTaps[DC__NUM_DPP__MAX]; + unsigned int WritebackChromaVTaps[DC__NUM_DPP__MAX]; + double WritebackHRatio[DC__NUM_DPP__MAX]; + double WritebackVRatio[DC__NUM_DPP__MAX]; + unsigned int HActive[DC__NUM_DPP__MAX]; + unsigned int VActive[DC__NUM_DPP__MAX]; + bool Interlace[DC__NUM_DPP__MAX]; + enum dm_swizzle_mode SurfaceTiling[DC__NUM_DPP__MAX]; + unsigned int ScalerRecoutWidth[DC__NUM_DPP__MAX]; + bool DynamicMetadataEnable[DC__NUM_DPP__MAX]; + unsigned int DynamicMetadataLinesBeforeActiveRequired[DC__NUM_DPP__MAX]; + unsigned int DynamicMetadataTransmittedBytes[DC__NUM_DPP__MAX]; + double DCCRate[DC__NUM_DPP__MAX]; + bool ODMCombineEnabled[DC__NUM_DPP__MAX]; + double OutputBpp[DC__NUM_DPP__MAX]; + unsigned int NumberOfDSCSlices[DC__NUM_DPP__MAX]; + bool DSCEnabled[DC__NUM_DPP__MAX]; + unsigned int DSCDelay[DC__NUM_DPP__MAX]; + unsigned int DSCInputBitPerComponent[DC__NUM_DPP__MAX]; + enum output_format_class OutputFormat[DC__NUM_DPP__MAX]; + enum output_encoder_class Output[DC__NUM_DPP__MAX]; + unsigned int BlendingAndTiming[DC__NUM_DPP__MAX]; + bool SynchronizedVBlank; + unsigned int NumberOfCursors[DC__NUM_DPP__MAX]; + unsigned int CursorWidth[DC__NUM_DPP__MAX][DC__NUM_CURSOR__MAX]; + unsigned int CursorBPP[DC__NUM_DPP__MAX][DC__NUM_CURSOR__MAX]; + bool XFCEnabled[DC__NUM_DPP__MAX]; + bool ScalerEnabled[DC__NUM_DPP__MAX]; + + // Intermediates/Informational + bool ImmediateFlipSupport; + unsigned int SwathHeightY[DC__NUM_DPP__MAX]; + unsigned int SwathHeightC[DC__NUM_DPP__MAX]; + unsigned int DETBufferSizeY[DC__NUM_DPP__MAX]; + unsigned int DETBufferSizeC[DC__NUM_DPP__MAX]; + unsigned int LBBitPerPixel[DC__NUM_DPP__MAX]; + double LastPixelOfLineExtraWatermark; + double TotalDataReadBandwidth; + unsigned int TotalActiveWriteback; + unsigned int EffectiveLBLatencyHidingSourceLinesLuma; + unsigned int EffectiveLBLatencyHidingSourceLinesChroma; + double BandwidthAvailableForImmediateFlip; + unsigned int PrefetchMode; + bool IgnoreViewportPositioning; + double PrefetchBandwidth[DC__NUM_DPP__MAX]; + bool ErrorResult[DC__NUM_DPP__MAX]; + double PDEAndMetaPTEBytesFrame[DC__NUM_DPP__MAX]; + + // + // Calculated dml_ml->vba.Outputs + // + double DCFClkDeepSleep; + double UrgentWatermark; + double UrgentExtraLatency; + double MemoryTripWatermark; + double WritebackUrgentWatermark; + double StutterExitWatermark; + double StutterEnterPlusExitWatermark; + double DRAMClockChangeWatermark; + double WritebackDRAMClockChangeWatermark; + double StutterEfficiency; + double StutterEfficiencyNotIncludingVBlank; + double MinUrgentLatencySupportUs; + double NonUrgentLatencyTolerance; + double MinActiveDRAMClockChangeLatencySupported; + enum clock_change_support DRAMClockChangeSupport; + + // These are the clocks calcuated by the library but they are not actually + // used explicitly. They are fetched by tests and then possibly used. The + // ultimate values to use are the ones specified by the parameters to DML + double DISPCLK_calculated; + double DSCCLK_calculated[DC__NUM_DPP__MAX]; + double DPPCLK_calculated[DC__NUM_DPP__MAX]; + + unsigned int VStartup[DC__NUM_DPP__MAX]; + unsigned int VUpdateOffsetPix[DC__NUM_DPP__MAX]; + unsigned int VUpdateWidthPix[DC__NUM_DPP__MAX]; + unsigned int VReadyOffsetPix[DC__NUM_DPP__MAX]; + unsigned int VStartupRequiredWhenNotEnoughTimeForDynamicMetadata; + + double ImmediateFlipBW; + unsigned int TotImmediateFlipBytes; + double TCalc; + double MinTTUVBlank[DC__NUM_DPP__MAX]; + double VRatioPrefetchY[DC__NUM_DPP__MAX]; + double VRatioPrefetchC[DC__NUM_DPP__MAX]; + double DSTXAfterScaler[DC__NUM_DPP__MAX]; + double DSTYAfterScaler[DC__NUM_DPP__MAX]; + + double DestinationLinesToRequestVMInVBlank[DC__NUM_DPP__MAX]; + double DestinationLinesToRequestRowInVBlank[DC__NUM_DPP__MAX]; + double DestinationLinesForPrefetch[DC__NUM_DPP__MAX]; + double DestinationLinesToRequestRowInImmediateFlip[DC__NUM_DPP__MAX]; + double DestinationLinesToRequestVMInImmediateFlip[DC__NUM_DPP__MAX]; + + double XFCTransferDelay[DC__NUM_DPP__MAX]; + double XFCPrechargeDelay[DC__NUM_DPP__MAX]; + double XFCRemoteSurfaceFlipLatency[DC__NUM_DPP__MAX]; + double XFCPrefetchMargin[DC__NUM_DPP__MAX]; + + display_e2e_pipe_params_st cache_pipes[DC__NUM_DPP__MAX]; + unsigned int cache_num_pipes; + unsigned int pipe_plane[DC__NUM_DPP__MAX]; + + /* vba mode support */ + /*inputs*/ + bool SupportGFX7CompatibleTilingIn32bppAnd64bpp; + double MaxHSCLRatio; + double MaxVSCLRatio; + unsigned int MaxNumWriteback; + bool WritebackLumaAndChromaScalingSupported; + bool Cursor64BppSupport; + double DCFCLKPerState[DC__VOLTAGE_STATES + 1]; + double FabricClockPerState[DC__VOLTAGE_STATES + 1]; + double SOCCLKPerState[DC__VOLTAGE_STATES + 1]; + double PHYCLKPerState[DC__VOLTAGE_STATES + 1]; + double MaxDppclk[DC__VOLTAGE_STATES + 1]; + double MaxDSCCLK[DC__VOLTAGE_STATES + 1]; + double DRAMSpeedPerState[DC__VOLTAGE_STATES + 1]; + double MaxDispclk[DC__VOLTAGE_STATES + 1]; + + /*outputs*/ + bool ScaleRatioAndTapsSupport; + bool SourceFormatPixelAndScanSupport; + unsigned int SwathWidthYSingleDPP[DC__NUM_DPP__MAX]; + double BytePerPixelInDETY[DC__NUM_DPP__MAX]; + double BytePerPixelInDETC[DC__NUM_DPP__MAX]; + double TotalReadBandwidthConsumedGBytePerSecond; + double ReadBandwidth[DC__NUM_DPP__MAX]; + double TotalWriteBandwidthConsumedGBytePerSecond; + double WriteBandwidth[DC__NUM_DPP__MAX]; + double TotalBandwidthConsumedGBytePerSecond; + bool DCCEnabledInAnyPlane; + bool WritebackLatencySupport; + bool WritebackModeSupport; + bool Writeback10bpc420Supported; + bool BandwidthSupport[DC__VOLTAGE_STATES + 1]; + unsigned int TotalNumberOfActiveWriteback; + double CriticalPoint; + double ReturnBWToDCNPerState; + double FabricAndDRAMBandwidthPerState[DC__VOLTAGE_STATES + 1]; + double ReturnBWPerState[DC__VOLTAGE_STATES + 1]; + double UrgentRoundTripAndOutOfOrderLatencyPerState[DC__VOLTAGE_STATES + 1]; + bool ODMCombineEnablePerState[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + bool PTEBufferSizeNotExceededY[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + bool PTEBufferSizeNotExceededC[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + bool PrefetchSupported[DC__VOLTAGE_STATES + 1]; + bool VRatioInPrefetchSupported[DC__VOLTAGE_STATES + 1]; + bool DISPCLK_DPPCLK_Support[DC__VOLTAGE_STATES + 1]; + bool TotalAvailablePipesSupport[DC__VOLTAGE_STATES + 1]; + bool UrgentLatencySupport[DC__VOLTAGE_STATES + 1]; + bool ModeSupport[DC__VOLTAGE_STATES + 1]; + bool DIOSupport[DC__VOLTAGE_STATES + 1]; + bool NotEnoughDSCUnits[DC__VOLTAGE_STATES + 1]; + bool DSCCLKRequiredMoreThanSupported[DC__VOLTAGE_STATES + 1]; + bool ROBSupport[DC__VOLTAGE_STATES + 1]; + bool PTEBufferSizeNotExceeded[DC__VOLTAGE_STATES + 1]; + bool RequiresDSC[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + bool IsErrorResult[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + bool ViewportSizeSupport[DC__VOLTAGE_STATES + 1]; + bool prefetch_vm_bw_valid; + bool prefetch_row_bw_valid; + bool NumberOfOTGSupport; + bool NonsupportedDSCInputBPC; + bool WritebackScaleRatioAndTapsSupport; + bool CursorSupport; + bool PitchSupport; + + double WritebackLineBufferLumaBufferSize; + double WritebackLineBufferChromaBufferSize; + double WritebackMinHSCLRatio; + double WritebackMinVSCLRatio; + double WritebackMaxHSCLRatio; + double WritebackMaxVSCLRatio; + double WritebackMaxHSCLTaps; + double WritebackMaxVSCLTaps; + unsigned int MaxNumDPP; + unsigned int MaxNumOTG; + double CursorBufferSize; + double CursorChunkSize; + unsigned int Mode; + unsigned int NoOfDPP[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double OutputLinkDPLanes[DC__NUM_DPP__MAX]; + double SwathWidthYPerState[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double SwathHeightYPerState[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double SwathHeightCPerState[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double UrgentLatencySupportUsPerState[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double VRatioPreY[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double VRatioPreC[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double RequiredPrefetchPixelDataBW[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double RequiredDPPCLK[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double RequiredDISPCLK[DC__VOLTAGE_STATES + 1]; + double TotalNumberOfActiveDPP[DC__VOLTAGE_STATES + 1]; + double TotalNumberOfDCCActiveDPP[DC__VOLTAGE_STATES + 1]; + double PrefetchBW[DC__NUM_DPP__MAX]; + double PDEAndMetaPTEBytesPerFrame[DC__NUM_DPP__MAX]; + double MetaRowBytes[DC__NUM_DPP__MAX]; + double DPTEBytesPerRow[DC__NUM_DPP__MAX]; + double PrefetchLinesY[DC__NUM_DPP__MAX]; + double PrefetchLinesC[DC__NUM_DPP__MAX]; + unsigned int MaxNumSwY[DC__NUM_DPP__MAX]; + unsigned int MaxNumSwC[DC__NUM_DPP__MAX]; + double PrefillY[DC__NUM_DPP__MAX]; + double PrefillC[DC__NUM_DPP__MAX]; + double LineTimesForPrefetch[DC__NUM_DPP__MAX]; + double LinesForMetaPTE[DC__NUM_DPP__MAX]; + double LinesForMetaAndDPTERow[DC__NUM_DPP__MAX]; + double MinDPPCLKUsingSingleDPP[DC__NUM_DPP__MAX]; + double RequiresFEC[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + unsigned int OutputBppPerState[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + double DSCDelayPerState[DC__VOLTAGE_STATES + 1][DC__NUM_DPP__MAX]; + unsigned int Read256BlockHeightY[DC__NUM_DPP__MAX]; + unsigned int Read256BlockWidthY[DC__NUM_DPP__MAX]; + unsigned int Read256BlockHeightC[DC__NUM_DPP__MAX]; + unsigned int Read256BlockWidthC[DC__NUM_DPP__MAX]; + unsigned int ImmediateFlipBytes[DC__NUM_DPP__MAX]; + double MaxSwathHeightY[DC__NUM_DPP__MAX]; + double MaxSwathHeightC[DC__NUM_DPP__MAX]; + double MinSwathHeightY[DC__NUM_DPP__MAX]; + double MinSwathHeightC[DC__NUM_DPP__MAX]; + double PSCL_FACTOR[DC__NUM_DPP__MAX]; + double PSCL_FACTOR_CHROMA[DC__NUM_DPP__MAX]; + double MaximumVStartup[DC__NUM_DPP__MAX]; + double AlignedDCCMetaPitch[DC__NUM_DPP__MAX]; + double AlignedYPitch[DC__NUM_DPP__MAX]; + double AlignedCPitch[DC__NUM_DPP__MAX]; + double MaximumSwathWidth[DC__NUM_DPP__MAX]; + double final_flip_bw[DC__NUM_DPP__MAX]; + double ImmediateFlipSupportedForState[DC__VOLTAGE_STATES + 1]; + + double WritebackLumaVExtra; + double WritebackChromaVExtra; + double WritebackRequiredDISPCLK; + double MaximumSwathWidthSupport; + double MaximumSwathWidthInDETBuffer; + double MaximumSwathWidthInLineBuffer; + double MaxDispclkRoundedDownToDFSGranularity; + double MaxDppclkRoundedDownToDFSGranularity; + double PlaneRequiredDISPCLKWithoutODMCombine; + double PlaneRequiredDISPCLK; + double TotalNumberOfActiveOTG; + double FECOverhead; + double EffectiveFECOverhead; + unsigned int Outbpp; + unsigned int OutbppDSC; + double TotalDSCUnitsRequired; + double bpp; + unsigned int slices; + double SwathWidthGranularityY; + double RoundedUpMaxSwathSizeBytesY; + double SwathWidthGranularityC; + double RoundedUpMaxSwathSizeBytesC; + double LinesInDETLuma; + double LinesInDETChroma; + double EffectiveDETLBLinesLuma; + double EffectiveDETLBLinesChroma; + double ProjectedDCFCLKDeepSleep; + double PDEAndMetaPTEBytesPerFrameY; + double PDEAndMetaPTEBytesPerFrameC; + unsigned int MetaRowBytesY; + unsigned int MetaRowBytesC; + unsigned int DPTEBytesPerRowC; + unsigned int DPTEBytesPerRowY; + double ExtraLatency; + double TimeCalc; + double TWait; + double MaximumReadBandwidthWithPrefetch; + double total_dcn_read_bw_with_flip; +}; + +#endif /* _DML2_DISPLAY_MODE_VBA_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_calc.c b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_calc.c new file mode 100644 index 000000000000..8ba962df42e6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_calc.c @@ -0,0 +1,1763 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "display_mode_lib.h" +#include "display_mode_vba.h" +#include "display_rq_dlg_calc.h" + +static void calculate_ttu_cursor(struct display_mode_lib *mode_lib, + double *refcyc_per_req_delivery_pre_cur, + double *refcyc_per_req_delivery_cur, + double refclk_freq_in_mhz, + double ref_freq_to_pix_freq, + double hscale_pixel_rate_l, + double hscl_ratio, + double vratio_pre_l, + double vratio_l, + unsigned int cur_width, + enum cursor_bpp cur_bpp); + +#include "dml_inline_defs.h" + +static unsigned int get_bytes_per_element(enum source_format_class source_format, bool is_chroma) +{ + unsigned int ret_val = 0; + + if (source_format == dm_444_16) { + if (!is_chroma) + ret_val = 2; + } else if (source_format == dm_444_32) { + if (!is_chroma) + ret_val = 4; + } else if (source_format == dm_444_64) { + if (!is_chroma) + ret_val = 8; + } else if (source_format == dm_420_8) { + if (is_chroma) + ret_val = 2; + else + ret_val = 1; + } else if (source_format == dm_420_10) { + if (is_chroma) + ret_val = 4; + else + ret_val = 2; + } else if (source_format == dm_444_8) { + ret_val = 1; + } + return ret_val; +} + +static bool is_dual_plane(enum source_format_class source_format) +{ + bool ret_val = 0; + + if ((source_format == dm_420_8) || (source_format == dm_420_10)) + ret_val = 1; + + return ret_val; +} + +static double get_refcyc_per_delivery(struct display_mode_lib *mode_lib, + double refclk_freq_in_mhz, + double pclk_freq_in_mhz, + bool odm_combine, + unsigned int recout_width, + unsigned int hactive, + double vratio, + double hscale_pixel_rate, + unsigned int delivery_width, + unsigned int req_per_swath_ub) +{ + double refcyc_per_delivery = 0.0; + + if (vratio <= 1.0) { + if (odm_combine) + refcyc_per_delivery = (double) refclk_freq_in_mhz + * dml_min((double) recout_width, (double) hactive / 2.0) + / pclk_freq_in_mhz / (double) req_per_swath_ub; + else + refcyc_per_delivery = (double) refclk_freq_in_mhz * (double) recout_width + / pclk_freq_in_mhz / (double) req_per_swath_ub; + } else { + refcyc_per_delivery = (double) refclk_freq_in_mhz * (double) delivery_width + / (double) hscale_pixel_rate / (double) req_per_swath_ub; + } + + dml_print("DML_DLG: %s: refclk_freq_in_mhz = %3.2f\n", __func__, refclk_freq_in_mhz); + dml_print("DML_DLG: %s: pclk_freq_in_mhz = %3.2f\n", __func__, pclk_freq_in_mhz); + dml_print("DML_DLG: %s: recout_width = %d\n", __func__, recout_width); + dml_print("DML_DLG: %s: vratio = %3.2f\n", __func__, vratio); + dml_print("DML_DLG: %s: req_per_swath_ub = %d\n", __func__, req_per_swath_ub); + dml_print("DML_DLG: %s: refcyc_per_delivery= %3.2f\n", __func__, refcyc_per_delivery); + + return refcyc_per_delivery; + +} + +static unsigned int get_blk_size_bytes(const enum source_macro_tile_size tile_size) +{ + if (tile_size == dm_256k_tile) + return (256 * 1024); + else if (tile_size == dm_64k_tile) + return (64 * 1024); + else + return (4 * 1024); +} + +static void extract_rq_sizing_regs(struct display_mode_lib *mode_lib, + display_data_rq_regs_st *rq_regs, + const display_data_rq_sizing_params_st rq_sizing) +{ + dml_print("DML_DLG: %s: rq_sizing param\n", __func__); + print__data_rq_sizing_params_st(mode_lib, rq_sizing); + + rq_regs->chunk_size = dml_log2(rq_sizing.chunk_bytes) - 10; + + if (rq_sizing.min_chunk_bytes == 0) + rq_regs->min_chunk_size = 0; + else + rq_regs->min_chunk_size = dml_log2(rq_sizing.min_chunk_bytes) - 8 + 1; + + rq_regs->meta_chunk_size = dml_log2(rq_sizing.meta_chunk_bytes) - 10; + if (rq_sizing.min_meta_chunk_bytes == 0) + rq_regs->min_meta_chunk_size = 0; + else + rq_regs->min_meta_chunk_size = dml_log2(rq_sizing.min_meta_chunk_bytes) - 6 + 1; + + rq_regs->dpte_group_size = dml_log2(rq_sizing.dpte_group_bytes) - 6; + rq_regs->mpte_group_size = dml_log2(rq_sizing.mpte_group_bytes) - 6; +} + +static void extract_rq_regs(struct display_mode_lib *mode_lib, + display_rq_regs_st *rq_regs, + const display_rq_params_st rq_param) +{ + unsigned int detile_buf_size_in_bytes = mode_lib->ip.det_buffer_size_kbytes * 1024; + unsigned int detile_buf_plane1_addr = 0; + + extract_rq_sizing_regs(mode_lib, &(rq_regs->rq_regs_l), rq_param.sizing.rq_l); + + rq_regs->rq_regs_l.pte_row_height_linear = dml_floor(dml_log2(rq_param.dlg.rq_l.dpte_row_height), + 1) - 3; + + if (rq_param.yuv420) { + extract_rq_sizing_regs(mode_lib, &(rq_regs->rq_regs_c), rq_param.sizing.rq_c); + rq_regs->rq_regs_c.pte_row_height_linear = dml_floor(dml_log2(rq_param.dlg.rq_c.dpte_row_height), + 1) - 3; + } + + rq_regs->rq_regs_l.swath_height = dml_log2(rq_param.dlg.rq_l.swath_height); + rq_regs->rq_regs_c.swath_height = dml_log2(rq_param.dlg.rq_c.swath_height); + + // FIXME: take the max between luma, chroma chunk size? + // okay for now, as we are setting chunk_bytes to 8kb anyways + if (rq_param.sizing.rq_l.chunk_bytes >= 32 * 1024) { //32kb + rq_regs->drq_expansion_mode = 0; + } else { + rq_regs->drq_expansion_mode = 2; + } + rq_regs->prq_expansion_mode = 1; + rq_regs->mrq_expansion_mode = 1; + rq_regs->crq_expansion_mode = 1; + + if (rq_param.yuv420) { + if ((double) rq_param.misc.rq_l.stored_swath_bytes + / (double) rq_param.misc.rq_c.stored_swath_bytes <= 1.5) { + detile_buf_plane1_addr = (detile_buf_size_in_bytes / 2.0 / 64.0); // half to chroma + } else { + detile_buf_plane1_addr = dml_round_to_multiple((unsigned int) ((2.0 * detile_buf_size_in_bytes) / 3.0), + 256, + 0) / 64.0; // 2/3 to chroma + } + } + rq_regs->plane1_base_address = detile_buf_plane1_addr; +} + +static void handle_det_buf_split(struct display_mode_lib *mode_lib, + display_rq_params_st *rq_param, + const display_pipe_source_params_st pipe_src_param) +{ + unsigned int total_swath_bytes = 0; + unsigned int swath_bytes_l = 0; + unsigned int swath_bytes_c = 0; + unsigned int full_swath_bytes_packed_l = 0; + unsigned int full_swath_bytes_packed_c = 0; + bool req128_l = 0; + bool req128_c = 0; + bool surf_linear = (pipe_src_param.sw_mode == dm_sw_linear); + bool surf_vert = (pipe_src_param.source_scan == dm_vert); + unsigned int log2_swath_height_l = 0; + unsigned int log2_swath_height_c = 0; + unsigned int detile_buf_size_in_bytes = mode_lib->ip.det_buffer_size_kbytes * 1024; + + full_swath_bytes_packed_l = rq_param->misc.rq_l.full_swath_bytes; + full_swath_bytes_packed_c = rq_param->misc.rq_c.full_swath_bytes; + + if (rq_param->yuv420_10bpc) { + full_swath_bytes_packed_l = dml_round_to_multiple(rq_param->misc.rq_l.full_swath_bytes * 2 / 3, + 256, + 1) + 256; + full_swath_bytes_packed_c = dml_round_to_multiple(rq_param->misc.rq_c.full_swath_bytes * 2 / 3, + 256, + 1) + 256; + } + + if (rq_param->yuv420) { + total_swath_bytes = 2 * full_swath_bytes_packed_l + 2 * full_swath_bytes_packed_c; + + if (total_swath_bytes <= detile_buf_size_in_bytes) { //full 256b request + req128_l = 0; + req128_c = 0; + swath_bytes_l = full_swath_bytes_packed_l; + swath_bytes_c = full_swath_bytes_packed_c; + } else { //128b request (for luma only for yuv420 8bpc) + req128_l = 1; + req128_c = 0; + swath_bytes_l = full_swath_bytes_packed_l / 2; + swath_bytes_c = full_swath_bytes_packed_c; + } + // Note: assumption, the config that pass in will fit into + // the detiled buffer. + } else { + total_swath_bytes = 2 * full_swath_bytes_packed_l; + + if (total_swath_bytes <= detile_buf_size_in_bytes) + req128_l = 0; + else + req128_l = 1; + + swath_bytes_l = total_swath_bytes; + swath_bytes_c = 0; + } + rq_param->misc.rq_l.stored_swath_bytes = swath_bytes_l; + rq_param->misc.rq_c.stored_swath_bytes = swath_bytes_c; + + if (surf_linear) { + log2_swath_height_l = 0; + log2_swath_height_c = 0; + } else if (!surf_vert) { + log2_swath_height_l = dml_log2(rq_param->misc.rq_l.blk256_height) - req128_l; + log2_swath_height_c = dml_log2(rq_param->misc.rq_c.blk256_height) - req128_c; + } else { + log2_swath_height_l = dml_log2(rq_param->misc.rq_l.blk256_width) - req128_l; + log2_swath_height_c = dml_log2(rq_param->misc.rq_c.blk256_width) - req128_c; + } + rq_param->dlg.rq_l.swath_height = 1 << log2_swath_height_l; + rq_param->dlg.rq_c.swath_height = 1 << log2_swath_height_c; + + dml_print("DML_DLG: %s: req128_l = %0d\n", __func__, req128_l); + dml_print("DML_DLG: %s: req128_c = %0d\n", __func__, req128_c); + dml_print("DML_DLG: %s: full_swath_bytes_packed_l = %0d\n", + __func__, + full_swath_bytes_packed_l); + dml_print("DML_DLG: %s: full_swath_bytes_packed_c = %0d\n", + __func__, + full_swath_bytes_packed_c); +} + +static void get_meta_and_pte_attr(struct display_mode_lib *mode_lib, + display_data_rq_dlg_params_st *rq_dlg_param, + display_data_rq_misc_params_st *rq_misc_param, + display_data_rq_sizing_params_st *rq_sizing_param, + unsigned int vp_width, + unsigned int vp_height, + unsigned int data_pitch, + unsigned int meta_pitch, + unsigned int source_format, + unsigned int tiling, + unsigned int macro_tile_size, + unsigned int source_scan, + unsigned int is_chroma) +{ + bool surf_linear = (tiling == dm_sw_linear); + bool surf_vert = (source_scan == dm_vert); + + unsigned int bytes_per_element; + unsigned int bytes_per_element_y = get_bytes_per_element((enum source_format_class)(source_format), + false); + unsigned int bytes_per_element_c = get_bytes_per_element((enum source_format_class)(source_format), + true); + + unsigned int blk256_width = 0; + unsigned int blk256_height = 0; + + unsigned int blk256_width_y = 0; + unsigned int blk256_height_y = 0; + unsigned int blk256_width_c = 0; + unsigned int blk256_height_c = 0; + unsigned int log2_bytes_per_element; + unsigned int log2_blk256_width; + unsigned int log2_blk256_height; + unsigned int blk_bytes; + unsigned int log2_blk_bytes; + unsigned int log2_blk_height; + unsigned int log2_blk_width; + unsigned int log2_meta_req_bytes; + unsigned int log2_meta_req_height; + unsigned int log2_meta_req_width; + unsigned int meta_req_width; + unsigned int meta_req_height; + unsigned int log2_meta_row_height; + unsigned int meta_row_width_ub; + unsigned int log2_meta_chunk_bytes; + unsigned int log2_meta_chunk_height; + + //full sized meta chunk width in unit of data elements + unsigned int log2_meta_chunk_width; + unsigned int log2_min_meta_chunk_bytes; + unsigned int min_meta_chunk_width; + unsigned int meta_chunk_width; + unsigned int meta_chunk_per_row_int; + unsigned int meta_row_remainder; + unsigned int meta_chunk_threshold; + unsigned int meta_blk_bytes; + unsigned int meta_blk_height; + unsigned int meta_blk_width; + unsigned int meta_surface_bytes; + unsigned int vmpg_bytes; + unsigned int meta_pte_req_per_frame_ub; + unsigned int meta_pte_bytes_per_frame_ub; + const unsigned int log2_vmpg_bytes = dml_log2(mode_lib->soc.vmm_page_size_bytes); + const unsigned int dpte_buf_in_pte_reqs = mode_lib->ip.dpte_buffer_size_in_pte_reqs; + const unsigned int pde_proc_buffer_size_64k_reqs = + mode_lib->ip.pde_proc_buffer_size_64k_reqs; + + unsigned int log2_vmpg_height = 0; + unsigned int log2_vmpg_width = 0; + unsigned int log2_dpte_req_height_ptes = 0; + unsigned int log2_dpte_req_height = 0; + unsigned int log2_dpte_req_width = 0; + unsigned int log2_dpte_row_height_linear = 0; + unsigned int log2_dpte_row_height = 0; + unsigned int log2_dpte_group_width = 0; + unsigned int dpte_row_width_ub = 0; + unsigned int dpte_req_height = 0; + unsigned int dpte_req_width = 0; + unsigned int dpte_group_width = 0; + unsigned int log2_dpte_group_bytes = 0; + unsigned int log2_dpte_group_length = 0; + unsigned int pde_buf_entries; + bool yuv420 = (source_format == dm_420_8 || source_format == dm_420_10); + + Calculate256BBlockSizes((enum source_format_class)(source_format), + (enum dm_swizzle_mode)(tiling), + bytes_per_element_y, + bytes_per_element_c, + &blk256_height_y, + &blk256_height_c, + &blk256_width_y, + &blk256_width_c); + + if (!is_chroma) { + blk256_width = blk256_width_y; + blk256_height = blk256_height_y; + bytes_per_element = bytes_per_element_y; + } else { + blk256_width = blk256_width_c; + blk256_height = blk256_height_c; + bytes_per_element = bytes_per_element_c; + } + + log2_bytes_per_element = dml_log2(bytes_per_element); + + dml_print("DML_DLG: %s: surf_linear = %d\n", __func__, surf_linear); + dml_print("DML_DLG: %s: surf_vert = %d\n", __func__, surf_vert); + dml_print("DML_DLG: %s: blk256_width = %d\n", __func__, blk256_width); + dml_print("DML_DLG: %s: blk256_height = %d\n", __func__, blk256_height); + + log2_blk256_width = dml_log2((double) blk256_width); + log2_blk256_height = dml_log2((double) blk256_height); + blk_bytes = surf_linear ? + 256 : get_blk_size_bytes((enum source_macro_tile_size) macro_tile_size); + log2_blk_bytes = dml_log2((double) blk_bytes); + log2_blk_height = 0; + log2_blk_width = 0; + + // remember log rule + // "+" in log is multiply + // "-" in log is divide + // "/2" is like square root + // blk is vertical biased + if (tiling != dm_sw_linear) + log2_blk_height = log2_blk256_height + + dml_ceil((double) (log2_blk_bytes - 8) / 2.0, 1); + else + log2_blk_height = 0; // blk height of 1 + + log2_blk_width = log2_blk_bytes - log2_bytes_per_element - log2_blk_height; + + if (!surf_vert) { + rq_dlg_param->swath_width_ub = dml_round_to_multiple(vp_width - 1, blk256_width, 1) + + blk256_width; + rq_dlg_param->req_per_swath_ub = rq_dlg_param->swath_width_ub >> log2_blk256_width; + } else { + rq_dlg_param->swath_width_ub = dml_round_to_multiple(vp_height - 1, blk256_height, 1) + + blk256_height; + rq_dlg_param->req_per_swath_ub = rq_dlg_param->swath_width_ub >> log2_blk256_height; + } + + if (!surf_vert) + rq_misc_param->full_swath_bytes = rq_dlg_param->swath_width_ub * blk256_height + * bytes_per_element; + else + rq_misc_param->full_swath_bytes = rq_dlg_param->swath_width_ub * blk256_width + * bytes_per_element; + + rq_misc_param->blk256_height = blk256_height; + rq_misc_param->blk256_width = blk256_width; + + // ------- + // meta + // ------- + log2_meta_req_bytes = 6; // meta request is 64b and is 8x8byte meta element + + // each 64b meta request for dcn is 8x8 meta elements and + // a meta element covers one 256b block of the the data surface. + log2_meta_req_height = log2_blk256_height + 3; // meta req is 8x8 byte, each byte represent 1 blk256 + log2_meta_req_width = log2_meta_req_bytes + 8 - log2_bytes_per_element + - log2_meta_req_height; + meta_req_width = 1 << log2_meta_req_width; + meta_req_height = 1 << log2_meta_req_height; + log2_meta_row_height = 0; + meta_row_width_ub = 0; + + // the dimensions of a meta row are meta_row_width x meta_row_height in elements. + // calculate upper bound of the meta_row_width + if (!surf_vert) { + log2_meta_row_height = log2_meta_req_height; + meta_row_width_ub = dml_round_to_multiple(vp_width - 1, meta_req_width, 1) + + meta_req_width; + rq_dlg_param->meta_req_per_row_ub = meta_row_width_ub / meta_req_width; + } else { + log2_meta_row_height = log2_meta_req_width; + meta_row_width_ub = dml_round_to_multiple(vp_height - 1, meta_req_height, 1) + + meta_req_height; + rq_dlg_param->meta_req_per_row_ub = meta_row_width_ub / meta_req_height; + } + rq_dlg_param->meta_bytes_per_row_ub = rq_dlg_param->meta_req_per_row_ub * 64; + + rq_dlg_param->meta_row_height = 1 << log2_meta_row_height; + + log2_meta_chunk_bytes = dml_log2(rq_sizing_param->meta_chunk_bytes); + log2_meta_chunk_height = log2_meta_row_height; + + //full sized meta chunk width in unit of data elements + log2_meta_chunk_width = log2_meta_chunk_bytes + 8 - log2_bytes_per_element + - log2_meta_chunk_height; + log2_min_meta_chunk_bytes = dml_log2(rq_sizing_param->min_meta_chunk_bytes); + min_meta_chunk_width = 1 + << (log2_min_meta_chunk_bytes + 8 - log2_bytes_per_element + - log2_meta_chunk_height); + meta_chunk_width = 1 << log2_meta_chunk_width; + meta_chunk_per_row_int = (unsigned int) (meta_row_width_ub / meta_chunk_width); + meta_row_remainder = meta_row_width_ub % meta_chunk_width; + meta_chunk_threshold = 0; + meta_blk_bytes = 4096; + meta_blk_height = blk256_height * 64; + meta_blk_width = meta_blk_bytes * 256 / bytes_per_element / meta_blk_height; + meta_surface_bytes = meta_pitch + * (dml_round_to_multiple(vp_height - 1, meta_blk_height, 1) + meta_blk_height) + * bytes_per_element / 256; + vmpg_bytes = mode_lib->soc.vmm_page_size_bytes; + meta_pte_req_per_frame_ub = (dml_round_to_multiple(meta_surface_bytes - vmpg_bytes, + 8 * vmpg_bytes, + 1) + 8 * vmpg_bytes) / (8 * vmpg_bytes); + meta_pte_bytes_per_frame_ub = meta_pte_req_per_frame_ub * 64; //64B mpte request + rq_dlg_param->meta_pte_bytes_per_frame_ub = meta_pte_bytes_per_frame_ub; + + dml_print("DML_DLG: %s: meta_blk_height = %d\n", __func__, meta_blk_height); + dml_print("DML_DLG: %s: meta_blk_width = %d\n", __func__, meta_blk_width); + dml_print("DML_DLG: %s: meta_surface_bytes = %d\n", __func__, meta_surface_bytes); + dml_print("DML_DLG: %s: meta_pte_req_per_frame_ub = %d\n", + __func__, + meta_pte_req_per_frame_ub); + dml_print("DML_DLG: %s: meta_pte_bytes_per_frame_ub = %d\n", + __func__, + meta_pte_bytes_per_frame_ub); + + if (!surf_vert) + meta_chunk_threshold = 2 * min_meta_chunk_width - meta_req_width; + else + meta_chunk_threshold = 2 * min_meta_chunk_width - meta_req_height; + + if (meta_row_remainder <= meta_chunk_threshold) + rq_dlg_param->meta_chunks_per_row_ub = meta_chunk_per_row_int + 1; + else + rq_dlg_param->meta_chunks_per_row_ub = meta_chunk_per_row_int + 2; + + // ------ + // dpte + // ------ + if (surf_linear) { + log2_vmpg_height = 0; // one line high + } else { + log2_vmpg_height = (log2_vmpg_bytes - 8) / 2 + log2_blk256_height; + } + log2_vmpg_width = log2_vmpg_bytes - log2_bytes_per_element - log2_vmpg_height; + + // only 3 possible shapes for dpte request in dimensions of ptes: 8x1, 4x2, 2x4. + if (surf_linear) { //one 64B PTE request returns 8 PTEs + log2_dpte_req_height_ptes = 0; + log2_dpte_req_width = log2_vmpg_width + 3; + log2_dpte_req_height = 0; + } else if (log2_blk_bytes == 12) { //4KB tile means 4kB page size + //one 64B req gives 8x1 PTEs for 4KB tile + log2_dpte_req_height_ptes = 0; + log2_dpte_req_width = log2_blk_width + 3; + log2_dpte_req_height = log2_blk_height + 0; + } else if ((log2_blk_bytes >= 16) && (log2_vmpg_bytes == 12)) { // tile block >= 64KB + //two 64B reqs of 2x4 PTEs give 16 PTEs to cover 64KB + log2_dpte_req_height_ptes = 4; + log2_dpte_req_width = log2_blk256_width + 4; // log2_64KB_width + log2_dpte_req_height = log2_blk256_height + 4; // log2_64KB_height + } else { //64KB page size and must 64KB tile block + //one 64B req gives 8x1 PTEs for 64KB tile + log2_dpte_req_height_ptes = 0; + log2_dpte_req_width = log2_blk_width + 3; + log2_dpte_req_height = log2_blk_height + 0; + } + + // The dpte request dimensions in data elements is dpte_req_width x dpte_req_height + // log2_vmpg_width is how much 1 pte represent, now calculating how much a 64b pte req represent + // That depends on the pte shape (i.e. 8x1, 4x2, 2x4) + //log2_dpte_req_height = log2_vmpg_height + log2_dpte_req_height_ptes; + //log2_dpte_req_width = log2_vmpg_width + log2_dpte_req_width_ptes; + dpte_req_height = 1 << log2_dpte_req_height; + dpte_req_width = 1 << log2_dpte_req_width; + + // calculate pitch dpte row buffer can hold + // round the result down to a power of two. + pde_buf_entries = yuv420 ? (pde_proc_buffer_size_64k_reqs >> 1) : pde_proc_buffer_size_64k_reqs; + if (surf_linear) { + unsigned int dpte_row_height; + + log2_dpte_row_height_linear = dml_floor(dml_log2(dml_min(64 * 1024 * pde_buf_entries + / bytes_per_element, + dpte_buf_in_pte_reqs + * dpte_req_width) + / data_pitch), + 1); + + ASSERT(log2_dpte_row_height_linear >= 3); + + if (log2_dpte_row_height_linear > 7) + log2_dpte_row_height_linear = 7; + + log2_dpte_row_height = log2_dpte_row_height_linear; + // For linear, the dpte row is pitch dependent and the pte requests wrap at the pitch boundary. + // the dpte_row_width_ub is the upper bound of data_pitch*dpte_row_height in elements with this unique buffering. + dpte_row_height = 1 << log2_dpte_row_height; + dpte_row_width_ub = dml_round_to_multiple(data_pitch * dpte_row_height - 1, + dpte_req_width, + 1) + dpte_req_width; + rq_dlg_param->dpte_req_per_row_ub = dpte_row_width_ub / dpte_req_width; + } else { + // the upper bound of the dpte_row_width without dependency on viewport position follows. + // for tiled mode, row height is the same as req height and row store up to vp size upper bound + if (!surf_vert) { + log2_dpte_row_height = log2_dpte_req_height; + dpte_row_width_ub = dml_round_to_multiple(vp_width - 1, dpte_req_width, 1) + + dpte_req_width; + rq_dlg_param->dpte_req_per_row_ub = dpte_row_width_ub / dpte_req_width; + } else { + log2_dpte_row_height = + (log2_blk_width < log2_dpte_req_width) ? + log2_blk_width : log2_dpte_req_width; + dpte_row_width_ub = dml_round_to_multiple(vp_height - 1, dpte_req_height, 1) + + dpte_req_height; + rq_dlg_param->dpte_req_per_row_ub = dpte_row_width_ub / dpte_req_height; + } + } + if (log2_blk_bytes >= 16 && log2_vmpg_bytes == 12) // tile block >= 64KB + rq_dlg_param->dpte_bytes_per_row_ub = rq_dlg_param->dpte_req_per_row_ub * 128; //2*64B dpte request + else + rq_dlg_param->dpte_bytes_per_row_ub = rq_dlg_param->dpte_req_per_row_ub * 64; //64B dpte request + + rq_dlg_param->dpte_row_height = 1 << log2_dpte_row_height; + + // the dpte_group_bytes is reduced for the specific case of vertical + // access of a tile surface that has dpte request of 8x1 ptes. + if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) //reduced, in this case, will have page fault within a group + rq_sizing_param->dpte_group_bytes = 512; + else + //full size + rq_sizing_param->dpte_group_bytes = 2048; + + //since pte request size is 64byte, the number of data pte requests per full sized group is as follows. + log2_dpte_group_bytes = dml_log2(rq_sizing_param->dpte_group_bytes); + log2_dpte_group_length = log2_dpte_group_bytes - 6; //length in 64b requests + + // full sized data pte group width in elements + if (!surf_vert) + log2_dpte_group_width = log2_dpte_group_length + log2_dpte_req_width; + else + log2_dpte_group_width = log2_dpte_group_length + log2_dpte_req_height; + + //But if the tile block >=64KB and the page size is 4KB, then each dPTE request is 2*64B + if ((log2_blk_bytes >= 16) && (log2_vmpg_bytes == 12)) // tile block >= 64KB + log2_dpte_group_width = log2_dpte_group_width - 1; + + dpte_group_width = 1 << log2_dpte_group_width; + + // since dpte groups are only aligned to dpte_req_width and not dpte_group_width, + // the upper bound for the dpte groups per row is as follows. + rq_dlg_param->dpte_groups_per_row_ub = dml_ceil((double) dpte_row_width_ub / dpte_group_width, + 1); +} + +static void get_surf_rq_param(struct display_mode_lib *mode_lib, + display_data_rq_sizing_params_st *rq_sizing_param, + display_data_rq_dlg_params_st *rq_dlg_param, + display_data_rq_misc_params_st *rq_misc_param, + const display_pipe_source_params_st pipe_src_param, + bool is_chroma) +{ + bool mode_422 = 0; + unsigned int vp_width = 0; + unsigned int vp_height = 0; + unsigned int data_pitch = 0; + unsigned int meta_pitch = 0; + unsigned int ppe = mode_422 ? 2 : 1; + + // FIXME check if ppe apply for both luma and chroma in 422 case + if (is_chroma) { + vp_width = pipe_src_param.viewport_width_c / ppe; + vp_height = pipe_src_param.viewport_height_c; + data_pitch = pipe_src_param.data_pitch_c; + meta_pitch = pipe_src_param.meta_pitch_c; + } else { + vp_width = pipe_src_param.viewport_width / ppe; + vp_height = pipe_src_param.viewport_height; + data_pitch = pipe_src_param.data_pitch; + meta_pitch = pipe_src_param.meta_pitch; + } + + rq_sizing_param->chunk_bytes = 8192; + + if (rq_sizing_param->chunk_bytes == 64 * 1024) + rq_sizing_param->min_chunk_bytes = 0; + else + rq_sizing_param->min_chunk_bytes = 1024; + + rq_sizing_param->meta_chunk_bytes = 2048; + rq_sizing_param->min_meta_chunk_bytes = 256; + + rq_sizing_param->mpte_group_bytes = 2048; + + get_meta_and_pte_attr(mode_lib, + rq_dlg_param, + rq_misc_param, + rq_sizing_param, + vp_width, + vp_height, + data_pitch, + meta_pitch, + pipe_src_param.source_format, + pipe_src_param.sw_mode, + pipe_src_param.macro_tile_size, + pipe_src_param.source_scan, + is_chroma); +} + +void dml_rq_dlg_get_rq_params(struct display_mode_lib *mode_lib, + display_rq_params_st *rq_param, + const display_pipe_source_params_st pipe_src_param) +{ + // get param for luma surface + rq_param->yuv420 = pipe_src_param.source_format == dm_420_8 + || pipe_src_param.source_format == dm_420_10; + rq_param->yuv420_10bpc = pipe_src_param.source_format == dm_420_10; + + get_surf_rq_param(mode_lib, + &(rq_param->sizing.rq_l), + &(rq_param->dlg.rq_l), + &(rq_param->misc.rq_l), + pipe_src_param, + 0); + + if (is_dual_plane((enum source_format_class)(pipe_src_param.source_format))) { + // get param for chroma surface + get_surf_rq_param(mode_lib, + &(rq_param->sizing.rq_c), + &(rq_param->dlg.rq_c), + &(rq_param->misc.rq_c), + pipe_src_param, + 1); + } + + // calculate how to split the det buffer space between luma and chroma + handle_det_buf_split(mode_lib, rq_param, pipe_src_param); + print__rq_params_st(mode_lib, *rq_param); +} + +void dml_rq_dlg_get_rq_reg(struct display_mode_lib *mode_lib, + display_rq_regs_st *rq_regs, + const display_pipe_source_params_st pipe_src_param) +{ + display_rq_params_st rq_param = {0}; + + memset(rq_regs, 0, sizeof(*rq_regs)); + dml_rq_dlg_get_rq_params(mode_lib, &rq_param, pipe_src_param); + extract_rq_regs(mode_lib, rq_regs, rq_param); + + print__rq_regs_st(mode_lib, *rq_regs); +} + +// Note: currently taken in as is. +// Nice to decouple code from hw register implement and extract code that are repeated for luma and chroma. +void dml_rq_dlg_get_dlg_params(struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *e2e_pipe_param, + const unsigned int num_pipes, + const unsigned int pipe_idx, + display_dlg_regs_st *disp_dlg_regs, + display_ttu_regs_st *disp_ttu_regs, + const display_rq_dlg_params_st rq_dlg_param, + const display_dlg_sys_params_st dlg_sys_param, + const bool cstate_en, + const bool pstate_en, + const bool vm_en, + const bool ignore_viewport_pos, + const bool immediate_flip_support) +{ + const display_pipe_source_params_st *src = &e2e_pipe_param[pipe_idx].pipe.src; + const display_pipe_dest_params_st *dst = &e2e_pipe_param[pipe_idx].pipe.dest; + const display_output_params_st *dout = &e2e_pipe_param[pipe_idx].dout; + const display_clocks_and_cfg_st *clks = &e2e_pipe_param[pipe_idx].clks_cfg; + const scaler_ratio_depth_st *scl = &e2e_pipe_param[pipe_idx].pipe.scale_ratio_depth; + const scaler_taps_st *taps = &e2e_pipe_param[pipe_idx].pipe.scale_taps; + + // ------------------------- + // Section 1.15.2.1: OTG dependent Params + // ------------------------- + // Timing + unsigned int htotal = dst->htotal; +// unsigned int hblank_start = dst.hblank_start; // TODO: Remove + unsigned int hblank_end = dst->hblank_end; + unsigned int vblank_start = dst->vblank_start; + unsigned int vblank_end = dst->vblank_end; + unsigned int min_vblank = mode_lib->ip.min_vblank_lines; + + double dppclk_freq_in_mhz = clks->dppclk_mhz; + double dispclk_freq_in_mhz = clks->dispclk_mhz; + double refclk_freq_in_mhz = clks->refclk_mhz; + double pclk_freq_in_mhz = dst->pixel_rate_mhz; + bool interlaced = dst->interlaced; + + double ref_freq_to_pix_freq = refclk_freq_in_mhz / pclk_freq_in_mhz; + + double min_dcfclk_mhz; + double t_calc_us; + double min_ttu_vblank; + + double min_dst_y_ttu_vblank; + unsigned int dlg_vblank_start; + bool dual_plane; + bool mode_422; + unsigned int access_dir; + unsigned int vp_height_l; + unsigned int vp_width_l; + unsigned int vp_height_c; + unsigned int vp_width_c; + + // Scaling + unsigned int htaps_l; + unsigned int htaps_c; + double hratio_l; + double hratio_c; + double vratio_l; + double vratio_c; + bool scl_enable; + + double line_time_in_us; + // double vinit_l; + // double vinit_c; + // double vinit_bot_l; + // double vinit_bot_c; + + // unsigned int swath_height_l; + unsigned int swath_width_ub_l; + // unsigned int dpte_bytes_per_row_ub_l; + unsigned int dpte_groups_per_row_ub_l; + // unsigned int meta_pte_bytes_per_frame_ub_l; + // unsigned int meta_bytes_per_row_ub_l; + + // unsigned int swath_height_c; + unsigned int swath_width_ub_c; + // unsigned int dpte_bytes_per_row_ub_c; + unsigned int dpte_groups_per_row_ub_c; + + unsigned int meta_chunks_per_row_ub_l; + unsigned int meta_chunks_per_row_ub_c; + unsigned int vupdate_offset; + unsigned int vupdate_width; + unsigned int vready_offset; + + unsigned int dppclk_delay_subtotal; + unsigned int dispclk_delay_subtotal; + unsigned int pixel_rate_delay_subtotal; + + unsigned int vstartup_start; + unsigned int dst_x_after_scaler; + unsigned int dst_y_after_scaler; + double line_wait; + double dst_y_prefetch; + double dst_y_per_vm_vblank; + double dst_y_per_row_vblank; + double dst_y_per_vm_flip; + double dst_y_per_row_flip; + double min_dst_y_per_vm_vblank; + double min_dst_y_per_row_vblank; + double lsw; + double vratio_pre_l; + double vratio_pre_c; + unsigned int req_per_swath_ub_l; + unsigned int req_per_swath_ub_c; + unsigned int meta_row_height_l; + unsigned int meta_row_height_c; + unsigned int swath_width_pixels_ub_l; + unsigned int swath_width_pixels_ub_c; + unsigned int scaler_rec_in_width_l; + unsigned int scaler_rec_in_width_c; + unsigned int dpte_row_height_l; + unsigned int dpte_row_height_c; + double hscale_pixel_rate_l; + double hscale_pixel_rate_c; + double min_hratio_fact_l; + double min_hratio_fact_c; + double refcyc_per_line_delivery_pre_l; + double refcyc_per_line_delivery_pre_c; + double refcyc_per_line_delivery_l; + double refcyc_per_line_delivery_c; + + double refcyc_per_req_delivery_pre_l; + double refcyc_per_req_delivery_pre_c; + double refcyc_per_req_delivery_l; + double refcyc_per_req_delivery_c; + + unsigned int full_recout_width; + double xfc_transfer_delay; + double xfc_precharge_delay; + double xfc_remote_surface_flip_latency; + double xfc_dst_y_delta_drq_limit; + double xfc_prefetch_margin; + double refcyc_per_req_delivery_pre_cur0; + double refcyc_per_req_delivery_cur0; + double refcyc_per_req_delivery_pre_cur1; + double refcyc_per_req_delivery_cur1; + + memset(disp_dlg_regs, 0, sizeof(*disp_dlg_regs)); + memset(disp_ttu_regs, 0, sizeof(*disp_ttu_regs)); + + dml_print("DML_DLG: %s: cstate_en = %d\n", __func__, cstate_en); + dml_print("DML_DLG: %s: pstate_en = %d\n", __func__, pstate_en); + dml_print("DML_DLG: %s: vm_en = %d\n", __func__, vm_en); + dml_print("DML_DLG: %s: ignore_viewport_pos = %d\n", __func__, ignore_viewport_pos); + dml_print("DML_DLG: %s: immediate_flip_support = %d\n", __func__, immediate_flip_support); + + dml_print("DML_DLG: %s: dppclk_freq_in_mhz = %3.2f\n", __func__, dppclk_freq_in_mhz); + dml_print("DML_DLG: %s: dispclk_freq_in_mhz = %3.2f\n", __func__, dispclk_freq_in_mhz); + dml_print("DML_DLG: %s: refclk_freq_in_mhz = %3.2f\n", __func__, refclk_freq_in_mhz); + dml_print("DML_DLG: %s: pclk_freq_in_mhz = %3.2f\n", __func__, pclk_freq_in_mhz); + dml_print("DML_DLG: %s: interlaced = %d\n", __func__, interlaced); + ASSERT(ref_freq_to_pix_freq < 4.0); + + disp_dlg_regs->ref_freq_to_pix_freq = + (unsigned int) (ref_freq_to_pix_freq * dml_pow(2, 19)); + disp_dlg_regs->refcyc_per_htotal = (unsigned int) (ref_freq_to_pix_freq * (double) htotal + * dml_pow(2, 8)); + disp_dlg_regs->dlg_vblank_end = interlaced ? (vblank_end / 2) : vblank_end; // 15 bits + disp_dlg_regs->refcyc_h_blank_end = (unsigned int) ((double) hblank_end + * (double) ref_freq_to_pix_freq); + ASSERT(disp_dlg_regs->refcyc_h_blank_end < (unsigned int) dml_pow(2, 13)); + + min_dcfclk_mhz = dlg_sys_param.deepsleep_dcfclk_mhz; + set_prefetch_mode(mode_lib, cstate_en, pstate_en, ignore_viewport_pos, immediate_flip_support); + t_calc_us = get_tcalc(mode_lib, e2e_pipe_param, num_pipes); + min_ttu_vblank = get_min_ttu_vblank(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + + min_dst_y_ttu_vblank = min_ttu_vblank * pclk_freq_in_mhz / (double) htotal; + dlg_vblank_start = interlaced ? (vblank_start / 2) : vblank_start; + + disp_dlg_regs->min_dst_y_next_start = (unsigned int) (((double) dlg_vblank_start + + min_dst_y_ttu_vblank) * dml_pow(2, 2)); + ASSERT(disp_dlg_regs->min_dst_y_next_start < (unsigned int) dml_pow(2, 18)); + + dml_print("DML_DLG: %s: min_dcfclk_mhz = %3.2f\n", + __func__, + min_dcfclk_mhz); + dml_print("DML_DLG: %s: min_ttu_vblank = %3.2f\n", + __func__, + min_ttu_vblank); + dml_print("DML_DLG: %s: min_dst_y_ttu_vblank = %3.2f\n", + __func__, + min_dst_y_ttu_vblank); + dml_print("DML_DLG: %s: t_calc_us = %3.2f\n", + __func__, + t_calc_us); + dml_print("DML_DLG: %s: disp_dlg_regs->min_dst_y_next_start = 0x%0x\n", + __func__, + disp_dlg_regs->min_dst_y_next_start); + dml_print("DML_DLG: %s: ref_freq_to_pix_freq = %3.2f\n", + __func__, + ref_freq_to_pix_freq); + + // ------------------------- + // Section 1.15.2.2: Prefetch, Active and TTU + // ------------------------- + // Prefetch Calc + // Source +// dcc_en = src.dcc; + dual_plane = is_dual_plane((enum source_format_class)(src->source_format)); + mode_422 = 0; // FIXME + access_dir = (src->source_scan == dm_vert); // vp access direction: horizontal or vertical accessed +// bytes_per_element_l = get_bytes_per_element(source_format_class(src.source_format), 0); +// bytes_per_element_c = get_bytes_per_element(source_format_class(src.source_format), 1); + vp_height_l = src->viewport_height; + vp_width_l = src->viewport_width; + vp_height_c = src->viewport_height_c; + vp_width_c = src->viewport_width_c; + + // Scaling + htaps_l = taps->htaps; + htaps_c = taps->htaps_c; + hratio_l = scl->hscl_ratio; + hratio_c = scl->hscl_ratio_c; + vratio_l = scl->vscl_ratio; + vratio_c = scl->vscl_ratio_c; + scl_enable = scl->scl_enable; + + line_time_in_us = (htotal / pclk_freq_in_mhz); +// vinit_l = scl.vinit; +// vinit_c = scl.vinit_c; +// vinit_bot_l = scl.vinit_bot; +// vinit_bot_c = scl.vinit_bot_c; + +// unsigned int swath_height_l = rq_dlg_param.rq_l.swath_height; + swath_width_ub_l = rq_dlg_param.rq_l.swath_width_ub; +// unsigned int dpte_bytes_per_row_ub_l = rq_dlg_param.rq_l.dpte_bytes_per_row_ub; + dpte_groups_per_row_ub_l = rq_dlg_param.rq_l.dpte_groups_per_row_ub; +// unsigned int meta_pte_bytes_per_frame_ub_l = rq_dlg_param.rq_l.meta_pte_bytes_per_frame_ub; +// unsigned int meta_bytes_per_row_ub_l = rq_dlg_param.rq_l.meta_bytes_per_row_ub; + +// unsigned int swath_height_c = rq_dlg_param.rq_c.swath_height; + swath_width_ub_c = rq_dlg_param.rq_c.swath_width_ub; + // dpte_bytes_per_row_ub_c = rq_dlg_param.rq_c.dpte_bytes_per_row_ub; + dpte_groups_per_row_ub_c = rq_dlg_param.rq_c.dpte_groups_per_row_ub; + + meta_chunks_per_row_ub_l = rq_dlg_param.rq_l.meta_chunks_per_row_ub; + meta_chunks_per_row_ub_c = rq_dlg_param.rq_c.meta_chunks_per_row_ub; + vupdate_offset = dst->vupdate_offset; + vupdate_width = dst->vupdate_width; + vready_offset = dst->vready_offset; + + dppclk_delay_subtotal = mode_lib->ip.dppclk_delay_subtotal; + dispclk_delay_subtotal = mode_lib->ip.dispclk_delay_subtotal; + + if (scl_enable) + dppclk_delay_subtotal += mode_lib->ip.dppclk_delay_scl; + else + dppclk_delay_subtotal += mode_lib->ip.dppclk_delay_scl_lb_only; + + dppclk_delay_subtotal += mode_lib->ip.dppclk_delay_cnvc_formatter + + src->num_cursors * mode_lib->ip.dppclk_delay_cnvc_cursor; + + if (dout->dsc_enable) { + double dsc_delay = get_dsc_delay(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + + dispclk_delay_subtotal += dsc_delay; + } + + pixel_rate_delay_subtotal = dppclk_delay_subtotal * pclk_freq_in_mhz / dppclk_freq_in_mhz + + dispclk_delay_subtotal * pclk_freq_in_mhz / dispclk_freq_in_mhz; + + vstartup_start = dst->vstartup_start; + if (interlaced) { + if (vstartup_start / 2.0 + - (double) (vready_offset + vupdate_width + vupdate_offset) / htotal + <= vblank_end / 2.0) + disp_dlg_regs->vready_after_vcount0 = 1; + else + disp_dlg_regs->vready_after_vcount0 = 0; + } else { + if (vstartup_start + - (double) (vready_offset + vupdate_width + vupdate_offset) / htotal + <= vblank_end) + disp_dlg_regs->vready_after_vcount0 = 1; + else + disp_dlg_regs->vready_after_vcount0 = 0; + } + + // TODO: Where is this coming from? + if (interlaced) + vstartup_start = vstartup_start / 2; + + // TODO: What if this min_vblank doesn't match the value in the dml_config_settings.cpp? + if (vstartup_start >= min_vblank) { + dml_print("WARNING: DML_DLG: %s: vblank_start=%d vblank_end=%d\n", + __func__, + vblank_start, + vblank_end); + dml_print("WARNING: DML_DLG: %s: vstartup_start=%d should be less than min_vblank=%d\n", + __func__, + vstartup_start, + min_vblank); + min_vblank = vstartup_start + 1; + dml_print("WARNING: DML_DLG: %s: vstartup_start=%d should be less than min_vblank=%d\n", + __func__, + vstartup_start, + min_vblank); + } + + dst_x_after_scaler = get_dst_x_after_scaler(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + dst_y_after_scaler = get_dst_y_after_scaler(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + + dml_print("DML_DLG: %s: htotal = %d\n", __func__, htotal); + dml_print("DML_DLG: %s: pixel_rate_delay_subtotal = %d\n", + __func__, + pixel_rate_delay_subtotal); + dml_print("DML_DLG: %s: dst_x_after_scaler = %d\n", + __func__, + dst_x_after_scaler); + dml_print("DML_DLG: %s: dst_y_after_scaler = %d\n", + __func__, + dst_y_after_scaler); + + // Lwait + line_wait = mode_lib->soc.urgent_latency_us; + if (cstate_en) + line_wait = dml_max(mode_lib->soc.sr_enter_plus_exit_time_us, line_wait); + if (pstate_en) + line_wait = dml_max(mode_lib->soc.dram_clock_change_latency_us + + mode_lib->soc.urgent_latency_us, + line_wait); + line_wait = line_wait / line_time_in_us; + + dst_y_prefetch = get_dst_y_prefetch(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + dml_print("DML_DLG: %s: dst_y_prefetch (after rnd) = %3.2f\n", __func__, dst_y_prefetch); + + dst_y_per_vm_vblank = get_dst_y_per_vm_vblank(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_idx); + dst_y_per_row_vblank = get_dst_y_per_row_vblank(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_idx); + dst_y_per_vm_flip = get_dst_y_per_vm_flip(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + dst_y_per_row_flip = get_dst_y_per_row_flip(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + + min_dst_y_per_vm_vblank = 8.0; + min_dst_y_per_row_vblank = 16.0; + + // magic! + if (htotal <= 75) { + min_vblank = 300; + min_dst_y_per_vm_vblank = 100.0; + min_dst_y_per_row_vblank = 100.0; + } + + dml_print("DML_DLG: %s: dst_y_per_vm_vblank = %3.2f\n", __func__, dst_y_per_vm_vblank); + dml_print("DML_DLG: %s: dst_y_per_row_vblank = %3.2f\n", __func__, dst_y_per_row_vblank); + + ASSERT(dst_y_per_vm_vblank < min_dst_y_per_vm_vblank); + ASSERT(dst_y_per_row_vblank < min_dst_y_per_row_vblank); + + ASSERT(dst_y_prefetch > (dst_y_per_vm_vblank + dst_y_per_row_vblank)); + lsw = dst_y_prefetch - (dst_y_per_vm_vblank + dst_y_per_row_vblank); + + dml_print("DML_DLG: %s: lsw = %3.2f\n", __func__, lsw); + + vratio_pre_l = get_vratio_prefetch_l(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + vratio_pre_c = get_vratio_prefetch_c(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + + dml_print("DML_DLG: %s: vratio_pre_l=%3.2f\n", __func__, vratio_pre_l); + dml_print("DML_DLG: %s: vratio_pre_c=%3.2f\n", __func__, vratio_pre_c); + + // Active + req_per_swath_ub_l = rq_dlg_param.rq_l.req_per_swath_ub; + req_per_swath_ub_c = rq_dlg_param.rq_c.req_per_swath_ub; + meta_row_height_l = rq_dlg_param.rq_l.meta_row_height; + meta_row_height_c = rq_dlg_param.rq_c.meta_row_height; + swath_width_pixels_ub_l = 0; + swath_width_pixels_ub_c = 0; + scaler_rec_in_width_l = 0; + scaler_rec_in_width_c = 0; + dpte_row_height_l = rq_dlg_param.rq_l.dpte_row_height; + dpte_row_height_c = rq_dlg_param.rq_c.dpte_row_height; + + if (mode_422) { + swath_width_pixels_ub_l = swath_width_ub_l * 2; // *2 for 2 pixel per element + swath_width_pixels_ub_c = swath_width_ub_c * 2; + } else { + swath_width_pixels_ub_l = swath_width_ub_l * 1; + swath_width_pixels_ub_c = swath_width_ub_c * 1; + } + + hscale_pixel_rate_l = 0.; + hscale_pixel_rate_c = 0.; + min_hratio_fact_l = 1.0; + min_hratio_fact_c = 1.0; + + if (htaps_l <= 1) + min_hratio_fact_l = 2.0; + else if (htaps_l <= 6) { + if ((hratio_l * 2.0) > 4.0) + min_hratio_fact_l = 4.0; + else + min_hratio_fact_l = hratio_l * 2.0; + } else { + if (hratio_l > 4.0) + min_hratio_fact_l = 4.0; + else + min_hratio_fact_l = hratio_l; + } + + hscale_pixel_rate_l = min_hratio_fact_l * dppclk_freq_in_mhz; + + if (htaps_c <= 1) + min_hratio_fact_c = 2.0; + else if (htaps_c <= 6) { + if ((hratio_c * 2.0) > 4.0) + min_hratio_fact_c = 4.0; + else + min_hratio_fact_c = hratio_c * 2.0; + } else { + if (hratio_c > 4.0) + min_hratio_fact_c = 4.0; + else + min_hratio_fact_c = hratio_c; + } + + hscale_pixel_rate_c = min_hratio_fact_c * dppclk_freq_in_mhz; + + refcyc_per_line_delivery_pre_l = 0.; + refcyc_per_line_delivery_pre_c = 0.; + refcyc_per_line_delivery_l = 0.; + refcyc_per_line_delivery_c = 0.; + + refcyc_per_req_delivery_pre_l = 0.; + refcyc_per_req_delivery_pre_c = 0.; + refcyc_per_req_delivery_l = 0.; + refcyc_per_req_delivery_c = 0.; + + full_recout_width = 0; + // In ODM + if (src->is_hsplit) { + // This "hack" is only allowed (and valid) for MPC combine. In ODM + // combine, you MUST specify the full_recout_width...according to Oswin + if (dst->full_recout_width == 0 && !dst->odm_combine) { + dml_print("DML_DLG: %s: Warning: full_recout_width not set in hsplit mode\n", + __func__); + full_recout_width = dst->recout_width * 2; // assume half split for dcn1 + } else + full_recout_width = dst->full_recout_width; + } else + full_recout_width = dst->recout_width; + + // mpc_combine and odm_combine are mutually exclusive + refcyc_per_line_delivery_pre_l = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_pre_l, + hscale_pixel_rate_l, + swath_width_pixels_ub_l, + 1); // per line + + refcyc_per_line_delivery_l = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_l, + hscale_pixel_rate_l, + swath_width_pixels_ub_l, + 1); // per line + + dml_print("DML_DLG: %s: full_recout_width = %d\n", + __func__, + full_recout_width); + dml_print("DML_DLG: %s: hscale_pixel_rate_l = %3.2f\n", + __func__, + hscale_pixel_rate_l); + dml_print("DML_DLG: %s: refcyc_per_line_delivery_pre_l = %3.2f\n", + __func__, + refcyc_per_line_delivery_pre_l); + dml_print("DML_DLG: %s: refcyc_per_line_delivery_l = %3.2f\n", + __func__, + refcyc_per_line_delivery_l); + + if (dual_plane) { + refcyc_per_line_delivery_pre_c = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_pre_c, + hscale_pixel_rate_c, + swath_width_pixels_ub_c, + 1); // per line + + refcyc_per_line_delivery_c = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_c, + hscale_pixel_rate_c, + swath_width_pixels_ub_c, + 1); // per line + + dml_print("DML_DLG: %s: refcyc_per_line_delivery_pre_c = %3.2f\n", + __func__, + refcyc_per_line_delivery_pre_c); + dml_print("DML_DLG: %s: refcyc_per_line_delivery_c = %3.2f\n", + __func__, + refcyc_per_line_delivery_c); + } + + // TTU - Luma / Chroma + if (access_dir) { // vertical access + scaler_rec_in_width_l = vp_height_l; + scaler_rec_in_width_c = vp_height_c; + } else { + scaler_rec_in_width_l = vp_width_l; + scaler_rec_in_width_c = vp_width_c; + } + + refcyc_per_req_delivery_pre_l = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_pre_l, + hscale_pixel_rate_l, + scaler_rec_in_width_l, + req_per_swath_ub_l); // per req + refcyc_per_req_delivery_l = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_l, + hscale_pixel_rate_l, + scaler_rec_in_width_l, + req_per_swath_ub_l); // per req + + dml_print("DML_DLG: %s: refcyc_per_req_delivery_pre_l = %3.2f\n", + __func__, + refcyc_per_req_delivery_pre_l); + dml_print("DML_DLG: %s: refcyc_per_req_delivery_l = %3.2f\n", + __func__, + refcyc_per_req_delivery_l); + + ASSERT(refcyc_per_req_delivery_pre_l < dml_pow(2, 13)); + ASSERT(refcyc_per_req_delivery_l < dml_pow(2, 13)); + + if (dual_plane) { + refcyc_per_req_delivery_pre_c = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_pre_c, + hscale_pixel_rate_c, + scaler_rec_in_width_c, + req_per_swath_ub_c); // per req + refcyc_per_req_delivery_c = get_refcyc_per_delivery(mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + dst->odm_combine, + full_recout_width, + dst->hactive, + vratio_c, + hscale_pixel_rate_c, + scaler_rec_in_width_c, + req_per_swath_ub_c); // per req + + dml_print("DML_DLG: %s: refcyc_per_req_delivery_pre_c = %3.2f\n", + __func__, + refcyc_per_req_delivery_pre_c); + dml_print("DML_DLG: %s: refcyc_per_req_delivery_c = %3.2f\n", + __func__, + refcyc_per_req_delivery_c); + + ASSERT(refcyc_per_req_delivery_pre_c < dml_pow(2, 13)); + ASSERT(refcyc_per_req_delivery_c < dml_pow(2, 13)); + } + + // XFC + xfc_transfer_delay = get_xfc_transfer_delay(mode_lib, e2e_pipe_param, num_pipes, pipe_idx); + xfc_precharge_delay = get_xfc_precharge_delay(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_idx); + xfc_remote_surface_flip_latency = get_xfc_remote_surface_flip_latency(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_idx); + xfc_dst_y_delta_drq_limit = xfc_remote_surface_flip_latency; + xfc_prefetch_margin = get_xfc_prefetch_margin(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_idx); + + // TTU - Cursor + refcyc_per_req_delivery_pre_cur0 = 0.0; + refcyc_per_req_delivery_cur0 = 0.0; + if (src->num_cursors > 0) { + calculate_ttu_cursor(mode_lib, + &refcyc_per_req_delivery_pre_cur0, + &refcyc_per_req_delivery_cur0, + refclk_freq_in_mhz, + ref_freq_to_pix_freq, + hscale_pixel_rate_l, + scl->hscl_ratio, + vratio_pre_l, + vratio_l, + src->cur0_src_width, + (enum cursor_bpp)(src->cur0_bpp)); + } + + refcyc_per_req_delivery_pre_cur1 = 0.0; + refcyc_per_req_delivery_cur1 = 0.0; + if (src->num_cursors > 1) { + calculate_ttu_cursor(mode_lib, + &refcyc_per_req_delivery_pre_cur1, + &refcyc_per_req_delivery_cur1, + refclk_freq_in_mhz, + ref_freq_to_pix_freq, + hscale_pixel_rate_l, + scl->hscl_ratio, + vratio_pre_l, + vratio_l, + src->cur1_src_width, + (enum cursor_bpp)(src->cur1_bpp)); + } + + // TTU - Misc + // all hard-coded + + // Assignment to register structures + disp_dlg_regs->dst_y_after_scaler = dst_y_after_scaler; // in terms of line + disp_dlg_regs->refcyc_x_after_scaler = dst_x_after_scaler * ref_freq_to_pix_freq; // in terms of refclk + ASSERT(disp_dlg_regs->refcyc_x_after_scaler < (unsigned int) dml_pow(2, 13)); + disp_dlg_regs->dst_y_prefetch = (unsigned int) (dst_y_prefetch * dml_pow(2, 2)); + disp_dlg_regs->dst_y_per_vm_vblank = (unsigned int) (dst_y_per_vm_vblank * dml_pow(2, 2)); + disp_dlg_regs->dst_y_per_row_vblank = (unsigned int) (dst_y_per_row_vblank * dml_pow(2, 2)); + disp_dlg_regs->dst_y_per_vm_flip = (unsigned int) (dst_y_per_vm_flip * dml_pow(2, 2)); + disp_dlg_regs->dst_y_per_row_flip = (unsigned int) (dst_y_per_row_flip * dml_pow(2, 2)); + + disp_dlg_regs->vratio_prefetch = (unsigned int) (vratio_pre_l * dml_pow(2, 19)); + disp_dlg_regs->vratio_prefetch_c = (unsigned int) (vratio_pre_c * dml_pow(2, 19)); + + disp_dlg_regs->refcyc_per_pte_group_vblank_l = + (unsigned int) (dst_y_per_row_vblank * (double) htotal + * ref_freq_to_pix_freq / (double) dpte_groups_per_row_ub_l); + ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_l < (unsigned int) dml_pow(2, 13)); + + if (dual_plane) { + disp_dlg_regs->refcyc_per_pte_group_vblank_c = (unsigned int) (dst_y_per_row_vblank + * (double) htotal * ref_freq_to_pix_freq + / (double) dpte_groups_per_row_ub_c); + ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_c + < (unsigned int) dml_pow(2, 13)); + } + + disp_dlg_regs->refcyc_per_meta_chunk_vblank_l = + (unsigned int) (dst_y_per_row_vblank * (double) htotal + * ref_freq_to_pix_freq / (double) meta_chunks_per_row_ub_l); + ASSERT(disp_dlg_regs->refcyc_per_meta_chunk_vblank_l < (unsigned int) dml_pow(2, 13)); + + disp_dlg_regs->refcyc_per_meta_chunk_vblank_c = + disp_dlg_regs->refcyc_per_meta_chunk_vblank_l; // dcc for 4:2:0 is not supported in dcn1.0. assigned to be the same as _l for now + + disp_dlg_regs->refcyc_per_pte_group_flip_l = (unsigned int) (dst_y_per_row_flip * htotal + * ref_freq_to_pix_freq) / dpte_groups_per_row_ub_l; + disp_dlg_regs->refcyc_per_meta_chunk_flip_l = (unsigned int) (dst_y_per_row_flip * htotal + * ref_freq_to_pix_freq) / meta_chunks_per_row_ub_l; + + if (dual_plane) { + disp_dlg_regs->refcyc_per_pte_group_flip_c = (unsigned int) (dst_y_per_row_flip + * htotal * ref_freq_to_pix_freq) / dpte_groups_per_row_ub_c; + disp_dlg_regs->refcyc_per_meta_chunk_flip_c = (unsigned int) (dst_y_per_row_flip + * htotal * ref_freq_to_pix_freq) / meta_chunks_per_row_ub_c; + } + + disp_dlg_regs->dst_y_per_pte_row_nom_l = (unsigned int) ((double) dpte_row_height_l + / (double) vratio_l * dml_pow(2, 2)); + ASSERT(disp_dlg_regs->dst_y_per_pte_row_nom_l < (unsigned int) dml_pow(2, 17)); + + if (dual_plane) { + disp_dlg_regs->dst_y_per_pte_row_nom_c = (unsigned int) ((double) dpte_row_height_c + / (double) vratio_c * dml_pow(2, 2)); + if (disp_dlg_regs->dst_y_per_pte_row_nom_c >= (unsigned int) dml_pow(2, 17)) { + dml_print("DML_DLG: %s: Warning dst_y_per_pte_row_nom_c %u larger than supported by register format U15.2 %u\n", + __func__, + disp_dlg_regs->dst_y_per_pte_row_nom_c, + (unsigned int) dml_pow(2, 17) - 1); + } + } + + disp_dlg_regs->dst_y_per_meta_row_nom_l = (unsigned int) ((double) meta_row_height_l + / (double) vratio_l * dml_pow(2, 2)); + ASSERT(disp_dlg_regs->dst_y_per_meta_row_nom_l < (unsigned int) dml_pow(2, 17)); + + disp_dlg_regs->dst_y_per_meta_row_nom_c = disp_dlg_regs->dst_y_per_meta_row_nom_l; // TODO: dcc for 4:2:0 is not supported in dcn1.0. assigned to be the same as _l for now + + disp_dlg_regs->refcyc_per_pte_group_nom_l = (unsigned int) ((double) dpte_row_height_l + / (double) vratio_l * (double) htotal * ref_freq_to_pix_freq + / (double) dpte_groups_per_row_ub_l); + if (disp_dlg_regs->refcyc_per_pte_group_nom_l >= (unsigned int) dml_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_l = dml_pow(2, 23) - 1; + disp_dlg_regs->refcyc_per_meta_chunk_nom_l = (unsigned int) ((double) meta_row_height_l + / (double) vratio_l * (double) htotal * ref_freq_to_pix_freq + / (double) meta_chunks_per_row_ub_l); + if (disp_dlg_regs->refcyc_per_meta_chunk_nom_l >= (unsigned int) dml_pow(2, 23)) + disp_dlg_regs->refcyc_per_meta_chunk_nom_l = dml_pow(2, 23) - 1; + + if (dual_plane) { + disp_dlg_regs->refcyc_per_pte_group_nom_c = + (unsigned int) ((double) dpte_row_height_c / (double) vratio_c + * (double) htotal * ref_freq_to_pix_freq + / (double) dpte_groups_per_row_ub_c); + if (disp_dlg_regs->refcyc_per_pte_group_nom_c >= (unsigned int) dml_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_c = dml_pow(2, 23) - 1; + + // TODO: Is this the right calculation? Does htotal need to be halved? + disp_dlg_regs->refcyc_per_meta_chunk_nom_c = + (unsigned int) ((double) meta_row_height_c / (double) vratio_c + * (double) htotal * ref_freq_to_pix_freq + / (double) meta_chunks_per_row_ub_c); + if (disp_dlg_regs->refcyc_per_meta_chunk_nom_c >= (unsigned int) dml_pow(2, 23)) + disp_dlg_regs->refcyc_per_meta_chunk_nom_c = dml_pow(2, 23) - 1; + } + + disp_dlg_regs->refcyc_per_line_delivery_pre_l = (unsigned int) dml_floor(refcyc_per_line_delivery_pre_l, + 1); + disp_dlg_regs->refcyc_per_line_delivery_l = (unsigned int) dml_floor(refcyc_per_line_delivery_l, + 1); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_l < (unsigned int) dml_pow(2, 13)); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_l < (unsigned int) dml_pow(2, 13)); + + disp_dlg_regs->refcyc_per_line_delivery_pre_c = (unsigned int) dml_floor(refcyc_per_line_delivery_pre_c, + 1); + disp_dlg_regs->refcyc_per_line_delivery_c = (unsigned int) dml_floor(refcyc_per_line_delivery_c, + 1); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_c < (unsigned int) dml_pow(2, 13)); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_c < (unsigned int) dml_pow(2, 13)); + + disp_dlg_regs->chunk_hdl_adjust_cur0 = 3; + disp_dlg_regs->dst_y_offset_cur0 = 0; + disp_dlg_regs->chunk_hdl_adjust_cur1 = 3; + disp_dlg_regs->dst_y_offset_cur1 = 0; + + disp_dlg_regs->xfc_reg_transfer_delay = xfc_transfer_delay; + disp_dlg_regs->xfc_reg_precharge_delay = xfc_precharge_delay; + disp_dlg_regs->xfc_reg_remote_surface_flip_latency = xfc_remote_surface_flip_latency; + disp_dlg_regs->xfc_reg_prefetch_margin = dml_ceil(xfc_prefetch_margin * refclk_freq_in_mhz, + 1); + + // slave has to have this value also set to off + if (src->xfc_enable && !src->xfc_slave) + disp_dlg_regs->dst_y_delta_drq_limit = dml_ceil(xfc_dst_y_delta_drq_limit, 1); + else + disp_dlg_regs->dst_y_delta_drq_limit = 0x7fff; // off + + disp_ttu_regs->refcyc_per_req_delivery_pre_l = (unsigned int) (refcyc_per_req_delivery_pre_l + * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_l = (unsigned int) (refcyc_per_req_delivery_l + * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_pre_c = (unsigned int) (refcyc_per_req_delivery_pre_c + * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_c = (unsigned int) (refcyc_per_req_delivery_c + * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_pre_cur0 = + (unsigned int) (refcyc_per_req_delivery_pre_cur0 * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_cur0 = (unsigned int) (refcyc_per_req_delivery_cur0 + * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_pre_cur1 = + (unsigned int) (refcyc_per_req_delivery_pre_cur1 * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_cur1 = (unsigned int) (refcyc_per_req_delivery_cur1 + * dml_pow(2, 10)); + disp_ttu_regs->qos_level_low_wm = 0; + ASSERT(disp_ttu_regs->qos_level_low_wm < dml_pow(2, 14)); + disp_ttu_regs->qos_level_high_wm = (unsigned int) (4.0 * (double) htotal + * ref_freq_to_pix_freq); + ASSERT(disp_ttu_regs->qos_level_high_wm < dml_pow(2, 14)); + + disp_ttu_regs->qos_level_flip = 14; + disp_ttu_regs->qos_level_fixed_l = 8; + disp_ttu_regs->qos_level_fixed_c = 8; + disp_ttu_regs->qos_level_fixed_cur0 = 8; + disp_ttu_regs->qos_ramp_disable_l = 0; + disp_ttu_regs->qos_ramp_disable_c = 0; + disp_ttu_regs->qos_ramp_disable_cur0 = 0; + + disp_ttu_regs->min_ttu_vblank = min_ttu_vblank * refclk_freq_in_mhz; + ASSERT(disp_ttu_regs->min_ttu_vblank < dml_pow(2, 24)); + + print__ttu_regs_st(mode_lib, *disp_ttu_regs); + print__dlg_regs_st(mode_lib, *disp_dlg_regs); +} + +void dml_rq_dlg_get_dlg_reg(struct display_mode_lib *mode_lib, + display_dlg_regs_st *dlg_regs, + display_ttu_regs_st *ttu_regs, + display_e2e_pipe_params_st *e2e_pipe_param, + const unsigned int num_pipes, + const unsigned int pipe_idx, + const bool cstate_en, + const bool pstate_en, + const bool vm_en, + const bool ignore_viewport_pos, + const bool immediate_flip_support) +{ + display_rq_params_st rq_param = {0}; + display_dlg_sys_params_st dlg_sys_param = {0}; + + // Get watermark and Tex. + dlg_sys_param.t_urg_wm_us = get_wm_urgent(mode_lib, e2e_pipe_param, num_pipes); + dlg_sys_param.deepsleep_dcfclk_mhz = get_clk_dcf_deepsleep(mode_lib, + e2e_pipe_param, + num_pipes); + dlg_sys_param.t_extra_us = get_urgent_extra_latency(mode_lib, e2e_pipe_param, num_pipes); + dlg_sys_param.mem_trip_us = get_wm_memory_trip(mode_lib, e2e_pipe_param, num_pipes); + dlg_sys_param.t_mclk_wm_us = get_wm_dram_clock_change(mode_lib, e2e_pipe_param, num_pipes); + dlg_sys_param.t_sr_wm_us = get_wm_stutter_enter_exit(mode_lib, e2e_pipe_param, num_pipes); + dlg_sys_param.total_flip_bw = get_total_immediate_flip_bw(mode_lib, + e2e_pipe_param, + num_pipes); + dlg_sys_param.total_flip_bytes = get_total_immediate_flip_bytes(mode_lib, + e2e_pipe_param, + num_pipes); + dlg_sys_param.t_srx_delay_us = mode_lib->ip.dcfclk_cstate_latency + / dlg_sys_param.deepsleep_dcfclk_mhz; // TODO: Deprecated + + print__dlg_sys_params_st(mode_lib, dlg_sys_param); + + // system parameter calculation done + + dml_print("DML_DLG: Calculation for pipe[%d] start\n\n", pipe_idx); + dml_rq_dlg_get_rq_params(mode_lib, &rq_param, e2e_pipe_param[pipe_idx].pipe.src); + dml_rq_dlg_get_dlg_params(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_idx, + dlg_regs, + ttu_regs, + rq_param.dlg, + dlg_sys_param, + cstate_en, + pstate_en, + vm_en, + ignore_viewport_pos, + immediate_flip_support); + dml_print("DML_DLG: Calculation for pipe[%d] end\n", pipe_idx); +} + +void dml_rq_dlg_get_arb_params(struct display_mode_lib *mode_lib, display_arb_params_st *arb_param) +{ + memset(arb_param, 0, sizeof(*arb_param)); + arb_param->max_req_outstanding = 256; + arb_param->min_req_outstanding = 68; + arb_param->sat_level_us = 60; +} + +void calculate_ttu_cursor(struct display_mode_lib *mode_lib, + double *refcyc_per_req_delivery_pre_cur, + double *refcyc_per_req_delivery_cur, + double refclk_freq_in_mhz, + double ref_freq_to_pix_freq, + double hscale_pixel_rate_l, + double hscl_ratio, + double vratio_pre_l, + double vratio_l, + unsigned int cur_width, + enum cursor_bpp cur_bpp) +{ + unsigned int cur_src_width = cur_width; + unsigned int cur_req_size = 0; + unsigned int cur_req_width = 0; + double cur_width_ub = 0.0; + double cur_req_per_width = 0.0; + double hactive_cur = 0.0; + + ASSERT(cur_src_width <= 256); + + *refcyc_per_req_delivery_pre_cur = 0.0; + *refcyc_per_req_delivery_cur = 0.0; + if (cur_src_width > 0) { + unsigned int cur_bit_per_pixel = 0; + + if (cur_bpp == dm_cur_2bit) { + cur_req_size = 64; // byte + cur_bit_per_pixel = 2; + } else { // 32bit + cur_bit_per_pixel = 32; + if (cur_src_width >= 1 && cur_src_width <= 16) + cur_req_size = 64; + else if (cur_src_width >= 17 && cur_src_width <= 31) + cur_req_size = 128; + else + cur_req_size = 256; + } + + cur_req_width = (double) cur_req_size / ((double) cur_bit_per_pixel / 8.0); + cur_width_ub = dml_ceil((double) cur_src_width / (double) cur_req_width, 1) + * (double) cur_req_width; + cur_req_per_width = cur_width_ub / (double) cur_req_width; + hactive_cur = (double) cur_src_width / hscl_ratio; // FIXME: oswin to think about what to do for cursor + + if (vratio_pre_l <= 1.0) { + *refcyc_per_req_delivery_pre_cur = hactive_cur * ref_freq_to_pix_freq + / (double) cur_req_per_width; + } else { + *refcyc_per_req_delivery_pre_cur = (double) refclk_freq_in_mhz + * (double) cur_src_width / hscale_pixel_rate_l + / (double) cur_req_per_width; + } + + ASSERT(*refcyc_per_req_delivery_pre_cur < dml_pow(2, 13)); + + if (vratio_l <= 1.0) { + *refcyc_per_req_delivery_cur = hactive_cur * ref_freq_to_pix_freq + / (double) cur_req_per_width; + } else { + *refcyc_per_req_delivery_cur = (double) refclk_freq_in_mhz + * (double) cur_src_width / hscale_pixel_rate_l + / (double) cur_req_per_width; + } + + dml_print("DML_DLG: %s: cur_req_width = %d\n", + __func__, + cur_req_width); + dml_print("DML_DLG: %s: cur_width_ub = %3.2f\n", + __func__, + cur_width_ub); + dml_print("DML_DLG: %s: cur_req_per_width = %3.2f\n", + __func__, + cur_req_per_width); + dml_print("DML_DLG: %s: hactive_cur = %3.2f\n", + __func__, + hactive_cur); + dml_print("DML_DLG: %s: refcyc_per_req_delivery_pre_cur = %3.2f\n", + __func__, + *refcyc_per_req_delivery_pre_cur); + dml_print("DML_DLG: %s: refcyc_per_req_delivery_cur = %3.2f\n", + __func__, + *refcyc_per_req_delivery_cur); + + ASSERT(*refcyc_per_req_delivery_cur < dml_pow(2, 13)); + } +} + +unsigned int dml_rq_dlg_get_calculated_vstartup(struct display_mode_lib *mode_lib, + display_e2e_pipe_params_st *e2e_pipe_param, + const unsigned int num_pipes, + const unsigned int pipe_idx) +{ + unsigned int vstartup_pipe[DC__NUM_PIPES__MAX]; + bool visited[DC__NUM_PIPES__MAX]; + unsigned int pipe_inst = 0; + unsigned int i, j, k; + + for (k = 0; k < num_pipes; ++k) + visited[k] = false; + + for (i = 0; i < num_pipes; i++) { + if (e2e_pipe_param[i].pipe.src.is_hsplit && !visited[i]) { + unsigned int grp = e2e_pipe_param[i].pipe.src.hsplit_grp; + + for (j = i; j < num_pipes; j++) { + if (e2e_pipe_param[j].pipe.src.hsplit_grp == grp + && e2e_pipe_param[j].pipe.src.is_hsplit + && !visited[j]) { + vstartup_pipe[j] = get_vstartup_calculated(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_inst); + visited[j] = true; + } + } + + pipe_inst++; + } + + if (!visited[i]) { + vstartup_pipe[i] = get_vstartup_calculated(mode_lib, + e2e_pipe_param, + num_pipes, + pipe_inst); + visited[i] = true; + pipe_inst++; + } + } + + return vstartup_pipe[pipe_idx]; + +} + +void dml_rq_dlg_get_row_heights(struct display_mode_lib *mode_lib, + unsigned int *o_dpte_row_height, + unsigned int *o_meta_row_height, + unsigned int vp_width, + unsigned int data_pitch, + int source_format, + int tiling, + int macro_tile_size, + int source_scan, + int is_chroma) +{ + display_data_rq_dlg_params_st rq_dlg_param; + display_data_rq_misc_params_st rq_misc_param; + display_data_rq_sizing_params_st rq_sizing_param; + + get_meta_and_pte_attr(mode_lib, + &rq_dlg_param, + &rq_misc_param, + &rq_sizing_param, + vp_width, + 0, // dummy + data_pitch, + 0, // dummy + source_format, + tiling, + macro_tile_size, + source_scan, + is_chroma); + + *o_dpte_row_height = rq_dlg_param.dpte_row_height; + *o_meta_row_height = rq_dlg_param.meta_row_height; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_calc.h b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_calc.h new file mode 100644 index 000000000000..efdd4c73d8f3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_calc.h @@ -0,0 +1,148 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DML2_DISPLAY_RQ_DLG_CALC_H__ +#define __DML2_DISPLAY_RQ_DLG_CALC_H__ + +#include "dml_common_defs.h" +#include "display_rq_dlg_helpers.h" + +struct display_mode_lib; + +// Function: dml_rq_dlg_get_rq_params +// Calculate requestor related parameters that register definition agnostic +// (i.e. this layer does try to separate real values from register definition) +// Input: +// pipe_src_param - pipe source configuration (e.g. vp, pitch, etc.) +// Output: +// rq_param - values that can be used to setup RQ (e.g. swath_height, plane1_addr, etc.) +// +void dml_rq_dlg_get_rq_params( + struct display_mode_lib *mode_lib, + display_rq_params_st *rq_param, + const display_pipe_source_params_st pipe_src_param); + +// Function: dml_rq_dlg_get_rq_reg +// Main entry point for test to get the register values out of this DML class. +// This function calls and fucntions to calculate +// and then populate the rq_regs struct +// Input: +// pipe_src_param - pipe source configuration (e.g. vp, pitch, etc.) +// Output: +// rq_regs - struct that holds all the RQ registers field value. +// See also: +void dml_rq_dlg_get_rq_reg( + struct display_mode_lib *mode_lib, + display_rq_regs_st *rq_regs, + const display_pipe_source_params_st pipe_src_param); + +// Function: dml_rq_dlg_get_dlg_params +// Calculate deadline related parameters +// +void dml_rq_dlg_get_dlg_params(struct display_mode_lib *mode_lib, + const display_e2e_pipe_params_st *e2e_pipe_param, + const unsigned int num_pipes, + const unsigned int pipe_idx, + display_dlg_regs_st *disp_dlg_regs, + display_ttu_regs_st *disp_ttu_regs, + const display_rq_dlg_params_st rq_dlg_param, + const display_dlg_sys_params_st dlg_sys_param, + const bool cstate_en, + const bool pstate_en, + const bool vm_en, + const bool ignore_viewport_pos, + const bool immediate_flip_support); + +// Function: dml_rq_dlg_get_dlg_param_prefetch +// For flip_bw programming guide change, now dml needs to calculate the flip_bytes and prefetch_bw +// for ALL pipes and use this info to calculate the prefetch programming. +// Output: prefetch_param.prefetch_bw and flip_bytes +void dml_rq_dlg_get_dlg_params_prefetch( + struct display_mode_lib *mode_lib, + display_dlg_prefetch_param_st *prefetch_param, + display_rq_dlg_params_st rq_dlg_param, + display_dlg_sys_params_st dlg_sys_param, + display_e2e_pipe_params_st e2e_pipe_param, + const bool cstate_en, + const bool pstate_en, + const bool vm_en); + +// Function: dml_rq_dlg_get_dlg_reg +// Calculate and return DLG and TTU register struct given the system setting +// Output: +// dlg_regs - output DLG register struct +// ttu_regs - output DLG TTU register struct +// Input: +// e2e_pipe_param - "compacted" array of e2e pipe param struct +// num_pipes - num of active "pipe" or "route" +// pipe_idx - index that identifies the e2e_pipe_param that corresponding to this dlg +// cstate - 0: when calculate min_ttu_vblank it is assumed cstate is not required. 1: Normal mode, cstate is considered. +// Added for legacy or unrealistic timing tests. +void dml_rq_dlg_get_dlg_reg( + struct display_mode_lib *mode_lib, + display_dlg_regs_st *dlg_regs, + display_ttu_regs_st *ttu_regs, + display_e2e_pipe_params_st *e2e_pipe_param, + const unsigned int num_pipes, + const unsigned int pipe_idx, + const bool cstate_en, + const bool pstate_en, + const bool vm_en, + const bool ignore_viewport_pos, + const bool immediate_flip_support); + +// Function: dml_rq_dlg_get_calculated_vstartup +// Calculate and return vstartup +// Output: +// unsigned int vstartup +// Input: +// e2e_pipe_param - "compacted" array of e2e pipe param struct +// num_pipes - num of active "pipe" or "route" +// pipe_idx - index that identifies the e2e_pipe_param that corresponding to this dlg +// NOTE: this MUST be called after setting the prefetch mode! +unsigned int dml_rq_dlg_get_calculated_vstartup( + struct display_mode_lib *mode_lib, + display_e2e_pipe_params_st *e2e_pipe_param, + const unsigned int num_pipes, + const unsigned int pipe_idx); + +// Function: dml_rq_dlg_get_row_heights +// Calculate dpte and meta row heights +void dml_rq_dlg_get_row_heights( + struct display_mode_lib *mode_lib, + unsigned int *o_dpte_row_height, + unsigned int *o_meta_row_height, + unsigned int vp_width, + unsigned int data_pitch, + int source_format, + int tiling, + int macro_tile_size, + int source_scan, + int is_chroma); + +// Function: dml_rq_dlg_get_arb_params +void dml_rq_dlg_get_arb_params(struct display_mode_lib *mode_lib, display_arb_params_st *arb_param); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c new file mode 100644 index 000000000000..189052e911fc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c @@ -0,0 +1,392 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "display_rq_dlg_helpers.h" + +void print__rq_params_st(struct display_mode_lib *mode_lib, display_rq_params_st rq_param) +{ + dml_print("DML_RQ_DLG_CALC: ***************************\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_RQ_PARAM_ST\n"); + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_sizing_params_st(mode_lib, rq_param.sizing.rq_l); + dml_print("DML_RQ_DLG_CALC: ===\n"); + print__data_rq_sizing_params_st(mode_lib, rq_param.sizing.rq_c); + + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_dlg_params_st(mode_lib, rq_param.dlg.rq_l); + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_dlg_params_st(mode_lib, rq_param.dlg.rq_c); + + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_misc_params_st(mode_lib, rq_param.misc.rq_l); + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_misc_params_st(mode_lib, rq_param.misc.rq_c); + dml_print("DML_RQ_DLG_CALC: ***************************\n"); +} + +void print__data_rq_sizing_params_st(struct display_mode_lib *mode_lib, display_data_rq_sizing_params_st rq_sizing) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_SIZING_PARAM_ST\n"); + dml_print("DML_RQ_DLG_CALC: chunk_bytes = %0d\n", rq_sizing.chunk_bytes); + dml_print("DML_RQ_DLG_CALC: min_chunk_bytes = %0d\n", rq_sizing.min_chunk_bytes); + dml_print("DML_RQ_DLG_CALC: meta_chunk_bytes = %0d\n", rq_sizing.meta_chunk_bytes); + dml_print( + "DML_RQ_DLG_CALC: min_meta_chunk_bytes = %0d\n", + rq_sizing.min_meta_chunk_bytes); + dml_print("DML_RQ_DLG_CALC: mpte_group_bytes = %0d\n", rq_sizing.mpte_group_bytes); + dml_print("DML_RQ_DLG_CALC: dpte_group_bytes = %0d\n", rq_sizing.dpte_group_bytes); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__data_rq_dlg_params_st(struct display_mode_lib *mode_lib, display_data_rq_dlg_params_st rq_dlg_param) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_DLG_PARAM_ST\n"); + dml_print( + "DML_RQ_DLG_CALC: swath_width_ub = %0d\n", + rq_dlg_param.swath_width_ub); + dml_print( + "DML_RQ_DLG_CALC: swath_height = %0d\n", + rq_dlg_param.swath_height); + dml_print( + "DML_RQ_DLG_CALC: req_per_swath_ub = %0d\n", + rq_dlg_param.req_per_swath_ub); + dml_print( + "DML_RQ_DLG_CALC: meta_pte_bytes_per_frame_ub = %0d\n", + rq_dlg_param.meta_pte_bytes_per_frame_ub); + dml_print( + "DML_RQ_DLG_CALC: dpte_req_per_row_ub = %0d\n", + rq_dlg_param.dpte_req_per_row_ub); + dml_print( + "DML_RQ_DLG_CALC: dpte_groups_per_row_ub = %0d\n", + rq_dlg_param.dpte_groups_per_row_ub); + dml_print( + "DML_RQ_DLG_CALC: dpte_row_height = %0d\n", + rq_dlg_param.dpte_row_height); + dml_print( + "DML_RQ_DLG_CALC: dpte_bytes_per_row_ub = %0d\n", + rq_dlg_param.dpte_bytes_per_row_ub); + dml_print( + "DML_RQ_DLG_CALC: meta_chunks_per_row_ub = %0d\n", + rq_dlg_param.meta_chunks_per_row_ub); + dml_print( + "DML_RQ_DLG_CALC: meta_req_per_row_ub = %0d\n", + rq_dlg_param.meta_req_per_row_ub); + dml_print( + "DML_RQ_DLG_CALC: meta_row_height = %0d\n", + rq_dlg_param.meta_row_height); + dml_print( + "DML_RQ_DLG_CALC: meta_bytes_per_row_ub = %0d\n", + rq_dlg_param.meta_bytes_per_row_ub); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__data_rq_misc_params_st(struct display_mode_lib *mode_lib, display_data_rq_misc_params_st rq_misc_param) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_MISC_PARAM_ST\n"); + dml_print( + "DML_RQ_DLG_CALC: full_swath_bytes = %0d\n", + rq_misc_param.full_swath_bytes); + dml_print( + "DML_RQ_DLG_CALC: stored_swath_bytes = %0d\n", + rq_misc_param.stored_swath_bytes); + dml_print("DML_RQ_DLG_CALC: blk256_width = %0d\n", rq_misc_param.blk256_width); + dml_print("DML_RQ_DLG_CALC: blk256_height = %0d\n", rq_misc_param.blk256_height); + dml_print("DML_RQ_DLG_CALC: req_width = %0d\n", rq_misc_param.req_width); + dml_print("DML_RQ_DLG_CALC: req_height = %0d\n", rq_misc_param.req_height); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__rq_dlg_params_st(struct display_mode_lib *mode_lib, display_rq_dlg_params_st rq_dlg_param) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_RQ_DLG_PARAM_ST\n"); + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_dlg_params_st(mode_lib, rq_dlg_param.rq_l); + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_dlg_params_st(mode_lib, rq_dlg_param.rq_c); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__dlg_sys_params_st(struct display_mode_lib *mode_lib, display_dlg_sys_params_st dlg_sys_param) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_RQ_DLG_PARAM_ST\n"); + dml_print("DML_RQ_DLG_CALC: t_mclk_wm_us = %3.2f\n", dlg_sys_param.t_mclk_wm_us); + dml_print("DML_RQ_DLG_CALC: t_urg_wm_us = %3.2f\n", dlg_sys_param.t_urg_wm_us); + dml_print("DML_RQ_DLG_CALC: t_sr_wm_us = %3.2f\n", dlg_sys_param.t_sr_wm_us); + dml_print("DML_RQ_DLG_CALC: t_extra_us = %3.2f\n", dlg_sys_param.t_extra_us); + dml_print( + "DML_RQ_DLG_CALC: t_srx_delay_us = %3.2f\n", + dlg_sys_param.t_srx_delay_us); + dml_print( + "DML_RQ_DLG_CALC: deepsleep_dcfclk_mhz = %3.2f\n", + dlg_sys_param.deepsleep_dcfclk_mhz); + dml_print( + "DML_RQ_DLG_CALC: total_flip_bw = %3.2f\n", + dlg_sys_param.total_flip_bw); + dml_print( + "DML_RQ_DLG_CALC: total_flip_bytes = %i\n", + dlg_sys_param.total_flip_bytes); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__data_rq_regs_st(struct display_mode_lib *mode_lib, display_data_rq_regs_st rq_regs) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_REGS_ST\n"); + dml_print("DML_RQ_DLG_CALC: chunk_size = 0x%0x\n", rq_regs.chunk_size); + dml_print("DML_RQ_DLG_CALC: min_chunk_size = 0x%0x\n", rq_regs.min_chunk_size); + dml_print("DML_RQ_DLG_CALC: meta_chunk_size = 0x%0x\n", rq_regs.meta_chunk_size); + dml_print( + "DML_RQ_DLG_CALC: min_meta_chunk_size = 0x%0x\n", + rq_regs.min_meta_chunk_size); + dml_print("DML_RQ_DLG_CALC: dpte_group_size = 0x%0x\n", rq_regs.dpte_group_size); + dml_print("DML_RQ_DLG_CALC: mpte_group_size = 0x%0x\n", rq_regs.mpte_group_size); + dml_print("DML_RQ_DLG_CALC: swath_height = 0x%0x\n", rq_regs.swath_height); + dml_print( + "DML_RQ_DLG_CALC: pte_row_height_linear = 0x%0x\n", + rq_regs.pte_row_height_linear); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__rq_regs_st(struct display_mode_lib *mode_lib, display_rq_regs_st rq_regs) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_RQ_REGS_ST\n"); + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_regs_st(mode_lib, rq_regs.rq_regs_l); + dml_print("DML_RQ_DLG_CALC: \n"); + print__data_rq_regs_st(mode_lib, rq_regs.rq_regs_c); + dml_print("DML_RQ_DLG_CALC: drq_expansion_mode = 0x%0x\n", rq_regs.drq_expansion_mode); + dml_print("DML_RQ_DLG_CALC: prq_expansion_mode = 0x%0x\n", rq_regs.prq_expansion_mode); + dml_print("DML_RQ_DLG_CALC: mrq_expansion_mode = 0x%0x\n", rq_regs.mrq_expansion_mode); + dml_print("DML_RQ_DLG_CALC: crq_expansion_mode = 0x%0x\n", rq_regs.crq_expansion_mode); + dml_print("DML_RQ_DLG_CALC: plane1_base_address = 0x%0x\n", rq_regs.plane1_base_address); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__dlg_regs_st(struct display_mode_lib *mode_lib, display_dlg_regs_st dlg_regs) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_DLG_REGS_ST\n"); + dml_print( + "DML_RQ_DLG_CALC: refcyc_h_blank_end = 0x%0x\n", + dlg_regs.refcyc_h_blank_end); + dml_print( + "DML_RQ_DLG_CALC: dlg_vblank_end = 0x%0x\n", + dlg_regs.dlg_vblank_end); + dml_print( + "DML_RQ_DLG_CALC: min_dst_y_next_start = 0x%0x\n", + dlg_regs.min_dst_y_next_start); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_htotal = 0x%0x\n", + dlg_regs.refcyc_per_htotal); + dml_print( + "DML_RQ_DLG_CALC: refcyc_x_after_scaler = 0x%0x\n", + dlg_regs.refcyc_x_after_scaler); + dml_print( + "DML_RQ_DLG_CALC: dst_y_after_scaler = 0x%0x\n", + dlg_regs.dst_y_after_scaler); + dml_print( + "DML_RQ_DLG_CALC: dst_y_prefetch = 0x%0x\n", + dlg_regs.dst_y_prefetch); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_vm_vblank = 0x%0x\n", + dlg_regs.dst_y_per_vm_vblank); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_row_vblank = 0x%0x\n", + dlg_regs.dst_y_per_row_vblank); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_vm_flip = 0x%0x\n", + dlg_regs.dst_y_per_vm_flip); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_row_flip = 0x%0x\n", + dlg_regs.dst_y_per_row_flip); + dml_print( + "DML_RQ_DLG_CALC: ref_freq_to_pix_freq = 0x%0x\n", + dlg_regs.ref_freq_to_pix_freq); + dml_print( + "DML_RQ_DLG_CALC: vratio_prefetch = 0x%0x\n", + dlg_regs.vratio_prefetch); + dml_print( + "DML_RQ_DLG_CALC: vratio_prefetch_c = 0x%0x\n", + dlg_regs.vratio_prefetch_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_pte_group_vblank_l = 0x%0x\n", + dlg_regs.refcyc_per_pte_group_vblank_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_pte_group_vblank_c = 0x%0x\n", + dlg_regs.refcyc_per_pte_group_vblank_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_meta_chunk_vblank_l = 0x%0x\n", + dlg_regs.refcyc_per_meta_chunk_vblank_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_meta_chunk_vblank_c = 0x%0x\n", + dlg_regs.refcyc_per_meta_chunk_vblank_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_pte_group_flip_l = 0x%0x\n", + dlg_regs.refcyc_per_pte_group_flip_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_pte_group_flip_c = 0x%0x\n", + dlg_regs.refcyc_per_pte_group_flip_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_meta_chunk_flip_l = 0x%0x\n", + dlg_regs.refcyc_per_meta_chunk_flip_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_meta_chunk_flip_c = 0x%0x\n", + dlg_regs.refcyc_per_meta_chunk_flip_c); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_pte_row_nom_l = 0x%0x\n", + dlg_regs.dst_y_per_pte_row_nom_l); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_pte_row_nom_c = 0x%0x\n", + dlg_regs.dst_y_per_pte_row_nom_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_pte_group_nom_l = 0x%0x\n", + dlg_regs.refcyc_per_pte_group_nom_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_pte_group_nom_c = 0x%0x\n", + dlg_regs.refcyc_per_pte_group_nom_c); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_meta_row_nom_l = 0x%0x\n", + dlg_regs.dst_y_per_meta_row_nom_l); + dml_print( + "DML_RQ_DLG_CALC: dst_y_per_meta_row_nom_c = 0x%0x\n", + dlg_regs.dst_y_per_meta_row_nom_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_meta_chunk_nom_l = 0x%0x\n", + dlg_regs.refcyc_per_meta_chunk_nom_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_meta_chunk_nom_c = 0x%0x\n", + dlg_regs.refcyc_per_meta_chunk_nom_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_line_delivery_pre_l = 0x%0x\n", + dlg_regs.refcyc_per_line_delivery_pre_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_line_delivery_pre_c = 0x%0x\n", + dlg_regs.refcyc_per_line_delivery_pre_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_line_delivery_l = 0x%0x\n", + dlg_regs.refcyc_per_line_delivery_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_line_delivery_c = 0x%0x\n", + dlg_regs.refcyc_per_line_delivery_c); + dml_print( + "DML_RQ_DLG_CALC: chunk_hdl_adjust_cur0 = 0x%0x\n", + dlg_regs.chunk_hdl_adjust_cur0); + dml_print( + "DML_RQ_DLG_CALC: dst_y_offset_cur1 = 0x%0x\n", + dlg_regs.dst_y_offset_cur1); + dml_print( + "DML_RQ_DLG_CALC: chunk_hdl_adjust_cur1 = 0x%0x\n", + dlg_regs.chunk_hdl_adjust_cur1); + dml_print( + "DML_RQ_DLG_CALC: vready_after_vcount0 = 0x%0x\n", + dlg_regs.vready_after_vcount0); + dml_print( + "DML_RQ_DLG_CALC: dst_y_delta_drq_limit = 0x%0x\n", + dlg_regs.dst_y_delta_drq_limit); + dml_print( + "DML_RQ_DLG_CALC: xfc_reg_transfer_delay = 0x%0x\n", + dlg_regs.xfc_reg_transfer_delay); + dml_print( + "DML_RQ_DLG_CALC: xfc_reg_precharge_delay = 0x%0x\n", + dlg_regs.xfc_reg_precharge_delay); + dml_print( + "DML_RQ_DLG_CALC: xfc_reg_remote_surface_flip_latency = 0x%0x\n", + dlg_regs.xfc_reg_remote_surface_flip_latency); + + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} + +void print__ttu_regs_st(struct display_mode_lib *mode_lib, display_ttu_regs_st ttu_regs) +{ + dml_print("DML_RQ_DLG_CALC: =====================================\n"); + dml_print("DML_RQ_DLG_CALC: DISPLAY_TTU_REGS_ST\n"); + dml_print( + "DML_RQ_DLG_CALC: qos_level_low_wm = 0x%0x\n", + ttu_regs.qos_level_low_wm); + dml_print( + "DML_RQ_DLG_CALC: qos_level_high_wm = 0x%0x\n", + ttu_regs.qos_level_high_wm); + dml_print( + "DML_RQ_DLG_CALC: min_ttu_vblank = 0x%0x\n", + ttu_regs.min_ttu_vblank); + dml_print( + "DML_RQ_DLG_CALC: qos_level_flip = 0x%0x\n", + ttu_regs.qos_level_flip); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_pre_l = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_pre_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_l = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_l); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_pre_c = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_pre_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_c = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_c); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_cur0 = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_cur0); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_pre_cur0 = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_pre_cur0); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_cur1 = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_cur1); + dml_print( + "DML_RQ_DLG_CALC: refcyc_per_req_delivery_pre_cur1 = 0x%0x\n", + ttu_regs.refcyc_per_req_delivery_pre_cur1); + dml_print( + "DML_RQ_DLG_CALC: qos_level_fixed_l = 0x%0x\n", + ttu_regs.qos_level_fixed_l); + dml_print( + "DML_RQ_DLG_CALC: qos_ramp_disable_l = 0x%0x\n", + ttu_regs.qos_ramp_disable_l); + dml_print( + "DML_RQ_DLG_CALC: qos_level_fixed_c = 0x%0x\n", + ttu_regs.qos_level_fixed_c); + dml_print( + "DML_RQ_DLG_CALC: qos_ramp_disable_c = 0x%0x\n", + ttu_regs.qos_ramp_disable_c); + dml_print( + "DML_RQ_DLG_CALC: qos_level_fixed_cur0 = 0x%0x\n", + ttu_regs.qos_level_fixed_cur0); + dml_print( + "DML_RQ_DLG_CALC: qos_ramp_disable_cur0 = 0x%0x\n", + ttu_regs.qos_ramp_disable_cur0); + dml_print( + "DML_RQ_DLG_CALC: qos_level_fixed_cur1 = 0x%0x\n", + ttu_regs.qos_level_fixed_cur1); + dml_print( + "DML_RQ_DLG_CALC: qos_ramp_disable_cur1 = 0x%0x\n", + ttu_regs.qos_ramp_disable_cur1); + dml_print("DML_RQ_DLG_CALC: =====================================\n"); +} diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.h b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.h new file mode 100644 index 000000000000..1f24db830737 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.h @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DISPLAY_RQ_DLG_HELPERS_H__ +#define __DISPLAY_RQ_DLG_HELPERS_H__ + +#include "dml_common_defs.h" +#include "display_mode_lib.h" + +/* Function: Printer functions + * Print various struct + */ +void print__rq_params_st(struct display_mode_lib *mode_lib, display_rq_params_st rq_param); +void print__data_rq_sizing_params_st(struct display_mode_lib *mode_lib, display_data_rq_sizing_params_st rq_sizing); +void print__data_rq_dlg_params_st(struct display_mode_lib *mode_lib, display_data_rq_dlg_params_st rq_dlg_param); +void print__data_rq_misc_params_st(struct display_mode_lib *mode_lib, display_data_rq_misc_params_st rq_misc_param); +void print__rq_dlg_params_st(struct display_mode_lib *mode_lib, display_rq_dlg_params_st rq_dlg_param); +void print__dlg_sys_params_st(struct display_mode_lib *mode_lib, display_dlg_sys_params_st dlg_sys_param); + +void print__data_rq_regs_st(struct display_mode_lib *mode_lib, display_data_rq_regs_st data_rq_regs); +void print__rq_regs_st(struct display_mode_lib *mode_lib, display_rq_regs_st rq_regs); +void print__dlg_regs_st(struct display_mode_lib *mode_lib, display_dlg_regs_st dlg_regs); +void print__ttu_regs_st(struct display_mode_lib *mode_lib, display_ttu_regs_st ttu_regs); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c b/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c new file mode 100644 index 000000000000..1e4b1e383401 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c @@ -0,0 +1,1905 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dml1_display_rq_dlg_calc.h" +#include "display_mode_lib.h" + +#include "dml_inline_defs.h" + +static unsigned int get_bytes_per_element(enum source_format_class source_format, bool is_chroma) +{ + unsigned int ret_val = 0; + + if (source_format == dm_444_16) { + if (!is_chroma) + ret_val = 2; + } else if (source_format == dm_444_32) { + if (!is_chroma) + ret_val = 4; + } else if (source_format == dm_444_64) { + if (!is_chroma) + ret_val = 8; + } else if (source_format == dm_420_8) { + if (is_chroma) + ret_val = 2; + else + ret_val = 1; + } else if (source_format == dm_420_10) { + if (is_chroma) + ret_val = 4; + else + ret_val = 2; + } + return ret_val; +} + +static bool is_dual_plane(enum source_format_class source_format) +{ + bool ret_val = 0; + + if ((source_format == dm_420_8) || (source_format == dm_420_10)) + ret_val = 1; + + return ret_val; +} + +static void get_blk256_size( + unsigned int *blk256_width, + unsigned int *blk256_height, + unsigned int bytes_per_element) +{ + if (bytes_per_element == 1) { + *blk256_width = 16; + *blk256_height = 16; + } else if (bytes_per_element == 2) { + *blk256_width = 16; + *blk256_height = 8; + } else if (bytes_per_element == 4) { + *blk256_width = 8; + *blk256_height = 8; + } else if (bytes_per_element == 8) { + *blk256_width = 8; + *blk256_height = 4; + } +} + +static double get_refcyc_per_delivery( + struct display_mode_lib *mode_lib, + double refclk_freq_in_mhz, + double pclk_freq_in_mhz, + unsigned int recout_width, + double vratio, + double hscale_pixel_rate, + unsigned int delivery_width, + unsigned int req_per_swath_ub) +{ + double refcyc_per_delivery = 0.0; + + if (vratio <= 1.0) { + refcyc_per_delivery = (double) refclk_freq_in_mhz * (double) recout_width + / pclk_freq_in_mhz / (double) req_per_swath_ub; + } else { + refcyc_per_delivery = (double) refclk_freq_in_mhz * (double) delivery_width + / (double) hscale_pixel_rate / (double) req_per_swath_ub; + } + + DTRACE("DLG: %s: refclk_freq_in_mhz = %3.2f", __func__, refclk_freq_in_mhz); + DTRACE("DLG: %s: pclk_freq_in_mhz = %3.2f", __func__, pclk_freq_in_mhz); + DTRACE("DLG: %s: recout_width = %d", __func__, recout_width); + DTRACE("DLG: %s: vratio = %3.2f", __func__, vratio); + DTRACE("DLG: %s: req_per_swath_ub = %d", __func__, req_per_swath_ub); + DTRACE("DLG: %s: refcyc_per_delivery= %3.2f", __func__, refcyc_per_delivery); + + return refcyc_per_delivery; + +} + +static double get_vratio_pre( + struct display_mode_lib *mode_lib, + unsigned int max_num_sw, + unsigned int max_partial_sw, + unsigned int swath_height, + double vinit, + double l_sw) +{ + double prefill = dml_floor(vinit, 1); + double vratio_pre = 1.0; + + vratio_pre = (max_num_sw * swath_height + max_partial_sw) / l_sw; + + if (swath_height > 4) { + double tmp0 = (max_num_sw * swath_height) / (l_sw - (prefill - 3.0) / 2.0); + + if (tmp0 > vratio_pre) + vratio_pre = tmp0; + } + + DTRACE("DLG: %s: max_num_sw = %0d", __func__, max_num_sw); + DTRACE("DLG: %s: max_partial_sw = %0d", __func__, max_partial_sw); + DTRACE("DLG: %s: swath_height = %0d", __func__, swath_height); + DTRACE("DLG: %s: vinit = %3.2f", __func__, vinit); + DTRACE("DLG: %s: vratio_pre = %3.2f", __func__, vratio_pre); + + if (vratio_pre < 1.0) { + DTRACE("WARNING_DLG: %s: vratio_pre=%3.2f < 1.0, set to 1.0", __func__, vratio_pre); + vratio_pre = 1.0; + } + + if (vratio_pre > 4.0) { + DTRACE( + "WARNING_DLG: %s: vratio_pre=%3.2f > 4.0 (max scaling ratio). set to 4.0", + __func__, + vratio_pre); + vratio_pre = 4.0; + } + + return vratio_pre; +} + +static void get_swath_need( + struct display_mode_lib *mode_lib, + unsigned int *max_num_sw, + unsigned int *max_partial_sw, + unsigned int swath_height, + double vinit) +{ + double prefill = dml_floor(vinit, 1); + unsigned int max_partial_sw_int; + + DTRACE("DLG: %s: swath_height = %0d", __func__, swath_height); + DTRACE("DLG: %s: vinit = %3.2f", __func__, vinit); + + ASSERT(prefill > 0.0 && prefill <= 8.0); + + *max_num_sw = (unsigned int) (dml_ceil((prefill - 1.0) / (double) swath_height, 1) + 1.0); /* prefill has to be >= 1 */ + max_partial_sw_int = + (prefill == 1) ? + (swath_height - 1) : + ((unsigned int) (prefill - 2.0) % swath_height); + *max_partial_sw = (max_partial_sw_int < 1) ? 1 : max_partial_sw_int; /* ensure minimum of 1 is used */ + + DTRACE("DLG: %s: max_num_sw = %0d", __func__, *max_num_sw); + DTRACE("DLG: %s: max_partial_sw = %0d", __func__, *max_partial_sw); +} + +static unsigned int get_blk_size_bytes(const enum source_macro_tile_size tile_size) +{ + if (tile_size == dm_256k_tile) + return (256 * 1024); + else if (tile_size == dm_64k_tile) + return (64 * 1024); + else + return (4 * 1024); +} + +static void extract_rq_sizing_regs( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_data_rq_regs_st *rq_regs, + const struct _vcs_dpi_display_data_rq_sizing_params_st rq_sizing) +{ + DTRACE("DLG: %s: rq_sizing param", __func__); + print__data_rq_sizing_params_st(mode_lib, rq_sizing); + + rq_regs->chunk_size = dml_log2(rq_sizing.chunk_bytes) - 10; + + if (rq_sizing.min_chunk_bytes == 0) + rq_regs->min_chunk_size = 0; + else + rq_regs->min_chunk_size = dml_log2(rq_sizing.min_chunk_bytes) - 8 + 1; + + rq_regs->meta_chunk_size = dml_log2(rq_sizing.meta_chunk_bytes) - 10; + if (rq_sizing.min_meta_chunk_bytes == 0) + rq_regs->min_meta_chunk_size = 0; + else + rq_regs->min_meta_chunk_size = dml_log2(rq_sizing.min_meta_chunk_bytes) - 6 + 1; + + rq_regs->dpte_group_size = dml_log2(rq_sizing.dpte_group_bytes) - 6; + rq_regs->mpte_group_size = dml_log2(rq_sizing.mpte_group_bytes) - 6; +} + +void dml1_extract_rq_regs( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_rq_regs_st *rq_regs, + const struct _vcs_dpi_display_rq_params_st rq_param) +{ + unsigned int detile_buf_size_in_bytes = mode_lib->ip.det_buffer_size_kbytes * 1024; + unsigned int detile_buf_plane1_addr = 0; + + extract_rq_sizing_regs(mode_lib, &(rq_regs->rq_regs_l), rq_param.sizing.rq_l); + if (rq_param.yuv420) + extract_rq_sizing_regs(mode_lib, &(rq_regs->rq_regs_c), rq_param.sizing.rq_c); + + rq_regs->rq_regs_l.swath_height = dml_log2(rq_param.dlg.rq_l.swath_height); + rq_regs->rq_regs_c.swath_height = dml_log2(rq_param.dlg.rq_c.swath_height); + + /* FIXME: take the max between luma, chroma chunk size? + * okay for now, as we are setting chunk_bytes to 8kb anyways + */ + if (rq_param.sizing.rq_l.chunk_bytes >= 32 * 1024) { /*32kb */ + rq_regs->drq_expansion_mode = 0; + } else { + rq_regs->drq_expansion_mode = 2; + } + rq_regs->prq_expansion_mode = 1; + rq_regs->mrq_expansion_mode = 1; + rq_regs->crq_expansion_mode = 1; + + if (rq_param.yuv420) { + if ((double) rq_param.misc.rq_l.stored_swath_bytes + / (double) rq_param.misc.rq_c.stored_swath_bytes <= 1.5) { + detile_buf_plane1_addr = (detile_buf_size_in_bytes / 2.0 / 64.0); /* half to chroma */ + } else { + detile_buf_plane1_addr = dml_round_to_multiple( + (unsigned int) ((2.0 * detile_buf_size_in_bytes) / 3.0), + 256, + 0) / 64.0; /* 2/3 to chroma */ + } + } + rq_regs->plane1_base_address = detile_buf_plane1_addr; +} + +static void handle_det_buf_split( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_rq_params_st *rq_param, + const struct _vcs_dpi_display_pipe_source_params_st pipe_src_param) +{ + unsigned int total_swath_bytes = 0; + unsigned int swath_bytes_l = 0; + unsigned int swath_bytes_c = 0; + unsigned int full_swath_bytes_packed_l = 0; + unsigned int full_swath_bytes_packed_c = 0; + bool req128_l = 0; + bool req128_c = 0; + bool surf_linear = (pipe_src_param.sw_mode == dm_sw_linear); + bool surf_vert = (pipe_src_param.source_scan == dm_vert); + unsigned int log2_swath_height_l = 0; + unsigned int log2_swath_height_c = 0; + unsigned int detile_buf_size_in_bytes = mode_lib->ip.det_buffer_size_kbytes * 1024; + + full_swath_bytes_packed_l = rq_param->misc.rq_l.full_swath_bytes; + full_swath_bytes_packed_c = rq_param->misc.rq_c.full_swath_bytes; + + if (rq_param->yuv420_10bpc) { + full_swath_bytes_packed_l = dml_round_to_multiple( + rq_param->misc.rq_l.full_swath_bytes * 2 / 3, + 256, + 1) + 256; + full_swath_bytes_packed_c = dml_round_to_multiple( + rq_param->misc.rq_c.full_swath_bytes * 2 / 3, + 256, + 1) + 256; + } + + if (rq_param->yuv420) { + total_swath_bytes = 2 * full_swath_bytes_packed_l + 2 * full_swath_bytes_packed_c; + + if (total_swath_bytes <= detile_buf_size_in_bytes) { /*full 256b request */ + req128_l = 0; + req128_c = 0; + swath_bytes_l = full_swath_bytes_packed_l; + swath_bytes_c = full_swath_bytes_packed_c; + } else { /*128b request (for luma only for yuv420 8bpc) */ + req128_l = 1; + req128_c = 0; + swath_bytes_l = full_swath_bytes_packed_l / 2; + swath_bytes_c = full_swath_bytes_packed_c; + } + + /* Bug workaround, luma and chroma req size needs to be the same. (see: DEGVIDCN10-137) + * TODO: Remove after rtl fix + */ + if (req128_l == 1) { + req128_c = 1; + DTRACE("DLG: %s: bug workaround DEGVIDCN10-137", __func__); + } + + /* Note: assumption, the config that pass in will fit into + * the detiled buffer. + */ + } else { + total_swath_bytes = 2 * full_swath_bytes_packed_l; + + if (total_swath_bytes <= detile_buf_size_in_bytes) + req128_l = 0; + else + req128_l = 1; + + swath_bytes_l = total_swath_bytes; + swath_bytes_c = 0; + } + rq_param->misc.rq_l.stored_swath_bytes = swath_bytes_l; + rq_param->misc.rq_c.stored_swath_bytes = swath_bytes_c; + + if (surf_linear) { + log2_swath_height_l = 0; + log2_swath_height_c = 0; + } else if (!surf_vert) { + log2_swath_height_l = dml_log2(rq_param->misc.rq_l.blk256_height) - req128_l; + log2_swath_height_c = dml_log2(rq_param->misc.rq_c.blk256_height) - req128_c; + } else { + log2_swath_height_l = dml_log2(rq_param->misc.rq_l.blk256_width) - req128_l; + log2_swath_height_c = dml_log2(rq_param->misc.rq_c.blk256_width) - req128_c; + } + rq_param->dlg.rq_l.swath_height = 1 << log2_swath_height_l; + rq_param->dlg.rq_c.swath_height = 1 << log2_swath_height_c; + + DTRACE("DLG: %s: req128_l = %0d", __func__, req128_l); + DTRACE("DLG: %s: req128_c = %0d", __func__, req128_c); + DTRACE("DLG: %s: full_swath_bytes_packed_l = %0d", __func__, full_swath_bytes_packed_l); + DTRACE("DLG: %s: full_swath_bytes_packed_c = %0d", __func__, full_swath_bytes_packed_c); +} + +/* Need refactor. */ +static void dml1_rq_dlg_get_row_heights( + struct display_mode_lib *mode_lib, + unsigned int *o_dpte_row_height, + unsigned int *o_meta_row_height, + unsigned int vp_width, + unsigned int data_pitch, + int source_format, + int tiling, + int macro_tile_size, + int source_scan, + int is_chroma) +{ + bool surf_linear = (tiling == dm_sw_linear); + bool surf_vert = (source_scan == dm_vert); + + unsigned int bytes_per_element = get_bytes_per_element( + (enum source_format_class) source_format, + is_chroma); + unsigned int log2_bytes_per_element = dml_log2(bytes_per_element); + unsigned int blk256_width = 0; + unsigned int blk256_height = 0; + + unsigned int log2_blk256_height; + unsigned int blk_bytes; + unsigned int log2_blk_bytes; + unsigned int log2_blk_height; + unsigned int log2_blk_width; + unsigned int log2_meta_req_bytes; + unsigned int log2_meta_req_height; + unsigned int log2_meta_req_width; + unsigned int log2_meta_row_height; + unsigned int log2_vmpg_bytes; + unsigned int dpte_buf_in_pte_reqs; + unsigned int log2_vmpg_height; + unsigned int log2_vmpg_width; + unsigned int log2_dpte_req_height_ptes; + unsigned int log2_dpte_req_width_ptes; + unsigned int log2_dpte_req_height; + unsigned int log2_dpte_req_width; + unsigned int log2_dpte_row_height_linear; + unsigned int log2_dpte_row_height; + unsigned int dpte_req_width; + + if (surf_linear) { + blk256_width = 256; + blk256_height = 1; + } else { + get_blk256_size(&blk256_width, &blk256_height, bytes_per_element); + } + + log2_blk256_height = dml_log2((double) blk256_height); + blk_bytes = surf_linear ? + 256 : get_blk_size_bytes((enum source_macro_tile_size) macro_tile_size); + log2_blk_bytes = dml_log2((double) blk_bytes); + log2_blk_height = 0; + log2_blk_width = 0; + + /* remember log rule + * "+" in log is multiply + * "-" in log is divide + * "/2" is like square root + * blk is vertical biased + */ + if (tiling != dm_sw_linear) + log2_blk_height = log2_blk256_height + + dml_ceil((double) (log2_blk_bytes - 8) / 2.0, 1); + else + log2_blk_height = 0; /* blk height of 1 */ + + log2_blk_width = log2_blk_bytes - log2_bytes_per_element - log2_blk_height; + + /* ------- */ + /* meta */ + /* ------- */ + log2_meta_req_bytes = 6; /* meta request is 64b and is 8x8byte meta element */ + + /* each 64b meta request for dcn is 8x8 meta elements and + * a meta element covers one 256b block of the the data surface. + */ + log2_meta_req_height = log2_blk256_height + 3; /* meta req is 8x8 */ + log2_meta_req_width = log2_meta_req_bytes + 8 - log2_bytes_per_element + - log2_meta_req_height; + log2_meta_row_height = 0; + + /* the dimensions of a meta row are meta_row_width x meta_row_height in elements. + * calculate upper bound of the meta_row_width + */ + if (!surf_vert) + log2_meta_row_height = log2_meta_req_height; + else + log2_meta_row_height = log2_meta_req_width; + + *o_meta_row_height = 1 << log2_meta_row_height; + + /* ------ */ + /* dpte */ + /* ------ */ + log2_vmpg_bytes = dml_log2(mode_lib->soc.vmm_page_size_bytes); + dpte_buf_in_pte_reqs = mode_lib->ip.dpte_buffer_size_in_pte_reqs; + + log2_vmpg_height = 0; + log2_vmpg_width = 0; + log2_dpte_req_height_ptes = 0; + log2_dpte_req_width_ptes = 0; + log2_dpte_req_height = 0; + log2_dpte_req_width = 0; + log2_dpte_row_height_linear = 0; + log2_dpte_row_height = 0; + dpte_req_width = 0; /* 64b dpte req width in data element */ + + if (surf_linear) + log2_vmpg_height = 0; /* one line high */ + else + log2_vmpg_height = (log2_vmpg_bytes - 8) / 2 + log2_blk256_height; + log2_vmpg_width = log2_vmpg_bytes - log2_bytes_per_element - log2_vmpg_height; + + /* only 3 possible shapes for dpte request in dimensions of ptes: 8x1, 4x2, 2x4. */ + if (log2_blk_bytes <= log2_vmpg_bytes) + log2_dpte_req_height_ptes = 0; + else if (log2_blk_height - log2_vmpg_height >= 2) + log2_dpte_req_height_ptes = 2; + else + log2_dpte_req_height_ptes = log2_blk_height - log2_vmpg_height; + log2_dpte_req_width_ptes = 3 - log2_dpte_req_height_ptes; + + ASSERT((log2_dpte_req_width_ptes == 3 && log2_dpte_req_height_ptes == 0) || /* 8x1 */ + (log2_dpte_req_width_ptes == 2 && log2_dpte_req_height_ptes == 1) || /* 4x2 */ + (log2_dpte_req_width_ptes == 1 && log2_dpte_req_height_ptes == 2)); /* 2x4 */ + + /* the dpte request dimensions in data elements is dpte_req_width x dpte_req_height + * log2_wmpg_width is how much 1 pte represent, now trying to calculate how much 64b pte req represent + */ + log2_dpte_req_height = log2_vmpg_height + log2_dpte_req_height_ptes; + log2_dpte_req_width = log2_vmpg_width + log2_dpte_req_width_ptes; + dpte_req_width = 1 << log2_dpte_req_width; + + /* calculate pitch dpte row buffer can hold + * round the result down to a power of two. + */ + if (surf_linear) { + log2_dpte_row_height_linear = dml_floor( + dml_log2(dpte_buf_in_pte_reqs * dpte_req_width / data_pitch), + 1); + + ASSERT(log2_dpte_row_height_linear >= 3); + + if (log2_dpte_row_height_linear > 7) + log2_dpte_row_height_linear = 7; + + log2_dpte_row_height = log2_dpte_row_height_linear; + } else { + /* the upper bound of the dpte_row_width without dependency on viewport position follows. */ + if (!surf_vert) + log2_dpte_row_height = log2_dpte_req_height; + else + log2_dpte_row_height = + (log2_blk_width < log2_dpte_req_width) ? + log2_blk_width : log2_dpte_req_width; + } + + /* From programming guide: + * There is a special case of saving only half of ptes returned due to buffer space limits. + * this case applies to 4 and 8bpe in horizontal access of a vp_width greater than 2560+16 + * when the pte request is 2x4 ptes (which happens when vmpg_bytes =4kb and tile blk_bytes >=64kb). + */ + if (!surf_vert && vp_width > (2560 + 16) && bytes_per_element >= 4 && log2_vmpg_bytes == 12 + && log2_blk_bytes >= 16) + log2_dpte_row_height = log2_dpte_row_height - 1; /*half of the full height */ + + *o_dpte_row_height = 1 << log2_dpte_row_height; +} + +static void get_surf_rq_param( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_data_rq_sizing_params_st *rq_sizing_param, + struct _vcs_dpi_display_data_rq_dlg_params_st *rq_dlg_param, + struct _vcs_dpi_display_data_rq_misc_params_st *rq_misc_param, + const struct _vcs_dpi_display_pipe_source_params_st pipe_src_param, + bool is_chroma) +{ + bool mode_422 = 0; + unsigned int vp_width = 0; + unsigned int vp_height = 0; + unsigned int data_pitch = 0; + unsigned int meta_pitch = 0; + unsigned int ppe = mode_422 ? 2 : 1; + bool surf_linear; + bool surf_vert; + unsigned int bytes_per_element; + unsigned int log2_bytes_per_element; + unsigned int blk256_width; + unsigned int blk256_height; + unsigned int log2_blk256_width; + unsigned int log2_blk256_height; + unsigned int blk_bytes; + unsigned int log2_blk_bytes; + unsigned int log2_blk_height; + unsigned int log2_blk_width; + unsigned int log2_meta_req_bytes; + unsigned int log2_meta_req_height; + unsigned int log2_meta_req_width; + unsigned int meta_req_width; + unsigned int meta_req_height; + unsigned int log2_meta_row_height; + unsigned int meta_row_width_ub; + unsigned int log2_meta_chunk_bytes; + unsigned int log2_meta_chunk_height; + unsigned int log2_meta_chunk_width; + unsigned int log2_min_meta_chunk_bytes; + unsigned int min_meta_chunk_width; + unsigned int meta_chunk_width; + unsigned int meta_chunk_per_row_int; + unsigned int meta_row_remainder; + unsigned int meta_chunk_threshold; + unsigned int meta_blk_bytes; + unsigned int meta_blk_height; + unsigned int meta_blk_width; + unsigned int meta_surface_bytes; + unsigned int vmpg_bytes; + unsigned int meta_pte_req_per_frame_ub; + unsigned int meta_pte_bytes_per_frame_ub; + unsigned int log2_vmpg_bytes; + unsigned int dpte_buf_in_pte_reqs; + unsigned int log2_vmpg_height; + unsigned int log2_vmpg_width; + unsigned int log2_dpte_req_height_ptes; + unsigned int log2_dpte_req_width_ptes; + unsigned int log2_dpte_req_height; + unsigned int log2_dpte_req_width; + unsigned int log2_dpte_row_height_linear; + unsigned int log2_dpte_row_height; + unsigned int log2_dpte_group_width; + unsigned int dpte_row_width_ub; + unsigned int dpte_row_height; + unsigned int dpte_req_height; + unsigned int dpte_req_width; + unsigned int dpte_group_width; + unsigned int log2_dpte_group_bytes; + unsigned int log2_dpte_group_length; + unsigned int func_meta_row_height, func_dpte_row_height; + + /* FIXME check if ppe apply for both luma and chroma in 422 case */ + if (is_chroma) { + vp_width = pipe_src_param.viewport_width_c / ppe; + vp_height = pipe_src_param.viewport_height_c; + data_pitch = pipe_src_param.data_pitch_c; + meta_pitch = pipe_src_param.meta_pitch_c; + } else { + vp_width = pipe_src_param.viewport_width / ppe; + vp_height = pipe_src_param.viewport_height; + data_pitch = pipe_src_param.data_pitch; + meta_pitch = pipe_src_param.meta_pitch; + } + + rq_sizing_param->chunk_bytes = 8192; + + if (rq_sizing_param->chunk_bytes == 64 * 1024) + rq_sizing_param->min_chunk_bytes = 0; + else + rq_sizing_param->min_chunk_bytes = 1024; + + rq_sizing_param->meta_chunk_bytes = 2048; + rq_sizing_param->min_meta_chunk_bytes = 256; + + rq_sizing_param->mpte_group_bytes = 2048; + + surf_linear = (pipe_src_param.sw_mode == dm_sw_linear); + surf_vert = (pipe_src_param.source_scan == dm_vert); + + bytes_per_element = get_bytes_per_element( + (enum source_format_class) pipe_src_param.source_format, + is_chroma); + log2_bytes_per_element = dml_log2(bytes_per_element); + blk256_width = 0; + blk256_height = 0; + + if (surf_linear) { + blk256_width = 256 / bytes_per_element; + blk256_height = 1; + } else { + get_blk256_size(&blk256_width, &blk256_height, bytes_per_element); + } + + DTRACE("DLG: %s: surf_linear = %d", __func__, surf_linear); + DTRACE("DLG: %s: surf_vert = %d", __func__, surf_vert); + DTRACE("DLG: %s: blk256_width = %d", __func__, blk256_width); + DTRACE("DLG: %s: blk256_height = %d", __func__, blk256_height); + + log2_blk256_width = dml_log2((double) blk256_width); + log2_blk256_height = dml_log2((double) blk256_height); + blk_bytes = + surf_linear ? 256 : get_blk_size_bytes( + (enum source_macro_tile_size) pipe_src_param.macro_tile_size); + log2_blk_bytes = dml_log2((double) blk_bytes); + log2_blk_height = 0; + log2_blk_width = 0; + + /* remember log rule + * "+" in log is multiply + * "-" in log is divide + * "/2" is like square root + * blk is vertical biased + */ + if (pipe_src_param.sw_mode != dm_sw_linear) + log2_blk_height = log2_blk256_height + + dml_ceil((double) (log2_blk_bytes - 8) / 2.0, 1); + else + log2_blk_height = 0; /* blk height of 1 */ + + log2_blk_width = log2_blk_bytes - log2_bytes_per_element - log2_blk_height; + + if (!surf_vert) { + rq_dlg_param->swath_width_ub = dml_round_to_multiple(vp_width - 1, blk256_width, 1) + + blk256_width; + rq_dlg_param->req_per_swath_ub = rq_dlg_param->swath_width_ub >> log2_blk256_width; + } else { + rq_dlg_param->swath_width_ub = dml_round_to_multiple( + vp_height - 1, + blk256_height, + 1) + blk256_height; + rq_dlg_param->req_per_swath_ub = rq_dlg_param->swath_width_ub >> log2_blk256_height; + } + + if (!surf_vert) + rq_misc_param->full_swath_bytes = rq_dlg_param->swath_width_ub * blk256_height + * bytes_per_element; + else + rq_misc_param->full_swath_bytes = rq_dlg_param->swath_width_ub * blk256_width + * bytes_per_element; + + rq_misc_param->blk256_height = blk256_height; + rq_misc_param->blk256_width = blk256_width; + + /* ------- */ + /* meta */ + /* ------- */ + log2_meta_req_bytes = 6; /* meta request is 64b and is 8x8byte meta element */ + + /* each 64b meta request for dcn is 8x8 meta elements and + * a meta element covers one 256b block of the the data surface. + */ + log2_meta_req_height = log2_blk256_height + 3; /* meta req is 8x8 byte, each byte represent 1 blk256 */ + log2_meta_req_width = log2_meta_req_bytes + 8 - log2_bytes_per_element + - log2_meta_req_height; + meta_req_width = 1 << log2_meta_req_width; + meta_req_height = 1 << log2_meta_req_height; + log2_meta_row_height = 0; + meta_row_width_ub = 0; + + /* the dimensions of a meta row are meta_row_width x meta_row_height in elements. + * calculate upper bound of the meta_row_width + */ + if (!surf_vert) { + log2_meta_row_height = log2_meta_req_height; + meta_row_width_ub = dml_round_to_multiple(vp_width - 1, meta_req_width, 1) + + meta_req_width; + rq_dlg_param->meta_req_per_row_ub = meta_row_width_ub / meta_req_width; + } else { + log2_meta_row_height = log2_meta_req_width; + meta_row_width_ub = dml_round_to_multiple(vp_height - 1, meta_req_height, 1) + + meta_req_height; + rq_dlg_param->meta_req_per_row_ub = meta_row_width_ub / meta_req_height; + } + rq_dlg_param->meta_bytes_per_row_ub = rq_dlg_param->meta_req_per_row_ub * 64; + + log2_meta_chunk_bytes = dml_log2(rq_sizing_param->meta_chunk_bytes); + log2_meta_chunk_height = log2_meta_row_height; + + /*full sized meta chunk width in unit of data elements */ + log2_meta_chunk_width = log2_meta_chunk_bytes + 8 - log2_bytes_per_element + - log2_meta_chunk_height; + log2_min_meta_chunk_bytes = dml_log2(rq_sizing_param->min_meta_chunk_bytes); + min_meta_chunk_width = 1 + << (log2_min_meta_chunk_bytes + 8 - log2_bytes_per_element + - log2_meta_chunk_height); + meta_chunk_width = 1 << log2_meta_chunk_width; + meta_chunk_per_row_int = (unsigned int) (meta_row_width_ub / meta_chunk_width); + meta_row_remainder = meta_row_width_ub % meta_chunk_width; + meta_chunk_threshold = 0; + meta_blk_bytes = 4096; + meta_blk_height = blk256_height * 64; + meta_blk_width = meta_blk_bytes * 256 / bytes_per_element / meta_blk_height; + meta_surface_bytes = meta_pitch + * (dml_round_to_multiple(vp_height - 1, meta_blk_height, 1) + + meta_blk_height) * bytes_per_element / 256; + vmpg_bytes = mode_lib->soc.vmm_page_size_bytes; + meta_pte_req_per_frame_ub = (dml_round_to_multiple( + meta_surface_bytes - vmpg_bytes, + 8 * vmpg_bytes, + 1) + 8 * vmpg_bytes) / (8 * vmpg_bytes); + meta_pte_bytes_per_frame_ub = meta_pte_req_per_frame_ub * 64; /*64B mpte request */ + rq_dlg_param->meta_pte_bytes_per_frame_ub = meta_pte_bytes_per_frame_ub; + + DTRACE("DLG: %s: meta_blk_height = %d", __func__, meta_blk_height); + DTRACE("DLG: %s: meta_blk_width = %d", __func__, meta_blk_width); + DTRACE("DLG: %s: meta_surface_bytes = %d", __func__, meta_surface_bytes); + DTRACE("DLG: %s: meta_pte_req_per_frame_ub = %d", __func__, meta_pte_req_per_frame_ub); + DTRACE("DLG: %s: meta_pte_bytes_per_frame_ub = %d", __func__, meta_pte_bytes_per_frame_ub); + + if (!surf_vert) + meta_chunk_threshold = 2 * min_meta_chunk_width - meta_req_width; + else + meta_chunk_threshold = 2 * min_meta_chunk_width - meta_req_height; + + if (meta_row_remainder <= meta_chunk_threshold) + rq_dlg_param->meta_chunks_per_row_ub = meta_chunk_per_row_int + 1; + else + rq_dlg_param->meta_chunks_per_row_ub = meta_chunk_per_row_int + 2; + + rq_dlg_param->meta_row_height = 1 << log2_meta_row_height; + + /* ------ */ + /* dpte */ + /* ------ */ + log2_vmpg_bytes = dml_log2(mode_lib->soc.vmm_page_size_bytes); + dpte_buf_in_pte_reqs = mode_lib->ip.dpte_buffer_size_in_pte_reqs; + + log2_vmpg_height = 0; + log2_vmpg_width = 0; + log2_dpte_req_height_ptes = 0; + log2_dpte_req_width_ptes = 0; + log2_dpte_req_height = 0; + log2_dpte_req_width = 0; + log2_dpte_row_height_linear = 0; + log2_dpte_row_height = 0; + log2_dpte_group_width = 0; + dpte_row_width_ub = 0; + dpte_row_height = 0; + dpte_req_height = 0; /* 64b dpte req height in data element */ + dpte_req_width = 0; /* 64b dpte req width in data element */ + dpte_group_width = 0; + log2_dpte_group_bytes = 0; + log2_dpte_group_length = 0; + + if (surf_linear) + log2_vmpg_height = 0; /* one line high */ + else + log2_vmpg_height = (log2_vmpg_bytes - 8) / 2 + log2_blk256_height; + log2_vmpg_width = log2_vmpg_bytes - log2_bytes_per_element - log2_vmpg_height; + + /* only 3 possible shapes for dpte request in dimensions of ptes: 8x1, 4x2, 2x4. */ + if (log2_blk_bytes <= log2_vmpg_bytes) + log2_dpte_req_height_ptes = 0; + else if (log2_blk_height - log2_vmpg_height >= 2) + log2_dpte_req_height_ptes = 2; + else + log2_dpte_req_height_ptes = log2_blk_height - log2_vmpg_height; + log2_dpte_req_width_ptes = 3 - log2_dpte_req_height_ptes; + + /* Ensure we only have the 3 shapes */ + ASSERT((log2_dpte_req_width_ptes == 3 && log2_dpte_req_height_ptes == 0) || /* 8x1 */ + (log2_dpte_req_width_ptes == 2 && log2_dpte_req_height_ptes == 1) || /* 4x2 */ + (log2_dpte_req_width_ptes == 1 && log2_dpte_req_height_ptes == 2)); /* 2x4 */ + + /* The dpte request dimensions in data elements is dpte_req_width x dpte_req_height + * log2_vmpg_width is how much 1 pte represent, now calculating how much a 64b pte req represent + * That depends on the pte shape (i.e. 8x1, 4x2, 2x4) + */ + log2_dpte_req_height = log2_vmpg_height + log2_dpte_req_height_ptes; + log2_dpte_req_width = log2_vmpg_width + log2_dpte_req_width_ptes; + dpte_req_height = 1 << log2_dpte_req_height; + dpte_req_width = 1 << log2_dpte_req_width; + + /* calculate pitch dpte row buffer can hold + * round the result down to a power of two. + */ + if (surf_linear) { + log2_dpte_row_height_linear = dml_floor( + dml_log2(dpte_buf_in_pte_reqs * dpte_req_width / data_pitch), + 1); + + ASSERT(log2_dpte_row_height_linear >= 3); + + if (log2_dpte_row_height_linear > 7) + log2_dpte_row_height_linear = 7; + + log2_dpte_row_height = log2_dpte_row_height_linear; + rq_dlg_param->dpte_row_height = 1 << log2_dpte_row_height; + + /* For linear, the dpte row is pitch dependent and the pte requests wrap at the pitch boundary. + * the dpte_row_width_ub is the upper bound of data_pitch*dpte_row_height in elements with this unique buffering. + */ + dpte_row_width_ub = dml_round_to_multiple( + data_pitch * dpte_row_height - 1, + dpte_req_width, + 1) + dpte_req_width; + rq_dlg_param->dpte_req_per_row_ub = dpte_row_width_ub / dpte_req_width; + } else { + /* for tiled mode, row height is the same as req height and row store up to vp size upper bound */ + if (!surf_vert) { + log2_dpte_row_height = log2_dpte_req_height; + dpte_row_width_ub = dml_round_to_multiple(vp_width - 1, dpte_req_width, 1) + + dpte_req_width; + rq_dlg_param->dpte_req_per_row_ub = dpte_row_width_ub / dpte_req_width; + } else { + log2_dpte_row_height = + (log2_blk_width < log2_dpte_req_width) ? + log2_blk_width : log2_dpte_req_width; + dpte_row_width_ub = dml_round_to_multiple(vp_height - 1, dpte_req_height, 1) + + dpte_req_height; + rq_dlg_param->dpte_req_per_row_ub = dpte_row_width_ub / dpte_req_height; + } + rq_dlg_param->dpte_row_height = 1 << log2_dpte_row_height; + } + rq_dlg_param->dpte_bytes_per_row_ub = rq_dlg_param->dpte_req_per_row_ub * 64; + + /* From programming guide: + * There is a special case of saving only half of ptes returned due to buffer space limits. + * this case applies to 4 and 8bpe in horizontal access of a vp_width greater than 2560+16 + * when the pte request is 2x4 ptes (which happens when vmpg_bytes =4kb and tile blk_bytes >=64kb). + */ + if (!surf_vert && vp_width > (2560 + 16) && bytes_per_element >= 4 && log2_vmpg_bytes == 12 + && log2_blk_bytes >= 16) { + log2_dpte_row_height = log2_dpte_row_height - 1; /*half of the full height */ + rq_dlg_param->dpte_row_height = 1 << log2_dpte_row_height; + } + + /* the dpte_group_bytes is reduced for the specific case of vertical + * access of a tile surface that has dpte request of 8x1 ptes. + */ + if (!surf_linear & (log2_dpte_req_height_ptes == 0) & surf_vert) /*reduced, in this case, will have page fault within a group */ + rq_sizing_param->dpte_group_bytes = 512; + else + /*full size */ + rq_sizing_param->dpte_group_bytes = 2048; + + /*since pte request size is 64byte, the number of data pte requests per full sized group is as follows. */ + log2_dpte_group_bytes = dml_log2(rq_sizing_param->dpte_group_bytes); + log2_dpte_group_length = log2_dpte_group_bytes - 6; /*length in 64b requests */ + + /* full sized data pte group width in elements */ + if (!surf_vert) + log2_dpte_group_width = log2_dpte_group_length + log2_dpte_req_width; + else + log2_dpte_group_width = log2_dpte_group_length + log2_dpte_req_height; + + dpte_group_width = 1 << log2_dpte_group_width; + + /* since dpte groups are only aligned to dpte_req_width and not dpte_group_width, + * the upper bound for the dpte groups per row is as follows. + */ + rq_dlg_param->dpte_groups_per_row_ub = dml_ceil( + (double) dpte_row_width_ub / dpte_group_width, + 1); + + dml1_rq_dlg_get_row_heights( + mode_lib, + &func_dpte_row_height, + &func_meta_row_height, + vp_width, + data_pitch, + pipe_src_param.source_format, + pipe_src_param.sw_mode, + pipe_src_param.macro_tile_size, + pipe_src_param.source_scan, + is_chroma); + + /* Just a check to make sure this function and the new one give the same + * result. The standalone get_row_heights() function is based off of the + * code in this function so the same changes need to be made to both. + */ + if (rq_dlg_param->meta_row_height != func_meta_row_height) { + DTRACE( + "MISMATCH: rq_dlg_param->meta_row_height = %d", + rq_dlg_param->meta_row_height); + DTRACE("MISMATCH: func_meta_row_height = %d", func_meta_row_height); + ASSERT(0); + } + + if (rq_dlg_param->dpte_row_height != func_dpte_row_height) { + DTRACE( + "MISMATCH: rq_dlg_param->dpte_row_height = %d", + rq_dlg_param->dpte_row_height); + DTRACE("MISMATCH: func_dpte_row_height = %d", func_dpte_row_height); + ASSERT(0); + } +} + +void dml1_rq_dlg_get_rq_params( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_rq_params_st *rq_param, + const struct _vcs_dpi_display_pipe_source_params_st pipe_src_param) +{ + /* get param for luma surface */ + rq_param->yuv420 = pipe_src_param.source_format == dm_420_8 + || pipe_src_param.source_format == dm_420_10; + rq_param->yuv420_10bpc = pipe_src_param.source_format == dm_420_10; + + get_surf_rq_param( + mode_lib, + &(rq_param->sizing.rq_l), + &(rq_param->dlg.rq_l), + &(rq_param->misc.rq_l), + pipe_src_param, + 0); + + if (is_dual_plane((enum source_format_class) pipe_src_param.source_format)) { + /* get param for chroma surface */ + get_surf_rq_param( + mode_lib, + &(rq_param->sizing.rq_c), + &(rq_param->dlg.rq_c), + &(rq_param->misc.rq_c), + pipe_src_param, + 1); + } + + /* calculate how to split the det buffer space between luma and chroma */ + handle_det_buf_split(mode_lib, rq_param, pipe_src_param); + print__rq_params_st(mode_lib, *rq_param); +} + +/* Note: currently taken in as is. + * Nice to decouple code from hw register implement and extract code that are repeated for luma and chroma. + */ +void dml1_rq_dlg_get_dlg_params( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_dlg_regs_st *disp_dlg_regs, + struct _vcs_dpi_display_ttu_regs_st *disp_ttu_regs, + const struct _vcs_dpi_display_rq_dlg_params_st rq_dlg_param, + const struct _vcs_dpi_display_dlg_sys_params_st dlg_sys_param, + const struct _vcs_dpi_display_e2e_pipe_params_st e2e_pipe_param, + const bool cstate_en, + const bool pstate_en, + const bool vm_en, + const bool iflip_en) +{ + /* Timing */ + unsigned int htotal = e2e_pipe_param.pipe.dest.htotal; + unsigned int hblank_end = e2e_pipe_param.pipe.dest.hblank_end; + unsigned int vblank_start = e2e_pipe_param.pipe.dest.vblank_start; + unsigned int vblank_end = e2e_pipe_param.pipe.dest.vblank_end; + bool interlaced = e2e_pipe_param.pipe.dest.interlaced; + unsigned int min_vblank = mode_lib->ip.min_vblank_lines; + + double pclk_freq_in_mhz = e2e_pipe_param.pipe.dest.pixel_rate_mhz; + double refclk_freq_in_mhz = e2e_pipe_param.clks_cfg.refclk_mhz; + double dppclk_freq_in_mhz = e2e_pipe_param.clks_cfg.dppclk_mhz; + double dispclk_freq_in_mhz = e2e_pipe_param.clks_cfg.dispclk_mhz; + + double ref_freq_to_pix_freq; + double prefetch_xy_calc_in_dcfclk; + double min_dcfclk_mhz; + double t_calc_us; + double min_ttu_vblank; + double min_dst_y_ttu_vblank; + unsigned int dlg_vblank_start; + bool dcc_en; + bool dual_plane; + bool mode_422; + unsigned int access_dir; + unsigned int bytes_per_element_l; + unsigned int bytes_per_element_c; + unsigned int vp_height_l; + unsigned int vp_width_l; + unsigned int vp_height_c; + unsigned int vp_width_c; + unsigned int htaps_l; + unsigned int htaps_c; + double hratios_l; + double hratios_c; + double vratio_l; + double vratio_c; + double line_time_in_us; + double vinit_l; + double vinit_c; + double vinit_bot_l; + double vinit_bot_c; + unsigned int swath_height_l; + unsigned int swath_width_ub_l; + unsigned int dpte_bytes_per_row_ub_l; + unsigned int dpte_groups_per_row_ub_l; + unsigned int meta_pte_bytes_per_frame_ub_l; + unsigned int meta_bytes_per_row_ub_l; + unsigned int swath_height_c; + unsigned int swath_width_ub_c; + unsigned int dpte_bytes_per_row_ub_c; + unsigned int dpte_groups_per_row_ub_c; + unsigned int meta_chunks_per_row_ub_l; + unsigned int vupdate_offset; + unsigned int vupdate_width; + unsigned int vready_offset; + unsigned int dppclk_delay_subtotal; + unsigned int dispclk_delay_subtotal; + unsigned int pixel_rate_delay_subtotal; + unsigned int vstartup_start; + unsigned int dst_x_after_scaler; + unsigned int dst_y_after_scaler; + double line_wait; + double line_o; + double line_setup; + double line_calc; + double dst_y_prefetch; + double t_pre_us; + unsigned int vm_bytes; + unsigned int meta_row_bytes; + unsigned int max_num_sw_l; + unsigned int max_num_sw_c; + unsigned int max_partial_sw_l; + unsigned int max_partial_sw_c; + double max_vinit_l; + double max_vinit_c; + unsigned int lsw_l; + unsigned int lsw_c; + unsigned int sw_bytes_ub_l; + unsigned int sw_bytes_ub_c; + unsigned int sw_bytes; + unsigned int dpte_row_bytes; + double prefetch_bw; + double flip_bw; + double t_vm_us; + double t_r0_us; + double dst_y_per_vm_vblank; + double dst_y_per_row_vblank; + double min_dst_y_per_vm_vblank; + double min_dst_y_per_row_vblank; + double lsw; + double vratio_pre_l; + double vratio_pre_c; + unsigned int req_per_swath_ub_l; + unsigned int req_per_swath_ub_c; + unsigned int meta_row_height_l; + unsigned int swath_width_pixels_ub_l; + unsigned int swath_width_pixels_ub_c; + unsigned int scaler_rec_in_width_l; + unsigned int scaler_rec_in_width_c; + unsigned int dpte_row_height_l; + unsigned int dpte_row_height_c; + double hscale_pixel_rate_l; + double hscale_pixel_rate_c; + double min_hratio_fact_l; + double min_hratio_fact_c; + double refcyc_per_line_delivery_pre_l; + double refcyc_per_line_delivery_pre_c; + double refcyc_per_line_delivery_l; + double refcyc_per_line_delivery_c; + double refcyc_per_req_delivery_pre_l; + double refcyc_per_req_delivery_pre_c; + double refcyc_per_req_delivery_l; + double refcyc_per_req_delivery_c; + double refcyc_per_req_delivery_pre_cur0; + double refcyc_per_req_delivery_cur0; + unsigned int full_recout_width; + double hratios_cur0; + unsigned int cur0_src_width; + enum cursor_bpp cur0_bpp; + unsigned int cur0_req_size; + unsigned int cur0_req_width; + double cur0_width_ub; + double cur0_req_per_width; + double hactive_cur0; + + memset(disp_dlg_regs, 0, sizeof(*disp_dlg_regs)); + memset(disp_ttu_regs, 0, sizeof(*disp_ttu_regs)); + + DTRACE("DLG: %s: cstate_en = %d", __func__, cstate_en); + DTRACE("DLG: %s: pstate_en = %d", __func__, pstate_en); + DTRACE("DLG: %s: vm_en = %d", __func__, vm_en); + DTRACE("DLG: %s: iflip_en = %d", __func__, iflip_en); + + /* ------------------------- */ + /* Section 1.5.2.1: OTG dependent Params */ + /* ------------------------- */ + DTRACE("DLG: %s: dppclk_freq_in_mhz = %3.2f", __func__, dppclk_freq_in_mhz); + DTRACE("DLG: %s: dispclk_freq_in_mhz = %3.2f", __func__, dispclk_freq_in_mhz); + DTRACE("DLG: %s: refclk_freq_in_mhz = %3.2f", __func__, refclk_freq_in_mhz); + DTRACE("DLG: %s: pclk_freq_in_mhz = %3.2f", __func__, pclk_freq_in_mhz); + DTRACE("DLG: %s: interlaced = %d", __func__, interlaced); + + ref_freq_to_pix_freq = refclk_freq_in_mhz / pclk_freq_in_mhz; + ASSERT(ref_freq_to_pix_freq < 4.0); + disp_dlg_regs->ref_freq_to_pix_freq = + (unsigned int) (ref_freq_to_pix_freq * dml_pow(2, 19)); + disp_dlg_regs->refcyc_per_htotal = (unsigned int) (ref_freq_to_pix_freq * (double) htotal + * dml_pow(2, 8)); + disp_dlg_regs->refcyc_h_blank_end = (unsigned int) ((double) hblank_end + * (double) ref_freq_to_pix_freq); + ASSERT(disp_dlg_regs->refcyc_h_blank_end < (unsigned int) dml_pow(2, 13)); + disp_dlg_regs->dlg_vblank_end = interlaced ? (vblank_end / 2) : vblank_end; /* 15 bits */ + + prefetch_xy_calc_in_dcfclk = 24.0; /* FIXME: ip_param */ + min_dcfclk_mhz = dlg_sys_param.deepsleep_dcfclk_mhz; + t_calc_us = prefetch_xy_calc_in_dcfclk / min_dcfclk_mhz; + min_ttu_vblank = dlg_sys_param.t_urg_wm_us; + if (cstate_en) + min_ttu_vblank = dml_max(dlg_sys_param.t_sr_wm_us, min_ttu_vblank); + if (pstate_en) + min_ttu_vblank = dml_max(dlg_sys_param.t_mclk_wm_us, min_ttu_vblank); + min_ttu_vblank = min_ttu_vblank + t_calc_us; + + min_dst_y_ttu_vblank = min_ttu_vblank * pclk_freq_in_mhz / (double) htotal; + dlg_vblank_start = interlaced ? (vblank_start / 2) : vblank_start; + + disp_dlg_regs->min_dst_y_next_start = (unsigned int) (((double) dlg_vblank_start + + min_dst_y_ttu_vblank) * dml_pow(2, 2)); + ASSERT(disp_dlg_regs->min_dst_y_next_start < (unsigned int) dml_pow(2, 18)); + + DTRACE("DLG: %s: min_dcfclk_mhz = %3.2f", __func__, min_dcfclk_mhz); + DTRACE("DLG: %s: min_ttu_vblank = %3.2f", __func__, min_ttu_vblank); + DTRACE( + "DLG: %s: min_dst_y_ttu_vblank = %3.2f", + __func__, + min_dst_y_ttu_vblank); + DTRACE("DLG: %s: t_calc_us = %3.2f", __func__, t_calc_us); + DTRACE( + "DLG: %s: disp_dlg_regs->min_dst_y_next_start = 0x%0x", + __func__, + disp_dlg_regs->min_dst_y_next_start); + DTRACE( + "DLG: %s: ref_freq_to_pix_freq = %3.2f", + __func__, + ref_freq_to_pix_freq); + + /* ------------------------- */ + /* Section 1.5.2.2: Prefetch, Active and TTU */ + /* ------------------------- */ + /* Prefetch Calc */ + /* Source */ + dcc_en = e2e_pipe_param.pipe.src.dcc; + dual_plane = is_dual_plane( + (enum source_format_class) e2e_pipe_param.pipe.src.source_format); + mode_422 = 0; /* FIXME */ + access_dir = (e2e_pipe_param.pipe.src.source_scan == dm_vert); /* vp access direction: horizontal or vertical accessed */ + bytes_per_element_l = get_bytes_per_element( + (enum source_format_class) e2e_pipe_param.pipe.src.source_format, + 0); + bytes_per_element_c = get_bytes_per_element( + (enum source_format_class) e2e_pipe_param.pipe.src.source_format, + 1); + vp_height_l = e2e_pipe_param.pipe.src.viewport_height; + vp_width_l = e2e_pipe_param.pipe.src.viewport_width; + vp_height_c = e2e_pipe_param.pipe.src.viewport_height_c; + vp_width_c = e2e_pipe_param.pipe.src.viewport_width_c; + + /* Scaling */ + htaps_l = e2e_pipe_param.pipe.scale_taps.htaps; + htaps_c = e2e_pipe_param.pipe.scale_taps.htaps_c; + hratios_l = e2e_pipe_param.pipe.scale_ratio_depth.hscl_ratio; + hratios_c = e2e_pipe_param.pipe.scale_ratio_depth.hscl_ratio_c; + vratio_l = e2e_pipe_param.pipe.scale_ratio_depth.vscl_ratio; + vratio_c = e2e_pipe_param.pipe.scale_ratio_depth.vscl_ratio_c; + + line_time_in_us = (htotal / pclk_freq_in_mhz); + vinit_l = e2e_pipe_param.pipe.scale_ratio_depth.vinit; + vinit_c = e2e_pipe_param.pipe.scale_ratio_depth.vinit_c; + vinit_bot_l = e2e_pipe_param.pipe.scale_ratio_depth.vinit_bot; + vinit_bot_c = e2e_pipe_param.pipe.scale_ratio_depth.vinit_bot_c; + + swath_height_l = rq_dlg_param.rq_l.swath_height; + swath_width_ub_l = rq_dlg_param.rq_l.swath_width_ub; + dpte_bytes_per_row_ub_l = rq_dlg_param.rq_l.dpte_bytes_per_row_ub; + dpte_groups_per_row_ub_l = rq_dlg_param.rq_l.dpte_groups_per_row_ub; + meta_pte_bytes_per_frame_ub_l = rq_dlg_param.rq_l.meta_pte_bytes_per_frame_ub; + meta_bytes_per_row_ub_l = rq_dlg_param.rq_l.meta_bytes_per_row_ub; + + swath_height_c = rq_dlg_param.rq_c.swath_height; + swath_width_ub_c = rq_dlg_param.rq_c.swath_width_ub; + dpte_bytes_per_row_ub_c = rq_dlg_param.rq_c.dpte_bytes_per_row_ub; + dpte_groups_per_row_ub_c = rq_dlg_param.rq_c.dpte_groups_per_row_ub; + + meta_chunks_per_row_ub_l = rq_dlg_param.rq_l.meta_chunks_per_row_ub; + vupdate_offset = e2e_pipe_param.pipe.dest.vupdate_offset; + vupdate_width = e2e_pipe_param.pipe.dest.vupdate_width; + vready_offset = e2e_pipe_param.pipe.dest.vready_offset; + + dppclk_delay_subtotal = mode_lib->ip.dppclk_delay_subtotal; + dispclk_delay_subtotal = mode_lib->ip.dispclk_delay_subtotal; + pixel_rate_delay_subtotal = dppclk_delay_subtotal * pclk_freq_in_mhz / dppclk_freq_in_mhz + + dispclk_delay_subtotal * pclk_freq_in_mhz / dispclk_freq_in_mhz; + + vstartup_start = e2e_pipe_param.pipe.dest.vstartup_start; + + if (interlaced) + vstartup_start = vstartup_start / 2; + + if (vstartup_start >= min_vblank) { + DTRACE( + "WARNING_DLG: %s: vblank_start=%d vblank_end=%d", + __func__, + vblank_start, + vblank_end); + DTRACE( + "WARNING_DLG: %s: vstartup_start=%d should be less than min_vblank=%d", + __func__, + vstartup_start, + min_vblank); + min_vblank = vstartup_start + 1; + DTRACE( + "WARNING_DLG: %s: vstartup_start=%d should be less than min_vblank=%d", + __func__, + vstartup_start, + min_vblank); + } + + dst_x_after_scaler = 0; + dst_y_after_scaler = 0; + + if (e2e_pipe_param.pipe.src.is_hsplit) + dst_x_after_scaler = pixel_rate_delay_subtotal + + e2e_pipe_param.pipe.dest.recout_width; + else + dst_x_after_scaler = pixel_rate_delay_subtotal; + + if (e2e_pipe_param.dout.output_format == dm_420) + dst_y_after_scaler = 1; + else + dst_y_after_scaler = 0; + + if (dst_x_after_scaler >= htotal) { + dst_x_after_scaler = dst_x_after_scaler - htotal; + dst_y_after_scaler = dst_y_after_scaler + 1; + } + + DTRACE("DLG: %s: htotal = %d", __func__, htotal); + DTRACE( + "DLG: %s: pixel_rate_delay_subtotal = %d", + __func__, + pixel_rate_delay_subtotal); + DTRACE("DLG: %s: dst_x_after_scaler = %d", __func__, dst_x_after_scaler); + DTRACE("DLG: %s: dst_y_after_scaler = %d", __func__, dst_y_after_scaler); + + line_wait = mode_lib->soc.urgent_latency_us; + if (cstate_en) + line_wait = dml_max(mode_lib->soc.sr_enter_plus_exit_time_us, line_wait); + if (pstate_en) + line_wait = dml_max( + mode_lib->soc.dram_clock_change_latency_us + + mode_lib->soc.urgent_latency_us, + line_wait); + line_wait = line_wait / line_time_in_us; + + line_o = (double) dst_y_after_scaler + dst_x_after_scaler / (double) htotal; + line_setup = (double) (vupdate_offset + vupdate_width + vready_offset) / (double) htotal; + line_calc = t_calc_us / line_time_in_us; + + DTRACE( + "DLG: %s: soc.sr_enter_plus_exit_time_us = %3.2f", + __func__, + (double) mode_lib->soc.sr_enter_plus_exit_time_us); + DTRACE( + "DLG: %s: soc.dram_clock_change_latency_us = %3.2f", + __func__, + (double) mode_lib->soc.dram_clock_change_latency_us); + DTRACE( + "DLG: %s: soc.urgent_latency_us = %3.2f", + __func__, + mode_lib->soc.urgent_latency_us); + + DTRACE("DLG: %s: swath_height_l = %d", __func__, swath_height_l); + if (dual_plane) + DTRACE("DLG: %s: swath_height_c = %d", __func__, swath_height_c); + + DTRACE( + "DLG: %s: t_srx_delay_us = %3.2f", + __func__, + (double) dlg_sys_param.t_srx_delay_us); + DTRACE("DLG: %s: line_time_in_us = %3.2f", __func__, (double) line_time_in_us); + DTRACE("DLG: %s: vupdate_offset = %d", __func__, vupdate_offset); + DTRACE("DLG: %s: vupdate_width = %d", __func__, vupdate_width); + DTRACE("DLG: %s: vready_offset = %d", __func__, vready_offset); + DTRACE("DLG: %s: line_time_in_us = %3.2f", __func__, line_time_in_us); + DTRACE("DLG: %s: line_wait = %3.2f", __func__, line_wait); + DTRACE("DLG: %s: line_o = %3.2f", __func__, line_o); + DTRACE("DLG: %s: line_setup = %3.2f", __func__, line_setup); + DTRACE("DLG: %s: line_calc = %3.2f", __func__, line_calc); + + dst_y_prefetch = ((double) min_vblank - 1.0) + - (line_setup + line_calc + line_wait + line_o); + DTRACE("DLG: %s: dst_y_prefetch (before rnd) = %3.2f", __func__, dst_y_prefetch); + ASSERT(dst_y_prefetch >= 2.0); + + dst_y_prefetch = dml_floor(4.0 * (dst_y_prefetch + 0.125), 1) / 4; + DTRACE("DLG: %s: dst_y_prefetch (after rnd) = %3.2f", __func__, dst_y_prefetch); + + t_pre_us = dst_y_prefetch * line_time_in_us; + vm_bytes = 0; + meta_row_bytes = 0; + + if (dcc_en && vm_en) + vm_bytes = meta_pte_bytes_per_frame_ub_l; + if (dcc_en) + meta_row_bytes = meta_bytes_per_row_ub_l; + + max_num_sw_l = 0; + max_num_sw_c = 0; + max_partial_sw_l = 0; + max_partial_sw_c = 0; + + max_vinit_l = interlaced ? dml_max(vinit_l, vinit_bot_l) : vinit_l; + max_vinit_c = interlaced ? dml_max(vinit_c, vinit_bot_c) : vinit_c; + + get_swath_need(mode_lib, &max_num_sw_l, &max_partial_sw_l, swath_height_l, max_vinit_l); + if (dual_plane) + get_swath_need( + mode_lib, + &max_num_sw_c, + &max_partial_sw_c, + swath_height_c, + max_vinit_c); + + lsw_l = max_num_sw_l * swath_height_l + max_partial_sw_l; + lsw_c = max_num_sw_c * swath_height_c + max_partial_sw_c; + sw_bytes_ub_l = lsw_l * swath_width_ub_l * bytes_per_element_l; + sw_bytes_ub_c = lsw_c * swath_width_ub_c * bytes_per_element_c; + sw_bytes = 0; + dpte_row_bytes = 0; + + if (vm_en) { + if (dual_plane) + dpte_row_bytes = dpte_bytes_per_row_ub_l + dpte_bytes_per_row_ub_c; + else + dpte_row_bytes = dpte_bytes_per_row_ub_l; + } else { + dpte_row_bytes = 0; + } + + if (dual_plane) + sw_bytes = sw_bytes_ub_l + sw_bytes_ub_c; + else + sw_bytes = sw_bytes_ub_l; + + DTRACE("DLG: %s: sw_bytes_ub_l = %d", __func__, sw_bytes_ub_l); + DTRACE("DLG: %s: sw_bytes_ub_c = %d", __func__, sw_bytes_ub_c); + DTRACE("DLG: %s: sw_bytes = %d", __func__, sw_bytes); + DTRACE("DLG: %s: vm_bytes = %d", __func__, vm_bytes); + DTRACE("DLG: %s: meta_row_bytes = %d", __func__, meta_row_bytes); + DTRACE("DLG: %s: dpte_row_bytes = %d", __func__, dpte_row_bytes); + + prefetch_bw = (vm_bytes + 2 * dpte_row_bytes + 2 * meta_row_bytes + sw_bytes) / t_pre_us; + flip_bw = ((vm_bytes + dpte_row_bytes + meta_row_bytes) * dlg_sys_param.total_flip_bw) + / (double) dlg_sys_param.total_flip_bytes; + t_vm_us = line_time_in_us / 4.0; + if (vm_en && dcc_en) { + t_vm_us = dml_max( + dlg_sys_param.t_extra_us, + dml_max((double) vm_bytes / prefetch_bw, t_vm_us)); + + if (iflip_en && !dual_plane) { + t_vm_us = dml_max(mode_lib->soc.urgent_latency_us, t_vm_us); + if (flip_bw > 0.) + t_vm_us = dml_max(vm_bytes / flip_bw, t_vm_us); + } + } + + t_r0_us = dml_max(dlg_sys_param.t_extra_us - t_vm_us, line_time_in_us - t_vm_us); + + if (vm_en || dcc_en) { + t_r0_us = dml_max( + (double) (dpte_row_bytes + meta_row_bytes) / prefetch_bw, + dlg_sys_param.t_extra_us); + t_r0_us = dml_max((double) (line_time_in_us - t_vm_us), t_r0_us); + + if (iflip_en && !dual_plane) { + t_r0_us = dml_max(mode_lib->soc.urgent_latency_us * 2.0, t_r0_us); + if (flip_bw > 0.) + t_r0_us = dml_max( + (dpte_row_bytes + meta_row_bytes) / flip_bw, + t_r0_us); + } + } + + disp_dlg_regs->dst_y_after_scaler = dst_y_after_scaler; /* in terms of line */ + disp_dlg_regs->refcyc_x_after_scaler = dst_x_after_scaler * ref_freq_to_pix_freq; /* in terms of refclk */ + ASSERT(disp_dlg_regs->refcyc_x_after_scaler < (unsigned int) dml_pow(2, 13)); + DTRACE( + "DLG: %s: disp_dlg_regs->dst_y_after_scaler = 0x%0x", + __func__, + disp_dlg_regs->dst_y_after_scaler); + DTRACE( + "DLG: %s: disp_dlg_regs->refcyc_x_after_scaler = 0x%0x", + __func__, + disp_dlg_regs->refcyc_x_after_scaler); + + disp_dlg_regs->dst_y_prefetch = (unsigned int) (dst_y_prefetch * dml_pow(2, 2)); + DTRACE( + "DLG: %s: disp_dlg_regs->dst_y_prefetch = %d", + __func__, + disp_dlg_regs->dst_y_prefetch); + + dst_y_per_vm_vblank = 0.0; + dst_y_per_row_vblank = 0.0; + + dst_y_per_vm_vblank = t_vm_us / line_time_in_us; + dst_y_per_vm_vblank = dml_floor(4.0 * (dst_y_per_vm_vblank + 0.125), 1) / 4.0; + disp_dlg_regs->dst_y_per_vm_vblank = (unsigned int) (dst_y_per_vm_vblank * dml_pow(2, 2)); + + dst_y_per_row_vblank = t_r0_us / line_time_in_us; + dst_y_per_row_vblank = dml_floor(4.0 * (dst_y_per_row_vblank + 0.125), 1) / 4.0; + disp_dlg_regs->dst_y_per_row_vblank = (unsigned int) (dst_y_per_row_vblank * dml_pow(2, 2)); + + DTRACE("DLG: %s: lsw_l = %d", __func__, lsw_l); + DTRACE("DLG: %s: lsw_c = %d", __func__, lsw_c); + DTRACE("DLG: %s: dpte_bytes_per_row_ub_l = %d", __func__, dpte_bytes_per_row_ub_l); + DTRACE("DLG: %s: dpte_bytes_per_row_ub_c = %d", __func__, dpte_bytes_per_row_ub_c); + + DTRACE("DLG: %s: prefetch_bw = %3.2f", __func__, prefetch_bw); + DTRACE("DLG: %s: flip_bw = %3.2f", __func__, flip_bw); + DTRACE("DLG: %s: t_pre_us = %3.2f", __func__, t_pre_us); + DTRACE("DLG: %s: t_vm_us = %3.2f", __func__, t_vm_us); + DTRACE("DLG: %s: t_r0_us = %3.2f", __func__, t_r0_us); + DTRACE("DLG: %s: dst_y_per_vm_vblank = %3.2f", __func__, dst_y_per_vm_vblank); + DTRACE("DLG: %s: dst_y_per_row_vblank = %3.2f", __func__, dst_y_per_row_vblank); + DTRACE("DLG: %s: dst_y_prefetch = %3.2f", __func__, dst_y_prefetch); + + min_dst_y_per_vm_vblank = 8.0; + min_dst_y_per_row_vblank = 16.0; + if (htotal <= 75) { + min_vblank = 300; + min_dst_y_per_vm_vblank = 100.0; + min_dst_y_per_row_vblank = 100.0; + } + + ASSERT(dst_y_per_vm_vblank < min_dst_y_per_vm_vblank); + ASSERT(dst_y_per_row_vblank < min_dst_y_per_row_vblank); + + ASSERT(dst_y_prefetch > (dst_y_per_vm_vblank + dst_y_per_row_vblank)); + lsw = dst_y_prefetch - (dst_y_per_vm_vblank + dst_y_per_row_vblank); + + DTRACE("DLG: %s: lsw = %3.2f", __func__, lsw); + + vratio_pre_l = get_vratio_pre( + mode_lib, + max_num_sw_l, + max_partial_sw_l, + swath_height_l, + max_vinit_l, + lsw); + vratio_pre_c = 1.0; + if (dual_plane) + vratio_pre_c = get_vratio_pre( + mode_lib, + max_num_sw_c, + max_partial_sw_c, + swath_height_c, + max_vinit_c, + lsw); + + DTRACE("DLG: %s: vratio_pre_l=%3.2f", __func__, vratio_pre_l); + DTRACE("DLG: %s: vratio_pre_c=%3.2f", __func__, vratio_pre_c); + + ASSERT(vratio_pre_l <= 4.0); + if (vratio_pre_l >= 4.0) + disp_dlg_regs->vratio_prefetch = (unsigned int) dml_pow(2, 21) - 1; + else + disp_dlg_regs->vratio_prefetch = (unsigned int) (vratio_pre_l * dml_pow(2, 19)); + + ASSERT(vratio_pre_c <= 4.0); + if (vratio_pre_c >= 4.0) + disp_dlg_regs->vratio_prefetch_c = (unsigned int) dml_pow(2, 21) - 1; + else + disp_dlg_regs->vratio_prefetch_c = (unsigned int) (vratio_pre_c * dml_pow(2, 19)); + + disp_dlg_regs->refcyc_per_pte_group_vblank_l = + (unsigned int) (dst_y_per_row_vblank * (double) htotal + * ref_freq_to_pix_freq / (double) dpte_groups_per_row_ub_l); + ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_l < (unsigned int) dml_pow(2, 13)); + + disp_dlg_regs->refcyc_per_pte_group_vblank_c = + (unsigned int) (dst_y_per_row_vblank * (double) htotal + * ref_freq_to_pix_freq / (double) dpte_groups_per_row_ub_c); + ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_c < (unsigned int) dml_pow(2, 13)); + + disp_dlg_regs->refcyc_per_meta_chunk_vblank_l = + (unsigned int) (dst_y_per_row_vblank * (double) htotal + * ref_freq_to_pix_freq / (double) meta_chunks_per_row_ub_l); + ASSERT(disp_dlg_regs->refcyc_per_meta_chunk_vblank_l < (unsigned int) dml_pow(2, 13)); + + disp_dlg_regs->refcyc_per_meta_chunk_vblank_c = + disp_dlg_regs->refcyc_per_meta_chunk_vblank_l;/* dcc for 4:2:0 is not supported in dcn1.0. assigned to be the same as _l for now */ + + /* Active */ + req_per_swath_ub_l = rq_dlg_param.rq_l.req_per_swath_ub; + req_per_swath_ub_c = rq_dlg_param.rq_c.req_per_swath_ub; + meta_row_height_l = rq_dlg_param.rq_l.meta_row_height; + swath_width_pixels_ub_l = 0; + swath_width_pixels_ub_c = 0; + scaler_rec_in_width_l = 0; + scaler_rec_in_width_c = 0; + dpte_row_height_l = rq_dlg_param.rq_l.dpte_row_height; + dpte_row_height_c = rq_dlg_param.rq_c.dpte_row_height; + + disp_dlg_regs->dst_y_per_pte_row_nom_l = (unsigned int) ((double) dpte_row_height_l + / (double) vratio_l * dml_pow(2, 2)); + ASSERT(disp_dlg_regs->dst_y_per_pte_row_nom_l < (unsigned int) dml_pow(2, 17)); + + disp_dlg_regs->dst_y_per_pte_row_nom_c = (unsigned int) ((double) dpte_row_height_c + / (double) vratio_c * dml_pow(2, 2)); + ASSERT(disp_dlg_regs->dst_y_per_pte_row_nom_c < (unsigned int) dml_pow(2, 17)); + + disp_dlg_regs->dst_y_per_meta_row_nom_l = (unsigned int) ((double) meta_row_height_l + / (double) vratio_l * dml_pow(2, 2)); + ASSERT(disp_dlg_regs->dst_y_per_meta_row_nom_l < (unsigned int) dml_pow(2, 17)); + + disp_dlg_regs->dst_y_per_meta_row_nom_c = disp_dlg_regs->dst_y_per_meta_row_nom_l; /* dcc for 4:2:0 is not supported in dcn1.0. assigned to be the same as _l for now */ + + disp_dlg_regs->refcyc_per_pte_group_nom_l = (unsigned int) ((double) dpte_row_height_l + / (double) vratio_l * (double) htotal * ref_freq_to_pix_freq + / (double) dpte_groups_per_row_ub_l); + if (disp_dlg_regs->refcyc_per_pte_group_nom_l >= (unsigned int) dml_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_l = dml_pow(2, 23) - 1; + + disp_dlg_regs->refcyc_per_pte_group_nom_c = (unsigned int) ((double) dpte_row_height_c + / (double) vratio_c * (double) htotal * ref_freq_to_pix_freq + / (double) dpte_groups_per_row_ub_c); + if (disp_dlg_regs->refcyc_per_pte_group_nom_c >= (unsigned int) dml_pow(2, 23)) + disp_dlg_regs->refcyc_per_pte_group_nom_c = dml_pow(2, 23) - 1; + + disp_dlg_regs->refcyc_per_meta_chunk_nom_l = (unsigned int) ((double) meta_row_height_l + / (double) vratio_l * (double) htotal * ref_freq_to_pix_freq + / (double) meta_chunks_per_row_ub_l); + if (disp_dlg_regs->refcyc_per_meta_chunk_nom_l >= (unsigned int) dml_pow(2, 23)) + disp_dlg_regs->refcyc_per_meta_chunk_nom_l = dml_pow(2, 23) - 1; + + if (mode_422) { + swath_width_pixels_ub_l = swath_width_ub_l * 2; /* *2 for 2 pixel per element */ + swath_width_pixels_ub_c = swath_width_ub_c * 2; + } else { + swath_width_pixels_ub_l = swath_width_ub_l * 1; + swath_width_pixels_ub_c = swath_width_ub_c * 1; + } + + hscale_pixel_rate_l = 0.; + hscale_pixel_rate_c = 0.; + min_hratio_fact_l = 1.0; + min_hratio_fact_c = 1.0; + + if (htaps_l <= 1) + min_hratio_fact_l = 2.0; + else if (htaps_l <= 6) { + if ((hratios_l * 2.0) > 4.0) + min_hratio_fact_l = 4.0; + else + min_hratio_fact_l = hratios_l * 2.0; + } else { + if (hratios_l > 4.0) + min_hratio_fact_l = 4.0; + else + min_hratio_fact_l = hratios_l; + } + + hscale_pixel_rate_l = min_hratio_fact_l * dppclk_freq_in_mhz; + + if (htaps_c <= 1) + min_hratio_fact_c = 2.0; + else if (htaps_c <= 6) { + if ((hratios_c * 2.0) > 4.0) + min_hratio_fact_c = 4.0; + else + min_hratio_fact_c = hratios_c * 2.0; + } else { + if (hratios_c > 4.0) + min_hratio_fact_c = 4.0; + else + min_hratio_fact_c = hratios_c; + } + + hscale_pixel_rate_c = min_hratio_fact_c * dppclk_freq_in_mhz; + + refcyc_per_line_delivery_pre_l = 0.; + refcyc_per_line_delivery_pre_c = 0.; + refcyc_per_line_delivery_l = 0.; + refcyc_per_line_delivery_c = 0.; + + refcyc_per_req_delivery_pre_l = 0.; + refcyc_per_req_delivery_pre_c = 0.; + refcyc_per_req_delivery_l = 0.; + refcyc_per_req_delivery_c = 0.; + refcyc_per_req_delivery_pre_cur0 = 0.; + refcyc_per_req_delivery_cur0 = 0.; + + full_recout_width = 0; + if (e2e_pipe_param.pipe.src.is_hsplit) { + if (e2e_pipe_param.pipe.dest.full_recout_width == 0) { + DTRACE("DLG: %s: Warningfull_recout_width not set in hsplit mode", __func__); + full_recout_width = e2e_pipe_param.pipe.dest.recout_width * 2; /* assume half split for dcn1 */ + } else + full_recout_width = e2e_pipe_param.pipe.dest.full_recout_width; + } else + full_recout_width = e2e_pipe_param.pipe.dest.recout_width; + + refcyc_per_line_delivery_pre_l = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_pre_l, + hscale_pixel_rate_l, + swath_width_pixels_ub_l, + 1); /* per line */ + + refcyc_per_line_delivery_l = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_l, + hscale_pixel_rate_l, + swath_width_pixels_ub_l, + 1); /* per line */ + + DTRACE("DLG: %s: full_recout_width = %d", __func__, full_recout_width); + DTRACE("DLG: %s: hscale_pixel_rate_l = %3.2f", __func__, hscale_pixel_rate_l); + DTRACE( + "DLG: %s: refcyc_per_line_delivery_pre_l = %3.2f", + __func__, + refcyc_per_line_delivery_pre_l); + DTRACE( + "DLG: %s: refcyc_per_line_delivery_l = %3.2f", + __func__, + refcyc_per_line_delivery_l); + + disp_dlg_regs->refcyc_per_line_delivery_pre_l = (unsigned int) dml_floor( + refcyc_per_line_delivery_pre_l, + 1); + disp_dlg_regs->refcyc_per_line_delivery_l = (unsigned int) dml_floor( + refcyc_per_line_delivery_l, + 1); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_l < (unsigned int) dml_pow(2, 13)); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_l < (unsigned int) dml_pow(2, 13)); + + if (dual_plane) { + refcyc_per_line_delivery_pre_c = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_pre_c, + hscale_pixel_rate_c, + swath_width_pixels_ub_c, + 1); /* per line */ + + refcyc_per_line_delivery_c = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_c, + hscale_pixel_rate_c, + swath_width_pixels_ub_c, + 1); /* per line */ + + DTRACE( + "DLG: %s: refcyc_per_line_delivery_pre_c = %3.2f", + __func__, + refcyc_per_line_delivery_pre_c); + DTRACE( + "DLG: %s: refcyc_per_line_delivery_c = %3.2f", + __func__, + refcyc_per_line_delivery_c); + + disp_dlg_regs->refcyc_per_line_delivery_pre_c = (unsigned int) dml_floor( + refcyc_per_line_delivery_pre_c, + 1); + disp_dlg_regs->refcyc_per_line_delivery_c = (unsigned int) dml_floor( + refcyc_per_line_delivery_c, + 1); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_c < (unsigned int) dml_pow(2, 13)); + ASSERT(disp_dlg_regs->refcyc_per_line_delivery_c < (unsigned int) dml_pow(2, 13)); + } + disp_dlg_regs->chunk_hdl_adjust_cur0 = 3; + + /* TTU - Luma / Chroma */ + if (access_dir) { /* vertical access */ + scaler_rec_in_width_l = vp_height_l; + scaler_rec_in_width_c = vp_height_c; + } else { + scaler_rec_in_width_l = vp_width_l; + scaler_rec_in_width_c = vp_width_c; + } + + refcyc_per_req_delivery_pre_l = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_pre_l, + hscale_pixel_rate_l, + scaler_rec_in_width_l, + req_per_swath_ub_l); /* per req */ + refcyc_per_req_delivery_l = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_l, + hscale_pixel_rate_l, + scaler_rec_in_width_l, + req_per_swath_ub_l); /* per req */ + + DTRACE( + "DLG: %s: refcyc_per_req_delivery_pre_l = %3.2f", + __func__, + refcyc_per_req_delivery_pre_l); + DTRACE( + "DLG: %s: refcyc_per_req_delivery_l = %3.2f", + __func__, + refcyc_per_req_delivery_l); + + disp_ttu_regs->refcyc_per_req_delivery_pre_l = (unsigned int) (refcyc_per_req_delivery_pre_l + * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_l = (unsigned int) (refcyc_per_req_delivery_l + * dml_pow(2, 10)); + + ASSERT(refcyc_per_req_delivery_pre_l < dml_pow(2, 13)); + ASSERT(refcyc_per_req_delivery_l < dml_pow(2, 13)); + + if (dual_plane) { + refcyc_per_req_delivery_pre_c = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_pre_c, + hscale_pixel_rate_c, + scaler_rec_in_width_c, + req_per_swath_ub_c); /* per req */ + refcyc_per_req_delivery_c = get_refcyc_per_delivery( + mode_lib, + refclk_freq_in_mhz, + pclk_freq_in_mhz, + full_recout_width, + vratio_c, + hscale_pixel_rate_c, + scaler_rec_in_width_c, + req_per_swath_ub_c); /* per req */ + + DTRACE( + "DLG: %s: refcyc_per_req_delivery_pre_c = %3.2f", + __func__, + refcyc_per_req_delivery_pre_c); + DTRACE( + "DLG: %s: refcyc_per_req_delivery_c = %3.2f", + __func__, + refcyc_per_req_delivery_c); + + disp_ttu_regs->refcyc_per_req_delivery_pre_c = + (unsigned int) (refcyc_per_req_delivery_pre_c * dml_pow(2, 10)); + disp_ttu_regs->refcyc_per_req_delivery_c = (unsigned int) (refcyc_per_req_delivery_c + * dml_pow(2, 10)); + + ASSERT(refcyc_per_req_delivery_pre_c < dml_pow(2, 13)); + ASSERT(refcyc_per_req_delivery_c < dml_pow(2, 13)); + } + + /* TTU - Cursor */ + hratios_cur0 = e2e_pipe_param.pipe.scale_ratio_depth.hscl_ratio; + cur0_src_width = e2e_pipe_param.pipe.src.cur0_src_width; /* cursor source width */ + cur0_bpp = (enum cursor_bpp) e2e_pipe_param.pipe.src.cur0_bpp; + cur0_req_size = 0; + cur0_req_width = 0; + cur0_width_ub = 0.0; + cur0_req_per_width = 0.0; + hactive_cur0 = 0.0; + + ASSERT(cur0_src_width <= 256); + + if (cur0_src_width > 0) { + unsigned int cur0_bit_per_pixel = 0; + + if (cur0_bpp == dm_cur_2bit) { + cur0_req_size = 64; /* byte */ + cur0_bit_per_pixel = 2; + } else { /* 32bit */ + cur0_bit_per_pixel = 32; + if (cur0_src_width >= 1 && cur0_src_width <= 16) + cur0_req_size = 64; + else if (cur0_src_width >= 17 && cur0_src_width <= 31) + cur0_req_size = 128; + else + cur0_req_size = 256; + } + + cur0_req_width = (double) cur0_req_size / ((double) cur0_bit_per_pixel / 8.0); + cur0_width_ub = dml_ceil((double) cur0_src_width / (double) cur0_req_width, 1) + * (double) cur0_req_width; + cur0_req_per_width = cur0_width_ub / (double) cur0_req_width; + hactive_cur0 = (double) cur0_src_width / hratios_cur0; /* FIXME: oswin to think about what to do for cursor */ + + if (vratio_pre_l <= 1.0) { + refcyc_per_req_delivery_pre_cur0 = hactive_cur0 * ref_freq_to_pix_freq + / (double) cur0_req_per_width; + } else { + refcyc_per_req_delivery_pre_cur0 = (double) refclk_freq_in_mhz + * (double) cur0_src_width / hscale_pixel_rate_l + / (double) cur0_req_per_width; + } + + disp_ttu_regs->refcyc_per_req_delivery_pre_cur0 = + (unsigned int) (refcyc_per_req_delivery_pre_cur0 * dml_pow(2, 10)); + ASSERT(refcyc_per_req_delivery_pre_cur0 < dml_pow(2, 13)); + + if (vratio_l <= 1.0) { + refcyc_per_req_delivery_cur0 = hactive_cur0 * ref_freq_to_pix_freq + / (double) cur0_req_per_width; + } else { + refcyc_per_req_delivery_cur0 = (double) refclk_freq_in_mhz + * (double) cur0_src_width / hscale_pixel_rate_l + / (double) cur0_req_per_width; + } + + DTRACE("DLG: %s: cur0_req_width = %d", __func__, cur0_req_width); + DTRACE( + "DLG: %s: cur0_width_ub = %3.2f", + __func__, + cur0_width_ub); + DTRACE( + "DLG: %s: cur0_req_per_width = %3.2f", + __func__, + cur0_req_per_width); + DTRACE( + "DLG: %s: hactive_cur0 = %3.2f", + __func__, + hactive_cur0); + DTRACE( + "DLG: %s: refcyc_per_req_delivery_pre_cur0 = %3.2f", + __func__, + refcyc_per_req_delivery_pre_cur0); + DTRACE( + "DLG: %s: refcyc_per_req_delivery_cur0 = %3.2f", + __func__, + refcyc_per_req_delivery_cur0); + + disp_ttu_regs->refcyc_per_req_delivery_cur0 = + (unsigned int) (refcyc_per_req_delivery_cur0 * dml_pow(2, 10)); + ASSERT(refcyc_per_req_delivery_cur0 < dml_pow(2, 13)); + } else { + disp_ttu_regs->refcyc_per_req_delivery_pre_cur0 = 0; + disp_ttu_regs->refcyc_per_req_delivery_cur0 = 0; + } + + /* TTU - Misc */ + disp_ttu_regs->qos_level_low_wm = 0; + ASSERT(disp_ttu_regs->qos_level_low_wm < dml_pow(2, 14)); + disp_ttu_regs->qos_level_high_wm = (unsigned int) (4.0 * (double) htotal + * ref_freq_to_pix_freq); + ASSERT(disp_ttu_regs->qos_level_high_wm < dml_pow(2, 14)); + + disp_ttu_regs->qos_level_flip = 14; + disp_ttu_regs->qos_level_fixed_l = 8; + disp_ttu_regs->qos_level_fixed_c = 8; + disp_ttu_regs->qos_level_fixed_cur0 = 8; + disp_ttu_regs->qos_ramp_disable_l = 0; + disp_ttu_regs->qos_ramp_disable_c = 0; + disp_ttu_regs->qos_ramp_disable_cur0 = 0; + + disp_ttu_regs->min_ttu_vblank = min_ttu_vblank * refclk_freq_in_mhz; + ASSERT(disp_ttu_regs->min_ttu_vblank < dml_pow(2, 24)); + + print__ttu_regs_st(mode_lib, *disp_ttu_regs); + print__dlg_regs_st(mode_lib, *disp_dlg_regs); +} diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.h b/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.h new file mode 100644 index 000000000000..987d7671cd0f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.h @@ -0,0 +1,67 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DISPLAY_RQ_DLG_CALC_H__ +#define __DISPLAY_RQ_DLG_CALC_H__ + +#include "dml_common_defs.h" +#include "display_rq_dlg_helpers.h" + +struct display_mode_lib; + +void dml1_extract_rq_regs( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_rq_regs_st *rq_regs, + const struct _vcs_dpi_display_rq_params_st rq_param); +/* Function: dml_rq_dlg_get_rq_params + * Calculate requestor related parameters that register definition agnostic + * (i.e. this layer does try to separate real values from register definition) + * Input: + * pipe_src_param - pipe source configuration (e.g. vp, pitch, etc.) + * Output: + * rq_param - values that can be used to setup RQ (e.g. swath_height, plane1_addr, etc.) + */ +void dml1_rq_dlg_get_rq_params( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_rq_params_st *rq_param, + const struct _vcs_dpi_display_pipe_source_params_st pipe_src_param); + + +/* Function: dml_rq_dlg_get_dlg_params + * Calculate deadline related parameters + */ +void dml1_rq_dlg_get_dlg_params( + struct display_mode_lib *mode_lib, + struct _vcs_dpi_display_dlg_regs_st *dlg_regs, + struct _vcs_dpi_display_ttu_regs_st *ttu_regs, + const struct _vcs_dpi_display_rq_dlg_params_st rq_dlg_param, + const struct _vcs_dpi_display_dlg_sys_params_st dlg_sys_param, + const struct _vcs_dpi_display_e2e_pipe_params_st e2e_pipe_param, + const bool cstate_en, + const bool pstate_en, + const bool vm_en, + const bool iflip_en); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.c b/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.c new file mode 100644 index 000000000000..b953b02a1512 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.c @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dml_common_defs.h" +#include "../calcs/dcn_calc_math.h" + +#include "dml_inline_defs.h" + +double dml_round(double a) +{ + double round_pt = 0.5; + double ceil = dml_ceil(a, 1); + double floor = dml_floor(a, 1); + + if (a - floor >= round_pt) + return ceil; + else + return floor; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.h b/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.h new file mode 100644 index 000000000000..b2847bc469fe --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_COMMON_DEFS_H__ +#define __DC_COMMON_DEFS_H__ + +#include "dm_services.h" +#include "dc_features.h" +#include "display_mode_structs.h" +#include "display_mode_enums.h" + +#define dml_print(str, ...) {dm_logger_write(mode_lib->logger, LOG_DML, str, ##__VA_ARGS__); } +#define DTRACE(str, ...) {dm_logger_write(mode_lib->logger, LOG_DML, str, ##__VA_ARGS__); } + +double dml_round(double a); + +#endif /* __DC_COMMON_DEFS_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h b/drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h new file mode 100644 index 000000000000..e68086b8a22f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h @@ -0,0 +1,121 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DML_INLINE_DEFS_H__ +#define __DML_INLINE_DEFS_H__ + +#include "dml_common_defs.h" +#include "../calcs/dcn_calc_math.h" + +static inline double dml_min(double a, double b) +{ + return (double) dcn_bw_min2(a, b); +} + +static inline double dml_max(double a, double b) +{ + return (double) dcn_bw_max2(a, b); +} + +static inline double dml_max3(double a, double b, double c) +{ + return dml_max(dml_max(a, b), c); +} + +static inline double dml_max4(double a, double b, double c, double d) +{ + return dml_max(dml_max(a, b), dml_max(c, d)); +} + +static inline double dml_max5(double a, double b, double c, double d, double e) +{ + return dml_max(dml_max4(a, b, c, d), e); +} + +static inline double dml_ceil(double a, double granularity) +{ + return (double) dcn_bw_ceil2(a, granularity); +} + +static inline double dml_floor(double a, double granularity) +{ + return (double) dcn_bw_floor2(a, granularity); +} + +static inline int dml_log2(double x) +{ + return dml_round((double)dcn_bw_log(x, 2)); +} + +static inline double dml_pow(double a, int exp) +{ + return (double) dcn_bw_pow(a, exp); +} + +static inline double dml_fmod(double f, int val) +{ + return (double) dcn_bw_mod(f, val); +} + +static inline double dml_ceil_2(double f) +{ + return (double) dcn_bw_ceil2(f, 2); +} + +static inline double dml_ceil_ex(double x, double granularity) +{ + return (double) dcn_bw_ceil2(x, granularity); +} + +static inline double dml_floor_ex(double x, double granularity) +{ + return (double) dcn_bw_floor2(x, granularity); +} + +static inline double dml_log(double x, double base) +{ + return (double) dcn_bw_log(x, base); +} + +static inline unsigned int dml_round_to_multiple(unsigned int num, + unsigned int multiple, + bool up) +{ + unsigned int remainder; + + if (multiple == 0) + return num; + + remainder = num % multiple; + + if (remainder == 0) + return num; + + if (up) + return (num + multiple - remainder); + else + return (num - remainder); +} +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/soc_bounding_box.c b/drivers/gpu/drm/amd/display/dc/dml/soc_bounding_box.c new file mode 100644 index 000000000000..bc7d8c707221 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/soc_bounding_box.c @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "soc_bounding_box.h" +#include "display_mode_lib.h" +#include "dc_features.h" + +#include "dml_inline_defs.h" +void dml_socbb_set_latencies(soc_bounding_box_st *to_box, soc_bounding_box_st *from_box) +{ + to_box->dram_clock_change_latency_us = from_box->dram_clock_change_latency_us; + to_box->sr_exit_time_us = from_box->sr_exit_time_us; + to_box->sr_enter_plus_exit_time_us = from_box->sr_enter_plus_exit_time_us; + to_box->urgent_latency_us = from_box->urgent_latency_us; + to_box->writeback_latency_us = from_box->writeback_latency_us; +} + +voltage_scaling_st dml_socbb_voltage_scaling( + const soc_bounding_box_st *soc, + enum voltage_state voltage) +{ + const voltage_scaling_st *voltage_state; + const voltage_scaling_st * const voltage_end = soc->clock_limits + DC__VOLTAGE_STATES; + + for (voltage_state = soc->clock_limits; + voltage_state < voltage_end && voltage_state->state != voltage; + voltage_state++) { + } + + if (voltage_state < voltage_end) + return *voltage_state; + return soc->clock_limits[DC__VOLTAGE_STATES - 1]; +} + +double dml_socbb_return_bw_mhz(soc_bounding_box_st *box, enum voltage_state voltage) +{ + double return_bw; + + voltage_scaling_st state = dml_socbb_voltage_scaling(box, voltage); + + return_bw = dml_min((double) box->return_bus_width_bytes * state.dcfclk_mhz, + state.dram_bw_per_chan_gbps * 1000.0 * (double) box->num_chans + * box->ideal_dram_bw_after_urgent_percent / 100.0); + + return_bw = dml_min((double) box->return_bus_width_bytes * state.fabricclk_mhz, return_bw); + + return return_bw; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml/soc_bounding_box.h b/drivers/gpu/drm/amd/display/dc/dml/soc_bounding_box.h new file mode 100644 index 000000000000..7a65206a6d21 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/soc_bounding_box.h @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __SOC_BOUNDING_BOX_H__ +#define __SOC_BOUNDING_BOX_H__ + +#include "dml_common_defs.h" + +void dml_socbb_set_latencies(soc_bounding_box_st *to_box, soc_bounding_box_st *from_box); +voltage_scaling_st dml_socbb_voltage_scaling(const soc_bounding_box_st *box, enum voltage_state voltage); +double dml_socbb_return_bw_mhz(soc_bounding_box_st *box, enum voltage_state voltage); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/Makefile b/drivers/gpu/drm/amd/display/dc/gpio/Makefile new file mode 100644 index 000000000000..70d01a9e9676 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/Makefile @@ -0,0 +1,58 @@ +# +# Makefile for the 'gpio' sub-component of DAL. +# It provides the control and status of HW GPIO pins. + +GPIO = gpio_base.o gpio_service.o hw_factory.o \ + hw_gpio.o hw_hpd.o hw_ddc.o hw_translate.o + +AMD_DAL_GPIO = $(addprefix $(AMDDALPATH)/dc/gpio/,$(GPIO)) + +AMD_DISPLAY_FILES += $(AMD_DAL_GPIO) + +############################################################################### +# DCE 8x +############################################################################### +# all DCE8.x are derived from DCE8.0 +GPIO_DCE80 = hw_translate_dce80.o hw_factory_dce80.o + +AMD_DAL_GPIO_DCE80 = $(addprefix $(AMDDALPATH)/dc/gpio/dce80/,$(GPIO_DCE80)) + +AMD_DISPLAY_FILES += $(AMD_DAL_GPIO_DCE80) + +############################################################################### +# DCE 11x +############################################################################### +GPIO_DCE110 = hw_translate_dce110.o hw_factory_dce110.o + +AMD_DAL_GPIO_DCE110 = $(addprefix $(AMDDALPATH)/dc/gpio/dce110/,$(GPIO_DCE110)) + +AMD_DISPLAY_FILES += $(AMD_DAL_GPIO_DCE110) + +############################################################################### +# DCE 12x +############################################################################### +GPIO_DCE120 = hw_translate_dce120.o hw_factory_dce120.o + +AMD_DAL_GPIO_DCE120 = $(addprefix $(AMDDALPATH)/dc/gpio/dce120/,$(GPIO_DCE120)) + +AMD_DISPLAY_FILES += $(AMD_DAL_GPIO_DCE120) + +############################################################################### +# DCN 1x +############################################################################### +ifdef CONFIG_DRM_AMD_DC_DCN1_0 +GPIO_DCN10 = hw_translate_dcn10.o hw_factory_dcn10.o + +AMD_DAL_GPIO_DCN10 = $(addprefix $(AMDDALPATH)/dc/gpio/dcn10/,$(GPIO_DCN10)) + +AMD_DISPLAY_FILES += $(AMD_DAL_GPIO_DCN10) +endif + +############################################################################### +# Diagnostics on FPGA +############################################################################### +GPIO_DIAG_FPGA = hw_translate_diag.o hw_factory_diag.o + +AMD_DAL_GPIO_DIAG_FPGA = $(addprefix $(AMDDALPATH)/dc/gpio/diagnostics/,$(GPIO_DIAG_FPGA)) + +AMD_DISPLAY_FILES += $(AMD_DAL_GPIO_DIAG_FPGA) diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_factory_dce110.c b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_factory_dce110.c new file mode 100644 index 000000000000..20d81bca119c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_factory_dce110.c @@ -0,0 +1,178 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_factory.h" + +#include "hw_factory_dce110.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +/* set field name */ +#define SF_HPD(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define REG(reg_name)\ + mm ## reg_name + +#define REGI(reg_name, block, id)\ + mm ## block ## id ## _ ## reg_name + +#include "../hw_gpio.h" +#include "../hw_ddc.h" +#include "../hw_hpd.h" + +#include "reg_helper.h" +#include "../hpd_regs.h" + +#define hpd_regs(id) \ +{\ + HPD_REG_LIST(id)\ +} + +static const struct hpd_registers hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +static const struct hpd_sh_mask hpd_shift = { + HPD_MASK_SH_LIST(__SHIFT) +}; + +static const struct hpd_sh_mask hpd_mask = { + HPD_MASK_SH_LIST(_MASK) +}; + +#include "../ddc_regs.h" + + /* set field name */ +#define SF_DDC(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +static const struct ddc_registers ddc_data_regs[] = { + ddc_data_regs(1), + ddc_data_regs(2), + ddc_data_regs(3), + ddc_data_regs(4), + ddc_data_regs(5), + ddc_data_regs(6), + ddc_vga_data_regs, + ddc_i2c_data_regs +}; + +static const struct ddc_registers ddc_clk_regs[] = { + ddc_clk_regs(1), + ddc_clk_regs(2), + ddc_clk_regs(3), + ddc_clk_regs(4), + ddc_clk_regs(5), + ddc_clk_regs(6), + ddc_vga_clk_regs, + ddc_i2c_clk_regs +}; + +static const struct ddc_sh_mask ddc_shift = { + DDC_MASK_SH_LIST(__SHIFT) +}; + +static const struct ddc_sh_mask ddc_mask = { + DDC_MASK_SH_LIST(_MASK) +}; + +static void define_ddc_registers( + struct hw_gpio_pin *pin, + uint32_t en) +{ + struct hw_ddc *ddc = HW_DDC_FROM_BASE(pin); + + switch (pin->id) { + case GPIO_ID_DDC_DATA: + ddc->regs = &ddc_data_regs[en]; + ddc->base.regs = &ddc_data_regs[en].gpio; + break; + case GPIO_ID_DDC_CLOCK: + ddc->regs = &ddc_clk_regs[en]; + ddc->base.regs = &ddc_clk_regs[en].gpio; + break; + default: + ASSERT_CRITICAL(false); + return; + } + + ddc->shifts = &ddc_shift; + ddc->masks = &ddc_mask; + +} + +static void define_hpd_registers(struct hw_gpio_pin *pin, uint32_t en) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(pin); + + hpd->regs = &hpd_regs[en]; + hpd->shifts = &hpd_shift; + hpd->masks = &hpd_mask; + hpd->base.regs = &hpd_regs[en].gpio; +} + +static const struct hw_factory_funcs funcs = { + .create_ddc_data = dal_hw_ddc_create, + .create_ddc_clock = dal_hw_ddc_create, + .create_generic = NULL, + .create_hpd = dal_hw_hpd_create, + .create_sync = NULL, + .create_gsl = NULL, + .define_hpd_registers = define_hpd_registers, + .define_ddc_registers = define_ddc_registers +}; + +/* + * dal_hw_factory_dce110_init + * + * @brief + * Initialize HW factory function pointers and pin info + * + * @param + * struct hw_factory *factory - [out] struct of function pointers + */ +void dal_hw_factory_dce110_init(struct hw_factory *factory) +{ + /*TODO check ASIC CAPs*/ + factory->number_of_pins[GPIO_ID_DDC_DATA] = 8; + factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 8; + factory->number_of_pins[GPIO_ID_GENERIC] = 7; + factory->number_of_pins[GPIO_ID_HPD] = 6; + factory->number_of_pins[GPIO_ID_GPIO_PAD] = 31; + factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; + factory->number_of_pins[GPIO_ID_SYNC] = 2; + factory->number_of_pins[GPIO_ID_GSL] = 4; + + factory->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_factory_dce110.h b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_factory_dce110.h new file mode 100644 index 000000000000..ecf06ed0d587 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_factory_dce110.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_FACTORY_DCE110_H__ +#define __DAL_HW_FACTORY_DCE110_H__ + +/* Initialize HW factory function pointers and pin info */ +void dal_hw_factory_dce110_init(struct hw_factory *factory); + +#endif /* __DAL_HW_FACTORY_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_translate_dce110.c b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_translate_dce110.c new file mode 100644 index 000000000000..ac4cddbba815 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_translate_dce110.c @@ -0,0 +1,387 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_translate.h" + +#include "hw_translate_dce110.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +static bool offset_to_id( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en) +{ + switch (offset) { + /* GENERIC */ + case mmDC_GPIO_GENERIC_A: + *id = GPIO_ID_GENERIC; + switch (mask) { + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: + *en = GPIO_GENERIC_A; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: + *en = GPIO_GENERIC_B; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: + *en = GPIO_GENERIC_C; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: + *en = GPIO_GENERIC_D; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: + *en = GPIO_GENERIC_E; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: + *en = GPIO_GENERIC_F; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: + *en = GPIO_GENERIC_G; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* HPD */ + case mmDC_GPIO_HPD_A: + *id = GPIO_ID_HPD; + switch (mask) { + case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: + *en = GPIO_HPD_1; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: + *en = GPIO_HPD_2; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: + *en = GPIO_HPD_3; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: + *en = GPIO_HPD_4; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: + *en = GPIO_HPD_5; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: + *en = GPIO_HPD_6; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* SYNCA */ + case mmDC_GPIO_SYNCA_A: + *id = GPIO_ID_SYNC; + switch (mask) { + case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK: + *en = GPIO_SYNC_HSYNC_A; + return true; + case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK: + *en = GPIO_SYNC_VSYNC_A; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* mmDC_GPIO_GENLK_MASK */ + case mmDC_GPIO_GENLK_A: + *id = GPIO_ID_GSL; + switch (mask) { + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: + *en = GPIO_GSL_GENLOCK_CLOCK; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: + *en = GPIO_GSL_GENLOCK_VSYNC; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: + *en = GPIO_GSL_SWAPLOCK_A; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: + *en = GPIO_GSL_SWAPLOCK_B; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* DDC */ + /* we don't care about the GPIO_ID for DDC + * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK + * directly in the create method */ + case mmDC_GPIO_DDC1_A: + *en = GPIO_DDC_LINE_DDC1; + return true; + case mmDC_GPIO_DDC2_A: + *en = GPIO_DDC_LINE_DDC2; + return true; + case mmDC_GPIO_DDC3_A: + *en = GPIO_DDC_LINE_DDC3; + return true; + case mmDC_GPIO_DDC4_A: + *en = GPIO_DDC_LINE_DDC4; + return true; + case mmDC_GPIO_DDC5_A: + *en = GPIO_DDC_LINE_DDC5; + return true; + case mmDC_GPIO_DDC6_A: + *en = GPIO_DDC_LINE_DDC6; + return true; + case mmDC_GPIO_DDCVGA_A: + *en = GPIO_DDC_LINE_DDC_VGA; + return true; + /* GPIO_I2CPAD */ + case mmDC_GPIO_I2CPAD_A: + *en = GPIO_DDC_LINE_I2C_PAD; + return true; + /* Not implemented */ + case mmDC_GPIO_PWRSEQ_A: + case mmDC_GPIO_PAD_STRENGTH_1: + case mmDC_GPIO_PAD_STRENGTH_2: + case mmDC_GPIO_DEBUG: + return false; + /* UNEXPECTED */ + default: + ASSERT_CRITICAL(false); + return false; + } +} + +static bool id_to_offset( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info) +{ + bool result = true; + + switch (id) { + case GPIO_ID_DDC_DATA: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = mmDC_GPIO_DDC1_A; + break; + case GPIO_DDC_LINE_DDC2: + info->offset = mmDC_GPIO_DDC2_A; + break; + case GPIO_DDC_LINE_DDC3: + info->offset = mmDC_GPIO_DDC3_A; + break; + case GPIO_DDC_LINE_DDC4: + info->offset = mmDC_GPIO_DDC4_A; + break; + case GPIO_DDC_LINE_DDC5: + info->offset = mmDC_GPIO_DDC5_A; + break; + case GPIO_DDC_LINE_DDC6: + info->offset = mmDC_GPIO_DDC6_A; + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = mmDC_GPIO_DDCVGA_A; + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = mmDC_GPIO_I2CPAD_A; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_DDC_CLOCK: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = mmDC_GPIO_DDC1_A; + break; + case GPIO_DDC_LINE_DDC2: + info->offset = mmDC_GPIO_DDC2_A; + break; + case GPIO_DDC_LINE_DDC3: + info->offset = mmDC_GPIO_DDC3_A; + break; + case GPIO_DDC_LINE_DDC4: + info->offset = mmDC_GPIO_DDC4_A; + break; + case GPIO_DDC_LINE_DDC5: + info->offset = mmDC_GPIO_DDC5_A; + break; + case GPIO_DDC_LINE_DDC6: + info->offset = mmDC_GPIO_DDC6_A; + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = mmDC_GPIO_DDCVGA_A; + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = mmDC_GPIO_I2CPAD_A; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_GENERIC: + info->offset = mmDC_GPIO_GENERIC_A; + switch (en) { + case GPIO_GENERIC_A: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; + break; + case GPIO_GENERIC_B: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; + break; + case GPIO_GENERIC_C: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; + break; + case GPIO_GENERIC_D: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; + break; + case GPIO_GENERIC_E: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; + break; + case GPIO_GENERIC_F: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; + break; + case GPIO_GENERIC_G: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_HPD: + info->offset = mmDC_GPIO_HPD_A; + switch (en) { + case GPIO_HPD_1: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; + break; + case GPIO_HPD_2: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; + break; + case GPIO_HPD_3: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; + break; + case GPIO_HPD_4: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; + break; + case GPIO_HPD_5: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; + break; + case GPIO_HPD_6: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_SYNC: + switch (en) { + case GPIO_SYNC_HSYNC_A: + info->offset = mmDC_GPIO_SYNCA_A; + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK; + break; + case GPIO_SYNC_VSYNC_A: + info->offset = mmDC_GPIO_SYNCA_A; + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK; + break; + case GPIO_SYNC_HSYNC_B: + case GPIO_SYNC_VSYNC_B: + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_GSL: + switch (en) { + case GPIO_GSL_GENLOCK_CLOCK: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK; + break; + case GPIO_GSL_GENLOCK_VSYNC: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_A: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_B: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_VIP_PAD: + default: + ASSERT_CRITICAL(false); + result = false; + } + + if (result) { + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask_y = info->mask; + info->mask_en = info->mask; + info->mask_mask = info->mask; + } + + return result; +} + +/* function table */ +static const struct hw_translate_funcs funcs = { + .offset_to_id = offset_to_id, + .id_to_offset = id_to_offset, +}; + +/* + * dal_hw_translate_dce110_init + * + * @brief + * Initialize Hw translate function pointers. + * + * @param + * struct hw_translate *tr - [out] struct of function pointers + * + */ +void dal_hw_translate_dce110_init(struct hw_translate *tr) +{ + tr->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_translate_dce110.h b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_translate_dce110.h new file mode 100644 index 000000000000..4d16e09853c8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce110/hw_translate_dce110.h @@ -0,0 +1,34 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_TRANSLATE_DCE110_H__ +#define __DAL_HW_TRANSLATE_DCE110_H__ + +struct hw_translate; + +/* Initialize Hw translate function pointers */ +void dal_hw_translate_dce110_init(struct hw_translate *tr); + +#endif /* __DAL_HW_TRANSLATE_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_factory_dce120.c b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_factory_dce120.c new file mode 100644 index 000000000000..4ced9a7d63dd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_factory_dce120.c @@ -0,0 +1,197 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_factory.h" + + +#include "../hw_gpio.h" +#include "../hw_ddc.h" +#include "../hw_hpd.h" + +#include "hw_factory_dce120.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#define block HPD +#define reg_num 0 + +/* set field name */ +#define SF_HPD(reg_name, field_name, post_fix)\ + .field_name = HPD0_ ## reg_name ## __ ## field_name ## post_fix + +/* set field name */ +#define SF_HPD(reg_name, field_name, post_fix)\ + .field_name = HPD0_ ## reg_name ## __ ## field_name ## post_fix + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define REG(reg_name)\ + BASE(mm ## reg_name ## _BASE_IDX) + mm ## reg_name + +#define REGI(reg_name, block, id)\ + BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + + +#include "reg_helper.h" +#include "../hpd_regs.h" + +#define hpd_regs(id) \ +{\ + HPD_REG_LIST(id)\ +} + +static const struct hpd_registers hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +static const struct hpd_sh_mask hpd_shift = { + HPD_MASK_SH_LIST(__SHIFT) +}; + +static const struct hpd_sh_mask hpd_mask = { + HPD_MASK_SH_LIST(_MASK) +}; + +#include "../ddc_regs.h" + + /* set field name */ +#define SF_DDC(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +static const struct ddc_registers ddc_data_regs[] = { + ddc_data_regs(1), + ddc_data_regs(2), + ddc_data_regs(3), + ddc_data_regs(4), + ddc_data_regs(5), + ddc_data_regs(6), + ddc_vga_data_regs, + ddc_i2c_data_regs +}; + +static const struct ddc_registers ddc_clk_regs[] = { + ddc_clk_regs(1), + ddc_clk_regs(2), + ddc_clk_regs(3), + ddc_clk_regs(4), + ddc_clk_regs(5), + ddc_clk_regs(6), + ddc_vga_clk_regs, + ddc_i2c_clk_regs +}; + +static const struct ddc_sh_mask ddc_shift = { + DDC_MASK_SH_LIST(__SHIFT) +}; + +static const struct ddc_sh_mask ddc_mask = { + DDC_MASK_SH_LIST(_MASK) +}; + +static void define_ddc_registers( + struct hw_gpio_pin *pin, + uint32_t en) +{ + struct hw_ddc *ddc = HW_DDC_FROM_BASE(pin); + + switch (pin->id) { + case GPIO_ID_DDC_DATA: + ddc->regs = &ddc_data_regs[en]; + ddc->base.regs = &ddc_data_regs[en].gpio; + break; + case GPIO_ID_DDC_CLOCK: + ddc->regs = &ddc_clk_regs[en]; + ddc->base.regs = &ddc_clk_regs[en].gpio; + break; + default: + ASSERT_CRITICAL(false); + return; + } + + ddc->shifts = &ddc_shift; + ddc->masks = &ddc_mask; + +} + +static void define_hpd_registers(struct hw_gpio_pin *pin, uint32_t en) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(pin); + + hpd->regs = &hpd_regs[en]; + hpd->shifts = &hpd_shift; + hpd->masks = &hpd_mask; + hpd->base.regs = &hpd_regs[en].gpio; +} + + +/* fucntion table */ +static const struct hw_factory_funcs funcs = { + .create_ddc_data = dal_hw_ddc_create, + .create_ddc_clock = dal_hw_ddc_create, + .create_generic = NULL, + .create_hpd = dal_hw_hpd_create, + .create_sync = NULL, + .create_gsl = NULL, + .define_hpd_registers = define_hpd_registers, + .define_ddc_registers = define_ddc_registers +}; +/* + * dal_hw_factory_dce120_init + * + * @brief + * Initialize HW factory function pointers and pin info + * + * @param + * struct hw_factory *factory - [out] struct of function pointers + */ +void dal_hw_factory_dce120_init(struct hw_factory *factory) +{ + /*TODO check ASIC CAPs*/ + factory->number_of_pins[GPIO_ID_DDC_DATA] = 8; + factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 8; + factory->number_of_pins[GPIO_ID_GENERIC] = 7; + factory->number_of_pins[GPIO_ID_HPD] = 6; + factory->number_of_pins[GPIO_ID_GPIO_PAD] = 31; + factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; + factory->number_of_pins[GPIO_ID_SYNC] = 2; + factory->number_of_pins[GPIO_ID_GSL] = 4; + + factory->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_factory_dce120.h b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_factory_dce120.h new file mode 100644 index 000000000000..db260c351f73 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_factory_dce120.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_FACTORY_DCE120_H__ +#define __DAL_HW_FACTORY_DCE120_H__ + +/* Initialize HW factory function pointers and pin info */ +void dal_hw_factory_dce120_init(struct hw_factory *factory); + +#endif /* __DAL_HW_FACTORY_DCE120_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_translate_dce120.c b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_translate_dce120.c new file mode 100644 index 000000000000..af3843a69652 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_translate_dce120.c @@ -0,0 +1,408 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "hw_translate_dce120.h" + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_translate.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define REG(reg_name)\ + BASE(mm ## reg_name ## _BASE_IDX) + mm ## reg_name + +#define REGI(reg_name, block, id)\ + BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + +static bool offset_to_id( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en) +{ + switch (offset) { + /* GENERIC */ + case REG(DC_GPIO_GENERIC_A): + *id = GPIO_ID_GENERIC; + switch (mask) { + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: + *en = GPIO_GENERIC_A; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: + *en = GPIO_GENERIC_B; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: + *en = GPIO_GENERIC_C; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: + *en = GPIO_GENERIC_D; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: + *en = GPIO_GENERIC_E; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: + *en = GPIO_GENERIC_F; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: + *en = GPIO_GENERIC_G; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* HPD */ + case REG(DC_GPIO_HPD_A): + *id = GPIO_ID_HPD; + switch (mask) { + case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: + *en = GPIO_HPD_1; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: + *en = GPIO_HPD_2; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: + *en = GPIO_HPD_3; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: + *en = GPIO_HPD_4; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: + *en = GPIO_HPD_5; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: + *en = GPIO_HPD_6; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* SYNCA */ + case REG(DC_GPIO_SYNCA_A): + *id = GPIO_ID_SYNC; + switch (mask) { + case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK: + *en = GPIO_SYNC_HSYNC_A; + return true; + case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK: + *en = GPIO_SYNC_VSYNC_A; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* REG(DC_GPIO_GENLK_MASK */ + case REG(DC_GPIO_GENLK_A): + *id = GPIO_ID_GSL; + switch (mask) { + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: + *en = GPIO_GSL_GENLOCK_CLOCK; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: + *en = GPIO_GSL_GENLOCK_VSYNC; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: + *en = GPIO_GSL_SWAPLOCK_A; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: + *en = GPIO_GSL_SWAPLOCK_B; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* DDC */ + /* we don't care about the GPIO_ID for DDC + * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK + * directly in the create method */ + case REG(DC_GPIO_DDC1_A): + *en = GPIO_DDC_LINE_DDC1; + return true; + case REG(DC_GPIO_DDC2_A): + *en = GPIO_DDC_LINE_DDC2; + return true; + case REG(DC_GPIO_DDC3_A): + *en = GPIO_DDC_LINE_DDC3; + return true; + case REG(DC_GPIO_DDC4_A): + *en = GPIO_DDC_LINE_DDC4; + return true; + case REG(DC_GPIO_DDC5_A): + *en = GPIO_DDC_LINE_DDC5; + return true; + case REG(DC_GPIO_DDC6_A): + *en = GPIO_DDC_LINE_DDC6; + return true; + case REG(DC_GPIO_DDCVGA_A): + *en = GPIO_DDC_LINE_DDC_VGA; + return true; + /* GPIO_I2CPAD */ + case REG(DC_GPIO_I2CPAD_A): + *en = GPIO_DDC_LINE_I2C_PAD; + return true; + /* Not implemented */ + case REG(DC_GPIO_PWRSEQ_A): + case REG(DC_GPIO_PAD_STRENGTH_1): + case REG(DC_GPIO_PAD_STRENGTH_2): + case REG(DC_GPIO_DEBUG): + return false; + /* UNEXPECTED */ + default: + ASSERT_CRITICAL(false); + return false; + } +} + +static bool id_to_offset( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info) +{ + bool result = true; + + switch (id) { + case GPIO_ID_DDC_DATA: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = REG(DC_GPIO_DDC1_A); + break; + case GPIO_DDC_LINE_DDC2: + info->offset = REG(DC_GPIO_DDC2_A); + break; + case GPIO_DDC_LINE_DDC3: + info->offset = REG(DC_GPIO_DDC3_A); + break; + case GPIO_DDC_LINE_DDC4: + info->offset = REG(DC_GPIO_DDC4_A); + break; + case GPIO_DDC_LINE_DDC5: + info->offset = REG(DC_GPIO_DDC5_A); + break; + case GPIO_DDC_LINE_DDC6: + info->offset = REG(DC_GPIO_DDC6_A); + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = REG(DC_GPIO_DDCVGA_A); + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = REG(DC_GPIO_I2CPAD_A); + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_DDC_CLOCK: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = REG(DC_GPIO_DDC1_A); + break; + case GPIO_DDC_LINE_DDC2: + info->offset = REG(DC_GPIO_DDC2_A); + break; + case GPIO_DDC_LINE_DDC3: + info->offset = REG(DC_GPIO_DDC3_A); + break; + case GPIO_DDC_LINE_DDC4: + info->offset = REG(DC_GPIO_DDC4_A); + break; + case GPIO_DDC_LINE_DDC5: + info->offset = REG(DC_GPIO_DDC5_A); + break; + case GPIO_DDC_LINE_DDC6: + info->offset = REG(DC_GPIO_DDC6_A); + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = REG(DC_GPIO_DDCVGA_A); + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = REG(DC_GPIO_I2CPAD_A); + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_GENERIC: + info->offset = REG(DC_GPIO_GENERIC_A); + switch (en) { + case GPIO_GENERIC_A: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; + break; + case GPIO_GENERIC_B: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; + break; + case GPIO_GENERIC_C: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; + break; + case GPIO_GENERIC_D: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; + break; + case GPIO_GENERIC_E: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; + break; + case GPIO_GENERIC_F: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; + break; + case GPIO_GENERIC_G: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_HPD: + info->offset = REG(DC_GPIO_HPD_A); + switch (en) { + case GPIO_HPD_1: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; + break; + case GPIO_HPD_2: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; + break; + case GPIO_HPD_3: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; + break; + case GPIO_HPD_4: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; + break; + case GPIO_HPD_5: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; + break; + case GPIO_HPD_6: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_SYNC: + switch (en) { + case GPIO_SYNC_HSYNC_A: + info->offset = REG(DC_GPIO_SYNCA_A); + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK; + break; + case GPIO_SYNC_VSYNC_A: + info->offset = REG(DC_GPIO_SYNCA_A); + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK; + break; + case GPIO_SYNC_HSYNC_B: + case GPIO_SYNC_VSYNC_B: + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_GSL: + switch (en) { + case GPIO_GSL_GENLOCK_CLOCK: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK; + break; + case GPIO_GSL_GENLOCK_VSYNC: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_A: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_B: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_VIP_PAD: + default: + ASSERT_CRITICAL(false); + result = false; + } + + if (result) { + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask_y = info->mask; + info->mask_en = info->mask; + info->mask_mask = info->mask; + } + + return result; +} + +/* function table */ +static const struct hw_translate_funcs funcs = { + .offset_to_id = offset_to_id, + .id_to_offset = id_to_offset, +}; + +/* + * dal_hw_translate_dce120_init + * + * @brief + * Initialize Hw translate function pointers. + * + * @param + * struct hw_translate *tr - [out] struct of function pointers + * + */ +void dal_hw_translate_dce120_init(struct hw_translate *tr) +{ + tr->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_translate_dce120.h b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_translate_dce120.h new file mode 100644 index 000000000000..c21766894af3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce120/hw_translate_dce120.h @@ -0,0 +1,34 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_TRANSLATE_DCE120_H__ +#define __DAL_HW_TRANSLATE_DCE120_H__ + +struct hw_translate; + +/* Initialize Hw translate function pointers */ +void dal_hw_translate_dce120_init(struct hw_translate *tr); + +#endif /* __DAL_HW_TRANSLATE_DCE120_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.c b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.c new file mode 100644 index 000000000000..48b67866377e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.c @@ -0,0 +1,173 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_factory.h" + +#include "hw_factory_dce80.h" + +#include "../hw_gpio.h" +#include "../hw_ddc.h" +#include "../hw_hpd.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#define REG(reg_name)\ + mm ## reg_name + +#include "reg_helper.h" +#include "../hpd_regs.h" + +#define HPD_REG_LIST_DCE8(id) \ + HPD_GPIO_REG_LIST(id), \ + .int_status = mmDC_HPD ## id ## _INT_STATUS,\ + .toggle_filt_cntl = mmDC_HPD ## id ## _TOGGLE_FILT_CNTL + +#define HPD_MASK_SH_LIST_DCE8(mask_sh) \ + .DC_HPD_SENSE_DELAYED = DC_HPD1_INT_STATUS__DC_HPD1_SENSE_DELAYED ## mask_sh,\ + .DC_HPD_SENSE = DC_HPD1_INT_STATUS__DC_HPD1_SENSE ## mask_sh,\ + .DC_HPD_CONNECT_INT_DELAY = DC_HPD1_TOGGLE_FILT_CNTL__DC_HPD1_CONNECT_INT_DELAY ## mask_sh,\ + .DC_HPD_DISCONNECT_INT_DELAY = DC_HPD1_TOGGLE_FILT_CNTL__DC_HPD1_DISCONNECT_INT_DELAY ## mask_sh + +#define hpd_regs(id) \ +{\ + HPD_REG_LIST_DCE8(id)\ +} + +static const struct hpd_registers hpd_regs[] = { + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5), + hpd_regs(6) +}; + +static const struct hpd_sh_mask hpd_shift = { + HPD_MASK_SH_LIST_DCE8(__SHIFT) +}; + +static const struct hpd_sh_mask hpd_mask = { + HPD_MASK_SH_LIST_DCE8(_MASK) +}; + +#include "../ddc_regs.h" + + /* set field name */ +#define SF_DDC(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +static const struct ddc_registers ddc_data_regs[] = { + ddc_data_regs(1), + ddc_data_regs(2), + ddc_data_regs(3), + ddc_data_regs(4), + ddc_data_regs(5), + ddc_data_regs(6), + ddc_vga_data_regs, + ddc_i2c_data_regs +}; + +static const struct ddc_registers ddc_clk_regs[] = { + ddc_clk_regs(1), + ddc_clk_regs(2), + ddc_clk_regs(3), + ddc_clk_regs(4), + ddc_clk_regs(5), + ddc_clk_regs(6), + ddc_vga_clk_regs, + ddc_i2c_clk_regs +}; + +static const struct ddc_sh_mask ddc_shift = { + DDC_MASK_SH_LIST(__SHIFT) +}; + +static const struct ddc_sh_mask ddc_mask = { + DDC_MASK_SH_LIST(_MASK) +}; + +static void define_ddc_registers( + struct hw_gpio_pin *pin, + uint32_t en) +{ + struct hw_ddc *ddc = HW_DDC_FROM_BASE(pin); + + switch (pin->id) { + case GPIO_ID_DDC_DATA: + ddc->regs = &ddc_data_regs[en]; + ddc->base.regs = &ddc_data_regs[en].gpio; + break; + case GPIO_ID_DDC_CLOCK: + ddc->regs = &ddc_clk_regs[en]; + ddc->base.regs = &ddc_clk_regs[en].gpio; + break; + default: + ASSERT_CRITICAL(false); + return; + } + + ddc->shifts = &ddc_shift; + ddc->masks = &ddc_mask; + +} + +static void define_hpd_registers(struct hw_gpio_pin *pin, uint32_t en) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(pin); + + hpd->regs = &hpd_regs[en]; + hpd->shifts = &hpd_shift; + hpd->masks = &hpd_mask; + hpd->base.regs = &hpd_regs[en].gpio; +} + +static const struct hw_factory_funcs funcs = { + .create_ddc_data = dal_hw_ddc_create, + .create_ddc_clock = dal_hw_ddc_create, + .create_generic = NULL, + .create_hpd = dal_hw_hpd_create, + .create_sync = NULL, + .create_gsl = NULL, + .define_hpd_registers = define_hpd_registers, + .define_ddc_registers = define_ddc_registers +}; + +void dal_hw_factory_dce80_init( + struct hw_factory *factory) +{ + factory->number_of_pins[GPIO_ID_DDC_DATA] = 8; + factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 8; + factory->number_of_pins[GPIO_ID_GENERIC] = 7; + factory->number_of_pins[GPIO_ID_HPD] = 6; + factory->number_of_pins[GPIO_ID_GPIO_PAD] = 31; + factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; + factory->number_of_pins[GPIO_ID_SYNC] = 2; + factory->number_of_pins[GPIO_ID_GSL] = 4; + + factory->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.h b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.h new file mode 100644 index 000000000000..e78a8b36f35a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_FACTORY_DCE80_H__ +#define __DAL_HW_FACTORY_DCE80_H__ + +void dal_hw_factory_dce80_init( + struct hw_factory *factory); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c new file mode 100644 index 000000000000..fabb9da504be --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c @@ -0,0 +1,411 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/gpio_types.h" +#include "../hw_translate.h" + +#include "hw_translate_dce80.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +#include "smu/smu_7_0_1_d.h" + +/* + * @brief + * Returns index of first bit (starting with LSB) which is set + */ +static uint32_t index_from_vector( + uint32_t vector) +{ + uint32_t result = 0; + uint32_t mask = 1; + + do { + if (vector == mask) + return result; + + ++result; + mask <<= 1; + } while (mask); + + BREAK_TO_DEBUGGER(); + + return GPIO_ENUM_UNKNOWN; +} + +static bool offset_to_id( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en) +{ + switch (offset) { + /* GENERIC */ + case mmDC_GPIO_GENERIC_A: + *id = GPIO_ID_GENERIC; + switch (mask) { + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: + *en = GPIO_GENERIC_A; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: + *en = GPIO_GENERIC_B; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: + *en = GPIO_GENERIC_C; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: + *en = GPIO_GENERIC_D; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: + *en = GPIO_GENERIC_E; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: + *en = GPIO_GENERIC_F; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: + *en = GPIO_GENERIC_G; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* HPD */ + case mmDC_GPIO_HPD_A: + *id = GPIO_ID_HPD; + switch (mask) { + case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: + *en = GPIO_HPD_1; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: + *en = GPIO_HPD_2; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: + *en = GPIO_HPD_3; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: + *en = GPIO_HPD_4; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: + *en = GPIO_HPD_5; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: + *en = GPIO_HPD_6; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* SYNCA */ + case mmDC_GPIO_SYNCA_A: + *id = GPIO_ID_SYNC; + switch (mask) { + case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK: + *en = GPIO_SYNC_HSYNC_A; + return true; + case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK: + *en = GPIO_SYNC_VSYNC_A; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* mmDC_GPIO_GENLK_MASK */ + case mmDC_GPIO_GENLK_A: + *id = GPIO_ID_GSL; + switch (mask) { + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: + *en = GPIO_GSL_GENLOCK_CLOCK; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: + *en = GPIO_GSL_GENLOCK_VSYNC; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: + *en = GPIO_GSL_SWAPLOCK_A; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: + *en = GPIO_GSL_SWAPLOCK_B; + return true; + default: + BREAK_TO_DEBUGGER(); + return false; + } + break; + /* GPIOPAD */ + case mmGPIOPAD_A: + *id = GPIO_ID_GPIO_PAD; + *en = index_from_vector(mask); + return (*en <= GPIO_GPIO_PAD_MAX); + /* DDC */ + /* we don't care about the GPIO_ID for DDC + * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK + * directly in the create method */ + case mmDC_GPIO_DDC1_A: + *en = GPIO_DDC_LINE_DDC1; + return true; + case mmDC_GPIO_DDC2_A: + *en = GPIO_DDC_LINE_DDC2; + return true; + case mmDC_GPIO_DDC3_A: + *en = GPIO_DDC_LINE_DDC3; + return true; + case mmDC_GPIO_DDC4_A: + *en = GPIO_DDC_LINE_DDC4; + return true; + case mmDC_GPIO_DDC5_A: + *en = GPIO_DDC_LINE_DDC5; + return true; + case mmDC_GPIO_DDC6_A: + *en = GPIO_DDC_LINE_DDC6; + return true; + case mmDC_GPIO_DDCVGA_A: + *en = GPIO_DDC_LINE_DDC_VGA; + return true; + /* GPIO_I2CPAD */ + case mmDC_GPIO_I2CPAD_A: + *en = GPIO_DDC_LINE_I2C_PAD; + return true; + /* Not implemented */ + case mmDC_GPIO_PWRSEQ_A: + case mmDC_GPIO_PAD_STRENGTH_1: + case mmDC_GPIO_PAD_STRENGTH_2: + case mmDC_GPIO_DEBUG: + return false; + /* UNEXPECTED */ + default: + BREAK_TO_DEBUGGER(); + return false; + } +} + +static bool id_to_offset( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info) +{ + bool result = true; + + switch (id) { + case GPIO_ID_DDC_DATA: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = mmDC_GPIO_DDC1_A; + break; + case GPIO_DDC_LINE_DDC2: + info->offset = mmDC_GPIO_DDC2_A; + break; + case GPIO_DDC_LINE_DDC3: + info->offset = mmDC_GPIO_DDC3_A; + break; + case GPIO_DDC_LINE_DDC4: + info->offset = mmDC_GPIO_DDC4_A; + break; + case GPIO_DDC_LINE_DDC5: + info->offset = mmDC_GPIO_DDC5_A; + break; + case GPIO_DDC_LINE_DDC6: + info->offset = mmDC_GPIO_DDC6_A; + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = mmDC_GPIO_DDCVGA_A; + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = mmDC_GPIO_I2CPAD_A; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_DDC_CLOCK: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = mmDC_GPIO_DDC1_A; + break; + case GPIO_DDC_LINE_DDC2: + info->offset = mmDC_GPIO_DDC2_A; + break; + case GPIO_DDC_LINE_DDC3: + info->offset = mmDC_GPIO_DDC3_A; + break; + case GPIO_DDC_LINE_DDC4: + info->offset = mmDC_GPIO_DDC4_A; + break; + case GPIO_DDC_LINE_DDC5: + info->offset = mmDC_GPIO_DDC5_A; + break; + case GPIO_DDC_LINE_DDC6: + info->offset = mmDC_GPIO_DDC6_A; + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = mmDC_GPIO_DDCVGA_A; + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = mmDC_GPIO_I2CPAD_A; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_GENERIC: + info->offset = mmDC_GPIO_GENERIC_A; + switch (en) { + case GPIO_GENERIC_A: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; + break; + case GPIO_GENERIC_B: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; + break; + case GPIO_GENERIC_C: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; + break; + case GPIO_GENERIC_D: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; + break; + case GPIO_GENERIC_E: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; + break; + case GPIO_GENERIC_F: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; + break; + case GPIO_GENERIC_G: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_HPD: + info->offset = mmDC_GPIO_HPD_A; + switch (en) { + case GPIO_HPD_1: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; + break; + case GPIO_HPD_2: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; + break; + case GPIO_HPD_3: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; + break; + case GPIO_HPD_4: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; + break; + case GPIO_HPD_5: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; + break; + case GPIO_HPD_6: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_SYNC: + switch (en) { + case GPIO_SYNC_HSYNC_A: + info->offset = mmDC_GPIO_SYNCA_A; + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK; + break; + case GPIO_SYNC_VSYNC_A: + info->offset = mmDC_GPIO_SYNCA_A; + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK; + break; + case GPIO_SYNC_HSYNC_B: + case GPIO_SYNC_VSYNC_B: + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_GSL: + switch (en) { + case GPIO_GSL_GENLOCK_CLOCK: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK; + break; + case GPIO_GSL_GENLOCK_VSYNC: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_A: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_B: + info->offset = mmDC_GPIO_GENLK_A; + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK; + break; + default: + BREAK_TO_DEBUGGER(); + result = false; + } + break; + case GPIO_ID_GPIO_PAD: + info->offset = mmGPIOPAD_A; + info->mask = (1 << en); + result = (info->mask <= GPIO_GPIO_PAD_MAX); + break; + case GPIO_ID_VIP_PAD: + default: + BREAK_TO_DEBUGGER(); + result = false; + } + + if (result) { + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask_y = info->mask; + info->mask_en = info->mask; + info->mask_mask = info->mask; + } + + return result; +} + +static const struct hw_translate_funcs funcs = { + .offset_to_id = offset_to_id, + .id_to_offset = id_to_offset, +}; + +void dal_hw_translate_dce80_init( + struct hw_translate *translate) +{ + translate->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.h b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.h new file mode 100644 index 000000000000..374f2f3282a1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_TRANSLATE_DCE80_H__ +#define __DAL_HW_TRANSLATE_DCE80_H__ + +void dal_hw_translate_dce80_init( + struct hw_translate *tr); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.c new file mode 100644 index 000000000000..409763c70ce5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.c @@ -0,0 +1,192 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_factory.h" + + +#include "../hw_gpio.h" +#include "../hw_ddc.h" +#include "../hw_hpd.h" + +#include "hw_factory_dcn10.h" + +#include "raven1/DCN/dcn_1_0_offset.h" +#include "raven1/DCN/dcn_1_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#define block HPD +#define reg_num 0 + +/* set field name */ +#define SF_HPD(reg_name, field_name, post_fix)\ + .field_name = HPD0_ ## reg_name ## __ ## field_name ## post_fix + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define REG(reg_name)\ + BASE(mm ## reg_name ## _BASE_IDX) + mm ## reg_name + +#define REGI(reg_name, block, id)\ + BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +#include "reg_helper.h" +#include "../hpd_regs.h" + +#define hpd_regs(id) \ +{\ + HPD_REG_LIST(id)\ +} + +static const struct hpd_registers hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +static const struct hpd_sh_mask hpd_shift = { + HPD_MASK_SH_LIST(__SHIFT) +}; + +static const struct hpd_sh_mask hpd_mask = { + HPD_MASK_SH_LIST(_MASK) +}; + +#include "../ddc_regs.h" + + /* set field name */ +#define SF_DDC(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +static const struct ddc_registers ddc_data_regs[] = { + ddc_data_regs(1), + ddc_data_regs(2), + ddc_data_regs(3), + ddc_data_regs(4), + ddc_data_regs(5), + ddc_data_regs(6), + ddc_vga_data_regs, + ddc_i2c_data_regs +}; + +static const struct ddc_registers ddc_clk_regs[] = { + ddc_clk_regs(1), + ddc_clk_regs(2), + ddc_clk_regs(3), + ddc_clk_regs(4), + ddc_clk_regs(5), + ddc_clk_regs(6), + ddc_vga_clk_regs, + ddc_i2c_clk_regs +}; + +static const struct ddc_sh_mask ddc_shift = { + DDC_MASK_SH_LIST(__SHIFT) +}; + +static const struct ddc_sh_mask ddc_mask = { + DDC_MASK_SH_LIST(_MASK) +}; + +static void define_ddc_registers( + struct hw_gpio_pin *pin, + uint32_t en) +{ + struct hw_ddc *ddc = HW_DDC_FROM_BASE(pin); + + switch (pin->id) { + case GPIO_ID_DDC_DATA: + ddc->regs = &ddc_data_regs[en]; + ddc->base.regs = &ddc_data_regs[en].gpio; + break; + case GPIO_ID_DDC_CLOCK: + ddc->regs = &ddc_clk_regs[en]; + ddc->base.regs = &ddc_clk_regs[en].gpio; + break; + default: + ASSERT_CRITICAL(false); + return; + } + + ddc->shifts = &ddc_shift; + ddc->masks = &ddc_mask; + +} + +static void define_hpd_registers(struct hw_gpio_pin *pin, uint32_t en) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(pin); + + hpd->regs = &hpd_regs[en]; + hpd->shifts = &hpd_shift; + hpd->masks = &hpd_mask; + hpd->base.regs = &hpd_regs[en].gpio; +} + + +/* fucntion table */ +static const struct hw_factory_funcs funcs = { + .create_ddc_data = dal_hw_ddc_create, + .create_ddc_clock = dal_hw_ddc_create, + .create_generic = NULL, + .create_hpd = dal_hw_hpd_create, + .create_sync = NULL, + .create_gsl = NULL, + .define_hpd_registers = define_hpd_registers, + .define_ddc_registers = define_ddc_registers +}; +/* + * dal_hw_factory_dcn10_init + * + * @brief + * Initialize HW factory function pointers and pin info + * + * @param + * struct hw_factory *factory - [out] struct of function pointers + */ +void dal_hw_factory_dcn10_init(struct hw_factory *factory) +{ + /*TODO check ASIC CAPs*/ + factory->number_of_pins[GPIO_ID_DDC_DATA] = 8; + factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 8; + factory->number_of_pins[GPIO_ID_GENERIC] = 7; + factory->number_of_pins[GPIO_ID_HPD] = 6; + factory->number_of_pins[GPIO_ID_GPIO_PAD] = 31; + factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; + factory->number_of_pins[GPIO_ID_SYNC] = 2; + factory->number_of_pins[GPIO_ID_GSL] = 4; + + factory->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.h b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.h new file mode 100644 index 000000000000..2cc7a585b1f8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_FACTORY_DCN10_H__ +#define __DAL_HW_FACTORY_DCN10_H__ + +/* Initialize HW factory function pointers and pin info */ +void dal_hw_factory_dcn10_init(struct hw_factory *factory); + +#endif /* __DAL_HW_FACTORY_DCN10_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c new file mode 100644 index 000000000000..64a6915b846b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c @@ -0,0 +1,408 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "hw_translate_dcn10.h" + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_translate.h" + +#include "raven1/DCN/dcn_1_0_offset.h" +#include "raven1/DCN/dcn_1_0_sh_mask.h" +#include "vega10/soc15ip.h" + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define REG(reg_name)\ + BASE(mm ## reg_name ## _BASE_IDX) + mm ## reg_name + +#define REGI(reg_name, block, id)\ + BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + +static bool offset_to_id( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en) +{ + switch (offset) { + /* GENERIC */ + case REG(DC_GPIO_GENERIC_A): + *id = GPIO_ID_GENERIC; + switch (mask) { + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: + *en = GPIO_GENERIC_A; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: + *en = GPIO_GENERIC_B; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: + *en = GPIO_GENERIC_C; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: + *en = GPIO_GENERIC_D; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: + *en = GPIO_GENERIC_E; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: + *en = GPIO_GENERIC_F; + return true; + case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: + *en = GPIO_GENERIC_G; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* HPD */ + case REG(DC_GPIO_HPD_A): + *id = GPIO_ID_HPD; + switch (mask) { + case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: + *en = GPIO_HPD_1; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: + *en = GPIO_HPD_2; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: + *en = GPIO_HPD_3; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: + *en = GPIO_HPD_4; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: + *en = GPIO_HPD_5; + return true; + case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: + *en = GPIO_HPD_6; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* SYNCA */ + case REG(DC_GPIO_SYNCA_A): + *id = GPIO_ID_SYNC; + switch (mask) { + case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK: + *en = GPIO_SYNC_HSYNC_A; + return true; + case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK: + *en = GPIO_SYNC_VSYNC_A; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* REG(DC_GPIO_GENLK_MASK */ + case REG(DC_GPIO_GENLK_A): + *id = GPIO_ID_GSL; + switch (mask) { + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: + *en = GPIO_GSL_GENLOCK_CLOCK; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: + *en = GPIO_GSL_GENLOCK_VSYNC; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: + *en = GPIO_GSL_SWAPLOCK_A; + return true; + case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: + *en = GPIO_GSL_SWAPLOCK_B; + return true; + default: + ASSERT_CRITICAL(false); + return false; + } + break; + /* DDC */ + /* we don't care about the GPIO_ID for DDC + * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK + * directly in the create method */ + case REG(DC_GPIO_DDC1_A): + *en = GPIO_DDC_LINE_DDC1; + return true; + case REG(DC_GPIO_DDC2_A): + *en = GPIO_DDC_LINE_DDC2; + return true; + case REG(DC_GPIO_DDC3_A): + *en = GPIO_DDC_LINE_DDC3; + return true; + case REG(DC_GPIO_DDC4_A): + *en = GPIO_DDC_LINE_DDC4; + return true; + case REG(DC_GPIO_DDC5_A): + *en = GPIO_DDC_LINE_DDC5; + return true; + case REG(DC_GPIO_DDC6_A): + *en = GPIO_DDC_LINE_DDC6; + return true; + case REG(DC_GPIO_DDCVGA_A): + *en = GPIO_DDC_LINE_DDC_VGA; + return true; + /* GPIO_I2CPAD */ + case REG(DC_GPIO_I2CPAD_A): + *en = GPIO_DDC_LINE_I2C_PAD; + return true; + /* Not implemented */ + case REG(DC_GPIO_PWRSEQ_A): + case REG(DC_GPIO_PAD_STRENGTH_1): + case REG(DC_GPIO_PAD_STRENGTH_2): + case REG(DC_GPIO_DEBUG): + return false; + /* UNEXPECTED */ + default: + ASSERT_CRITICAL(false); + return false; + } +} + +static bool id_to_offset( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info) +{ + bool result = true; + + switch (id) { + case GPIO_ID_DDC_DATA: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = REG(DC_GPIO_DDC1_A); + break; + case GPIO_DDC_LINE_DDC2: + info->offset = REG(DC_GPIO_DDC2_A); + break; + case GPIO_DDC_LINE_DDC3: + info->offset = REG(DC_GPIO_DDC3_A); + break; + case GPIO_DDC_LINE_DDC4: + info->offset = REG(DC_GPIO_DDC4_A); + break; + case GPIO_DDC_LINE_DDC5: + info->offset = REG(DC_GPIO_DDC5_A); + break; + case GPIO_DDC_LINE_DDC6: + info->offset = REG(DC_GPIO_DDC6_A); + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = REG(DC_GPIO_DDCVGA_A); + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = REG(DC_GPIO_I2CPAD_A); + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_DDC_CLOCK: + info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; + switch (en) { + case GPIO_DDC_LINE_DDC1: + info->offset = REG(DC_GPIO_DDC1_A); + break; + case GPIO_DDC_LINE_DDC2: + info->offset = REG(DC_GPIO_DDC2_A); + break; + case GPIO_DDC_LINE_DDC3: + info->offset = REG(DC_GPIO_DDC3_A); + break; + case GPIO_DDC_LINE_DDC4: + info->offset = REG(DC_GPIO_DDC4_A); + break; + case GPIO_DDC_LINE_DDC5: + info->offset = REG(DC_GPIO_DDC5_A); + break; + case GPIO_DDC_LINE_DDC6: + info->offset = REG(DC_GPIO_DDC6_A); + break; + case GPIO_DDC_LINE_DDC_VGA: + info->offset = REG(DC_GPIO_DDCVGA_A); + break; + case GPIO_DDC_LINE_I2C_PAD: + info->offset = REG(DC_GPIO_I2CPAD_A); + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_GENERIC: + info->offset = REG(DC_GPIO_GENERIC_A); + switch (en) { + case GPIO_GENERIC_A: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; + break; + case GPIO_GENERIC_B: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; + break; + case GPIO_GENERIC_C: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; + break; + case GPIO_GENERIC_D: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; + break; + case GPIO_GENERIC_E: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; + break; + case GPIO_GENERIC_F: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; + break; + case GPIO_GENERIC_G: + info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_HPD: + info->offset = REG(DC_GPIO_HPD_A); + switch (en) { + case GPIO_HPD_1: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; + break; + case GPIO_HPD_2: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; + break; + case GPIO_HPD_3: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; + break; + case GPIO_HPD_4: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; + break; + case GPIO_HPD_5: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; + break; + case GPIO_HPD_6: + info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_SYNC: + switch (en) { + case GPIO_SYNC_HSYNC_A: + info->offset = REG(DC_GPIO_SYNCA_A); + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK; + break; + case GPIO_SYNC_VSYNC_A: + info->offset = REG(DC_GPIO_SYNCA_A); + info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK; + break; + case GPIO_SYNC_HSYNC_B: + case GPIO_SYNC_VSYNC_B: + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_GSL: + switch (en) { + case GPIO_GSL_GENLOCK_CLOCK: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK; + break; + case GPIO_GSL_GENLOCK_VSYNC: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_A: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK; + break; + case GPIO_GSL_SWAPLOCK_B: + info->offset = REG(DC_GPIO_GENLK_A); + info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK; + break; + default: + ASSERT_CRITICAL(false); + result = false; + } + break; + case GPIO_ID_VIP_PAD: + default: + ASSERT_CRITICAL(false); + result = false; + } + + if (result) { + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask_y = info->mask; + info->mask_en = info->mask; + info->mask_mask = info->mask; + } + + return result; +} + +/* function table */ +static const struct hw_translate_funcs funcs = { + .offset_to_id = offset_to_id, + .id_to_offset = id_to_offset, +}; + +/* + * dal_hw_translate_dcn10_init + * + * @brief + * Initialize Hw translate function pointers. + * + * @param + * struct hw_translate *tr - [out] struct of function pointers + * + */ +void dal_hw_translate_dcn10_init(struct hw_translate *tr) +{ + tr->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.h b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.h new file mode 100644 index 000000000000..9edef53c80a0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.h @@ -0,0 +1,34 @@ +/* + * Copyright 2013-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_TRANSLATE_DCN10_H__ +#define __DAL_HW_TRANSLATE_DCN10_H__ + +struct hw_translate; + +/* Initialize Hw translate function pointers */ +void dal_hw_translate_dcn10_init(struct hw_translate *tr); + +#endif /* __DAL_HW_TRANSLATE_DCN10_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/ddc_regs.h b/drivers/gpu/drm/amd/display/dc/gpio/ddc_regs.h new file mode 100644 index 000000000000..9c4a56c738c0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/ddc_regs.h @@ -0,0 +1,150 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_DDC_REGS_H_ +#define DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_DDC_REGS_H_ + +#include "gpio_regs.h" + +/****************************** new register headers */ +/*** following in header */ + +#define DDC_GPIO_REG_LIST_ENTRY(type,cd,id) \ + .type ## _reg = REG(DC_GPIO_DDC ## id ## _ ## type),\ + .type ## _mask = DC_GPIO_DDC ## id ## _ ## type ## __DC_GPIO_DDC ## id ## cd ## _ ## type ## _MASK,\ + .type ## _shift = DC_GPIO_DDC ## id ## _ ## type ## __DC_GPIO_DDC ## id ## cd ## _ ## type ## __SHIFT + +#define DDC_GPIO_REG_LIST(cd,id) \ + {\ + DDC_GPIO_REG_LIST_ENTRY(MASK,cd,id),\ + DDC_GPIO_REG_LIST_ENTRY(A,cd,id),\ + DDC_GPIO_REG_LIST_ENTRY(EN,cd,id),\ + DDC_GPIO_REG_LIST_ENTRY(Y,cd,id)\ + } + +#define DDC_REG_LIST(cd,id) \ + DDC_GPIO_REG_LIST(cd,id),\ + .ddc_setup = REG(DC_I2C_DDC ## id ## _SETUP) + +#define DDC_GPIO_VGA_REG_LIST_ENTRY(type,cd)\ + .type ## _reg = REG(DC_GPIO_DDCVGA_ ## type),\ + .type ## _mask = DC_GPIO_DDCVGA_ ## type ## __DC_GPIO_DDCVGA ## cd ## _ ## type ## _MASK,\ + .type ## _shift = DC_GPIO_DDCVGA_ ## type ## __DC_GPIO_DDCVGA ## cd ## _ ## type ## __SHIFT + +#define DDC_GPIO_VGA_REG_LIST(cd) \ + {\ + DDC_GPIO_VGA_REG_LIST_ENTRY(MASK,cd),\ + DDC_GPIO_VGA_REG_LIST_ENTRY(A,cd),\ + DDC_GPIO_VGA_REG_LIST_ENTRY(EN,cd),\ + DDC_GPIO_VGA_REG_LIST_ENTRY(Y,cd)\ + } + +#define DDC_VGA_REG_LIST(cd) \ + DDC_GPIO_VGA_REG_LIST(cd),\ + .ddc_setup = mmDC_I2C_DDCVGA_SETUP + +#define DDC_GPIO_I2C_REG_LIST_ENTRY(type,cd) \ + .type ## _reg = REG(DC_GPIO_I2CPAD_ ## type),\ + .type ## _mask = DC_GPIO_I2CPAD_ ## type ## __DC_GPIO_ ## cd ## _ ## type ## _MASK,\ + .type ## _shift = DC_GPIO_I2CPAD_ ## type ## __DC_GPIO_ ## cd ## _ ## type ## __SHIFT + +#define DDC_GPIO_I2C_REG_LIST(cd) \ + {\ + DDC_GPIO_I2C_REG_LIST_ENTRY(MASK,cd),\ + DDC_GPIO_I2C_REG_LIST_ENTRY(A,cd),\ + DDC_GPIO_I2C_REG_LIST_ENTRY(EN,cd),\ + DDC_GPIO_I2C_REG_LIST_ENTRY(Y,cd)\ + } + +#define DDC_I2C_REG_LIST(cd) \ + DDC_GPIO_I2C_REG_LIST(cd),\ + .ddc_setup = 0 + +#define DDC_MASK_SH_LIST(mask_sh) \ + SF_DDC(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE, mask_sh),\ + SF_DDC(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_EDID_DETECT_ENABLE, mask_sh),\ + SF_DDC(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_EDID_DETECT_MODE, mask_sh),\ + SF_DDC(DC_GPIO_DDC1_MASK, DC_GPIO_DDC1DATA_PD_EN, mask_sh),\ + SF_DDC(DC_GPIO_DDC1_MASK, DC_GPIO_DDC1CLK_PD_EN, mask_sh),\ + SF_DDC(DC_GPIO_DDC1_MASK, AUX_PAD1_MODE, mask_sh),\ + SF_DDC(DC_GPIO_I2CPAD_MASK, DC_GPIO_SDA_PD_DIS, mask_sh),\ + SF_DDC(DC_GPIO_I2CPAD_MASK, DC_GPIO_SCL_PD_DIS, mask_sh) + + +struct ddc_registers { + struct gpio_registers gpio; + uint32_t ddc_setup; +}; + +struct ddc_sh_mask { + /* i2c_dd_setup */ + uint32_t DC_I2C_DDC1_ENABLE; + uint32_t DC_I2C_DDC1_EDID_DETECT_ENABLE; + uint32_t DC_I2C_DDC1_EDID_DETECT_MODE; + /* ddc1_mask */ + uint32_t DC_GPIO_DDC1DATA_PD_EN; + uint32_t DC_GPIO_DDC1CLK_PD_EN; + uint32_t AUX_PAD1_MODE; + /* i2cpad_mask */ + uint32_t DC_GPIO_SDA_PD_DIS; + uint32_t DC_GPIO_SCL_PD_DIS; +}; + + + +/*** following in dc_resource */ + +#define ddc_data_regs(id) \ +{\ + DDC_REG_LIST(DATA,id)\ +} + +#define ddc_clk_regs(id) \ +{\ + DDC_REG_LIST(CLK,id)\ +} + +#define ddc_vga_data_regs \ +{\ + DDC_VGA_REG_LIST(DATA)\ +} + +#define ddc_vga_clk_regs \ +{\ + DDC_VGA_REG_LIST(CLK)\ +} + +#define ddc_i2c_data_regs \ +{\ + DDC_I2C_REG_LIST(SDA)\ +} + +#define ddc_i2c_clk_regs \ +{\ + DDC_I2C_REG_LIST(SCL)\ +} + + +#endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_DDC_REGS_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_factory_diag.c b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_factory_diag.c new file mode 100644 index 000000000000..26695b963c58 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_factory_diag.c @@ -0,0 +1,63 @@ +/* + * Copyright 2013-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "../hw_factory.h" + +/* + * Header of this unit + */ + +#include "../hw_gpio.h" +#include "../hw_ddc.h" +#include "../hw_hpd.h" + +/* function table */ +static const struct hw_factory_funcs funcs = { + .create_ddc_data = NULL, + .create_ddc_clock = NULL, + .create_generic = NULL, + .create_hpd = NULL, + .create_sync = NULL, + .create_gsl = NULL, +}; + +void dal_hw_factory_diag_fpga_init(struct hw_factory *factory) +{ + factory->number_of_pins[GPIO_ID_DDC_DATA] = 8; + factory->number_of_pins[GPIO_ID_DDC_CLOCK] = 8; + factory->number_of_pins[GPIO_ID_GENERIC] = 7; + factory->number_of_pins[GPIO_ID_HPD] = 6; + factory->number_of_pins[GPIO_ID_GPIO_PAD] = 31; + factory->number_of_pins[GPIO_ID_VIP_PAD] = 0; + factory->number_of_pins[GPIO_ID_SYNC] = 2; + factory->number_of_pins[GPIO_ID_GSL] = 4; + factory->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_factory_diag.h b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_factory_diag.h new file mode 100644 index 000000000000..8a74f6adb8ee --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_factory_diag.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_FACTORY_DIAG_FPGA_H__ +#define __DAL_HW_FACTORY_DIAG_FPGA_H__ + +/* Initialize HW factory function pointers and pin info */ +void dal_hw_factory_diag_fpga_init(struct hw_factory *factory); + +#endif /* __DAL_HW_FACTORY_DIAG_FPGA_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_translate_diag.c b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_translate_diag.c new file mode 100644 index 000000000000..bf9068846927 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_translate_diag.c @@ -0,0 +1,40 @@ +/* + * Copyright 2013-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/gpio_types.h" + +#include "../hw_translate.h" + +/* function table */ +static const struct hw_translate_funcs funcs = { + .offset_to_id = NULL, + .id_to_offset = NULL, +}; + +void dal_hw_translate_diag_fpga_init(struct hw_translate *tr) +{ + tr->funcs = &funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_translate_diag.h b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_translate_diag.h new file mode 100644 index 000000000000..4f053241fe96 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/diagnostics/hw_translate_diag.h @@ -0,0 +1,34 @@ +/* + * Copyright 2013-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_TRANSLATE_DIAG_FPGA_H__ +#define __DAL_HW_TRANSLATE_DIAG_FPGA_H__ + +struct hw_translate; + +/* Initialize Hw translate function pointers */ +void dal_hw_translate_diag_fpga_init(struct hw_translate *tr); + +#endif /* __DAL_HW_TRANSLATE_DIAG_FPGA_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c b/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c new file mode 100644 index 000000000000..1d1efd72b291 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c @@ -0,0 +1,272 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "dm_services.h" + +#include "include/gpio_interface.h" +#include "include/gpio_service_interface.h" +#include "hw_gpio.h" +#include "hw_translate.h" +#include "hw_factory.h" +#include "gpio_service.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +/* + * @brief + * Public API + */ + +enum gpio_result dal_gpio_open( + struct gpio *gpio, + enum gpio_mode mode) +{ + return dal_gpio_open_ex(gpio, mode); +} + +enum gpio_result dal_gpio_open_ex( + struct gpio *gpio, + enum gpio_mode mode) +{ + if (gpio->pin) { + ASSERT_CRITICAL(false); + return GPIO_RESULT_ALREADY_OPENED; + } + + gpio->mode = mode; + + return dal_gpio_service_open( + gpio->service, gpio->id, gpio->en, mode, &gpio->pin); +} + +enum gpio_result dal_gpio_get_value( + const struct gpio *gpio, + uint32_t *value) +{ + if (!gpio->pin) { + BREAK_TO_DEBUGGER(); + return GPIO_RESULT_NULL_HANDLE; + } + + return gpio->pin->funcs->get_value(gpio->pin, value); +} + +enum gpio_result dal_gpio_set_value( + const struct gpio *gpio, + uint32_t value) +{ + if (!gpio->pin) { + BREAK_TO_DEBUGGER(); + return GPIO_RESULT_NULL_HANDLE; + } + + return gpio->pin->funcs->set_value(gpio->pin, value); +} + +enum gpio_mode dal_gpio_get_mode( + const struct gpio *gpio) +{ + return gpio->mode; +} + +enum gpio_result dal_gpio_change_mode( + struct gpio *gpio, + enum gpio_mode mode) +{ + if (!gpio->pin) { + BREAK_TO_DEBUGGER(); + return GPIO_RESULT_NULL_HANDLE; + } + + return gpio->pin->funcs->change_mode(gpio->pin, mode); +} + +enum gpio_id dal_gpio_get_id( + const struct gpio *gpio) +{ + return gpio->id; +} + +uint32_t dal_gpio_get_enum( + const struct gpio *gpio) +{ + return gpio->en; +} + +enum gpio_result dal_gpio_set_config( + struct gpio *gpio, + const struct gpio_config_data *config_data) +{ + if (!gpio->pin) { + BREAK_TO_DEBUGGER(); + return GPIO_RESULT_NULL_HANDLE; + } + + return gpio->pin->funcs->set_config(gpio->pin, config_data); +} + +enum gpio_result dal_gpio_get_pin_info( + const struct gpio *gpio, + struct gpio_pin_info *pin_info) +{ + return gpio->service->translate.funcs->id_to_offset( + gpio->id, gpio->en, pin_info) ? + GPIO_RESULT_OK : GPIO_RESULT_INVALID_DATA; +} + +enum sync_source dal_gpio_get_sync_source( + const struct gpio *gpio) +{ + switch (gpio->id) { + case GPIO_ID_GENERIC: + switch (gpio->en) { + case GPIO_GENERIC_A: + return SYNC_SOURCE_IO_GENERIC_A; + case GPIO_GENERIC_B: + return SYNC_SOURCE_IO_GENERIC_B; + case GPIO_GENERIC_C: + return SYNC_SOURCE_IO_GENERIC_C; + case GPIO_GENERIC_D: + return SYNC_SOURCE_IO_GENERIC_D; + case GPIO_GENERIC_E: + return SYNC_SOURCE_IO_GENERIC_E; + case GPIO_GENERIC_F: + return SYNC_SOURCE_IO_GENERIC_F; + default: + return SYNC_SOURCE_NONE; + } + break; + case GPIO_ID_SYNC: + switch (gpio->en) { + case GPIO_SYNC_HSYNC_A: + return SYNC_SOURCE_IO_HSYNC_A; + case GPIO_SYNC_VSYNC_A: + return SYNC_SOURCE_IO_VSYNC_A; + case GPIO_SYNC_HSYNC_B: + return SYNC_SOURCE_IO_HSYNC_B; + case GPIO_SYNC_VSYNC_B: + return SYNC_SOURCE_IO_VSYNC_B; + default: + return SYNC_SOURCE_NONE; + } + break; + case GPIO_ID_HPD: + switch (gpio->en) { + case GPIO_HPD_1: + return SYNC_SOURCE_IO_HPD1; + case GPIO_HPD_2: + return SYNC_SOURCE_IO_HPD2; + default: + return SYNC_SOURCE_NONE; + } + break; + case GPIO_ID_GSL: + switch (gpio->en) { + case GPIO_GSL_GENLOCK_CLOCK: + return SYNC_SOURCE_GSL_IO_GENLOCK_CLOCK; + case GPIO_GSL_GENLOCK_VSYNC: + return SYNC_SOURCE_GSL_IO_GENLOCK_VSYNC; + case GPIO_GSL_SWAPLOCK_A: + return SYNC_SOURCE_GSL_IO_SWAPLOCK_A; + case GPIO_GSL_SWAPLOCK_B: + return SYNC_SOURCE_GSL_IO_SWAPLOCK_B; + default: + return SYNC_SOURCE_NONE; + } + break; + default: + return SYNC_SOURCE_NONE; + } +} + +enum gpio_pin_output_state dal_gpio_get_output_state( + const struct gpio *gpio) +{ + return gpio->output_state; +} + +void dal_gpio_close( + struct gpio *gpio) +{ + if (!gpio) + return; + + dal_gpio_service_close(gpio->service, &gpio->pin); + + gpio->mode = GPIO_MODE_UNKNOWN; +} + +/* + * @brief + * Creation and destruction + */ + +struct gpio *dal_gpio_create( + struct gpio_service *service, + enum gpio_id id, + uint32_t en, + enum gpio_pin_output_state output_state) +{ + struct gpio *gpio = kzalloc(sizeof(struct gpio), GFP_KERNEL); + + if (!gpio) { + ASSERT_CRITICAL(false); + return NULL; + } + + gpio->service = service; + gpio->pin = NULL; + gpio->id = id; + gpio->en = en; + gpio->mode = GPIO_MODE_UNKNOWN; + gpio->output_state = output_state; + + return gpio; +} + +void dal_gpio_destroy( + struct gpio **gpio) +{ + if (!gpio || !*gpio) { + ASSERT_CRITICAL(false); + return; + } + + dal_gpio_close(*gpio); + + kfree(*gpio); + + *gpio = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_regs.h b/drivers/gpu/drm/amd/display/dc/gpio/gpio_regs.h new file mode 100644 index 000000000000..5c5925299f8d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_regs.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_GPIO_REGS_H_ +#define DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_GPIO_REGS_H_ + +struct gpio_registers { + uint32_t MASK_reg; + uint32_t MASK_mask; + uint32_t MASK_shift; + uint32_t A_reg; + uint32_t A_mask; + uint32_t A_shift; + uint32_t EN_reg; + uint32_t EN_mask; + uint32_t EN_shift; + uint32_t Y_reg; + uint32_t Y_mask; + uint32_t Y_shift; +}; + + +#endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_GPIO_REGS_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c new file mode 100644 index 000000000000..80038e0e610f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c @@ -0,0 +1,591 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "dm_services.h" +#include "include/gpio_interface.h" +#include "include/gpio_service_interface.h" +#include "hw_translate.h" +#include "hw_factory.h" + +/* + * Header of this unit + */ + +#include "gpio_service.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "hw_gpio.h" + +/* + * @brief + * Public API. + */ + +struct gpio_service *dal_gpio_service_create( + enum dce_version dce_version_major, + enum dce_version dce_version_minor, + struct dc_context *ctx) +{ + struct gpio_service *service; + + uint32_t index_of_id; + + service = kzalloc(sizeof(struct gpio_service), GFP_KERNEL); + + if (!service) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (!dal_hw_translate_init(&service->translate, dce_version_major, + dce_version_minor)) { + BREAK_TO_DEBUGGER(); + goto failure_1; + } + + if (!dal_hw_factory_init(&service->factory, dce_version_major, + dce_version_minor)) { + BREAK_TO_DEBUGGER(); + goto failure_1; + } + + /* allocate and initialize business storage */ + { + const uint32_t bits_per_uint = sizeof(uint32_t) << 3; + + index_of_id = 0; + service->ctx = ctx; + + do { + uint32_t number_of_bits = + service->factory.number_of_pins[index_of_id]; + + uint32_t number_of_uints = + (number_of_bits + bits_per_uint - 1) / + bits_per_uint; + + uint32_t *slot; + + if (number_of_bits) { + uint32_t index_of_uint = 0; + + slot = kzalloc(number_of_uints * sizeof(uint32_t), + GFP_KERNEL); + + if (!slot) { + BREAK_TO_DEBUGGER(); + goto failure_2; + } + + do { + slot[index_of_uint] = 0; + + ++index_of_uint; + } while (index_of_uint < number_of_uints); + } else + slot = NULL; + + service->busyness[index_of_id] = slot; + + ++index_of_id; + } while (index_of_id < GPIO_ID_COUNT); + } + + return service; + +failure_2: + while (index_of_id) { + uint32_t *slot; + + --index_of_id; + + slot = service->busyness[index_of_id]; + + kfree(slot); + } + +failure_1: + kfree(service); + + return NULL; +} + +struct gpio *dal_gpio_service_create_irq( + struct gpio_service *service, + uint32_t offset, + uint32_t mask) +{ + enum gpio_id id; + uint32_t en; + + if (!service->translate.funcs->offset_to_id(offset, mask, &id, &en)) { + ASSERT_CRITICAL(false); + return NULL; + } + + return dal_gpio_create_irq(service, id, en); +} + +void dal_gpio_service_destroy( + struct gpio_service **ptr) +{ + if (!ptr || !*ptr) { + BREAK_TO_DEBUGGER(); + return; + } + + /* free business storage */ + { + uint32_t index_of_id = 0; + + do { + uint32_t *slot = (*ptr)->busyness[index_of_id]; + + kfree(slot); + + ++index_of_id; + } while (index_of_id < GPIO_ID_COUNT); + } + + kfree(*ptr); + + *ptr = NULL; +} + +/* + * @brief + * Private API. + */ + +static bool is_pin_busy( + const struct gpio_service *service, + enum gpio_id id, + uint32_t en) +{ + const uint32_t bits_per_uint = sizeof(uint32_t) << 3; + + const uint32_t *slot = service->busyness[id] + (en / bits_per_uint); + + return 0 != (*slot & (1 << (en % bits_per_uint))); +} + +static void set_pin_busy( + struct gpio_service *service, + enum gpio_id id, + uint32_t en) +{ + const uint32_t bits_per_uint = sizeof(uint32_t) << 3; + + service->busyness[id][en / bits_per_uint] |= + (1 << (en % bits_per_uint)); +} + +static void set_pin_free( + struct gpio_service *service, + enum gpio_id id, + uint32_t en) +{ + const uint32_t bits_per_uint = sizeof(uint32_t) << 3; + + service->busyness[id][en / bits_per_uint] &= + ~(1 << (en % bits_per_uint)); +} + +enum gpio_result dal_gpio_service_open( + struct gpio_service *service, + enum gpio_id id, + uint32_t en, + enum gpio_mode mode, + struct hw_gpio_pin **ptr) +{ + struct hw_gpio_pin *pin; + + if (!service->busyness[id]) { + ASSERT_CRITICAL(false); + return GPIO_RESULT_OPEN_FAILED; + } + + if (is_pin_busy(service, id, en)) { + ASSERT_CRITICAL(false); + return GPIO_RESULT_DEVICE_BUSY; + } + + switch (id) { + case GPIO_ID_DDC_DATA: + pin = service->factory.funcs->create_ddc_data( + service->ctx, id, en); + service->factory.funcs->define_ddc_registers(pin, en); + break; + case GPIO_ID_DDC_CLOCK: + pin = service->factory.funcs->create_ddc_clock( + service->ctx, id, en); + service->factory.funcs->define_ddc_registers(pin, en); + break; + case GPIO_ID_GENERIC: + pin = service->factory.funcs->create_generic( + service->ctx, id, en); + break; + case GPIO_ID_HPD: + pin = service->factory.funcs->create_hpd( + service->ctx, id, en); + service->factory.funcs->define_hpd_registers(pin, en); + break; + case GPIO_ID_SYNC: + pin = service->factory.funcs->create_sync( + service->ctx, id, en); + break; + case GPIO_ID_GSL: + pin = service->factory.funcs->create_gsl( + service->ctx, id, en); + break; + default: + ASSERT_CRITICAL(false); + return GPIO_RESULT_NON_SPECIFIC_ERROR; + } + + if (!pin) { + ASSERT_CRITICAL(false); + return GPIO_RESULT_NON_SPECIFIC_ERROR; + } + + if (!pin->funcs->open(pin, mode)) { + ASSERT_CRITICAL(false); + dal_gpio_service_close(service, &pin); + return GPIO_RESULT_OPEN_FAILED; + } + + set_pin_busy(service, id, en); + *ptr = pin; + return GPIO_RESULT_OK; +} + +void dal_gpio_service_close( + struct gpio_service *service, + struct hw_gpio_pin **ptr) +{ + struct hw_gpio_pin *pin; + + if (!ptr) { + ASSERT_CRITICAL(false); + return; + } + + pin = *ptr; + + if (pin) { + set_pin_free(service, pin->id, pin->en); + + pin->funcs->close(pin); + + pin->funcs->destroy(ptr); + } +} + + +enum dc_irq_source dal_irq_get_source( + const struct gpio *irq) +{ + enum gpio_id id = dal_gpio_get_id(irq); + + switch (id) { + case GPIO_ID_HPD: + return (enum dc_irq_source)(DC_IRQ_SOURCE_HPD1 + + dal_gpio_get_enum(irq)); + case GPIO_ID_GPIO_PAD: + return (enum dc_irq_source)(DC_IRQ_SOURCE_GPIOPAD0 + + dal_gpio_get_enum(irq)); + default: + return DC_IRQ_SOURCE_INVALID; + } +} + +enum dc_irq_source dal_irq_get_rx_source( + const struct gpio *irq) +{ + enum gpio_id id = dal_gpio_get_id(irq); + + switch (id) { + case GPIO_ID_HPD: + return (enum dc_irq_source)(DC_IRQ_SOURCE_HPD1RX + + dal_gpio_get_enum(irq)); + default: + return DC_IRQ_SOURCE_INVALID; + } +} + +enum gpio_result dal_irq_setup_hpd_filter( + struct gpio *irq, + struct gpio_hpd_config *config) +{ + struct gpio_config_data config_data; + + if (!config) + return GPIO_RESULT_INVALID_DATA; + + config_data.type = GPIO_CONFIG_TYPE_HPD; + config_data.config.hpd = *config; + + return dal_gpio_set_config(irq, &config_data); +} + +/* + * @brief + * Creation and destruction + */ + +struct gpio *dal_gpio_create_irq( + struct gpio_service *service, + enum gpio_id id, + uint32_t en) +{ + struct gpio *irq; + + switch (id) { + case GPIO_ID_HPD: + case GPIO_ID_GPIO_PAD: + break; + default: + ASSERT_CRITICAL(false); + return NULL; + } + + irq = dal_gpio_create( + service, id, en, GPIO_PIN_OUTPUT_STATE_DEFAULT); + + if (irq) + return irq; + + ASSERT_CRITICAL(false); + return NULL; +} + +void dal_gpio_destroy_irq( + struct gpio **irq) +{ + if (!irq || !*irq) { + ASSERT_CRITICAL(false); + return; + } + + dal_gpio_close(*irq); + dal_gpio_destroy(irq); + kfree(*irq); + + *irq = NULL; +} + +struct ddc *dal_gpio_create_ddc( + struct gpio_service *service, + uint32_t offset, + uint32_t mask, + struct gpio_ddc_hw_info *info) +{ + enum gpio_id id; + uint32_t en; + struct ddc *ddc; + + if (!service->translate.funcs->offset_to_id(offset, mask, &id, &en)) + return NULL; + + ddc = kzalloc(sizeof(struct ddc), GFP_KERNEL); + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + ddc->pin_data = dal_gpio_create( + service, GPIO_ID_DDC_DATA, en, GPIO_PIN_OUTPUT_STATE_DEFAULT); + + if (!ddc->pin_data) { + BREAK_TO_DEBUGGER(); + goto failure_1; + } + + ddc->pin_clock = dal_gpio_create( + service, GPIO_ID_DDC_CLOCK, en, GPIO_PIN_OUTPUT_STATE_DEFAULT); + + if (!ddc->pin_clock) { + BREAK_TO_DEBUGGER(); + goto failure_2; + } + + ddc->hw_info = *info; + + ddc->ctx = service->ctx; + + return ddc; + +failure_2: + dal_gpio_destroy(&ddc->pin_data); + +failure_1: + kfree(ddc); + + return NULL; +} + +void dal_gpio_destroy_ddc( + struct ddc **ddc) +{ + if (!ddc || !*ddc) { + BREAK_TO_DEBUGGER(); + return; + } + + dal_ddc_close(*ddc); + dal_gpio_destroy(&(*ddc)->pin_data); + dal_gpio_destroy(&(*ddc)->pin_clock); + kfree(*ddc); + + *ddc = NULL; +} + +enum gpio_result dal_ddc_open( + struct ddc *ddc, + enum gpio_mode mode, + enum gpio_ddc_config_type config_type) +{ + enum gpio_result result; + + struct gpio_config_data config_data; + struct hw_gpio *hw_data; + struct hw_gpio *hw_clock; + + result = dal_gpio_open_ex(ddc->pin_data, mode); + + if (result != GPIO_RESULT_OK) { + BREAK_TO_DEBUGGER(); + return result; + } + + result = dal_gpio_open_ex(ddc->pin_clock, mode); + + if (result != GPIO_RESULT_OK) { + BREAK_TO_DEBUGGER(); + goto failure; + } + + /* DDC clock and data pins should belong + * to the same DDC block id, + * we use the data pin to set the pad mode. */ + + if (mode == GPIO_MODE_INPUT) + /* this is from detect_sink_type, + * we need extra delay there */ + config_data.type = GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE; + else + config_data.type = GPIO_CONFIG_TYPE_DDC; + + config_data.config.ddc.type = config_type; + + hw_data = FROM_HW_GPIO_PIN(ddc->pin_data->pin); + hw_clock = FROM_HW_GPIO_PIN(ddc->pin_clock->pin); + + config_data.config.ddc.data_en_bit_present = hw_data->store.en != 0; + config_data.config.ddc.clock_en_bit_present = hw_clock->store.en != 0; + + result = dal_gpio_set_config(ddc->pin_data, &config_data); + + if (result == GPIO_RESULT_OK) + return result; + + BREAK_TO_DEBUGGER(); + + dal_gpio_close(ddc->pin_clock); + +failure: + dal_gpio_close(ddc->pin_data); + + return result; +} + +enum gpio_result dal_ddc_change_mode( + struct ddc *ddc, + enum gpio_mode mode) +{ + enum gpio_result result; + + enum gpio_mode original_mode = + dal_gpio_get_mode(ddc->pin_data); + + result = dal_gpio_change_mode(ddc->pin_data, mode); + + /* [anaumov] DAL2 code returns GPIO_RESULT_NON_SPECIFIC_ERROR + * in case of failures; + * set_mode() is so that, in case of failure, + * we must explicitly set original mode */ + + if (result != GPIO_RESULT_OK) + goto failure; + + result = dal_gpio_change_mode(ddc->pin_clock, mode); + + if (result == GPIO_RESULT_OK) + return result; + + dal_gpio_change_mode(ddc->pin_clock, original_mode); + +failure: + dal_gpio_change_mode(ddc->pin_data, original_mode); + + return result; +} + +enum gpio_ddc_line dal_ddc_get_line( + const struct ddc *ddc) +{ + return (enum gpio_ddc_line)dal_gpio_get_enum(ddc->pin_data); +} + +enum gpio_result dal_ddc_set_config( + struct ddc *ddc, + enum gpio_ddc_config_type config_type) +{ + struct gpio_config_data config_data; + + config_data.type = GPIO_CONFIG_TYPE_DDC; + + config_data.config.ddc.type = config_type; + config_data.config.ddc.data_en_bit_present = false; + config_data.config.ddc.clock_en_bit_present = false; + + return dal_gpio_set_config(ddc->pin_data, &config_data); +} + +void dal_ddc_close( + struct ddc *ddc) +{ + dal_gpio_close(ddc->pin_clock); + dal_gpio_close(ddc->pin_data); +} + diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.h b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.h new file mode 100644 index 000000000000..c7f3081f59cc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.h @@ -0,0 +1,56 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GPIO_SERVICE_H__ +#define __DAL_GPIO_SERVICE_H__ + +struct hw_translate; +struct hw_factory; + +struct gpio_service { + struct dc_context *ctx; + struct hw_translate translate; + struct hw_factory factory; + /* + * @brief + * Business storage. + * For each member of 'enum gpio_id', + * store array of bits (packed into uint32_t slots), + * index individual bit by 'en' value */ + uint32_t *busyness[GPIO_ID_COUNT]; +}; + +enum gpio_result dal_gpio_service_open( + struct gpio_service *service, + enum gpio_id id, + uint32_t en, + enum gpio_mode mode, + struct hw_gpio_pin **ptr); + +void dal_gpio_service_close( + struct gpio_service *service, + struct hw_gpio_pin **ptr); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hpd_regs.h b/drivers/gpu/drm/amd/display/dc/gpio/hpd_regs.h new file mode 100644 index 000000000000..dcfdd71b2304 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hpd_regs.h @@ -0,0 +1,79 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_HPD_REGS_H_ +#define DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_HPD_REGS_H_ + +#include "gpio_regs.h" + +#define ONE_MORE_0 1 +#define ONE_MORE_1 2 +#define ONE_MORE_2 3 +#define ONE_MORE_3 4 +#define ONE_MORE_4 5 +#define ONE_MORE_5 6 + + +#define HPD_GPIO_REG_LIST_ENTRY(type,cd,id) \ + .type ## _reg = REG(DC_GPIO_HPD_## type),\ + .type ## _mask = DC_GPIO_HPD_ ## type ## __DC_GPIO_HPD ## id ## _ ## type ## _MASK,\ + .type ## _shift = DC_GPIO_HPD_ ## type ## __DC_GPIO_HPD ## id ## _ ## type ## __SHIFT + +#define HPD_GPIO_REG_LIST(id) \ + {\ + HPD_GPIO_REG_LIST_ENTRY(MASK,cd,id),\ + HPD_GPIO_REG_LIST_ENTRY(A,cd,id),\ + HPD_GPIO_REG_LIST_ENTRY(EN,cd,id),\ + HPD_GPIO_REG_LIST_ENTRY(Y,cd,id)\ + } + +#define HPD_REG_LIST(id) \ + HPD_GPIO_REG_LIST(ONE_MORE_ ## id), \ + .int_status = REGI(DC_HPD_INT_STATUS, HPD, id),\ + .toggle_filt_cntl = REGI(DC_HPD_TOGGLE_FILT_CNTL, HPD, id) + + #define HPD_MASK_SH_LIST(mask_sh) \ + SF_HPD(DC_HPD_INT_STATUS, DC_HPD_SENSE_DELAYED, mask_sh),\ + SF_HPD(DC_HPD_INT_STATUS, DC_HPD_SENSE, mask_sh),\ + SF_HPD(DC_HPD_TOGGLE_FILT_CNTL, DC_HPD_CONNECT_INT_DELAY, mask_sh),\ + SF_HPD(DC_HPD_TOGGLE_FILT_CNTL, DC_HPD_DISCONNECT_INT_DELAY, mask_sh) + +struct hpd_registers { + struct gpio_registers gpio; + uint32_t int_status; + uint32_t toggle_filt_cntl; +}; + +struct hpd_sh_mask { + /* int_status */ + uint32_t DC_HPD_SENSE_DELAYED; + uint32_t DC_HPD_SENSE; + /* toggle_filt_cntl */ + uint32_t DC_HPD_CONNECT_INT_DELAY; + uint32_t DC_HPD_DISCONNECT_INT_DELAY; +}; + + +#endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_GPIO_HPD_REGS_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c new file mode 100644 index 000000000000..310f48965b27 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c @@ -0,0 +1,232 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/gpio_types.h" +#include "hw_gpio.h" +#include "hw_ddc.h" + +#include "reg_helper.h" +#include "gpio_regs.h" + + +#undef FN +#define FN(reg_name, field_name) \ + ddc->shifts->field_name, ddc->masks->field_name + +#define CTX \ + ddc->base.base.ctx +#define REG(reg)\ + (ddc->regs->reg) + +static void destruct( + struct hw_ddc *pin) +{ + dal_hw_gpio_destruct(&pin->base); +} + +static void destroy( + struct hw_gpio_pin **ptr) +{ + struct hw_ddc *pin = HW_DDC_FROM_BASE(*ptr); + + destruct(pin); + + kfree(pin); + + *ptr = NULL; +} + +static enum gpio_result set_config( + struct hw_gpio_pin *ptr, + const struct gpio_config_data *config_data) +{ + struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr); + struct hw_gpio *hw_gpio = NULL; + uint32_t regval; + uint32_t ddc_data_pd_en = 0; + uint32_t ddc_clk_pd_en = 0; + uint32_t aux_pad_mode = 0; + + hw_gpio = &ddc->base; + + if (hw_gpio == NULL) { + ASSERT_CRITICAL(false); + return GPIO_RESULT_NULL_HANDLE; + } + + regval = REG_GET_3(gpio.MASK_reg, + DC_GPIO_DDC1DATA_PD_EN, &ddc_data_pd_en, + DC_GPIO_DDC1CLK_PD_EN, &ddc_clk_pd_en, + AUX_PAD1_MODE, &aux_pad_mode); + + switch (config_data->config.ddc.type) { + case GPIO_DDC_CONFIG_TYPE_MODE_I2C: + /* On plug-in, there is a transient level on the pad + * which must be discharged through the internal pull-down. + * Enable internal pull-down, 2.5msec discharge time + * is required for detection of AUX mode */ + if (hw_gpio->base.en != GPIO_DDC_LINE_VIP_PAD) { + if (!ddc_data_pd_en || !ddc_clk_pd_en) { + + REG_SET_2(gpio.MASK_reg, regval, + DC_GPIO_DDC1DATA_PD_EN, 1, + DC_GPIO_DDC1CLK_PD_EN, 1); + + if (config_data->type == + GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) + msleep(3); + } + } else { + uint32_t reg2; + uint32_t sda_pd_dis = 0; + uint32_t scl_pd_dis = 0; + + reg2 = REG_GET_2(gpio.MASK_reg, + DC_GPIO_SDA_PD_DIS, &sda_pd_dis, + DC_GPIO_SCL_PD_DIS, &scl_pd_dis); + + if (sda_pd_dis) { + REG_SET(gpio.MASK_reg, regval, + DC_GPIO_SDA_PD_DIS, 0); + + if (config_data->type == + GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) + msleep(3); + } + + if (!scl_pd_dis) { + REG_SET(gpio.MASK_reg, regval, + DC_GPIO_SCL_PD_DIS, 1); + + if (config_data->type == + GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE) + msleep(3); + } + } + + if (aux_pad_mode) { + /* let pins to get de-asserted + * before setting pad to I2C mode */ + if (config_data->config.ddc.data_en_bit_present || + config_data->config.ddc.clock_en_bit_present) + /* [anaumov] in DAL2, there was + * dc_service_delay_in_microseconds(2000); */ + msleep(2); + + /* set the I2C pad mode */ + /* read the register again, + * some bits may have been changed */ + REG_UPDATE(gpio.MASK_reg, + AUX_PAD1_MODE, 0); + } + + return GPIO_RESULT_OK; + case GPIO_DDC_CONFIG_TYPE_MODE_AUX: + /* set the AUX pad mode */ + if (!aux_pad_mode) { + REG_SET(gpio.MASK_reg, regval, + AUX_PAD1_MODE, 1); + } + + return GPIO_RESULT_OK; + case GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT: + if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && + (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { + REG_UPDATE_3(ddc_setup, + DC_I2C_DDC1_ENABLE, 1, + DC_I2C_DDC1_EDID_DETECT_ENABLE, 1, + DC_I2C_DDC1_EDID_DETECT_MODE, 0); + return GPIO_RESULT_OK; + } + break; + case GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT: + if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && + (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { + REG_UPDATE_3(ddc_setup, + DC_I2C_DDC1_ENABLE, 1, + DC_I2C_DDC1_EDID_DETECT_ENABLE, 1, + DC_I2C_DDC1_EDID_DETECT_MODE, 1); + return GPIO_RESULT_OK; + } + break; + case GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING: + if ((hw_gpio->base.en >= GPIO_DDC_LINE_DDC1) && + (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA)) { + REG_UPDATE_2(ddc_setup, + DC_I2C_DDC1_ENABLE, 0, + DC_I2C_DDC1_EDID_DETECT_ENABLE, 0); + return GPIO_RESULT_OK; + } + break; + } + + BREAK_TO_DEBUGGER(); + + return GPIO_RESULT_NON_SPECIFIC_ERROR; +} + +static const struct hw_gpio_pin_funcs funcs = { + .destroy = destroy, + .open = dal_hw_gpio_open, + .get_value = dal_hw_gpio_get_value, + .set_value = dal_hw_gpio_set_value, + .set_config = set_config, + .change_mode = dal_hw_gpio_change_mode, + .close = dal_hw_gpio_close, +}; + +static void construct( + struct hw_ddc *ddc, + enum gpio_id id, + uint32_t en, + struct dc_context *ctx) +{ + dal_hw_gpio_construct(&ddc->base, id, en, ctx); + ddc->base.base.funcs = &funcs; +} + +struct hw_gpio_pin *dal_hw_ddc_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en) +{ + struct hw_ddc *pin; + + if ((en < GPIO_DDC_LINE_MIN) || (en > GPIO_DDC_LINE_MAX)) { + ASSERT_CRITICAL(false); + return NULL; + } + + pin = kzalloc(sizeof(struct hw_ddc), GFP_KERNEL); + if (!pin) { + ASSERT_CRITICAL(false); + return NULL; + } + + construct(pin, id, en, ctx); + return &pin->base.base; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.h b/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.h new file mode 100644 index 000000000000..9690e2a885d7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_DDC_H__ +#define __DAL_HW_DDC_H__ + +#include "ddc_regs.h" + +struct hw_ddc { + struct hw_gpio base; + const struct ddc_registers *regs; + const struct ddc_sh_mask *shifts; + const struct ddc_sh_mask *masks; +}; + +#define HW_DDC_FROM_BASE(hw_gpio) \ + container_of((HW_GPIO_FROM_BASE(hw_gpio)), struct hw_ddc, base) + +struct hw_gpio_pin *dal_hw_ddc_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c new file mode 100644 index 000000000000..87b580fa4bc9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c @@ -0,0 +1,107 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/gpio_types.h" + +/* + * Header of this unit + */ + +#include "hw_factory.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce80/hw_factory_dce80.h" +#include "dce110/hw_factory_dce110.h" +#include "dce120/hw_factory_dce120.h" +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "dcn10/hw_factory_dcn10.h" +#endif + +#include "diagnostics/hw_factory_diag.h" + +/* + * This unit + */ + +bool dal_hw_factory_init( + struct hw_factory *factory, + enum dce_version dce_version, + enum dce_environment dce_environment) +{ + if (IS_FPGA_MAXIMUS_DC(dce_environment)) { + dal_hw_factory_diag_fpga_init(factory); + return true; + } + + switch (dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + dal_hw_factory_dce80_init(factory); + return true; + + case DCE_VERSION_10_0: + dal_hw_factory_dce110_init(factory); + return true; + case DCE_VERSION_11_0: + case DCE_VERSION_11_2: + dal_hw_factory_dce110_init(factory); + return true; + case DCE_VERSION_12_0: + dal_hw_factory_dce120_init(factory); + return true; +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case DCN_VERSION_1_0: + dal_hw_factory_dcn10_init(factory); + return true; +#endif + + default: + ASSERT_CRITICAL(false); + return false; + } +} + +void dal_hw_factory_destroy( + struct dc_context *ctx, + struct hw_factory **factory) +{ + if (!factory || !*factory) { + BREAK_TO_DEBUGGER(); + return; + } + + kfree(*factory); + + *factory = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.h b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.h new file mode 100644 index 000000000000..6e4dd3521935 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.h @@ -0,0 +1,74 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_FACTORY_H__ +#define __DAL_HW_FACTORY_H__ + +struct hw_gpio_pin; +struct hw_hpd; + +struct hw_factory { + uint32_t number_of_pins[GPIO_ID_COUNT]; + + const struct hw_factory_funcs { + struct hw_gpio_pin *(*create_ddc_data)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_ddc_clock)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_generic)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_hpd)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_sync)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_gsl)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + void (*define_hpd_registers)( + struct hw_gpio_pin *pin, + uint32_t en); + void (*define_ddc_registers)( + struct hw_gpio_pin *pin, + uint32_t en); + } *funcs; +}; + +bool dal_hw_factory_init( + struct hw_factory *factory, + enum dce_version dce_version, + enum dce_environment dce_environment); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c new file mode 100644 index 000000000000..660510842ecf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c @@ -0,0 +1,203 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/gpio_types.h" +#include "hw_gpio.h" + +#include "reg_helper.h" +#include "gpio_regs.h" + +#undef FN +#define FN(reg_name, field_name) \ + gpio->regs->field_name ## _shift, gpio->regs->field_name ## _mask + +#define CTX \ + gpio->base.ctx +#define REG(reg)\ + (gpio->regs->reg) + +static void store_registers( + struct hw_gpio *gpio) +{ + REG_GET(MASK_reg, MASK, &gpio->store.mask); + REG_GET(A_reg, A, &gpio->store.a); + REG_GET(EN_reg, EN, &gpio->store.en); + /* TODO store GPIO_MUX_CONTROL if we ever use it */ +} + +static void restore_registers( + struct hw_gpio *gpio) +{ + REG_UPDATE(MASK_reg, MASK, gpio->store.mask); + REG_UPDATE(A_reg, A, gpio->store.a); + REG_UPDATE(EN_reg, EN, gpio->store.en); + /* TODO restore GPIO_MUX_CONTROL if we ever use it */ +} + +bool dal_hw_gpio_open( + struct hw_gpio_pin *ptr, + enum gpio_mode mode) +{ + struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); + + store_registers(pin); + + ptr->opened = (dal_hw_gpio_config_mode(pin, mode) == GPIO_RESULT_OK); + + return ptr->opened; +} + +enum gpio_result dal_hw_gpio_get_value( + const struct hw_gpio_pin *ptr, + uint32_t *value) +{ + const struct hw_gpio *gpio = FROM_HW_GPIO_PIN(ptr); + + enum gpio_result result = GPIO_RESULT_OK; + + switch (ptr->mode) { + case GPIO_MODE_INPUT: + case GPIO_MODE_OUTPUT: + case GPIO_MODE_HARDWARE: + case GPIO_MODE_FAST_OUTPUT: + REG_GET(Y_reg, Y, value); + break; + default: + result = GPIO_RESULT_NON_SPECIFIC_ERROR; + } + + return result; +} + +enum gpio_result dal_hw_gpio_set_value( + const struct hw_gpio_pin *ptr, + uint32_t value) +{ + struct hw_gpio *gpio = FROM_HW_GPIO_PIN(ptr); + + /* This is the public interface + * where the input comes from client, not shifted yet + * (because client does not know the shifts). */ + + switch (ptr->mode) { + case GPIO_MODE_OUTPUT: + REG_UPDATE(A_reg, A, value); + return GPIO_RESULT_OK; + case GPIO_MODE_FAST_OUTPUT: + /* We use (EN) to faster switch (used in DDC GPIO). + * So (A) is grounded, output is driven by (EN = 0) + * to pull the line down (output == 0) and (EN=1) + * then output is tri-state */ + REG_UPDATE(EN_reg, EN, ~value); + return GPIO_RESULT_OK; + default: + return GPIO_RESULT_NON_SPECIFIC_ERROR; + } +} + +enum gpio_result dal_hw_gpio_change_mode( + struct hw_gpio_pin *ptr, + enum gpio_mode mode) +{ + struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); + + return dal_hw_gpio_config_mode(pin, mode); +} + +void dal_hw_gpio_close( + struct hw_gpio_pin *ptr) +{ + struct hw_gpio *pin = FROM_HW_GPIO_PIN(ptr); + + restore_registers(pin); + + ptr->mode = GPIO_MODE_UNKNOWN; + ptr->opened = false; +} + +enum gpio_result dal_hw_gpio_config_mode( + struct hw_gpio *gpio, + enum gpio_mode mode) +{ + gpio->base.mode = mode; + + switch (mode) { + case GPIO_MODE_INPUT: + /* turn off output enable, act as input pin; + * program the pin as GPIO, mask out signal driven by HW */ + REG_UPDATE(EN_reg, EN, 0); + REG_UPDATE(MASK_reg, MASK, 1); + return GPIO_RESULT_OK; + case GPIO_MODE_OUTPUT: + /* turn on output enable, act as output pin; + * program the pin as GPIO, mask out signal driven by HW */ + REG_UPDATE(A_reg, A, 0); + REG_UPDATE(MASK_reg, MASK, 1); + return GPIO_RESULT_OK; + case GPIO_MODE_FAST_OUTPUT: + /* grounding the A register then use the EN register bit + * will have faster effect on the rise time */ + REG_UPDATE(A_reg, A, 0); + REG_UPDATE(MASK_reg, MASK, 1); + return GPIO_RESULT_OK; + case GPIO_MODE_HARDWARE: + /* program the pin as tri-state, pin is driven by HW */ + REG_UPDATE(MASK_reg, MASK, 0); + return GPIO_RESULT_OK; + case GPIO_MODE_INTERRUPT: + /* Interrupt mode supported only by HPD (IrqGpio) pins. */ + REG_UPDATE(MASK_reg, MASK, 0); + return GPIO_RESULT_OK; + default: + return GPIO_RESULT_NON_SPECIFIC_ERROR; + } +} + +void dal_hw_gpio_construct( + struct hw_gpio *pin, + enum gpio_id id, + uint32_t en, + struct dc_context *ctx) +{ + pin->base.ctx = ctx; + pin->base.id = id; + pin->base.en = en; + pin->base.mode = GPIO_MODE_UNKNOWN; + pin->base.opened = false; + + pin->store.mask = 0; + pin->store.a = 0; + pin->store.en = 0; + pin->store.mux = 0; + + pin->mux_supported = false; +} + +void dal_hw_gpio_destruct( + struct hw_gpio *pin) +{ + ASSERT(!pin->base.opened); +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.h b/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.h new file mode 100644 index 000000000000..bca0cef18ff9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.h @@ -0,0 +1,144 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_GPIO_H__ +#define __DAL_HW_GPIO_H__ + +#include "gpio_regs.h" + +#define FROM_HW_GPIO_PIN(ptr) \ + container_of((ptr), struct hw_gpio, base) + +struct addr_mask { + uint32_t addr; + uint32_t mask; +}; + +struct hw_gpio_pin { + const struct hw_gpio_pin_funcs *funcs; + enum gpio_id id; + uint32_t en; + enum gpio_mode mode; + bool opened; + struct dc_context *ctx; +}; + +struct hw_gpio_pin_funcs { + void (*destroy)( + struct hw_gpio_pin **ptr); + bool (*open)( + struct hw_gpio_pin *pin, + enum gpio_mode mode); + enum gpio_result (*get_value)( + const struct hw_gpio_pin *pin, + uint32_t *value); + enum gpio_result (*set_value)( + const struct hw_gpio_pin *pin, + uint32_t value); + enum gpio_result (*set_config)( + struct hw_gpio_pin *pin, + const struct gpio_config_data *config_data); + enum gpio_result (*change_mode)( + struct hw_gpio_pin *pin, + enum gpio_mode mode); + void (*close)( + struct hw_gpio_pin *pin); +}; + + +struct hw_gpio; + +/* Register indices are represented by member variables + * and are to be filled in by constructors of derived classes. + * These members permit the use of common code + * for programming registers, where the sequence is the same + * but register sets are different. + * Some GPIOs have HW mux which allows to choose + * what is the source of the signal in HW mode */ + +struct hw_gpio_pin_reg { + struct addr_mask DC_GPIO_DATA_MASK; + struct addr_mask DC_GPIO_DATA_A; + struct addr_mask DC_GPIO_DATA_EN; + struct addr_mask DC_GPIO_DATA_Y; +}; + +struct hw_gpio_mux_reg { + struct addr_mask GPIO_MUX_CONTROL; + struct addr_mask GPIO_MUX_STEREO_SEL; +}; + +struct hw_gpio { + struct hw_gpio_pin base; + + /* variables to save register value */ + struct { + uint32_t mask; + uint32_t a; + uint32_t en; + uint32_t mux; + } store; + + /* GPIO MUX support */ + bool mux_supported; + const struct gpio_registers *regs; +}; + +#define HW_GPIO_FROM_BASE(hw_gpio_pin) \ + container_of((hw_gpio_pin), struct hw_gpio, base) + +void dal_hw_gpio_construct( + struct hw_gpio *pin, + enum gpio_id id, + uint32_t en, + struct dc_context *ctx); + +bool dal_hw_gpio_open( + struct hw_gpio_pin *pin, + enum gpio_mode mode); + +enum gpio_result dal_hw_gpio_get_value( + const struct hw_gpio_pin *pin, + uint32_t *value); + +enum gpio_result dal_hw_gpio_config_mode( + struct hw_gpio *pin, + enum gpio_mode mode); + +void dal_hw_gpio_destruct( + struct hw_gpio *pin); + +enum gpio_result dal_hw_gpio_set_value( + const struct hw_gpio_pin *ptr, + uint32_t value); + +enum gpio_result dal_hw_gpio_change_mode( + struct hw_gpio_pin *ptr, + enum gpio_mode mode); + +void dal_hw_gpio_close( + struct hw_gpio_pin *ptr); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_hpd.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_hpd.c new file mode 100644 index 000000000000..784feccc5853 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_hpd.c @@ -0,0 +1,162 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/gpio_types.h" +#include "hw_gpio.h" +#include "hw_hpd.h" + +#include "reg_helper.h" +#include "hpd_regs.h" + +#undef FN +#define FN(reg_name, field_name) \ + hpd->shifts->field_name, hpd->masks->field_name + +#define CTX \ + hpd->base.base.ctx +#define REG(reg)\ + (hpd->regs->reg) + +static void dal_hw_hpd_construct( + struct hw_hpd *pin, + enum gpio_id id, + uint32_t en, + struct dc_context *ctx) +{ + dal_hw_gpio_construct(&pin->base, id, en, ctx); +} + +static void dal_hw_hpd_destruct( + struct hw_hpd *pin) +{ + dal_hw_gpio_destruct(&pin->base); +} + + +static void destruct( + struct hw_hpd *hpd) +{ + dal_hw_hpd_destruct(hpd); +} + +static void destroy( + struct hw_gpio_pin **ptr) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(*ptr); + + destruct(hpd); + + kfree(hpd); + + *ptr = NULL; +} + +static enum gpio_result get_value( + const struct hw_gpio_pin *ptr, + uint32_t *value) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(ptr); + uint32_t hpd_delayed = 0; + + /* in Interrupt mode we ask for SENSE bit */ + + if (ptr->mode == GPIO_MODE_INTERRUPT) { + + REG_GET(int_status, + DC_HPD_SENSE_DELAYED, &hpd_delayed); + + *value = hpd_delayed; + return GPIO_RESULT_OK; + } + + /* in any other modes, operate as normal GPIO */ + + return dal_hw_gpio_get_value(ptr, value); +} + +static enum gpio_result set_config( + struct hw_gpio_pin *ptr, + const struct gpio_config_data *config_data) +{ + struct hw_hpd *hpd = HW_HPD_FROM_BASE(ptr); + + if (!config_data) + return GPIO_RESULT_INVALID_DATA; + + REG_UPDATE_2(toggle_filt_cntl, + DC_HPD_CONNECT_INT_DELAY, config_data->config.hpd.delay_on_connect / 10, + DC_HPD_DISCONNECT_INT_DELAY, config_data->config.hpd.delay_on_disconnect / 10); + + return GPIO_RESULT_OK; +} + +static const struct hw_gpio_pin_funcs funcs = { + .destroy = destroy, + .open = dal_hw_gpio_open, + .get_value = get_value, + .set_value = dal_hw_gpio_set_value, + .set_config = set_config, + .change_mode = dal_hw_gpio_change_mode, + .close = dal_hw_gpio_close, +}; + +static void construct( + struct hw_hpd *hpd, + enum gpio_id id, + uint32_t en, + struct dc_context *ctx) +{ + dal_hw_hpd_construct(hpd, id, en, ctx); + hpd->base.base.funcs = &funcs; +} + +struct hw_gpio_pin *dal_hw_hpd_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en) +{ + struct hw_hpd *hpd; + + if (id != GPIO_ID_HPD) { + ASSERT_CRITICAL(false); + return NULL; + } + + if ((en < GPIO_HPD_MIN) || (en > GPIO_HPD_MAX)) { + ASSERT_CRITICAL(false); + return NULL; + } + + hpd = kzalloc(sizeof(struct hw_hpd), GFP_KERNEL); + if (!hpd) { + ASSERT_CRITICAL(false); + return NULL; + } + + construct(hpd, id, en, ctx); + return &hpd->base.base; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_hpd.h b/drivers/gpu/drm/amd/display/dc/gpio/hw_hpd.h new file mode 100644 index 000000000000..4ab7a208f781 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_hpd.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_HPD_H__ +#define __DAL_HW_HPD_H__ + +#include "hpd_regs.h" + +struct hw_hpd { + struct hw_gpio base; + const struct hpd_registers *regs; + const struct hpd_sh_mask *shifts; + const struct hpd_sh_mask *masks; +}; + +#define HW_HPD_FROM_BASE(hw_gpio) \ + container_of((HW_GPIO_FROM_BASE(hw_gpio)), struct hw_hpd, base) + +struct hw_gpio_pin *dal_hw_hpd_create( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c new file mode 100644 index 000000000000..0ae8ace25739 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c @@ -0,0 +1,90 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/gpio_types.h" + +/* + * Header of this unit + */ + +#include "hw_translate.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce80/hw_translate_dce80.h" +#include "dce110/hw_translate_dce110.h" +#include "dce120/hw_translate_dce120.h" +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "dcn10/hw_translate_dcn10.h" +#endif + +#include "diagnostics/hw_translate_diag.h" + +/* + * This unit + */ + +bool dal_hw_translate_init( + struct hw_translate *translate, + enum dce_version dce_version, + enum dce_environment dce_environment) +{ + if (IS_FPGA_MAXIMUS_DC(dce_environment)) { + dal_hw_translate_diag_fpga_init(translate); + return true; + } + + switch (dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + dal_hw_translate_dce80_init(translate); + return true; + case DCE_VERSION_10_0: + case DCE_VERSION_11_0: + case DCE_VERSION_11_2: + dal_hw_translate_dce110_init(translate); + return true; + case DCE_VERSION_12_0: + dal_hw_translate_dce120_init(translate); + return true; +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case DCN_VERSION_1_0: + dal_hw_translate_dcn10_init(translate); + return true; +#endif + + default: + BREAK_TO_DEBUGGER(); + return false; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h new file mode 100644 index 000000000000..3a7d89ca1605 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h @@ -0,0 +1,50 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_TRANSLATE_H__ +#define __DAL_HW_TRANSLATE_H__ + +struct hw_translate_funcs { + bool (*offset_to_id)( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en); + bool (*id_to_offset)( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info); +}; + +struct hw_translate { + const struct hw_translate_funcs *funcs; +}; + +bool dal_hw_translate_init( + struct hw_translate *translate, + enum dce_version dce_version, + enum dce_environment dce_environment); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/Makefile b/drivers/gpu/drm/amd/display/dc/i2caux/Makefile new file mode 100644 index 000000000000..55603400acd9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/Makefile @@ -0,0 +1,78 @@ +# +# Makefile for the 'i2c' sub-component of DAL. +# It provides the control and status of HW i2c engine of the adapter. + +I2CAUX = aux_engine.o engine_base.o i2caux.o i2c_engine.o \ + i2c_generic_hw_engine.o i2c_hw_engine.o i2c_sw_engine.o + +AMD_DAL_I2CAUX = $(addprefix $(AMDDALPATH)/dc/i2caux/,$(I2CAUX)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX) + +############################################################################### +# DCE 8x family +############################################################################### +I2CAUX_DCE80 = i2caux_dce80.o i2c_hw_engine_dce80.o \ + i2c_sw_engine_dce80.o + +AMD_DAL_I2CAUX_DCE80 = $(addprefix $(AMDDALPATH)/dc/i2caux/dce80/,$(I2CAUX_DCE80)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX_DCE80) + +############################################################################### +# DCE 100 family +############################################################################### +I2CAUX_DCE100 = i2caux_dce100.o + +AMD_DAL_I2CAUX_DCE100 = $(addprefix $(AMDDALPATH)/dc/i2caux/dce100/,$(I2CAUX_DCE100)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX_DCE100) + +############################################################################### +# DCE 110 family +############################################################################### +I2CAUX_DCE110 = i2caux_dce110.o i2c_sw_engine_dce110.o i2c_hw_engine_dce110.o \ + aux_engine_dce110.o + +AMD_DAL_I2CAUX_DCE110 = $(addprefix $(AMDDALPATH)/dc/i2caux/dce110/,$(I2CAUX_DCE110)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX_DCE110) + +############################################################################### +# DCE 112 family +############################################################################### +I2CAUX_DCE112 = i2caux_dce112.o + +AMD_DAL_I2CAUX_DCE112 = $(addprefix $(AMDDALPATH)/dc/i2caux/dce112/,$(I2CAUX_DCE112)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX_DCE112) + +############################################################################### +# DCN 1.0 family +############################################################################### +ifdef CONFIG_DRM_AMD_DC_DCN1_0 +I2CAUX_DCN1 = i2caux_dcn10.o + +AMD_DAL_I2CAUX_DCN1 = $(addprefix $(AMDDALPATH)/dc/i2caux/dcn10/,$(I2CAUX_DCN1)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX_DCN1) +endif + +############################################################################### +# DCE 120 family +############################################################################### +I2CAUX_DCE120 = i2caux_dce120.o + +AMD_DAL_I2CAUX_DCE120 = $(addprefix $(AMDDALPATH)/dc/i2caux/dce120/,$(I2CAUX_DCE120)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX_DCE120) + +############################################################################### +# Diagnostics on FPGA +############################################################################### +I2CAUX_DIAG = i2caux_diag.o + +AMD_DAL_I2CAUX_DIAG = $(addprefix $(AMDDALPATH)/dc/i2caux/diagnostics/,$(I2CAUX_DIAG)) + +AMD_DISPLAY_FILES += $(AMD_DAL_I2CAUX_DIAG) + diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.c b/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.c new file mode 100644 index 000000000000..fc7a7d4ebca5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.c @@ -0,0 +1,571 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "engine.h" + +/* + * Header of this unit + */ + +#include "aux_engine.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "include/link_service_types.h" + +/* + * This unit + */ + +enum { + AUX_INVALID_REPLY_RETRY_COUNTER = 1, + AUX_TIMED_OUT_RETRY_COUNTER = 2, + AUX_DEFER_RETRY_COUNTER = 6 +}; + +#define FROM_ENGINE(ptr) \ + container_of((ptr), struct aux_engine, base) + +enum i2caux_engine_type dal_aux_engine_get_engine_type( + const struct engine *engine) +{ + return I2CAUX_ENGINE_TYPE_AUX; +} + +bool dal_aux_engine_acquire( + struct engine *engine, + struct ddc *ddc) +{ + struct aux_engine *aux_engine = FROM_ENGINE(engine); + + enum gpio_result result; + if (aux_engine->funcs->is_engine_available) { + /*check whether SW could use the engine*/ + if (!aux_engine->funcs->is_engine_available(aux_engine)) { + return false; + } + } + + result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, + GPIO_DDC_CONFIG_TYPE_MODE_AUX); + + if (result != GPIO_RESULT_OK) + return false; + + if (!aux_engine->funcs->acquire_engine(aux_engine)) { + dal_ddc_close(ddc); + return false; + } + + engine->ddc = ddc; + + return true; +} + +struct read_command_context { + uint8_t *buffer; + uint32_t current_read_length; + uint32_t offset; + enum i2caux_transaction_status status; + + struct aux_request_transaction_data request; + struct aux_reply_transaction_data reply; + + uint8_t returned_byte; + + uint32_t timed_out_retry_aux; + uint32_t invalid_reply_retry_aux; + uint32_t defer_retry_aux; + uint32_t defer_retry_i2c; + uint32_t invalid_reply_retry_aux_on_ack; + + bool transaction_complete; + bool operation_succeeded; +}; + +static void process_read_reply( + struct aux_engine *engine, + struct read_command_context *ctx) +{ + engine->funcs->process_channel_reply(engine, &ctx->reply); + + switch (ctx->reply.status) { + case AUX_TRANSACTION_REPLY_AUX_ACK: + ctx->defer_retry_aux = 0; + if (ctx->returned_byte > ctx->current_read_length) { + ctx->status = + I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; + ctx->operation_succeeded = false; + } else if (ctx->returned_byte < ctx->current_read_length) { + ctx->current_read_length -= ctx->returned_byte; + + ctx->offset += ctx->returned_byte; + + ++ctx->invalid_reply_retry_aux_on_ack; + + if (ctx->invalid_reply_retry_aux_on_ack > + AUX_INVALID_REPLY_RETRY_COUNTER) { + ctx->status = + I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; + ctx->operation_succeeded = false; + } + } else { + ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; + ctx->transaction_complete = true; + ctx->operation_succeeded = true; + } + break; + case AUX_TRANSACTION_REPLY_AUX_NACK: + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; + ctx->operation_succeeded = false; + break; + case AUX_TRANSACTION_REPLY_AUX_DEFER: + ++ctx->defer_retry_aux; + + if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) { + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + ctx->operation_succeeded = false; + } + break; + case AUX_TRANSACTION_REPLY_I2C_DEFER: + ctx->defer_retry_aux = 0; + + ++ctx->defer_retry_i2c; + + if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) { + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + ctx->operation_succeeded = false; + } + break; + default: + ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; + ctx->operation_succeeded = false; + } +} + +static void process_read_request( + struct aux_engine *engine, + struct read_command_context *ctx) +{ + enum aux_channel_operation_result operation_result; + + engine->funcs->submit_channel_request(engine, &ctx->request); + + operation_result = engine->funcs->get_channel_status( + engine, &ctx->returned_byte); + + switch (operation_result) { + case AUX_CHANNEL_OPERATION_SUCCEEDED: + if (ctx->returned_byte > ctx->current_read_length) { + ctx->status = + I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; + ctx->operation_succeeded = false; + } else { + ctx->timed_out_retry_aux = 0; + ctx->invalid_reply_retry_aux = 0; + + ctx->reply.length = ctx->returned_byte; + ctx->reply.data = ctx->buffer; + + process_read_reply(engine, ctx); + } + break; + case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: + ++ctx->invalid_reply_retry_aux; + + if (ctx->invalid_reply_retry_aux > + AUX_INVALID_REPLY_RETRY_COUNTER) { + ctx->status = + I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; + ctx->operation_succeeded = false; + } else + udelay(400); + break; + case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: + ++ctx->timed_out_retry_aux; + + if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + ctx->operation_succeeded = false; + } else { + /* DP 1.2a, table 2-58: + * "S3: AUX Request CMD PENDING: + * retry 3 times, with 400usec wait on each" + * The HW timeout is set to 550usec, + * so we should not wait here */ + } + break; + default: + ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; + ctx->operation_succeeded = false; + } +} + +static bool read_command( + struct aux_engine *engine, + struct i2caux_transaction_request *request, + bool middle_of_transaction) +{ + struct read_command_context ctx; + + ctx.buffer = request->payload.data; + ctx.current_read_length = request->payload.length; + ctx.offset = 0; + ctx.timed_out_retry_aux = 0; + ctx.invalid_reply_retry_aux = 0; + ctx.defer_retry_aux = 0; + ctx.defer_retry_i2c = 0; + ctx.invalid_reply_retry_aux_on_ack = 0; + ctx.transaction_complete = false; + ctx.operation_succeeded = true; + + if (request->payload.address_space == + I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { + ctx.request.type = AUX_TRANSACTION_TYPE_DP; + ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ; + ctx.request.address = request->payload.address; + } else if (request->payload.address_space == + I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { + ctx.request.type = AUX_TRANSACTION_TYPE_I2C; + ctx.request.action = middle_of_transaction ? + I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_READ; + ctx.request.address = request->payload.address >> 1; + } else { + /* in DAL2, there was no return in such case */ + BREAK_TO_DEBUGGER(); + return false; + } + + ctx.request.delay = 0; + + do { + memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length); + + ctx.request.data = ctx.buffer + ctx.offset; + ctx.request.length = ctx.current_read_length; + + process_read_request(engine, &ctx); + + request->status = ctx.status; + + if (ctx.operation_succeeded && !ctx.transaction_complete) + if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) + msleep(engine->delay); + } while (ctx.operation_succeeded && !ctx.transaction_complete); + + return ctx.operation_succeeded; +} + +struct write_command_context { + bool mot; + + uint8_t *buffer; + uint32_t current_write_length; + enum i2caux_transaction_status status; + + struct aux_request_transaction_data request; + struct aux_reply_transaction_data reply; + + uint8_t returned_byte; + + uint32_t timed_out_retry_aux; + uint32_t invalid_reply_retry_aux; + uint32_t defer_retry_aux; + uint32_t defer_retry_i2c; + uint32_t max_defer_retry; + uint32_t ack_m_retry; + + uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE]; + + bool transaction_complete; + bool operation_succeeded; +}; + +static void process_write_reply( + struct aux_engine *engine, + struct write_command_context *ctx) +{ + engine->funcs->process_channel_reply(engine, &ctx->reply); + + switch (ctx->reply.status) { + case AUX_TRANSACTION_REPLY_AUX_ACK: + ctx->operation_succeeded = true; + + if (ctx->returned_byte) { + ctx->request.action = ctx->mot ? + I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; + + ctx->current_write_length = 0; + + ++ctx->ack_m_retry; + + if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) { + ctx->status = + I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + ctx->operation_succeeded = false; + } else + udelay(300); + } else { + ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; + ctx->defer_retry_aux = 0; + ctx->ack_m_retry = 0; + ctx->transaction_complete = true; + } + break; + case AUX_TRANSACTION_REPLY_AUX_NACK: + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; + ctx->operation_succeeded = false; + break; + case AUX_TRANSACTION_REPLY_AUX_DEFER: + ++ctx->defer_retry_aux; + + if (ctx->defer_retry_aux > ctx->max_defer_retry) { + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + ctx->operation_succeeded = false; + } + break; + case AUX_TRANSACTION_REPLY_I2C_DEFER: + ctx->defer_retry_aux = 0; + ctx->current_write_length = 0; + + ctx->request.action = ctx->mot ? + I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; + + ++ctx->defer_retry_i2c; + + if (ctx->defer_retry_i2c > ctx->max_defer_retry) { + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + ctx->operation_succeeded = false; + } + break; + default: + ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; + ctx->operation_succeeded = false; + } +} + +static void process_write_request( + struct aux_engine *engine, + struct write_command_context *ctx) +{ + enum aux_channel_operation_result operation_result; + + engine->funcs->submit_channel_request(engine, &ctx->request); + + operation_result = engine->funcs->get_channel_status( + engine, &ctx->returned_byte); + + switch (operation_result) { + case AUX_CHANNEL_OPERATION_SUCCEEDED: + ctx->timed_out_retry_aux = 0; + ctx->invalid_reply_retry_aux = 0; + + ctx->reply.length = ctx->returned_byte; + ctx->reply.data = ctx->reply_data; + + process_write_reply(engine, ctx); + break; + case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: + ++ctx->invalid_reply_retry_aux; + + if (ctx->invalid_reply_retry_aux > + AUX_INVALID_REPLY_RETRY_COUNTER) { + ctx->status = + I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; + ctx->operation_succeeded = false; + } else + udelay(400); + break; + case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: + ++ctx->timed_out_retry_aux; + + if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { + ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + ctx->operation_succeeded = false; + } else { + /* DP 1.2a, table 2-58: + * "S3: AUX Request CMD PENDING: + * retry 3 times, with 400usec wait on each" + * The HW timeout is set to 550usec, + * so we should not wait here */ + } + break; + default: + ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; + ctx->operation_succeeded = false; + } +} + +static bool write_command( + struct aux_engine *engine, + struct i2caux_transaction_request *request, + bool middle_of_transaction) +{ + struct write_command_context ctx; + + ctx.mot = middle_of_transaction; + ctx.buffer = request->payload.data; + ctx.current_write_length = request->payload.length; + ctx.timed_out_retry_aux = 0; + ctx.invalid_reply_retry_aux = 0; + ctx.defer_retry_aux = 0; + ctx.defer_retry_i2c = 0; + ctx.ack_m_retry = 0; + ctx.transaction_complete = false; + ctx.operation_succeeded = true; + + if (request->payload.address_space == + I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { + ctx.request.type = AUX_TRANSACTION_TYPE_DP; + ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE; + ctx.request.address = request->payload.address; + } else if (request->payload.address_space == + I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { + ctx.request.type = AUX_TRANSACTION_TYPE_I2C; + ctx.request.action = middle_of_transaction ? + I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_WRITE; + ctx.request.address = request->payload.address >> 1; + } else { + /* in DAL2, there was no return in such case */ + BREAK_TO_DEBUGGER(); + return false; + } + + ctx.request.delay = 0; + + ctx.max_defer_retry = + (engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ? + engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER; + + do { + ctx.request.data = ctx.buffer; + ctx.request.length = ctx.current_write_length; + + process_write_request(engine, &ctx); + + request->status = ctx.status; + + if (ctx.operation_succeeded && !ctx.transaction_complete) + if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) + msleep(engine->delay); + } while (ctx.operation_succeeded && !ctx.transaction_complete); + + return ctx.operation_succeeded; +} + +static bool end_of_transaction_command( + struct aux_engine *engine, + struct i2caux_transaction_request *request) +{ + struct i2caux_transaction_request dummy_request; + uint8_t dummy_data; + + /* [tcheng] We only need to send the stop (read with MOT = 0) + * for I2C-over-Aux, not native AUX */ + + if (request->payload.address_space != + I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) + return false; + + dummy_request.operation = request->operation; + dummy_request.payload.address_space = request->payload.address_space; + dummy_request.payload.address = request->payload.address; + + /* + * Add a dummy byte due to some receiver quirk + * where one byte is sent along with MOT = 0. + * Ideally this should be 0. + */ + + dummy_request.payload.length = 0; + dummy_request.payload.data = &dummy_data; + + if (request->operation == I2CAUX_TRANSACTION_READ) + return read_command(engine, &dummy_request, false); + else + return write_command(engine, &dummy_request, false); + + /* according Syed, it does not need now DoDummyMOT */ +} + +bool dal_aux_engine_submit_request( + struct engine *engine, + struct i2caux_transaction_request *request, + bool middle_of_transaction) +{ + struct aux_engine *aux_engine = FROM_ENGINE(engine); + + bool result; + bool mot_used = true; + + switch (request->operation) { + case I2CAUX_TRANSACTION_READ: + result = read_command(aux_engine, request, mot_used); + break; + case I2CAUX_TRANSACTION_WRITE: + result = write_command(aux_engine, request, mot_used); + break; + default: + result = false; + } + + /* [tcheng] + * need to send stop for the last transaction to free up the AUX + * if the above command fails, this would be the last transaction */ + + if (!middle_of_transaction || !result) + end_of_transaction_command(aux_engine, request); + + /* mask AUX interrupt */ + + return result; +} + +void dal_aux_engine_construct( + struct aux_engine *engine, + struct dc_context *ctx) +{ + dal_i2caux_construct_engine(&engine->base, ctx); + engine->delay = 0; + engine->max_defer_write_retry = 0; +} + +void dal_aux_engine_destruct( + struct aux_engine *engine) +{ + dal_i2caux_destruct_engine(&engine->base); +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.h b/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.h new file mode 100644 index 000000000000..8e71324ccb10 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.h @@ -0,0 +1,119 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_AUX_ENGINE_H__ +#define __DAL_AUX_ENGINE_H__ + +enum aux_transaction_type { + AUX_TRANSACTION_TYPE_DP, + AUX_TRANSACTION_TYPE_I2C +}; + +struct aux_request_transaction_data { + enum aux_transaction_type type; + enum i2caux_transaction_action action; + /* 20-bit AUX channel transaction address */ + uint32_t address; + /* delay, in 100-microsecond units */ + uint8_t delay; + uint32_t length; + uint8_t *data; +}; + +enum aux_transaction_reply { + AUX_TRANSACTION_REPLY_AUX_ACK = 0x00, + AUX_TRANSACTION_REPLY_AUX_NACK = 0x01, + AUX_TRANSACTION_REPLY_AUX_DEFER = 0x02, + + AUX_TRANSACTION_REPLY_I2C_ACK = 0x00, + AUX_TRANSACTION_REPLY_I2C_NACK = 0x10, + AUX_TRANSACTION_REPLY_I2C_DEFER = 0x20, + + AUX_TRANSACTION_REPLY_INVALID = 0xFF +}; + +struct aux_reply_transaction_data { + enum aux_transaction_reply status; + uint32_t length; + uint8_t *data; +}; + +enum aux_channel_operation_result { + AUX_CHANNEL_OPERATION_SUCCEEDED, + AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN, + AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY, + AUX_CHANNEL_OPERATION_FAILED_TIMEOUT +}; + +struct aux_engine; + +struct aux_engine_funcs { + void (*destroy)( + struct aux_engine **ptr); + bool (*acquire_engine)( + struct aux_engine *engine); + void (*configure)( + struct aux_engine *engine, + union aux_config cfg); + void (*submit_channel_request)( + struct aux_engine *engine, + struct aux_request_transaction_data *request); + void (*process_channel_reply)( + struct aux_engine *engine, + struct aux_reply_transaction_data *reply); + enum aux_channel_operation_result (*get_channel_status)( + struct aux_engine *engine, + uint8_t *returned_bytes); + bool (*is_engine_available) ( + struct aux_engine *engine); +}; + +struct aux_engine { + struct engine base; + const struct aux_engine_funcs *funcs; + /* following values are expressed in milliseconds */ + uint32_t delay; + uint32_t max_defer_write_retry; + + bool acquire_reset; +}; + +void dal_aux_engine_construct( + struct aux_engine *engine, + struct dc_context *ctx); + +void dal_aux_engine_destruct( + struct aux_engine *engine); +bool dal_aux_engine_submit_request( + struct engine *ptr, + struct i2caux_transaction_request *request, + bool middle_of_transaction); +bool dal_aux_engine_acquire( + struct engine *ptr, + struct ddc *ddc); +enum i2caux_engine_type dal_aux_engine_get_engine_type( + const struct engine *engine); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce100/i2caux_dce100.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce100/i2caux_dce100.c new file mode 100644 index 000000000000..e8d3781deaed --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce100/i2caux_dce100.c @@ -0,0 +1,105 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/i2caux_interface.h" +#include "../i2caux.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "../i2c_hw_engine.h" + +#include "../dce110/aux_engine_dce110.h" +#include "../dce110/i2c_hw_engine_dce110.h" +#include "../dce110/i2caux_dce110.h" + +#include "dce/dce_10_0_d.h" +#include "dce/dce_10_0_sh_mask.h" + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +#define aux_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +#define hw_engine_regs(id)\ +{\ + I2C_HW_ENGINE_COMMON_REG_LIST(id) \ +} + +static const struct dce110_aux_registers dce100_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5), +}; + +static const struct dce110_i2c_hw_engine_registers dce100_hw_engine_regs[] = { + hw_engine_regs(1), + hw_engine_regs(2), + hw_engine_regs(3), + hw_engine_regs(4), + hw_engine_regs(5), + hw_engine_regs(6) +}; + +static const struct dce110_i2c_hw_engine_shift i2c_shift = { + I2C_COMMON_MASK_SH_LIST_DCE100(__SHIFT) +}; + +static const struct dce110_i2c_hw_engine_mask i2c_mask = { + I2C_COMMON_MASK_SH_LIST_DCE100(_MASK) +}; + +struct i2caux *dal_i2caux_dce100_create( + struct dc_context *ctx) +{ + struct i2caux_dce110 *i2caux_dce110 = + kzalloc(sizeof(struct i2caux_dce110), GFP_KERNEL); + + if (!i2caux_dce110) { + ASSERT_CRITICAL(false); + return NULL; + } + + dal_i2caux_dce110_construct(i2caux_dce110, + ctx, + dce100_aux_regs, + dce100_hw_engine_regs, + &i2c_shift, + &i2c_mask); + return &i2caux_dce110->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce100/i2caux_dce100.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce100/i2caux_dce100.h new file mode 100644 index 000000000000..2b508d3e0ef4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce100/i2caux_dce100.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DCE100_H__ +#define __DAL_I2C_AUX_DCE100_H__ + +struct i2caux *dal_i2caux_dce100_create( + struct dc_context *ctx); + +#endif /* __DAL_I2C_AUX_DCE100_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.c new file mode 100644 index 000000000000..81f9f3e34c10 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.c @@ -0,0 +1,470 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../aux_engine.h" + +/* + * Header of this unit + */ + +#include "aux_engine_dce110.h" + +/* + * Post-requisites: headers required by this unit + */ +#include "dce/dce_11_0_sh_mask.h" + +#define CTX \ + aux110->base.base.ctx +#define REG(reg_name)\ + (aux110->regs->reg_name) +#include "reg_helper.h" + +/* + * This unit + */ + +/* + * @brief + * Cast 'struct aux_engine *' + * to 'struct aux_engine_dce110 *' + */ +#define FROM_AUX_ENGINE(ptr) \ + container_of((ptr), struct aux_engine_dce110, base) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct aux_engine_dce110 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_AUX_ENGINE(container_of((ptr), struct aux_engine, base)) + +static void release_engine( + struct engine *engine) +{ + struct aux_engine_dce110 *aux110 = FROM_ENGINE(engine); + + REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1); +} + +static void destruct( + struct aux_engine_dce110 *engine); + +static void destroy( + struct aux_engine **aux_engine) +{ + struct aux_engine_dce110 *engine = FROM_AUX_ENGINE(*aux_engine); + + destruct(engine); + + kfree(engine); + + *aux_engine = NULL; +} + +#define SW_CAN_ACCESS_AUX 1 +#define DMCU_CAN_ACCESS_AUX 2 + +static bool is_engine_available( + struct aux_engine *engine) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + uint32_t value = REG_READ(AUX_ARB_CONTROL); + uint32_t field = get_reg_field_value( + value, + AUX_ARB_CONTROL, + AUX_REG_RW_CNTL_STATUS); + + return (field != DMCU_CAN_ACCESS_AUX); +} +static bool acquire_engine( + struct aux_engine *engine) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + uint32_t value = REG_READ(AUX_ARB_CONTROL); + uint32_t field = get_reg_field_value( + value, + AUX_ARB_CONTROL, + AUX_REG_RW_CNTL_STATUS); + if (field == DMCU_CAN_ACCESS_AUX) + return false; + /* enable AUX before request SW to access AUX */ + value = REG_READ(AUX_CONTROL); + field = get_reg_field_value(value, + AUX_CONTROL, + AUX_EN); + + if (field == 0) { + set_reg_field_value( + value, + 1, + AUX_CONTROL, + AUX_EN); + + if (REG(AUX_RESET_MASK)) { + /*DP_AUX block as part of the enable sequence*/ + set_reg_field_value( + value, + 1, + AUX_CONTROL, + AUX_RESET); + } + + REG_WRITE(AUX_CONTROL, value); + + if (REG(AUX_RESET_MASK)) { + /*poll HW to make sure reset it done*/ + + REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 1, + 1, 11); + + set_reg_field_value( + value, + 0, + AUX_CONTROL, + AUX_RESET); + + REG_WRITE(AUX_CONTROL, value); + + REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 0, + 1, 11); + } + } /*if (field)*/ + + /* request SW to access AUX */ + REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, 1); + + value = REG_READ(AUX_ARB_CONTROL); + field = get_reg_field_value( + value, + AUX_ARB_CONTROL, + AUX_REG_RW_CNTL_STATUS); + + return (field == SW_CAN_ACCESS_AUX); +} + +#define COMPOSE_AUX_SW_DATA_16_20(command, address) \ + ((command) | ((0xF0000 & (address)) >> 16)) + +#define COMPOSE_AUX_SW_DATA_8_15(address) \ + ((0xFF00 & (address)) >> 8) + +#define COMPOSE_AUX_SW_DATA_0_7(address) \ + (0xFF & (address)) + +static void submit_channel_request( + struct aux_engine *engine, + struct aux_request_transaction_data *request) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + uint32_t value; + uint32_t length; + + bool is_write = + ((request->type == AUX_TRANSACTION_TYPE_DP) && + (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || + ((request->type == AUX_TRANSACTION_TYPE_I2C) && + ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || + (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); + + /* clear_aux_error */ + REG_UPDATE_SEQ(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, + 1, + 0); + + REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, + 1, + 0); + + /* force_default_calibrate */ + REG_UPDATE_1BY1_2(AUXN_IMPCAL, + AUXN_IMPCAL_ENABLE, 1, + AUXN_IMPCAL_OVERRIDE_ENABLE, 0); + + /* bug? why AUXN update EN and OVERRIDE_EN 1 by 1 while AUX P toggles OVERRIDE? */ + + REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, + 1, + 0); + + /* set the delay and the number of bytes to write */ + + /* The length include + * the 4 bit header and the 20 bit address + * (that is 3 byte). + * If the requested length is non zero this means + * an addition byte specifying the length is required. */ + + length = request->length ? 4 : 3; + if (is_write) + length += request->length; + + REG_UPDATE_2(AUX_SW_CONTROL, + AUX_SW_START_DELAY, request->delay, + AUX_SW_WR_BYTES, length); + + /* program action and address and payload data (if 'is_write') */ + value = REG_UPDATE_4(AUX_SW_DATA, + AUX_SW_INDEX, 0, + AUX_SW_DATA_RW, 0, + AUX_SW_AUTOINCREMENT_DISABLE, 1, + AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address)); + + value = REG_SET_2(AUX_SW_DATA, value, + AUX_SW_AUTOINCREMENT_DISABLE, 0, + AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address)); + + value = REG_SET(AUX_SW_DATA, value, + AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address)); + + if (request->length) { + value = REG_SET(AUX_SW_DATA, value, + AUX_SW_DATA, request->length - 1); + } + + if (is_write) { + /* Load the HW buffer with the Data to be sent. + * This is relevant for write operation. + * For read, the data recived data will be + * processed in process_channel_reply(). */ + uint32_t i = 0; + + while (i < request->length) { + value = REG_SET(AUX_SW_DATA, value, + AUX_SW_DATA, request->data[i]); + + ++i; + } + } + + REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1); + REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, + 10, aux110->timeout_period/10); + REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); +} + +static void process_channel_reply( + struct aux_engine *engine, + struct aux_reply_transaction_data *reply) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + /* Need to do a read to get the number of bytes to process + * Alternatively, this information can be passed - + * but that causes coupling which isn't good either. */ + + uint32_t bytes_replied; + uint32_t value; + + value = REG_GET(AUX_SW_STATUS, + AUX_SW_REPLY_BYTE_COUNT, &bytes_replied); + + if (bytes_replied) { + uint32_t reply_result; + + REG_UPDATE_1BY1_3(AUX_SW_DATA, + AUX_SW_INDEX, 0, + AUX_SW_AUTOINCREMENT_DISABLE, 1, + AUX_SW_DATA_RW, 1); + + REG_GET(AUX_SW_DATA, + AUX_SW_DATA, &reply_result); + + reply_result = reply_result >> 4; + + switch (reply_result) { + case 0: /* ACK */ { + uint32_t i = 0; + + /* first byte was already used + * to get the command status */ + --bytes_replied; + + while (i < bytes_replied) { + uint32_t aux_sw_data_val; + + REG_GET(AUX_SW_DATA, + AUX_SW_DATA, &aux_sw_data_val); + + reply->data[i] = aux_sw_data_val; + ++i; + } + + reply->status = AUX_TRANSACTION_REPLY_AUX_ACK; + } + break; + case 1: /* NACK */ + reply->status = AUX_TRANSACTION_REPLY_AUX_NACK; + break; + case 2: /* DEFER */ + reply->status = AUX_TRANSACTION_REPLY_AUX_DEFER; + break; + case 4: /* AUX ACK / I2C NACK */ + reply->status = AUX_TRANSACTION_REPLY_I2C_NACK; + break; + case 8: /* AUX ACK / I2C DEFER */ + reply->status = AUX_TRANSACTION_REPLY_I2C_DEFER; + break; + default: + reply->status = AUX_TRANSACTION_REPLY_INVALID; + } + } else { + /* Need to handle an error case... + * hopefully, upper layer function won't call this function + * if the number of bytes in the reply was 0 + * because there was surely an error that was asserted + * that should have been handled + * for hot plug case, this could happens*/ + if (!(value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) + ASSERT_CRITICAL(false); + } +} + +static enum aux_channel_operation_result get_channel_status( + struct aux_engine *engine, + uint8_t *returned_bytes) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + uint32_t value; + + if (returned_bytes == NULL) { + /*caller pass NULL pointer*/ + ASSERT_CRITICAL(false); + return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; + } + *returned_bytes = 0; + + /* poll to make sure that SW_DONE is asserted */ + value = REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1, + 10, aux110->timeout_period/10); + + /* Note that the following bits are set in 'status.bits' + * during CTS 4.2.1.2 (FW 3.3.1): + * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, + * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. + * + * AUX_SW_RX_MIN_COUNT_VIOL is an internal, + * HW debugging bit and should be ignored. */ + if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) { + if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) || + (value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK)) + return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; + + else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) || + (value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) || + (value & + AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) || + (value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK)) + return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; + + *returned_bytes = get_reg_field_value(value, + AUX_SW_STATUS, + AUX_SW_REPLY_BYTE_COUNT); + + if (*returned_bytes == 0) + return + AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; + else { + *returned_bytes -= 1; + return AUX_CHANNEL_OPERATION_SUCCEEDED; + } + } else { + /*time_elapsed >= aux_engine->timeout_period */ + if (!(value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) + ASSERT_CRITICAL(false); + + return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; + } +} + +static const struct aux_engine_funcs aux_engine_funcs = { + .destroy = destroy, + .acquire_engine = acquire_engine, + .submit_channel_request = submit_channel_request, + .process_channel_reply = process_channel_reply, + .get_channel_status = get_channel_status, + .is_engine_available = is_engine_available, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .submit_request = dal_aux_engine_submit_request, + .get_engine_type = dal_aux_engine_get_engine_type, + .acquire = dal_aux_engine_acquire, +}; + +static void construct( + struct aux_engine_dce110 *engine, + const struct aux_engine_dce110_init_data *aux_init_data) +{ + dal_aux_engine_construct(&engine->base, aux_init_data->ctx); + engine->base.base.funcs = &engine_funcs; + engine->base.funcs = &aux_engine_funcs; + + engine->timeout_period = aux_init_data->timeout_period; + engine->regs = aux_init_data->regs; +} + +static void destruct( + struct aux_engine_dce110 *engine) +{ + struct aux_engine_dce110 *aux110 = engine; +/*temp w/a, to do*/ + REG_UPDATE(AUX_ARB_CONTROL, AUX_DMCU_DONE_USING_AUX_REG, 1); + REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1); + dal_aux_engine_destruct(&engine->base); +} + +struct aux_engine *dal_aux_engine_dce110_create( + const struct aux_engine_dce110_init_data *aux_init_data) +{ + struct aux_engine_dce110 *engine; + + if (!aux_init_data) { + ASSERT_CRITICAL(false); + return NULL; + } + + engine = kzalloc(sizeof(*engine), GFP_KERNEL); + + if (!engine) { + ASSERT_CRITICAL(false); + return NULL; + } + + construct(engine, aux_init_data); + return &engine->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.h new file mode 100644 index 000000000000..85ee82162590 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.h @@ -0,0 +1,78 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_AUX_ENGINE_DCE110_H__ +#define __DAL_AUX_ENGINE_DCE110_H__ + +#include "../aux_engine.h" + +#define AUX_COMMON_REG_LIST(id)\ + SRI(AUX_CONTROL, DP_AUX, id), \ + SRI(AUX_ARB_CONTROL, DP_AUX, id), \ + SRI(AUX_SW_DATA, DP_AUX, id), \ + SRI(AUX_SW_CONTROL, DP_AUX, id), \ + SRI(AUX_INTERRUPT_CONTROL, DP_AUX, id), \ + SRI(AUX_SW_STATUS, DP_AUX, id), \ + SR(AUXN_IMPCAL), \ + SR(AUXP_IMPCAL) + +struct dce110_aux_registers { + uint32_t AUX_CONTROL; + uint32_t AUX_ARB_CONTROL; + uint32_t AUX_SW_DATA; + uint32_t AUX_SW_CONTROL; + uint32_t AUX_INTERRUPT_CONTROL; + uint32_t AUX_SW_STATUS; + uint32_t AUXN_IMPCAL; + uint32_t AUXP_IMPCAL; + + uint32_t AUX_RESET_MASK; +}; + +struct aux_engine_dce110 { + struct aux_engine base; + const struct dce110_aux_registers *regs; + struct { + uint32_t aux_control; + uint32_t aux_arb_control; + uint32_t aux_sw_data; + uint32_t aux_sw_control; + uint32_t aux_interrupt_control; + uint32_t aux_sw_status; + } addr; + uint32_t timeout_period; +}; + +struct aux_engine_dce110_init_data { + uint32_t engine_id; + uint32_t timeout_period; + struct dc_context *ctx; + const struct dce110_aux_registers *regs; +}; + +struct aux_engine *dal_aux_engine_dce110_create( + const struct aux_engine_dce110_init_data *aux_init_data); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_hw_engine_dce110.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_hw_engine_dce110.c new file mode 100644 index 000000000000..56e25b3d65fd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_hw_engine_dce110.c @@ -0,0 +1,570 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "include/logger_interface.h" +/* + * Pre-requisites: headers required by header of this unit + */ + +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_hw_engine.h" +#include "../i2c_generic_hw_engine.h" +/* + * Header of this unit + */ + +#include "i2c_hw_engine_dce110.h" + +/* + * Post-requisites: headers required by this unit + */ +#include "reg_helper.h" + +/* + * This unit + */ + +enum dc_i2c_status { + DC_I2C_STATUS__DC_I2C_STATUS_IDLE, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW +}; + +enum dc_i2c_arbitration { + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_HIGH +}; + +enum { + /* No timeout in HW + * (timeout implemented in SW by querying status) */ + I2C_SETUP_TIME_LIMIT = 255, + I2C_HW_BUFFER_SIZE = 538 +}; + +/* + * @brief + * Cast pointer to 'struct i2c_hw_engine *' + * to pointer 'struct i2c_hw_engine_dce110 *' + */ +#define FROM_I2C_HW_ENGINE(ptr) \ + container_of((ptr), struct i2c_hw_engine_dce110, base) +/* + * @brief + * Cast pointer to 'struct i2c_engine *' + * to pointer to 'struct i2c_hw_engine_dce110 *' + */ +#define FROM_I2C_ENGINE(ptr) \ + FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) + +/* + * @brief + * Cast pointer to 'struct engine *' + * to 'pointer to struct i2c_hw_engine_dce110 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +#define CTX \ + hw_engine->base.base.base.ctx + +#define REG(reg_name)\ + (hw_engine->regs->reg_name) + +#undef FN +#define FN(reg_name, field_name) \ + hw_engine->i2c_shift->field_name, hw_engine->i2c_mask->field_name + +#include "reg_helper.h" + +static void disable_i2c_hw_engine( + struct i2c_hw_engine_dce110 *hw_engine) +{ + REG_UPDATE_N(SETUP, 1, FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 0); +} + +static void release_engine( + struct engine *engine) +{ + struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); + + struct i2c_engine *base = NULL; + bool safe_to_reset; + + base = &hw_engine->base.base; + + /* Restore original HW engine speed */ + + base->funcs->set_speed(base, hw_engine->base.original_speed); + + /* Release I2C */ + REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, 1); + + /* Reset HW engine */ + { + uint32_t i2c_sw_status = 0; + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + /* if used by SW, safe to reset */ + safe_to_reset = (i2c_sw_status == 1); + } + + if (safe_to_reset) + REG_UPDATE_2( + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET, 1, + DC_I2C_SW_STATUS_RESET, 1); + else + REG_UPDATE(DC_I2C_CONTROL, DC_I2C_SW_STATUS_RESET, 1); + + /* HW I2c engine - clock gating feature */ + if (!hw_engine->engine_keep_power_up_count) + disable_i2c_hw_engine(hw_engine); +} + +static bool setup_engine( + struct i2c_engine *i2c_engine) +{ + struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); + + /* Program pin select */ + REG_UPDATE_6( + DC_I2C_CONTROL, + DC_I2C_GO, 0, + DC_I2C_SOFT_RESET, 0, + DC_I2C_SEND_RESET, 0, + DC_I2C_SW_STATUS_RESET, 1, + DC_I2C_TRANSACTION_COUNT, 0, + DC_I2C_DDC_SELECT, hw_engine->engine_id); + + /* Program time limit */ + REG_UPDATE_N( + SETUP, 2, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), I2C_SETUP_TIME_LIMIT, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1); + + /* Program HW priority + * set to High - interrupt software I2C at any time + * Enable restart of SW I2C that was interrupted by HW + * disable queuing of software while I2C is in use by HW */ + REG_UPDATE_2( + DC_I2C_ARBITRATION, + DC_I2C_NO_QUEUED_SW_GO, 0, + DC_I2C_SW_PRIORITY, DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL); + + return true; +} + +static uint32_t get_speed( + const struct i2c_engine *i2c_engine) +{ + const struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); + uint32_t pre_scale = 0; + + REG_GET(SPEED, DC_I2C_DDC1_PRESCALE, &pre_scale); + + /* [anaumov] it seems following is unnecessary */ + /*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/ + return pre_scale ? + hw_engine->reference_frequency / pre_scale : + hw_engine->base.default_speed; +} + +static void set_speed( + struct i2c_engine *i2c_engine, + uint32_t speed) +{ + struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); + + if (speed) { + if (hw_engine->i2c_mask->DC_I2C_DDC1_START_STOP_TIMING_CNTL) + REG_UPDATE_N( + SPEED, 3, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), hw_engine->reference_frequency / speed, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL), speed > 50 ? 2:1); + else + REG_UPDATE_N( + SPEED, 2, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), hw_engine->reference_frequency / speed, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2); + } +} + +static inline void reset_hw_engine(struct engine *engine) +{ + struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); + + REG_UPDATE_2( + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET, 1, + DC_I2C_SW_STATUS_RESET, 1); +} + +static bool is_hw_busy(struct engine *engine) +{ + struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); + uint32_t i2c_sw_status = 0; + + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_IDLE) + return false; + + reset_hw_engine(engine); + + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + return i2c_sw_status != DC_I2C_STATUS__DC_I2C_STATUS_IDLE; +} + + +#define STOP_TRANS_PREDICAT \ + ((hw_engine->transaction_count == 3) || \ + (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || \ + (request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) + +#define SET_I2C_TRANSACTION(id) \ + do { \ + REG_UPDATE_N(DC_I2C_TRANSACTION##id, 5, \ + FN(DC_I2C_TRANSACTION0, DC_I2C_STOP_ON_NACK0), 1, \ + FN(DC_I2C_TRANSACTION0, DC_I2C_START0), 1, \ + FN(DC_I2C_TRANSACTION0, DC_I2C_STOP0), STOP_TRANS_PREDICAT ? 1:0, \ + FN(DC_I2C_TRANSACTION0, DC_I2C_RW0), (0 != (request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)), \ + FN(DC_I2C_TRANSACTION0, DC_I2C_COUNT0), length); \ + if (STOP_TRANS_PREDICAT) \ + last_transaction = true; \ + } while (false) + + +static bool process_transaction( + struct i2c_hw_engine_dce110 *hw_engine, + struct i2c_request_transaction_data *request) +{ + uint32_t length = request->length; + uint8_t *buffer = request->data; + uint32_t value = 0; + + bool last_transaction = false; + + struct dc_context *ctx = NULL; + + ctx = hw_engine->base.base.base.ctx; + + + + switch (hw_engine->transaction_count) { + case 0: + SET_I2C_TRANSACTION(0); + break; + case 1: + SET_I2C_TRANSACTION(1); + break; + case 2: + SET_I2C_TRANSACTION(2); + break; + case 3: + SET_I2C_TRANSACTION(3); + break; + default: + /* TODO Warning ? */ + break; + } + + + /* Write the I2C address and I2C data + * into the hardware circular buffer, one byte per entry. + * As an example, the 7-bit I2C slave address for CRT monitor + * for reading DDC/EDID information is 0b1010001. + * For an I2C send operation, the LSB must be programmed to 0; + * for I2C receive operation, the LSB must be programmed to 1. */ + if (hw_engine->transaction_count == 0) { + value = REG_SET_4(DC_I2C_DATA, 0, + DC_I2C_DATA_RW, false, + DC_I2C_DATA, request->address, + DC_I2C_INDEX, 0, + DC_I2C_INDEX_WRITE, 1); + hw_engine->buffer_used_write = 0; + } else + value = REG_SET_2(DC_I2C_DATA, 0, + DC_I2C_DATA_RW, false, + DC_I2C_DATA, request->address); + + hw_engine->buffer_used_write++; + + if (!(request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { + while (length) { + REG_SET_2(DC_I2C_DATA, value, + DC_I2C_INDEX_WRITE, 0, + DC_I2C_DATA, *buffer++); + hw_engine->buffer_used_write++; + --length; + } + } + + ++hw_engine->transaction_count; + hw_engine->buffer_used_bytes += length + 1; + + return last_transaction; +} + +static void execute_transaction( + struct i2c_hw_engine_dce110 *hw_engine) +{ + REG_UPDATE_N(SETUP, 5, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_EN), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_DRIVE_EN), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_SEL), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_TRANSACTION_DELAY), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_BYTE_DELAY), 0); + + + REG_UPDATE_5(DC_I2C_CONTROL, + DC_I2C_SOFT_RESET, 0, + DC_I2C_SW_STATUS_RESET, 0, + DC_I2C_SEND_RESET, 0, + DC_I2C_GO, 0, + DC_I2C_TRANSACTION_COUNT, hw_engine->transaction_count - 1); + + /* start I2C transfer */ + REG_UPDATE(DC_I2C_CONTROL, DC_I2C_GO, 1); + + /* all transactions were executed and HW buffer became empty + * (even though it actually happens when status becomes DONE) */ + hw_engine->transaction_count = 0; + hw_engine->buffer_used_bytes = 0; +} + +static void submit_channel_request( + struct i2c_engine *engine, + struct i2c_request_transaction_data *request) +{ + request->status = I2C_CHANNEL_OPERATION_SUCCEEDED; + + if (!process_transaction(FROM_I2C_ENGINE(engine), request)) + return; + + if (is_hw_busy(&engine->base)) { + request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; + return; + } + + execute_transaction(FROM_I2C_ENGINE(engine)); +} + +static void process_channel_reply( + struct i2c_engine *engine, + struct i2c_reply_transaction_data *reply) +{ + uint32_t length = reply->length; + uint8_t *buffer = reply->data; + + struct i2c_hw_engine_dce110 *hw_engine = + FROM_I2C_ENGINE(engine); + + + REG_SET_3(DC_I2C_DATA, 0, + DC_I2C_INDEX, hw_engine->buffer_used_write, + DC_I2C_DATA_RW, 1, + DC_I2C_INDEX_WRITE, 1); + + while (length) { + /* after reading the status, + * if the I2C operation executed successfully + * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller + * should read data bytes from I2C circular data buffer */ + + uint32_t i2c_data; + + REG_GET(DC_I2C_DATA, DC_I2C_DATA, &i2c_data); + *buffer++ = i2c_data; + + --length; + } +} + +static enum i2c_channel_operation_result get_channel_status( + struct i2c_engine *i2c_engine, + uint8_t *returned_bytes) +{ + uint32_t i2c_sw_status = 0; + struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); + uint32_t value = + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) + return I2C_CHANNEL_OPERATION_ENGINE_BUSY; + else if (value & hw_engine->i2c_mask->DC_I2C_SW_STOPPED_ON_NACK) + return I2C_CHANNEL_OPERATION_NO_RESPONSE; + else if (value & hw_engine->i2c_mask->DC_I2C_SW_TIMEOUT) + return I2C_CHANNEL_OPERATION_TIMEOUT; + else if (value & hw_engine->i2c_mask->DC_I2C_SW_ABORTED) + return I2C_CHANNEL_OPERATION_FAILED; + else if (value & hw_engine->i2c_mask->DC_I2C_SW_DONE) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + /* + * this is the case when HW used for communication, I2C_SW_STATUS + * could be zero + */ + return I2C_CHANNEL_OPERATION_SUCCEEDED; +} + +static uint32_t get_hw_buffer_available_size( + const struct i2c_hw_engine *engine) +{ + return I2C_HW_BUFFER_SIZE - + FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes; +} + +static uint32_t get_transaction_timeout( + const struct i2c_hw_engine *engine, + uint32_t length) +{ + uint32_t speed = engine->base.funcs->get_speed(&engine->base); + + uint32_t period_timeout; + uint32_t num_of_clock_stretches; + + if (!speed) + return 0; + + period_timeout = (1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed; + + num_of_clock_stretches = 1 + (length << 3) + 1; + num_of_clock_stretches += + (FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes << 3) + + (FROM_I2C_HW_ENGINE(engine)->transaction_count << 1); + + return period_timeout * num_of_clock_stretches; +} + +static void destroy( + struct i2c_engine **i2c_engine) +{ + struct i2c_hw_engine_dce110 *engine_dce110 = + FROM_I2C_ENGINE(*i2c_engine); + + dal_i2c_hw_engine_destruct(&engine_dce110->base); + + kfree(engine_dce110); + + *i2c_engine = NULL; +} + +static const struct i2c_engine_funcs i2c_engine_funcs = { + .destroy = destroy, + .get_speed = get_speed, + .set_speed = set_speed, + .setup_engine = setup_engine, + .submit_channel_request = submit_channel_request, + .process_channel_reply = process_channel_reply, + .get_channel_status = get_channel_status, + .acquire_engine = dal_i2c_hw_engine_acquire_engine, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .get_engine_type = dal_i2c_hw_engine_get_engine_type, + .acquire = dal_i2c_engine_acquire, + .submit_request = dal_i2c_hw_engine_submit_request, +}; + +static const struct i2c_hw_engine_funcs i2c_hw_engine_funcs = { + .get_hw_buffer_available_size = get_hw_buffer_available_size, + .get_transaction_timeout = get_transaction_timeout, + .wait_on_operation_result = dal_i2c_hw_engine_wait_on_operation_result, +}; + +static void construct( + struct i2c_hw_engine_dce110 *hw_engine, + const struct i2c_hw_engine_dce110_create_arg *arg) +{ + uint32_t xtal_ref_div = 0; + + dal_i2c_hw_engine_construct(&hw_engine->base, arg->ctx); + + hw_engine->base.base.base.funcs = &engine_funcs; + hw_engine->base.base.funcs = &i2c_engine_funcs; + hw_engine->base.funcs = &i2c_hw_engine_funcs; + hw_engine->base.default_speed = arg->default_speed; + + hw_engine->regs = arg->regs; + hw_engine->i2c_shift = arg->i2c_shift; + hw_engine->i2c_mask = arg->i2c_mask; + + hw_engine->engine_id = arg->engine_id; + + hw_engine->buffer_used_bytes = 0; + hw_engine->transaction_count = 0; + hw_engine->engine_keep_power_up_count = 1; + + + REG_GET(MICROSECOND_TIME_BASE_DIV, XTAL_REF_DIV, &xtal_ref_div); + + if (xtal_ref_div == 0) { + dm_logger_write( + hw_engine->base.base.base.ctx->logger, LOG_WARNING, + "Invalid base timer divider\n", + __func__); + xtal_ref_div = 2; + } + + /*Calculating Reference Clock by divding original frequency by + * XTAL_REF_DIV. + * At upper level, uint32_t reference_frequency = + * dal_i2caux_get_reference_clock(as) >> 1 + * which already divided by 2. So we need x2 to get original + * reference clock from ppll_info + */ + hw_engine->reference_frequency = + (arg->reference_frequency * 2) / xtal_ref_div; +} + +struct i2c_engine *dal_i2c_hw_engine_dce110_create( + const struct i2c_hw_engine_dce110_create_arg *arg) +{ + struct i2c_hw_engine_dce110 *engine_dce10; + + if (!arg) { + ASSERT_CRITICAL(false); + return NULL; + } + if (!arg->reference_frequency) { + ASSERT_CRITICAL(false); + return NULL; + } + + engine_dce10 = kzalloc(sizeof(struct i2c_hw_engine_dce110), + GFP_KERNEL); + + if (!engine_dce10) { + ASSERT_CRITICAL(false); + return NULL; + } + + construct(engine_dce10, arg); + return &engine_dce10->base.base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_hw_engine_dce110.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_hw_engine_dce110.h new file mode 100644 index 000000000000..5bb04085f670 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_hw_engine_dce110.h @@ -0,0 +1,210 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_HW_ENGINE_DCE110_H__ +#define __DAL_I2C_HW_ENGINE_DCE110_H__ + +#define I2C_HW_ENGINE_COMMON_REG_LIST(id)\ + SRI(SETUP, DC_I2C_DDC, id),\ + SRI(SPEED, DC_I2C_DDC, id),\ + SR(DC_I2C_ARBITRATION),\ + SR(DC_I2C_CONTROL),\ + SR(DC_I2C_SW_STATUS),\ + SR(DC_I2C_TRANSACTION0),\ + SR(DC_I2C_TRANSACTION1),\ + SR(DC_I2C_TRANSACTION2),\ + SR(DC_I2C_TRANSACTION3),\ + SR(DC_I2C_DATA),\ + SR(MICROSECOND_TIME_BASE_DIV) + +#define I2C_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_EN, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_DRIVE_EN, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_SEL, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_TRANSACTION_DELAY, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_BYTE_DELAY, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_NO_QUEUED_SW_GO, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_SW_PRIORITY, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SOFT_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SW_STATUS_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_GO, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SEND_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_TRANSACTION_COUNT, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_DDC_SELECT, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_STOPPED_ON_NACK, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_TIMEOUT, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_ABORTED, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_DONE, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_STOP_ON_NACK0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_START0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_RW0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_STOP0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_COUNT0, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_DATA_RW, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_DATA, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_INDEX, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_INDEX_WRITE, mask_sh),\ + I2C_SF(MICROSECOND_TIME_BASE_DIV, XTAL_REF_DIV, mask_sh) + +#define I2C_COMMON_MASK_SH_LIST_DCE100(mask_sh)\ + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) + +#define I2C_COMMON_MASK_SH_LIST_DCE110(mask_sh)\ + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL, mask_sh) + +struct dce110_i2c_hw_engine_shift { + uint8_t DC_I2C_DDC1_ENABLE; + uint8_t DC_I2C_DDC1_TIME_LIMIT; + uint8_t DC_I2C_DDC1_DATA_DRIVE_EN; + uint8_t DC_I2C_DDC1_CLK_DRIVE_EN; + uint8_t DC_I2C_DDC1_DATA_DRIVE_SEL; + uint8_t DC_I2C_DDC1_INTRA_TRANSACTION_DELAY; + uint8_t DC_I2C_DDC1_INTRA_BYTE_DELAY; + uint8_t DC_I2C_SW_DONE_USING_I2C_REG; + uint8_t DC_I2C_NO_QUEUED_SW_GO; + uint8_t DC_I2C_SW_PRIORITY; + uint8_t DC_I2C_SOFT_RESET; + uint8_t DC_I2C_SW_STATUS_RESET; + uint8_t DC_I2C_GO; + uint8_t DC_I2C_SEND_RESET; + uint8_t DC_I2C_TRANSACTION_COUNT; + uint8_t DC_I2C_DDC_SELECT; + uint8_t DC_I2C_DDC1_PRESCALE; + uint8_t DC_I2C_DDC1_THRESHOLD; + uint8_t DC_I2C_DDC1_START_STOP_TIMING_CNTL; + uint8_t DC_I2C_SW_STOPPED_ON_NACK; + uint8_t DC_I2C_SW_TIMEOUT; + uint8_t DC_I2C_SW_ABORTED; + uint8_t DC_I2C_SW_DONE; + uint8_t DC_I2C_SW_STATUS; + uint8_t DC_I2C_STOP_ON_NACK0; + uint8_t DC_I2C_START0; + uint8_t DC_I2C_RW0; + uint8_t DC_I2C_STOP0; + uint8_t DC_I2C_COUNT0; + uint8_t DC_I2C_DATA_RW; + uint8_t DC_I2C_DATA; + uint8_t DC_I2C_INDEX; + uint8_t DC_I2C_INDEX_WRITE; + uint8_t XTAL_REF_DIV; +}; + +struct dce110_i2c_hw_engine_mask { + uint32_t DC_I2C_DDC1_ENABLE; + uint32_t DC_I2C_DDC1_TIME_LIMIT; + uint32_t DC_I2C_DDC1_DATA_DRIVE_EN; + uint32_t DC_I2C_DDC1_CLK_DRIVE_EN; + uint32_t DC_I2C_DDC1_DATA_DRIVE_SEL; + uint32_t DC_I2C_DDC1_INTRA_TRANSACTION_DELAY; + uint32_t DC_I2C_DDC1_INTRA_BYTE_DELAY; + uint32_t DC_I2C_SW_DONE_USING_I2C_REG; + uint32_t DC_I2C_NO_QUEUED_SW_GO; + uint32_t DC_I2C_SW_PRIORITY; + uint32_t DC_I2C_SOFT_RESET; + uint32_t DC_I2C_SW_STATUS_RESET; + uint32_t DC_I2C_GO; + uint32_t DC_I2C_SEND_RESET; + uint32_t DC_I2C_TRANSACTION_COUNT; + uint32_t DC_I2C_DDC_SELECT; + uint32_t DC_I2C_DDC1_PRESCALE; + uint32_t DC_I2C_DDC1_THRESHOLD; + uint32_t DC_I2C_DDC1_START_STOP_TIMING_CNTL; + uint32_t DC_I2C_SW_STOPPED_ON_NACK; + uint32_t DC_I2C_SW_TIMEOUT; + uint32_t DC_I2C_SW_ABORTED; + uint32_t DC_I2C_SW_DONE; + uint32_t DC_I2C_SW_STATUS; + uint32_t DC_I2C_STOP_ON_NACK0; + uint32_t DC_I2C_START0; + uint32_t DC_I2C_RW0; + uint32_t DC_I2C_STOP0; + uint32_t DC_I2C_COUNT0; + uint32_t DC_I2C_DATA_RW; + uint32_t DC_I2C_DATA; + uint32_t DC_I2C_INDEX; + uint32_t DC_I2C_INDEX_WRITE; + uint32_t XTAL_REF_DIV; +}; + +struct dce110_i2c_hw_engine_registers { + uint32_t SETUP; + uint32_t SPEED; + uint32_t DC_I2C_ARBITRATION; + uint32_t DC_I2C_CONTROL; + uint32_t DC_I2C_SW_STATUS; + uint32_t DC_I2C_TRANSACTION0; + uint32_t DC_I2C_TRANSACTION1; + uint32_t DC_I2C_TRANSACTION2; + uint32_t DC_I2C_TRANSACTION3; + uint32_t DC_I2C_DATA; + uint32_t MICROSECOND_TIME_BASE_DIV; +}; + +struct i2c_hw_engine_dce110 { + struct i2c_hw_engine base; + const struct dce110_i2c_hw_engine_registers *regs; + const struct dce110_i2c_hw_engine_shift *i2c_shift; + const struct dce110_i2c_hw_engine_mask *i2c_mask; + struct { + uint32_t DC_I2C_DDCX_SETUP; + uint32_t DC_I2C_DDCX_SPEED; + } addr; + uint32_t engine_id; + /* expressed in kilohertz */ + uint32_t reference_frequency; + /* number of bytes currently used in HW buffer */ + uint32_t buffer_used_bytes; + /* number of bytes used for write transaction in HW buffer + * - this will be used as the index to read from*/ + uint32_t buffer_used_write; + /* number of pending transactions (before GO) */ + uint32_t transaction_count; + uint32_t engine_keep_power_up_count; +}; + +struct i2c_hw_engine_dce110_create_arg { + uint32_t engine_id; + uint32_t reference_frequency; + uint32_t default_speed; + struct dc_context *ctx; + const struct dce110_i2c_hw_engine_registers *regs; + const struct dce110_i2c_hw_engine_shift *i2c_shift; + const struct dce110_i2c_hw_engine_mask *i2c_mask; +}; + +struct i2c_engine *dal_i2c_hw_engine_dce110_create( + const struct i2c_hw_engine_dce110_create_arg *arg); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_sw_engine_dce110.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_sw_engine_dce110.c new file mode 100644 index 000000000000..3aa7f791e523 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_sw_engine_dce110.c @@ -0,0 +1,160 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" + +/* + * Header of this unit + */ + +#include "i2c_sw_engine_dce110.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +/* + * @brief + * Cast 'struct i2c_sw_engine *' + * to 'struct i2c_sw_engine_dce110 *' + */ +#define FROM_I2C_SW_ENGINE(ptr) \ + container_of((ptr), struct i2c_sw_engine_dce110, base) +/* + * @brief + * Cast 'struct i2c_engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_I2C_ENGINE(ptr) \ + FROM_I2C_SW_ENGINE(container_of((ptr), struct i2c_sw_engine, base)) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +static void release_engine( + struct engine *engine) +{ +} + +static void destruct( + struct i2c_sw_engine_dce110 *engine) +{ + dal_i2c_sw_engine_destruct(&engine->base); +} + +static void destroy( + struct i2c_engine **engine) +{ + struct i2c_sw_engine_dce110 *sw_engine = FROM_I2C_ENGINE(*engine); + + destruct(sw_engine); + + kfree(sw_engine); + + *engine = NULL; +} + +static bool acquire_engine( + struct i2c_engine *engine, + struct ddc *ddc_handle) +{ + return dal_i2caux_i2c_sw_engine_acquire_engine(engine, ddc_handle); +} + +static const struct i2c_engine_funcs i2c_engine_funcs = { + .acquire_engine = acquire_engine, + .destroy = destroy, + .get_speed = dal_i2c_sw_engine_get_speed, + .set_speed = dal_i2c_sw_engine_set_speed, + .setup_engine = dal_i2c_engine_setup_i2c_engine, + .submit_channel_request = dal_i2c_sw_engine_submit_channel_request, + .process_channel_reply = dal_i2c_engine_process_channel_reply, + .get_channel_status = dal_i2c_sw_engine_get_channel_status, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .get_engine_type = dal_i2c_sw_engine_get_engine_type, + .acquire = dal_i2c_engine_acquire, + .submit_request = dal_i2c_sw_engine_submit_request, +}; + +static void construct( + struct i2c_sw_engine_dce110 *engine_dce110, + const struct i2c_sw_engine_dce110_create_arg *arg_dce110) +{ + struct i2c_sw_engine_create_arg arg_base; + + arg_base.ctx = arg_dce110->ctx; + arg_base.default_speed = arg_dce110->default_speed; + + dal_i2c_sw_engine_construct(&engine_dce110->base, &arg_base); + + /*struct engine struct engine_funcs*/ + engine_dce110->base.base.base.funcs = &engine_funcs; + /*struct i2c_engine struct i2c_engine_funcs*/ + engine_dce110->base.base.funcs = &i2c_engine_funcs; + engine_dce110->base.default_speed = arg_dce110->default_speed; + engine_dce110->engine_id = arg_dce110->engine_id; +} + +struct i2c_engine *dal_i2c_sw_engine_dce110_create( + const struct i2c_sw_engine_dce110_create_arg *arg) +{ + struct i2c_sw_engine_dce110 *engine_dce110; + + if (!arg) { + ASSERT_CRITICAL(false); + return NULL; + } + + engine_dce110 = kzalloc(sizeof(struct i2c_sw_engine_dce110), + GFP_KERNEL); + + if (!engine_dce110) { + ASSERT_CRITICAL(false); + return NULL; + } + + construct(engine_dce110, arg); + return &engine_dce110->base.base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_sw_engine_dce110.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_sw_engine_dce110.h new file mode 100644 index 000000000000..c48c61f540a8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2c_sw_engine_dce110.h @@ -0,0 +1,43 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_SW_ENGINE_DCE110_H__ +#define __DAL_I2C_SW_ENGINE_DCE110_H__ + +struct i2c_sw_engine_dce110 { + struct i2c_sw_engine base; + uint32_t engine_id; +}; + +struct i2c_sw_engine_dce110_create_arg { + uint32_t engine_id; + uint32_t default_speed; + struct dc_context *ctx; +}; + +struct i2c_engine *dal_i2c_sw_engine_dce110_create( + const struct i2c_sw_engine_dce110_create_arg *arg); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.c new file mode 100644 index 000000000000..2a047f8ca0e9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.c @@ -0,0 +1,311 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../i2caux.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "../i2c_hw_engine.h" + +/* + * Header of this unit + */ +#include "i2caux_dce110.h" + +#include "i2c_sw_engine_dce110.h" +#include "i2c_hw_engine_dce110.h" +#include "aux_engine_dce110.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ +/*cast pointer to struct i2caux TO pointer to struct i2caux_dce110*/ +#define FROM_I2C_AUX(ptr) \ + container_of((ptr), struct i2caux_dce110, base) + +static void destruct( + struct i2caux_dce110 *i2caux_dce110) +{ + dal_i2caux_destruct(&i2caux_dce110->base); +} + +static void destroy( + struct i2caux **i2c_engine) +{ + struct i2caux_dce110 *i2caux_dce110 = FROM_I2C_AUX(*i2c_engine); + + destruct(i2caux_dce110); + + kfree(i2caux_dce110); + + *i2c_engine = NULL; +} + +static struct i2c_engine *acquire_i2c_hw_engine( + struct i2caux *i2caux, + struct ddc *ddc) +{ + struct i2caux_dce110 *i2caux_dce110 = FROM_I2C_AUX(i2caux); + + struct i2c_engine *engine = NULL; + /* generic hw engine is not used for EDID read + * It may be needed for external i2c device, like thermal chip, + * TODO will be implemented when needed. + * check dce80 bool non_generic for generic hw engine; + */ + + if (!ddc) + return NULL; + + if (ddc->hw_info.hw_supported) { + enum gpio_ddc_line line = dal_ddc_get_line(ddc); + + if (line < GPIO_DDC_LINE_COUNT) + engine = i2caux->i2c_hw_engines[line]; + } + + if (!engine) + return NULL; + + if (!i2caux_dce110->i2c_hw_buffer_in_use && + engine->base.funcs->acquire(&engine->base, ddc)) { + i2caux_dce110->i2c_hw_buffer_in_use = true; + return engine; + } + + return NULL; +} + +static void release_engine( + struct i2caux *i2caux, + struct engine *engine) +{ + struct i2caux_dce110 *i2caux_dce110 = FROM_I2C_AUX(i2caux); + + if (engine->funcs->get_engine_type(engine) == + I2CAUX_ENGINE_TYPE_I2C_DDC_HW) + i2caux_dce110->i2c_hw_buffer_in_use = false; + + dal_i2caux_release_engine(i2caux, engine); +} + +static const enum gpio_ddc_line hw_ddc_lines[] = { + GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_DDC2, + GPIO_DDC_LINE_DDC3, + GPIO_DDC_LINE_DDC4, + GPIO_DDC_LINE_DDC5, + GPIO_DDC_LINE_DDC6, +}; + +static const enum gpio_ddc_line hw_aux_lines[] = { + GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_DDC2, + GPIO_DDC_LINE_DDC3, + GPIO_DDC_LINE_DDC4, + GPIO_DDC_LINE_DDC5, + GPIO_DDC_LINE_DDC6, +}; + +/* function table */ +static const struct i2caux_funcs i2caux_funcs = { + .destroy = destroy, + .acquire_i2c_hw_engine = acquire_i2c_hw_engine, + .release_engine = release_engine, + .acquire_i2c_sw_engine = dal_i2caux_acquire_i2c_sw_engine, + .acquire_aux_engine = dal_i2caux_acquire_aux_engine, +}; + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +#define aux_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = AUX_CONTROL__AUX_RESET_MASK \ +} + +#define hw_engine_regs(id)\ +{\ + I2C_HW_ENGINE_COMMON_REG_LIST(id) \ +} + +static const struct dce110_aux_registers dce110_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +static const struct dce110_i2c_hw_engine_registers i2c_hw_engine_regs[] = { + hw_engine_regs(1), + hw_engine_regs(2), + hw_engine_regs(3), + hw_engine_regs(4), + hw_engine_regs(5), + hw_engine_regs(6) +}; + +static const struct dce110_i2c_hw_engine_shift i2c_shift = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce110_i2c_hw_engine_mask i2c_mask = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +void dal_i2caux_dce110_construct( + struct i2caux_dce110 *i2caux_dce110, + struct dc_context *ctx, + const struct dce110_aux_registers aux_regs[], + const struct dce110_i2c_hw_engine_registers i2c_hw_engine_regs[], + const struct dce110_i2c_hw_engine_shift *i2c_shift, + const struct dce110_i2c_hw_engine_mask *i2c_mask) +{ + uint32_t i = 0; + uint32_t reference_frequency = 0; + bool use_i2c_sw_engine = false; + struct i2caux *base = NULL; + /*TODO: For CZ bring up, if dal_i2caux_get_reference_clock + * does not return 48KHz, we need hard coded for 48Khz. + * Some BIOS setting incorrect cause this + * For production, we always get value from BIOS*/ + reference_frequency = + dal_i2caux_get_reference_clock(ctx->dc_bios) >> 1; + + base = &i2caux_dce110->base; + + dal_i2caux_construct(base, ctx); + + i2caux_dce110->base.funcs = &i2caux_funcs; + i2caux_dce110->i2c_hw_buffer_in_use = false; + /* Create I2C engines (DDC lines per connector) + * different I2C/AUX usage cases, DDC, Generic GPIO, AUX. + */ + do { + enum gpio_ddc_line line_id = hw_ddc_lines[i]; + + struct i2c_hw_engine_dce110_create_arg hw_arg_dce110; + + if (use_i2c_sw_engine) { + struct i2c_sw_engine_dce110_create_arg sw_arg; + + sw_arg.engine_id = i; + sw_arg.default_speed = base->default_i2c_sw_speed; + sw_arg.ctx = ctx; + base->i2c_sw_engines[line_id] = + dal_i2c_sw_engine_dce110_create(&sw_arg); + } + + hw_arg_dce110.engine_id = i; + hw_arg_dce110.reference_frequency = reference_frequency; + hw_arg_dce110.default_speed = base->default_i2c_hw_speed; + hw_arg_dce110.ctx = ctx; + hw_arg_dce110.regs = &i2c_hw_engine_regs[i]; + hw_arg_dce110.i2c_shift = i2c_shift; + hw_arg_dce110.i2c_mask = i2c_mask; + + base->i2c_hw_engines[line_id] = + dal_i2c_hw_engine_dce110_create(&hw_arg_dce110); + + ++i; + } while (i < ARRAY_SIZE(hw_ddc_lines)); + + /* Create AUX engines for all lines which has assisted HW AUX + * 'i' (loop counter) used as DDC/AUX engine_id */ + + i = 0; + + do { + enum gpio_ddc_line line_id = hw_aux_lines[i]; + + struct aux_engine_dce110_init_data aux_init_data; + + aux_init_data.engine_id = i; + aux_init_data.timeout_period = base->aux_timeout_period; + aux_init_data.ctx = ctx; + aux_init_data.regs = &aux_regs[i]; + + base->aux_engines[line_id] = + dal_aux_engine_dce110_create(&aux_init_data); + + ++i; + } while (i < ARRAY_SIZE(hw_aux_lines)); + + /*TODO Generic I2C SW and HW*/ +} + +/* + * dal_i2caux_dce110_create + * + * @brief + * public interface to allocate memory for DCE11 I2CAUX + * + * @param + * struct adapter_service *as - [in] + * struct dc_context *ctx - [in] + * + * @return + * pointer to the base struct of DCE11 I2CAUX + */ +struct i2caux *dal_i2caux_dce110_create( + struct dc_context *ctx) +{ + struct i2caux_dce110 *i2caux_dce110 = + kzalloc(sizeof(struct i2caux_dce110), GFP_KERNEL); + + if (!i2caux_dce110) { + ASSERT_CRITICAL(false); + return NULL; + } + + dal_i2caux_dce110_construct(i2caux_dce110, + ctx, + dce110_aux_regs, + i2c_hw_engine_regs, + &i2c_shift, + &i2c_mask); + return &i2caux_dce110->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.h new file mode 100644 index 000000000000..1b1f71c60ac9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.h @@ -0,0 +1,53 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DCE110_H__ +#define __DAL_I2C_AUX_DCE110_H__ + +#include "../i2caux.h" + +struct i2caux_dce110 { + struct i2caux base; + /* indicate the I2C HW circular buffer is in use */ + bool i2c_hw_buffer_in_use; +}; + +struct dce110_aux_registers; +struct dce110_i2c_hw_engine_registers; +struct dce110_i2c_hw_engine_shift; +struct dce110_i2c_hw_engine_mask; + +struct i2caux *dal_i2caux_dce110_create( + struct dc_context *ctx); + +void dal_i2caux_dce110_construct( + struct i2caux_dce110 *i2caux_dce110, + struct dc_context *ctx, + const struct dce110_aux_registers *aux_regs, + const struct dce110_i2c_hw_engine_registers *i2c_hw_engine_regs, + const struct dce110_i2c_hw_engine_shift *i2c_shift, + const struct dce110_i2c_hw_engine_mask *i2c_mask); + +#endif /* __DAL_I2C_AUX_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce112/i2caux_dce112.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce112/i2caux_dce112.c new file mode 100644 index 000000000000..dafc1a727f7f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce112/i2caux_dce112.c @@ -0,0 +1,128 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/i2caux_interface.h" +#include "../i2caux.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "../i2c_hw_engine.h" + +#include "../dce110/i2caux_dce110.h" +#include "i2caux_dce112.h" + +#include "../dce110/aux_engine_dce110.h" + +#include "../dce110/i2c_hw_engine_dce110.h" + +#include "dce/dce_11_2_d.h" +#include "dce/dce_11_2_sh_mask.h" + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +#define aux_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = AUX_CONTROL__AUX_RESET_MASK \ +} + +#define hw_engine_regs(id)\ +{\ + I2C_HW_ENGINE_COMMON_REG_LIST(id) \ +} + +static const struct dce110_aux_registers dce112_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5), +}; + +static const struct dce110_i2c_hw_engine_registers dce112_hw_engine_regs[] = { + hw_engine_regs(1), + hw_engine_regs(2), + hw_engine_regs(3), + hw_engine_regs(4), + hw_engine_regs(5), + hw_engine_regs(6) +}; + +static const struct dce110_i2c_hw_engine_shift i2c_shift = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce110_i2c_hw_engine_mask i2c_mask = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +static void construct( + struct i2caux_dce110 *i2caux_dce110, + struct dc_context *ctx) +{ + dal_i2caux_dce110_construct(i2caux_dce110, + ctx, + dce112_aux_regs, + dce112_hw_engine_regs, + &i2c_shift, + &i2c_mask); +} + +/* + * dal_i2caux_dce110_create + * + * @brief + * public interface to allocate memory for DCE11 I2CAUX + * + * @param + * struct adapter_service *as - [in] + * struct dc_context *ctx - [in] + * + * @return + * pointer to the base struct of DCE11 I2CAUX + */ +struct i2caux *dal_i2caux_dce112_create( + struct dc_context *ctx) +{ + struct i2caux_dce110 *i2caux_dce110 = + kzalloc(sizeof(struct i2caux_dce110), GFP_KERNEL); + + if (!i2caux_dce110) { + ASSERT_CRITICAL(false); + return NULL; + } + + construct(i2caux_dce110, ctx); + return &i2caux_dce110->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce112/i2caux_dce112.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce112/i2caux_dce112.h new file mode 100644 index 000000000000..8d35453c25b6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce112/i2caux_dce112.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DCE112_H__ +#define __DAL_I2C_AUX_DCE112_H__ + +struct i2caux *dal_i2caux_dce112_create( + struct dc_context *ctx); + +#endif /* __DAL_I2C_AUX_DCE112_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce120/i2caux_dce120.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce120/i2caux_dce120.c new file mode 100644 index 000000000000..668981a4c285 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce120/i2caux_dce120.c @@ -0,0 +1,118 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/i2caux_interface.h" +#include "../i2caux.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "../i2c_hw_engine.h" + +#include "../dce110/i2c_hw_engine_dce110.h" +#include "../dce110/aux_engine_dce110.h" +#include "../dce110/i2caux_dce110.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define SR(reg_name)\ + .reg_name = BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +#define SRI(reg_name, block, id)\ + .reg_name = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name +/* macros to expend register list macro defined in HW object header file + * end *********************/ + +#define aux_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = DP_AUX0_AUX_CONTROL__AUX_RESET_MASK \ +} + +static const struct dce110_aux_registers dce120_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5), +}; + +#define hw_engine_regs(id)\ +{\ + I2C_HW_ENGINE_COMMON_REG_LIST(id) \ +} + +static const struct dce110_i2c_hw_engine_registers dce120_hw_engine_regs[] = { + hw_engine_regs(1), + hw_engine_regs(2), + hw_engine_regs(3), + hw_engine_regs(4), + hw_engine_regs(5), + hw_engine_regs(6) +}; + +static const struct dce110_i2c_hw_engine_shift i2c_shift = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce110_i2c_hw_engine_mask i2c_mask = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +struct i2caux *dal_i2caux_dce120_create( + struct dc_context *ctx) +{ + struct i2caux_dce110 *i2caux_dce110 = + kzalloc(sizeof(struct i2caux_dce110), GFP_KERNEL); + + if (!i2caux_dce110) { + ASSERT_CRITICAL(false); + return NULL; + } + + dal_i2caux_dce110_construct(i2caux_dce110, + ctx, + dce120_aux_regs, + dce120_hw_engine_regs, + &i2c_shift, + &i2c_mask); + return &i2caux_dce110->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce120/i2caux_dce120.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce120/i2caux_dce120.h new file mode 100644 index 000000000000..b6ac47617c70 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce120/i2caux_dce120.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DCE120_H__ +#define __DAL_I2C_AUX_DCE120_H__ + +struct i2caux *dal_i2caux_dce120_create( + struct dc_context *ctx); + +#endif /* __DAL_I2C_AUX_DCE120_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_hw_engine_dce80.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_hw_engine_dce80.c new file mode 100644 index 000000000000..fd0832dd2c75 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_hw_engine_dce80.c @@ -0,0 +1,875 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_hw_engine.h" +#include "../i2c_generic_hw_engine.h" +/* + * Header of this unit + */ + +#include "i2c_hw_engine_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" +/* + * This unit + */ + +enum dc_i2c_status { + DC_I2C_STATUS__DC_I2C_STATUS_IDLE, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW +}; + +enum dc_i2c_arbitration { + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_HIGH +}; + +enum { + /* No timeout in HW + * (timeout implemented in SW by querying status) */ + I2C_SETUP_TIME_LIMIT = 255, + I2C_HW_BUFFER_SIZE = 144 +}; + +/* + * @brief + * Cast 'struct i2c_hw_engine *' + * to 'struct i2c_hw_engine_dce80 *' + */ +#define FROM_I2C_HW_ENGINE(ptr) \ + container_of((ptr), struct i2c_hw_engine_dce80, base) + +/* + * @brief + * Cast pointer to 'struct i2c_engine *' + * to pointer to 'struct i2c_hw_engine_dce80 *' + */ +#define FROM_I2C_ENGINE(ptr) \ + FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) + +/* + * @brief + * Cast pointer to 'struct engine *' + * to 'pointer to struct i2c_hw_engine_dce80 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +static void disable_i2c_hw_engine( + struct i2c_hw_engine_dce80 *engine) +{ + const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; + uint32_t value = 0; + + struct dc_context *ctx = NULL; + + ctx = engine->base.base.base.ctx; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_ENABLE); + + dm_write_reg(ctx, addr, value); +} + +static void release_engine( + struct engine *engine) +{ + struct i2c_hw_engine_dce80 *hw_engine = FROM_ENGINE(engine); + + struct i2c_engine *base = NULL; + bool safe_to_reset; + uint32_t value = 0; + + base = &hw_engine->base.base; + + /* Restore original HW engine speed */ + + base->funcs->set_speed(base, hw_engine->base.original_speed); + + /* Release I2C */ + { + value = dm_read_reg(engine->ctx, mmDC_I2C_ARBITRATION); + + set_reg_field_value( + value, + 1, + DC_I2C_ARBITRATION, + DC_I2C_SW_DONE_USING_I2C_REG); + + dm_write_reg(engine->ctx, mmDC_I2C_ARBITRATION, value); + } + + /* Reset HW engine */ + { + uint32_t i2c_sw_status = 0; + + value = dm_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + /* if used by SW, safe to reset */ + safe_to_reset = (i2c_sw_status == 1); + } + { + value = dm_read_reg(engine->ctx, mmDC_I2C_CONTROL); + + if (safe_to_reset) + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + dm_write_reg(engine->ctx, mmDC_I2C_CONTROL, value); + } + + /* HW I2c engine - clock gating feature */ + if (!hw_engine->engine_keep_power_up_count) + disable_i2c_hw_engine(hw_engine); +} + +static void destruct( + struct i2c_hw_engine_dce80 *engine) +{ + dal_i2c_hw_engine_destruct(&engine->base); +} + +static void destroy( + struct i2c_engine **i2c_engine) +{ + struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(*i2c_engine); + + destruct(engine); + + kfree(engine); + + *i2c_engine = NULL; +} + +static bool setup_engine( + struct i2c_engine *i2c_engine) +{ + uint32_t value = 0; + struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(i2c_engine); + + /* Program pin select */ + { + const uint32_t addr = mmDC_I2C_CONTROL; + + value = dm_read_reg(i2c_engine->base.ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_GO); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SEND_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_TRANSACTION_COUNT); + + set_reg_field_value( + value, + engine->engine_id, + DC_I2C_CONTROL, + DC_I2C_DDC_SELECT); + + dm_write_reg(i2c_engine->base.ctx, addr, value); + } + + /* Program time limit */ + { + const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; + + value = dm_read_reg(i2c_engine->base.ctx, addr); + + set_reg_field_value( + value, + I2C_SETUP_TIME_LIMIT, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_TIME_LIMIT); + + set_reg_field_value( + value, + 1, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_ENABLE); + + dm_write_reg(i2c_engine->base.ctx, addr, value); + } + + /* Program HW priority + * set to High - interrupt software I2C at any time + * Enable restart of SW I2C that was interrupted by HW + * disable queuing of software while I2C is in use by HW */ + { + value = dm_read_reg(i2c_engine->base.ctx, + mmDC_I2C_ARBITRATION); + + set_reg_field_value( + value, + 0, + DC_I2C_ARBITRATION, + DC_I2C_NO_QUEUED_SW_GO); + + set_reg_field_value( + value, + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, + DC_I2C_ARBITRATION, + DC_I2C_SW_PRIORITY); + + dm_write_reg(i2c_engine->base.ctx, + mmDC_I2C_ARBITRATION, value); + } + + return true; +} + +static uint32_t get_speed( + const struct i2c_engine *i2c_engine) +{ + const struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(i2c_engine); + + const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED; + + uint32_t pre_scale = 0; + + uint32_t value = dm_read_reg(i2c_engine->base.ctx, addr); + + pre_scale = get_reg_field_value( + value, + DC_I2C_DDC1_SPEED, + DC_I2C_DDC1_PRESCALE); + + /* [anaumov] it seems following is unnecessary */ + /*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/ + + return pre_scale ? + engine->reference_frequency / pre_scale : + engine->base.default_speed; +} + +static void set_speed( + struct i2c_engine *i2c_engine, + uint32_t speed) +{ + struct i2c_hw_engine_dce80 *engine = FROM_I2C_ENGINE(i2c_engine); + + if (speed) { + const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED; + + uint32_t value = dm_read_reg(i2c_engine->base.ctx, addr); + + set_reg_field_value( + value, + engine->reference_frequency / speed, + DC_I2C_DDC1_SPEED, + DC_I2C_DDC1_PRESCALE); + + set_reg_field_value( + value, + 2, + DC_I2C_DDC1_SPEED, + DC_I2C_DDC1_THRESHOLD); + + dm_write_reg(i2c_engine->base.ctx, addr, value); + } +} + +static inline void reset_hw_engine(struct engine *engine) +{ + uint32_t value = dm_read_reg(engine->ctx, mmDC_I2C_CONTROL); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + dm_write_reg(engine->ctx, mmDC_I2C_CONTROL, value); +} + +static bool is_hw_busy(struct engine *engine) +{ + uint32_t i2c_sw_status = 0; + + uint32_t value = dm_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_IDLE) + return false; + + reset_hw_engine(engine); + + value = dm_read_reg(engine->ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + + return i2c_sw_status != DC_I2C_STATUS__DC_I2C_STATUS_IDLE; +} + +/* + * @brief + * DC_GPIO_DDC MM register offsets + */ +static const uint32_t transaction_addr[] = { + mmDC_I2C_TRANSACTION0, + mmDC_I2C_TRANSACTION1, + mmDC_I2C_TRANSACTION2, + mmDC_I2C_TRANSACTION3 +}; + +static bool process_transaction( + struct i2c_hw_engine_dce80 *engine, + struct i2c_request_transaction_data *request) +{ + uint32_t length = request->length; + uint8_t *buffer = request->data; + + bool last_transaction = false; + uint32_t value = 0; + + struct dc_context *ctx = NULL; + + ctx = engine->base.base.base.ctx; + + { + const uint32_t addr = + transaction_addr[engine->transaction_count]; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 1, + DC_I2C_TRANSACTION0, + DC_I2C_STOP_ON_NACK0); + + set_reg_field_value( + value, + 1, + DC_I2C_TRANSACTION0, + DC_I2C_START0); + + if ((engine->transaction_count == 3) || + (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || + (request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { + + set_reg_field_value( + value, + 1, + DC_I2C_TRANSACTION0, + DC_I2C_STOP0); + + last_transaction = true; + } else + set_reg_field_value( + value, + 0, + DC_I2C_TRANSACTION0, + DC_I2C_STOP0); + + set_reg_field_value( + value, + (0 != (request->action & + I2CAUX_TRANSACTION_ACTION_I2C_READ)), + DC_I2C_TRANSACTION0, + DC_I2C_RW0); + + set_reg_field_value( + value, + length, + DC_I2C_TRANSACTION0, + DC_I2C_COUNT0); + + dm_write_reg(ctx, addr, value); + } + + /* Write the I2C address and I2C data + * into the hardware circular buffer, one byte per entry. + * As an example, the 7-bit I2C slave address for CRT monitor + * for reading DDC/EDID information is 0b1010001. + * For an I2C send operation, the LSB must be programmed to 0; + * for I2C receive operation, the LSB must be programmed to 1. */ + + { + value = 0; + + set_reg_field_value( + value, + false, + DC_I2C_DATA, + DC_I2C_DATA_RW); + + set_reg_field_value( + value, + request->address, + DC_I2C_DATA, + DC_I2C_DATA); + + if (engine->transaction_count == 0) { + set_reg_field_value( + value, + 0, + DC_I2C_DATA, + DC_I2C_INDEX); + + /*enable index write*/ + set_reg_field_value( + value, + 1, + DC_I2C_DATA, + DC_I2C_INDEX_WRITE); + } + + dm_write_reg(ctx, mmDC_I2C_DATA, value); + + if (!(request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { + + set_reg_field_value( + value, + 0, + DC_I2C_DATA, + DC_I2C_INDEX_WRITE); + + while (length) { + + set_reg_field_value( + value, + *buffer++, + DC_I2C_DATA, + DC_I2C_DATA); + + dm_write_reg(ctx, mmDC_I2C_DATA, value); + --length; + } + } + } + + ++engine->transaction_count; + engine->buffer_used_bytes += length + 1; + + return last_transaction; +} + +static void execute_transaction( + struct i2c_hw_engine_dce80 *engine) +{ + uint32_t value = 0; + struct dc_context *ctx = NULL; + + ctx = engine->base.base.base.ctx; + + { + const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_DATA_DRIVE_EN); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_CLK_DRIVE_EN); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_DATA_DRIVE_SEL); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_INTRA_TRANSACTION_DELAY); + + set_reg_field_value( + value, + 0, + DC_I2C_DDC1_SETUP, + DC_I2C_DDC1_INTRA_BYTE_DELAY); + + dm_write_reg(ctx, addr, value); + } + + { + const uint32_t addr = mmDC_I2C_CONTROL; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SOFT_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_SEND_RESET); + + set_reg_field_value( + value, + 0, + DC_I2C_CONTROL, + DC_I2C_GO); + + set_reg_field_value( + value, + engine->transaction_count - 1, + DC_I2C_CONTROL, + DC_I2C_TRANSACTION_COUNT); + + dm_write_reg(ctx, addr, value); + } + + /* start I2C transfer */ + { + const uint32_t addr = mmDC_I2C_CONTROL; + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 1, + DC_I2C_CONTROL, + DC_I2C_GO); + + dm_write_reg(ctx, addr, value); + } + + /* all transactions were executed and HW buffer became empty + * (even though it actually happens when status becomes DONE) */ + engine->transaction_count = 0; + engine->buffer_used_bytes = 0; +} + +static void submit_channel_request( + struct i2c_engine *engine, + struct i2c_request_transaction_data *request) +{ + request->status = I2C_CHANNEL_OPERATION_SUCCEEDED; + + if (!process_transaction(FROM_I2C_ENGINE(engine), request)) + return; + + if (is_hw_busy(&engine->base)) { + request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; + return; + } + + execute_transaction(FROM_I2C_ENGINE(engine)); +} + +static void process_channel_reply( + struct i2c_engine *engine, + struct i2c_reply_transaction_data *reply) +{ + uint32_t length = reply->length; + uint8_t *buffer = reply->data; + + uint32_t value = 0; + + /*set index*/ + set_reg_field_value( + value, + length - 1, + DC_I2C_DATA, + DC_I2C_INDEX); + + set_reg_field_value( + value, + 1, + DC_I2C_DATA, + DC_I2C_DATA_RW); + + set_reg_field_value( + value, + 1, + DC_I2C_DATA, + DC_I2C_INDEX_WRITE); + + dm_write_reg(engine->base.ctx, mmDC_I2C_DATA, value); + + while (length) { + /* after reading the status, + * if the I2C operation executed successfully + * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller + * should read data bytes from I2C circular data buffer */ + + value = dm_read_reg(engine->base.ctx, mmDC_I2C_DATA); + + *buffer++ = get_reg_field_value( + value, + DC_I2C_DATA, + DC_I2C_DATA); + + --length; + } +} + +static enum i2c_channel_operation_result get_channel_status( + struct i2c_engine *engine, + uint8_t *returned_bytes) +{ + uint32_t i2c_sw_status = 0; + uint32_t value = dm_read_reg(engine->base.ctx, mmDC_I2C_SW_STATUS); + + i2c_sw_status = get_reg_field_value( + value, + DC_I2C_SW_STATUS, + DC_I2C_SW_STATUS); + + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) + return I2C_CHANNEL_OPERATION_ENGINE_BUSY; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_STOPPED_ON_NACK_MASK) + return I2C_CHANNEL_OPERATION_NO_RESPONSE; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_TIMEOUT_MASK) + return I2C_CHANNEL_OPERATION_TIMEOUT; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_ABORTED_MASK) + return I2C_CHANNEL_OPERATION_FAILED; + else if (value & DC_I2C_SW_STATUS__DC_I2C_SW_DONE_MASK) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + /* + * this is the case when HW used for communication, I2C_SW_STATUS + * could be zero + */ + return I2C_CHANNEL_OPERATION_SUCCEEDED; +} + +static uint32_t get_hw_buffer_available_size( + const struct i2c_hw_engine *engine) +{ + return I2C_HW_BUFFER_SIZE - + FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes; +} + +static uint32_t get_transaction_timeout( + const struct i2c_hw_engine *engine, + uint32_t length) +{ + uint32_t speed = engine->base.funcs->get_speed(&engine->base); + + uint32_t period_timeout; + uint32_t num_of_clock_stretches; + + if (!speed) + return 0; + + period_timeout = (1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed; + + num_of_clock_stretches = 1 + (length << 3) + 1; + num_of_clock_stretches += + (FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes << 3) + + (FROM_I2C_HW_ENGINE(engine)->transaction_count << 1); + + return period_timeout * num_of_clock_stretches; +} + +/* + * @brief + * DC_I2C_DDC1_SETUP MM register offsets + * + * @note + * The indices of this offset array are DDC engine IDs + */ +static const int32_t ddc_setup_offset[] = { + + mmDC_I2C_DDC1_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 1 */ + mmDC_I2C_DDC2_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 2 */ + mmDC_I2C_DDC3_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 3 */ + mmDC_I2C_DDC4_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 4 */ + mmDC_I2C_DDC5_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 5 */ + mmDC_I2C_DDC6_SETUP - mmDC_I2C_DDC1_SETUP, /* DDC Engine 6 */ + mmDC_I2C_DDCVGA_SETUP - mmDC_I2C_DDC1_SETUP /* DDC Engine 7 */ +}; + +/* + * @brief + * DC_I2C_DDC1_SPEED MM register offsets + * + * @note + * The indices of this offset array are DDC engine IDs + */ +static const int32_t ddc_speed_offset[] = { + mmDC_I2C_DDC1_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 1 */ + mmDC_I2C_DDC2_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 2 */ + mmDC_I2C_DDC3_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 3 */ + mmDC_I2C_DDC4_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 4 */ + mmDC_I2C_DDC5_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 5 */ + mmDC_I2C_DDC6_SPEED - mmDC_I2C_DDC1_SPEED, /* DDC Engine 6 */ + mmDC_I2C_DDCVGA_SPEED - mmDC_I2C_DDC1_SPEED /* DDC Engine 7 */ +}; + +static const struct i2c_engine_funcs i2c_engine_funcs = { + .destroy = destroy, + .get_speed = get_speed, + .set_speed = set_speed, + .setup_engine = setup_engine, + .submit_channel_request = submit_channel_request, + .process_channel_reply = process_channel_reply, + .get_channel_status = get_channel_status, + .acquire_engine = dal_i2c_hw_engine_acquire_engine, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .get_engine_type = dal_i2c_hw_engine_get_engine_type, + .acquire = dal_i2c_engine_acquire, + .submit_request = dal_i2c_hw_engine_submit_request, +}; + +static const struct i2c_hw_engine_funcs i2c_hw_engine_funcs = { + .get_hw_buffer_available_size = + get_hw_buffer_available_size, + .get_transaction_timeout = + get_transaction_timeout, + .wait_on_operation_result = + dal_i2c_hw_engine_wait_on_operation_result, +}; + +static void construct( + struct i2c_hw_engine_dce80 *engine, + const struct i2c_hw_engine_dce80_create_arg *arg) +{ + dal_i2c_hw_engine_construct(&engine->base, arg->ctx); + + engine->base.base.base.funcs = &engine_funcs; + engine->base.base.funcs = &i2c_engine_funcs; + engine->base.funcs = &i2c_hw_engine_funcs; + engine->base.default_speed = arg->default_speed; + engine->addr.DC_I2C_DDCX_SETUP = + mmDC_I2C_DDC1_SETUP + ddc_setup_offset[arg->engine_id]; + engine->addr.DC_I2C_DDCX_SPEED = + mmDC_I2C_DDC1_SPEED + ddc_speed_offset[arg->engine_id]; + + engine->engine_id = arg->engine_id; + engine->reference_frequency = arg->reference_frequency; + engine->buffer_used_bytes = 0; + engine->transaction_count = 0; + engine->engine_keep_power_up_count = 1; +} + +struct i2c_engine *dal_i2c_hw_engine_dce80_create( + const struct i2c_hw_engine_dce80_create_arg *arg) +{ + struct i2c_hw_engine_dce80 *engine; + + if (!arg) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if ((arg->engine_id >= sizeof(ddc_setup_offset) / sizeof(int32_t)) || + (arg->engine_id >= sizeof(ddc_speed_offset) / sizeof(int32_t)) || + !arg->reference_frequency) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + engine = kzalloc(sizeof(struct i2c_hw_engine_dce80), GFP_KERNEL); + + if (!engine) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + construct(engine, arg); + return &engine->base.base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_hw_engine_dce80.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_hw_engine_dce80.h new file mode 100644 index 000000000000..5c6116fb5479 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_hw_engine_dce80.h @@ -0,0 +1,54 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_HW_ENGINE_DCE80_H__ +#define __DAL_I2C_HW_ENGINE_DCE80_H__ + +struct i2c_hw_engine_dce80 { + struct i2c_hw_engine base; + struct { + uint32_t DC_I2C_DDCX_SETUP; + uint32_t DC_I2C_DDCX_SPEED; + } addr; + uint32_t engine_id; + /* expressed in kilohertz */ + uint32_t reference_frequency; + /* number of bytes currently used in HW buffer */ + uint32_t buffer_used_bytes; + /* number of pending transactions (before GO) */ + uint32_t transaction_count; + uint32_t engine_keep_power_up_count; +}; + +struct i2c_hw_engine_dce80_create_arg { + uint32_t engine_id; + uint32_t reference_frequency; + uint32_t default_speed; + struct dc_context *ctx; +}; + +struct i2c_engine *dal_i2c_hw_engine_dce80_create( + const struct i2c_hw_engine_dce80_create_arg *arg); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_sw_engine_dce80.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_sw_engine_dce80.c new file mode 100644 index 000000000000..4853ee26096a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_sw_engine_dce80.c @@ -0,0 +1,173 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" + +/* + * Header of this unit + */ + +#include "i2c_sw_engine_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +/* + * This unit + */ + +static const uint32_t ddc_hw_status_addr[] = { + mmDC_I2C_DDC1_HW_STATUS, + mmDC_I2C_DDC2_HW_STATUS, + mmDC_I2C_DDC3_HW_STATUS, + mmDC_I2C_DDC4_HW_STATUS, + mmDC_I2C_DDC5_HW_STATUS, + mmDC_I2C_DDC6_HW_STATUS, + mmDC_I2C_DDCVGA_HW_STATUS +}; + +/* + * @brief + * Cast 'struct i2c_sw_engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_I2C_SW_ENGINE(ptr) \ + container_of((ptr), struct i2c_sw_engine_dce80, base) + +/* + * @brief + * Cast 'struct i2c_engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_I2C_ENGINE(ptr) \ + FROM_I2C_SW_ENGINE(container_of((ptr), struct i2c_sw_engine, base)) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct i2c_sw_engine_dce80 *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +static void release_engine( + struct engine *engine) +{ + +} + +static void destruct( + struct i2c_sw_engine_dce80 *engine) +{ + dal_i2c_sw_engine_destruct(&engine->base); +} + +static void destroy( + struct i2c_engine **engine) +{ + struct i2c_sw_engine_dce80 *sw_engine = FROM_I2C_ENGINE(*engine); + + destruct(sw_engine); + + kfree(sw_engine); + + *engine = NULL; +} + +static bool acquire_engine( + struct i2c_engine *engine, + struct ddc *ddc_handle) +{ + return dal_i2caux_i2c_sw_engine_acquire_engine(engine, ddc_handle); +} + +static const struct i2c_engine_funcs i2c_engine_funcs = { + .acquire_engine = acquire_engine, + .destroy = destroy, + .get_speed = dal_i2c_sw_engine_get_speed, + .set_speed = dal_i2c_sw_engine_set_speed, + .setup_engine = dal_i2c_engine_setup_i2c_engine, + .submit_channel_request = dal_i2c_sw_engine_submit_channel_request, + .process_channel_reply = dal_i2c_engine_process_channel_reply, + .get_channel_status = dal_i2c_sw_engine_get_channel_status, +}; + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .get_engine_type = dal_i2c_sw_engine_get_engine_type, + .acquire = dal_i2c_engine_acquire, + .submit_request = dal_i2c_sw_engine_submit_request, +}; + +static void construct( + struct i2c_sw_engine_dce80 *engine, + const struct i2c_sw_engine_dce80_create_arg *arg) +{ + struct i2c_sw_engine_create_arg arg_base; + + arg_base.ctx = arg->ctx; + arg_base.default_speed = arg->default_speed; + + dal_i2c_sw_engine_construct(&engine->base, &arg_base); + + engine->base.base.base.funcs = &engine_funcs; + engine->base.base.funcs = &i2c_engine_funcs; + engine->base.default_speed = arg->default_speed; + engine->engine_id = arg->engine_id; +} + +struct i2c_engine *dal_i2c_sw_engine_dce80_create( + const struct i2c_sw_engine_dce80_create_arg *arg) +{ + struct i2c_sw_engine_dce80 *engine; + + if (!arg) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + engine = kzalloc(sizeof(struct i2c_sw_engine_dce80), GFP_KERNEL); + + if (!engine) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + construct(engine, arg); + return &engine->base.base; +} + diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_sw_engine_dce80.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_sw_engine_dce80.h new file mode 100644 index 000000000000..26355c088746 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2c_sw_engine_dce80.h @@ -0,0 +1,43 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_SW_ENGINE_DCE80_H__ +#define __DAL_I2C_SW_ENGINE_DCE80_H__ + +struct i2c_sw_engine_dce80 { + struct i2c_sw_engine base; + uint32_t engine_id; +}; + +struct i2c_sw_engine_dce80_create_arg { + uint32_t engine_id; + uint32_t default_speed; + struct dc_context *ctx; +}; + +struct i2c_engine *dal_i2c_sw_engine_dce80_create( + const struct i2c_sw_engine_dce80_create_arg *arg); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.c new file mode 100644 index 000000000000..ed48596dd2a5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.c @@ -0,0 +1,284 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../i2caux.h" + +/* + * Header of this unit + */ + +#include "i2caux_dce80.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "i2c_sw_engine_dce80.h" +#include "../i2c_hw_engine.h" +#include "i2c_hw_engine_dce80.h" +#include "../i2c_generic_hw_engine.h" +#include "../aux_engine.h" + + +#include "../dce110/aux_engine_dce110.h" +#include "../dce110/i2caux_dce110.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +#define aux_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers dce80_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +/* + * This unit + */ + +#define FROM_I2C_AUX(ptr) \ + container_of((ptr), struct i2caux_dce80, base) + +static void destruct( + struct i2caux_dce80 *i2caux_dce80) +{ + dal_i2caux_destruct(&i2caux_dce80->base); +} + +static void destroy( + struct i2caux **i2c_engine) +{ + struct i2caux_dce80 *i2caux_dce80 = FROM_I2C_AUX(*i2c_engine); + + destruct(i2caux_dce80); + + kfree(i2caux_dce80); + + *i2c_engine = NULL; +} + +static struct i2c_engine *acquire_i2c_hw_engine( + struct i2caux *i2caux, + struct ddc *ddc) +{ + struct i2caux_dce80 *i2caux_dce80 = FROM_I2C_AUX(i2caux); + + struct i2c_engine *engine = NULL; + bool non_generic; + + if (!ddc) + return NULL; + + if (ddc->hw_info.hw_supported) { + enum gpio_ddc_line line = dal_ddc_get_line(ddc); + + if (line < GPIO_DDC_LINE_COUNT) { + non_generic = true; + engine = i2caux->i2c_hw_engines[line]; + } + } + + if (!engine) { + non_generic = false; + engine = i2caux->i2c_generic_hw_engine; + } + + if (!engine) + return NULL; + + if (non_generic) { + if (!i2caux_dce80->i2c_hw_buffer_in_use && + engine->base.funcs->acquire(&engine->base, ddc)) { + i2caux_dce80->i2c_hw_buffer_in_use = true; + return engine; + } + } else { + if (engine->base.funcs->acquire(&engine->base, ddc)) + return engine; + } + + return NULL; +} + +static void release_engine( + struct i2caux *i2caux, + struct engine *engine) +{ + if (engine->funcs->get_engine_type(engine) == + I2CAUX_ENGINE_TYPE_I2C_DDC_HW) + FROM_I2C_AUX(i2caux)->i2c_hw_buffer_in_use = false; + + dal_i2caux_release_engine(i2caux, engine); +} + +static const enum gpio_ddc_line hw_ddc_lines[] = { + GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_DDC2, + GPIO_DDC_LINE_DDC3, + GPIO_DDC_LINE_DDC4, + GPIO_DDC_LINE_DDC5, + GPIO_DDC_LINE_DDC6, + GPIO_DDC_LINE_DDC_VGA +}; + +static const enum gpio_ddc_line hw_aux_lines[] = { + GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_DDC2, + GPIO_DDC_LINE_DDC3, + GPIO_DDC_LINE_DDC4, + GPIO_DDC_LINE_DDC5, + GPIO_DDC_LINE_DDC6 +}; + +static const struct i2caux_funcs i2caux_funcs = { + .destroy = destroy, + .acquire_i2c_hw_engine = acquire_i2c_hw_engine, + .release_engine = release_engine, + .acquire_i2c_sw_engine = dal_i2caux_acquire_i2c_sw_engine, + .acquire_aux_engine = dal_i2caux_acquire_aux_engine, +}; + +static void construct( + struct i2caux_dce80 *i2caux_dce80, + struct dc_context *ctx) +{ + /* Entire family have I2C engine reference clock frequency + * changed from XTALIN (27) to XTALIN/2 (13.5) */ + + struct i2caux *base = &i2caux_dce80->base; + + uint32_t reference_frequency = + dal_i2caux_get_reference_clock(ctx->dc_bios) >> 1; + + /*bool use_i2c_sw_engine = dal_adapter_service_is_feature_supported(as, + FEATURE_RESTORE_USAGE_I2C_SW_ENGINE);*/ + + /* Use SWI2C for dce8 currently, sicne we have bug with hwi2c */ + bool use_i2c_sw_engine = true; + + uint32_t i; + + dal_i2caux_construct(base, ctx); + + i2caux_dce80->base.funcs = &i2caux_funcs; + i2caux_dce80->i2c_hw_buffer_in_use = false; + + /* Create I2C HW engines (HW + SW pairs) + * for all lines which has assisted HW DDC + * 'i' (loop counter) used as DDC/AUX engine_id */ + + i = 0; + + do { + enum gpio_ddc_line line_id = hw_ddc_lines[i]; + + struct i2c_hw_engine_dce80_create_arg hw_arg; + + if (use_i2c_sw_engine) { + struct i2c_sw_engine_dce80_create_arg sw_arg; + + sw_arg.engine_id = i; + sw_arg.default_speed = base->default_i2c_sw_speed; + sw_arg.ctx = ctx; + base->i2c_sw_engines[line_id] = + dal_i2c_sw_engine_dce80_create(&sw_arg); + } + + hw_arg.engine_id = i; + hw_arg.reference_frequency = reference_frequency; + hw_arg.default_speed = base->default_i2c_hw_speed; + hw_arg.ctx = ctx; + + base->i2c_hw_engines[line_id] = + dal_i2c_hw_engine_dce80_create(&hw_arg); + + ++i; + } while (i < ARRAY_SIZE(hw_ddc_lines)); + + /* Create AUX engines for all lines which has assisted HW AUX + * 'i' (loop counter) used as DDC/AUX engine_id */ + + i = 0; + + do { + enum gpio_ddc_line line_id = hw_aux_lines[i]; + + struct aux_engine_dce110_init_data arg; + + arg.engine_id = i; + arg.timeout_period = base->aux_timeout_period; + arg.ctx = ctx; + arg.regs = &dce80_aux_regs[i]; + + base->aux_engines[line_id] = + dal_aux_engine_dce110_create(&arg); + + ++i; + } while (i < ARRAY_SIZE(hw_aux_lines)); + + /* TODO Generic I2C SW and HW */ +} + +struct i2caux *dal_i2caux_dce80_create( + struct dc_context *ctx) +{ + struct i2caux_dce80 *i2caux_dce80 = + kzalloc(sizeof(struct i2caux_dce80), GFP_KERNEL); + + if (!i2caux_dce80) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + construct(i2caux_dce80, ctx); + return &i2caux_dce80->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.h b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.h new file mode 100644 index 000000000000..21908629e973 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.h @@ -0,0 +1,38 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DCE80_H__ +#define __DAL_I2C_AUX_DCE80_H__ + +struct i2caux_dce80 { + struct i2caux base; + /* indicate the I2C HW circular buffer is in use */ + bool i2c_hw_buffer_in_use; +}; + +struct i2caux *dal_i2caux_dce80_create( + struct dc_context *ctx); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dcn10/i2caux_dcn10.c b/drivers/gpu/drm/amd/display/dc/i2caux/dcn10/i2caux_dcn10.c new file mode 100644 index 000000000000..13b807d8aff8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dcn10/i2caux_dcn10.c @@ -0,0 +1,118 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/i2caux_interface.h" +#include "../i2caux.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "../i2c_hw_engine.h" + +#include "../dce110/aux_engine_dce110.h" +#include "../dce110/i2c_hw_engine_dce110.h" +#include "../dce110/i2caux_dce110.h" + +#include "raven1/DCN/dcn_1_0_offset.h" +#include "raven1/DCN/dcn_1_0_sh_mask.h" +#include "vega10/soc15ip.h" + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define SR(reg_name)\ + .reg_name = BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +#define SRI(reg_name, block, id)\ + .reg_name = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name +/* macros to expend register list macro defined in HW object header file + * end *********************/ + +#define aux_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = DP_AUX0_AUX_CONTROL__AUX_RESET_MASK \ +} + +#define hw_engine_regs(id)\ +{\ + I2C_HW_ENGINE_COMMON_REG_LIST(id) \ +} + +static const struct dce110_aux_registers dcn10_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5), +}; + +static const struct dce110_i2c_hw_engine_registers dcn10_hw_engine_regs[] = { + hw_engine_regs(1), + hw_engine_regs(2), + hw_engine_regs(3), + hw_engine_regs(4), + hw_engine_regs(5), + hw_engine_regs(6) +}; + +static const struct dce110_i2c_hw_engine_shift i2c_shift = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce110_i2c_hw_engine_mask i2c_mask = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +struct i2caux *dal_i2caux_dcn10_create( + struct dc_context *ctx) +{ + struct i2caux_dce110 *i2caux_dce110 = + kzalloc(sizeof(struct i2caux_dce110), GFP_KERNEL); + + if (!i2caux_dce110) { + ASSERT_CRITICAL(false); + return NULL; + } + + dal_i2caux_dce110_construct(i2caux_dce110, + ctx, + dcn10_aux_regs, + dcn10_hw_engine_regs, + &i2c_shift, + &i2c_mask); + return &i2caux_dce110->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dcn10/i2caux_dcn10.h b/drivers/gpu/drm/amd/display/dc/i2caux/dcn10/i2caux_dcn10.h new file mode 100644 index 000000000000..aeb4a86463d4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dcn10/i2caux_dcn10.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DCN10_H__ +#define __DAL_I2C_AUX_DCN10_H__ + +struct i2caux *dal_i2caux_dcn10_create( + struct dc_context *ctx); + +#endif /* __DAL_I2C_AUX_DCN10_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/diagnostics/i2caux_diag.c b/drivers/gpu/drm/amd/display/dc/i2caux/diagnostics/i2caux_diag.c new file mode 100644 index 000000000000..e6408f644086 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/diagnostics/i2caux_diag.c @@ -0,0 +1,97 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "../i2caux.h" +#include "../engine.h" +#include "../i2c_engine.h" +#include "../i2c_sw_engine.h" +#include "../i2c_hw_engine.h" + +/* + * Header of this unit + */ +#include "i2caux_diag.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +static void destruct( + struct i2caux *i2caux) +{ + dal_i2caux_destruct(i2caux); +} + +static void destroy( + struct i2caux **i2c_engine) +{ + destruct(*i2c_engine); + + kfree(*i2c_engine); + + *i2c_engine = NULL; +} + +/* function table */ +static const struct i2caux_funcs i2caux_funcs = { + .destroy = destroy, + .acquire_i2c_hw_engine = NULL, + .release_engine = NULL, + .acquire_i2c_sw_engine = NULL, + .acquire_aux_engine = NULL, +}; + +static void construct( + struct i2caux *i2caux, + struct dc_context *ctx) +{ + dal_i2caux_construct(i2caux, ctx); + i2caux->funcs = &i2caux_funcs; +} + +struct i2caux *dal_i2caux_diag_fpga_create( + struct dc_context *ctx) +{ + struct i2caux *i2caux = kzalloc(sizeof(struct i2caux), + GFP_KERNEL); + + if (!i2caux) { + ASSERT_CRITICAL(false); + return NULL; + } + + construct(i2caux, ctx); + return i2caux; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/diagnostics/i2caux_diag.h b/drivers/gpu/drm/amd/display/dc/i2caux/diagnostics/i2caux_diag.h new file mode 100644 index 000000000000..a83eeb748283 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/diagnostics/i2caux_diag.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_DIAG_FPGA_H__ +#define __DAL_I2C_AUX_DIAG_FPGA_H__ + +struct i2caux *dal_i2caux_diag_fpga_create( + struct dc_context *ctx); + +#endif /* __DAL_I2C_AUX_DIAG_FPGA_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/engine.h b/drivers/gpu/drm/amd/display/dc/i2caux/engine.h new file mode 100644 index 000000000000..33de8a8834dc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/engine.h @@ -0,0 +1,120 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_ENGINE_H__ +#define __DAL_ENGINE_H__ + +enum i2caux_transaction_operation { + I2CAUX_TRANSACTION_READ, + I2CAUX_TRANSACTION_WRITE +}; + +enum i2caux_transaction_address_space { + I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C = 1, + I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD +}; + +struct i2caux_transaction_payload { + enum i2caux_transaction_address_space address_space; + uint32_t address; + uint32_t length; + uint8_t *data; +}; + +enum i2caux_transaction_status { + I2CAUX_TRANSACTION_STATUS_UNKNOWN = (-1L), + I2CAUX_TRANSACTION_STATUS_SUCCEEDED, + I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY, + I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT, + I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR, + I2CAUX_TRANSACTION_STATUS_FAILED_NACK, + I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE, + I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION, + I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION, + I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW +}; + +struct i2caux_transaction_request { + enum i2caux_transaction_operation operation; + struct i2caux_transaction_payload payload; + enum i2caux_transaction_status status; +}; + +enum i2caux_engine_type { + I2CAUX_ENGINE_TYPE_UNKNOWN = (-1L), + I2CAUX_ENGINE_TYPE_AUX, + I2CAUX_ENGINE_TYPE_I2C_DDC_HW, + I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW, + I2CAUX_ENGINE_TYPE_I2C_SW +}; + +enum i2c_default_speed { + I2CAUX_DEFAULT_I2C_HW_SPEED = 50, + I2CAUX_DEFAULT_I2C_SW_SPEED = 50 +}; + +enum i2caux_transaction_action { + I2CAUX_TRANSACTION_ACTION_I2C_WRITE = 0x00, + I2CAUX_TRANSACTION_ACTION_I2C_READ = 0x10, + I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST = 0x20, + + I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT = 0x40, + I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT = 0x50, + I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT = 0x60, + + I2CAUX_TRANSACTION_ACTION_DP_WRITE = 0x80, + I2CAUX_TRANSACTION_ACTION_DP_READ = 0x90 +}; + +struct engine; + +struct engine_funcs { + enum i2caux_engine_type (*get_engine_type)( + const struct engine *engine); + bool (*acquire)( + struct engine *engine, + struct ddc *ddc); + bool (*submit_request)( + struct engine *engine, + struct i2caux_transaction_request *request, + bool middle_of_transaction); + void (*release_engine)( + struct engine *engine); +}; + +struct engine { + const struct engine_funcs *funcs; + struct ddc *ddc; + struct dc_context *ctx; +}; + +void dal_i2caux_construct_engine( + struct engine *engine, + struct dc_context *ctx); + +void dal_i2caux_destruct_engine( + struct engine *engine); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/engine_base.c b/drivers/gpu/drm/amd/display/dc/i2caux/engine_base.c new file mode 100644 index 000000000000..5d155d36d353 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/engine_base.c @@ -0,0 +1,52 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" + +/* + * Header of this unit + */ + +#include "engine.h" + +void dal_i2caux_construct_engine( + struct engine *engine, + struct dc_context *ctx) +{ + engine->ddc = NULL; + engine->ctx = ctx; +} + +void dal_i2caux_destruct_engine( + struct engine *engine) +{ + /* nothing to do */ +} + diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_engine.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_engine.c new file mode 100644 index 000000000000..70e20bd47ce4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_engine.c @@ -0,0 +1,118 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "engine.h" + +/* + * Header of this unit + */ + +#include "i2c_engine.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +#define FROM_ENGINE(ptr) \ + container_of((ptr), struct i2c_engine, base) + +bool dal_i2c_engine_acquire( + struct engine *engine, + struct ddc *ddc_handle) +{ + struct i2c_engine *i2c_engine = FROM_ENGINE(engine); + + uint32_t counter = 0; + bool result; + + do { + result = i2c_engine->funcs->acquire_engine( + i2c_engine, ddc_handle); + + if (result) + break; + + /* i2c_engine is busy by VBios, lets wait and retry */ + + udelay(10); + + ++counter; + } while (counter < 2); + + if (result) { + if (!i2c_engine->funcs->setup_engine(i2c_engine)) { + engine->funcs->release_engine(engine); + result = false; + } + } + + return result; +} + +bool dal_i2c_engine_setup_i2c_engine( + struct i2c_engine *engine) +{ + /* Derivative classes do not have to override this */ + + return true; +} + +void dal_i2c_engine_submit_channel_request( + struct i2c_engine *engine, + struct i2c_request_transaction_data *request) +{ + +} + +void dal_i2c_engine_process_channel_reply( + struct i2c_engine *engine, + struct i2c_reply_transaction_data *reply) +{ + +} + +void dal_i2c_engine_construct( + struct i2c_engine *engine, + struct dc_context *ctx) +{ + dal_i2caux_construct_engine(&engine->base, ctx); + engine->timeout_delay = 0; +} + +void dal_i2c_engine_destruct( + struct i2c_engine *engine) +{ + dal_i2caux_destruct_engine(&engine->base); +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_engine.h b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_engine.h new file mode 100644 index 000000000000..58fc0f25eceb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_engine.h @@ -0,0 +1,113 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_ENGINE_H__ +#define __DAL_I2C_ENGINE_H__ + +enum i2c_channel_operation_result { + I2C_CHANNEL_OPERATION_SUCCEEDED, + I2C_CHANNEL_OPERATION_FAILED, + I2C_CHANNEL_OPERATION_NOT_GRANTED, + I2C_CHANNEL_OPERATION_IS_BUSY, + I2C_CHANNEL_OPERATION_NO_HANDLE_PROVIDED, + I2C_CHANNEL_OPERATION_CHANNEL_IN_USE, + I2C_CHANNEL_OPERATION_CHANNEL_CLIENT_MAX_ALLOWED, + I2C_CHANNEL_OPERATION_ENGINE_BUSY, + I2C_CHANNEL_OPERATION_TIMEOUT, + I2C_CHANNEL_OPERATION_NO_RESPONSE, + I2C_CHANNEL_OPERATION_HW_REQUEST_I2C_BUS, + I2C_CHANNEL_OPERATION_WRONG_PARAMETER, + I2C_CHANNEL_OPERATION_OUT_NB_OF_RETRIES, + I2C_CHANNEL_OPERATION_NOT_STARTED +}; + +struct i2c_request_transaction_data { + enum i2caux_transaction_action action; + enum i2c_channel_operation_result status; + uint8_t address; + uint32_t length; + uint8_t *data; +}; + +struct i2c_reply_transaction_data { + uint32_t length; + uint8_t *data; +}; + +struct i2c_engine; + +struct i2c_engine_funcs { + void (*destroy)( + struct i2c_engine **ptr); + uint32_t (*get_speed)( + const struct i2c_engine *engine); + void (*set_speed)( + struct i2c_engine *engine, + uint32_t speed); + bool (*acquire_engine)( + struct i2c_engine *engine, + struct ddc *ddc); + bool (*setup_engine)( + struct i2c_engine *engine); + void (*submit_channel_request)( + struct i2c_engine *engine, + struct i2c_request_transaction_data *request); + void (*process_channel_reply)( + struct i2c_engine *engine, + struct i2c_reply_transaction_data *reply); + enum i2c_channel_operation_result (*get_channel_status)( + struct i2c_engine *engine, + uint8_t *returned_bytes); +}; + +struct i2c_engine { + struct engine base; + const struct i2c_engine_funcs *funcs; + uint32_t timeout_delay; +}; + +void dal_i2c_engine_construct( + struct i2c_engine *engine, + struct dc_context *ctx); + +void dal_i2c_engine_destruct( + struct i2c_engine *engine); + +bool dal_i2c_engine_setup_i2c_engine( + struct i2c_engine *engine); + +void dal_i2c_engine_submit_channel_request( + struct i2c_engine *engine, + struct i2c_request_transaction_data *request); + +void dal_i2c_engine_process_channel_reply( + struct i2c_engine *engine, + struct i2c_reply_transaction_data *reply); + +bool dal_i2c_engine_acquire( + struct engine *ptr, + struct ddc *ddc_handle); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_generic_hw_engine.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_generic_hw_engine.c new file mode 100644 index 000000000000..5a4295e0fae5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_generic_hw_engine.c @@ -0,0 +1,284 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "engine.h" +#include "i2c_engine.h" +#include "i2c_hw_engine.h" + +/* + * Header of this unit + */ + +#include "i2c_generic_hw_engine.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +/* + * @brief + * Cast 'struct i2c_hw_engine *' + * to 'struct i2c_generic_hw_engine *' + */ +#define FROM_I2C_HW_ENGINE(ptr) \ + container_of((ptr), struct i2c_generic_hw_engine, base) + +/* + * @brief + * Cast 'struct i2c_engine *' + * to 'struct i2c_generic_hw_engine *' + */ +#define FROM_I2C_ENGINE(ptr) \ + FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct i2c_generic_hw_engine *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type( + const struct engine *engine) +{ + return I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW; +} + +/* + * @brief + * Single transaction handling. + * Since transaction may be bigger than HW buffer size, + * it divides transaction to sub-transactions + * and uses batch transaction feature of the engine. + */ +bool dal_i2c_generic_hw_engine_submit_request( + struct engine *engine, + struct i2caux_transaction_request *i2caux_request, + bool middle_of_transaction) +{ + struct i2c_generic_hw_engine *hw_engine = FROM_ENGINE(engine); + + struct i2c_hw_engine *base = &hw_engine->base; + + uint32_t max_payload_size = + base->funcs->get_hw_buffer_available_size(base); + + bool initial_stop_bit = !middle_of_transaction; + + struct i2c_generic_transaction_attributes attributes; + + enum i2c_channel_operation_result operation_result = + I2C_CHANNEL_OPERATION_FAILED; + + bool result = false; + + /* setup transaction initial properties */ + + uint8_t address = i2caux_request->payload.address; + uint8_t *current_payload = i2caux_request->payload.data; + uint32_t remaining_payload_size = i2caux_request->payload.length; + + bool first_iteration = true; + + if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) + attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_READ; + else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) + attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE; + else { + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; + return false; + } + + /* Do batch transaction. + * Divide read/write data into payloads which fit HW buffer size. + * 1. Single transaction: + * start_bit = 1, stop_bit depends on session state, ack_on_read = 0; + * 2. Start of batch transaction: + * start_bit = 1, stop_bit = 0, ack_on_read = 1; + * 3. Middle of batch transaction: + * start_bit = 0, stop_bit = 0, ack_on_read = 1; + * 4. End of batch transaction: + * start_bit = 0, stop_bit depends on session state, ack_on_read = 0. + * Session stop bit is set if 'middle_of_transaction' = 0. */ + + while (remaining_payload_size) { + uint32_t current_transaction_size; + uint32_t current_payload_size; + + bool last_iteration; + bool stop_bit; + + /* Calculate current transaction size and payload size. + * Transaction size = total number of bytes in transaction, + * including slave's address; + * Payload size = number of data bytes in transaction. */ + + if (first_iteration) { + /* In the first sub-transaction we send slave's address + * thus we need to reserve one byte for it */ + current_transaction_size = + (remaining_payload_size > max_payload_size - 1) ? + max_payload_size : + remaining_payload_size + 1; + + current_payload_size = current_transaction_size - 1; + } else { + /* Second and further sub-transactions will have + * entire buffer reserved for data */ + current_transaction_size = + (remaining_payload_size > max_payload_size) ? + max_payload_size : + remaining_payload_size; + + current_payload_size = current_transaction_size; + } + + last_iteration = + (remaining_payload_size == current_payload_size); + + stop_bit = last_iteration ? initial_stop_bit : false; + + /* write slave device address */ + + if (first_iteration) + hw_engine->funcs->write_address(hw_engine, address); + + /* write current portion of data, if requested */ + + if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) + hw_engine->funcs->write_data( + hw_engine, + current_payload, + current_payload_size); + + /* execute transaction */ + + attributes.start_bit = first_iteration; + attributes.stop_bit = stop_bit; + attributes.last_read = last_iteration; + attributes.transaction_size = current_transaction_size; + + hw_engine->funcs->execute_transaction(hw_engine, &attributes); + + /* wait until transaction is processed; if it fails - quit */ + + operation_result = base->funcs->wait_on_operation_result( + base, + base->funcs->get_transaction_timeout( + base, current_transaction_size), + I2C_CHANNEL_OPERATION_ENGINE_BUSY); + + if (operation_result != I2C_CHANNEL_OPERATION_SUCCEEDED) + break; + + /* read current portion of data, if requested */ + + /* the read offset should be 1 for first sub-transaction, + * and 0 for any next one */ + + if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) + hw_engine->funcs->read_data(hw_engine, current_payload, + current_payload_size, first_iteration ? 1 : 0); + + /* update loop variables */ + + first_iteration = false; + current_payload += current_payload_size; + remaining_payload_size -= current_payload_size; + } + + /* update transaction status */ + + switch (operation_result) { + case I2C_CHANNEL_OPERATION_SUCCEEDED: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_SUCCEEDED; + result = true; + break; + case I2C_CHANNEL_OPERATION_NO_RESPONSE: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_NACK; + break; + case I2C_CHANNEL_OPERATION_TIMEOUT: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + break; + case I2C_CHANNEL_OPERATION_FAILED: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; + break; + default: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; + } + + return result; +} + +/* + * @brief + * Returns number of microseconds to wait until timeout to be considered + */ +uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout( + const struct i2c_hw_engine *engine, + uint32_t length) +{ + const struct i2c_engine *base = &engine->base; + + uint32_t speed = base->funcs->get_speed(base); + + if (!speed) + return 0; + + /* total timeout = period_timeout * (start + data bits count + stop) */ + + return ((1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed) * + (1 + (length << 3) + 1); +} + +void dal_i2c_generic_hw_engine_construct( + struct i2c_generic_hw_engine *engine, + struct dc_context *ctx) +{ + dal_i2c_hw_engine_construct(&engine->base, ctx); +} + +void dal_i2c_generic_hw_engine_destruct( + struct i2c_generic_hw_engine *engine) +{ + dal_i2c_hw_engine_destruct(&engine->base); +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_generic_hw_engine.h b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_generic_hw_engine.h new file mode 100644 index 000000000000..1da0397b04a2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_generic_hw_engine.h @@ -0,0 +1,77 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_GENERIC_HW_ENGINE_H__ +#define __DAL_I2C_GENERIC_HW_ENGINE_H__ + +struct i2c_generic_transaction_attributes { + enum i2caux_transaction_action action; + uint32_t transaction_size; + bool start_bit; + bool stop_bit; + bool last_read; +}; + +struct i2c_generic_hw_engine; + +struct i2c_generic_hw_engine_funcs { + void (*write_address)( + struct i2c_generic_hw_engine *engine, + uint8_t address); + void (*write_data)( + struct i2c_generic_hw_engine *engine, + const uint8_t *buffer, + uint32_t length); + void (*read_data)( + struct i2c_generic_hw_engine *engine, + uint8_t *buffer, + uint32_t length, + uint32_t offset); + void (*execute_transaction)( + struct i2c_generic_hw_engine *engine, + struct i2c_generic_transaction_attributes *attributes); +}; + +struct i2c_generic_hw_engine { + struct i2c_hw_engine base; + const struct i2c_generic_hw_engine_funcs *funcs; +}; + +void dal_i2c_generic_hw_engine_construct( + struct i2c_generic_hw_engine *engine, + struct dc_context *ctx); + +void dal_i2c_generic_hw_engine_destruct( + struct i2c_generic_hw_engine *engine); +enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type( + const struct engine *engine); +bool dal_i2c_generic_hw_engine_submit_request( + struct engine *ptr, + struct i2caux_transaction_request *i2caux_request, + bool middle_of_transaction); +uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout( + const struct i2c_hw_engine *engine, + uint32_t length); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.c new file mode 100644 index 000000000000..4b54fcfb28ec --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.c @@ -0,0 +1,244 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "engine.h" +#include "i2c_engine.h" + +/* + * Header of this unit + */ + +#include "i2c_hw_engine.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +/* + * @brief + * Cast 'struct i2c_engine *' + * to 'struct i2c_hw_engine *' + */ +#define FROM_I2C_ENGINE(ptr) \ + container_of((ptr), struct i2c_hw_engine, base) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct i2c_hw_engine *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +enum i2caux_engine_type dal_i2c_hw_engine_get_engine_type( + const struct engine *engine) +{ + return I2CAUX_ENGINE_TYPE_I2C_DDC_HW; +} + +bool dal_i2c_hw_engine_submit_request( + struct engine *engine, + struct i2caux_transaction_request *i2caux_request, + bool middle_of_transaction) +{ + struct i2c_hw_engine *hw_engine = FROM_ENGINE(engine); + + struct i2c_request_transaction_data request; + + uint32_t transaction_timeout; + + enum i2c_channel_operation_result operation_result; + + bool result = false; + + /* We need following: + * transaction length will not exceed + * the number of free bytes in HW buffer (minus one for address)*/ + + if (i2caux_request->payload.length >= + hw_engine->funcs->get_hw_buffer_available_size(hw_engine)) { + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW; + return false; + } + + if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) + request.action = middle_of_transaction ? + I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_READ; + else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) + request.action = middle_of_transaction ? + I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_WRITE; + else { + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; + /* [anaumov] in DAL2, there was no "return false" */ + return false; + } + + request.address = (uint8_t)i2caux_request->payload.address; + request.length = i2caux_request->payload.length; + request.data = i2caux_request->payload.data; + + /* obtain timeout value before submitting request */ + + transaction_timeout = hw_engine->funcs->get_transaction_timeout( + hw_engine, i2caux_request->payload.length + 1); + + hw_engine->base.funcs->submit_channel_request( + &hw_engine->base, &request); + + if ((request.status == I2C_CHANNEL_OPERATION_FAILED) || + (request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) { + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY; + return false; + } + + /* wait until transaction proceed */ + + operation_result = hw_engine->funcs->wait_on_operation_result( + hw_engine, + transaction_timeout, + I2C_CHANNEL_OPERATION_ENGINE_BUSY); + + /* update transaction status */ + + switch (operation_result) { + case I2C_CHANNEL_OPERATION_SUCCEEDED: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_SUCCEEDED; + result = true; + break; + case I2C_CHANNEL_OPERATION_NO_RESPONSE: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_NACK; + break; + case I2C_CHANNEL_OPERATION_TIMEOUT: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + break; + case I2C_CHANNEL_OPERATION_FAILED: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; + break; + default: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; + } + + if (result && (i2caux_request->operation == I2CAUX_TRANSACTION_READ)) { + struct i2c_reply_transaction_data reply; + + reply.data = i2caux_request->payload.data; + reply.length = i2caux_request->payload.length; + + hw_engine->base.funcs-> + process_channel_reply(&hw_engine->base, &reply); + } + + return result; +} + +bool dal_i2c_hw_engine_acquire_engine( + struct i2c_engine *engine, + struct ddc *ddc) +{ + enum gpio_result result; + uint32_t current_speed; + + result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, + GPIO_DDC_CONFIG_TYPE_MODE_I2C); + + if (result != GPIO_RESULT_OK) + return false; + + engine->base.ddc = ddc; + + current_speed = engine->funcs->get_speed(engine); + + if (current_speed) + FROM_I2C_ENGINE(engine)->original_speed = current_speed; + + return true; +} +/* + * @brief + * Queries in a loop for current engine status + * until retrieved status matches 'expected_result', or timeout occurs. + * Timeout given in microseconds + * and the status query frequency is also one per microsecond. + */ +enum i2c_channel_operation_result dal_i2c_hw_engine_wait_on_operation_result( + struct i2c_hw_engine *engine, + uint32_t timeout, + enum i2c_channel_operation_result expected_result) +{ + enum i2c_channel_operation_result result; + uint32_t i = 0; + + if (!timeout) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + do { + result = engine->base.funcs->get_channel_status( + &engine->base, NULL); + + if (result != expected_result) + break; + + udelay(1); + + ++i; + } while (i < timeout); + + return result; +} + +void dal_i2c_hw_engine_construct( + struct i2c_hw_engine *engine, + struct dc_context *ctx) +{ + dal_i2c_engine_construct(&engine->base, ctx); + engine->original_speed = I2CAUX_DEFAULT_I2C_HW_SPEED; + engine->default_speed = I2CAUX_DEFAULT_I2C_HW_SPEED; +} + +void dal_i2c_hw_engine_destruct( + struct i2c_hw_engine *engine) +{ + dal_i2c_engine_destruct(&engine->base); +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.h b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.h new file mode 100644 index 000000000000..8936a994804a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.h @@ -0,0 +1,80 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_HW_ENGINE_H__ +#define __DAL_I2C_HW_ENGINE_H__ + +enum { + TRANSACTION_TIMEOUT_IN_I2C_CLOCKS = 32 +}; + +struct i2c_hw_engine; + +struct i2c_hw_engine_funcs { + uint32_t (*get_hw_buffer_available_size)( + const struct i2c_hw_engine *engine); + enum i2c_channel_operation_result (*wait_on_operation_result)( + struct i2c_hw_engine *engine, + uint32_t timeout, + enum i2c_channel_operation_result expected_result); + uint32_t (*get_transaction_timeout)( + const struct i2c_hw_engine *engine, + uint32_t length); +}; + +struct i2c_hw_engine { + struct i2c_engine base; + const struct i2c_hw_engine_funcs *funcs; + + /* Values below are in kilohertz */ + uint32_t original_speed; + uint32_t default_speed; +}; + +void dal_i2c_hw_engine_construct( + struct i2c_hw_engine *engine, + struct dc_context *ctx); + +void dal_i2c_hw_engine_destruct( + struct i2c_hw_engine *engine); + +enum i2c_channel_operation_result dal_i2c_hw_engine_wait_on_operation_result( + struct i2c_hw_engine *engine, + uint32_t timeout, + enum i2c_channel_operation_result expected_result); + +bool dal_i2c_hw_engine_acquire_engine( + struct i2c_engine *engine, + struct ddc *ddc); + +bool dal_i2c_hw_engine_submit_request( + struct engine *ptr, + struct i2caux_transaction_request *i2caux_request, + bool middle_of_transaction); + +enum i2caux_engine_type dal_i2c_hw_engine_get_engine_type( + const struct engine *engine); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_sw_engine.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_sw_engine.c new file mode 100644 index 000000000000..8e19bb629394 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_sw_engine.c @@ -0,0 +1,601 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "engine.h" +#include "i2c_engine.h" + +/* + * Header of this unit + */ + +#include "i2c_sw_engine.h" + +/* + * Post-requisites: headers required by this unit + */ + +/* + * This unit + */ + +#define SCL false +#define SDA true + +static inline bool read_bit_from_ddc( + struct ddc *ddc, + bool data_nor_clock) +{ + uint32_t value = 0; + + if (data_nor_clock) + dal_gpio_get_value(ddc->pin_data, &value); + else + dal_gpio_get_value(ddc->pin_clock, &value); + + return (value != 0); +} + +static inline void write_bit_to_ddc( + struct ddc *ddc, + bool data_nor_clock, + bool bit) +{ + uint32_t value = bit ? 1 : 0; + + if (data_nor_clock) + dal_gpio_set_value(ddc->pin_data, value); + else + dal_gpio_set_value(ddc->pin_clock, value); +} + +static bool wait_for_scl_high( + struct dc_context *ctx, + struct ddc *ddc, + uint16_t clock_delay_div_4) +{ + uint32_t scl_retry = 0; + uint32_t scl_retry_max = I2C_SW_TIMEOUT_DELAY / clock_delay_div_4; + + udelay(clock_delay_div_4); + + /* 3 milliseconds delay + * to wake up some displays from "low power" state. + */ + + do { + if (read_bit_from_ddc(ddc, SCL)) + return true; + + udelay(clock_delay_div_4); + + ++scl_retry; + } while (scl_retry <= scl_retry_max); + + return false; +} + +static bool start_sync( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4) +{ + uint32_t retry = 0; + + /* The I2C communications start signal is: + * the SDA going low from high, while the SCL is high. */ + + write_bit_to_ddc(ddc_handle, SCL, true); + + udelay(clock_delay_div_4); + + do { + write_bit_to_ddc(ddc_handle, SDA, true); + + if (!read_bit_from_ddc(ddc_handle, SDA)) { + ++retry; + continue; + } + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) + break; + + write_bit_to_ddc(ddc_handle, SDA, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + return true; + } while (retry <= I2C_SW_RETRIES); + + return false; +} + +static bool stop_sync( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4) +{ + uint32_t retry = 0; + + /* The I2C communications stop signal is: + * the SDA going high from low, while the SCL is high. */ + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SDA, true); + + do { + udelay(clock_delay_div_4); + + if (read_bit_from_ddc(ddc_handle, SDA)) + return true; + + ++retry; + } while (retry <= 2); + + return false; +} + +static bool write_byte( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t byte) +{ + int32_t shift = 7; + bool ack; + + /* bits are transmitted serially, starting from MSB */ + + do { + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, (byte >> shift) & 1); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SCL, false); + + --shift; + } while (shift >= 0); + + /* The display sends ACK by preventing the SDA from going high + * after the SCL pulse we use to send our last data bit. + * If the SDA goes high after that bit, it's a NACK */ + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, true); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) + return false; + + /* read ACK bit */ + + ack = !read_bit_from_ddc(ddc_handle, SDA); + + udelay(clock_delay_div_4 << 1); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4 << 1); + + return ack; +} + +static bool read_byte( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t *byte, + bool more) +{ + int32_t shift = 7; + + uint8_t data = 0; + + /* The data bits are read from MSB to LSB; + * bit is read while SCL is high */ + + do { + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) + return false; + + if (read_bit_from_ddc(ddc_handle, SDA)) + data |= (1 << shift); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4 << 1); + + --shift; + } while (shift >= 0); + + /* read only whole byte */ + + *byte = data; + + udelay(clock_delay_div_4); + + /* send the acknowledge bit: + * SDA low means ACK, SDA high means NACK */ + + write_bit_to_ddc(ddc_handle, SDA, !more); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, true); + + udelay(clock_delay_div_4); + + return true; +} + +static bool i2c_write( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t address, + uint32_t length, + const uint8_t *data) +{ + uint32_t i = 0; + + if (!write_byte(ctx, ddc_handle, clock_delay_div_4, address)) + return false; + + while (i < length) { + if (!write_byte(ctx, ddc_handle, clock_delay_div_4, data[i])) + return false; + ++i; + } + + return true; +} + +static bool i2c_read( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t address, + uint32_t length, + uint8_t *data) +{ + uint32_t i = 0; + + if (!write_byte(ctx, ddc_handle, clock_delay_div_4, address)) + return false; + + while (i < length) { + if (!read_byte(ctx, ddc_handle, clock_delay_div_4, data + i, + i < length - 1)) + return false; + ++i; + } + + return true; +} + +/* + * @brief + * Cast 'struct i2c_engine *' + * to 'struct i2c_sw_engine *' + */ +#define FROM_I2C_ENGINE(ptr) \ + container_of((ptr), struct i2c_sw_engine, base) + +/* + * @brief + * Cast 'struct engine *' + * to 'struct i2c_sw_engine *' + */ +#define FROM_ENGINE(ptr) \ + FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) + +enum i2caux_engine_type dal_i2c_sw_engine_get_engine_type( + const struct engine *engine) +{ + return I2CAUX_ENGINE_TYPE_I2C_SW; +} + +bool dal_i2c_sw_engine_submit_request( + struct engine *engine, + struct i2caux_transaction_request *i2caux_request, + bool middle_of_transaction) +{ + struct i2c_sw_engine *sw_engine = FROM_ENGINE(engine); + + struct i2c_engine *base = &sw_engine->base; + + struct i2c_request_transaction_data request; + bool operation_succeeded = false; + + if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) + request.action = middle_of_transaction ? + I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_READ; + else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) + request.action = middle_of_transaction ? + I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : + I2CAUX_TRANSACTION_ACTION_I2C_WRITE; + else { + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; + /* in DAL2, there was no "return false" */ + return false; + } + + request.address = (uint8_t)i2caux_request->payload.address; + request.length = i2caux_request->payload.length; + request.data = i2caux_request->payload.data; + + base->funcs->submit_channel_request(base, &request); + + if ((request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY) || + (request.status == I2C_CHANNEL_OPERATION_FAILED)) + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY; + else { + enum i2c_channel_operation_result operation_result; + + do { + operation_result = + base->funcs->get_channel_status(base, NULL); + + switch (operation_result) { + case I2C_CHANNEL_OPERATION_SUCCEEDED: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_SUCCEEDED; + operation_succeeded = true; + break; + case I2C_CHANNEL_OPERATION_NO_RESPONSE: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_NACK; + break; + case I2C_CHANNEL_OPERATION_TIMEOUT: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; + break; + case I2C_CHANNEL_OPERATION_FAILED: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; + break; + default: + i2caux_request->status = + I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; + break; + } + } while (operation_result == I2C_CHANNEL_OPERATION_ENGINE_BUSY); + } + + return operation_succeeded; +} + +uint32_t dal_i2c_sw_engine_get_speed( + const struct i2c_engine *engine) +{ + return FROM_I2C_ENGINE(engine)->speed; +} + +void dal_i2c_sw_engine_set_speed( + struct i2c_engine *engine, + uint32_t speed) +{ + struct i2c_sw_engine *sw_engine = FROM_I2C_ENGINE(engine); + + ASSERT(speed); + + sw_engine->speed = speed ? speed : I2CAUX_DEFAULT_I2C_SW_SPEED; + + sw_engine->clock_delay = 1000 / sw_engine->speed; + + if (sw_engine->clock_delay < 12) + sw_engine->clock_delay = 12; +} + +bool dal_i2caux_i2c_sw_engine_acquire_engine( + struct i2c_engine *engine, + struct ddc *ddc) +{ + enum gpio_result result; + + result = dal_ddc_open(ddc, GPIO_MODE_FAST_OUTPUT, + GPIO_DDC_CONFIG_TYPE_MODE_I2C); + + if (result != GPIO_RESULT_OK) + return false; + + engine->base.ddc = ddc; + + return true; +} + +void dal_i2c_sw_engine_submit_channel_request( + struct i2c_engine *engine, + struct i2c_request_transaction_data *req) +{ + struct i2c_sw_engine *sw_engine = FROM_I2C_ENGINE(engine); + + struct ddc *ddc = engine->base.ddc; + uint16_t clock_delay_div_4 = sw_engine->clock_delay >> 2; + + /* send sync (start / repeated start) */ + + bool result = start_sync(engine->base.ctx, ddc, clock_delay_div_4); + + /* process payload */ + + if (result) { + switch (req->action) { + case I2CAUX_TRANSACTION_ACTION_I2C_WRITE: + case I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT: + result = i2c_write(engine->base.ctx, ddc, clock_delay_div_4, + req->address, req->length, req->data); + break; + case I2CAUX_TRANSACTION_ACTION_I2C_READ: + case I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT: + result = i2c_read(engine->base.ctx, ddc, clock_delay_div_4, + req->address, req->length, req->data); + break; + default: + result = false; + break; + } + } + + /* send stop if not 'mot' or operation failed */ + + if (!result || + (req->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || + (req->action == I2CAUX_TRANSACTION_ACTION_I2C_READ)) + if (!stop_sync(engine->base.ctx, ddc, clock_delay_div_4)) + result = false; + + req->status = result ? + I2C_CHANNEL_OPERATION_SUCCEEDED : + I2C_CHANNEL_OPERATION_FAILED; +} + +enum i2c_channel_operation_result dal_i2c_sw_engine_get_channel_status( + struct i2c_engine *engine, + uint8_t *returned_bytes) +{ + /* No arbitration with VBIOS is performed since DCE 6.0 */ + return I2C_CHANNEL_OPERATION_SUCCEEDED; +} + +void dal_i2c_sw_engine_destruct( + struct i2c_sw_engine *engine) +{ + dal_i2c_engine_destruct(&engine->base); +} + +static void destroy( + struct i2c_engine **ptr) +{ + dal_i2c_sw_engine_destruct(FROM_I2C_ENGINE(*ptr)); + + kfree(*ptr); + *ptr = NULL; +} + +static const struct i2c_engine_funcs i2c_engine_funcs = { + .acquire_engine = dal_i2caux_i2c_sw_engine_acquire_engine, + .destroy = destroy, + .get_speed = dal_i2c_sw_engine_get_speed, + .set_speed = dal_i2c_sw_engine_set_speed, + .setup_engine = dal_i2c_engine_setup_i2c_engine, + .submit_channel_request = dal_i2c_sw_engine_submit_channel_request, + .process_channel_reply = dal_i2c_engine_process_channel_reply, + .get_channel_status = dal_i2c_sw_engine_get_channel_status, +}; + +static void release_engine( + struct engine *engine) +{ + +} + +static const struct engine_funcs engine_funcs = { + .release_engine = release_engine, + .get_engine_type = dal_i2c_sw_engine_get_engine_type, + .acquire = dal_i2c_engine_acquire, + .submit_request = dal_i2c_sw_engine_submit_request, +}; + +void dal_i2c_sw_engine_construct( + struct i2c_sw_engine *engine, + const struct i2c_sw_engine_create_arg *arg) +{ + dal_i2c_engine_construct(&engine->base, arg->ctx); + dal_i2c_sw_engine_set_speed(&engine->base, arg->default_speed); + engine->base.funcs = &i2c_engine_funcs; + engine->base.base.funcs = &engine_funcs; +} + +struct i2c_engine *dal_i2c_sw_engine_create( + const struct i2c_sw_engine_create_arg *arg) +{ + struct i2c_sw_engine *engine; + + if (!arg) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + engine = kzalloc(sizeof(struct i2c_sw_engine), GFP_KERNEL); + + if (!engine) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dal_i2c_sw_engine_construct(engine, arg); + return &engine->base; +} diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_sw_engine.h b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_sw_engine.h new file mode 100644 index 000000000000..546f15b0d3f1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_sw_engine.h @@ -0,0 +1,81 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_SW_ENGINE_H__ +#define __DAL_I2C_SW_ENGINE_H__ + +enum { + I2C_SW_RETRIES = 10, + I2C_SW_SCL_READ_RETRIES = 128, + /* following value is in microseconds */ + I2C_SW_TIMEOUT_DELAY = 3000 +}; + +struct i2c_sw_engine; + +struct i2c_sw_engine { + struct i2c_engine base; + uint32_t clock_delay; + /* Values below are in KHz */ + uint32_t speed; + uint32_t default_speed; +}; + +struct i2c_sw_engine_create_arg { + uint32_t default_speed; + struct dc_context *ctx; +}; + +void dal_i2c_sw_engine_construct( + struct i2c_sw_engine *engine, + const struct i2c_sw_engine_create_arg *arg); + +bool dal_i2caux_i2c_sw_engine_acquire_engine( + struct i2c_engine *engine, + struct ddc *ddc_handle); + +void dal_i2c_sw_engine_destruct( + struct i2c_sw_engine *engine); + +struct i2c_engine *dal_i2c_sw_engine_create( + const struct i2c_sw_engine_create_arg *arg); +enum i2caux_engine_type dal_i2c_sw_engine_get_engine_type( + const struct engine *engine); +bool dal_i2c_sw_engine_submit_request( + struct engine *ptr, + struct i2caux_transaction_request *i2caux_request, + bool middle_of_transaction); +uint32_t dal_i2c_sw_engine_get_speed( + const struct i2c_engine *engine); +void dal_i2c_sw_engine_set_speed( + struct i2c_engine *ptr, + uint32_t speed); +void dal_i2c_sw_engine_submit_channel_request( + struct i2c_engine *ptr, + struct i2c_request_transaction_data *req); +enum i2c_channel_operation_result dal_i2c_sw_engine_get_channel_status( + struct i2c_engine *engine, + uint8_t *returned_bytes); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c new file mode 100644 index 000000000000..e1593ffe5a2b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c @@ -0,0 +1,485 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* + * Pre-requisites: headers required by header of this unit + */ +#include "include/i2caux_interface.h" +#include "dc_bios_types.h" + +/* + * Header of this unit + */ + +#include "i2caux.h" + +/* + * Post-requisites: headers required by this unit + */ + +#include "engine.h" +#include "i2c_engine.h" +#include "aux_engine.h" + +/* + * This unit + */ + +#include "dce80/i2caux_dce80.h" + +#include "dce100/i2caux_dce100.h" + +#include "dce110/i2caux_dce110.h" + +#include "dce112/i2caux_dce112.h" + +#include "dce120/i2caux_dce120.h" + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "dcn10/i2caux_dcn10.h" +#endif + +#include "diagnostics/i2caux_diag.h" + +/* + * @brief + * Plain API, available publicly + */ + +struct i2caux *dal_i2caux_create( + struct dc_context *ctx) +{ + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) { + return dal_i2caux_diag_fpga_create(ctx); + } + + switch (ctx->dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + return dal_i2caux_dce80_create(ctx); + case DCE_VERSION_11_2: + return dal_i2caux_dce112_create(ctx); + case DCE_VERSION_11_0: + return dal_i2caux_dce110_create(ctx); + case DCE_VERSION_10_0: + return dal_i2caux_dce100_create(ctx); + case DCE_VERSION_12_0: + return dal_i2caux_dce120_create(ctx); +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) + case DCN_VERSION_1_0: + return dal_i2caux_dcn10_create(ctx); +#endif + + default: + BREAK_TO_DEBUGGER(); + return NULL; + } +} + +bool dal_i2caux_submit_i2c_command( + struct i2caux *i2caux, + struct ddc *ddc, + struct i2c_command *cmd) +{ + struct i2c_engine *engine; + uint8_t index_of_payload = 0; + bool result; + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!cmd) { + BREAK_TO_DEBUGGER(); + return false; + } + + /* + * default will be SW, however there is a feature flag in adapter + * service that determines whether SW i2c_engine will be available or + * not, if sw i2c is not available we will fallback to hw. This feature + * flag is set to not creating sw i2c engine for every dce except dce80 + * currently + */ + switch (cmd->engine) { + case I2C_COMMAND_ENGINE_DEFAULT: + case I2C_COMMAND_ENGINE_SW: + /* try to acquire SW engine first, + * acquire HW engine if SW engine not available */ + engine = i2caux->funcs->acquire_i2c_sw_engine(i2caux, ddc); + + if (!engine) + engine = i2caux->funcs->acquire_i2c_hw_engine( + i2caux, ddc); + break; + case I2C_COMMAND_ENGINE_HW: + default: + /* try to acquire HW engine first, + * acquire SW engine if HW engine not available */ + engine = i2caux->funcs->acquire_i2c_hw_engine(i2caux, ddc); + + if (!engine) + engine = i2caux->funcs->acquire_i2c_sw_engine( + i2caux, ddc); + } + + if (!engine) + return false; + + engine->funcs->set_speed(engine, cmd->speed); + + result = true; + + while (index_of_payload < cmd->number_of_payloads) { + bool mot = (index_of_payload != cmd->number_of_payloads - 1); + + struct i2c_payload *payload = cmd->payloads + index_of_payload; + + struct i2caux_transaction_request request = { 0 }; + + request.operation = payload->write ? + I2CAUX_TRANSACTION_WRITE : + I2CAUX_TRANSACTION_READ; + + request.payload.address_space = + I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C; + request.payload.address = (payload->address << 1) | + !payload->write; + request.payload.length = payload->length; + request.payload.data = payload->data; + + if (!engine->base.funcs->submit_request( + &engine->base, &request, mot)) { + result = false; + break; + } + + ++index_of_payload; + } + + i2caux->funcs->release_engine(i2caux, &engine->base); + + return result; +} + +bool dal_i2caux_submit_aux_command( + struct i2caux *i2caux, + struct ddc *ddc, + struct aux_command *cmd) +{ + struct aux_engine *engine; + uint8_t index_of_payload = 0; + bool result; + bool mot; + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!cmd) { + BREAK_TO_DEBUGGER(); + return false; + } + + engine = i2caux->funcs->acquire_aux_engine(i2caux, ddc); + + if (!engine) + return false; + + engine->delay = cmd->defer_delay; + engine->max_defer_write_retry = cmd->max_defer_write_retry; + + result = true; + + while (index_of_payload < cmd->number_of_payloads) { + struct aux_payload *payload = cmd->payloads + index_of_payload; + struct i2caux_transaction_request request = { 0 }; + + if (cmd->mot == I2C_MOT_UNDEF) + mot = (index_of_payload != cmd->number_of_payloads - 1); + else + mot = (cmd->mot == I2C_MOT_TRUE); + + request.operation = payload->write ? + I2CAUX_TRANSACTION_WRITE : + I2CAUX_TRANSACTION_READ; + + if (payload->i2c_over_aux) { + request.payload.address_space = + I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C; + + request.payload.address = (payload->address << 1) | + !payload->write; + } else { + request.payload.address_space = + I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD; + + request.payload.address = payload->address; + } + + request.payload.length = payload->length; + request.payload.data = payload->data; + + if (!engine->base.funcs->submit_request( + &engine->base, &request, mot)) { + result = false; + break; + } + + ++index_of_payload; + } + + i2caux->funcs->release_engine(i2caux, &engine->base); + + return result; +} + +static bool get_hw_supported_ddc_line( + struct ddc *ddc, + enum gpio_ddc_line *line) +{ + enum gpio_ddc_line line_found; + + *line = GPIO_DDC_LINE_UNKNOWN; + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!ddc->hw_info.hw_supported) + return false; + + line_found = dal_ddc_get_line(ddc); + + if (line_found >= GPIO_DDC_LINE_COUNT) + return false; + + *line = line_found; + + return true; +} + +void dal_i2caux_configure_aux( + struct i2caux *i2caux, + struct ddc *ddc, + union aux_config cfg) +{ + struct aux_engine *engine = + i2caux->funcs->acquire_aux_engine(i2caux, ddc); + + if (!engine) + return; + + engine->funcs->configure(engine, cfg); + + i2caux->funcs->release_engine(i2caux, &engine->base); +} + +void dal_i2caux_destroy( + struct i2caux **i2caux) +{ + if (!i2caux || !*i2caux) { + BREAK_TO_DEBUGGER(); + return; + } + + (*i2caux)->funcs->destroy(i2caux); + + *i2caux = NULL; +} + +/* + * @brief + * An utility function used by 'struct i2caux' and its descendants + */ + +uint32_t dal_i2caux_get_reference_clock( + struct dc_bios *bios) +{ + struct dc_firmware_info info = { { 0 } }; + + if (bios->funcs->get_firmware_info(bios, &info) != BP_RESULT_OK) + return 0; + + return info.pll_info.crystal_frequency; +} + +/* + * @brief + * i2caux + */ + +enum { + /* following are expressed in KHz */ + DEFAULT_I2C_SW_SPEED = 50, + DEFAULT_I2C_HW_SPEED = 50, + + DEFAULT_I2C_SW_SPEED_100KHZ = 100, + DEFAULT_I2C_HW_SPEED_100KHZ = 100, + + /* This is the timeout as defined in DP 1.2a, + * 2.3.4 "Detailed uPacket TX AUX CH State Description". */ + AUX_TIMEOUT_PERIOD = 400, + + /* Ideally, the SW timeout should be just above 550usec + * which is programmed in HW. + * But the SW timeout of 600usec is not reliable, + * because on some systems, delay_in_microseconds() + * returns faster than it should. + * EPR #379763: by trial-and-error on different systems, + * 700usec is the minimum reliable SW timeout for polling + * the AUX_SW_STATUS.AUX_SW_DONE bit. + * This timeout expires *only* when there is + * AUX Error or AUX Timeout conditions - not during normal operation. + * During normal operation, AUX_SW_STATUS.AUX_SW_DONE bit is set + * at most within ~240usec. That means, + * increasing this timeout will not affect normal operation, + * and we'll timeout after + * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD = 1600usec. + * This timeout is especially important for + * resume from S3 and CTS. */ + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER = 4 +}; + +struct i2c_engine *dal_i2caux_acquire_i2c_sw_engine( + struct i2caux *i2caux, + struct ddc *ddc) +{ + enum gpio_ddc_line line; + struct i2c_engine *engine = NULL; + + if (get_hw_supported_ddc_line(ddc, &line)) + engine = i2caux->i2c_sw_engines[line]; + + if (!engine) + engine = i2caux->i2c_generic_sw_engine; + + if (!engine) + return NULL; + + if (!engine->base.funcs->acquire(&engine->base, ddc)) + return NULL; + + return engine; +} + +struct aux_engine *dal_i2caux_acquire_aux_engine( + struct i2caux *i2caux, + struct ddc *ddc) +{ + enum gpio_ddc_line line; + struct aux_engine *engine; + + if (!get_hw_supported_ddc_line(ddc, &line)) + return NULL; + + engine = i2caux->aux_engines[line]; + + if (!engine) + return NULL; + + if (!engine->base.funcs->acquire(&engine->base, ddc)) + return NULL; + + return engine; +} + +void dal_i2caux_release_engine( + struct i2caux *i2caux, + struct engine *engine) +{ + engine->funcs->release_engine(engine); + + dal_ddc_close(engine->ddc); + + engine->ddc = NULL; +} + +void dal_i2caux_construct( + struct i2caux *i2caux, + struct dc_context *ctx) +{ + uint32_t i = 0; + + i2caux->ctx = ctx; + do { + i2caux->i2c_sw_engines[i] = NULL; + i2caux->i2c_hw_engines[i] = NULL; + i2caux->aux_engines[i] = NULL; + + ++i; + } while (i < GPIO_DDC_LINE_COUNT); + + i2caux->i2c_generic_sw_engine = NULL; + i2caux->i2c_generic_hw_engine = NULL; + + i2caux->aux_timeout_period = + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD; + + if (ctx->dce_version >= DCE_VERSION_11_2) { + i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED_100KHZ; + i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED_100KHZ; + } else { + i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED; + i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED; + } +} + +void dal_i2caux_destruct( + struct i2caux *i2caux) +{ + uint32_t i = 0; + + if (i2caux->i2c_generic_hw_engine) + i2caux->i2c_generic_hw_engine->funcs->destroy( + &i2caux->i2c_generic_hw_engine); + + if (i2caux->i2c_generic_sw_engine) + i2caux->i2c_generic_sw_engine->funcs->destroy( + &i2caux->i2c_generic_sw_engine); + + do { + if (i2caux->aux_engines[i]) + i2caux->aux_engines[i]->funcs->destroy( + &i2caux->aux_engines[i]); + + if (i2caux->i2c_hw_engines[i]) + i2caux->i2c_hw_engines[i]->funcs->destroy( + &i2caux->i2c_hw_engines[i]); + + if (i2caux->i2c_sw_engines[i]) + i2caux->i2c_sw_engines[i]->funcs->destroy( + &i2caux->i2c_sw_engines[i]); + + ++i; + } while (i < GPIO_DDC_LINE_COUNT); +} + diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.h b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.h new file mode 100644 index 000000000000..64f51bb06915 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.h @@ -0,0 +1,122 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2C_AUX_H__ +#define __DAL_I2C_AUX_H__ + +uint32_t dal_i2caux_get_reference_clock( + struct dc_bios *bios); + +struct i2caux; + +struct engine; + +struct i2caux_funcs { + void (*destroy)(struct i2caux **ptr); + struct i2c_engine * (*acquire_i2c_sw_engine)( + struct i2caux *i2caux, + struct ddc *ddc); + struct i2c_engine * (*acquire_i2c_hw_engine)( + struct i2caux *i2caux, + struct ddc *ddc); + struct aux_engine * (*acquire_aux_engine)( + struct i2caux *i2caux, + struct ddc *ddc); + void (*release_engine)( + struct i2caux *i2caux, + struct engine *engine); +}; + +struct i2c_engine; +struct aux_engine; + +struct i2caux { + struct dc_context *ctx; + const struct i2caux_funcs *funcs; + /* On ASIC we have certain amount of lines with HW DDC engine + * (4, 6, or maybe more in the future). + * For every such line, we create separate HW DDC engine + * (since we have these engines in HW) and separate SW DDC engine + * (to allow concurrent use of few lines). + * In similar way we have AUX engines. */ + + /* I2C SW engines, per DDC line. + * Only lines with HW DDC support will be initialized */ + struct i2c_engine *i2c_sw_engines[GPIO_DDC_LINE_COUNT]; + + /* I2C HW engines, per DDC line. + * Only lines with HW DDC support will be initialized */ + struct i2c_engine *i2c_hw_engines[GPIO_DDC_LINE_COUNT]; + + /* AUX engines, per DDC line. + * Only lines with HW AUX support will be initialized */ + struct aux_engine *aux_engines[GPIO_DDC_LINE_COUNT]; + + /* For all other lines, we can use + * single instance of generic I2C HW engine + * (since in HW, there is single instance of it) + * or single instance of generic I2C SW engine. + * AUX is not supported for other lines. */ + + /* General-purpose I2C SW engine. + * Can be assigned dynamically to any line per transaction */ + struct i2c_engine *i2c_generic_sw_engine; + + /* General-purpose I2C generic HW engine. + * Can be assigned dynamically to almost any line per transaction */ + struct i2c_engine *i2c_generic_hw_engine; + + /* [anaumov] in DAL2, there is a Mutex */ + + uint32_t aux_timeout_period; + + /* expressed in KHz */ + uint32_t default_i2c_sw_speed; + uint32_t default_i2c_hw_speed; +}; + +void dal_i2caux_construct( + struct i2caux *i2caux, + struct dc_context *ctx); + +void dal_i2caux_release_engine( + struct i2caux *i2caux, + struct engine *engine); + +void dal_i2caux_destruct( + struct i2caux *i2caux); + +void dal_i2caux_destroy( + struct i2caux **ptr); + +struct i2c_engine *dal_i2caux_acquire_i2c_sw_engine( + struct i2caux *i2caux, + struct ddc *ddc); + +struct aux_engine *dal_i2caux_acquire_aux_engine( + struct i2caux *i2caux, + struct ddc *ddc); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/bw_fixed.h b/drivers/gpu/drm/amd/display/dc/inc/bw_fixed.h new file mode 100644 index 000000000000..39ee8eba3c31 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/bw_fixed.h @@ -0,0 +1,166 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef BW_FIXED_H_ +#define BW_FIXED_H_ + +#define BW_FIXED_BITS_PER_FRACTIONAL_PART 24 + +#define BW_FIXED_GET_INTEGER_PART(x) ((x) >> BW_FIXED_BITS_PER_FRACTIONAL_PART) +struct bw_fixed { + int64_t value; +}; + +#define BW_FIXED_MIN_I32 \ + (int64_t)(-(1LL << (63 - BW_FIXED_BITS_PER_FRACTIONAL_PART))) + +#define BW_FIXED_MAX_I32 \ + (int64_t)((1ULL << (63 - BW_FIXED_BITS_PER_FRACTIONAL_PART)) - 1) + +static inline struct bw_fixed bw_min2(const struct bw_fixed arg1, + const struct bw_fixed arg2) +{ + return (arg1.value <= arg2.value) ? arg1 : arg2; +} + +static inline struct bw_fixed bw_max2(const struct bw_fixed arg1, + const struct bw_fixed arg2) +{ + return (arg2.value <= arg1.value) ? arg1 : arg2; +} + +static inline struct bw_fixed bw_min3(struct bw_fixed v1, + struct bw_fixed v2, + struct bw_fixed v3) +{ + return bw_min2(bw_min2(v1, v2), v3); +} + +static inline struct bw_fixed bw_max3(struct bw_fixed v1, + struct bw_fixed v2, + struct bw_fixed v3) +{ + return bw_max2(bw_max2(v1, v2), v3); +} + +struct bw_fixed bw_int_to_fixed_nonconst(int64_t value); +static inline struct bw_fixed bw_int_to_fixed(int64_t value) +{ + if (__builtin_constant_p(value)) { + struct bw_fixed res; + BUILD_BUG_ON(value > BW_FIXED_MAX_I32 || value < BW_FIXED_MIN_I32); + res.value = value << BW_FIXED_BITS_PER_FRACTIONAL_PART; + return res; + } else + return bw_int_to_fixed_nonconst(value); +} + +static inline int32_t bw_fixed_to_int(struct bw_fixed value) +{ + return BW_FIXED_GET_INTEGER_PART(value.value); +} + +struct bw_fixed bw_frc_to_fixed(int64_t num, int64_t denum); + +static inline struct bw_fixed fixed31_32_to_bw_fixed(int64_t raw) +{ + struct bw_fixed result = { 0 }; + + if (raw < 0) { + raw = -raw; + result.value = -(raw >> (32 - BW_FIXED_BITS_PER_FRACTIONAL_PART)); + } else { + result.value = raw >> (32 - BW_FIXED_BITS_PER_FRACTIONAL_PART); + } + + return result; +} + +static inline struct bw_fixed bw_add(const struct bw_fixed arg1, + const struct bw_fixed arg2) +{ + struct bw_fixed res; + + res.value = arg1.value + arg2.value; + + return res; +} + +static inline struct bw_fixed bw_sub(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + struct bw_fixed res; + + res.value = arg1.value - arg2.value; + + return res; +} + +struct bw_fixed bw_mul(const struct bw_fixed arg1, const struct bw_fixed arg2); +static inline struct bw_fixed bw_div(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + return bw_frc_to_fixed(arg1.value, arg2.value); +} + +static inline struct bw_fixed bw_mod(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + struct bw_fixed res; + div64_u64_rem(arg1.value, arg2.value, &res.value); + return res; +} + +struct bw_fixed bw_floor2(const struct bw_fixed arg, const struct bw_fixed significance); +struct bw_fixed bw_ceil2(const struct bw_fixed arg, const struct bw_fixed significance); + +static inline bool bw_equ(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + return arg1.value == arg2.value; +} + +static inline bool bw_neq(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + return arg1.value != arg2.value; +} + +static inline bool bw_leq(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + return arg1.value <= arg2.value; +} + +static inline bool bw_meq(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + return arg1.value >= arg2.value; +} + +static inline bool bw_ltn(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + return arg1.value < arg2.value; +} + +static inline bool bw_mtn(const struct bw_fixed arg1, const struct bw_fixed arg2) +{ + return arg1.value > arg2.value; +} + +#endif //BW_FIXED_H_ diff --git a/drivers/gpu/drm/amd/display/dc/inc/clock_source.h b/drivers/gpu/drm/amd/display/dc/inc/clock_source.h new file mode 100644 index 000000000000..ebcf67b5fc57 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/clock_source.h @@ -0,0 +1,182 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_CLOCK_SOURCE_H__ +#define __DC_CLOCK_SOURCE_H__ + +#include "dc_types.h" +#include "include/grph_object_id.h" +#include "include/bios_parser_types.h" + +struct clock_source; + +struct spread_spectrum_data { + uint32_t percentage; /*> In unit of 0.01% or 0.001%*/ + uint32_t percentage_divider; /*> 100 or 1000 */ + uint32_t freq_range_khz; + uint32_t modulation_freq_hz; + + struct spread_spectrum_flags flags; +}; + +struct delta_sigma_data { + uint32_t feedback_amount; + uint32_t nfrac_amount; + uint32_t ds_frac_size; + uint32_t ds_frac_amount; +}; + +/** + * Pixel Clock Parameters structure + * These parameters are required as input + * when calculating Pixel Clock Dividers for requested Pixel Clock + */ +struct pixel_clk_flags { + uint32_t ENABLE_SS:1; + uint32_t DISPLAY_BLANKED:1; + uint32_t PROGRAM_PIXEL_CLOCK:1; + uint32_t PROGRAM_ID_CLOCK:1; + uint32_t SUPPORT_YCBCR420:1; +}; + +/** + * Display Port HW De spread of Reference Clock related Parameters structure + * Store it once at boot for later usage + */ +struct csdp_ref_clk_ds_params { + bool hw_dso_n_dp_ref_clk; +/* Flag for HW De Spread enabled (if enabled SS on DP Reference Clock)*/ + uint32_t avg_dp_ref_clk_khz; +/* Average DP Reference clock (in KHz)*/ + uint32_t ss_percentage_on_dp_ref_clk; +/* DP Reference clock SS percentage + * (not to be mixed with DP IDCLK SS from PLL Settings)*/ + uint32_t ss_percentage_divider; +/* DP Reference clock SS percentage divider */ +}; + +struct pixel_clk_params { + uint32_t requested_pix_clk; /* in KHz */ +/*> Requested Pixel Clock + * (based on Video Timing standard used for requested mode)*/ + uint32_t requested_sym_clk; /* in KHz */ +/*> Requested Sym Clock (relevant only for display port)*/ + uint32_t dp_ref_clk; /* in KHz */ +/*> DP reference clock - calculated only for DP signal for specific cases*/ + struct graphics_object_id encoder_object_id; +/*> Encoder object Id - needed by VBIOS Exec table*/ + enum signal_type signal_type; +/*> signalType -> Encoder Mode - needed by VBIOS Exec table*/ + enum controller_id controller_id; +/*> ControllerId - which controller using this PLL*/ + enum dc_color_depth color_depth; + struct csdp_ref_clk_ds_params de_spread_params; +/*> de-spread info, relevant only for on-the-fly tune-up pixel rate*/ + enum dc_pixel_encoding pixel_encoding; + struct pixel_clk_flags flags; +}; + +/** + * Pixel Clock Dividers structure with desired Pixel Clock + * (adjusted after VBIOS exec table), + * with actually calculated Clock and reference Crystal frequency + */ +struct pll_settings { + uint32_t actual_pix_clk; + uint32_t adjusted_pix_clk; + uint32_t calculated_pix_clk; + uint32_t vco_freq; + uint32_t reference_freq; + uint32_t reference_divider; + uint32_t feedback_divider; + uint32_t fract_feedback_divider; + uint32_t pix_clk_post_divider; + uint32_t ss_percentage; + bool use_external_clk; +}; + +struct calc_pll_clock_source_init_data { + struct dc_bios *bp; + uint32_t min_pix_clk_pll_post_divider; + uint32_t max_pix_clk_pll_post_divider; + uint32_t min_pll_ref_divider; + uint32_t max_pll_ref_divider; + uint32_t min_override_input_pxl_clk_pll_freq_khz; +/* if not 0, override the firmware info */ + + uint32_t max_override_input_pxl_clk_pll_freq_khz; +/* if not 0, override the firmware info */ + + uint32_t num_fract_fb_divider_decimal_point; +/* number of decimal point for fractional feedback divider value */ + + uint32_t num_fract_fb_divider_decimal_point_precision; +/* number of decimal point to round off for fractional feedback divider value*/ + struct dc_context *ctx; + +}; + +struct calc_pll_clock_source { + uint32_t ref_freq_khz; + uint32_t min_pix_clock_pll_post_divider; + uint32_t max_pix_clock_pll_post_divider; + uint32_t min_pll_ref_divider; + uint32_t max_pll_ref_divider; + + uint32_t max_vco_khz; + uint32_t min_vco_khz; + uint32_t min_pll_input_freq_khz; + uint32_t max_pll_input_freq_khz; + + uint32_t fract_fb_divider_decimal_points_num; + uint32_t fract_fb_divider_factor; + uint32_t fract_fb_divider_precision; + uint32_t fract_fb_divider_precision_factor; + struct dc_context *ctx; +}; + +struct clock_source_funcs { + bool (*cs_power_down)( + struct clock_source *); + bool (*program_pix_clk)(struct clock_source *, + struct pixel_clk_params *, struct pll_settings *); + uint32_t (*get_pix_clk_dividers)( + struct clock_source *, + struct pixel_clk_params *, + struct pll_settings *); + uint32_t (*get_pix_rate_in_hz)( + struct clock_source *, + struct pixel_clk_params *, + struct pll_settings *); +}; + +struct clock_source { + const struct clock_source_funcs *funcs; + struct dc_context *ctx; + enum clock_source_id id; + bool dp_clk_src; +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/compressor.h b/drivers/gpu/drm/amd/display/dc/inc/compressor.h new file mode 100644 index 000000000000..bcb18f5e1e60 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/compressor.h @@ -0,0 +1,136 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_COMPRESSOR_H__ +#define __DAL_COMPRESSOR_H__ + +#include "include/grph_object_id.h" +#include "bios_parser_interface.h" + +enum fbc_compress_ratio { + FBC_COMPRESS_RATIO_INVALID = 0, + FBC_COMPRESS_RATIO_1TO1 = 1, + FBC_COMPRESS_RATIO_2TO1 = 2, + FBC_COMPRESS_RATIO_4TO1 = 4, + FBC_COMPRESS_RATIO_8TO1 = 8, +}; + +union fbc_physical_address { + struct { + uint32_t low_part; + int32_t high_part; + } addr; + uint64_t quad_part; +}; + +struct compr_addr_and_pitch_params { + /* enum controller_id controller_id; */ + uint32_t inst; + uint32_t source_view_width; + uint32_t source_view_height; +}; + +enum fbc_hw_max_resolution_supported { + FBC_MAX_X = 3840, + FBC_MAX_Y = 2400, + FBC_MAX_X_SG = 1920, + FBC_MAX_Y_SG = 1080, +}; + +struct compressor; + +struct compressor_funcs { + + void (*power_up_fbc)(struct compressor *cp); + void (*enable_fbc)(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + void (*disable_fbc)(struct compressor *cp); + void (*set_fbc_invalidation_triggers)(struct compressor *cp, + uint32_t fbc_trigger); + void (*surface_address_and_pitch)( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + bool (*is_fbc_enabled_in_hw)(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); +}; +struct compressor { + struct dc_context *ctx; + uint32_t attached_inst; + bool is_enabled; + const struct compressor_funcs *funcs; + union { + uint32_t raw; + struct { + uint32_t FBC_SUPPORT:1; + uint32_t FB_POOL:1; + uint32_t DYNAMIC_ALLOC:1; + uint32_t LPT_SUPPORT:1; + uint32_t LPT_MC_CONFIG:1; + uint32_t DUMMY_BACKEND:1; + uint32_t CLK_GATING_DISABLED:1; + + } bits; + } options; + + union fbc_physical_address compr_surface_address; + + uint32_t embedded_panel_h_size; + uint32_t embedded_panel_v_size; + uint32_t memory_bus_width; + uint32_t banks_num; + uint32_t raw_size; + uint32_t channel_interleave_size; + uint32_t dram_channels_num; + + uint32_t allocated_size; + uint32_t preferred_requested_size; + uint32_t lpt_channels_num; + enum fbc_compress_ratio min_compress_ratio; +}; + +struct fbc_input_info { + bool dynamic_fbc_buffer_alloc; + unsigned int source_view_width; + unsigned int source_view_height; + unsigned int num_of_active_targets; +}; + + +struct fbc_requested_compressed_size { + unsigned int preferred_size; + unsigned int preferred_size_alignment; + unsigned int min_size; + unsigned int min_size_alignment; + union { + struct { + /* Above preferedSize must be allocated in FB pool */ + unsigned int preferred_must_be_framebuffer_pool : 1; + /* Above minSize must be allocated in FB pool */ + unsigned int min_must_be_framebuffer_pool : 1; + } bits; + unsigned int flags; + }; +}; +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_status.h b/drivers/gpu/drm/amd/display/dc/inc/core_status.h new file mode 100644 index 000000000000..01df85641684 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/core_status.h @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _CORE_STATUS_H_ +#define _CORE_STATUS_H_ + +enum dc_status { + DC_OK = 1, + + DC_NO_CONTROLLER_RESOURCE = 2, + DC_NO_STREAM_ENG_RESOURCE = 3, + DC_NO_CLOCK_SOURCE_RESOURCE = 4, + DC_FAIL_CONTROLLER_VALIDATE = 5, + DC_FAIL_ENC_VALIDATE = 6, + DC_FAIL_ATTACH_SURFACES = 7, + DC_FAIL_DETACH_SURFACES = 8, + DC_FAIL_SURFACE_VALIDATE = 9, + DC_NO_DP_LINK_BANDWIDTH = 10, + DC_EXCEED_DONGLE_MAX_CLK = 11, + DC_SURFACE_PIXEL_FORMAT_UNSUPPORTED = 12, + DC_FAIL_BANDWIDTH_VALIDATE = 13, /* BW and Watermark validation */ + DC_FAIL_SCALING = 14, + DC_FAIL_DP_LINK_TRAINING = 15, + + DC_ERROR_UNEXPECTED = -1 +}; + +#endif /* _CORE_STATUS_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h new file mode 100644 index 000000000000..b69f321e2ab6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -0,0 +1,283 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _CORE_TYPES_H_ +#define _CORE_TYPES_H_ + +#include "dc.h" +#include "dce_calcs.h" +#include "dcn_calcs.h" +#include "ddc_service_types.h" +#include "dc_bios_types.h" +#include "mem_input.h" +#include "hubp.h" +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "mpc.h" +#endif + +#define MAX_CLOCK_SOURCES 7 + +void enable_surface_flip_reporting(struct dc_plane_state *plane_state, + uint32_t controller_id); + +#include "grph_object_id.h" +#include "link_encoder.h" +#include "stream_encoder.h" +#include "clock_source.h" +#include "audio.h" +#include "dm_pp_smu.h" + + +/************ link *****************/ +struct link_init_data { + const struct dc *dc; + struct dc_context *ctx; /* TODO: remove 'dal' when DC is complete. */ + uint32_t connector_index; /* this will be mapped to the HPD pins */ + uint32_t link_index; /* this is mapped to DAL display_index + TODO: remove it when DC is complete. */ +}; + +enum { + FREE_ACQUIRED_RESOURCE = 0, + KEEP_ACQUIRED_RESOURCE = 1, +}; + +struct dc_link *link_create(const struct link_init_data *init_params); +void link_destroy(struct dc_link **link); + +enum dc_status dc_link_validate_mode_timing( + const struct dc_stream_state *stream, + struct dc_link *link, + const struct dc_crtc_timing *timing); + +void core_link_resume(struct dc_link *link); + +void core_link_enable_stream( + struct dc_state *state, + struct pipe_ctx *pipe_ctx); + +void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option); + +void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable); +/********** DAL Core*********************/ +#include "display_clock.h" +#include "transform.h" +#include "dpp.h" + +struct resource_pool; +struct dc_state; +struct resource_context; + +struct resource_funcs { + void (*destroy)(struct resource_pool **pool); + struct link_encoder *(*link_enc_create)( + const struct encoder_init_data *init); + + enum dc_status (*validate_guaranteed)( + struct dc *dc, + struct dc_stream_state *stream, + struct dc_state *context); + + bool (*validate_bandwidth)( + struct dc *dc, + struct dc_state *context); + + enum dc_status (*validate_global)( + struct dc *dc, + struct dc_state *context); + + struct pipe_ctx *(*acquire_idle_pipe_for_layer)( + struct dc_state *context, + const struct resource_pool *pool, + struct dc_stream_state *stream); + + enum dc_status (*validate_plane)(const struct dc_plane_state *plane_state, struct dc_caps *caps); + + enum dc_status (*add_stream_to_ctx)( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream); +}; + +struct audio_support{ + bool dp_audio; + bool hdmi_audio_on_dongle; + bool hdmi_audio_native; +}; + +#define NO_UNDERLAY_PIPE -1 + +struct resource_pool { + struct mem_input *mis[MAX_PIPES]; + struct hubp *hubps[MAX_PIPES]; + struct input_pixel_processor *ipps[MAX_PIPES]; + struct transform *transforms[MAX_PIPES]; + struct dpp *dpps[MAX_PIPES]; + struct output_pixel_processor *opps[MAX_PIPES]; + struct timing_generator *timing_generators[MAX_PIPES]; + struct stream_encoder *stream_enc[MAX_PIPES * 2]; + + struct mpc *mpc; + struct pp_smu_funcs_rv *pp_smu; + struct pp_smu_display_requirement_rv pp_smu_req; + + unsigned int pipe_count; + unsigned int underlay_pipe_index; + unsigned int stream_enc_count; + unsigned int ref_clock_inKhz; + + /* + * reserved clock source for DP + */ + struct clock_source *dp_clock_source; + + struct clock_source *clock_sources[MAX_CLOCK_SOURCES]; + unsigned int clk_src_count; + + struct audio *audios[MAX_PIPES]; + unsigned int audio_count; + struct audio_support audio_support; + + struct display_clock *display_clock; + struct irq_service *irqs; + + struct abm *abm; + struct dmcu *dmcu; + + const struct resource_funcs *funcs; + const struct resource_caps *res_cap; +}; + +struct stream_resource { + struct output_pixel_processor *opp; + struct timing_generator *tg; + struct stream_encoder *stream_enc; + struct audio *audio; + + struct pixel_clk_params pix_clk_params; + struct encoder_info_frame encoder_info_frame; +}; + +struct plane_resource { + struct scaler_data scl_data; + struct hubp *hubp; + struct mem_input *mi; + struct input_pixel_processor *ipp; + struct transform *xfm; + struct dpp *dpp; +}; + +struct pipe_ctx { + struct dc_plane_state *plane_state; + struct dc_stream_state *stream; + + struct plane_resource plane_res; + struct stream_resource stream_res; + + struct clock_source *clock_source; + + struct pll_settings pll_settings; + + uint8_t pipe_idx; + + struct pipe_ctx *top_pipe; + struct pipe_ctx *bottom_pipe; + +#ifdef CONFIG_DRM_AMD_DC_DCN1_0 + struct _vcs_dpi_display_dlg_regs_st dlg_regs; + struct _vcs_dpi_display_ttu_regs_st ttu_regs; + struct _vcs_dpi_display_rq_regs_st rq_regs; + struct _vcs_dpi_display_pipe_dest_params_st pipe_dlg_param; +#endif + struct dwbc *dwbc; +}; + +struct resource_context { + struct pipe_ctx pipe_ctx[MAX_PIPES]; + bool is_stream_enc_acquired[MAX_PIPES * 2]; + bool is_audio_acquired[MAX_PIPES]; + uint8_t clock_source_ref_count[MAX_CLOCK_SOURCES]; + uint8_t dp_clock_source_ref_count; +}; + +struct dce_bw_output { + bool cpuc_state_change_enable; + bool cpup_state_change_enable; + bool stutter_mode_enable; + bool nbp_state_change_enable; + bool all_displays_in_sync; + struct dce_watermarks urgent_wm_ns[MAX_PIPES]; + struct dce_watermarks stutter_exit_wm_ns[MAX_PIPES]; + struct dce_watermarks nbp_state_change_wm_ns[MAX_PIPES]; + int sclk_khz; + int sclk_deep_sleep_khz; + int yclk_khz; + int dispclk_khz; + int blackout_recovery_time_us; +}; + +struct dcn_bw_clocks { + int dispclk_khz; + bool dppclk_div; + int dcfclk_khz; + int dcfclk_deep_sleep_khz; + int fclk_khz; + int dram_ccm_us; + int min_active_dram_ccm_us; +}; + +struct dcn_bw_output { + struct dcn_bw_clocks cur_clk; + struct dcn_bw_clocks calc_clk; + struct dcn_watermark_set watermarks; +}; + +union bw_context { + struct dcn_bw_output dcn; + struct dce_bw_output dce; +}; + +struct dc_state { + struct dc_stream_state *streams[MAX_PIPES]; + struct dc_stream_status stream_status[MAX_PIPES]; + uint8_t stream_count; + + struct resource_context res_ctx; + + /* The output from BW and WM calculations. */ + union bw_context bw; + + /* Note: these are big structures, do *not* put on stack! */ + struct dm_pp_display_configuration pp_display_cfg; +#ifdef CONFIG_DRM_AMD_DC_DCN1_0 + struct dcn_bw_internal_vars dcn_bw_vars; +#endif + + struct display_clock *dis_clk; + + struct kref refcount; +}; + +#endif /* _CORE_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/custom_float.h b/drivers/gpu/drm/amd/display/dc/inc/custom_float.h new file mode 100644 index 000000000000..f57239672216 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/custom_float.h @@ -0,0 +1,40 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef CUSTOM_FLOAT_H_ +#define CUSTOM_FLOAT_H_ + +#include "bw_fixed.h" +#include "hw_shared.h" +#include "opp.h" + + +bool convert_to_custom_float_format( + struct fixed31_32 value, + const struct custom_float_format *format, + uint32_t *result); + + +#endif //CUSTOM_FLOAT_H_ diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h b/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h new file mode 100644 index 000000000000..0bf73b742f1f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h @@ -0,0 +1,141 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_DDC_SERVICE_H__ +#define __DAL_DDC_SERVICE_H__ + +#include "include/ddc_service_types.h" +#include "include/i2caux_interface.h" + +#define EDID_SEGMENT_SIZE 256 + +/* Address range from 0x00 to 0x1F.*/ +#define DP_ADAPTOR_TYPE2_SIZE 0x20 +#define DP_ADAPTOR_TYPE2_REG_ID 0x10 +#define DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK 0x1D +/* Identifies adaptor as Dual-mode adaptor */ +#define DP_ADAPTOR_TYPE2_ID 0xA0 +/* MHz*/ +#define DP_ADAPTOR_TYPE2_MAX_TMDS_CLK 600 +/* MHz*/ +#define DP_ADAPTOR_TYPE2_MIN_TMDS_CLK 25 +/* kHZ*/ +#define DP_ADAPTOR_DVI_MAX_TMDS_CLK 165000 +/* kHZ*/ +#define DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK 165000 + +#define DDC_I2C_COMMAND_ENGINE I2C_COMMAND_ENGINE_SW + +struct ddc_service; +struct graphics_object_id; +enum ddc_result; +struct av_sync_data; +struct dp_receiver_id_info; + +struct i2c_payloads; +struct aux_payloads; + +void dal_ddc_i2c_payloads_add( + struct i2c_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write); + +void dal_ddc_aux_payloads_add( + struct aux_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write); + +struct ddc_service_init_data { + struct graphics_object_id id; + struct dc_context *ctx; + struct dc_link *link; +}; + +struct ddc_service *dal_ddc_service_create( + struct ddc_service_init_data *ddc_init_data); + +void dal_ddc_service_destroy(struct ddc_service **ddc); + +enum ddc_service_type dal_ddc_service_get_type(struct ddc_service *ddc); + +void dal_ddc_service_set_transaction_type( + struct ddc_service *ddc, + enum ddc_transaction_type type); + +bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc); + +void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( + struct ddc_service *ddc, + struct display_sink_capability *sink_cap); + +bool dal_ddc_service_query_ddc_data( + struct ddc_service *ddc, + uint32_t address, + uint8_t *write_buf, + uint32_t write_size, + uint8_t *read_buf, + uint32_t read_size); + +enum ddc_result dal_ddc_service_read_dpcd_data( + struct ddc_service *ddc, + bool i2c, + enum i2c_mot_mode mot, + uint32_t address, + uint8_t *data, + uint32_t len); + +enum ddc_result dal_ddc_service_write_dpcd_data( + struct ddc_service *ddc, + bool i2c, + enum i2c_mot_mode mot, + uint32_t address, + const uint8_t *data, + uint32_t len); + +void dal_ddc_service_write_scdc_data( + struct ddc_service *ddc_service, + uint32_t pix_clk, + bool lte_340_scramble); + +void dal_ddc_service_read_scdc_data( + struct ddc_service *ddc_service); + +void ddc_service_set_dongle_type(struct ddc_service *ddc, + enum display_dongle_type dongle_type); + +void dal_ddc_service_set_ddc_pin( + struct ddc_service *ddc_service, + struct ddc *ddc); + +struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service); + +uint32_t get_defer_delay(struct ddc_service *ddc); + +#endif /* __DAL_DDC_SERVICE_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h b/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h new file mode 100644 index 000000000000..616c73e2b0bd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h @@ -0,0 +1,64 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_LINK_DP_H__ +#define __DC_LINK_DP_H__ + +#define LINK_TRAINING_ATTEMPTS 4 +#define LINK_TRAINING_RETRY_DELAY 50 /* ms */ + +struct dc_link; +struct dc_stream_state; +struct dc_link_settings; + +bool dp_hbr_verify_link_cap( + struct dc_link *link, + struct dc_link_settings *known_limit_link_setting); + +bool dp_validate_mode_timing( + struct dc_link *link, + const struct dc_crtc_timing *timing); + +void decide_link_settings( + struct dc_stream_state *stream, + struct dc_link_settings *link_setting); + +bool perform_link_training_with_retries( + struct dc_link *link, + const struct dc_link_settings *link_setting, + bool skip_video_pattern, + int attempts); + +bool is_mst_supported(struct dc_link *link); + +void detect_dp_sink_caps(struct dc_link *link); + +void detect_edp_sink_caps(struct dc_link *link); + +bool is_dp_active_dongle(const struct dc_link *link); + +void dp_enable_mst_on_sink(struct dc_link *link, bool enable); + +#endif /* __DC_LINK_DP_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/dce_calcs.h b/drivers/gpu/drm/amd/display/dc/inc/dce_calcs.h new file mode 100644 index 000000000000..ae2399f16d1c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/dce_calcs.h @@ -0,0 +1,481 @@ +/* + * Copyright 2015-2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/** + * Bandwidth and Watermark calculations interface. + * (Refer to "DCEx_mode_support.xlsm" from Perforce.) + */ +#ifndef __DCE_CALCS_H__ +#define __DCE_CALCS_H__ + +#include "bw_fixed.h" + +struct pipe_ctx; +struct dc; +struct dc_state; +struct dce_bw_output; + +enum bw_calcs_version { + BW_CALCS_VERSION_INVALID, + BW_CALCS_VERSION_CARRIZO, + BW_CALCS_VERSION_POLARIS10, + BW_CALCS_VERSION_POLARIS11, + BW_CALCS_VERSION_STONEY, + BW_CALCS_VERSION_VEGA10 +}; + +/******************************************************************************* + * There are three types of input into Calculations: + * 1. per-DCE static values - these are "hardcoded" properties of the DCEIP + * 2. board-level values - these are generally coming from VBIOS parser + * 3. mode/configuration values - depending Mode, Scaling number of Displays etc. + ******************************************************************************/ + +enum bw_defines { + //Common + bw_def_no = 0, + bw_def_none = 0, + bw_def_yes = 1, + bw_def_ok = 1, + bw_def_high = 2, + bw_def_mid = 1, + bw_def_low = 0, + + //Internal + bw_defs_start = 255, + bw_def_underlay422, + bw_def_underlay420_luma, + bw_def_underlay420_chroma, + bw_def_underlay444, + bw_def_graphics, + bw_def_display_write_back420_luma, + bw_def_display_write_back420_chroma, + bw_def_portrait, + bw_def_hsr_mtn_4, + bw_def_hsr_mtn_h_taps, + bw_def_ceiling__h_taps_div_4___meq_hsr, + bw_def_invalid_linear_or_stereo_mode, + bw_def_invalid_rotation_or_bpp_or_stereo, + bw_def_vsr_mtn_v_taps, + bw_def_vsr_mtn_4, + bw_def_auto, + bw_def_manual, + bw_def_exceeded_allowed_maximum_sclk, + bw_def_exceeded_allowed_page_close_open, + bw_def_exceeded_allowed_outstanding_pte_req_queue_size, + bw_def_exceeded_allowed_maximum_bw, + bw_def_landscape, + + //Panning and bezel + bw_def_any_lines, + + //Underlay mode + bw_def_underlay_only, + bw_def_blended, + bw_def_blend, + + //Stereo mode + bw_def_mono, + bw_def_side_by_side, + bw_def_top_bottom, + + //Underlay surface type + bw_def_420, + bw_def_422, + bw_def_444, + + //Tiling mode + bw_def_linear, + bw_def_tiled, + bw_def_array_linear_general, + bw_def_array_linear_aligned, + bw_def_rotated_micro_tiling, + bw_def_display_micro_tiling, + + //Memory type + bw_def_gddr5, + bw_def_hbm, + + //Voltage + bw_def_high_no_nbp_state_change, + bw_def_0_72, + bw_def_0_8, + bw_def_0_9, + + bw_def_notok = -1, + bw_def_na = -1 +}; + +struct bw_calcs_dceip { + enum bw_calcs_version version; + bool large_cursor; + uint32_t cursor_max_outstanding_group_num; + bool dmif_pipe_en_fbc_chunk_tracker; + struct bw_fixed dmif_request_buffer_size; + uint32_t lines_interleaved_into_lb; + uint32_t low_power_tiling_mode; + uint32_t chunk_width; + uint32_t number_of_graphics_pipes; + uint32_t number_of_underlay_pipes; + bool display_write_back_supported; + bool argb_compression_support; + struct bw_fixed underlay_vscaler_efficiency6_bit_per_component; + struct bw_fixed underlay_vscaler_efficiency8_bit_per_component; + struct bw_fixed underlay_vscaler_efficiency10_bit_per_component; + struct bw_fixed underlay_vscaler_efficiency12_bit_per_component; + struct bw_fixed graphics_vscaler_efficiency6_bit_per_component; + struct bw_fixed graphics_vscaler_efficiency8_bit_per_component; + struct bw_fixed graphics_vscaler_efficiency10_bit_per_component; + struct bw_fixed graphics_vscaler_efficiency12_bit_per_component; + struct bw_fixed alpha_vscaler_efficiency; + uint32_t max_dmif_buffer_allocated; + uint32_t graphics_dmif_size; + uint32_t underlay_luma_dmif_size; + uint32_t underlay_chroma_dmif_size; + bool pre_downscaler_enabled; + bool underlay_downscale_prefetch_enabled; + struct bw_fixed lb_write_pixels_per_dispclk; + struct bw_fixed lb_size_per_component444; + bool graphics_lb_nodownscaling_multi_line_prefetching; + struct bw_fixed stutter_and_dram_clock_state_change_gated_before_cursor; + struct bw_fixed underlay420_luma_lb_size_per_component; + struct bw_fixed underlay420_chroma_lb_size_per_component; + struct bw_fixed underlay422_lb_size_per_component; + struct bw_fixed cursor_chunk_width; + struct bw_fixed cursor_dcp_buffer_lines; + struct bw_fixed underlay_maximum_width_efficient_for_tiling; + struct bw_fixed underlay_maximum_height_efficient_for_tiling; + struct bw_fixed peak_pte_request_to_eviction_ratio_limiting_multiple_displays_or_single_rotated_display; + struct bw_fixed peak_pte_request_to_eviction_ratio_limiting_single_display_no_rotation; + struct bw_fixed minimum_outstanding_pte_request_limit; + struct bw_fixed maximum_total_outstanding_pte_requests_allowed_by_saw; + bool limit_excessive_outstanding_dmif_requests; + struct bw_fixed linear_mode_line_request_alternation_slice; + uint32_t scatter_gather_lines_of_pte_prefetching_in_linear_mode; + uint32_t display_write_back420_luma_mcifwr_buffer_size; + uint32_t display_write_back420_chroma_mcifwr_buffer_size; + struct bw_fixed request_efficiency; + struct bw_fixed dispclk_per_request; + struct bw_fixed dispclk_ramping_factor; + struct bw_fixed display_pipe_throughput_factor; + uint32_t scatter_gather_pte_request_rows_in_tiling_mode; + struct bw_fixed mcifwr_all_surfaces_burst_time; +}; + +struct bw_calcs_vbios { + enum bw_defines memory_type; + uint32_t dram_channel_width_in_bits; + uint32_t number_of_dram_channels; + uint32_t number_of_dram_banks; + struct bw_fixed low_yclk; /*m_hz*/ + struct bw_fixed mid_yclk; /*m_hz*/ + struct bw_fixed high_yclk; /*m_hz*/ + struct bw_fixed low_sclk; /*m_hz*/ + struct bw_fixed mid1_sclk; /*m_hz*/ + struct bw_fixed mid2_sclk; /*m_hz*/ + struct bw_fixed mid3_sclk; /*m_hz*/ + struct bw_fixed mid4_sclk; /*m_hz*/ + struct bw_fixed mid5_sclk; /*m_hz*/ + struct bw_fixed mid6_sclk; /*m_hz*/ + struct bw_fixed high_sclk; /*m_hz*/ + struct bw_fixed low_voltage_max_dispclk; /*m_hz*/ + struct bw_fixed mid_voltage_max_dispclk; /*m_hz*/ + struct bw_fixed high_voltage_max_dispclk; /*m_hz*/ + struct bw_fixed low_voltage_max_phyclk; + struct bw_fixed mid_voltage_max_phyclk; + struct bw_fixed high_voltage_max_phyclk; + struct bw_fixed data_return_bus_width; + struct bw_fixed trc; + struct bw_fixed dmifmc_urgent_latency; + struct bw_fixed stutter_self_refresh_exit_latency; + struct bw_fixed stutter_self_refresh_entry_latency; + struct bw_fixed nbp_state_change_latency; + struct bw_fixed mcifwrmc_urgent_latency; + bool scatter_gather_enable; + struct bw_fixed down_spread_percentage; + uint32_t cursor_width; + uint32_t average_compression_rate; + uint32_t number_of_request_slots_gmc_reserves_for_dmif_per_channel; + struct bw_fixed blackout_duration; + struct bw_fixed maximum_blackout_recovery_time; +}; + +/******************************************************************************* + * Temporary data structure(s). + ******************************************************************************/ +#define maximum_number_of_surfaces 12 +/*Units : MHz, us */ + +struct bw_calcs_data { + /* data for all displays */ + uint32_t number_of_displays; + enum bw_defines underlay_surface_type; + enum bw_defines panning_and_bezel_adjustment; + enum bw_defines graphics_tiling_mode; + uint32_t graphics_lb_bpc; + uint32_t underlay_lb_bpc; + enum bw_defines underlay_tiling_mode; + enum bw_defines d0_underlay_mode; + bool d1_display_write_back_dwb_enable; + enum bw_defines d1_underlay_mode; + + bool cpup_state_change_enable; + bool cpuc_state_change_enable; + bool nbp_state_change_enable; + bool stutter_mode_enable; + uint32_t y_clk_level; + uint32_t sclk_level; + uint32_t number_of_underlay_surfaces; + uint32_t number_of_dram_wrchannels; + uint32_t chunk_request_delay; + uint32_t number_of_dram_channels; + enum bw_defines underlay_micro_tile_mode; + enum bw_defines graphics_micro_tile_mode; + struct bw_fixed max_phyclk; + struct bw_fixed dram_efficiency; + struct bw_fixed src_width_after_surface_type; + struct bw_fixed src_height_after_surface_type; + struct bw_fixed hsr_after_surface_type; + struct bw_fixed vsr_after_surface_type; + struct bw_fixed src_width_after_rotation; + struct bw_fixed src_height_after_rotation; + struct bw_fixed hsr_after_rotation; + struct bw_fixed vsr_after_rotation; + struct bw_fixed source_height_pixels; + struct bw_fixed hsr_after_stereo; + struct bw_fixed vsr_after_stereo; + struct bw_fixed source_width_in_lb; + struct bw_fixed lb_line_pitch; + struct bw_fixed underlay_maximum_source_efficient_for_tiling; + struct bw_fixed num_lines_at_frame_start; + struct bw_fixed min_dmif_size_in_time; + struct bw_fixed min_mcifwr_size_in_time; + struct bw_fixed total_requests_for_dmif_size; + struct bw_fixed peak_pte_request_to_eviction_ratio_limiting; + struct bw_fixed useful_pte_per_pte_request; + struct bw_fixed scatter_gather_pte_request_rows; + struct bw_fixed scatter_gather_row_height; + struct bw_fixed scatter_gather_pte_requests_in_vblank; + struct bw_fixed inefficient_linear_pitch_in_bytes; + struct bw_fixed cursor_total_data; + struct bw_fixed cursor_total_request_groups; + struct bw_fixed scatter_gather_total_pte_requests; + struct bw_fixed scatter_gather_total_pte_request_groups; + struct bw_fixed tile_width_in_pixels; + struct bw_fixed dmif_total_number_of_data_request_page_close_open; + struct bw_fixed mcifwr_total_number_of_data_request_page_close_open; + struct bw_fixed bytes_per_page_close_open; + struct bw_fixed mcifwr_total_page_close_open_time; + struct bw_fixed total_requests_for_adjusted_dmif_size; + struct bw_fixed total_dmifmc_urgent_trips; + struct bw_fixed total_dmifmc_urgent_latency; + struct bw_fixed total_display_reads_required_data; + struct bw_fixed total_display_reads_required_dram_access_data; + struct bw_fixed total_display_writes_required_data; + struct bw_fixed total_display_writes_required_dram_access_data; + struct bw_fixed display_reads_required_data; + struct bw_fixed display_reads_required_dram_access_data; + struct bw_fixed dmif_total_page_close_open_time; + struct bw_fixed min_cursor_memory_interface_buffer_size_in_time; + struct bw_fixed min_read_buffer_size_in_time; + struct bw_fixed display_reads_time_for_data_transfer; + struct bw_fixed display_writes_time_for_data_transfer; + struct bw_fixed dmif_required_dram_bandwidth; + struct bw_fixed mcifwr_required_dram_bandwidth; + struct bw_fixed required_dmifmc_urgent_latency_for_page_close_open; + struct bw_fixed required_mcifmcwr_urgent_latency; + struct bw_fixed required_dram_bandwidth_gbyte_per_second; + struct bw_fixed dram_bandwidth; + struct bw_fixed dmif_required_sclk; + struct bw_fixed mcifwr_required_sclk; + struct bw_fixed required_sclk; + struct bw_fixed downspread_factor; + struct bw_fixed v_scaler_efficiency; + struct bw_fixed scaler_limits_factor; + struct bw_fixed display_pipe_pixel_throughput; + struct bw_fixed total_dispclk_required_with_ramping; + struct bw_fixed total_dispclk_required_without_ramping; + struct bw_fixed total_read_request_bandwidth; + struct bw_fixed total_write_request_bandwidth; + struct bw_fixed dispclk_required_for_total_read_request_bandwidth; + struct bw_fixed total_dispclk_required_with_ramping_with_request_bandwidth; + struct bw_fixed total_dispclk_required_without_ramping_with_request_bandwidth; + struct bw_fixed dispclk; + struct bw_fixed blackout_recovery_time; + struct bw_fixed min_pixels_per_data_fifo_entry; + struct bw_fixed sclk_deep_sleep; + struct bw_fixed chunk_request_time; + struct bw_fixed cursor_request_time; + struct bw_fixed line_source_pixels_transfer_time; + struct bw_fixed dmifdram_access_efficiency; + struct bw_fixed mcifwrdram_access_efficiency; + struct bw_fixed total_average_bandwidth_no_compression; + struct bw_fixed total_average_bandwidth; + struct bw_fixed total_stutter_cycle_duration; + struct bw_fixed stutter_burst_time; + struct bw_fixed time_in_self_refresh; + struct bw_fixed stutter_efficiency; + struct bw_fixed worst_number_of_trips_to_memory; + struct bw_fixed immediate_flip_time; + struct bw_fixed latency_for_non_dmif_clients; + struct bw_fixed latency_for_non_mcifwr_clients; + struct bw_fixed dmifmc_urgent_latency_supported_in_high_sclk_and_yclk; + struct bw_fixed nbp_state_dram_speed_change_margin; + struct bw_fixed display_reads_time_for_data_transfer_and_urgent_latency; + struct bw_fixed dram_speed_change_margin; + struct bw_fixed min_vblank_dram_speed_change_margin; + struct bw_fixed min_stutter_refresh_duration; + uint32_t total_stutter_dmif_buffer_size; + uint32_t total_bytes_requested; + uint32_t min_stutter_dmif_buffer_size; + uint32_t num_stutter_bursts; + struct bw_fixed v_blank_nbp_state_dram_speed_change_latency_supported; + struct bw_fixed nbp_state_dram_speed_change_latency_supported; + bool fbc_en[maximum_number_of_surfaces]; + bool lpt_en[maximum_number_of_surfaces]; + bool displays_match_flag[maximum_number_of_surfaces]; + bool use_alpha[maximum_number_of_surfaces]; + bool orthogonal_rotation[maximum_number_of_surfaces]; + bool enable[maximum_number_of_surfaces]; + bool access_one_channel_only[maximum_number_of_surfaces]; + bool scatter_gather_enable_for_pipe[maximum_number_of_surfaces]; + bool interlace_mode[maximum_number_of_surfaces]; + bool display_pstate_change_enable[maximum_number_of_surfaces]; + bool line_buffer_prefetch[maximum_number_of_surfaces]; + uint32_t bytes_per_pixel[maximum_number_of_surfaces]; + uint32_t max_chunks_non_fbc_mode[maximum_number_of_surfaces]; + uint32_t lb_bpc[maximum_number_of_surfaces]; + uint32_t output_bpphdmi[maximum_number_of_surfaces]; + uint32_t output_bppdp4_lane_hbr[maximum_number_of_surfaces]; + uint32_t output_bppdp4_lane_hbr2[maximum_number_of_surfaces]; + uint32_t output_bppdp4_lane_hbr3[maximum_number_of_surfaces]; + enum bw_defines stereo_mode[maximum_number_of_surfaces]; + struct bw_fixed dmif_buffer_transfer_time[maximum_number_of_surfaces]; + struct bw_fixed displays_with_same_mode[maximum_number_of_surfaces]; + struct bw_fixed stutter_dmif_buffer_size[maximum_number_of_surfaces]; + struct bw_fixed stutter_refresh_duration[maximum_number_of_surfaces]; + struct bw_fixed stutter_exit_watermark[maximum_number_of_surfaces]; + struct bw_fixed stutter_entry_watermark[maximum_number_of_surfaces]; + struct bw_fixed h_total[maximum_number_of_surfaces]; + struct bw_fixed v_total[maximum_number_of_surfaces]; + struct bw_fixed pixel_rate[maximum_number_of_surfaces]; + struct bw_fixed src_width[maximum_number_of_surfaces]; + struct bw_fixed pitch_in_pixels[maximum_number_of_surfaces]; + struct bw_fixed pitch_in_pixels_after_surface_type[maximum_number_of_surfaces]; + struct bw_fixed src_height[maximum_number_of_surfaces]; + struct bw_fixed scale_ratio[maximum_number_of_surfaces]; + struct bw_fixed h_taps[maximum_number_of_surfaces]; + struct bw_fixed v_taps[maximum_number_of_surfaces]; + struct bw_fixed h_scale_ratio[maximum_number_of_surfaces]; + struct bw_fixed v_scale_ratio[maximum_number_of_surfaces]; + struct bw_fixed rotation_angle[maximum_number_of_surfaces]; + struct bw_fixed compression_rate[maximum_number_of_surfaces]; + struct bw_fixed hsr[maximum_number_of_surfaces]; + struct bw_fixed vsr[maximum_number_of_surfaces]; + struct bw_fixed source_width_rounded_up_to_chunks[maximum_number_of_surfaces]; + struct bw_fixed source_width_pixels[maximum_number_of_surfaces]; + struct bw_fixed source_height_rounded_up_to_chunks[maximum_number_of_surfaces]; + struct bw_fixed display_bandwidth[maximum_number_of_surfaces]; + struct bw_fixed request_bandwidth[maximum_number_of_surfaces]; + struct bw_fixed bytes_per_request[maximum_number_of_surfaces]; + struct bw_fixed useful_bytes_per_request[maximum_number_of_surfaces]; + struct bw_fixed lines_interleaved_in_mem_access[maximum_number_of_surfaces]; + struct bw_fixed latency_hiding_lines[maximum_number_of_surfaces]; + struct bw_fixed lb_partitions[maximum_number_of_surfaces]; + struct bw_fixed lb_partitions_max[maximum_number_of_surfaces]; + struct bw_fixed dispclk_required_with_ramping[maximum_number_of_surfaces]; + struct bw_fixed dispclk_required_without_ramping[maximum_number_of_surfaces]; + struct bw_fixed data_buffer_size[maximum_number_of_surfaces]; + struct bw_fixed outstanding_chunk_request_limit[maximum_number_of_surfaces]; + struct bw_fixed urgent_watermark[maximum_number_of_surfaces]; + struct bw_fixed nbp_state_change_watermark[maximum_number_of_surfaces]; + struct bw_fixed v_filter_init[maximum_number_of_surfaces]; + struct bw_fixed stutter_cycle_duration[maximum_number_of_surfaces]; + struct bw_fixed average_bandwidth[maximum_number_of_surfaces]; + struct bw_fixed average_bandwidth_no_compression[maximum_number_of_surfaces]; + struct bw_fixed scatter_gather_pte_request_limit[maximum_number_of_surfaces]; + struct bw_fixed lb_size_per_component[maximum_number_of_surfaces]; + struct bw_fixed memory_chunk_size_in_bytes[maximum_number_of_surfaces]; + struct bw_fixed pipe_chunk_size_in_bytes[maximum_number_of_surfaces]; + struct bw_fixed number_of_trips_to_memory_for_getting_apte_row[maximum_number_of_surfaces]; + struct bw_fixed adjusted_data_buffer_size[maximum_number_of_surfaces]; + struct bw_fixed adjusted_data_buffer_size_in_memory[maximum_number_of_surfaces]; + struct bw_fixed pixels_per_data_fifo_entry[maximum_number_of_surfaces]; + struct bw_fixed scatter_gather_pte_requests_in_row[maximum_number_of_surfaces]; + struct bw_fixed pte_request_per_chunk[maximum_number_of_surfaces]; + struct bw_fixed scatter_gather_page_width[maximum_number_of_surfaces]; + struct bw_fixed scatter_gather_page_height[maximum_number_of_surfaces]; + struct bw_fixed lb_lines_in_per_line_out_in_beginning_of_frame[maximum_number_of_surfaces]; + struct bw_fixed lb_lines_in_per_line_out_in_middle_of_frame[maximum_number_of_surfaces]; + struct bw_fixed cursor_width_pixels[maximum_number_of_surfaces]; + struct bw_fixed minimum_latency_hiding[maximum_number_of_surfaces]; + struct bw_fixed maximum_latency_hiding[maximum_number_of_surfaces]; + struct bw_fixed minimum_latency_hiding_with_cursor[maximum_number_of_surfaces]; + struct bw_fixed maximum_latency_hiding_with_cursor[maximum_number_of_surfaces]; + struct bw_fixed src_pixels_for_first_output_pixel[maximum_number_of_surfaces]; + struct bw_fixed src_pixels_for_last_output_pixel[maximum_number_of_surfaces]; + struct bw_fixed src_data_for_first_output_pixel[maximum_number_of_surfaces]; + struct bw_fixed src_data_for_last_output_pixel[maximum_number_of_surfaces]; + struct bw_fixed active_time[maximum_number_of_surfaces]; + struct bw_fixed horizontal_blank_and_chunk_granularity_factor[maximum_number_of_surfaces]; + struct bw_fixed cursor_latency_hiding[maximum_number_of_surfaces]; + struct bw_fixed v_blank_dram_speed_change_margin[maximum_number_of_surfaces]; + uint32_t num_displays_with_margin[3][8]; + struct bw_fixed dmif_burst_time[3][8]; + struct bw_fixed mcifwr_burst_time[3][8]; + struct bw_fixed line_source_transfer_time[maximum_number_of_surfaces][3][8]; + struct bw_fixed dram_speed_change_line_source_transfer_time[maximum_number_of_surfaces][3][8]; + struct bw_fixed min_dram_speed_change_margin[3][8]; + struct bw_fixed dispclk_required_for_dram_speed_change[3][8]; + struct bw_fixed blackout_duration_margin[3][8]; + struct bw_fixed dispclk_required_for_blackout_duration[3][8]; + struct bw_fixed dispclk_required_for_blackout_recovery[3][8]; + struct bw_fixed dmif_required_sclk_for_urgent_latency[6]; +}; + +/** + * Initialize structures with data which will NOT change at runtime. + */ +void bw_calcs_init( + struct bw_calcs_dceip *bw_dceip, + struct bw_calcs_vbios *bw_vbios, + struct hw_asic_id asic_id); + +/** + * Return: + * true - Display(s) configuration supported. + * In this case 'calcs_output' contains data for HW programming + * false - Display(s) configuration not supported (not enough bandwidth). + */ +bool bw_calcs( + struct dc_context *ctx, + const struct bw_calcs_dceip *dceip, + const struct bw_calcs_vbios *vbios, + const struct pipe_ctx *pipe, + int pipe_count, + struct dce_bw_output *calcs_output); + +#endif /* __BANDWIDTH_CALCS_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/inc/dcn_calcs.h b/drivers/gpu/drm/amd/display/dc/inc/dcn_calcs.h new file mode 100644 index 000000000000..1e231f6de732 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/dcn_calcs.h @@ -0,0 +1,635 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/** + * Bandwidth and Watermark calculations interface. + * (Refer to "DCEx_mode_support.xlsm" from Perforce.) + */ +#ifndef __DCN_CALCS_H__ +#define __DCN_CALCS_H__ + +#include "bw_fixed.h" +#include "display_clock.h" +#include "../dml/display_mode_lib.h" + +struct dc; +struct dc_state; + +/******************************************************************************* + * DCN data structures. + ******************************************************************************/ + +#define number_of_planes 6 +#define number_of_planes_minus_one 5 +#define number_of_states 4 +#define number_of_states_plus_one 5 + +#define ddr4_dram_width 64 +#define ddr4_dram_factor_single_Channel 16 +enum dcn_bw_defs { + dcn_bw_v_min0p65, + dcn_bw_v_mid0p72, + dcn_bw_v_nom0p8, + dcn_bw_v_max0p9, + dcn_bw_v_max0p91, + dcn_bw_no_support = 5, + dcn_bw_yes, + dcn_bw_hor, + dcn_bw_vert, + dcn_bw_override, + dcn_bw_rgb_sub_64, + dcn_bw_rgb_sub_32, + dcn_bw_rgb_sub_16, + dcn_bw_no, + dcn_bw_sw_linear, + dcn_bw_sw_4_kb_d, + dcn_bw_sw_4_kb_d_x, + dcn_bw_sw_64_kb_d, + dcn_bw_sw_64_kb_d_t, + dcn_bw_sw_64_kb_d_x, + dcn_bw_sw_var_d, + dcn_bw_sw_var_d_x, + dcn_bw_yuv420_sub_8, + dcn_bw_sw_4_kb_s, + dcn_bw_sw_4_kb_s_x, + dcn_bw_sw_64_kb_s, + dcn_bw_sw_64_kb_s_t, + dcn_bw_sw_64_kb_s_x, + dcn_bw_writeback, + dcn_bw_444, + dcn_bw_dp, + dcn_bw_420, + dcn_bw_hdmi, + dcn_bw_sw_var_s, + dcn_bw_sw_var_s_x, + dcn_bw_yuv420_sub_10, + dcn_bw_supported_in_v_active, + dcn_bw_supported_in_v_blank, + dcn_bw_not_supported, + dcn_bw_na, + dcn_bw_encoder_8bpc, + dcn_bw_encoder_10bpc, + dcn_bw_encoder_12bpc, + dcn_bw_encoder_16bpc, +}; + +/*bounding box parameters*/ +/*mode parameters*/ +/*system configuration*/ +/* display configuration*/ +struct dcn_bw_internal_vars { + float voltage[number_of_states_plus_one + 1]; + float max_dispclk[number_of_states_plus_one + 1]; + float max_dppclk[number_of_states_plus_one + 1]; + float dcfclk_per_state[number_of_states_plus_one + 1]; + float phyclk_per_state[number_of_states_plus_one + 1]; + float fabric_and_dram_bandwidth_per_state[number_of_states_plus_one + 1]; + float sr_exit_time; + float sr_enter_plus_exit_time; + float dram_clock_change_latency; + float urgent_latency; + float write_back_latency; + float percent_of_ideal_drambw_received_after_urg_latency; + float dcfclkv_max0p9; + float dcfclkv_nom0p8; + float dcfclkv_mid0p72; + float dcfclkv_min0p65; + float max_dispclk_vmax0p9; + float max_dppclk_vmax0p9; + float max_dispclk_vnom0p8; + float max_dppclk_vnom0p8; + float max_dispclk_vmid0p72; + float max_dppclk_vmid0p72; + float max_dispclk_vmin0p65; + float max_dppclk_vmin0p65; + float socclk; + float fabric_and_dram_bandwidth_vmax0p9; + float fabric_and_dram_bandwidth_vnom0p8; + float fabric_and_dram_bandwidth_vmid0p72; + float fabric_and_dram_bandwidth_vmin0p65; + float round_trip_ping_latency_cycles; + float urgent_out_of_order_return_per_channel; + float number_of_channels; + float vmm_page_size; + float return_bus_width; + float rob_buffer_size_in_kbyte; + float det_buffer_size_in_kbyte; + float dpp_output_buffer_pixels; + float opp_output_buffer_lines; + float pixel_chunk_size_in_kbyte; + float pte_chunk_size; + float meta_chunk_size; + float writeback_chunk_size; + enum dcn_bw_defs odm_capability; + enum dcn_bw_defs dsc_capability; + float line_buffer_size; + enum dcn_bw_defs is_line_buffer_bpp_fixed; + float line_buffer_fixed_bpp; + float max_line_buffer_lines; + float writeback_luma_buffer_size; + float writeback_chroma_buffer_size; + float max_num_dpp; + float max_num_writeback; + float max_dchub_topscl_throughput; + float max_pscl_tolb_throughput; + float max_lb_tovscl_throughput; + float max_vscl_tohscl_throughput; + float max_hscl_ratio; + float max_vscl_ratio; + float max_hscl_taps; + float max_vscl_taps; + float under_scan_factor; + float phyclkv_max0p9; + float phyclkv_nom0p8; + float phyclkv_mid0p72; + float phyclkv_min0p65; + float pte_buffer_size_in_requests; + float dispclk_ramping_margin; + float downspreading; + float max_inter_dcn_tile_repeaters; + enum dcn_bw_defs can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one; + enum dcn_bw_defs bug_forcing_luma_and_chroma_request_to_same_size_fixed; + int mode; + float viewport_width[number_of_planes_minus_one + 1]; + float htotal[number_of_planes_minus_one + 1]; + float vtotal[number_of_planes_minus_one + 1]; + float v_sync_plus_back_porch[number_of_planes_minus_one + 1]; + float vactive[number_of_planes_minus_one + 1]; + float pixel_clock[number_of_planes_minus_one + 1]; /*MHz*/ + float viewport_height[number_of_planes_minus_one + 1]; + enum dcn_bw_defs dcc_enable[number_of_planes_minus_one + 1]; + float dcc_rate[number_of_planes_minus_one + 1]; + enum dcn_bw_defs source_scan[number_of_planes_minus_one + 1]; + float lb_bit_per_pixel[number_of_planes_minus_one + 1]; + enum dcn_bw_defs source_pixel_format[number_of_planes_minus_one + 1]; + enum dcn_bw_defs source_surface_mode[number_of_planes_minus_one + 1]; + enum dcn_bw_defs output_format[number_of_planes_minus_one + 1]; + enum dcn_bw_defs output_deep_color[number_of_planes_minus_one + 1]; + enum dcn_bw_defs output[number_of_planes_minus_one + 1]; + float scaler_rec_out_width[number_of_planes_minus_one + 1]; + float scaler_recout_height[number_of_planes_minus_one + 1]; + float underscan_output[number_of_planes_minus_one + 1]; + float interlace_output[number_of_planes_minus_one + 1]; + float override_hta_ps[number_of_planes_minus_one + 1]; + float override_vta_ps[number_of_planes_minus_one + 1]; + float override_hta_pschroma[number_of_planes_minus_one + 1]; + float override_vta_pschroma[number_of_planes_minus_one + 1]; + float urgent_latency_support_us[number_of_planes_minus_one + 1]; + float h_ratio[number_of_planes_minus_one + 1]; + float v_ratio[number_of_planes_minus_one + 1]; + float htaps[number_of_planes_minus_one + 1]; + float vtaps[number_of_planes_minus_one + 1]; + float hta_pschroma[number_of_planes_minus_one + 1]; + float vta_pschroma[number_of_planes_minus_one + 1]; + enum dcn_bw_defs pte_enable; + enum dcn_bw_defs synchronized_vblank; + enum dcn_bw_defs ta_pscalculation; + int voltage_override_level; + int number_of_active_planes; + int voltage_level; + enum dcn_bw_defs immediate_flip_supported; + float dcfclk; + float max_phyclk; + float fabric_and_dram_bandwidth; + float dpp_per_plane_per_ratio[1 + 1][number_of_planes_minus_one + 1]; + enum dcn_bw_defs dispclk_dppclk_support_per_ratio[1 + 1]; + float required_dispclk_per_ratio[1 + 1]; + enum dcn_bw_defs error_message[1 + 1]; + int dispclk_dppclk_ratio; + float dpp_per_plane[number_of_planes_minus_one + 1]; + float det_buffer_size_y[number_of_planes_minus_one + 1]; + float det_buffer_size_c[number_of_planes_minus_one + 1]; + float swath_height_y[number_of_planes_minus_one + 1]; + float swath_height_c[number_of_planes_minus_one + 1]; + enum dcn_bw_defs final_error_message; + float frequency; + float header_line; + float header; + enum dcn_bw_defs voltage_override; + enum dcn_bw_defs allow_different_hratio_vratio; + float acceptable_quality_hta_ps; + float acceptable_quality_vta_ps; + float no_of_dpp[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float swath_width_yper_state[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float swath_height_yper_state[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float swath_height_cper_state[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float urgent_latency_support_us_per_state[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float v_ratio_pre_ywith_immediate_flip[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float v_ratio_pre_cwith_immediate_flip[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float required_prefetch_pixel_data_bw_with_immediate_flip[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float v_ratio_pre_ywithout_immediate_flip[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float v_ratio_pre_cwithout_immediate_flip[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + float required_prefetch_pixel_data_bw_without_immediate_flip[number_of_states_plus_one + 1][1 + 1][number_of_planes_minus_one + 1]; + enum dcn_bw_defs prefetch_supported_with_immediate_flip[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs prefetch_supported_without_immediate_flip[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs v_ratio_in_prefetch_supported_with_immediate_flip[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs v_ratio_in_prefetch_supported_without_immediate_flip[number_of_states_plus_one + 1][1 + 1]; + float required_dispclk[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs dispclk_dppclk_support[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs total_available_pipes_support[number_of_states_plus_one + 1][1 + 1]; + float total_number_of_active_dpp[number_of_states_plus_one + 1][1 + 1]; + float total_number_of_dcc_active_dpp[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs urgent_latency_support[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs mode_support_with_immediate_flip[number_of_states_plus_one + 1][1 + 1]; + enum dcn_bw_defs mode_support_without_immediate_flip[number_of_states_plus_one + 1][1 + 1]; + float return_bw_per_state[number_of_states_plus_one + 1]; + enum dcn_bw_defs dio_support[number_of_states_plus_one + 1]; + float urgent_round_trip_and_out_of_order_latency_per_state[number_of_states_plus_one + 1]; + enum dcn_bw_defs rob_support[number_of_states_plus_one + 1]; + enum dcn_bw_defs bandwidth_support[number_of_states_plus_one + 1]; + float prefetch_bw[number_of_planes_minus_one + 1]; + float meta_pte_bytes_per_frame[number_of_planes_minus_one + 1]; + float meta_row_bytes[number_of_planes_minus_one + 1]; + float dpte_bytes_per_row[number_of_planes_minus_one + 1]; + float prefetch_lines_y[number_of_planes_minus_one + 1]; + float prefetch_lines_c[number_of_planes_minus_one + 1]; + float max_num_sw_y[number_of_planes_minus_one + 1]; + float max_num_sw_c[number_of_planes_minus_one + 1]; + float line_times_for_prefetch[number_of_planes_minus_one + 1]; + float lines_for_meta_pte_with_immediate_flip[number_of_planes_minus_one + 1]; + float lines_for_meta_pte_without_immediate_flip[number_of_planes_minus_one + 1]; + float lines_for_meta_and_dpte_row_with_immediate_flip[number_of_planes_minus_one + 1]; + float lines_for_meta_and_dpte_row_without_immediate_flip[number_of_planes_minus_one + 1]; + float min_dppclk_using_single_dpp[number_of_planes_minus_one + 1]; + float swath_width_ysingle_dpp[number_of_planes_minus_one + 1]; + float byte_per_pixel_in_dety[number_of_planes_minus_one + 1]; + float byte_per_pixel_in_detc[number_of_planes_minus_one + 1]; + float number_of_dpp_required_for_det_and_lb_size[number_of_planes_minus_one + 1]; + float required_phyclk[number_of_planes_minus_one + 1]; + float read256_block_height_y[number_of_planes_minus_one + 1]; + float read256_block_width_y[number_of_planes_minus_one + 1]; + float read256_block_height_c[number_of_planes_minus_one + 1]; + float read256_block_width_c[number_of_planes_minus_one + 1]; + float max_swath_height_y[number_of_planes_minus_one + 1]; + float max_swath_height_c[number_of_planes_minus_one + 1]; + float min_swath_height_y[number_of_planes_minus_one + 1]; + float min_swath_height_c[number_of_planes_minus_one + 1]; + float read_bandwidth[number_of_planes_minus_one + 1]; + float write_bandwidth[number_of_planes_minus_one + 1]; + float pscl_factor[number_of_planes_minus_one + 1]; + float pscl_factor_chroma[number_of_planes_minus_one + 1]; + enum dcn_bw_defs scale_ratio_support; + enum dcn_bw_defs source_format_pixel_and_scan_support; + float total_read_bandwidth_consumed_gbyte_per_second; + float total_write_bandwidth_consumed_gbyte_per_second; + float total_bandwidth_consumed_gbyte_per_second; + enum dcn_bw_defs dcc_enabled_in_any_plane; + float return_bw_todcn_per_state; + float critical_point; + enum dcn_bw_defs writeback_latency_support; + float required_output_bw; + float total_number_of_active_writeback; + enum dcn_bw_defs total_available_writeback_support; + float maximum_swath_width; + float number_of_dpp_required_for_det_size; + float number_of_dpp_required_for_lb_size; + float min_dispclk_using_single_dpp; + float min_dispclk_using_dual_dpp; + enum dcn_bw_defs viewport_size_support; + float swath_width_granularity_y; + float rounded_up_max_swath_size_bytes_y; + float swath_width_granularity_c; + float rounded_up_max_swath_size_bytes_c; + float lines_in_det_luma; + float lines_in_det_chroma; + float effective_lb_latency_hiding_source_lines_luma; + float effective_lb_latency_hiding_source_lines_chroma; + float effective_detlb_lines_luma; + float effective_detlb_lines_chroma; + float projected_dcfclk_deep_sleep; + float meta_req_height_y; + float meta_req_width_y; + float meta_surface_width_y; + float meta_surface_height_y; + float meta_pte_bytes_per_frame_y; + float meta_row_bytes_y; + float macro_tile_block_size_bytes_y; + float macro_tile_block_height_y; + float data_pte_req_height_y; + float data_pte_req_width_y; + float dpte_bytes_per_row_y; + float meta_req_height_c; + float meta_req_width_c; + float meta_surface_width_c; + float meta_surface_height_c; + float meta_pte_bytes_per_frame_c; + float meta_row_bytes_c; + float macro_tile_block_size_bytes_c; + float macro_tile_block_height_c; + float macro_tile_block_width_c; + float data_pte_req_height_c; + float data_pte_req_width_c; + float dpte_bytes_per_row_c; + float v_init_y; + float max_partial_sw_y; + float v_init_c; + float max_partial_sw_c; + float dst_x_after_scaler; + float dst_y_after_scaler; + float time_calc; + float v_update_offset[number_of_planes_minus_one + 1]; + float total_repeater_delay; + float v_update_width[number_of_planes_minus_one + 1]; + float v_ready_offset[number_of_planes_minus_one + 1]; + float time_setup; + float extra_latency; + float maximum_vstartup; + float bw_available_for_immediate_flip; + float total_immediate_flip_bytes[number_of_planes_minus_one + 1]; + float time_for_meta_pte_with_immediate_flip; + float time_for_meta_pte_without_immediate_flip; + float time_for_meta_and_dpte_row_with_immediate_flip; + float time_for_meta_and_dpte_row_without_immediate_flip; + float line_times_to_request_prefetch_pixel_data_with_immediate_flip; + float line_times_to_request_prefetch_pixel_data_without_immediate_flip; + float maximum_read_bandwidth_with_prefetch_with_immediate_flip; + float maximum_read_bandwidth_with_prefetch_without_immediate_flip; + float voltage_level_with_immediate_flip; + float voltage_level_without_immediate_flip; + float total_number_of_active_dpp_per_ratio[1 + 1]; + float byte_per_pix_dety; + float byte_per_pix_detc; + float read256_bytes_block_height_y; + float read256_bytes_block_width_y; + float read256_bytes_block_height_c; + float read256_bytes_block_width_c; + float maximum_swath_height_y; + float maximum_swath_height_c; + float minimum_swath_height_y; + float minimum_swath_height_c; + float swath_width; + float prefetch_bandwidth[number_of_planes_minus_one + 1]; + float v_init_pre_fill_y[number_of_planes_minus_one + 1]; + float v_init_pre_fill_c[number_of_planes_minus_one + 1]; + float max_num_swath_y[number_of_planes_minus_one + 1]; + float max_num_swath_c[number_of_planes_minus_one + 1]; + float prefill_y[number_of_planes_minus_one + 1]; + float prefill_c[number_of_planes_minus_one + 1]; + float v_startup[number_of_planes_minus_one + 1]; + enum dcn_bw_defs allow_dram_clock_change_during_vblank[number_of_planes_minus_one + 1]; + float allow_dram_self_refresh_during_vblank[number_of_planes_minus_one + 1]; + float v_ratio_prefetch_y[number_of_planes_minus_one + 1]; + float v_ratio_prefetch_c[number_of_planes_minus_one + 1]; + float destination_lines_for_prefetch[number_of_planes_minus_one + 1]; + float destination_lines_to_request_vm_inv_blank[number_of_planes_minus_one + 1]; + float destination_lines_to_request_row_in_vblank[number_of_planes_minus_one + 1]; + float min_ttuv_blank[number_of_planes_minus_one + 1]; + float byte_per_pixel_dety[number_of_planes_minus_one + 1]; + float byte_per_pixel_detc[number_of_planes_minus_one + 1]; + float swath_width_y[number_of_planes_minus_one + 1]; + float lines_in_dety[number_of_planes_minus_one + 1]; + float lines_in_dety_rounded_down_to_swath[number_of_planes_minus_one + 1]; + float lines_in_detc[number_of_planes_minus_one + 1]; + float lines_in_detc_rounded_down_to_swath[number_of_planes_minus_one + 1]; + float full_det_buffering_time_y[number_of_planes_minus_one + 1]; + float full_det_buffering_time_c[number_of_planes_minus_one + 1]; + float active_dram_clock_change_latency_margin[number_of_planes_minus_one + 1]; + float v_blank_dram_clock_change_latency_margin[number_of_planes_minus_one + 1]; + float dcfclk_deep_sleep_per_plane[number_of_planes_minus_one + 1]; + float read_bandwidth_plane_luma[number_of_planes_minus_one + 1]; + float read_bandwidth_plane_chroma[number_of_planes_minus_one + 1]; + float display_pipe_line_delivery_time_luma[number_of_planes_minus_one + 1]; + float display_pipe_line_delivery_time_chroma[number_of_planes_minus_one + 1]; + float display_pipe_line_delivery_time_luma_prefetch[number_of_planes_minus_one + 1]; + float display_pipe_line_delivery_time_chroma_prefetch[number_of_planes_minus_one + 1]; + float pixel_pte_bytes_per_row[number_of_planes_minus_one + 1]; + float meta_pte_bytes_frame[number_of_planes_minus_one + 1]; + float meta_row_byte[number_of_planes_minus_one + 1]; + float prefetch_source_lines_y[number_of_planes_minus_one + 1]; + float prefetch_source_lines_c[number_of_planes_minus_one + 1]; + float pscl_throughput[number_of_planes_minus_one + 1]; + float pscl_throughput_chroma[number_of_planes_minus_one + 1]; + float output_bpphdmi[number_of_planes_minus_one + 1]; + float output_bppdp4_lane_hbr[number_of_planes_minus_one + 1]; + float output_bppdp4_lane_hbr2[number_of_planes_minus_one + 1]; + float output_bppdp4_lane_hbr3[number_of_planes_minus_one + 1]; + float max_vstartup_lines[number_of_planes_minus_one + 1]; + float dispclk_with_ramping; + float dispclk_without_ramping; + float dppclk_using_single_dpp_luma; + float dppclk_using_single_dpp; + float dppclk_using_single_dpp_chroma; + enum dcn_bw_defs odm_capable; + float dispclk; + float dppclk; + float return_bandwidth_to_dcn; + enum dcn_bw_defs dcc_enabled_any_plane; + float return_bw; + float critical_compression; + float total_data_read_bandwidth; + float total_active_dpp; + float total_dcc_active_dpp; + float urgent_round_trip_and_out_of_order_latency; + float last_pixel_of_line_extra_watermark; + float data_fabric_line_delivery_time_luma; + float data_fabric_line_delivery_time_chroma; + float urgent_extra_latency; + float urgent_watermark; + float ptemeta_urgent_watermark; + float dram_clock_change_watermark; + float total_active_writeback; + float writeback_dram_clock_change_watermark; + float min_full_det_buffering_time; + float frame_time_for_min_full_det_buffering_time; + float average_read_bandwidth_gbyte_per_second; + float part_of_burst_that_fits_in_rob; + float stutter_burst_time; + float stutter_efficiency_not_including_vblank; + float smallest_vblank; + float v_blank_time; + float stutter_efficiency; + float dcf_clk_deep_sleep; + float stutter_exit_watermark; + float stutter_enter_plus_exit_watermark; + float effective_det_plus_lb_lines_luma; + float urgent_latency_support_us_luma; + float effective_det_plus_lb_lines_chroma; + float urgent_latency_support_us_chroma; + float min_urgent_latency_support_us; + float non_urgent_latency_tolerance; + float block_height256_bytes_y; + float block_height256_bytes_c; + float meta_request_width_y; + float meta_surf_width_y; + float meta_surf_height_y; + float meta_pte_bytes_frame_y; + float meta_row_byte_y; + float macro_tile_size_byte_y; + float macro_tile_height_y; + float pixel_pte_req_height_y; + float pixel_pte_req_width_y; + float pixel_pte_bytes_per_row_y; + float meta_request_width_c; + float meta_surf_width_c; + float meta_surf_height_c; + float meta_pte_bytes_frame_c; + float meta_row_byte_c; + float macro_tile_size_bytes_c; + float macro_tile_height_c; + float pixel_pte_req_height_c; + float pixel_pte_req_width_c; + float pixel_pte_bytes_per_row_c; + float max_partial_swath_y; + float max_partial_swath_c; + float t_calc; + float next_prefetch_mode; + float v_startup_lines; + enum dcn_bw_defs planes_with_room_to_increase_vstartup_prefetch_bw_less_than_active_bw; + enum dcn_bw_defs planes_with_room_to_increase_vstartup_vratio_prefetch_more_than4; + enum dcn_bw_defs planes_with_room_to_increase_vstartup_destination_line_times_for_prefetch_less_than2; + enum dcn_bw_defs v_ratio_prefetch_more_than4; + enum dcn_bw_defs destination_line_times_for_prefetch_less_than2; + float prefetch_mode; + float dstx_after_scaler; + float dsty_after_scaler; + float v_update_offset_pix; + float total_repeater_delay_time; + float v_update_width_pix; + float v_ready_offset_pix; + float t_setup; + float t_wait; + float bandwidth_available_for_immediate_flip; + float tot_immediate_flip_bytes; + float max_rd_bandwidth; + float time_for_fetching_meta_pte; + float time_for_fetching_row_in_vblank; + float lines_to_request_prefetch_pixel_data; + float required_prefetch_pix_data_bw; + enum dcn_bw_defs prefetch_mode_supported; + float active_dp_ps; + float lb_latency_hiding_source_lines_y; + float lb_latency_hiding_source_lines_c; + float effective_lb_latency_hiding_y; + float effective_lb_latency_hiding_c; + float dpp_output_buffer_lines_y; + float dpp_output_buffer_lines_c; + float dppopp_buffering_y; + float max_det_buffering_time_y; + float active_dram_clock_change_latency_margin_y; + float dppopp_buffering_c; + float max_det_buffering_time_c; + float active_dram_clock_change_latency_margin_c; + float writeback_dram_clock_change_latency_margin; + float min_active_dram_clock_change_margin; + float v_blank_of_min_active_dram_clock_change_margin; + float second_min_active_dram_clock_change_margin; + float min_vblank_dram_clock_change_margin; + float dram_clock_change_margin; + float dram_clock_change_support; + float wr_bandwidth; + float max_used_bw; +}; + +struct dcn_soc_bounding_box { + float sr_exit_time; /*us*/ + float sr_enter_plus_exit_time; /*us*/ + float urgent_latency; /*us*/ + float write_back_latency; /*us*/ + float percent_of_ideal_drambw_received_after_urg_latency; /*%*/ + int max_request_size; /*bytes*/ + float dcfclkv_max0p9; /*MHz*/ + float dcfclkv_nom0p8; /*MHz*/ + float dcfclkv_mid0p72; /*MHz*/ + float dcfclkv_min0p65; /*MHz*/ + float max_dispclk_vmax0p9; /*MHz*/ + float max_dispclk_vmid0p72; /*MHz*/ + float max_dispclk_vnom0p8; /*MHz*/ + float max_dispclk_vmin0p65; /*MHz*/ + float max_dppclk_vmax0p9; /*MHz*/ + float max_dppclk_vnom0p8; /*MHz*/ + float max_dppclk_vmid0p72; /*MHz*/ + float max_dppclk_vmin0p65; /*MHz*/ + float socclk; /*MHz*/ + float fabric_and_dram_bandwidth_vmax0p9; /*GB/s*/ + float fabric_and_dram_bandwidth_vnom0p8; /*GB/s*/ + float fabric_and_dram_bandwidth_vmid0p72; /*GB/s*/ + float fabric_and_dram_bandwidth_vmin0p65; /*GB/s*/ + float phyclkv_max0p9; /*MHz*/ + float phyclkv_nom0p8; /*MHz*/ + float phyclkv_mid0p72; /*MHz*/ + float phyclkv_min0p65; /*MHz*/ + float downspreading; /*%*/ + int round_trip_ping_latency_cycles; /*DCFCLK Cycles*/ + int urgent_out_of_order_return_per_channel; /*bytes*/ + int number_of_channels; + int vmm_page_size; /*bytes*/ + float dram_clock_change_latency; /*us*/ + int return_bus_width; /*bytes*/ + float percent_disp_bw_limit; /*%*/ +}; +extern const struct dcn_soc_bounding_box dcn10_soc_defaults; + +struct dcn_ip_params { + float rob_buffer_size_in_kbyte; + float det_buffer_size_in_kbyte; + float dpp_output_buffer_pixels; + float opp_output_buffer_lines; + float pixel_chunk_size_in_kbyte; + enum dcn_bw_defs pte_enable; + int pte_chunk_size; /*kbytes*/ + int meta_chunk_size; /*kbytes*/ + int writeback_chunk_size; /*kbytes*/ + enum dcn_bw_defs odm_capability; + enum dcn_bw_defs dsc_capability; + int line_buffer_size; /*bit*/ + int max_line_buffer_lines; + enum dcn_bw_defs is_line_buffer_bpp_fixed; + int line_buffer_fixed_bpp; + int writeback_luma_buffer_size; /*kbytes*/ + int writeback_chroma_buffer_size; /*kbytes*/ + int max_num_dpp; + int max_num_writeback; + int max_dchub_topscl_throughput; /*pixels/dppclk*/ + int max_pscl_tolb_throughput; /*pixels/dppclk*/ + int max_lb_tovscl_throughput; /*pixels/dppclk*/ + int max_vscl_tohscl_throughput; /*pixels/dppclk*/ + float max_hscl_ratio; + float max_vscl_ratio; + int max_hscl_taps; + int max_vscl_taps; + int pte_buffer_size_in_requests; + float dispclk_ramping_margin; /*%*/ + float under_scan_factor; + int max_inter_dcn_tile_repeaters; + enum dcn_bw_defs can_vstartup_lines_exceed_vsync_plus_back_porch_lines_minus_one; + enum dcn_bw_defs bug_forcing_luma_and_chroma_request_to_same_size_fixed; + int dcfclk_cstate_latency; +}; +extern const struct dcn_ip_params dcn10_ip_defaults; + +bool dcn_validate_bandwidth( + struct dc *dc, + struct dc_state *context); + +unsigned int dcn_find_dcfclk_suits_all( + const struct dc *dc, + struct clocks_value *clocks); + +void dcn_bw_update_from_pplib(struct dc *dc); +void dcn_bw_notify_pplib_of_wm_ranges(struct dc *dc); +void dcn_bw_sync_calcs_and_dml(struct dc *dc); + +#endif /* __DCN_CALCS_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/abm.h b/drivers/gpu/drm/amd/display/dc/inc/hw/abm.h new file mode 100644 index 000000000000..c93b9b9a817c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/abm.h @@ -0,0 +1,48 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_ABM_H__ +#define __DC_ABM_H__ + +#include "dm_services_types.h" + +struct abm { + struct dc_context *ctx; + const struct abm_funcs *funcs; +}; + +struct abm_funcs { + void (*abm_init)(struct abm *abm); + bool (*set_abm_level)(struct abm *abm, unsigned int abm_level); + bool (*set_abm_immediate_disable)(struct abm *abm); + bool (*init_backlight)(struct abm *abm); + bool (*set_backlight_level)(struct abm *abm, + unsigned int backlight_level, + unsigned int frame_ramp, + unsigned int controller_id); + unsigned int (*get_current_backlight_8_bit)(struct abm *abm); + bool (*is_dmcu_initialized)(struct abm *abm); +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/audio.h b/drivers/gpu/drm/amd/display/dc/inc/hw/audio.h new file mode 100644 index 000000000000..925204f49717 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/audio.h @@ -0,0 +1,62 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_AUDIO_H__ +#define __DAL_AUDIO_H__ + +#include "audio_types.h" + +struct audio; + +struct audio_funcs { + + bool (*endpoint_valid)(struct audio *audio); + + void (*hw_init)(struct audio *audio); + + void (*az_enable)(struct audio *audio); + + void (*az_disable)(struct audio *audio); + + void (*az_configure)(struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_info *audio_info); + + void (*wall_dto_setup)(struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info); + + void (*destroy)(struct audio **audio); +}; + +struct audio { + const struct audio_funcs *funcs; + struct dc_context *ctx; + unsigned int inst; +}; + +#endif /* __DAL_AUDIO__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/display_clock.h b/drivers/gpu/drm/amd/display/dc/inc/hw/display_clock.h new file mode 100644 index 000000000000..f5f69cd81f6f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/display_clock.h @@ -0,0 +1,85 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DISPLAY_CLOCK_H__ +#define __DISPLAY_CLOCK_H__ + +#include "dm_services_types.h" + + +struct clocks_value { + int dispclk_in_khz; + int max_pixelclk_in_khz; + int max_non_dp_phyclk_in_khz; + int max_dp_phyclk_in_khz; + bool dispclk_notify_pplib_done; + bool pixelclk_notify_pplib_done; + bool phyclk_notigy_pplib_done; + int dcfclock_in_khz; + int dppclk_in_khz; + int mclk_in_khz; + int phyclk_in_khz; + int common_vdd_level; +}; + + +/* Structure containing all state-dependent clocks + * (dependent on "enum clocks_state") */ +struct state_dependent_clocks { + int display_clk_khz; + int pixel_clk_khz; +}; + +struct display_clock { + struct dc_context *ctx; + const struct display_clock_funcs *funcs; + + enum dm_pp_clocks_state max_clks_state; + enum dm_pp_clocks_state cur_min_clks_state; + struct clocks_value cur_clocks_value; +}; + +struct display_clock_funcs { + int (*set_clock)(struct display_clock *disp_clk, + int requested_clock_khz); + + enum dm_pp_clocks_state (*get_required_clocks_state)( + struct display_clock *disp_clk, + struct state_dependent_clocks *req_clocks); + + bool (*set_min_clocks_state)(struct display_clock *disp_clk, + enum dm_pp_clocks_state dm_pp_clocks_state); + + int (*get_dp_ref_clk_frequency)(struct display_clock *disp_clk); + + bool (*apply_clock_voltage_request)( + struct display_clock *disp_clk, + enum dm_pp_clock_type clocks_type, + int clocks_in_khz, + bool pre_mode_set, + bool update_dp_phyclk); +}; + +#endif /* __DISPLAY_CLOCK_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h new file mode 100644 index 000000000000..0574c29cc4a8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h @@ -0,0 +1,50 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_DMCU_H__ +#define __DC_DMCU_H__ + +#include "dm_services_types.h" + +struct dmcu { + struct dc_context *ctx; + const struct dmcu_funcs *funcs; +}; + +struct dmcu_funcs { + bool (*load_iram)(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, + unsigned int bytes); + void (*set_psr_enable)(struct dmcu *dmcu, bool enable, bool wait); + void (*setup_psr)(struct dmcu *dmcu, + struct dc_link *link, + struct psr_context *psr_context); + void (*get_psr_state)(struct dmcu *dmcu, uint32_t *psr_state); + void (*set_psr_wait_loop)(struct dmcu *dmcu, + unsigned int wait_loop_number); + void (*get_psr_wait_loop)(unsigned int *psr_wait_loop_number); +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h new file mode 100644 index 000000000000..83a68460edcd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h @@ -0,0 +1,134 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#ifndef __DAL_DPP_H__ +#define __DAL_DPP_H__ + +#include "transform.h" + +struct dpp { + const struct dpp_funcs *funcs; + struct dc_context *ctx; + int inst; + struct dpp_caps *caps; + struct pwl_params regamma_params; +}; + +struct dpp_grph_csc_adjustment { + struct fixed31_32 temperature_matrix[CSC_TEMPERATURE_MATRIX_SIZE]; + enum graphics_gamut_adjust_type gamut_adjust_type; +}; + +struct dpp_funcs { + void (*dpp_reset)(struct dpp *dpp); + + void (*dpp_set_scaler)(struct dpp *dpp, + const struct scaler_data *scl_data); + + void (*dpp_set_pixel_storage_depth)( + struct dpp *dpp, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params); + + bool (*dpp_get_optimal_number_of_taps)( + struct dpp *dpp, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps); + + void (*dpp_set_gamut_remap)( + struct dpp *dpp, + const struct dpp_grph_csc_adjustment *adjust); + + void (*opp_set_csc_default)( + struct dpp *dpp, + const struct default_adjustment *default_adjust); + + void (*opp_set_csc_adjustment)( + struct dpp *dpp, + const struct out_csc_color_matrix *tbl_entry); + + void (*opp_power_on_regamma_lut)( + struct dpp *dpp, + bool power_on); + + void (*opp_program_regamma_lut)( + struct dpp *dpp, + const struct pwl_result_data *rgb, + uint32_t num); + + void (*opp_configure_regamma_lut)( + struct dpp *dpp, + bool is_ram_a); + + void (*opp_program_regamma_lutb_settings)( + struct dpp *dpp, + const struct pwl_params *params); + + void (*opp_program_regamma_luta_settings)( + struct dpp *dpp, + const struct pwl_params *params); + + void (*opp_program_regamma_pwl)( + struct dpp *dpp, const struct pwl_params *params); + + void (*opp_set_regamma_mode)( + struct dpp *dpp_base, + enum opp_regamma mode); + + void (*ipp_set_degamma)( + struct dpp *dpp_base, + enum ipp_degamma_mode mode); + + void (*ipp_program_input_lut)( + struct dpp *dpp_base, + const struct dc_gamma *gamma); + + void (*ipp_program_degamma_pwl)(struct dpp *dpp_base, + const struct pwl_params *params); + + void (*ipp_setup)( + struct dpp *dpp_base, + enum surface_pixel_format input_format, + enum expansion_mode mode); + + void (*ipp_full_bypass)(struct dpp *dpp_base); + + void (*set_cursor_attributes)( + struct dpp *dpp_base, + const struct dc_cursor_attributes *attr); + + void (*set_cursor_position)( + struct dpp *dpp_base, + const struct dc_cursor_position *pos, + const struct dc_cursor_mi_param *param, + uint32_t width + ); + +}; + + + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/gpio.h b/drivers/gpu/drm/amd/display/dc/inc/hw/gpio.h new file mode 100644 index 000000000000..90d0148430fb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/gpio.h @@ -0,0 +1,86 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GPIO_H__ +#define __DAL_GPIO_H__ + +#include "gpio_types.h" + +struct gpio { + struct gpio_service *service; + struct hw_gpio_pin *pin; + enum gpio_id id; + uint32_t en; + enum gpio_mode mode; + /* when GPIO comes from VBIOS, it has defined output state */ + enum gpio_pin_output_state output_state; +}; + +#if 0 +struct gpio_funcs { + + struct hw_gpio_pin *(*create_ddc_data)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_ddc_clock)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_generic)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_hpd)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_gpio_pad)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_sync)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + struct hw_gpio_pin *(*create_gsl)( + struct dc_context *ctx, + enum gpio_id id, + uint32_t en); + + /* HW translation */ + bool (*offset_to_id)( + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en); + bool (*id_to_offset)( + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info); +}; +#endif + +#endif /* __DAL_GPIO__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h new file mode 100644 index 000000000000..0d186be24cf4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -0,0 +1,105 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HUBP_H__ +#define __DAL_HUBP_H__ + +#include "mem_input.h" + +struct hubp { + struct hubp_funcs *funcs; + struct dc_context *ctx; + struct dc_plane_address request_address; + struct dc_plane_address current_address; + int inst; + int opp_id; + int mpcc_id; + struct dc_cursor_attributes curs_attr; +}; + + +struct hubp_funcs { + void (*hubp_setup)( + struct hubp *hubp, + struct _vcs_dpi_display_dlg_regs_st *dlg_regs, + struct _vcs_dpi_display_ttu_regs_st *ttu_regs, + struct _vcs_dpi_display_rq_regs_st *rq_regs, + struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest); + + void (*dcc_control)(struct hubp *hubp, bool enable, + bool independent_64b_blks); + void (*mem_program_viewport)( + struct hubp *hubp, + const struct rect *viewport, + const struct rect *viewport_c); + + bool (*hubp_program_surface_flip_and_addr)( + struct hubp *hubp, + const struct dc_plane_address *address, + bool flip_immediate); + + void (*hubp_program_pte_vm)( + struct hubp *hubp, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation); + + void (*hubp_set_vm_system_aperture_settings)( + struct hubp *hubp, + struct vm_system_aperture_param *apt); + + void (*hubp_set_vm_context0_settings)( + struct hubp *hubp, + const struct vm_context0_param *vm0); + + void (*hubp_program_surface_config)( + struct hubp *hubp, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror); + + bool (*hubp_is_flip_pending)(struct hubp *hubp); + + void (*hubp_update_dchub)(struct hubp *hubp, + struct dchub_init_data *dh_data); + + void (*set_blank)(struct hubp *hubp, bool blank); + void (*set_hubp_blank_en)(struct hubp *hubp, bool blank); + + void (*set_cursor_attributes)( + struct hubp *hubp, + const struct dc_cursor_attributes *attr); + + void (*set_cursor_position)( + struct hubp *hubp, + const struct dc_cursor_position *pos, + const struct dc_cursor_mi_param *param); + +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h new file mode 100644 index 000000000000..9602f261b614 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h @@ -0,0 +1,141 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_HW_SHARED_H__ +#define __DAL_HW_SHARED_H__ + +#include "os_types.h" +#include "fixed31_32.h" +#include "dc_hw_types.h" + +/****************************************************************************** + * Data types shared between different Virtual HW blocks + ******************************************************************************/ + +#define MAX_PIPES 6 + +struct gamma_curve { + uint32_t offset; + uint32_t segments_num; +}; + +struct curve_points { + struct fixed31_32 x; + struct fixed31_32 y; + struct fixed31_32 offset; + struct fixed31_32 slope; + + uint32_t custom_float_x; + uint32_t custom_float_y; + uint32_t custom_float_offset; + uint32_t custom_float_slope; +}; + +struct pwl_result_data { + struct fixed31_32 red; + struct fixed31_32 green; + struct fixed31_32 blue; + + struct fixed31_32 delta_red; + struct fixed31_32 delta_green; + struct fixed31_32 delta_blue; + + uint32_t red_reg; + uint32_t green_reg; + uint32_t blue_reg; + + uint32_t delta_red_reg; + uint32_t delta_green_reg; + uint32_t delta_blue_reg; +}; + +struct pwl_params { + struct gamma_curve arr_curve_points[34]; + struct curve_points arr_points[3]; + struct pwl_result_data rgb_resulted[256 + 3]; + uint32_t hw_points_num; +}; + +/* move to dpp + * while we are moving functionality out of opp to dpp to align + * HW programming to HW IP, we define these struct in hw_shared + * so we can still compile while refactoring + */ + +enum lb_pixel_depth { + /* do not change the values because it is used as bit vector */ + LB_PIXEL_DEPTH_18BPP = 1, + LB_PIXEL_DEPTH_24BPP = 2, + LB_PIXEL_DEPTH_30BPP = 4, + LB_PIXEL_DEPTH_36BPP = 8 +}; + +enum graphics_csc_adjust_type { + GRAPHICS_CSC_ADJUST_TYPE_BYPASS = 0, + GRAPHICS_CSC_ADJUST_TYPE_HW, /* without adjustments */ + GRAPHICS_CSC_ADJUST_TYPE_SW /*use adjustments */ +}; + +enum ipp_degamma_mode { + IPP_DEGAMMA_MODE_BYPASS, + IPP_DEGAMMA_MODE_HW_sRGB, + IPP_DEGAMMA_MODE_HW_xvYCC, + IPP_DEGAMMA_MODE_USER_PWL +}; + +enum ipp_output_format { + IPP_OUTPUT_FORMAT_12_BIT_FIX, + IPP_OUTPUT_FORMAT_16_BIT_BYPASS, + IPP_OUTPUT_FORMAT_FLOAT +}; + +enum expansion_mode { + EXPANSION_MODE_DYNAMIC, + EXPANSION_MODE_ZERO +}; + +struct default_adjustment { + enum lb_pixel_depth lb_color_depth; + enum dc_color_space out_color_space; + enum dc_color_space in_color_space; + enum dc_color_depth color_depth; + enum pixel_format surface_pixel_format; + enum graphics_csc_adjust_type csc_adjust_type; + bool force_hw_default; +}; + +struct out_csc_color_matrix { + enum dc_color_space color_space; + uint16_t regval[12]; +}; + +enum opp_regamma { + OPP_REGAMMA_BYPASS = 0, + OPP_REGAMMA_SRGB, + OPP_REGAMMA_3_6, + OPP_REGAMMA_USER +}; + +#endif /* __DAL_HW_SHARED_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/ipp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/ipp.h new file mode 100644 index 000000000000..f11aa484f46e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/ipp.h @@ -0,0 +1,112 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IPP_H__ +#define __DAL_IPP_H__ + +#include "hw_shared.h" +#include "dc_hw_types.h" + +#define MAXTRIX_COEFFICIENTS_NUMBER 12 +#define MAXTRIX_COEFFICIENTS_WRAP_NUMBER (MAXTRIX_COEFFICIENTS_NUMBER + 4) +#define MAX_OVL_MATRIX_COUNT 12 + +/* IPP RELATED */ +struct input_pixel_processor { + struct dc_context *ctx; + unsigned int inst; + const struct ipp_funcs *funcs; +}; + +enum ipp_prescale_mode { + IPP_PRESCALE_MODE_BYPASS, + IPP_PRESCALE_MODE_FIXED_SIGNED, + IPP_PRESCALE_MODE_FLOAT_SIGNED, + IPP_PRESCALE_MODE_FIXED_UNSIGNED, + IPP_PRESCALE_MODE_FLOAT_UNSIGNED +}; + +struct ipp_prescale_params { + enum ipp_prescale_mode mode; + uint16_t bias; + uint16_t scale; +}; + + + +enum ovl_color_space { + OVL_COLOR_SPACE_UNKNOWN = 0, + OVL_COLOR_SPACE_RGB, + OVL_COLOR_SPACE_YUV601, + OVL_COLOR_SPACE_YUV709 +}; + + +struct ipp_funcs { + + /*** cursor ***/ + void (*ipp_cursor_set_position)( + struct input_pixel_processor *ipp, + const struct dc_cursor_position *position, + const struct dc_cursor_mi_param *param); + + void (*ipp_cursor_set_attributes)( + struct input_pixel_processor *ipp, + const struct dc_cursor_attributes *attributes); + + /*** setup input pixel processing ***/ + + /* put the entire pixel processor to bypass */ + void (*ipp_full_bypass)( + struct input_pixel_processor *ipp); + + /* setup ipp to expand/convert input to pixel processor internal format */ + void (*ipp_setup)( + struct input_pixel_processor *ipp, + enum surface_pixel_format input_format, + enum expansion_mode mode); + + /* DCE function to setup IPP. TODO: see if we can consolidate to setup */ + void (*ipp_program_prescale)( + struct input_pixel_processor *ipp, + struct ipp_prescale_params *params); + + void (*ipp_program_input_lut)( + struct input_pixel_processor *ipp, + const struct dc_gamma *gamma); + + /*** DEGAMMA RELATED ***/ + void (*ipp_set_degamma)( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode); + + void (*ipp_program_degamma_pwl)( + struct input_pixel_processor *ipp, + const struct pwl_params *params); + + void (*ipp_destroy)(struct input_pixel_processor **ipp); +}; + +#endif /* __DAL_IPP_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h new file mode 100644 index 000000000000..3d33bcda7059 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h @@ -0,0 +1,134 @@ +/* + * link_encoder.h + * + * Created on: Oct 6, 2015 + * Author: yonsun + */ + +#ifndef LINK_ENCODER_H_ +#define LINK_ENCODER_H_ + +#include "grph_object_defs.h" +#include "signal_types.h" +#include "dc_types.h" + +struct dc_context; +struct encoder_set_dp_phy_pattern_param; +struct link_mst_stream_allocation_table; +struct dc_link_settings; +struct link_training_settings; +struct pipe_ctx; + +struct encoder_init_data { + enum channel_id channel; + struct graphics_object_id connector; + enum hpd_source_id hpd_source; + /* TODO: in DAL2, here was pointer to EventManagerInterface */ + struct graphics_object_id encoder; + struct dc_context *ctx; + enum transmitter transmitter; +}; + +struct encoder_feature_support { + union { + struct { + uint32_t IS_HBR2_CAPABLE:1; + uint32_t IS_HBR3_CAPABLE:1; + uint32_t IS_TPS3_CAPABLE:1; + uint32_t IS_TPS4_CAPABLE:1; + uint32_t IS_YCBCR_CAPABLE:1; + uint32_t HDMI_6GB_EN:1; + } bits; + uint32_t raw; + } flags; + + enum dc_color_depth max_hdmi_deep_color; + unsigned int max_hdmi_pixel_clock; + bool ycbcr420_supported; +}; + +union dpcd_psr_configuration { + struct { + unsigned char ENABLE : 1; + unsigned char TRANSMITTER_ACTIVE_IN_PSR : 1; + unsigned char CRC_VERIFICATION : 1; + unsigned char FRAME_CAPTURE_INDICATION : 1; + /* For eDP 1.4, PSR v2*/ + unsigned char LINE_CAPTURE_INDICATION : 1; + /* For eDP 1.4, PSR v2*/ + unsigned char IRQ_HPD_WITH_CRC_ERROR : 1; + unsigned char RESERVED : 2; + } bits; + unsigned char raw; +}; + +union psr_error_status { + struct { + unsigned char LINK_CRC_ERROR :1; + unsigned char RFB_STORAGE_ERROR :1; + unsigned char RESERVED :6; + } bits; + unsigned char raw; +}; + +union psr_sink_psr_status { + struct { + unsigned char SINK_SELF_REFRESH_STATUS :3; + unsigned char RESERVED :5; + } bits; + unsigned char raw; +}; + +struct link_encoder { + const struct link_encoder_funcs *funcs; + int32_t aux_channel_offset; + struct dc_context *ctx; + struct graphics_object_id id; + struct graphics_object_id connector; + uint32_t output_signals; + enum engine_id preferred_engine; + struct encoder_feature_support features; + enum transmitter transmitter; + enum hpd_source_id hpd_source; +}; + +struct link_encoder_funcs { + bool (*validate_output_with_stream)( + struct link_encoder *enc, const struct dc_stream_state *stream); + void (*hw_init)(struct link_encoder *enc); + void (*setup)(struct link_encoder *enc, + enum signal_type signal); + void (*enable_tmds_output)(struct link_encoder *enc, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + bool hdmi, + bool dual_link, + uint32_t pixel_clock); + void (*enable_dp_output)(struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source); + void (*enable_dp_mst_output)(struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source); + void (*disable_output)(struct link_encoder *link_enc, + enum signal_type signal, struct dc_link *link); + void (*dp_set_lane_settings)(struct link_encoder *enc, + const struct link_training_settings *link_settings); + void (*dp_set_phy_pattern)(struct link_encoder *enc, + const struct encoder_set_dp_phy_pattern_param *para); + void (*update_mst_stream_allocation_table)( + struct link_encoder *enc, + const struct link_mst_stream_allocation_table *table); + void (*psr_program_dp_dphy_fast_training)(struct link_encoder *enc, + bool exit_link_training_required); + void (*psr_program_secondary_packet)(struct link_encoder *enc, + unsigned int sdp_transmit_line_num_deadline); + void (*connect_dig_be_to_fe)(struct link_encoder *enc, + enum engine_id engine, + bool connect); + void (*enable_hpd)(struct link_encoder *enc); + void (*disable_hpd)(struct link_encoder *enc); + void (*destroy)(struct link_encoder **enc); +}; + +#endif /* LINK_ENCODER_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h new file mode 100644 index 000000000000..3e1e7e6a8792 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h @@ -0,0 +1,175 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DAL_MEM_INPUT_H__ +#define __DAL_MEM_INPUT_H__ + +#include "dc.h" +#include "include/grph_object_id.h" + +#include "dml/display_mode_structs.h" + +struct dchub_init_data; +struct cstate_pstate_watermarks_st { + uint32_t cstate_exit_ns; + uint32_t cstate_enter_plus_exit_ns; + uint32_t pstate_change_ns; +}; + +struct dcn_watermarks { + uint32_t pte_meta_urgent_ns; + uint32_t urgent_ns; + struct cstate_pstate_watermarks_st cstate_pstate; +}; + +struct dcn_watermark_set { + struct dcn_watermarks a; + struct dcn_watermarks b; + struct dcn_watermarks c; + struct dcn_watermarks d; +}; + +struct dce_watermarks { + int a_mark; + int b_mark; + int c_mark; + int d_mark; +}; + +struct stutter_modes { + bool enhanced; + bool quad_dmif_buffer; + bool watermark_nb_pstate; +}; + +struct mem_input { + struct mem_input_funcs *funcs; + struct dc_context *ctx; + struct dc_plane_address request_address; + struct dc_plane_address current_address; + int inst; + struct stutter_modes stutter_mode; +}; + +struct vm_system_aperture_param { + PHYSICAL_ADDRESS_LOC sys_default; + PHYSICAL_ADDRESS_LOC sys_low; + PHYSICAL_ADDRESS_LOC sys_high; +}; + +struct vm_context0_param { + PHYSICAL_ADDRESS_LOC pte_base; + PHYSICAL_ADDRESS_LOC pte_start; + PHYSICAL_ADDRESS_LOC pte_end; + PHYSICAL_ADDRESS_LOC fault_default; +}; + +struct mem_input_funcs { + void (*mem_input_setup)( + struct mem_input *mem_input, + struct _vcs_dpi_display_dlg_regs_st *dlg_regs, + struct _vcs_dpi_display_ttu_regs_st *ttu_regs, + struct _vcs_dpi_display_rq_regs_st *rq_regs, + struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest); + + void (*dcc_control)(struct mem_input *mem_input, bool enable, + bool independent_64b_blks); + void (*mem_program_viewport)( + struct mem_input *mem_input, + const struct rect *viewport, + const struct rect *viewport_c); + + void (*mem_input_program_display_marks)( + struct mem_input *mem_input, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns); + + void (*mem_input_program_chroma_display_marks)( + struct mem_input *mem_input, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns); + + void (*allocate_mem_input)( + struct mem_input *mem_input, + uint32_t h_total,/* for current target */ + uint32_t v_total,/* for current target */ + uint32_t pix_clk_khz,/* for current target */ + uint32_t total_streams_num); + + void (*free_mem_input)( + struct mem_input *mem_input, + uint32_t paths_num); + + bool (*mem_input_program_surface_flip_and_addr)( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate); + + void (*mem_input_program_pte_vm)( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation); + + void (*mem_input_set_vm_system_aperture_settings)( + struct mem_input *mem_input, + struct vm_system_aperture_param *apt); + + void (*mem_input_set_vm_context0_settings)( + struct mem_input *mem_input, + const struct vm_context0_param *vm0); + + void (*mem_input_program_surface_config)( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror); + + bool (*mem_input_is_flip_pending)(struct mem_input *mem_input); + + void (*mem_input_update_dchub)(struct mem_input *mem_input, + struct dchub_init_data *dh_data); + + void (*set_blank)(struct mem_input *mi, bool blank); + void (*set_hubp_blank_en)(struct mem_input *mi, bool blank); + + void (*set_cursor_attributes)( + struct mem_input *mem_input, + const struct dc_cursor_attributes *attr); + + void (*set_cursor_position)( + struct mem_input *mem_input, + const struct dc_cursor_position *pos, + const struct dc_cursor_mi_param *param); + +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h new file mode 100644 index 000000000000..d4188b2c0626 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h @@ -0,0 +1,61 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_MPCC_H__ +#define __DC_MPCC_H__ + +#include "dc_hw_types.h" +#include "opp.h" + +struct mpcc_cfg { + int dpp_id; + int opp_id; + struct mpc_tree_cfg *tree_cfg; + unsigned int z_index; + + struct tg_color black_color; + bool per_pixel_alpha; + bool pre_multiplied_alpha; +}; + +struct mpc { + const struct mpc_funcs *funcs; + struct dc_context *ctx; +}; + +struct mpc_funcs { + int (*add)(struct mpc *mpc, struct mpcc_cfg *cfg); + + void (*remove)(struct mpc *mpc, + struct mpc_tree_cfg *tree_cfg, + int opp_id, + int mpcc_inst); + + void (*wait_for_idle)(struct mpc *mpc, int id); + + void (*update_blend_mode)(struct mpc *mpc, struct mpcc_cfg *cfg); + +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/opp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/opp.h new file mode 100644 index 000000000000..75adb8fec551 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/opp.h @@ -0,0 +1,289 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_OPP_H__ +#define __DAL_OPP_H__ + +#include "hw_shared.h" +#include "dc_hw_types.h" +#include "transform.h" + +struct fixed31_32; + +/* TODO: Need cleanup */ +enum clamping_range { + CLAMPING_FULL_RANGE = 0, /* No Clamping */ + CLAMPING_LIMITED_RANGE_8BPC, /* 8 bpc: Clamping 1 to FE */ + CLAMPING_LIMITED_RANGE_10BPC, /* 10 bpc: Clamping 4 to 3FB */ + CLAMPING_LIMITED_RANGE_12BPC, /* 12 bpc: Clamping 10 to FEF */ + /* Use programmable clampping value on FMT_CLAMP_COMPONENT_R/G/B. */ + CLAMPING_LIMITED_RANGE_PROGRAMMABLE +}; + +struct clamping_and_pixel_encoding_params { + enum dc_pixel_encoding pixel_encoding; /* Pixel Encoding */ + enum clamping_range clamping_level; /* Clamping identifier */ + enum dc_color_depth c_depth; /* Deep color use. */ +}; + +struct bit_depth_reduction_params { + struct { + /* truncate/round */ + /* trunc/round enabled*/ + uint32_t TRUNCATE_ENABLED:1; + /* 2 bits: 0=6 bpc, 1=8 bpc, 2 = 10bpc*/ + uint32_t TRUNCATE_DEPTH:2; + /* truncate or round*/ + uint32_t TRUNCATE_MODE:1; + + /* spatial dither */ + /* Spatial Bit Depth Reduction enabled*/ + uint32_t SPATIAL_DITHER_ENABLED:1; + /* 2 bits: 0=6 bpc, 1 = 8 bpc, 2 = 10bpc*/ + uint32_t SPATIAL_DITHER_DEPTH:2; + /* 0-3 to select patterns*/ + uint32_t SPATIAL_DITHER_MODE:2; + /* Enable RGB random dithering*/ + uint32_t RGB_RANDOM:1; + /* Enable Frame random dithering*/ + uint32_t FRAME_RANDOM:1; + /* Enable HighPass random dithering*/ + uint32_t HIGHPASS_RANDOM:1; + + /* temporal dither*/ + /* frame modulation enabled*/ + uint32_t FRAME_MODULATION_ENABLED:1; + /* same as for trunc/spatial*/ + uint32_t FRAME_MODULATION_DEPTH:2; + /* 2/4 gray levels*/ + uint32_t TEMPORAL_LEVEL:1; + uint32_t FRC25:2; + uint32_t FRC50:2; + uint32_t FRC75:2; + } flags; + + uint32_t r_seed_value; + uint32_t b_seed_value; + uint32_t g_seed_value; + enum dc_pixel_encoding pixel_encoding; +}; + +enum wide_gamut_regamma_mode { + /* 0x0 - BITS2:0 Bypass */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS, + /* 0x1 - Fixed curve sRGB 2.4 */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24, + /* 0x2 - Fixed curve xvYCC 2.22 */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_XYYCC22, + /* 0x3 - Programmable control A */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A, + /* 0x4 - Programmable control B */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_B, + /* 0x0 - BITS6:4 Bypass */ + WIDE_GAMUT_REGAMMA_MODE_OVL_BYPASS, + /* 0x1 - Fixed curve sRGB 2.4 */ + WIDE_GAMUT_REGAMMA_MODE_OVL_SRGB24, + /* 0x2 - Fixed curve xvYCC 2.22 */ + WIDE_GAMUT_REGAMMA_MODE_OVL_XYYCC22, + /* 0x3 - Programmable control A */ + WIDE_GAMUT_REGAMMA_MODE_OVL_MATRIX_A, + /* 0x4 - Programmable control B */ + WIDE_GAMUT_REGAMMA_MODE_OVL_MATRIX_B +}; + +struct gamma_pixel { + struct fixed31_32 r; + struct fixed31_32 g; + struct fixed31_32 b; +}; + +enum channel_name { + CHANNEL_NAME_RED, + CHANNEL_NAME_GREEN, + CHANNEL_NAME_BLUE +}; + +struct custom_float_format { + uint32_t mantissa_bits; + uint32_t exponenta_bits; + bool sign; +}; + +struct custom_float_value { + uint32_t mantissa; + uint32_t exponenta; + uint32_t value; + bool negative; +}; + +struct hw_x_point { + uint32_t custom_float_x; + struct fixed31_32 x; + struct fixed31_32 regamma_y_red; + struct fixed31_32 regamma_y_green; + struct fixed31_32 regamma_y_blue; + +}; + +struct pwl_float_data_ex { + struct fixed31_32 r; + struct fixed31_32 g; + struct fixed31_32 b; + struct fixed31_32 delta_r; + struct fixed31_32 delta_g; + struct fixed31_32 delta_b; +}; + +enum hw_point_position { + /* hw point sits between left and right sw points */ + HW_POINT_POSITION_MIDDLE, + /* hw point lays left from left (smaller) sw point */ + HW_POINT_POSITION_LEFT, + /* hw point lays stays from right (bigger) sw point */ + HW_POINT_POSITION_RIGHT +}; + +struct gamma_point { + int32_t left_index; + int32_t right_index; + enum hw_point_position pos; + struct fixed31_32 coeff; +}; + +struct pixel_gamma_point { + struct gamma_point r; + struct gamma_point g; + struct gamma_point b; +}; + +struct gamma_coefficients { + struct fixed31_32 a0[3]; + struct fixed31_32 a1[3]; + struct fixed31_32 a2[3]; + struct fixed31_32 a3[3]; + struct fixed31_32 user_gamma[3]; + struct fixed31_32 user_contrast; + struct fixed31_32 user_brightness; +}; + +struct pwl_float_data { + struct fixed31_32 r; + struct fixed31_32 g; + struct fixed31_32 b; +}; + +struct mpc_tree_cfg { + int num_pipes; + int dpp[MAX_PIPES]; + int mpcc[MAX_PIPES]; +}; + +struct output_pixel_processor { + struct dc_context *ctx; + uint32_t inst; + struct pwl_params regamma_params; + struct mpc_tree_cfg mpc_tree; + bool mpcc_disconnect_pending[MAX_PIPES]; + const struct opp_funcs *funcs; +}; + +enum fmt_stereo_action { + FMT_STEREO_ACTION_ENABLE = 0, + FMT_STEREO_ACTION_DISABLE, + FMT_STEREO_ACTION_UPDATE_POLARITY +}; + +struct opp_grph_csc_adjustment { + //enum grph_color_adjust_option color_adjust_option; + enum dc_color_space c_space; + enum dc_color_depth color_depth; /* clean up to uint32_t */ + enum graphics_csc_adjust_type csc_adjust_type; + int32_t adjust_divider; + int32_t grph_cont; + int32_t grph_sat; + int32_t grph_bright; + int32_t grph_hue; +}; + +/* Underlay related types */ + +struct hw_adjustment_range { + int32_t hw_default; + int32_t min; + int32_t max; + int32_t step; + uint32_t divider; /* (actually HW range is min/divider; divider !=0) */ +}; + +enum ovl_csc_adjust_item { + OVERLAY_BRIGHTNESS = 0, + OVERLAY_GAMMA, + OVERLAY_CONTRAST, + OVERLAY_SATURATION, + OVERLAY_HUE, + OVERLAY_ALPHA, + OVERLAY_ALPHA_PER_PIX, + OVERLAY_COLOR_TEMPERATURE +}; + +struct opp_funcs { + + + /* FORMATTER RELATED */ + + void (*opp_program_fmt)( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping); + + void (*opp_set_dyn_expansion)( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal); + + void (*opp_program_bit_depth_reduction)( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params); + + /* underlay related */ + void (*opp_get_underlay_adjustment_range)( + struct output_pixel_processor *opp, + enum ovl_csc_adjust_item overlay_adjust_item, + struct hw_adjustment_range *range); + + void (*opp_destroy)(struct output_pixel_processor **opp); + + void (*opp_set_stereo_polarity)( + struct output_pixel_processor *opp, + bool enable, + bool rightEyePolarity); + + void (*opp_set_test_pattern)( + struct output_pixel_processor *opp, + bool enable); +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h new file mode 100644 index 000000000000..3050afe8e8a9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h @@ -0,0 +1,130 @@ +/* + * stream_encoder.h + * + */ + +#ifndef STREAM_ENCODER_H_ +#define STREAM_ENCODER_H_ + +#include "audio_types.h" + +struct dc_bios; +struct dc_context; +struct dc_crtc_timing; + +struct encoder_info_packet { + bool valid; + uint8_t hb0; + uint8_t hb1; + uint8_t hb2; + uint8_t hb3; + uint8_t sb[32]; +}; + +struct encoder_info_frame { + /* auxiliary video information */ + struct encoder_info_packet avi; + struct encoder_info_packet gamut; + struct encoder_info_packet vendor; + /* source product description */ + struct encoder_info_packet spd; + /* video stream configuration */ + struct encoder_info_packet vsc; + /* HDR Static MetaData */ + struct encoder_info_packet hdrsmd; +}; + +struct encoder_unblank_param { + struct dc_link_settings link_settings; + unsigned int pixel_clk_khz; +}; + +struct encoder_set_dp_phy_pattern_param { + enum dp_test_pattern dp_phy_pattern; + const uint8_t *custom_pattern; + uint32_t custom_pattern_size; + enum dp_panel_mode dp_panel_mode; +}; + +struct stream_encoder { + const struct stream_encoder_funcs *funcs; + struct dc_context *ctx; + struct dc_bios *bp; + enum engine_id id; +}; + +struct stream_encoder_funcs { + void (*dp_set_stream_attribute)( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + enum dc_color_space output_color_space); + + void (*hdmi_set_stream_attribute)( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + int actual_pix_clk_khz, + bool enable_audio); + + void (*dvi_set_stream_attribute)( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool is_dual_link); + + void (*set_mst_bandwidth)( + struct stream_encoder *enc, + struct fixed31_32 avg_time_slots_per_mtp); + + void (*update_hdmi_info_packets)( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame); + + void (*stop_hdmi_info_packets)( + struct stream_encoder *enc); + + void (*update_dp_info_packets)( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame); + + void (*stop_dp_info_packets)( + struct stream_encoder *enc); + + void (*dp_blank)( + struct stream_encoder *enc); + + void (*dp_unblank)( + struct stream_encoder *enc, + const struct encoder_unblank_param *param); + + void (*audio_mute_control)( + struct stream_encoder *enc, bool mute); + + void (*dp_audio_setup)( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info); + + void (*dp_audio_enable) ( + struct stream_encoder *enc); + + void (*dp_audio_disable) ( + struct stream_encoder *enc); + + void (*hdmi_audio_setup)( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info); + + void (*hdmi_audio_disable) ( + struct stream_encoder *enc); + + void (*setup_stereo_sync) ( + struct stream_encoder *enc, + int tg_inst, + bool enable); + + void (*set_avmute)( + struct stream_encoder *enc, bool enable); +}; + +#endif /* STREAM_ENCODER_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h new file mode 100644 index 000000000000..c6ab38c5b2be --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h @@ -0,0 +1,183 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_TIMING_GENERATOR_TYPES_H__ +#define __DAL_TIMING_GENERATOR_TYPES_H__ + +struct dc_bios; + +/* Contains CRTC vertical/horizontal pixel counters */ +struct crtc_position { + int32_t vertical_count; + int32_t horizontal_count; + int32_t nominal_vcount; +}; + +struct dcp_gsl_params { + int gsl_group; + int gsl_master; +}; + +/* define the structure of Dynamic Refresh Mode */ +struct drr_params { + uint32_t vertical_total_min; + uint32_t vertical_total_max; + bool immediate_flip; +}; + +#define LEFT_EYE_3D_PRIMARY_SURFACE 1 +#define RIGHT_EYE_3D_PRIMARY_SURFACE 0 + +enum test_pattern_dyn_range { + TEST_PATTERN_DYN_RANGE_VESA = 0, + TEST_PATTERN_DYN_RANGE_CEA +}; + +enum test_pattern_mode { + TEST_PATTERN_MODE_COLORSQUARES_RGB = 0, + TEST_PATTERN_MODE_COLORSQUARES_YCBCR601, + TEST_PATTERN_MODE_COLORSQUARES_YCBCR709, + TEST_PATTERN_MODE_VERTICALBARS, + TEST_PATTERN_MODE_HORIZONTALBARS, + TEST_PATTERN_MODE_SINGLERAMP_RGB, + TEST_PATTERN_MODE_DUALRAMP_RGB +}; + +enum test_pattern_color_format { + TEST_PATTERN_COLOR_FORMAT_BPC_6 = 0, + TEST_PATTERN_COLOR_FORMAT_BPC_8, + TEST_PATTERN_COLOR_FORMAT_BPC_10, + TEST_PATTERN_COLOR_FORMAT_BPC_12 +}; + +enum controller_dp_test_pattern { + CONTROLLER_DP_TEST_PATTERN_D102 = 0, + CONTROLLER_DP_TEST_PATTERN_SYMBOLERROR, + CONTROLLER_DP_TEST_PATTERN_PRBS7, + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES, + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS, + CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS, + CONTROLLER_DP_TEST_PATTERN_COLORRAMP, + CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, + CONTROLLER_DP_TEST_PATTERN_RESERVED_8, + CONTROLLER_DP_TEST_PATTERN_RESERVED_9, + CONTROLLER_DP_TEST_PATTERN_RESERVED_A, + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA +}; + +enum crtc_state { + CRTC_STATE_VBLANK = 0, + CRTC_STATE_VACTIVE +}; + +struct _dlg_otg_param { + int vstartup_start; + int vupdate_offset; + int vupdate_width; + int vready_offset; + enum signal_type signal; +}; + +struct crtc_stereo_flags { + uint8_t PROGRAM_STEREO : 1; + uint8_t PROGRAM_POLARITY : 1; + uint8_t RIGHT_EYE_POLARITY : 1; + uint8_t FRAME_PACKED : 1; + uint8_t DISABLE_STEREO_DP_SYNC : 1; +}; + +struct timing_generator { + const struct timing_generator_funcs *funcs; + struct dc_bios *bp; + struct dc_context *ctx; + struct _dlg_otg_param dlg_otg_param; + int inst; +}; + +struct dc_crtc_timing; + +struct drr_params; + +struct timing_generator_funcs { + bool (*validate_timing)(struct timing_generator *tg, + const struct dc_crtc_timing *timing); + void (*program_timing)(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios); + bool (*enable_crtc)(struct timing_generator *tg); + bool (*disable_crtc)(struct timing_generator *tg); + bool (*is_counter_moving)(struct timing_generator *tg); + void (*get_position)(struct timing_generator *tg, + struct crtc_position *position); + + uint32_t (*get_frame_count)(struct timing_generator *tg); + void (*get_scanoutpos)( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position); + void (*set_early_control)(struct timing_generator *tg, + uint32_t early_cntl); + void (*wait_for_state)(struct timing_generator *tg, + enum crtc_state state); + void (*set_blank)(struct timing_generator *tg, + bool enable_blanking); + bool (*is_blanked)(struct timing_generator *tg); + void (*set_overscan_blank_color) (struct timing_generator *tg, const struct tg_color *color); + void (*set_blank_color)(struct timing_generator *tg, const struct tg_color *color); + void (*set_colors)(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color); + + void (*disable_vga)(struct timing_generator *tg); + bool (*did_triggered_reset_occur)(struct timing_generator *tg); + void (*setup_global_swap_lock)(struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params); + void (*unlock)(struct timing_generator *tg); + void (*lock)(struct timing_generator *tg); + void (*enable_reset_trigger)(struct timing_generator *tg, int source_tg_inst); + void (*disable_reset_trigger)(struct timing_generator *tg); + void (*tear_down_global_swap_lock)(struct timing_generator *tg); + void (*enable_advanced_request)(struct timing_generator *tg, + bool enable, const struct dc_crtc_timing *timing); + void (*set_drr)(struct timing_generator *tg, const struct drr_params *params); + void (*set_static_screen_control)(struct timing_generator *tg, + uint32_t value); + void (*set_test_pattern)( + struct timing_generator *tg, + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth); + + bool (*arm_vert_intr)(struct timing_generator *tg, uint8_t width); + + void (*program_global_sync)(struct timing_generator *tg); + void (*enable_optc_clock)(struct timing_generator *tg, bool enable); + void (*program_stereo)(struct timing_generator *tg, + const struct dc_crtc_timing *timing, struct crtc_stereo_flags *flags); + bool (*is_stereo_left_eye)(struct timing_generator *tg); +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/transform.h b/drivers/gpu/drm/amd/display/dc/inc/hw/transform.h new file mode 100644 index 000000000000..7c08bc62c1f5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/transform.h @@ -0,0 +1,311 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_TRANSFORM_H__ +#define __DAL_TRANSFORM_H__ + +#include "hw_shared.h" +#include "dc_hw_types.h" +#include "fixed31_32.h" + +#define CSC_TEMPERATURE_MATRIX_SIZE 9 + +struct bit_depth_reduction_params; + +struct transform { + const struct transform_funcs *funcs; + struct dc_context *ctx; + int inst; + struct dpp_caps *caps; + struct pwl_params regamma_params; +}; + +/* Colorimetry */ +enum colorimetry { + COLORIMETRY_NO_DATA = 0, + COLORIMETRY_ITU601 = 1, + COLORIMETRY_ITU709 = 2, + COLORIMETRY_EXTENDED = 3 +}; + +enum colorimetry_ext { + COLORIMETRYEX_XVYCC601 = 0, + COLORIMETRYEX_XVYCC709 = 1, + COLORIMETRYEX_SYCC601 = 2, + COLORIMETRYEX_ADOBEYCC601 = 3, + COLORIMETRYEX_ADOBERGB = 4, + COLORIMETRYEX_BT2020YCC = 5, + COLORIMETRYEX_BT2020RGBYCBCR = 6, + COLORIMETRYEX_RESERVED = 7 +}; + +enum active_format_info { + ACTIVE_FORMAT_NO_DATA = 0, + ACTIVE_FORMAT_VALID = 1 +}; + +/* Active format aspect ratio */ +enum active_format_aspect_ratio { + ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE = 8, + ACTIVE_FORMAT_ASPECT_RATIO_4_3 = 9, + ACTIVE_FORMAT_ASPECT_RATIO_16_9 = 0XA, + ACTIVE_FORMAT_ASPECT_RATIO_14_9 = 0XB +}; + +enum bar_info { + BAR_INFO_NOT_VALID = 0, + BAR_INFO_VERTICAL_VALID = 1, + BAR_INFO_HORIZONTAL_VALID = 2, + BAR_INFO_BOTH_VALID = 3 +}; + +enum picture_scaling { + PICTURE_SCALING_UNIFORM = 0, + PICTURE_SCALING_HORIZONTAL = 1, + PICTURE_SCALING_VERTICAL = 2, + PICTURE_SCALING_BOTH = 3 +}; + +/* RGB quantization range */ +enum rgb_quantization_range { + RGB_QUANTIZATION_DEFAULT_RANGE = 0, + RGB_QUANTIZATION_LIMITED_RANGE = 1, + RGB_QUANTIZATION_FULL_RANGE = 2, + RGB_QUANTIZATION_RESERVED = 3 +}; + +/* YYC quantization range */ +enum yyc_quantization_range { + YYC_QUANTIZATION_LIMITED_RANGE = 0, + YYC_QUANTIZATION_FULL_RANGE = 1, + YYC_QUANTIZATION_RESERVED2 = 2, + YYC_QUANTIZATION_RESERVED3 = 3 +}; + +enum graphics_gamut_adjust_type { + GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS = 0, + GRAPHICS_GAMUT_ADJUST_TYPE_HW, /* without adjustments */ + GRAPHICS_GAMUT_ADJUST_TYPE_SW /* use adjustments */ +}; + +enum lb_memory_config { + /* Enable all 3 pieces of memory */ + LB_MEMORY_CONFIG_0 = 0, + + /* Enable only the first piece of memory */ + LB_MEMORY_CONFIG_1 = 1, + + /* Enable only the second piece of memory */ + LB_MEMORY_CONFIG_2 = 2, + + /* Only applicable in 4:2:0 mode, enable all 3 pieces of memory and the + * last piece of chroma memory used for the luma storage + */ + LB_MEMORY_CONFIG_3 = 3 +}; + +struct xfm_grph_csc_adjustment { + struct fixed31_32 temperature_matrix[CSC_TEMPERATURE_MATRIX_SIZE]; + enum graphics_gamut_adjust_type gamut_adjust_type; +}; + +struct overscan_info { + int left; + int right; + int top; + int bottom; +}; + +struct scaling_ratios { + struct fixed31_32 horz; + struct fixed31_32 vert; + struct fixed31_32 horz_c; + struct fixed31_32 vert_c; +}; + +struct sharpness_adj { + int horz; + int vert; +}; + +struct line_buffer_params { + bool alpha_en; + bool pixel_expan_mode; + bool interleave_en; + int dynamic_pixel_depth; + enum lb_pixel_depth depth; +}; + +struct scl_inits { + struct fixed31_32 h; + struct fixed31_32 h_c; + struct fixed31_32 v; + struct fixed31_32 v_bot; + struct fixed31_32 v_c; + struct fixed31_32 v_c_bot; +}; + +struct scaler_data { + int h_active; + int v_active; + struct scaling_taps taps; + struct rect viewport; + struct rect viewport_c; + struct rect recout; + struct scaling_ratios ratios; + struct scl_inits inits; + struct sharpness_adj sharpness; + enum pixel_format format; + struct line_buffer_params lb_params; +}; + +struct transform_funcs { + void (*transform_reset)(struct transform *xfm); + + void (*transform_set_scaler)(struct transform *xfm, + const struct scaler_data *scl_data); + + void (*transform_set_pixel_storage_depth)( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params); + + bool (*transform_get_optimal_number_of_taps)( + struct transform *xfm, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps); + + void (*transform_set_gamut_remap)( + struct transform *xfm, + const struct xfm_grph_csc_adjustment *adjust); + + void (*opp_set_csc_default)( + struct transform *xfm, + const struct default_adjustment *default_adjust); + + void (*opp_set_csc_adjustment)( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry); + + void (*opp_power_on_regamma_lut)( + struct transform *xfm, + bool power_on); + + void (*opp_program_regamma_lut)( + struct transform *xfm, + const struct pwl_result_data *rgb, + uint32_t num); + + void (*opp_configure_regamma_lut)( + struct transform *xfm, + bool is_ram_a); + + void (*opp_program_regamma_lutb_settings)( + struct transform *xfm, + const struct pwl_params *params); + + void (*opp_program_regamma_luta_settings)( + struct transform *xfm, + const struct pwl_params *params); + + void (*opp_program_regamma_pwl)( + struct transform *xfm, const struct pwl_params *params); + + void (*opp_set_regamma_mode)( + struct transform *xfm_base, + enum opp_regamma mode); + + void (*ipp_set_degamma)( + struct transform *xfm_base, + enum ipp_degamma_mode mode); + + void (*ipp_program_input_lut)( + struct transform *xfm_base, + const struct dc_gamma *gamma); + + void (*ipp_program_degamma_pwl)(struct transform *xfm_base, + const struct pwl_params *params); + + void (*ipp_setup)( + struct transform *xfm_base, + enum surface_pixel_format input_format, + enum expansion_mode mode); + + void (*ipp_full_bypass)(struct transform *xfm_base); + + void (*set_cursor_attributes)( + struct transform *xfm_base, + const struct dc_cursor_attributes *attr); + + void (*set_cursor_position)( + struct transform *xfm_base, + const struct dc_cursor_position *pos, + const struct dc_cursor_mi_param *param, + uint32_t width + ); + +}; + +const uint16_t *get_filter_2tap_16p(void); +const uint16_t *get_filter_2tap_64p(void); +const uint16_t *get_filter_3tap_16p(struct fixed31_32 ratio); +const uint16_t *get_filter_3tap_64p(struct fixed31_32 ratio); +const uint16_t *get_filter_4tap_16p(struct fixed31_32 ratio); +const uint16_t *get_filter_4tap_64p(struct fixed31_32 ratio); +const uint16_t *get_filter_5tap_64p(struct fixed31_32 ratio); +const uint16_t *get_filter_6tap_64p(struct fixed31_32 ratio); +const uint16_t *get_filter_7tap_64p(struct fixed31_32 ratio); +const uint16_t *get_filter_8tap_64p(struct fixed31_32 ratio); + + +/* Defines the pixel processing capability of the DSCL */ +enum dscl_data_processing_format { + DSCL_DATA_PRCESSING_FIXED_FORMAT, /* The DSCL processes pixel data in fixed format */ + DSCL_DATA_PRCESSING_FLOAT_FORMAT, /* The DSCL processes pixel data in float format */ +}; + +/* + * The DPP capabilities structure contains enumerations to specify the + * HW processing features and an associated function pointers to + * provide the function interface that can be overloaded for implementations + * based on different capabilities + */ +struct dpp_caps { + /* DSCL processing pixel data in fixed or float format */ + enum dscl_data_processing_format dscl_data_proc_format; + + /* Calculates the number of partitions in the line buffer. + * The implementation of this function is overloaded for + * different versions of DSCL LB. + */ + void (*dscl_calc_lb_num_partitions)( + const struct scaler_data *scl_data, + enum lb_memory_config lb_config, + int *num_part_y, + int *num_part_c); +}; + + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h new file mode 100644 index 000000000000..8734689a9245 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h @@ -0,0 +1,197 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HW_SEQUENCER_H__ +#define __DC_HW_SEQUENCER_H__ +#include "dc_types.h" +#include "clock_source.h" +#include "inc/hw/timing_generator.h" +#include "inc/hw/link_encoder.h" +#include "core_status.h" + +enum pipe_gating_control { + PIPE_GATING_CONTROL_DISABLE = 0, + PIPE_GATING_CONTROL_ENABLE, + PIPE_GATING_CONTROL_INIT +}; + +struct dce_hwseq_wa { + bool blnd_crtc_trigger; +}; + +struct dce_hwseq { + struct dc_context *ctx; + const struct dce_hwseq_registers *regs; + const struct dce_hwseq_shift *shifts; + const struct dce_hwseq_mask *masks; + struct dce_hwseq_wa wa; +}; + +struct pipe_ctx; +struct dc_state; +struct dchub_init_data; +struct dc_static_screen_events; +struct resource_pool; +struct resource_context; + +struct hw_sequencer_funcs { + + void (*init_hw)(struct dc *dc); + + enum dc_status (*apply_ctx_to_hw)( + struct dc *dc, struct dc_state *context); + + void (*reset_hw_ctx_wrap)( + struct dc *dc, struct dc_state *context); + + void (*apply_ctx_for_surface)( + struct dc *dc, + const struct dc_stream_state *stream, + int num_planes, + struct dc_state *context); + + void (*set_plane_config)( + const struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct resource_context *res_ctx); + + void (*program_gamut_remap)( + struct pipe_ctx *pipe_ctx); + + void (*program_csc_matrix)( + struct pipe_ctx *pipe_ctx, + enum dc_color_space colorspace, + uint16_t *matrix); + + void (*update_plane_addr)( + const struct dc *dc, + struct pipe_ctx *pipe_ctx); + + void (*update_dchub)( + struct dce_hwseq *hws, + struct dchub_init_data *dh_data); + + void (*update_pending_status)( + struct pipe_ctx *pipe_ctx); + + bool (*set_input_transfer_func)( + struct pipe_ctx *pipe_ctx, + const struct dc_plane_state *plane_state); + + bool (*set_output_transfer_func)( + struct pipe_ctx *pipe_ctx, + const struct dc_stream_state *stream); + + void (*power_down)(struct dc *dc); + + void (*enable_accelerated_mode)(struct dc *dc); + + void (*enable_timing_synchronization)( + struct dc *dc, + int group_index, + int group_size, + struct pipe_ctx *grouped_pipes[]); + + void (*enable_display_pipe_clock_gating)( + struct dc_context *ctx, + bool clock_gating); + + bool (*enable_display_power_gating)( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating); + + void (*power_down_front_end)(struct dc *dc, int fe_idx); + + void (*power_on_front_end)(struct dc *dc, + struct pipe_ctx *pipe, + struct dc_state *context); + + void (*update_info_frame)(struct pipe_ctx *pipe_ctx); + + void (*enable_stream)(struct pipe_ctx *pipe_ctx); + + void (*disable_stream)(struct pipe_ctx *pipe_ctx, + int option); + + void (*unblank_stream)(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings); + + void (*pipe_control_lock)( + struct dc *dc, + struct pipe_ctx *pipe, + bool lock); + + void (*set_bandwidth)( + struct dc *dc, + struct dc_state *context, + bool decrease_allowed); + + void (*set_drr)(struct pipe_ctx **pipe_ctx, int num_pipes, + int vmin, int vmax); + + void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes, + struct crtc_position *position); + + void (*set_static_screen_control)(struct pipe_ctx **pipe_ctx, + int num_pipes, const struct dc_static_screen_events *events); + + enum dc_status (*prog_pixclk_crtc_otg)( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc); + + void (*setup_stereo)( + struct pipe_ctx *pipe_ctx, + struct dc *dc); + + void (*set_avmute)(struct pipe_ctx *pipe_ctx, bool enable); + + void (*log_hw_state)(struct dc *dc); + + void (*wait_for_mpcc_disconnect)(struct dc *dc, + struct resource_pool *res_pool, + struct pipe_ctx *pipe_ctx); + + void (*ready_shared_resources)(struct dc *dc, struct dc_state *context); + void (*optimize_shared_resources)(struct dc *dc); + void (*edp_power_control)( + struct link_encoder *enc, + bool enable); + void (*edp_backlight_control)( + struct dc_link *link, + bool enable); +}; + +void color_space_to_black_color( + const struct dc *dc, + enum dc_color_space colorspace, + struct tg_color *black_color); + +bool hwss_wait_for_blank_complete( + struct timing_generator *tg); + +#endif /* __DC_HW_SEQUENCER_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_hwss.h b/drivers/gpu/drm/amd/display/dc/inc/link_hwss.h new file mode 100644 index 000000000000..f2b8c9a376d5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/link_hwss.h @@ -0,0 +1,79 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_LINK_HWSS_H__ +#define __DC_LINK_HWSS_H__ + +#include "inc/core_status.h" + +enum dc_status core_link_read_dpcd( + struct dc_link *link, + uint32_t address, + uint8_t *data, + uint32_t size); + +enum dc_status core_link_write_dpcd( + struct dc_link *link, + uint32_t address, + const uint8_t *data, + uint32_t size); + +struct gpio *get_hpd_gpio(struct dc_bios *dcb, + struct graphics_object_id link_id, + struct gpio_service *gpio_service); + +void dp_enable_link_phy( + struct dc_link *link, + enum signal_type signal, + enum clock_source_id clock_source, + const struct dc_link_settings *link_settings); + +void dp_receiver_power_ctrl(struct dc_link *link, bool on); + +void dp_disable_link_phy(struct dc_link *link, enum signal_type signal); + +void dp_disable_link_phy_mst(struct dc_link *link, enum signal_type signal); + +bool dp_set_hw_training_pattern( + struct dc_link *link, + enum hw_dp_training_pattern pattern); + +void dp_set_hw_lane_settings( + struct dc_link *link, + const struct link_training_settings *link_settings); + +void dp_set_hw_test_pattern( + struct dc_link *link, + enum dp_test_pattern test_pattern, + uint8_t *custom_pattern, + uint32_t custom_pattern_size); + +enum dp_panel_mode dp_get_panel_mode(struct dc_link *link); + +void dp_retrain_link_dp_test(struct dc_link *link, + struct dc_link_settings *link_setting, + bool skip_video_pattern); + +#endif /* __DC_LINK_HWSS_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h new file mode 100644 index 000000000000..77eb72874e90 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h @@ -0,0 +1,392 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + */ + +#ifndef DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_REG_HELPER_H_ +#define DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_REG_HELPER_H_ + +#include "dm_services.h" + +/* macro for register read/write + * user of macro need to define + * + * CTX ==> macro to ptr to dc_context + * eg. aud110->base.ctx + * + * REG ==> macro to location of register offset + * eg. aud110->regs->reg + */ +#define REG_READ(reg_name) \ + dm_read_reg(CTX, REG(reg_name)) + +#define REG_WRITE(reg_name, value) \ + dm_write_reg(CTX, REG(reg_name), value) + +#ifdef REG_SET +#undef REG_SET +#endif + +#ifdef REG_GET +#undef REG_GET +#endif + +/* macro to set register fields. */ +#define REG_SET_N(reg_name, n, initial_val, ...) \ + generic_reg_update_ex(CTX, \ + REG(reg_name), \ + initial_val, \ + n, __VA_ARGS__) + +#define FN(reg_name, field) \ + FD(reg_name##__##field) + +#define REG_SET(reg_name, initial_val, field, val) \ + REG_SET_N(reg_name, 1, initial_val, \ + FN(reg_name, field), val) + +#define REG_SET_2(reg, init_value, f1, v1, f2, v2) \ + REG_SET_N(reg, 2, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2) + +#define REG_SET_3(reg, init_value, f1, v1, f2, v2, f3, v3) \ + REG_SET_N(reg, 3, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2,\ + FN(reg, f3), v3) + +#define REG_SET_4(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4) \ + REG_SET_N(reg, 4, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2,\ + FN(reg, f3), v3,\ + FN(reg, f4), v4) + +#define REG_SET_5(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4, \ + f5, v5) \ + REG_SET_N(reg, 5, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2,\ + FN(reg, f3), v3,\ + FN(reg, f4), v4,\ + FN(reg, f5), v5) + +#define REG_SET_6(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4, \ + f5, v5, f6, v6) \ + REG_SET_N(reg, 6, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2,\ + FN(reg, f3), v3,\ + FN(reg, f4), v4,\ + FN(reg, f5), v5,\ + FN(reg, f6), v6) + +#define REG_SET_7(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4, \ + f5, v5, f6, v6, f7, v7) \ + REG_SET_N(reg, 7, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2,\ + FN(reg, f3), v3,\ + FN(reg, f4), v4,\ + FN(reg, f5), v5,\ + FN(reg, f6), v6,\ + FN(reg, f7), v7) + +#define REG_SET_8(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4, \ + f5, v5, f6, v6, f7, v7, f8, v8) \ + REG_SET_N(reg, 8, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2,\ + FN(reg, f3), v3,\ + FN(reg, f4), v4,\ + FN(reg, f5), v5,\ + FN(reg, f6), v6,\ + FN(reg, f7), v7,\ + FN(reg, f8), v8) + +#define REG_SET_9(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4, f5, \ + v5, f6, v6, f7, v7, f8, v8, f9, v9) \ + REG_SET_N(reg, 9, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8, \ + FN(reg, f9), v9) + +#define REG_SET_10(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4, f5, \ + v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10) \ + REG_SET_N(reg, 10, init_value, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8, \ + FN(reg, f9), v9, \ + FN(reg, f10), v10) + +/* macro to get register fields + * read given register and fill in field value in output parameter */ +#define REG_GET(reg_name, field, val) \ + generic_reg_get(CTX, REG(reg_name), \ + FN(reg_name, field), val) + +#define REG_GET_2(reg_name, f1, v1, f2, v2) \ + generic_reg_get2(CTX, REG(reg_name), \ + FN(reg_name, f1), v1, \ + FN(reg_name, f2), v2) + +#define REG_GET_3(reg_name, f1, v1, f2, v2, f3, v3) \ + generic_reg_get3(CTX, REG(reg_name), \ + FN(reg_name, f1), v1, \ + FN(reg_name, f2), v2, \ + FN(reg_name, f3), v3) + +#define REG_GET_4(reg_name, f1, v1, f2, v2, f3, v3, f4, v4) \ + generic_reg_get4(CTX, REG(reg_name), \ + FN(reg_name, f1), v1, \ + FN(reg_name, f2), v2, \ + FN(reg_name, f3), v3, \ + FN(reg_name, f4), v4) + +#define REG_GET_5(reg_name, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5) \ + generic_reg_get5(CTX, REG(reg_name), \ + FN(reg_name, f1), v1, \ + FN(reg_name, f2), v2, \ + FN(reg_name, f3), v3, \ + FN(reg_name, f4), v4, \ + FN(reg_name, f5), v5) + +/* macro to poll and wait for a register field to read back given value */ + +#define REG_WAIT(reg_name, field, val, delay_between_poll_us, max_try) \ + generic_reg_wait(CTX, \ + REG(reg_name), FN(reg_name, field), val,\ + delay_between_poll_us, max_try, __func__, __LINE__) + +/* macro to update (read, modify, write) register fields + */ +#define REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_ex(CTX, \ + REG(reg_name), \ + REG_READ(reg_name), \ + n, __VA_ARGS__) + +#define REG_UPDATE(reg_name, field, val) \ + REG_UPDATE_N(reg_name, 1, \ + FN(reg_name, field), val) + +#define REG_UPDATE_2(reg, f1, v1, f2, v2) \ + REG_UPDATE_N(reg, 2,\ + FN(reg, f1), v1,\ + FN(reg, f2), v2) + +#define REG_UPDATE_3(reg, f1, v1, f2, v2, f3, v3) \ + REG_UPDATE_N(reg, 3, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3) + +#define REG_UPDATE_4(reg, f1, v1, f2, v2, f3, v3, f4, v4) \ + REG_UPDATE_N(reg, 4, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4) + +#define REG_UPDATE_5(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5) \ + REG_UPDATE_N(reg, 5, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5) + +#define REG_UPDATE_6(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6) \ + REG_UPDATE_N(reg, 6, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6) + +#define REG_UPDATE_7(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7) \ + REG_UPDATE_N(reg, 7, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7) + +#define REG_UPDATE_8(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8) \ + REG_UPDATE_N(reg, 8, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8) + +#define REG_UPDATE_9(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9) \ + REG_UPDATE_N(reg, 9, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8, \ + FN(reg, f9), v9) + +#define REG_UPDATE_10(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10)\ + REG_UPDATE_N(reg, 10, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8, \ + FN(reg, f9), v9, \ + FN(reg, f10), v10) + +#define REG_UPDATE_14(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10,\ + v10, f11, v11, f12, v12, f13, v13, f14, v14)\ + REG_UPDATE_N(reg, 14, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8, \ + FN(reg, f9), v9, \ + FN(reg, f10), v10, \ + FN(reg, f11), v11, \ + FN(reg, f12), v12, \ + FN(reg, f13), v13, \ + FN(reg, f14), v14) + +#define REG_UPDATE_19(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10,\ + v10, f11, v11, f12, v12, f13, v13, f14, v14, f15, v15, f16, v16, f17, v17, f18, v18, f19, v19)\ + REG_UPDATE_N(reg, 19, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8, \ + FN(reg, f9), v9, \ + FN(reg, f10), v10, \ + FN(reg, f11), v11, \ + FN(reg, f12), v12, \ + FN(reg, f13), v13, \ + FN(reg, f14), v14, \ + FN(reg, f15), v15, \ + FN(reg, f16), v16, \ + FN(reg, f17), v17, \ + FN(reg, f18), v18, \ + FN(reg, f19), v19) + +#define REG_UPDATE_20(reg, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10,\ + v10, f11, v11, f12, v12, f13, v13, f14, v14, f15, v15, f16, v16, f17, v17, f18, v18, f19, v19, f20, v20)\ + REG_UPDATE_N(reg, 20, \ + FN(reg, f1), v1,\ + FN(reg, f2), v2, \ + FN(reg, f3), v3, \ + FN(reg, f4), v4, \ + FN(reg, f5), v5, \ + FN(reg, f6), v6, \ + FN(reg, f7), v7, \ + FN(reg, f8), v8, \ + FN(reg, f9), v9, \ + FN(reg, f10), v10, \ + FN(reg, f11), v11, \ + FN(reg, f12), v12, \ + FN(reg, f13), v13, \ + FN(reg, f14), v14, \ + FN(reg, f15), v15, \ + FN(reg, f16), v16, \ + FN(reg, f17), v17, \ + FN(reg, f18), v18, \ + FN(reg, f19), v19, \ + FN(reg, f20), v20) +/* macro to update a register field to specified values in given sequences. + * useful when toggling bits + */ +#define REG_UPDATE_SEQ(reg, field, value1, value2) \ +{ uint32_t val = REG_UPDATE(reg, field, value1); \ + REG_SET(reg, val, field, value2); } + +/* macro to update fields in register 1 field at a time in given order */ +#define REG_UPDATE_1BY1_2(reg, f1, v1, f2, v2) \ +{ uint32_t val = REG_UPDATE(reg, f1, v1); \ + REG_SET(reg, val, f2, v2); } + +#define REG_UPDATE_1BY1_3(reg, f1, v1, f2, v2, f3, v3) \ +{ uint32_t val = REG_UPDATE(reg, f1, v1); \ + val = REG_SET(reg, val, f2, v2); \ + REG_SET(reg, val, f3, v3); } + +uint32_t generic_reg_get(const struct dc_context *ctx, uint32_t addr, + uint8_t shift, uint32_t mask, uint32_t *field_value); + +uint32_t generic_reg_get2(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2); + +uint32_t generic_reg_get3(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2, + uint8_t shift3, uint32_t mask3, uint32_t *field_value3); + +uint32_t generic_reg_get4(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2, + uint8_t shift3, uint32_t mask3, uint32_t *field_value3, + uint8_t shift4, uint32_t mask4, uint32_t *field_value4); + +uint32_t generic_reg_get5(const struct dc_context *ctx, uint32_t addr, + uint8_t shift1, uint32_t mask1, uint32_t *field_value1, + uint8_t shift2, uint32_t mask2, uint32_t *field_value2, + uint8_t shift3, uint32_t mask3, uint32_t *field_value3, + uint8_t shift4, uint32_t mask4, uint32_t *field_value4, + uint8_t shift5, uint32_t mask5, uint32_t *field_value5); + +#endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_REG_HELPER_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h new file mode 100644 index 000000000000..5467332faf7b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -0,0 +1,172 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + */ + +#ifndef DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_RESOURCE_H_ +#define DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_RESOURCE_H_ + +#include "core_types.h" +#include "core_status.h" +#include "dal_asic_id.h" +#include "dm_pp_smu.h" + +/* TODO unhardcode, 4 for CZ*/ +#define MEMORY_TYPE_MULTIPLIER 4 + +enum dce_version resource_parse_asic_id( + struct hw_asic_id asic_id); + +struct resource_caps { + int num_timing_generator; + int num_video_plane; + int num_audio; + int num_stream_encoder; + int num_pll; + int num_dwb; +}; + +struct resource_straps { + uint32_t hdmi_disable; + uint32_t dc_pinstraps_audio; + uint32_t audio_stream_number; +}; + +struct resource_create_funcs { + void (*read_dce_straps)( + struct dc_context *ctx, struct resource_straps *straps); + + struct audio *(*create_audio)( + struct dc_context *ctx, unsigned int inst); + + struct stream_encoder *(*create_stream_encoder)( + enum engine_id eng_id, struct dc_context *ctx); + + struct dce_hwseq *(*create_hwseq)( + struct dc_context *ctx); +}; + +bool resource_construct( + unsigned int num_virtual_links, + struct dc *dc, + struct resource_pool *pool, + const struct resource_create_funcs *create_funcs); + +struct resource_pool *dc_create_resource_pool( + struct dc *dc, + int num_virtual_links, + enum dce_version dc_version, + struct hw_asic_id asic_id); + +void dc_destroy_resource_pool(struct dc *dc); + +enum dc_status resource_map_pool_resources( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream); + +bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx); + +enum dc_status resource_build_scaling_params_for_context( + const struct dc *dc, + struct dc_state *context); + +void resource_build_info_frame(struct pipe_ctx *pipe_ctx); + +void resource_unreference_clock_source( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct clock_source *clock_source); + +void resource_reference_clock_source( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct clock_source *clock_source); + +bool resource_are_streams_timing_synchronizable( + struct dc_stream_state *stream1, + struct dc_stream_state *stream2); + +struct clock_source *resource_find_used_clk_src_for_sharing( + struct resource_context *res_ctx, + struct pipe_ctx *pipe_ctx); + +struct clock_source *dc_resource_find_first_free_pll( + struct resource_context *res_ctx, + const struct resource_pool *pool); + +struct pipe_ctx *resource_get_head_pipe_for_stream( + struct resource_context *res_ctx, + struct dc_stream_state *stream); + +bool resource_attach_surfaces_to_context( + struct dc_plane_state *const *plane_state, + int surface_count, + struct dc_stream_state *dc_stream, + struct dc_state *context, + const struct resource_pool *pool); + +struct pipe_ctx *find_idle_secondary_pipe( + struct resource_context *res_ctx, + const struct resource_pool *pool); + +bool resource_is_stream_unchanged( + struct dc_state *old_context, struct dc_stream_state *stream); + +bool resource_validate_attach_surfaces( + const struct dc_validation_set set[], + int set_count, + const struct dc_state *old_context, + struct dc_state *context, + const struct resource_pool *pool); + +void validate_guaranteed_copy_streams( + struct dc_state *context, + int max_streams); + +void resource_validate_ctx_update_pointer_after_copy( + const struct dc_state *src_ctx, + struct dc_state *dst_ctx); + +enum dc_status resource_map_clock_resources( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream); + +enum dc_status resource_map_phy_clock_resources( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream); + +bool pipe_need_reprogram( + struct pipe_ctx *pipe_ctx_old, + struct pipe_ctx *pipe_ctx); + +void resource_build_bit_depth_reduction_params(struct dc_stream_state *stream, + struct bit_depth_reduction_params *fmt_bit_depth); + +void update_audio_usage( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct audio *audio, + bool acquired); +#endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_RESOURCE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/irq/Makefile b/drivers/gpu/drm/amd/display/dc/irq/Makefile new file mode 100644 index 000000000000..c7e93f7223bd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/Makefile @@ -0,0 +1,48 @@ +# +# Makefile for the 'audio' sub-component of DAL. +# It provides the control and status of HW adapter resources, +# that are global for the ASIC and sharable between pipes. + +IRQ = irq_service.o + +AMD_DAL_IRQ = $(addprefix $(AMDDALPATH)/dc/irq/,$(IRQ)) + +AMD_DISPLAY_FILES += $(AMD_DAL_IRQ) + +############################################################################### +# DCE 8x +############################################################################### +IRQ_DCE80 = irq_service_dce80.o + +AMD_DAL_IRQ_DCE80 = $(addprefix $(AMDDALPATH)/dc/irq/dce80/,$(IRQ_DCE80)) + +AMD_DISPLAY_FILES += $(AMD_DAL_IRQ_DCE80) + +############################################################################### +# DCE 11x +############################################################################### +IRQ_DCE11 = irq_service_dce110.o + +AMD_DAL_IRQ_DCE11 = $(addprefix $(AMDDALPATH)/dc/irq/dce110/,$(IRQ_DCE11)) + +AMD_DISPLAY_FILES += $(AMD_DAL_IRQ_DCE11) + +############################################################################### +# DCE 12x +############################################################################### +IRQ_DCE12 = irq_service_dce120.o + +AMD_DAL_IRQ_DCE12 = $(addprefix $(AMDDALPATH)/dc/irq/dce120/,$(IRQ_DCE12)) + +AMD_DISPLAY_FILES += $(AMD_DAL_IRQ_DCE12) + +############################################################################### +# DCN 1x +############################################################################### +ifdef CONFIG_DRM_AMD_DC_DCN1_0 +IRQ_DCN1 = irq_service_dcn10.o + +AMD_DAL_IRQ_DCN1 = $(addprefix $(AMDDALPATH)/dc/irq/dcn10/,$(IRQ_DCN1)) + +AMD_DISPLAY_FILES += $(AMD_DAL_IRQ_DCN1) +endif diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c new file mode 100644 index 000000000000..f7e40b292dfb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c @@ -0,0 +1,430 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/logger_interface.h" + +#include "irq_service_dce110.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "ivsrcid/ivsrcid_vislands30.h" + +#include "dc.h" +#include "core_types.h" +static bool hpd_ack( + struct irq_service *irq_service, + const struct irq_source_info *info) +{ + uint32_t addr = info->status_reg; + uint32_t value = dm_read_reg(irq_service->ctx, addr); + uint32_t current_status = + get_reg_field_value( + value, + DC_HPD_INT_STATUS, + DC_HPD_SENSE_DELAYED); + + dal_irq_service_ack_generic(irq_service, info); + + value = dm_read_reg(irq_service->ctx, info->enable_reg); + + set_reg_field_value( + value, + current_status ? 0 : 1, + DC_HPD_INT_CONTROL, + DC_HPD_INT_POLARITY); + + dm_write_reg(irq_service->ctx, info->enable_reg, value); + + return true; +} + +static const struct irq_source_info_funcs hpd_irq_info_funcs = { + .set = NULL, + .ack = hpd_ack +}; + +static const struct irq_source_info_funcs hpd_rx_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs pflip_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs vblank_irq_info_funcs = { + .set = dce110_vblank_set, + .ack = NULL +}; + +#define hpd_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1 + reg_num] = {\ + .enable_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ + .enable_mask = DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK,\ + .enable_value = {\ + DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK,\ + ~DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK\ + },\ + .ack_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ + .ack_mask = DC_HPD_INT_CONTROL__DC_HPD_INT_ACK_MASK,\ + .ack_value = DC_HPD_INT_CONTROL__DC_HPD_INT_ACK_MASK,\ + .status_reg = mmHPD ## reg_num ## _DC_HPD_INT_STATUS,\ + .funcs = &hpd_irq_info_funcs\ + } + +#define hpd_rx_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1RX + reg_num] = {\ + .enable_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ + .enable_mask = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK,\ + .enable_value = {\ + DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK,\ + ~DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK },\ + .ack_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ + .ack_mask = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_ACK_MASK,\ + .ack_value = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_ACK_MASK,\ + .status_reg = mmHPD ## reg_num ## _DC_HPD_INT_STATUS,\ + .funcs = &hpd_rx_irq_info_funcs\ + } +#define pflip_int_entry(reg_num)\ + [DC_IRQ_SOURCE_PFLIP1 + reg_num] = {\ + .enable_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_CONTROL,\ + .enable_mask =\ + GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ + .enable_value = {\ + GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ + ~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK},\ + .ack_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_STATUS,\ + .ack_mask = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ + .ack_value = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ + .status_reg = mmDCP ## reg_num ##_GRPH_INTERRUPT_STATUS,\ + .funcs = &pflip_irq_info_funcs\ + } + +#define vupdate_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VUPDATE1 + reg_num] = {\ + .enable_reg = mmCRTC ## reg_num ## _CRTC_INTERRUPT_CONTROL,\ + .enable_mask =\ + CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ + .enable_value = {\ + CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ + ~CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK},\ + .ack_reg = mmCRTC ## reg_num ## _CRTC_V_UPDATE_INT_STATUS,\ + .ack_mask =\ + CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ + .ack_value =\ + CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ + .funcs = &vblank_irq_info_funcs\ + } + +#define vblank_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VBLANK1 + reg_num] = {\ + .enable_reg = mmCRTC ## reg_num ## _CRTC_VERTICAL_INTERRUPT0_CONTROL,\ + .enable_mask =\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ + .enable_value = {\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ + ~CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK},\ + .ack_reg = mmCRTC ## reg_num ## _CRTC_VERTICAL_INTERRUPT0_CONTROL,\ + .ack_mask =\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_CLEAR_MASK,\ + .ack_value =\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_CLEAR_MASK,\ + .funcs = &vblank_irq_info_funcs,\ + .src_id = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 + reg_num\ + } + +#define dummy_irq_entry() \ + {\ + .funcs = &dummy_irq_info_funcs\ + } + +#define i2c_int_entry(reg_num) \ + [DC_IRQ_SOURCE_I2C_DDC ## reg_num] = dummy_irq_entry() + +#define dp_sink_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DPSINK ## reg_num] = dummy_irq_entry() + +#define gpio_pad_int_entry(reg_num) \ + [DC_IRQ_SOURCE_GPIOPAD ## reg_num] = dummy_irq_entry() + +#define dc_underflow_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DC ## reg_num ## UNDERFLOW] = dummy_irq_entry() + +bool dal_irq_service_dummy_set( + struct irq_service *irq_service, + const struct irq_source_info *info, + bool enable) +{ + dm_logger_write( + irq_service->ctx->logger, LOG_ERROR, + "%s: called for non-implemented irq source\n", + __func__); + return false; +} + +bool dal_irq_service_dummy_ack( + struct irq_service *irq_service, + const struct irq_source_info *info) +{ + dm_logger_write( + irq_service->ctx->logger, LOG_ERROR, + "%s: called for non-implemented irq source\n", + __func__); + return false; +} + + +bool dce110_vblank_set( + struct irq_service *irq_service, + const struct irq_source_info *info, + bool enable) +{ + struct dc_context *dc_ctx = irq_service->ctx; + struct dc *core_dc = irq_service->ctx->dc; + enum dc_irq_source dal_irq_src = dc_interrupt_to_irq_source( + irq_service->ctx->dc, + info->src_id, + info->ext_id); + uint8_t pipe_offset = dal_irq_src - IRQ_TYPE_VBLANK; + + struct timing_generator *tg = + core_dc->current_state->res_ctx.pipe_ctx[pipe_offset].stream_res.tg; + + if (enable) { + if (!tg->funcs->arm_vert_intr(tg, 2)) { + DC_ERROR("Failed to get VBLANK!\n"); + return false; + } + } + + dal_irq_service_set_generic(irq_service, info, enable); + return true; + +} + +static const struct irq_source_info_funcs dummy_irq_info_funcs = { + .set = dal_irq_service_dummy_set, + .ack = dal_irq_service_dummy_ack +}; + +static const struct irq_source_info +irq_source_info_dce110[DAL_IRQ_SOURCES_NUMBER] = { + [DC_IRQ_SOURCE_INVALID] = dummy_irq_entry(), + hpd_int_entry(0), + hpd_int_entry(1), + hpd_int_entry(2), + hpd_int_entry(3), + hpd_int_entry(4), + hpd_int_entry(5), + hpd_rx_int_entry(0), + hpd_rx_int_entry(1), + hpd_rx_int_entry(2), + hpd_rx_int_entry(3), + hpd_rx_int_entry(4), + hpd_rx_int_entry(5), + i2c_int_entry(1), + i2c_int_entry(2), + i2c_int_entry(3), + i2c_int_entry(4), + i2c_int_entry(5), + i2c_int_entry(6), + dp_sink_int_entry(1), + dp_sink_int_entry(2), + dp_sink_int_entry(3), + dp_sink_int_entry(4), + dp_sink_int_entry(5), + dp_sink_int_entry(6), + [DC_IRQ_SOURCE_TIMER] = dummy_irq_entry(), + pflip_int_entry(0), + pflip_int_entry(1), + pflip_int_entry(2), + pflip_int_entry(3), + pflip_int_entry(4), + pflip_int_entry(5), + [DC_IRQ_SOURCE_PFLIP_UNDERLAY0] = dummy_irq_entry(), + gpio_pad_int_entry(0), + gpio_pad_int_entry(1), + gpio_pad_int_entry(2), + gpio_pad_int_entry(3), + gpio_pad_int_entry(4), + gpio_pad_int_entry(5), + gpio_pad_int_entry(6), + gpio_pad_int_entry(7), + gpio_pad_int_entry(8), + gpio_pad_int_entry(9), + gpio_pad_int_entry(10), + gpio_pad_int_entry(11), + gpio_pad_int_entry(12), + gpio_pad_int_entry(13), + gpio_pad_int_entry(14), + gpio_pad_int_entry(15), + gpio_pad_int_entry(16), + gpio_pad_int_entry(17), + gpio_pad_int_entry(18), + gpio_pad_int_entry(19), + gpio_pad_int_entry(20), + gpio_pad_int_entry(21), + gpio_pad_int_entry(22), + gpio_pad_int_entry(23), + gpio_pad_int_entry(24), + gpio_pad_int_entry(25), + gpio_pad_int_entry(26), + gpio_pad_int_entry(27), + gpio_pad_int_entry(28), + gpio_pad_int_entry(29), + gpio_pad_int_entry(30), + dc_underflow_int_entry(1), + dc_underflow_int_entry(2), + dc_underflow_int_entry(3), + dc_underflow_int_entry(4), + dc_underflow_int_entry(5), + dc_underflow_int_entry(6), + [DC_IRQ_SOURCE_DMCU_SCP] = dummy_irq_entry(), + [DC_IRQ_SOURCE_VBIOS_SW] = dummy_irq_entry(), + vupdate_int_entry(0), + vupdate_int_entry(1), + vupdate_int_entry(2), + vupdate_int_entry(3), + vupdate_int_entry(4), + vupdate_int_entry(5), + vblank_int_entry(0), + vblank_int_entry(1), + vblank_int_entry(2), + vblank_int_entry(3), + vblank_int_entry(4), + vblank_int_entry(5), + +}; + +enum dc_irq_source to_dal_irq_source_dce110( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id) +{ + switch (src_id) { + case VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0: + return DC_IRQ_SOURCE_VBLANK1; + case VISLANDS30_IV_SRCID_D2_VERTICAL_INTERRUPT0: + return DC_IRQ_SOURCE_VBLANK2; + case VISLANDS30_IV_SRCID_D3_VERTICAL_INTERRUPT0: + return DC_IRQ_SOURCE_VBLANK3; + case VISLANDS30_IV_SRCID_D4_VERTICAL_INTERRUPT0: + return DC_IRQ_SOURCE_VBLANK4; + case VISLANDS30_IV_SRCID_D5_VERTICAL_INTERRUPT0: + return DC_IRQ_SOURCE_VBLANK5; + case VISLANDS30_IV_SRCID_D6_VERTICAL_INTERRUPT0: + return DC_IRQ_SOURCE_VBLANK6; + case VISLANDS30_IV_SRCID_D1_V_UPDATE_INT: + return DC_IRQ_SOURCE_VUPDATE1; + case VISLANDS30_IV_SRCID_D2_V_UPDATE_INT: + return DC_IRQ_SOURCE_VUPDATE2; + case VISLANDS30_IV_SRCID_D3_V_UPDATE_INT: + return DC_IRQ_SOURCE_VUPDATE3; + case VISLANDS30_IV_SRCID_D4_V_UPDATE_INT: + return DC_IRQ_SOURCE_VUPDATE4; + case VISLANDS30_IV_SRCID_D5_V_UPDATE_INT: + return DC_IRQ_SOURCE_VUPDATE5; + case VISLANDS30_IV_SRCID_D6_V_UPDATE_INT: + return DC_IRQ_SOURCE_VUPDATE6; + case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP: + return DC_IRQ_SOURCE_PFLIP1; + case VISLANDS30_IV_SRCID_D2_GRPH_PFLIP: + return DC_IRQ_SOURCE_PFLIP2; + case VISLANDS30_IV_SRCID_D3_GRPH_PFLIP: + return DC_IRQ_SOURCE_PFLIP3; + case VISLANDS30_IV_SRCID_D4_GRPH_PFLIP: + return DC_IRQ_SOURCE_PFLIP4; + case VISLANDS30_IV_SRCID_D5_GRPH_PFLIP: + return DC_IRQ_SOURCE_PFLIP5; + case VISLANDS30_IV_SRCID_D6_GRPH_PFLIP: + return DC_IRQ_SOURCE_PFLIP6; + + case VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A: + /* generic src_id for all HPD and HPDRX interrupts */ + switch (ext_id) { + case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_A: + return DC_IRQ_SOURCE_HPD1; + case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_B: + return DC_IRQ_SOURCE_HPD2; + case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_C: + return DC_IRQ_SOURCE_HPD3; + case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_D: + return DC_IRQ_SOURCE_HPD4; + case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_E: + return DC_IRQ_SOURCE_HPD5; + case VISLANDS30_IV_EXTID_HOTPLUG_DETECT_F: + return DC_IRQ_SOURCE_HPD6; + case VISLANDS30_IV_EXTID_HPD_RX_A: + return DC_IRQ_SOURCE_HPD1RX; + case VISLANDS30_IV_EXTID_HPD_RX_B: + return DC_IRQ_SOURCE_HPD2RX; + case VISLANDS30_IV_EXTID_HPD_RX_C: + return DC_IRQ_SOURCE_HPD3RX; + case VISLANDS30_IV_EXTID_HPD_RX_D: + return DC_IRQ_SOURCE_HPD4RX; + case VISLANDS30_IV_EXTID_HPD_RX_E: + return DC_IRQ_SOURCE_HPD5RX; + case VISLANDS30_IV_EXTID_HPD_RX_F: + return DC_IRQ_SOURCE_HPD6RX; + default: + return DC_IRQ_SOURCE_INVALID; + } + break; + + default: + return DC_IRQ_SOURCE_INVALID; + } +} + +static const struct irq_service_funcs irq_service_funcs_dce110 = { + .to_dal_irq_source = to_dal_irq_source_dce110 +}; + +static void construct( + struct irq_service *irq_service, + struct irq_service_init_data *init_data) +{ + dal_irq_service_construct(irq_service, init_data); + + irq_service->info = irq_source_info_dce110; + irq_service->funcs = &irq_service_funcs_dce110; +} + +struct irq_service *dal_irq_service_dce110_create( + struct irq_service_init_data *init_data) +{ + struct irq_service *irq_service = kzalloc(sizeof(*irq_service), + GFP_KERNEL); + + if (!irq_service) + return NULL; + + construct(irq_service, init_data); + return irq_service; +} diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.h b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.h new file mode 100644 index 000000000000..9237646c0959 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.h @@ -0,0 +1,53 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IRQ_SERVICE_DCE110_H__ +#define __DAL_IRQ_SERVICE_DCE110_H__ + +#include "../irq_service.h" + +struct irq_service *dal_irq_service_dce110_create( + struct irq_service_init_data *init_data); + +enum dc_irq_source to_dal_irq_source_dce110( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id); + +bool dal_irq_service_dummy_set( + struct irq_service *irq_service, + const struct irq_source_info *info, + bool enable); + +bool dal_irq_service_dummy_ack( + struct irq_service *irq_service, + const struct irq_source_info *info); + +bool dce110_vblank_set( + struct irq_service *irq_service, + const struct irq_source_info *info, + bool enable); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c b/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c new file mode 100644 index 000000000000..2ad56b1a4099 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c @@ -0,0 +1,289 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/logger_interface.h" + +#include "irq_service_dce120.h" +#include "../dce110/irq_service_dce110.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "ivsrcid/ivsrcid_vislands30.h" + +static bool hpd_ack( + struct irq_service *irq_service, + const struct irq_source_info *info) +{ + uint32_t addr = info->status_reg; + uint32_t value = dm_read_reg(irq_service->ctx, addr); + uint32_t current_status = + get_reg_field_value( + value, + HPD0_DC_HPD_INT_STATUS, + DC_HPD_SENSE_DELAYED); + + dal_irq_service_ack_generic(irq_service, info); + + value = dm_read_reg(irq_service->ctx, info->enable_reg); + + set_reg_field_value( + value, + current_status ? 0 : 1, + HPD0_DC_HPD_INT_CONTROL, + DC_HPD_INT_POLARITY); + + dm_write_reg(irq_service->ctx, info->enable_reg, value); + + return true; +} + +static const struct irq_source_info_funcs hpd_irq_info_funcs = { + .set = NULL, + .ack = hpd_ack +}; + +static const struct irq_source_info_funcs hpd_rx_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs pflip_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs vblank_irq_info_funcs = { + .set = dce110_vblank_set, + .ack = NULL +}; + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +#define BASE(seg) \ + BASE_INNER(seg) + +#define SRI(reg_name, block, id)\ + BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + + +#define IRQ_REG_ENTRY(block, reg_num, reg1, mask1, reg2, mask2)\ + .enable_reg = SRI(reg1, block, reg_num),\ + .enable_mask = \ + block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ + .enable_value = {\ + block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ + ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + },\ + .ack_reg = SRI(reg2, block, reg_num),\ + .ack_mask = \ + block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ + .ack_value = \ + block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK \ + +#define hpd_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1 + reg_num] = {\ + IRQ_REG_ENTRY(HPD, reg_num,\ + DC_HPD_INT_CONTROL, DC_HPD_INT_EN,\ + DC_HPD_INT_CONTROL, DC_HPD_INT_ACK),\ + .status_reg = SRI(DC_HPD_INT_STATUS, HPD, reg_num),\ + .funcs = &hpd_irq_info_funcs\ + } + +#define hpd_rx_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1RX + reg_num] = {\ + IRQ_REG_ENTRY(HPD, reg_num,\ + DC_HPD_INT_CONTROL, DC_HPD_RX_INT_EN,\ + DC_HPD_INT_CONTROL, DC_HPD_RX_INT_ACK),\ + .status_reg = SRI(DC_HPD_INT_STATUS, HPD, reg_num),\ + .funcs = &hpd_rx_irq_info_funcs\ + } +#define pflip_int_entry(reg_num)\ + [DC_IRQ_SOURCE_PFLIP1 + reg_num] = {\ + IRQ_REG_ENTRY(DCP, reg_num, \ + GRPH_INTERRUPT_CONTROL, GRPH_PFLIP_INT_MASK, \ + GRPH_INTERRUPT_STATUS, GRPH_PFLIP_INT_CLEAR),\ + .status_reg = SRI(GRPH_INTERRUPT_STATUS, DCP, reg_num),\ + .funcs = &pflip_irq_info_funcs\ + } + +#define vupdate_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VUPDATE1 + reg_num] = {\ + IRQ_REG_ENTRY(CRTC, reg_num,\ + CRTC_INTERRUPT_CONTROL, CRTC_V_UPDATE_INT_MSK,\ + CRTC_V_UPDATE_INT_STATUS, CRTC_V_UPDATE_INT_CLEAR),\ + .funcs = &vblank_irq_info_funcs\ + } + +#define vblank_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VBLANK1 + reg_num] = {\ + IRQ_REG_ENTRY(CRTC, reg_num,\ + CRTC_VERTICAL_INTERRUPT0_CONTROL, CRTC_VERTICAL_INTERRUPT0_INT_ENABLE,\ + CRTC_VERTICAL_INTERRUPT0_CONTROL, CRTC_VERTICAL_INTERRUPT0_CLEAR),\ + .funcs = &vblank_irq_info_funcs,\ + .src_id = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 + reg_num\ + } + +#define dummy_irq_entry() \ + {\ + .funcs = &dummy_irq_info_funcs\ + } + +#define i2c_int_entry(reg_num) \ + [DC_IRQ_SOURCE_I2C_DDC ## reg_num] = dummy_irq_entry() + +#define dp_sink_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DPSINK ## reg_num] = dummy_irq_entry() + +#define gpio_pad_int_entry(reg_num) \ + [DC_IRQ_SOURCE_GPIOPAD ## reg_num] = dummy_irq_entry() + +#define dc_underflow_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DC ## reg_num ## UNDERFLOW] = dummy_irq_entry() + +static const struct irq_source_info_funcs dummy_irq_info_funcs = { + .set = dal_irq_service_dummy_set, + .ack = dal_irq_service_dummy_ack +}; + +static const struct irq_source_info +irq_source_info_dce120[DAL_IRQ_SOURCES_NUMBER] = { + [DC_IRQ_SOURCE_INVALID] = dummy_irq_entry(), + hpd_int_entry(0), + hpd_int_entry(1), + hpd_int_entry(2), + hpd_int_entry(3), + hpd_int_entry(4), + hpd_int_entry(5), + hpd_rx_int_entry(0), + hpd_rx_int_entry(1), + hpd_rx_int_entry(2), + hpd_rx_int_entry(3), + hpd_rx_int_entry(4), + hpd_rx_int_entry(5), + i2c_int_entry(1), + i2c_int_entry(2), + i2c_int_entry(3), + i2c_int_entry(4), + i2c_int_entry(5), + i2c_int_entry(6), + dp_sink_int_entry(1), + dp_sink_int_entry(2), + dp_sink_int_entry(3), + dp_sink_int_entry(4), + dp_sink_int_entry(5), + dp_sink_int_entry(6), + [DC_IRQ_SOURCE_TIMER] = dummy_irq_entry(), + pflip_int_entry(0), + pflip_int_entry(1), + pflip_int_entry(2), + pflip_int_entry(3), + pflip_int_entry(4), + pflip_int_entry(5), + [DC_IRQ_SOURCE_PFLIP_UNDERLAY0] = dummy_irq_entry(), + gpio_pad_int_entry(0), + gpio_pad_int_entry(1), + gpio_pad_int_entry(2), + gpio_pad_int_entry(3), + gpio_pad_int_entry(4), + gpio_pad_int_entry(5), + gpio_pad_int_entry(6), + gpio_pad_int_entry(7), + gpio_pad_int_entry(8), + gpio_pad_int_entry(9), + gpio_pad_int_entry(10), + gpio_pad_int_entry(11), + gpio_pad_int_entry(12), + gpio_pad_int_entry(13), + gpio_pad_int_entry(14), + gpio_pad_int_entry(15), + gpio_pad_int_entry(16), + gpio_pad_int_entry(17), + gpio_pad_int_entry(18), + gpio_pad_int_entry(19), + gpio_pad_int_entry(20), + gpio_pad_int_entry(21), + gpio_pad_int_entry(22), + gpio_pad_int_entry(23), + gpio_pad_int_entry(24), + gpio_pad_int_entry(25), + gpio_pad_int_entry(26), + gpio_pad_int_entry(27), + gpio_pad_int_entry(28), + gpio_pad_int_entry(29), + gpio_pad_int_entry(30), + dc_underflow_int_entry(1), + dc_underflow_int_entry(2), + dc_underflow_int_entry(3), + dc_underflow_int_entry(4), + dc_underflow_int_entry(5), + dc_underflow_int_entry(6), + [DC_IRQ_SOURCE_DMCU_SCP] = dummy_irq_entry(), + [DC_IRQ_SOURCE_VBIOS_SW] = dummy_irq_entry(), + vupdate_int_entry(0), + vupdate_int_entry(1), + vupdate_int_entry(2), + vupdate_int_entry(3), + vupdate_int_entry(4), + vupdate_int_entry(5), + vblank_int_entry(0), + vblank_int_entry(1), + vblank_int_entry(2), + vblank_int_entry(3), + vblank_int_entry(4), + vblank_int_entry(5), +}; + +static const struct irq_service_funcs irq_service_funcs_dce120 = { + .to_dal_irq_source = to_dal_irq_source_dce110 +}; + +static void construct( + struct irq_service *irq_service, + struct irq_service_init_data *init_data) +{ + dal_irq_service_construct(irq_service, init_data); + + irq_service->info = irq_source_info_dce120; + irq_service->funcs = &irq_service_funcs_dce120; +} + +struct irq_service *dal_irq_service_dce120_create( + struct irq_service_init_data *init_data) +{ + struct irq_service *irq_service = kzalloc(sizeof(*irq_service), + GFP_KERNEL); + + if (!irq_service) + return NULL; + + construct(irq_service, init_data); + return irq_service; +} diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.h b/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.h new file mode 100644 index 000000000000..420c96e8fefc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IRQ_SERVICE_DCE120_H__ +#define __DAL_IRQ_SERVICE_DCE120_H__ + +#include "../irq_service.h" + +struct irq_service *dal_irq_service_dce120_create( + struct irq_service_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c b/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c new file mode 100644 index 000000000000..8a2066c313fe --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c @@ -0,0 +1,303 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/logger_interface.h" + +#include "irq_service_dce80.h" +#include "../dce110/irq_service_dce110.h" + +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "ivsrcid/ivsrcid_vislands30.h" + +#include "dc_types.h" + +static bool hpd_ack( + struct irq_service *irq_service, + const struct irq_source_info *info) +{ + uint32_t addr = info->status_reg; + uint32_t value = dm_read_reg(irq_service->ctx, addr); + uint32_t current_status = + get_reg_field_value( + value, + DC_HPD1_INT_STATUS, + DC_HPD1_SENSE_DELAYED); + + dal_irq_service_ack_generic(irq_service, info); + + value = dm_read_reg(irq_service->ctx, info->enable_reg); + + set_reg_field_value( + value, + current_status ? 0 : 1, + DC_HPD1_INT_CONTROL, + DC_HPD1_INT_POLARITY); + + dm_write_reg(irq_service->ctx, info->enable_reg, value); + + return true; +} + +static const struct irq_source_info_funcs hpd_irq_info_funcs = { + .set = NULL, + .ack = hpd_ack +}; + +static const struct irq_source_info_funcs hpd_rx_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs pflip_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs vblank_irq_info_funcs = { + .set = dce110_vblank_set, + .ack = NULL +}; + + +#define hpd_int_entry(reg_num)\ + [DC_IRQ_SOURCE_INVALID + reg_num] = {\ + .enable_reg = mmDC_HPD ## reg_num ## _INT_CONTROL,\ + .enable_mask = DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK,\ + .enable_value = {\ + DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK,\ + ~DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK\ + },\ + .ack_reg = mmDC_HPD ## reg_num ## _INT_CONTROL,\ + .ack_mask = DC_HPD1_INT_CONTROL__DC_HPD1_INT_ACK_MASK,\ + .ack_value = DC_HPD1_INT_CONTROL__DC_HPD1_INT_ACK_MASK,\ + .status_reg = mmDC_HPD ## reg_num ## _INT_STATUS,\ + .funcs = &hpd_irq_info_funcs\ + } + +#define hpd_rx_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD6 + reg_num] = {\ + .enable_reg = mmDC_HPD ## reg_num ## _INT_CONTROL,\ + .enable_mask = DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_EN_MASK,\ + .enable_value = {\ + DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_EN_MASK,\ + ~DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_EN_MASK },\ + .ack_reg = mmDC_HPD ## reg_num ## _INT_CONTROL,\ + .ack_mask = DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_ACK_MASK,\ + .ack_value = DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_ACK_MASK,\ + .status_reg = mmDC_HPD ## reg_num ## _INT_STATUS,\ + .funcs = &hpd_rx_irq_info_funcs\ + } + +#define pflip_int_entry(reg_num)\ + [DC_IRQ_SOURCE_PFLIP1 + reg_num] = {\ + .enable_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_CONTROL,\ + .enable_mask =\ + GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ + .enable_value = {\ + GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ + ~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK},\ + .ack_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_STATUS,\ + .ack_mask = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ + .ack_value = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ + .status_reg = mmDCP ## reg_num ##_GRPH_INTERRUPT_STATUS,\ + .funcs = &pflip_irq_info_funcs\ + } + +#define vupdate_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VUPDATE1 + reg_num] = {\ + .enable_reg = mmCRTC ## reg_num ## _CRTC_INTERRUPT_CONTROL,\ + .enable_mask =\ + CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ + .enable_value = {\ + CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ + ~CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK},\ + .ack_reg = mmCRTC ## reg_num ## _CRTC_V_UPDATE_INT_STATUS,\ + .ack_mask =\ + CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ + .ack_value =\ + CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ + .funcs = &vblank_irq_info_funcs\ + } + +#define vblank_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VBLANK1 + reg_num] = {\ + .enable_reg = mmCRTC ## reg_num ## _CRTC_VERTICAL_INTERRUPT0_CONTROL,\ + .enable_mask =\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ + .enable_value = {\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ + ~CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK},\ + .ack_reg = mmCRTC ## reg_num ## _CRTC_VERTICAL_INTERRUPT0_CONTROL,\ + .ack_mask =\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_CLEAR_MASK,\ + .ack_value =\ + CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_CLEAR_MASK,\ + .funcs = &vblank_irq_info_funcs,\ + .src_id = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 + reg_num\ + } + +#define dummy_irq_entry() \ + {\ + .funcs = &dummy_irq_info_funcs\ + } + +#define i2c_int_entry(reg_num) \ + [DC_IRQ_SOURCE_I2C_DDC ## reg_num] = dummy_irq_entry() + +#define dp_sink_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DPSINK ## reg_num] = dummy_irq_entry() + +#define gpio_pad_int_entry(reg_num) \ + [DC_IRQ_SOURCE_GPIOPAD ## reg_num] = dummy_irq_entry() + +#define dc_underflow_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DC ## reg_num ## UNDERFLOW] = dummy_irq_entry() + + +static const struct irq_source_info_funcs dummy_irq_info_funcs = { + .set = dal_irq_service_dummy_set, + .ack = dal_irq_service_dummy_ack +}; + +static const struct irq_source_info +irq_source_info_dce80[DAL_IRQ_SOURCES_NUMBER] = { + [DC_IRQ_SOURCE_INVALID] = dummy_irq_entry(), + hpd_int_entry(1), + hpd_int_entry(2), + hpd_int_entry(3), + hpd_int_entry(4), + hpd_int_entry(5), + hpd_int_entry(6), + hpd_rx_int_entry(1), + hpd_rx_int_entry(2), + hpd_rx_int_entry(3), + hpd_rx_int_entry(4), + hpd_rx_int_entry(5), + hpd_rx_int_entry(6), + i2c_int_entry(1), + i2c_int_entry(2), + i2c_int_entry(3), + i2c_int_entry(4), + i2c_int_entry(5), + i2c_int_entry(6), + dp_sink_int_entry(1), + dp_sink_int_entry(2), + dp_sink_int_entry(3), + dp_sink_int_entry(4), + dp_sink_int_entry(5), + dp_sink_int_entry(6), + [DC_IRQ_SOURCE_TIMER] = dummy_irq_entry(), + pflip_int_entry(0), + pflip_int_entry(1), + pflip_int_entry(2), + pflip_int_entry(3), + pflip_int_entry(4), + pflip_int_entry(5), + [DC_IRQ_SOURCE_PFLIP_UNDERLAY0] = dummy_irq_entry(), + gpio_pad_int_entry(0), + gpio_pad_int_entry(1), + gpio_pad_int_entry(2), + gpio_pad_int_entry(3), + gpio_pad_int_entry(4), + gpio_pad_int_entry(5), + gpio_pad_int_entry(6), + gpio_pad_int_entry(7), + gpio_pad_int_entry(8), + gpio_pad_int_entry(9), + gpio_pad_int_entry(10), + gpio_pad_int_entry(11), + gpio_pad_int_entry(12), + gpio_pad_int_entry(13), + gpio_pad_int_entry(14), + gpio_pad_int_entry(15), + gpio_pad_int_entry(16), + gpio_pad_int_entry(17), + gpio_pad_int_entry(18), + gpio_pad_int_entry(19), + gpio_pad_int_entry(20), + gpio_pad_int_entry(21), + gpio_pad_int_entry(22), + gpio_pad_int_entry(23), + gpio_pad_int_entry(24), + gpio_pad_int_entry(25), + gpio_pad_int_entry(26), + gpio_pad_int_entry(27), + gpio_pad_int_entry(28), + gpio_pad_int_entry(29), + gpio_pad_int_entry(30), + dc_underflow_int_entry(1), + dc_underflow_int_entry(2), + dc_underflow_int_entry(3), + dc_underflow_int_entry(4), + dc_underflow_int_entry(5), + dc_underflow_int_entry(6), + [DC_IRQ_SOURCE_DMCU_SCP] = dummy_irq_entry(), + [DC_IRQ_SOURCE_VBIOS_SW] = dummy_irq_entry(), + vupdate_int_entry(0), + vupdate_int_entry(1), + vupdate_int_entry(2), + vupdate_int_entry(3), + vupdate_int_entry(4), + vupdate_int_entry(5), + vblank_int_entry(0), + vblank_int_entry(1), + vblank_int_entry(2), + vblank_int_entry(3), + vblank_int_entry(4), + vblank_int_entry(5), +}; + +static const struct irq_service_funcs irq_service_funcs_dce80 = { + .to_dal_irq_source = to_dal_irq_source_dce110 +}; + +static void construct( + struct irq_service *irq_service, + struct irq_service_init_data *init_data) +{ + dal_irq_service_construct(irq_service, init_data); + + irq_service->info = irq_source_info_dce80; + irq_service->funcs = &irq_service_funcs_dce80; +} + +struct irq_service *dal_irq_service_dce80_create( + struct irq_service_init_data *init_data) +{ + struct irq_service *irq_service = kzalloc(sizeof(*irq_service), + GFP_KERNEL); + + if (!irq_service) + return NULL; + + construct(irq_service, init_data); + return irq_service; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.h b/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.h new file mode 100644 index 000000000000..3dd1013576ea --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.h @@ -0,0 +1,35 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IRQ_SERVICE_DCE80_H__ +#define __DAL_IRQ_SERVICE_DCE80_H__ + +#include "../irq_service.h" + +struct irq_service *dal_irq_service_dce80_create( + struct irq_service_init_data *init_data); + +#endif + diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c new file mode 100644 index 000000000000..74ad24714f6b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c @@ -0,0 +1,356 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/logger_interface.h" + +#include "../dce110/irq_service_dce110.h" + +#include "raven1/DCN/dcn_1_0_offset.h" +#include "raven1/DCN/dcn_1_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "irq_service_dcn10.h" + +#include "ivsrcid/irqsrcs_dcn_1_0.h" + +enum dc_irq_source to_dal_irq_source_dcn10( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id) +{ + switch (src_id) { + case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK1; + case DCN_1_0__SRCID__DC_D2_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK2; + case DCN_1_0__SRCID__DC_D3_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK3; + case DCN_1_0__SRCID__DC_D4_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK4; + case DCN_1_0__SRCID__DC_D5_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK5; + case DCN_1_0__SRCID__DC_D6_OTG_VSTARTUP: + return DC_IRQ_SOURCE_VBLANK6; + case DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP1; + case DCN_1_0__SRCID__HUBP1_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP2; + case DCN_1_0__SRCID__HUBP2_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP3; + case DCN_1_0__SRCID__HUBP3_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP4; + case DCN_1_0__SRCID__HUBP4_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP5; + case DCN_1_0__SRCID__HUBP5_FLIP_INTERRUPT: + return DC_IRQ_SOURCE_PFLIP6; + + case DCN_1_0__SRCID__DC_HPD1_INT: + /* generic src_id for all HPD and HPDRX interrupts */ + switch (ext_id) { + case DCN_1_0__CTXID__DC_HPD1_INT: + return DC_IRQ_SOURCE_HPD1; + case DCN_1_0__CTXID__DC_HPD2_INT: + return DC_IRQ_SOURCE_HPD2; + case DCN_1_0__CTXID__DC_HPD3_INT: + return DC_IRQ_SOURCE_HPD3; + case DCN_1_0__CTXID__DC_HPD4_INT: + return DC_IRQ_SOURCE_HPD4; + case DCN_1_0__CTXID__DC_HPD5_INT: + return DC_IRQ_SOURCE_HPD5; + case DCN_1_0__CTXID__DC_HPD6_INT: + return DC_IRQ_SOURCE_HPD6; + case DCN_1_0__CTXID__DC_HPD1_RX_INT: + return DC_IRQ_SOURCE_HPD1RX; + case DCN_1_0__CTXID__DC_HPD2_RX_INT: + return DC_IRQ_SOURCE_HPD2RX; + case DCN_1_0__CTXID__DC_HPD3_RX_INT: + return DC_IRQ_SOURCE_HPD3RX; + case DCN_1_0__CTXID__DC_HPD4_RX_INT: + return DC_IRQ_SOURCE_HPD4RX; + case DCN_1_0__CTXID__DC_HPD5_RX_INT: + return DC_IRQ_SOURCE_HPD5RX; + case DCN_1_0__CTXID__DC_HPD6_RX_INT: + return DC_IRQ_SOURCE_HPD6RX; + default: + return DC_IRQ_SOURCE_INVALID; + } + break; + + default: + return DC_IRQ_SOURCE_INVALID; + } +} + +static bool hpd_ack( + struct irq_service *irq_service, + const struct irq_source_info *info) +{ + uint32_t addr = info->status_reg; + uint32_t value = dm_read_reg(irq_service->ctx, addr); + uint32_t current_status = + get_reg_field_value( + value, + HPD0_DC_HPD_INT_STATUS, + DC_HPD_SENSE_DELAYED); + + dal_irq_service_ack_generic(irq_service, info); + + value = dm_read_reg(irq_service->ctx, info->enable_reg); + + set_reg_field_value( + value, + current_status ? 0 : 1, + HPD0_DC_HPD_INT_CONTROL, + DC_HPD_INT_POLARITY); + + dm_write_reg(irq_service->ctx, info->enable_reg, value); + + return true; +} + +static const struct irq_source_info_funcs hpd_irq_info_funcs = { + .set = NULL, + .ack = hpd_ack +}; + +static const struct irq_source_info_funcs hpd_rx_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs pflip_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +static const struct irq_source_info_funcs vblank_irq_info_funcs = { + .set = NULL, + .ack = NULL +}; + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +#define BASE(seg) \ + BASE_INNER(seg) + +#define SRI(reg_name, block, id)\ + BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + + +#define IRQ_REG_ENTRY(block, reg_num, reg1, mask1, reg2, mask2)\ + .enable_reg = SRI(reg1, block, reg_num),\ + .enable_mask = \ + block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ + .enable_value = {\ + block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ + ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + },\ + .ack_reg = SRI(reg2, block, reg_num),\ + .ack_mask = \ + block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ + .ack_value = \ + block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK \ + +#define hpd_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1 + reg_num] = {\ + IRQ_REG_ENTRY(HPD, reg_num,\ + DC_HPD_INT_CONTROL, DC_HPD_INT_EN,\ + DC_HPD_INT_CONTROL, DC_HPD_INT_ACK),\ + .status_reg = SRI(DC_HPD_INT_STATUS, HPD, reg_num),\ + .funcs = &hpd_irq_info_funcs\ + } + +#define hpd_rx_int_entry(reg_num)\ + [DC_IRQ_SOURCE_HPD1RX + reg_num] = {\ + IRQ_REG_ENTRY(HPD, reg_num,\ + DC_HPD_INT_CONTROL, DC_HPD_RX_INT_EN,\ + DC_HPD_INT_CONTROL, DC_HPD_RX_INT_ACK),\ + .status_reg = SRI(DC_HPD_INT_STATUS, HPD, reg_num),\ + .funcs = &hpd_rx_irq_info_funcs\ + } +#define pflip_int_entry(reg_num)\ + [DC_IRQ_SOURCE_PFLIP1 + reg_num] = {\ + IRQ_REG_ENTRY(HUBPREQ, reg_num,\ + DCSURF_SURFACE_FLIP_INTERRUPT, SURFACE_FLIP_INT_MASK,\ + DCSURF_SURFACE_FLIP_INTERRUPT, SURFACE_FLIP_CLEAR),\ + .funcs = &pflip_irq_info_funcs\ + } + +#define vupdate_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VUPDATE1 + reg_num] = {\ + IRQ_REG_ENTRY(OTG, reg_num,\ + OTG_GLOBAL_SYNC_STATUS, VUPDATE_INT_EN,\ + OTG_GLOBAL_SYNC_STATUS, VUPDATE_EVENT_CLEAR),\ + .funcs = &vblank_irq_info_funcs\ + } + +#define vblank_int_entry(reg_num)\ + [DC_IRQ_SOURCE_VBLANK1 + reg_num] = {\ + IRQ_REG_ENTRY(OTG, reg_num,\ + OTG_GLOBAL_SYNC_STATUS, VSTARTUP_INT_EN,\ + OTG_GLOBAL_SYNC_STATUS, VSTARTUP_EVENT_CLEAR),\ + .funcs = &vblank_irq_info_funcs\ + } + +#define dummy_irq_entry() \ + {\ + .funcs = &dummy_irq_info_funcs\ + } + +#define i2c_int_entry(reg_num) \ + [DC_IRQ_SOURCE_I2C_DDC ## reg_num] = dummy_irq_entry() + +#define dp_sink_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DPSINK ## reg_num] = dummy_irq_entry() + +#define gpio_pad_int_entry(reg_num) \ + [DC_IRQ_SOURCE_GPIOPAD ## reg_num] = dummy_irq_entry() + +#define dc_underflow_int_entry(reg_num) \ + [DC_IRQ_SOURCE_DC ## reg_num ## UNDERFLOW] = dummy_irq_entry() + +static const struct irq_source_info_funcs dummy_irq_info_funcs = { + .set = dal_irq_service_dummy_set, + .ack = dal_irq_service_dummy_ack +}; + +static const struct irq_source_info +irq_source_info_dcn10[DAL_IRQ_SOURCES_NUMBER] = { + [DC_IRQ_SOURCE_INVALID] = dummy_irq_entry(), + hpd_int_entry(0), + hpd_int_entry(1), + hpd_int_entry(2), + hpd_int_entry(3), + hpd_int_entry(4), + hpd_int_entry(5), + hpd_rx_int_entry(0), + hpd_rx_int_entry(1), + hpd_rx_int_entry(2), + hpd_rx_int_entry(3), + hpd_rx_int_entry(4), + hpd_rx_int_entry(5), + i2c_int_entry(1), + i2c_int_entry(2), + i2c_int_entry(3), + i2c_int_entry(4), + i2c_int_entry(5), + i2c_int_entry(6), + dp_sink_int_entry(1), + dp_sink_int_entry(2), + dp_sink_int_entry(3), + dp_sink_int_entry(4), + dp_sink_int_entry(5), + dp_sink_int_entry(6), + [DC_IRQ_SOURCE_TIMER] = dummy_irq_entry(), + pflip_int_entry(0), + pflip_int_entry(1), + pflip_int_entry(2), + pflip_int_entry(3), + [DC_IRQ_SOURCE_PFLIP5] = dummy_irq_entry(), + [DC_IRQ_SOURCE_PFLIP6] = dummy_irq_entry(), + [DC_IRQ_SOURCE_PFLIP_UNDERLAY0] = dummy_irq_entry(), + gpio_pad_int_entry(0), + gpio_pad_int_entry(1), + gpio_pad_int_entry(2), + gpio_pad_int_entry(3), + gpio_pad_int_entry(4), + gpio_pad_int_entry(5), + gpio_pad_int_entry(6), + gpio_pad_int_entry(7), + gpio_pad_int_entry(8), + gpio_pad_int_entry(9), + gpio_pad_int_entry(10), + gpio_pad_int_entry(11), + gpio_pad_int_entry(12), + gpio_pad_int_entry(13), + gpio_pad_int_entry(14), + gpio_pad_int_entry(15), + gpio_pad_int_entry(16), + gpio_pad_int_entry(17), + gpio_pad_int_entry(18), + gpio_pad_int_entry(19), + gpio_pad_int_entry(20), + gpio_pad_int_entry(21), + gpio_pad_int_entry(22), + gpio_pad_int_entry(23), + gpio_pad_int_entry(24), + gpio_pad_int_entry(25), + gpio_pad_int_entry(26), + gpio_pad_int_entry(27), + gpio_pad_int_entry(28), + gpio_pad_int_entry(29), + gpio_pad_int_entry(30), + dc_underflow_int_entry(1), + dc_underflow_int_entry(2), + dc_underflow_int_entry(3), + dc_underflow_int_entry(4), + dc_underflow_int_entry(5), + dc_underflow_int_entry(6), + [DC_IRQ_SOURCE_DMCU_SCP] = dummy_irq_entry(), + [DC_IRQ_SOURCE_VBIOS_SW] = dummy_irq_entry(), + vupdate_int_entry(0), + vupdate_int_entry(1), + vupdate_int_entry(2), + vupdate_int_entry(3), + vupdate_int_entry(4), + vupdate_int_entry(5), + vblank_int_entry(0), + vblank_int_entry(1), + vblank_int_entry(2), + vblank_int_entry(3), + vblank_int_entry(4), + vblank_int_entry(5), +}; + +static const struct irq_service_funcs irq_service_funcs_dcn10 = { + .to_dal_irq_source = to_dal_irq_source_dcn10 +}; + +static void construct( + struct irq_service *irq_service, + struct irq_service_init_data *init_data) +{ + dal_irq_service_construct(irq_service, init_data); + + irq_service->info = irq_source_info_dcn10; + irq_service->funcs = &irq_service_funcs_dcn10; +} + +struct irq_service *dal_irq_service_dcn10_create( + struct irq_service_init_data *init_data) +{ + struct irq_service *irq_service = kzalloc(sizeof(*irq_service), + GFP_KERNEL); + + if (!irq_service) + return NULL; + + construct(irq_service, init_data); + return irq_service; +} diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.h b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.h new file mode 100644 index 000000000000..fd2ca4d0c316 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IRQ_SERVICE_DCN10_H__ +#define __DAL_IRQ_SERVICE_DCN10_H__ + +#include "../irq_service.h" + +struct irq_service *dal_irq_service_dcn10_create( + struct irq_service_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/irq/irq_service.c b/drivers/gpu/drm/amd/display/dc/irq/irq_service.c new file mode 100644 index 000000000000..b106513fc2dc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/irq_service.c @@ -0,0 +1,170 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "include/irq_service_interface.h" +#include "include/logger_interface.h" + +#include "dce110/irq_service_dce110.h" + + +#include "dce80/irq_service_dce80.h" + +#include "dce120/irq_service_dce120.h" + + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include "dcn10/irq_service_dcn10.h" +#endif + +#include "reg_helper.h" +#include "irq_service.h" + + + +#define CTX \ + irq_service->ctx + +void dal_irq_service_construct( + struct irq_service *irq_service, + struct irq_service_init_data *init_data) +{ + if (!init_data || !init_data->ctx) { + BREAK_TO_DEBUGGER(); + return; + } + + irq_service->ctx = init_data->ctx; +} + +void dal_irq_service_destroy(struct irq_service **irq_service) +{ + if (!irq_service || !*irq_service) { + BREAK_TO_DEBUGGER(); + return; + } + + kfree(*irq_service); + + *irq_service = NULL; +} + +const struct irq_source_info *find_irq_source_info( + struct irq_service *irq_service, + enum dc_irq_source source) +{ + if (source > DAL_IRQ_SOURCES_NUMBER || source < DC_IRQ_SOURCE_INVALID) + return NULL; + + return &irq_service->info[source]; +} + +void dal_irq_service_set_generic( + struct irq_service *irq_service, + const struct irq_source_info *info, + bool enable) +{ + uint32_t addr = info->enable_reg; + uint32_t value = dm_read_reg(irq_service->ctx, addr); + + value = (value & ~info->enable_mask) | + (info->enable_value[enable ? 0 : 1] & info->enable_mask); + dm_write_reg(irq_service->ctx, addr, value); +} + +bool dal_irq_service_set( + struct irq_service *irq_service, + enum dc_irq_source source, + bool enable) +{ + const struct irq_source_info *info = + find_irq_source_info(irq_service, source); + + if (!info) { + dm_logger_write( + irq_service->ctx->logger, LOG_ERROR, + "%s: cannot find irq info table entry for %d\n", + __func__, + source); + return false; + } + + dal_irq_service_ack(irq_service, source); + + if (info->funcs->set) + return info->funcs->set(irq_service, info, enable); + + dal_irq_service_set_generic(irq_service, info, enable); + + return true; +} + +void dal_irq_service_ack_generic( + struct irq_service *irq_service, + const struct irq_source_info *info) +{ + uint32_t addr = info->ack_reg; + uint32_t value = dm_read_reg(irq_service->ctx, addr); + + value = (value & ~info->ack_mask) | + (info->ack_value & info->ack_mask); + dm_write_reg(irq_service->ctx, addr, value); +} + +bool dal_irq_service_ack( + struct irq_service *irq_service, + enum dc_irq_source source) +{ + const struct irq_source_info *info = + find_irq_source_info(irq_service, source); + + if (!info) { + dm_logger_write( + irq_service->ctx->logger, LOG_ERROR, + "%s: cannot find irq info table entry for %d\n", + __func__, + source); + return false; + } + + if (info->funcs->ack) + return info->funcs->ack(irq_service, info); + + dal_irq_service_ack_generic(irq_service, info); + + return true; +} + +enum dc_irq_source dal_irq_service_to_irq_source( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id) +{ + return irq_service->funcs->to_dal_irq_source( + irq_service, + src_id, + ext_id); +} diff --git a/drivers/gpu/drm/amd/display/dc/irq/irq_service.h b/drivers/gpu/drm/amd/display/dc/irq/irq_service.h new file mode 100644 index 000000000000..dbfcb096eedd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq/irq_service.h @@ -0,0 +1,85 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IRQ_SERVICE_H__ +#define __DAL_IRQ_SERVICE_H__ + +#include "include/irq_service_interface.h" + +#include "irq_types.h" + +struct irq_service; +struct irq_source_info; + +struct irq_source_info_funcs { + bool (*set)( + struct irq_service *irq_service, + const struct irq_source_info *info, + bool enable); + bool (*ack)( + struct irq_service *irq_service, + const struct irq_source_info *info); +}; + +struct irq_source_info { + uint32_t src_id; + uint32_t ext_id; + uint32_t enable_reg; + uint32_t enable_mask; + uint32_t enable_value[2]; + uint32_t ack_reg; + uint32_t ack_mask; + uint32_t ack_value; + uint32_t status_reg; + const struct irq_source_info_funcs *funcs; +}; + +struct irq_service_funcs { + enum dc_irq_source (*to_dal_irq_source)( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id); +}; + +struct irq_service { + struct dc_context *ctx; + const struct irq_source_info *info; + const struct irq_service_funcs *funcs; +}; + +void dal_irq_service_construct( + struct irq_service *irq_service, + struct irq_service_init_data *init_data); + +void dal_irq_service_ack_generic( + struct irq_service *irq_service, + const struct irq_source_info *info); + +void dal_irq_service_set_generic( + struct irq_service *irq_service, + const struct irq_source_info *info, + bool enable); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/irq_types.h b/drivers/gpu/drm/amd/display/dc/irq_types.h new file mode 100644 index 000000000000..a506c2e939f5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/irq_types.h @@ -0,0 +1,193 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IRQ_TYPES_H__ +#define __DAL_IRQ_TYPES_H__ + +struct dc_context; + +typedef void (*interrupt_handler)(void *); + +typedef void *irq_handler_idx; +#define DAL_INVALID_IRQ_HANDLER_IDX NULL + +/* The order of the IRQ sources is important and MUST match the one's +of base driver */ +enum dc_irq_source { + /* Use as mask to specify invalid irq source */ + DC_IRQ_SOURCE_INVALID = 0, + + DC_IRQ_SOURCE_HPD1, + DC_IRQ_SOURCE_HPD2, + DC_IRQ_SOURCE_HPD3, + DC_IRQ_SOURCE_HPD4, + DC_IRQ_SOURCE_HPD5, + DC_IRQ_SOURCE_HPD6, + + DC_IRQ_SOURCE_HPD1RX, + DC_IRQ_SOURCE_HPD2RX, + DC_IRQ_SOURCE_HPD3RX, + DC_IRQ_SOURCE_HPD4RX, + DC_IRQ_SOURCE_HPD5RX, + DC_IRQ_SOURCE_HPD6RX, + + DC_IRQ_SOURCE_I2C_DDC1, + DC_IRQ_SOURCE_I2C_DDC2, + DC_IRQ_SOURCE_I2C_DDC3, + DC_IRQ_SOURCE_I2C_DDC4, + DC_IRQ_SOURCE_I2C_DDC5, + DC_IRQ_SOURCE_I2C_DDC6, + + DC_IRQ_SOURCE_DPSINK1, + DC_IRQ_SOURCE_DPSINK2, + DC_IRQ_SOURCE_DPSINK3, + DC_IRQ_SOURCE_DPSINK4, + DC_IRQ_SOURCE_DPSINK5, + DC_IRQ_SOURCE_DPSINK6, + + DC_IRQ_SOURCE_TIMER, + + DC_IRQ_SOURCE_PFLIP_FIRST, + DC_IRQ_SOURCE_PFLIP1 = DC_IRQ_SOURCE_PFLIP_FIRST, + DC_IRQ_SOURCE_PFLIP2, + DC_IRQ_SOURCE_PFLIP3, + DC_IRQ_SOURCE_PFLIP4, + DC_IRQ_SOURCE_PFLIP5, + DC_IRQ_SOURCE_PFLIP6, + DC_IRQ_SOURCE_PFLIP_UNDERLAY0, + DC_IRQ_SOURCE_PFLIP_LAST = DC_IRQ_SOURCE_PFLIP_UNDERLAY0, + + DC_IRQ_SOURCE_GPIOPAD0, + DC_IRQ_SOURCE_GPIOPAD1, + DC_IRQ_SOURCE_GPIOPAD2, + DC_IRQ_SOURCE_GPIOPAD3, + DC_IRQ_SOURCE_GPIOPAD4, + DC_IRQ_SOURCE_GPIOPAD5, + DC_IRQ_SOURCE_GPIOPAD6, + DC_IRQ_SOURCE_GPIOPAD7, + DC_IRQ_SOURCE_GPIOPAD8, + DC_IRQ_SOURCE_GPIOPAD9, + DC_IRQ_SOURCE_GPIOPAD10, + DC_IRQ_SOURCE_GPIOPAD11, + DC_IRQ_SOURCE_GPIOPAD12, + DC_IRQ_SOURCE_GPIOPAD13, + DC_IRQ_SOURCE_GPIOPAD14, + DC_IRQ_SOURCE_GPIOPAD15, + DC_IRQ_SOURCE_GPIOPAD16, + DC_IRQ_SOURCE_GPIOPAD17, + DC_IRQ_SOURCE_GPIOPAD18, + DC_IRQ_SOURCE_GPIOPAD19, + DC_IRQ_SOURCE_GPIOPAD20, + DC_IRQ_SOURCE_GPIOPAD21, + DC_IRQ_SOURCE_GPIOPAD22, + DC_IRQ_SOURCE_GPIOPAD23, + DC_IRQ_SOURCE_GPIOPAD24, + DC_IRQ_SOURCE_GPIOPAD25, + DC_IRQ_SOURCE_GPIOPAD26, + DC_IRQ_SOURCE_GPIOPAD27, + DC_IRQ_SOURCE_GPIOPAD28, + DC_IRQ_SOURCE_GPIOPAD29, + DC_IRQ_SOURCE_GPIOPAD30, + + DC_IRQ_SOURCE_DC1UNDERFLOW, + DC_IRQ_SOURCE_DC2UNDERFLOW, + DC_IRQ_SOURCE_DC3UNDERFLOW, + DC_IRQ_SOURCE_DC4UNDERFLOW, + DC_IRQ_SOURCE_DC5UNDERFLOW, + DC_IRQ_SOURCE_DC6UNDERFLOW, + + DC_IRQ_SOURCE_DMCU_SCP, + DC_IRQ_SOURCE_VBIOS_SW, + + DC_IRQ_SOURCE_VUPDATE1, + DC_IRQ_SOURCE_VUPDATE2, + DC_IRQ_SOURCE_VUPDATE3, + DC_IRQ_SOURCE_VUPDATE4, + DC_IRQ_SOURCE_VUPDATE5, + DC_IRQ_SOURCE_VUPDATE6, + + DC_IRQ_SOURCE_VBLANK1, + DC_IRQ_SOURCE_VBLANK2, + DC_IRQ_SOURCE_VBLANK3, + DC_IRQ_SOURCE_VBLANK4, + DC_IRQ_SOURCE_VBLANK5, + DC_IRQ_SOURCE_VBLANK6, + + DAL_IRQ_SOURCES_NUMBER +}; + +enum irq_type +{ + IRQ_TYPE_PFLIP = DC_IRQ_SOURCE_PFLIP1, + IRQ_TYPE_VUPDATE = DC_IRQ_SOURCE_VUPDATE1, + IRQ_TYPE_VBLANK = DC_IRQ_SOURCE_VBLANK1, +}; + +#define DAL_VALID_IRQ_SRC_NUM(src) \ + ((src) <= DAL_IRQ_SOURCES_NUMBER && (src) > DC_IRQ_SOURCE_INVALID) + +/* Number of Page Flip IRQ Sources. */ +#define DAL_PFLIP_IRQ_SRC_NUM \ + (DC_IRQ_SOURCE_PFLIP_LAST - DC_IRQ_SOURCE_PFLIP_FIRST + 1) + +/* the number of contexts may be expanded in the future based on needs */ +enum dc_interrupt_context { + INTERRUPT_LOW_IRQ_CONTEXT = 0, + INTERRUPT_HIGH_IRQ_CONTEXT, + INTERRUPT_CONTEXT_NUMBER +}; + +enum dc_interrupt_porlarity { + INTERRUPT_POLARITY_DEFAULT = 0, + INTERRUPT_POLARITY_LOW = INTERRUPT_POLARITY_DEFAULT, + INTERRUPT_POLARITY_HIGH, + INTERRUPT_POLARITY_BOTH +}; + +#define DC_DECODE_INTERRUPT_POLARITY(int_polarity) \ + (int_polarity == INTERRUPT_POLARITY_LOW) ? "Low" : \ + (int_polarity == INTERRUPT_POLARITY_HIGH) ? "High" : \ + (int_polarity == INTERRUPT_POLARITY_BOTH) ? "Both" : "Invalid" + +struct dc_timer_interrupt_params { + uint32_t micro_sec_interval; + enum dc_interrupt_context int_context; +}; + +struct dc_interrupt_params { + /* The polarity *change* which will trigger an interrupt. + * If 'requested_polarity == INTERRUPT_POLARITY_BOTH', then + * 'current_polarity' must be initialised. */ + enum dc_interrupt_porlarity requested_polarity; + /* If 'requested_polarity == INTERRUPT_POLARITY_BOTH', + * 'current_polarity' should contain the current state, which means + * the interrupt will be triggered when state changes from what is, + * in 'current_polarity'. */ + enum dc_interrupt_porlarity current_polarity; + enum dc_irq_source irq_source; + enum dc_interrupt_context int_context; +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/os_types.h b/drivers/gpu/drm/amd/display/dc/os_types.h new file mode 100644 index 000000000000..a87c0329541f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/os_types.h @@ -0,0 +1,96 @@ +/* + * Copyright 2012-16 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef _OS_TYPES_H_ +#define _OS_TYPES_H_ + +#if defined __KERNEL__ + +#include +#include +#include + +#include + +#include "cgs_linux.h" + +#if defined(__BIG_ENDIAN) && !defined(BIGENDIAN_CPU) +#define BIGENDIAN_CPU +#elif defined(__LITTLE_ENDIAN) && !defined(LITTLEENDIAN_CPU) +#define LITTLEENDIAN_CPU +#endif + +#undef READ +#undef WRITE +#undef FRAME_SIZE + +#define dm_output_to_console(fmt, ...) DRM_INFO(fmt, ##__VA_ARGS__) + +#define dm_error(fmt, ...) DRM_ERROR(fmt, ##__VA_ARGS__) + +#define dm_debug(fmt, ...) DRM_DEBUG_KMS(fmt, ##__VA_ARGS__) + +#define dm_vlog(fmt, args) vprintk(fmt, args) + +#endif + +/* + * + * general debug capabilities + * + */ +#if defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB) +#define ASSERT_CRITICAL(expr) do { \ + if (WARN_ON(!(expr))) { \ + kgdb_breakpoint(); \ + } \ +} while (0) +#else +#define ASSERT_CRITICAL(expr) do { \ + if (WARN_ON(!(expr))) { \ + ; \ + } \ +} while (0) +#endif + +#if defined(CONFIG_DEBUG_KERNEL_DC) +#define ASSERT(expr) ASSERT_CRITICAL(expr) + +#else +#define ASSERT(expr) WARN_ON(!(expr)) +#endif + +#define BREAK_TO_DEBUGGER() ASSERT(0) + +#define DC_ERR(...) do { \ + dm_error(__VA_ARGS__); \ + BREAK_TO_DEBUGGER(); \ +} while (0) + +#if defined(CONFIG_DRM_AMD_DC_DCN1_0) +#include +#endif + +#endif /* _OS_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/virtual/Makefile b/drivers/gpu/drm/amd/display/dc/virtual/Makefile new file mode 100644 index 000000000000..fc0b7318d9cc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/virtual/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the virtual sub-component of DAL. +# It provides the control and status of HW CRTC block. + +VIRTUAL = virtual_link_encoder.o virtual_stream_encoder.o + +AMD_DAL_VIRTUAL = $(addprefix $(AMDDALPATH)/dc/virtual/,$(VIRTUAL)) + +AMD_DISPLAY_FILES += $(AMD_DAL_VIRTUAL) diff --git a/drivers/gpu/drm/amd/display/dc/virtual/virtual_link_encoder.c b/drivers/gpu/drm/amd/display/dc/virtual/virtual_link_encoder.c new file mode 100644 index 000000000000..88c2bde3f039 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/virtual/virtual_link_encoder.c @@ -0,0 +1,124 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dm_services_types.h" + +#include "virtual_link_encoder.h" + +static bool virtual_link_encoder_validate_output_with_stream( + struct link_encoder *enc, + const struct dc_stream_state *stream) { return true; } + +static void virtual_link_encoder_hw_init(struct link_encoder *enc) {} + +static void virtual_link_encoder_setup( + struct link_encoder *enc, + enum signal_type signal) {} + +static void virtual_link_encoder_enable_tmds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + bool hdmi, + bool dual_link, + uint32_t pixel_clock) {} + +static void virtual_link_encoder_enable_dp_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source) {} + +static void virtual_link_encoder_enable_dp_mst_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source) {} + +static void virtual_link_encoder_disable_output( + struct link_encoder *link_enc, + enum signal_type signal, + struct dc_link *link) {} + +static void virtual_link_encoder_dp_set_lane_settings( + struct link_encoder *enc, + const struct link_training_settings *link_settings) {} + +static void virtual_link_encoder_dp_set_phy_pattern( + struct link_encoder *enc, + const struct encoder_set_dp_phy_pattern_param *param) {} + +static void virtual_link_encoder_update_mst_stream_allocation_table( + struct link_encoder *enc, + const struct link_mst_stream_allocation_table *table) {} + +static void virtual_link_encoder_connect_dig_be_to_fe( + struct link_encoder *enc, + enum engine_id engine, + bool connect) {} + +static void virtual_link_encoder_destroy(struct link_encoder **enc) +{ + kfree(*enc); + *enc = NULL; +} + + +static const struct link_encoder_funcs virtual_lnk_enc_funcs = { + .validate_output_with_stream = + virtual_link_encoder_validate_output_with_stream, + .hw_init = virtual_link_encoder_hw_init, + .setup = virtual_link_encoder_setup, + .enable_tmds_output = virtual_link_encoder_enable_tmds_output, + .enable_dp_output = virtual_link_encoder_enable_dp_output, + .enable_dp_mst_output = virtual_link_encoder_enable_dp_mst_output, + .disable_output = virtual_link_encoder_disable_output, + .dp_set_lane_settings = virtual_link_encoder_dp_set_lane_settings, + .dp_set_phy_pattern = virtual_link_encoder_dp_set_phy_pattern, + .update_mst_stream_allocation_table = + virtual_link_encoder_update_mst_stream_allocation_table, + .connect_dig_be_to_fe = virtual_link_encoder_connect_dig_be_to_fe, + .destroy = virtual_link_encoder_destroy +}; + +bool virtual_link_encoder_construct( + struct link_encoder *enc, const struct encoder_init_data *init_data) +{ + enc->funcs = &virtual_lnk_enc_funcs; + enc->ctx = init_data->ctx; + enc->id = init_data->encoder; + + enc->hpd_source = init_data->hpd_source; + enc->connector = init_data->connector; + + enc->transmitter = init_data->transmitter; + + enc->output_signals = SIGNAL_TYPE_VIRTUAL; + + enc->preferred_engine = ENGINE_ID_VIRTUAL; + + return true; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/virtual/virtual_link_encoder.h b/drivers/gpu/drm/amd/display/dc/virtual/virtual_link_encoder.h new file mode 100644 index 000000000000..eb1a94fb8a9b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/virtual/virtual_link_encoder.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_VIRTUAL_LINK_ENCODER_H__ +#define __DC_VIRTUAL_LINK_ENCODER_H__ + +#include "link_encoder.h" + +bool virtual_link_encoder_construct( + struct link_encoder *enc, const struct encoder_init_data *init_data); + +#endif /* __DC_VIRTUAL_LINK_ENCODER_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c new file mode 100644 index 000000000000..3dc1733eea20 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c @@ -0,0 +1,136 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "virtual_stream_encoder.h" + +static void virtual_stream_encoder_dp_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + enum dc_color_space output_color_space) {} + +static void virtual_stream_encoder_hdmi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + int actual_pix_clk_khz, + bool enable_audio) {} + +static void virtual_stream_encoder_dvi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool is_dual_link) {} + +static void virtual_stream_encoder_set_mst_bandwidth( + struct stream_encoder *enc, + struct fixed31_32 avg_time_slots_per_mtp) {} + +static void virtual_stream_encoder_update_hdmi_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) {} + +static void virtual_stream_encoder_stop_hdmi_info_packets( + struct stream_encoder *enc) {} + +static void virtual_stream_encoder_set_avmute( + struct stream_encoder *enc, + bool enable) {} +static void virtual_stream_encoder_update_dp_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) {} + +static void virtual_stream_encoder_stop_dp_info_packets( + struct stream_encoder *enc) {} + +static void virtual_stream_encoder_dp_blank( + struct stream_encoder *enc) {} + +static void virtual_stream_encoder_dp_unblank( + struct stream_encoder *enc, + const struct encoder_unblank_param *param) {} + +static void virtual_audio_mute_control( + struct stream_encoder *enc, + bool mute) {} + +static const struct stream_encoder_funcs virtual_str_enc_funcs = { + .dp_set_stream_attribute = + virtual_stream_encoder_dp_set_stream_attribute, + .hdmi_set_stream_attribute = + virtual_stream_encoder_hdmi_set_stream_attribute, + .dvi_set_stream_attribute = + virtual_stream_encoder_dvi_set_stream_attribute, + .set_mst_bandwidth = + virtual_stream_encoder_set_mst_bandwidth, + .update_hdmi_info_packets = + virtual_stream_encoder_update_hdmi_info_packets, + .stop_hdmi_info_packets = + virtual_stream_encoder_stop_hdmi_info_packets, + .update_dp_info_packets = + virtual_stream_encoder_update_dp_info_packets, + .stop_dp_info_packets = + virtual_stream_encoder_stop_dp_info_packets, + .dp_blank = + virtual_stream_encoder_dp_blank, + .dp_unblank = + virtual_stream_encoder_dp_unblank, + + .audio_mute_control = virtual_audio_mute_control, + .set_avmute = virtual_stream_encoder_set_avmute, +}; + +bool virtual_stream_encoder_construct( + struct stream_encoder *enc, + struct dc_context *ctx, + struct dc_bios *bp) +{ + if (!enc) + return false; + if (!bp) + return false; + + enc->funcs = &virtual_str_enc_funcs; + enc->ctx = ctx; + enc->id = ENGINE_ID_VIRTUAL; + enc->bp = bp; + + return true; +} + +struct stream_encoder *virtual_stream_encoder_create( + struct dc_context *ctx, struct dc_bios *bp) +{ + struct stream_encoder *enc = kzalloc(sizeof(*enc), GFP_KERNEL); + + if (!enc) + return NULL; + + if (virtual_stream_encoder_construct(enc, ctx, bp)) + return enc; + + BREAK_TO_DEBUGGER(); + kfree(enc); + return NULL; +} + diff --git a/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.h new file mode 100644 index 000000000000..bf3422c66976 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.h @@ -0,0 +1,39 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_VIRTUAL_STREAM_ENCODER_H__ +#define __DC_VIRTUAL_STREAM_ENCODER_H__ + +#include "stream_encoder.h" + +struct stream_encoder *virtual_stream_encoder_create( + struct dc_context *ctx, struct dc_bios *bp); + +bool virtual_stream_encoder_construct( + struct stream_encoder *enc, + struct dc_context *ctx, + struct dc_bios *bp); + +#endif /* __DC_VIRTUAL_STREAM_ENCODER_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/audio_types.h b/drivers/gpu/drm/amd/display/include/audio_types.h new file mode 100644 index 000000000000..6364fbc24cfe --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/audio_types.h @@ -0,0 +1,106 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __AUDIO_TYPES_H__ +#define __AUDIO_TYPES_H__ + +#include "signal_types.h" + +#define AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS 20 +#define MAX_HW_AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS 18 +#define MULTI_CHANNEL_SPLIT_NO_ASSO_INFO 0xFFFFFFFF + + +struct audio_crtc_info { + uint32_t h_total; + uint32_t h_active; + uint32_t v_active; + uint32_t pixel_repetition; + uint32_t requested_pixel_clock; /* in KHz */ + uint32_t calculated_pixel_clock; /* in KHz */ + uint32_t refresh_rate; + enum dc_color_depth color_depth; + bool interlaced; +}; +struct azalia_clock_info { + uint32_t pixel_clock_in_10khz; + uint32_t audio_dto_phase; + uint32_t audio_dto_module; + uint32_t audio_dto_wall_clock_ratio; +}; + +enum audio_dto_source { + DTO_SOURCE_UNKNOWN = 0, + DTO_SOURCE_ID0, + DTO_SOURCE_ID1, + DTO_SOURCE_ID2, + DTO_SOURCE_ID3, + DTO_SOURCE_ID4, + DTO_SOURCE_ID5 +}; + +/* PLL information required for AZALIA DTO calculation */ + +struct audio_pll_info { + uint32_t dp_dto_source_clock_in_khz; + uint32_t feed_back_divider; + enum audio_dto_source dto_source; + bool ss_enabled; + uint32_t ss_percentage; + uint32_t ss_percentage_divider; +}; + +struct audio_channel_associate_info { + union { + struct { + uint32_t ALL_CHANNEL_FL:4; + uint32_t ALL_CHANNEL_FR:4; + uint32_t ALL_CHANNEL_FC:4; + uint32_t ALL_CHANNEL_Sub:4; + uint32_t ALL_CHANNEL_SL:4; + uint32_t ALL_CHANNEL_SR:4; + uint32_t ALL_CHANNEL_BL:4; + uint32_t ALL_CHANNEL_BR:4; + } bits; + uint32_t u32all; + }; +}; + +struct audio_output { + /* Front DIG id. */ + enum engine_id engine_id; + /* encoder output signal */ + enum signal_type signal; + /* video timing */ + struct audio_crtc_info crtc_info; + /* PLL for audio */ + struct audio_pll_info pll_info; +}; + +enum audio_payload { + CHANNEL_SPLIT_MAPPINGCHANG = 0x9, +}; + +#endif /* __AUDIO_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/bios_parser_interface.h b/drivers/gpu/drm/amd/display/include/bios_parser_interface.h new file mode 100644 index 000000000000..d51101c5c6b0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/bios_parser_interface.h @@ -0,0 +1,44 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BIOS_PARSER_INTERFACE_H__ +#define __DAL_BIOS_PARSER_INTERFACE_H__ + +#include "dc_bios_types.h" + +struct bios_parser; + +struct bp_init_data { + struct dc_context *ctx; + uint8_t *bios; +}; + +struct dc_bios *dal_bios_parser_create( + struct bp_init_data *init, + enum dce_version dce_version); + +void dal_bios_parser_destroy(struct dc_bios **dcb); + +#endif /* __DAL_BIOS_PARSER_INTERFACE_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/bios_parser_types.h b/drivers/gpu/drm/amd/display/include/bios_parser_types.h new file mode 100644 index 000000000000..0840f69cde99 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/bios_parser_types.h @@ -0,0 +1,310 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_BIOS_PARSER_TYPES_H__ + +#define __DAL_BIOS_PARSER_TYPES_H__ + +#include "dm_services.h" +#include "include/signal_types.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/gpio_types.h" +#include "include/link_service_types.h" + +/* TODO: include signal_types.h and remove this enum */ +enum as_signal_type { + AS_SIGNAL_TYPE_NONE = 0L, /* no signal */ + AS_SIGNAL_TYPE_DVI, + AS_SIGNAL_TYPE_HDMI, + AS_SIGNAL_TYPE_LVDS, + AS_SIGNAL_TYPE_DISPLAY_PORT, + AS_SIGNAL_TYPE_GPU_PLL, + AS_SIGNAL_TYPE_UNKNOWN +}; + +enum bp_result { + BP_RESULT_OK = 0, /* There was no error */ + BP_RESULT_BADINPUT, /*Bad input parameter */ + BP_RESULT_BADBIOSTABLE, /* Bad BIOS table */ + BP_RESULT_UNSUPPORTED, /* BIOS Table is not supported */ + BP_RESULT_NORECORD, /* Record can't be found */ + BP_RESULT_FAILURE +}; + +enum bp_encoder_control_action { + /* direct VBIOS translation! Just to simplify the translation */ + ENCODER_CONTROL_DISABLE = 0, + ENCODER_CONTROL_ENABLE, + ENCODER_CONTROL_SETUP, + ENCODER_CONTROL_INIT +}; + +enum bp_transmitter_control_action { + /* direct VBIOS translation! Just to simplify the translation */ + TRANSMITTER_CONTROL_DISABLE = 0, + TRANSMITTER_CONTROL_ENABLE, + TRANSMITTER_CONTROL_BACKLIGHT_OFF, + TRANSMITTER_CONTROL_BACKLIGHT_ON, + TRANSMITTER_CONTROL_BACKLIGHT_BRIGHTNESS, + TRANSMITTER_CONTROL_LCD_SETF_TEST_START, + TRANSMITTER_CONTROL_LCD_SELF_TEST_STOP, + TRANSMITTER_CONTROL_INIT, + TRANSMITTER_CONTROL_DEACTIVATE, + TRANSMITTER_CONTROL_ACTIAVATE, + TRANSMITTER_CONTROL_SETUP, + TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS, + /* ATOM_TRANSMITTER_ACTION_POWER_ON. This action is for eDP only + * (power up the panel) + */ + TRANSMITTER_CONTROL_POWER_ON, + /* ATOM_TRANSMITTER_ACTION_POWER_OFF. This action is for eDP only + * (power down the panel) + */ + TRANSMITTER_CONTROL_POWER_OFF +}; + +enum bp_external_encoder_control_action { + EXTERNAL_ENCODER_CONTROL_DISABLE = 0, + EXTERNAL_ENCODER_CONTROL_ENABLE = 1, + EXTERNAL_ENCODER_CONTROL_INIT = 0x7, + EXTERNAL_ENCODER_CONTROL_SETUP = 0xf, + EXTERNAL_ENCODER_CONTROL_UNBLANK = 0x10, + EXTERNAL_ENCODER_CONTROL_BLANK = 0x11, +}; + +enum bp_pipe_control_action { + ASIC_PIPE_DISABLE = 0, + ASIC_PIPE_ENABLE, + ASIC_PIPE_INIT +}; + +struct bp_encoder_control { + enum bp_encoder_control_action action; + enum engine_id engine_id; + enum transmitter transmitter; + enum signal_type signal; + enum dc_lane_count lanes_number; + enum dc_color_depth color_depth; + bool enable_dp_audio; + uint32_t pixel_clock; /* khz */ +}; + +struct bp_external_encoder_control { + enum bp_external_encoder_control_action action; + enum engine_id engine_id; + enum dc_link_rate link_rate; + enum dc_lane_count lanes_number; + enum signal_type signal; + enum dc_color_depth color_depth; + bool coherent; + struct graphics_object_id encoder_id; + struct graphics_object_id connector_obj_id; + uint32_t pixel_clock; /* in KHz */ +}; + +struct bp_crtc_source_select { + enum engine_id engine_id; + enum controller_id controller_id; + /* from GPU Tx aka asic_signal */ + enum signal_type signal; + /* sink_signal may differ from asicSignal if Translator encoder */ + enum signal_type sink_signal; + enum display_output_bit_depth display_output_bit_depth; + bool enable_dp_audio; +}; + +struct bp_transmitter_control { + enum bp_transmitter_control_action action; + enum engine_id engine_id; + enum transmitter transmitter; /* PhyId */ + enum dc_lane_count lanes_number; + enum clock_source_id pll_id; /* needed for DCE 4.0 */ + enum signal_type signal; + enum dc_color_depth color_depth; /* not used for DCE6.0 */ + enum hpd_source_id hpd_sel; /* ucHPDSel, used for DCe6.0 */ + struct graphics_object_id connector_obj_id; + /* symClock; in 10kHz, pixel clock, in HDMI deep color mode, it should + * be pixel clock * deep_color_ratio (in KHz) + */ + uint32_t pixel_clock; + uint32_t lane_select; + uint32_t lane_settings; + bool coherent; + bool multi_path; + bool single_pll_mode; +}; + +struct bp_hw_crtc_timing_parameters { + enum controller_id controller_id; + /* horizontal part */ + uint32_t h_total; + uint32_t h_addressable; + uint32_t h_overscan_left; + uint32_t h_overscan_right; + uint32_t h_sync_start; + uint32_t h_sync_width; + + /* vertical part */ + uint32_t v_total; + uint32_t v_addressable; + uint32_t v_overscan_top; + uint32_t v_overscan_bottom; + uint32_t v_sync_start; + uint32_t v_sync_width; + + struct timing_flags { + uint32_t INTERLACE:1; + uint32_t PIXEL_REPETITION:4; + uint32_t HSYNC_POSITIVE_POLARITY:1; + uint32_t VSYNC_POSITIVE_POLARITY:1; + uint32_t HORZ_COUNT_BY_TWO:1; + } flags; +}; + +struct bp_adjust_pixel_clock_parameters { + /* Input: Signal Type - to be converted to Encoder mode */ + enum signal_type signal_type; + /* Input: Encoder object id */ + struct graphics_object_id encoder_object_id; + /* Input: Pixel Clock (requested Pixel clock based on Video timing + * standard used) in KHz + */ + uint32_t pixel_clock; + /* Output: Adjusted Pixel Clock (after VBIOS exec table) in KHz */ + uint32_t adjusted_pixel_clock; + /* Output: If non-zero, this refDiv value should be used to calculate + * other ppll params */ + uint32_t reference_divider; + /* Output: If non-zero, this postDiv value should be used to calculate + * other ppll params */ + uint32_t pixel_clock_post_divider; + /* Input: Enable spread spectrum */ + bool ss_enable; +}; + +struct bp_pixel_clock_parameters { + enum controller_id controller_id; /* (Which CRTC uses this PLL) */ + enum clock_source_id pll_id; /* Clock Source Id */ + /* signal_type -> Encoder Mode - needed by VBIOS Exec table */ + enum signal_type signal_type; + /* Adjusted Pixel Clock (after VBIOS exec table) + * that becomes Target Pixel Clock (KHz) */ + uint32_t target_pixel_clock; + /* Calculated Reference divider of Display PLL */ + uint32_t reference_divider; + /* Calculated Feedback divider of Display PLL */ + uint32_t feedback_divider; + /* Calculated Fractional Feedback divider of Display PLL */ + uint32_t fractional_feedback_divider; + /* Calculated Pixel Clock Post divider of Display PLL */ + uint32_t pixel_clock_post_divider; + struct graphics_object_id encoder_object_id; /* Encoder object id */ + /* VBIOS returns a fixed display clock when DFS-bypass feature + * is enabled (KHz) */ + uint32_t dfs_bypass_display_clock; + /* color depth to support HDMI deep color */ + enum transmitter_color_depth color_depth; + + struct program_pixel_clock_flags { + uint32_t FORCE_PROGRAMMING_OF_PLL:1; + /* Use Engine Clock as source for Display Clock when + * programming PLL */ + uint32_t USE_E_CLOCK_AS_SOURCE_FOR_D_CLOCK:1; + /* Use external reference clock (refDivSrc for PLL) */ + uint32_t SET_EXTERNAL_REF_DIV_SRC:1; + /* Force program PHY PLL only */ + uint32_t PROGRAM_PHY_PLL_ONLY:1; + /* Support for YUV420 */ + uint32_t SUPPORT_YUV_420:1; + /* Use XTALIN reference clock source */ + uint32_t SET_XTALIN_REF_SRC:1; + /* Use GENLK reference clock source */ + uint32_t SET_GENLOCK_REF_DIV_SRC:1; + } flags; +}; + +enum bp_dce_clock_type { + DCECLOCK_TYPE_DISPLAY_CLOCK = 0, + DCECLOCK_TYPE_DPREFCLK = 1 +}; + +/* DCE Clock Parameters structure for SetDceClock Exec command table */ +struct bp_set_dce_clock_parameters { + enum clock_source_id pll_id; /* Clock Source Id */ + /* Display clock or DPREFCLK value */ + uint32_t target_clock_frequency; + /* Clock to set: =0: DISPCLK =1: DPREFCLK =2: PIXCLK */ + enum bp_dce_clock_type clock_type; + + struct set_dce_clock_flags { + uint32_t USE_GENERICA_AS_SOURCE_FOR_DPREFCLK:1; + /* Use XTALIN reference clock source */ + uint32_t USE_XTALIN_AS_SOURCE_FOR_DPREFCLK:1; + /* Use PCIE reference clock source */ + uint32_t USE_PCIE_AS_SOURCE_FOR_DPREFCLK:1; + /* Use GENLK reference clock source */ + uint32_t USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK:1; + } flags; +}; + +struct spread_spectrum_flags { + /* 1 = Center Spread; 0 = down spread */ + uint32_t CENTER_SPREAD:1; + /* 1 = external; 0 = internal */ + uint32_t EXTERNAL_SS:1; + /* 1 = delta-sigma type parameter; 0 = ver1 */ + uint32_t DS_TYPE:1; +}; + +struct bp_spread_spectrum_parameters { + enum clock_source_id pll_id; + uint32_t percentage; + uint32_t ds_frac_amount; + + union { + struct { + uint32_t step; + uint32_t delay; + uint32_t range; /* In Hz unit */ + } ver1; + struct { + uint32_t feedback_amount; + uint32_t nfrac_amount; + uint32_t ds_frac_size; + } ds; + }; + + struct spread_spectrum_flags flags; +}; + +struct bp_encoder_cap_info { + uint32_t DP_HBR2_CAP:1; + uint32_t DP_HBR2_EN:1; + uint32_t DP_HBR3_EN:1; + uint32_t HDMI_6GB_EN:1; + uint32_t RESERVED:30; +}; + +#endif /*__DAL_BIOS_PARSER_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/dal_asic_id.h b/drivers/gpu/drm/amd/display/include/dal_asic_id.h new file mode 100644 index 000000000000..7abe663ecc6e --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/dal_asic_id.h @@ -0,0 +1,143 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_ASIC_ID_H__ +#define __DAL_ASIC_ID_H__ + +/* + * ASIC internal revision ID + */ + +/* DCE80 (based on ci_id.h in Perforce) */ +#define CI_BONAIRE_M_A0 0x14 +#define CI_BONAIRE_M_A1 0x15 +#define CI_HAWAII_P_A0 0x28 + +#define CI_UNKNOWN 0xFF + +#define ASIC_REV_IS_BONAIRE_M(rev) \ + ((rev >= CI_BONAIRE_M_A0) && (rev < CI_HAWAII_P_A0)) + +#define ASIC_REV_IS_HAWAII_P(rev) \ + (rev >= CI_HAWAII_P_A0) + +/* KV1 with Spectre GFX core, 8-8-1-2 (CU-Pix-Primitive-RB) */ +#define KV_SPECTRE_A0 0x01 + +/* KV2 with Spooky GFX core, including downgraded from Spectre core, + * 3-4-1-1 (CU-Pix-Primitive-RB) */ +#define KV_SPOOKY_A0 0x41 + +/* KB with Kalindi GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ +#define KB_KALINDI_A0 0x81 + +/* KB with Kalindi GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ +#define KB_KALINDI_A1 0x82 + +/* BV with Kalindi GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ +#define BV_KALINDI_A2 0x85 + +/* ML with Godavari GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ +#define ML_GODAVARI_A0 0xA1 + +/* ML with Godavari GFX core, 2-4-1-1 (CU-Pix-Primitive-RB) */ +#define ML_GODAVARI_A1 0xA2 + +#define KV_UNKNOWN 0xFF + +#define ASIC_REV_IS_KALINDI(rev) \ + ((rev >= KB_KALINDI_A0) && (rev < KV_UNKNOWN)) + +#define ASIC_REV_IS_BHAVANI(rev) \ + ((rev >= BV_KALINDI_A2) && (rev < ML_GODAVARI_A0)) + +#define ASIC_REV_IS_GODAVARI(rev) \ + ((rev >= ML_GODAVARI_A0) && (rev < KV_UNKNOWN)) + +/* VI Family */ +/* DCE10 */ +#define VI_TONGA_P_A0 20 +#define VI_TONGA_P_A1 21 +#define VI_FIJI_P_A0 60 + +/* DCE112 */ +#define VI_POLARIS10_P_A0 80 +#define VI_POLARIS11_M_A0 90 +#define VI_POLARIS12_V_A0 100 + +#define VI_UNKNOWN 0xFF + +#define ASIC_REV_IS_TONGA_P(eChipRev) ((eChipRev >= VI_TONGA_P_A0) && \ + (eChipRev < 40)) +#define ASIC_REV_IS_FIJI_P(eChipRev) ((eChipRev >= VI_FIJI_P_A0) && \ + (eChipRev < 80)) + +#define ASIC_REV_IS_POLARIS10_P(eChipRev) ((eChipRev >= VI_POLARIS10_P_A0) && \ + (eChipRev < VI_POLARIS11_M_A0)) +#define ASIC_REV_IS_POLARIS11_M(eChipRev) ((eChipRev >= VI_POLARIS11_M_A0) && \ + (eChipRev < VI_POLARIS12_V_A0)) +#define ASIC_REV_IS_POLARIS12_V(eChipRev) (eChipRev >= VI_POLARIS12_V_A0) + +/* DCE11 */ +#define CZ_CARRIZO_A0 0x01 + +#define STONEY_A0 0x61 +#define CZ_UNKNOWN 0xFF + +#define ASIC_REV_IS_STONEY(rev) \ + ((rev >= STONEY_A0) && (rev < CZ_UNKNOWN)) + +/* DCN1_0 */ +#define INTERNAL_REV_RAVEN_A0 0x00 /* First spin of Raven */ +#define RAVEN_A0 0x01 +#define RAVEN_B0 0x21 +#define RAVEN_UNKNOWN 0xFF + +#define ASIC_REV_IS_RAVEN(eChipRev) ((eChipRev >= RAVEN_A0) && eChipRev < RAVEN_UNKNOWN) +#define RAVEN1_F0 0xF0 +#define ASICREV_IS_RV1_F0(eChipRev) ((eChipRev >= RAVEN1_F0) && (eChipRev < RAVEN_UNKNOWN)) + + +#define FAMILY_RV 142 /* DCN 1*/ + +/* + * ASIC chip ID + */ +/* DCE80 */ +#define DEVICE_ID_KALINDI_9834 0x9834 +#define DEVICE_ID_TEMASH_9839 0x9839 +#define DEVICE_ID_TEMASH_983D 0x983D + +/* Asic Family IDs for different asic family. */ +#define FAMILY_CI 120 /* Sea Islands: Hawaii (P), Bonaire (M) */ +#define FAMILY_KV 125 /* Fusion => Kaveri: Spectre, Spooky; Kabini: Kalindi */ +#define FAMILY_VI 130 /* Volcanic Islands: Iceland (V), Tonga (M) */ +#define FAMILY_CZ 135 /* Carrizo */ + +#define FAMILY_AI 141 + +#define FAMILY_UNKNOWN 0xFF + +#endif /* __DAL_ASIC_ID_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/dal_types.h b/drivers/gpu/drm/amd/display/include/dal_types.h new file mode 100644 index 000000000000..fa543965feb5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/dal_types.h @@ -0,0 +1,49 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_TYPES_H__ +#define __DAL_TYPES_H__ + +#include "signal_types.h" +#include "dc_types.h" + +struct dal_logger; +struct dc_bios; + +enum dce_version { + DCE_VERSION_UNKNOWN = (-1), + DCE_VERSION_8_0, + DCE_VERSION_8_1, + DCE_VERSION_8_3, + DCE_VERSION_10_0, + DCE_VERSION_11_0, + DCE_VERSION_11_2, + DCE_VERSION_12_0, + DCE_VERSION_MAX, + DCN_VERSION_1_0, + DCN_VERSION_MAX +}; + +#endif /* __DAL_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/ddc_service_types.h b/drivers/gpu/drm/amd/display/include/ddc_service_types.h new file mode 100644 index 000000000000..0ff2a899b8f7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/ddc_service_types.h @@ -0,0 +1,154 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __DAL_DDC_SERVICE_TYPES_H__ +#define __DAL_DDC_SERVICE_TYPES_H__ + +#define DP_BRANCH_DEVICE_ID_1 0x0010FA +#define DP_BRANCH_DEVICE_ID_2 0x0022B9 +#define DP_SINK_DEVICE_ID_1 0x4CE000 +#define DP_BRANCH_DEVICE_ID_3 0x00001A +#define DP_BRANCH_DEVICE_ID_4 0x0080e1 +#define DP_BRANCH_DEVICE_ID_5 0x006037 +#define DP_SINK_DEVICE_ID_2 0x001CF8 + + +enum ddc_result { + DDC_RESULT_UNKNOWN = 0, + DDC_RESULT_SUCESSFULL, + DDC_RESULT_FAILED_CHANNEL_BUSY, + DDC_RESULT_FAILED_TIMEOUT, + DDC_RESULT_FAILED_PROTOCOL_ERROR, + DDC_RESULT_FAILED_NACK, + DDC_RESULT_FAILED_INCOMPLETE, + DDC_RESULT_FAILED_OPERATION, + DDC_RESULT_FAILED_INVALID_OPERATION, + DDC_RESULT_FAILED_BUFFER_OVERFLOW +}; + +enum ddc_service_type { + DDC_SERVICE_TYPE_CONNECTOR, + DDC_SERVICE_TYPE_DISPLAY_PORT_MST, +}; + +/** + * display sink capability + */ +struct display_sink_capability { + /* dongle type (DP converter, CV smart dongle) */ + enum display_dongle_type dongle_type; + + /********************************************************** + capabilities going INTO SINK DEVICE (stream capabilities) + **********************************************************/ + /* Dongle's downstream count. */ + uint32_t downstrm_sink_count; + /* Is dongle's downstream count info field (downstrm_sink_count) + * valid. */ + bool downstrm_sink_count_valid; + + /* Maximum additional audio delay in microsecond (us) */ + uint32_t additional_audio_delay; + /* Audio latency value in microsecond (us) */ + uint32_t audio_latency; + /* Interlace video latency value in microsecond (us) */ + uint32_t video_latency_interlace; + /* Progressive video latency value in microsecond (us) */ + uint32_t video_latency_progressive; + /* Dongle caps: Maximum pixel clock supported over dongle for HDMI */ + uint32_t max_hdmi_pixel_clock; + /* Dongle caps: Maximum deep color supported over dongle for HDMI */ + enum dc_color_depth max_hdmi_deep_color; + + /************************************************************ + capabilities going OUT OF SOURCE DEVICE (link capabilities) + ************************************************************/ + /* support for Spread Spectrum(SS) */ + bool ss_supported; + /* DP link settings (laneCount, linkRate, Spread) */ + uint32_t dp_link_lane_count; + uint32_t dp_link_rate; + uint32_t dp_link_spead; + + /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, + indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ + bool is_dp_hdmi_s3d_converter; + /* to check if we have queried the display capability + * for eDP panel already. */ + bool is_edp_sink_cap_valid; + + enum ddc_transaction_type transaction_type; + enum signal_type signal; +}; + +struct av_sync_data { + uint8_t av_granularity;/* DPCD 00023h */ + uint8_t aud_dec_lat1;/* DPCD 00024h */ + uint8_t aud_dec_lat2;/* DPCD 00025h */ + uint8_t aud_pp_lat1;/* DPCD 00026h */ + uint8_t aud_pp_lat2;/* DPCD 00027h */ + uint8_t vid_inter_lat;/* DPCD 00028h */ + uint8_t vid_prog_lat;/* DPCD 00029h */ + uint8_t aud_del_ins1;/* DPCD 0002Bh */ + uint8_t aud_del_ins2;/* DPCD 0002Ch */ + uint8_t aud_del_ins3;/* DPCD 0002Dh */ +}; + +/*DP to VGA converter*/ +static const uint8_t DP_VGA_CONVERTER_ID_1[] = "mVGAa"; +/*DP to Dual link DVI converter*/ +static const uint8_t DP_DVI_CONVERTER_ID_1[] = "m2DVIa"; +/*Travis*/ +static const uint8_t DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT"; +/*Nutmeg*/ +static const uint8_t DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA"; +/*DP to VGA converter*/ +static const uint8_t DP_VGA_CONVERTER_ID_4[] = "DpVga"; +/*DP to Dual link DVI converter*/ +static const uint8_t DP_DVI_CONVERTER_ID_4[] = "m2DVIa"; +/*DP to Dual link DVI converter 2*/ +static const uint8_t DP_DVI_CONVERTER_ID_42[] = "v2DVIa"; + +static const uint8_t DP_SINK_DEV_STRING_ID2_REV0[] = "\0\0\0\0\0\0"; + +/* Identifies second generation PSR TCON from Parade: Device ID string: + * yy-xx-**-**-**-** + */ +/* xx - Hw ID high byte */ +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_HIGH_BYTE = + 0x06; + +/* yy - HW ID low byte, the same silicon has several package/feature flavors */ +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE1 = + 0x61; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE2 = + 0x62; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE3 = + 0x63; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE4 = + 0x72; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE5 = + 0x73; + +#endif /* __DAL_DDC_SERVICE_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/dpcd_defs.h b/drivers/gpu/drm/amd/display/include/dpcd_defs.h new file mode 100644 index 000000000000..d8e52e3b8e3c --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/dpcd_defs.h @@ -0,0 +1,149 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_DPCD_DEFS_H__ +#define __DAL_DPCD_DEFS_H__ + +#include + +enum dpcd_revision { + DPCD_REV_10 = 0x10, + DPCD_REV_11 = 0x11, + DPCD_REV_12 = 0x12, + DPCD_REV_13 = 0x13, + DPCD_REV_14 = 0x14 +}; + +/* these are the types stored at DOWNSTREAMPORT_PRESENT */ +enum dpcd_downstream_port_type { + DOWNSTREAM_DP = 0, + DOWNSTREAM_VGA, + DOWNSTREAM_DVI_HDMI, + DOWNSTREAM_NONDDC /* has no EDID (TV,CV) */ +}; + +enum dpcd_link_test_patterns { + LINK_TEST_PATTERN_NONE = 0, + LINK_TEST_PATTERN_COLOR_RAMP, + LINK_TEST_PATTERN_VERTICAL_BARS, + LINK_TEST_PATTERN_COLOR_SQUARES +}; + +enum dpcd_test_color_format { + TEST_COLOR_FORMAT_RGB = 0, + TEST_COLOR_FORMAT_YCBCR422, + TEST_COLOR_FORMAT_YCBCR444 +}; + +enum dpcd_test_bit_depth { + TEST_BIT_DEPTH_6 = 0, + TEST_BIT_DEPTH_8, + TEST_BIT_DEPTH_10, + TEST_BIT_DEPTH_12, + TEST_BIT_DEPTH_16 +}; + +/* PHY (encoder) test patterns +The order of test patterns follows DPCD register PHY_TEST_PATTERN (0x248) +*/ +enum dpcd_phy_test_patterns { + PHY_TEST_PATTERN_NONE = 0, + PHY_TEST_PATTERN_D10_2, + PHY_TEST_PATTERN_SYMBOL_ERROR, + PHY_TEST_PATTERN_PRBS7, + PHY_TEST_PATTERN_80BIT_CUSTOM,/* For DP1.2 only */ + PHY_TEST_PATTERN_CP2520_1, + PHY_TEST_PATTERN_CP2520_2, + PHY_TEST_PATTERN_CP2520_3, /* same as TPS4 */ +}; + +enum dpcd_test_dyn_range { + TEST_DYN_RANGE_VESA = 0, + TEST_DYN_RANGE_CEA +}; + +enum dpcd_audio_test_pattern { + AUDIO_TEST_PATTERN_OPERATOR_DEFINED = 0,/* direct HW translation */ + AUDIO_TEST_PATTERN_SAWTOOTH +}; + +enum dpcd_audio_sampling_rate { + AUDIO_SAMPLING_RATE_32KHZ = 0,/* direct HW translation */ + AUDIO_SAMPLING_RATE_44_1KHZ, + AUDIO_SAMPLING_RATE_48KHZ, + AUDIO_SAMPLING_RATE_88_2KHZ, + AUDIO_SAMPLING_RATE_96KHZ, + AUDIO_SAMPLING_RATE_176_4KHZ, + AUDIO_SAMPLING_RATE_192KHZ +}; + +enum dpcd_audio_channels { + AUDIO_CHANNELS_1 = 0,/* direct HW translation */ + AUDIO_CHANNELS_2, + AUDIO_CHANNELS_3, + AUDIO_CHANNELS_4, + AUDIO_CHANNELS_5, + AUDIO_CHANNELS_6, + AUDIO_CHANNELS_7, + AUDIO_CHANNELS_8, + + AUDIO_CHANNELS_COUNT +}; + +enum dpcd_audio_test_pattern_periods { + DPCD_AUDIO_TEST_PATTERN_PERIOD_NOTUSED = 0,/* direct HW translation */ + DPCD_AUDIO_TEST_PATTERN_PERIOD_3, + DPCD_AUDIO_TEST_PATTERN_PERIOD_6, + DPCD_AUDIO_TEST_PATTERN_PERIOD_12, + DPCD_AUDIO_TEST_PATTERN_PERIOD_24, + DPCD_AUDIO_TEST_PATTERN_PERIOD_48, + DPCD_AUDIO_TEST_PATTERN_PERIOD_96, + DPCD_AUDIO_TEST_PATTERN_PERIOD_192, + DPCD_AUDIO_TEST_PATTERN_PERIOD_384, + DPCD_AUDIO_TEST_PATTERN_PERIOD_768, + DPCD_AUDIO_TEST_PATTERN_PERIOD_1536 +}; + +/* This enum is for programming DPCD TRAINING_PATTERN_SET */ +enum dpcd_training_patterns { + DPCD_TRAINING_PATTERN_VIDEOIDLE = 0,/* direct HW translation! */ + DPCD_TRAINING_PATTERN_1, + DPCD_TRAINING_PATTERN_2, + DPCD_TRAINING_PATTERN_3, + DPCD_TRAINING_PATTERN_4 = 7 +}; + +/* This enum is for use with PsrSinkPsrStatus.bits.sinkSelfRefreshStatus +It defines the possible PSR states. */ +enum dpcd_psr_sink_states { + PSR_SINK_STATE_INACTIVE = 0, + PSR_SINK_STATE_ACTIVE_CAPTURE_DISPLAY_ON_SOURCE_TIMING = 1, + PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB = 2, + PSR_SINK_STATE_ACTIVE_CAPTURE_DISPLAY_ON_SINK_TIMING = 3, + PSR_SINK_STATE_ACTIVE_CAPTURE_TIMING_RESYNC = 4, + PSR_SINK_STATE_SINK_INTERNAL_ERROR = 7, +}; + +#endif /* __DAL_DPCD_DEFS_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/fixed31_32.h b/drivers/gpu/drm/amd/display/include/fixed31_32.h new file mode 100644 index 000000000000..3248f699daf2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/fixed31_32.h @@ -0,0 +1,466 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_FIXED31_32_H__ +#define __DAL_FIXED31_32_H__ + +#include "os_types.h" + +#define FIXED31_32_BITS_PER_FRACTIONAL_PART 32 + +/* + * @brief + * Arithmetic operations on real numbers + * represented as fixed-point numbers. + * There are: 1 bit for sign, + * 31 bit for integer part, + * 32 bits for fractional part. + * + * @note + * Currently, overflows and underflows are asserted; + * no special result returned. + */ + +struct fixed31_32 { + int64_t value; +}; + +/* + * @brief + * Useful constants + */ + +static const struct fixed31_32 dal_fixed31_32_zero = { 0 }; +static const struct fixed31_32 dal_fixed31_32_epsilon = { 1LL }; +static const struct fixed31_32 dal_fixed31_32_half = { 0x80000000LL }; +static const struct fixed31_32 dal_fixed31_32_one = { 0x100000000LL }; + +static const struct fixed31_32 dal_fixed31_32_pi = { 13493037705LL }; +static const struct fixed31_32 dal_fixed31_32_two_pi = { 26986075409LL }; +static const struct fixed31_32 dal_fixed31_32_e = { 11674931555LL }; +static const struct fixed31_32 dal_fixed31_32_ln2 = { 2977044471LL }; +static const struct fixed31_32 dal_fixed31_32_ln2_div_2 = { 1488522236LL }; + +/* + * @brief + * Initialization routines + */ + +/* + * @brief + * result = numerator / denominator + */ +struct fixed31_32 dal_fixed31_32_from_fraction( + int64_t numerator, + int64_t denominator); + +/* + * @brief + * result = arg + */ +struct fixed31_32 dal_fixed31_32_from_int_nonconst(int64_t arg); +static inline struct fixed31_32 dal_fixed31_32_from_int(int64_t arg) +{ + if (__builtin_constant_p(arg)) { + struct fixed31_32 res; + BUILD_BUG_ON((LONG_MIN > arg) || (arg > LONG_MAX)); + res.value = arg << FIXED31_32_BITS_PER_FRACTIONAL_PART; + return res; + } else + return dal_fixed31_32_from_int_nonconst(arg); +} + +/* + * @brief + * Unary operators + */ + +/* + * @brief + * result = -arg + */ +static inline struct fixed31_32 dal_fixed31_32_neg(struct fixed31_32 arg) +{ + struct fixed31_32 res; + + res.value = -arg.value; + + return res; +} + +/* + * @brief + * result = abs(arg) := (arg >= 0) ? arg : -arg + */ +static inline struct fixed31_32 dal_fixed31_32_abs(struct fixed31_32 arg) +{ + if (arg.value < 0) + return dal_fixed31_32_neg(arg); + else + return arg; +} + +/* + * @brief + * Binary relational operators + */ + +/* + * @brief + * result = arg1 < arg2 + */ +static inline bool dal_fixed31_32_lt(struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + return arg1.value < arg2.value; +} + +/* + * @brief + * result = arg1 <= arg2 + */ +static inline bool dal_fixed31_32_le(struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + return arg1.value <= arg2.value; +} + +/* + * @brief + * result = arg1 == arg2 + */ +static inline bool dal_fixed31_32_eq(struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + return arg1.value == arg2.value; +} + +/* + * @brief + * result = min(arg1, arg2) := (arg1 <= arg2) ? arg1 : arg2 + */ +static inline struct fixed31_32 dal_fixed31_32_min(struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + if (arg1.value <= arg2.value) + return arg1; + else + return arg2; +} + +/* + * @brief + * result = max(arg1, arg2) := (arg1 <= arg2) ? arg2 : arg1 + */ +static inline struct fixed31_32 dal_fixed31_32_max(struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + if (arg1.value <= arg2.value) + return arg2; + else + return arg1; +} + +/* + * @brief + * | min_value, when arg <= min_value + * result = | arg, when min_value < arg < max_value + * | max_value, when arg >= max_value + */ +static inline struct fixed31_32 dal_fixed31_32_clamp( + struct fixed31_32 arg, + struct fixed31_32 min_value, + struct fixed31_32 max_value) +{ + if (dal_fixed31_32_le(arg, min_value)) + return min_value; + else if (dal_fixed31_32_le(max_value, arg)) + return max_value; + else + return arg; +} + +/* + * @brief + * Binary shift operators + */ + +/* + * @brief + * result = arg << shift + */ +struct fixed31_32 dal_fixed31_32_shl( + struct fixed31_32 arg, + uint8_t shift); + +/* + * @brief + * result = arg >> shift + */ +static inline struct fixed31_32 dal_fixed31_32_shr( + struct fixed31_32 arg, + uint8_t shift) +{ + struct fixed31_32 res; + res.value = arg.value >> shift; + return res; +} + +/* + * @brief + * Binary additive operators + */ + +/* + * @brief + * result = arg1 + arg2 + */ +struct fixed31_32 dal_fixed31_32_add( + struct fixed31_32 arg1, + struct fixed31_32 arg2); + +/* + * @brief + * result = arg1 + arg2 + */ +static inline struct fixed31_32 dal_fixed31_32_add_int(struct fixed31_32 arg1, + int32_t arg2) +{ + return dal_fixed31_32_add(arg1, + dal_fixed31_32_from_int(arg2)); +} + +/* + * @brief + * result = arg1 - arg2 + */ +struct fixed31_32 dal_fixed31_32_sub( + struct fixed31_32 arg1, + struct fixed31_32 arg2); + +/* + * @brief + * result = arg1 - arg2 + */ +static inline struct fixed31_32 dal_fixed31_32_sub_int(struct fixed31_32 arg1, + int32_t arg2) +{ + return dal_fixed31_32_sub(arg1, + dal_fixed31_32_from_int(arg2)); +} + + +/* + * @brief + * Binary multiplicative operators + */ + +/* + * @brief + * result = arg1 * arg2 + */ +struct fixed31_32 dal_fixed31_32_mul( + struct fixed31_32 arg1, + struct fixed31_32 arg2); + + +/* + * @brief + * result = arg1 * arg2 + */ +static inline struct fixed31_32 dal_fixed31_32_mul_int(struct fixed31_32 arg1, + int32_t arg2) +{ + return dal_fixed31_32_mul(arg1, + dal_fixed31_32_from_int(arg2)); +} + +/* + * @brief + * result = square(arg) := arg * arg + */ +struct fixed31_32 dal_fixed31_32_sqr( + struct fixed31_32 arg); + +/* + * @brief + * result = arg1 / arg2 + */ +static inline struct fixed31_32 dal_fixed31_32_div_int(struct fixed31_32 arg1, + int64_t arg2) +{ + return dal_fixed31_32_from_fraction(arg1.value, + dal_fixed31_32_from_int(arg2).value); +} + +/* + * @brief + * result = arg1 / arg2 + */ +static inline struct fixed31_32 dal_fixed31_32_div(struct fixed31_32 arg1, + struct fixed31_32 arg2) +{ + return dal_fixed31_32_from_fraction(arg1.value, + arg2.value); +} + +/* + * @brief + * Reciprocal function + */ + +/* + * @brief + * result = reciprocal(arg) := 1 / arg + * + * @note + * No special actions taken in case argument is zero. + */ +struct fixed31_32 dal_fixed31_32_recip( + struct fixed31_32 arg); + +/* + * @brief + * Trigonometric functions + */ + +/* + * @brief + * result = sinc(arg) := sin(arg) / arg + * + * @note + * Argument specified in radians, + * internally it's normalized to [-2pi...2pi] range. + */ +struct fixed31_32 dal_fixed31_32_sinc( + struct fixed31_32 arg); + +/* + * @brief + * result = sin(arg) + * + * @note + * Argument specified in radians, + * internally it's normalized to [-2pi...2pi] range. + */ +struct fixed31_32 dal_fixed31_32_sin( + struct fixed31_32 arg); + +/* + * @brief + * result = cos(arg) + * + * @note + * Argument specified in radians + * and should be in [-2pi...2pi] range - + * passing arguments outside that range + * will cause incorrect result! + */ +struct fixed31_32 dal_fixed31_32_cos( + struct fixed31_32 arg); + +/* + * @brief + * Transcendent functions + */ + +/* + * @brief + * result = exp(arg) + * + * @note + * Currently, function is verified for abs(arg) <= 1. + */ +struct fixed31_32 dal_fixed31_32_exp( + struct fixed31_32 arg); + +/* + * @brief + * result = log(arg) + * + * @note + * Currently, abs(arg) should be less than 1. + * No normalization is done. + * Currently, no special actions taken + * in case of invalid argument(s). Take care! + */ +struct fixed31_32 dal_fixed31_32_log( + struct fixed31_32 arg); + +/* + * @brief + * Power function + */ + +/* + * @brief + * result = pow(arg1, arg2) + * + * @note + * Currently, abs(arg1) should be less than 1. Take care! + */ +struct fixed31_32 dal_fixed31_32_pow( + struct fixed31_32 arg1, + struct fixed31_32 arg2); + +/* + * @brief + * Rounding functions + */ + +/* + * @brief + * result = floor(arg) := greatest integer lower than or equal to arg + */ +int32_t dal_fixed31_32_floor( + struct fixed31_32 arg); + +/* + * @brief + * result = round(arg) := integer nearest to arg + */ +int32_t dal_fixed31_32_round( + struct fixed31_32 arg); + +/* + * @brief + * result = ceil(arg) := lowest integer greater than or equal to arg + */ +int32_t dal_fixed31_32_ceil( + struct fixed31_32 arg); + +/* the following two function are used in scaler hw programming to convert fixed + * point value to format 2 bits from integer part and 19 bits from fractional + * part. The same applies for u0d19, 0 bits from integer part and 19 bits from + * fractional + */ + +uint32_t dal_fixed31_32_u2d19( + struct fixed31_32 arg); + +uint32_t dal_fixed31_32_u0d19( + struct fixed31_32 arg); + +#endif diff --git a/drivers/gpu/drm/amd/display/include/fixed32_32.h b/drivers/gpu/drm/amd/display/include/fixed32_32.h new file mode 100644 index 000000000000..9c70341fe026 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/fixed32_32.h @@ -0,0 +1,129 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#ifndef __DAL_FIXED32_32_H__ +#define __DAL_FIXED32_32_H__ + +#include "os_types.h" + +struct fixed32_32 { + uint64_t value; +}; + +static const struct fixed32_32 dal_fixed32_32_zero = { 0 }; +static const struct fixed32_32 dal_fixed32_32_one = { 0x100000000LL }; +static const struct fixed32_32 dal_fixed32_32_half = { 0x80000000LL }; + +struct fixed32_32 dal_fixed32_32_from_fraction(uint32_t n, uint32_t d); +static inline struct fixed32_32 dal_fixed32_32_from_int(uint32_t value) +{ + struct fixed32_32 fx; + + fx.value = (uint64_t)value<<32; + return fx; +} + +struct fixed32_32 dal_fixed32_32_add( + struct fixed32_32 lhs, + struct fixed32_32 rhs); +struct fixed32_32 dal_fixed32_32_add_int( + struct fixed32_32 lhs, + uint32_t rhs); +struct fixed32_32 dal_fixed32_32_sub( + struct fixed32_32 lhs, + struct fixed32_32 rhs); +struct fixed32_32 dal_fixed32_32_sub_int( + struct fixed32_32 lhs, + uint32_t rhs); +struct fixed32_32 dal_fixed32_32_mul( + struct fixed32_32 lhs, + struct fixed32_32 rhs); +struct fixed32_32 dal_fixed32_32_mul_int( + struct fixed32_32 lhs, + uint32_t rhs); +struct fixed32_32 dal_fixed32_32_div( + struct fixed32_32 lhs, + struct fixed32_32 rhs); +struct fixed32_32 dal_fixed32_32_div_int( + struct fixed32_32 lhs, + uint32_t rhs); + +static inline struct fixed32_32 dal_fixed32_32_min(struct fixed32_32 lhs, + struct fixed32_32 rhs) +{ + return (lhs.value < rhs.value) ? lhs : rhs; +} + +static inline struct fixed32_32 dal_fixed32_32_max(struct fixed32_32 lhs, + struct fixed32_32 rhs) +{ + return (lhs.value > rhs.value) ? lhs : rhs; +} + +static inline bool dal_fixed32_32_gt(struct fixed32_32 lhs, struct fixed32_32 rhs) +{ + return lhs.value > rhs.value; +} + +static inline bool dal_fixed32_32_gt_int(struct fixed32_32 lhs, uint32_t rhs) +{ + return lhs.value > ((uint64_t)rhs<<32); +} + +static inline bool dal_fixed32_32_lt(struct fixed32_32 lhs, struct fixed32_32 rhs) +{ + return lhs.value < rhs.value; +} + +static inline bool dal_fixed32_32_lt_int(struct fixed32_32 lhs, uint32_t rhs) +{ + return lhs.value < ((uint64_t)rhs<<32); +} + +static inline bool dal_fixed32_32_le(struct fixed32_32 lhs, struct fixed32_32 rhs) +{ + return lhs.value <= rhs.value; +} + +static inline bool dal_fixed32_32_le_int(struct fixed32_32 lhs, uint32_t rhs) +{ + return lhs.value <= ((uint64_t)rhs<<32); +} + +static inline bool dal_fixed32_32_eq(struct fixed32_32 lhs, struct fixed32_32 rhs) +{ + return lhs.value == rhs.value; +} + +uint32_t dal_fixed32_32_ceil(struct fixed32_32 value); +static inline uint32_t dal_fixed32_32_floor(struct fixed32_32 value) +{ + return value.value>>32; +} + +uint32_t dal_fixed32_32_round(struct fixed32_32 value); + +#endif diff --git a/drivers/gpu/drm/amd/display/include/gpio_interface.h b/drivers/gpu/drm/amd/display/include/gpio_interface.h new file mode 100644 index 000000000000..e4fd31024b92 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/gpio_interface.h @@ -0,0 +1,92 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GPIO_INTERFACE_H__ +#define __DAL_GPIO_INTERFACE_H__ + +#include "gpio_types.h" +#include "grph_object_defs.h" + +struct gpio; + +/* Open the handle for future use */ +enum gpio_result dal_gpio_open( + struct gpio *gpio, + enum gpio_mode mode); + +enum gpio_result dal_gpio_open_ex( + struct gpio *gpio, + enum gpio_mode mode); + +/* Get high or low from the pin */ +enum gpio_result dal_gpio_get_value( + const struct gpio *gpio, + uint32_t *value); + +/* Set pin high or low */ +enum gpio_result dal_gpio_set_value( + const struct gpio *gpio, + uint32_t value); + +/* Get current mode */ +enum gpio_mode dal_gpio_get_mode( + const struct gpio *gpio); + +/* Change mode of the handle */ +enum gpio_result dal_gpio_change_mode( + struct gpio *gpio, + enum gpio_mode mode); + +/* Get the GPIO id */ +enum gpio_id dal_gpio_get_id( + const struct gpio *gpio); + +/* Get the GPIO enum */ +uint32_t dal_gpio_get_enum( + const struct gpio *gpio); + +/* Set the GPIO pin configuration */ +enum gpio_result dal_gpio_set_config( + struct gpio *gpio, + const struct gpio_config_data *config_data); + +/* Obtain GPIO pin info */ +enum gpio_result dal_gpio_get_pin_info( + const struct gpio *gpio, + struct gpio_pin_info *pin_info); + +/* Obtain GPIO sync source */ +enum sync_source dal_gpio_get_sync_source( + const struct gpio *gpio); + +/* Obtain GPIO pin output state (active low or active high) */ +enum gpio_pin_output_state dal_gpio_get_output_state( + const struct gpio *gpio); + +/* Close the handle */ +void dal_gpio_close( + struct gpio *gpio); + +#endif diff --git a/drivers/gpu/drm/amd/display/include/gpio_service_interface.h b/drivers/gpu/drm/amd/display/include/gpio_service_interface.h new file mode 100644 index 000000000000..f40259bade40 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/gpio_service_interface.h @@ -0,0 +1,105 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GPIO_SERVICE_INTERFACE_H__ +#define __DAL_GPIO_SERVICE_INTERFACE_H__ + +#include "gpio_types.h" +#include "gpio_interface.h" +#include "hw/gpio.h" + +struct gpio_service; + +struct gpio *dal_gpio_create( + struct gpio_service *service, + enum gpio_id id, + uint32_t en, + enum gpio_pin_output_state output_state); + +void dal_gpio_destroy( + struct gpio **ptr); + +struct gpio_service *dal_gpio_service_create( + enum dce_version dce_version_major, + enum dce_version dce_version_minor, + struct dc_context *ctx); + +struct gpio *dal_gpio_service_create_irq( + struct gpio_service *service, + uint32_t offset, + uint32_t mask); + +struct ddc *dal_gpio_create_ddc( + struct gpio_service *service, + uint32_t offset, + uint32_t mask, + struct gpio_ddc_hw_info *info); + + +void dal_gpio_destroy_ddc( + struct ddc **ddc); + +void dal_gpio_service_destroy( + struct gpio_service **ptr); + +enum dc_irq_source dal_irq_get_source( + const struct gpio *irq); + +enum dc_irq_source dal_irq_get_rx_source( + const struct gpio *irq); + +enum gpio_result dal_irq_setup_hpd_filter( + struct gpio *irq, + struct gpio_hpd_config *config); + +struct gpio *dal_gpio_create_irq( + struct gpio_service *service, + enum gpio_id id, + uint32_t en); + +void dal_gpio_destroy_irq( + struct gpio **ptr); + + +enum gpio_result dal_ddc_open( + struct ddc *ddc, + enum gpio_mode mode, + enum gpio_ddc_config_type config_type); + +enum gpio_result dal_ddc_change_mode( + struct ddc *ddc, + enum gpio_mode mode); + +enum gpio_ddc_line dal_ddc_get_line( + const struct ddc *ddc); + +enum gpio_result dal_ddc_set_config( + struct ddc *ddc, + enum gpio_ddc_config_type config_type); + +void dal_ddc_close( + struct ddc *ddc); + +#endif diff --git a/drivers/gpu/drm/amd/display/include/gpio_types.h b/drivers/gpu/drm/amd/display/include/gpio_types.h new file mode 100644 index 000000000000..8dd46ed799e5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/gpio_types.h @@ -0,0 +1,332 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GPIO_TYPES_H__ +#define __DAL_GPIO_TYPES_H__ + +#define BUNDLE_A_MASK 0x00FFF000L +#define BUNDLE_B_MASK 0x00000FFFL + +/* + * gpio_result + * + * @brief + * The possible return codes that the GPIO object can return. + * These return codes can be generated + * directly by the GPIO object or from the GPIOPin object. + */ +enum gpio_result { + GPIO_RESULT_OK, + GPIO_RESULT_NULL_HANDLE, + GPIO_RESULT_INVALID_DATA, + GPIO_RESULT_DEVICE_BUSY, + GPIO_RESULT_OPEN_FAILED, + GPIO_RESULT_ALREADY_OPENED, + GPIO_RESULT_NON_SPECIFIC_ERROR +}; + +/* + * @brief + * Used to identify the specific GPIO device + * + * @notes + * These constants are used as indices in a vector. + * Thus they should start from zero and be contiguous. + */ +enum gpio_id { + GPIO_ID_UNKNOWN = (-1), + GPIO_ID_DDC_DATA, + GPIO_ID_DDC_CLOCK, + GPIO_ID_GENERIC, + GPIO_ID_HPD, + GPIO_ID_GPIO_PAD, + GPIO_ID_VIP_PAD, + GPIO_ID_SYNC, + GPIO_ID_GSL, /* global swap lock */ + GPIO_ID_COUNT, + GPIO_ID_MIN = GPIO_ID_DDC_DATA, + GPIO_ID_MAX = GPIO_ID_GSL +}; + +#define GPIO_ENUM_UNKNOWN \ + 32 + +struct gpio_pin_info { + uint32_t offset; + uint32_t offset_y; + uint32_t offset_en; + uint32_t offset_mask; + + uint32_t mask; + uint32_t mask_y; + uint32_t mask_en; + uint32_t mask_mask; +}; + +enum gpio_pin_output_state { + GPIO_PIN_OUTPUT_STATE_ACTIVE_LOW, + GPIO_PIN_OUTPUT_STATE_ACTIVE_HIGH, + GPIO_PIN_OUTPUT_STATE_DEFAULT = GPIO_PIN_OUTPUT_STATE_ACTIVE_LOW +}; + +enum gpio_generic { + GPIO_GENERIC_UNKNOWN = (-1), + GPIO_GENERIC_A, + GPIO_GENERIC_B, + GPIO_GENERIC_C, + GPIO_GENERIC_D, + GPIO_GENERIC_E, + GPIO_GENERIC_F, + GPIO_GENERIC_G, + GPIO_GENERIC_COUNT, + GPIO_GENERIC_MIN = GPIO_GENERIC_A, + GPIO_GENERIC_MAX = GPIO_GENERIC_B +}; + +enum gpio_hpd { + GPIO_HPD_UNKNOWN = (-1), + GPIO_HPD_1, + GPIO_HPD_2, + GPIO_HPD_3, + GPIO_HPD_4, + GPIO_HPD_5, + GPIO_HPD_6, + GPIO_HPD_COUNT, + GPIO_HPD_MIN = GPIO_HPD_1, + GPIO_HPD_MAX = GPIO_HPD_6 +}; + +enum gpio_gpio_pad { + GPIO_GPIO_PAD_UNKNOWN = (-1), + GPIO_GPIO_PAD_0, + GPIO_GPIO_PAD_1, + GPIO_GPIO_PAD_2, + GPIO_GPIO_PAD_3, + GPIO_GPIO_PAD_4, + GPIO_GPIO_PAD_5, + GPIO_GPIO_PAD_6, + GPIO_GPIO_PAD_7, + GPIO_GPIO_PAD_8, + GPIO_GPIO_PAD_9, + GPIO_GPIO_PAD_10, + GPIO_GPIO_PAD_11, + GPIO_GPIO_PAD_12, + GPIO_GPIO_PAD_13, + GPIO_GPIO_PAD_14, + GPIO_GPIO_PAD_15, + GPIO_GPIO_PAD_16, + GPIO_GPIO_PAD_17, + GPIO_GPIO_PAD_18, + GPIO_GPIO_PAD_19, + GPIO_GPIO_PAD_20, + GPIO_GPIO_PAD_21, + GPIO_GPIO_PAD_22, + GPIO_GPIO_PAD_23, + GPIO_GPIO_PAD_24, + GPIO_GPIO_PAD_25, + GPIO_GPIO_PAD_26, + GPIO_GPIO_PAD_27, + GPIO_GPIO_PAD_28, + GPIO_GPIO_PAD_29, + GPIO_GPIO_PAD_30, + GPIO_GPIO_PAD_COUNT, + GPIO_GPIO_PAD_MIN = GPIO_GPIO_PAD_0, + GPIO_GPIO_PAD_MAX = GPIO_GPIO_PAD_30 +}; + +enum gpio_vip_pad { + GPIO_VIP_PAD_UNKNOWN = (-1), + /* following never used - + * GPIO_ID_DDC_CLOCK::GPIO_DDC_LINE_VIP_PAD defined instead */ + GPIO_VIP_PAD_SCL, + /* following never used - + * GPIO_ID_DDC_DATA::GPIO_DDC_LINE_VIP_PAD defined instead */ + GPIO_VIP_PAD_SDA, + GPIO_VIP_PAD_VHAD, + GPIO_VIP_PAD_VPHCTL, + GPIO_VIP_PAD_VIPCLK, + GPIO_VIP_PAD_VID, + GPIO_VIP_PAD_VPCLK0, + GPIO_VIP_PAD_DVALID, + GPIO_VIP_PAD_PSYNC, + GPIO_VIP_PAD_COUNT, + GPIO_VIP_PAD_MIN = GPIO_VIP_PAD_SCL, + GPIO_VIP_PAD_MAX = GPIO_VIP_PAD_PSYNC +}; + +enum gpio_sync { + GPIO_SYNC_UNKNOWN = (-1), + GPIO_SYNC_HSYNC_A, + GPIO_SYNC_VSYNC_A, + GPIO_SYNC_HSYNC_B, + GPIO_SYNC_VSYNC_B, + GPIO_SYNC_COUNT, + GPIO_SYNC_MIN = GPIO_SYNC_HSYNC_A, + GPIO_SYNC_MAX = GPIO_SYNC_VSYNC_B +}; + +enum gpio_gsl { + GPIO_GSL_UNKNOWN = (-1), + GPIO_GSL_GENLOCK_CLOCK, + GPIO_GSL_GENLOCK_VSYNC, + GPIO_GSL_SWAPLOCK_A, + GPIO_GSL_SWAPLOCK_B, + GPIO_GSL_COUNT, + GPIO_GSL_MIN = GPIO_GSL_GENLOCK_CLOCK, + GPIO_GSL_MAX = GPIO_GSL_SWAPLOCK_B +}; + +/* + * @brief + * Unique Id for DDC handle. + * Values are meaningful (used as indexes to array) + */ +enum gpio_ddc_line { + GPIO_DDC_LINE_UNKNOWN = (-1), + GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_DDC2, + GPIO_DDC_LINE_DDC3, + GPIO_DDC_LINE_DDC4, + GPIO_DDC_LINE_DDC5, + GPIO_DDC_LINE_DDC6, + GPIO_DDC_LINE_DDC_VGA, + GPIO_DDC_LINE_VIP_PAD, + GPIO_DDC_LINE_I2C_PAD = GPIO_DDC_LINE_VIP_PAD, + GPIO_DDC_LINE_COUNT, + GPIO_DDC_LINE_MIN = GPIO_DDC_LINE_DDC1, + GPIO_DDC_LINE_MAX = GPIO_DDC_LINE_I2C_PAD +}; + +/* + * @brief + * Identifies the mode of operation to open a GPIO device. + * A GPIO device (pin) can be programmed in only one of these modes at a time. + */ +enum gpio_mode { + GPIO_MODE_UNKNOWN = (-1), + GPIO_MODE_INPUT, + GPIO_MODE_OUTPUT, + GPIO_MODE_FAST_OUTPUT, + GPIO_MODE_HARDWARE, + GPIO_MODE_INTERRUPT +}; + +/* + * @brief + * Identifies the source of the signal when GPIO is in HW mode. + * get_signal_source() will return GPIO_SYGNAL_SOURCE__UNKNOWN + * when one of the following holds: + * 1. GPIO is input GPIO + * 2. GPIO is not opened in HW mode + * 3. GPIO does not have fixed signal source + * (like DC_GenericA have mux instead fixed) + */ +enum gpio_signal_source { + GPIO_SIGNAL_SOURCE_UNKNOWN = (-1), + GPIO_SIGNAL_SOURCE_DACA_STEREO_SYNC, + GPIO_SIGNAL_SOURCE_PASS_THROUGH_STEREO_SYNC, + GPIO_SIGNAL_SOURCE_DACB_STEREO_SYNC, + GPIO_SIGNAL_SOURCE_DACA_HSYNC, + GPIO_SIGNAL_SOURCE_DACB_HSYNC, + GPIO_SIGNAL_SOURCE_DACA_VSYNC, + GPIO_SIGNAL_SOURCE_DACB_VSYNC, +}; + +enum gpio_stereo_source { + GPIO_STEREO_SOURCE_UNKNOWN = (-1), + GPIO_STEREO_SOURCE_D1, + GPIO_STEREO_SOURCE_D2, + GPIO_STEREO_SOURCE_D3, + GPIO_STEREO_SOURCE_D4, + GPIO_STEREO_SOURCE_D5, + GPIO_STEREO_SOURCE_D6 +}; + +/* + * GPIO config + */ + +enum gpio_config_type { + GPIO_CONFIG_TYPE_NONE, + GPIO_CONFIG_TYPE_DDC, + GPIO_CONFIG_TYPE_HPD, + GPIO_CONFIG_TYPE_GENERIC_MUX, + GPIO_CONFIG_TYPE_GSL_MUX, + GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE +}; + +/* DDC configuration */ + +enum gpio_ddc_config_type { + GPIO_DDC_CONFIG_TYPE_MODE_AUX, + GPIO_DDC_CONFIG_TYPE_MODE_I2C, + GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT, + GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT, + GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING +}; + +struct gpio_ddc_config { + enum gpio_ddc_config_type type; + bool data_en_bit_present; + bool clock_en_bit_present; +}; + +/* HPD configuration */ + +struct gpio_hpd_config { + uint32_t delay_on_connect; /* milliseconds */ + uint32_t delay_on_disconnect; /* milliseconds */ +}; + +struct gpio_generic_mux_config { + bool enable_output_from_mux; + enum gpio_signal_source mux_select; + enum gpio_stereo_source stereo_select; +}; + +enum gpio_gsl_mux_config_type { + GPIO_GSL_MUX_CONFIG_TYPE_DISABLE, + GPIO_GSL_MUX_CONFIG_TYPE_TIMING_SYNC, + GPIO_GSL_MUX_CONFIG_TYPE_FLIP_SYNC +}; + +struct gpio_gsl_mux_config { + enum gpio_gsl_mux_config_type type; + /* Actually sync_source type, + * however we want to avoid inter-component includes here */ + uint32_t gsl_group; +}; + +struct gpio_config_data { + enum gpio_config_type type; + union { + struct gpio_ddc_config ddc; + struct gpio_hpd_config hpd; + struct gpio_generic_mux_config generic_mux; + struct gpio_gsl_mux_config gsl_mux; + } config; +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h new file mode 100644 index 000000000000..7a9b43f84a31 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h @@ -0,0 +1,445 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GRPH_OBJECT_CTRL_DEFS_H__ +#define __DAL_GRPH_OBJECT_CTRL_DEFS_H__ + +#include "grph_object_defs.h" + +/* + * ##################################################### + * ##################################################### + * + * These defines shared between asic_control/bios_parser and other + * DAL components + * + * ##################################################### + * ##################################################### + */ + +enum display_output_bit_depth { + PANEL_UNDEFINE = 0, + PANEL_6BIT_COLOR = 1, + PANEL_8BIT_COLOR = 2, + PANEL_10BIT_COLOR = 3, + PANEL_12BIT_COLOR = 4, + PANEL_16BIT_COLOR = 5, +}; + + +/* Device type as abstracted by ATOM BIOS */ +enum dal_device_type { + DEVICE_TYPE_UNKNOWN = 0, + DEVICE_TYPE_LCD, + DEVICE_TYPE_CRT, + DEVICE_TYPE_DFP, + DEVICE_TYPE_CV, + DEVICE_TYPE_TV, + DEVICE_TYPE_CF, + DEVICE_TYPE_WIRELESS +}; + +/* Device ID as abstracted by ATOM BIOS */ +struct device_id { + enum dal_device_type device_type:16; + uint32_t enum_id:16; /* 1 based enum */ + uint16_t raw_device_tag; +}; + +struct graphics_object_i2c_info { + struct gpio_info { + uint32_t clk_mask_register_index; + uint32_t clk_en_register_index; + uint32_t clk_y_register_index; + uint32_t clk_a_register_index; + uint32_t data_mask_register_index; + uint32_t data_en_register_index; + uint32_t data_y_register_index; + uint32_t data_a_register_index; + + uint32_t clk_mask_shift; + uint32_t clk_en_shift; + uint32_t clk_y_shift; + uint32_t clk_a_shift; + uint32_t data_mask_shift; + uint32_t data_en_shift; + uint32_t data_y_shift; + uint32_t data_a_shift; + } gpio_info; + + bool i2c_hw_assist; + uint32_t i2c_line; + uint32_t i2c_engine_id; + uint32_t i2c_slave_address; +}; + +struct graphics_object_hpd_info { + uint8_t hpd_int_gpio_uid; + uint8_t hpd_active; +}; + +struct connector_device_tag_info { + uint32_t acpi_device; + struct device_id dev_id; +}; + +struct device_timing { + struct misc_info { + uint32_t HORIZONTAL_CUT_OFF:1; + /* 0=Active High, 1=Active Low */ + uint32_t H_SYNC_POLARITY:1; + /* 0=Active High, 1=Active Low */ + uint32_t V_SYNC_POLARITY:1; + uint32_t VERTICAL_CUT_OFF:1; + uint32_t H_REPLICATION_BY2:1; + uint32_t V_REPLICATION_BY2:1; + uint32_t COMPOSITE_SYNC:1; + uint32_t INTERLACE:1; + uint32_t DOUBLE_CLOCK:1; + uint32_t RGB888:1; + uint32_t GREY_LEVEL:2; + uint32_t SPATIAL:1; + uint32_t TEMPORAL:1; + uint32_t API_ENABLED:1; + } misc_info; + + uint32_t pixel_clk; /* in KHz */ + uint32_t horizontal_addressable; + uint32_t horizontal_blanking_time; + uint32_t vertical_addressable; + uint32_t vertical_blanking_time; + uint32_t horizontal_sync_offset; + uint32_t horizontal_sync_width; + uint32_t vertical_sync_offset; + uint32_t vertical_sync_width; + uint32_t horizontal_border; + uint32_t vertical_border; +}; + +struct supported_refresh_rate { + uint32_t REFRESH_RATE_30HZ:1; + uint32_t REFRESH_RATE_40HZ:1; + uint32_t REFRESH_RATE_48HZ:1; + uint32_t REFRESH_RATE_50HZ:1; + uint32_t REFRESH_RATE_60HZ:1; +}; + +struct embedded_panel_info { + struct device_timing lcd_timing; + uint32_t ss_id; + struct supported_refresh_rate supported_rr; + uint32_t drr_enabled; + uint32_t min_drr_refresh_rate; + bool realtek_eDPToLVDS; +}; + +struct dc_firmware_info { + struct pll_info { + uint32_t crystal_frequency; /* in KHz */ + uint32_t min_input_pxl_clk_pll_frequency; /* in KHz */ + uint32_t max_input_pxl_clk_pll_frequency; /* in KHz */ + uint32_t min_output_pxl_clk_pll_frequency; /* in KHz */ + uint32_t max_output_pxl_clk_pll_frequency; /* in KHz */ + } pll_info; + + struct firmware_feature { + uint32_t memory_clk_ss_percentage; + uint32_t engine_clk_ss_percentage; + } feature; + + uint32_t default_display_engine_pll_frequency; /* in KHz */ + uint32_t external_clock_source_frequency_for_dp; /* in KHz */ + uint32_t smu_gpu_pll_output_freq; /* in KHz */ + uint8_t min_allowed_bl_level; + uint8_t remote_display_config; + uint32_t default_memory_clk; /* in KHz */ + uint32_t default_engine_clk; /* in KHz */ + uint32_t dp_phy_ref_clk; /* in KHz - DCE12 only */ + uint32_t i2c_engine_ref_clk; /* in KHz - DCE12 only */ + + +}; + +struct step_and_delay_info { + uint32_t step; + uint32_t delay; + uint32_t recommended_ref_div; +}; + +struct spread_spectrum_info { + struct spread_spectrum_type { + bool CENTER_MODE:1; + bool EXTERNAL:1; + bool STEP_AND_DELAY_INFO:1; + } type; + + /* in unit of 0.01% (spreadPercentageDivider = 100), + otherwise in 0.001% units (spreadPercentageDivider = 1000); */ + uint32_t spread_spectrum_percentage; + uint32_t spread_percentage_divider; /* 100 or 1000 */ + uint32_t spread_spectrum_range; /* modulation freq (HZ)*/ + + union { + struct step_and_delay_info step_and_delay_info; + /* For mem/engine/uvd, Clock Out frequence (VCO ), + in unit of kHz. For TMDS/HDMI/LVDS, it is pixel clock, + for DP, it is link clock ( 270000 or 162000 ) */ + uint32_t target_clock_range; /* in KHz */ + }; + +}; + +struct graphics_object_encoder_cap_info { + uint32_t dp_hbr2_cap:1; + uint32_t dp_hbr2_validated:1; + /* + * TODO: added MST and HDMI 6G capable flags + */ + uint32_t reserved:15; +}; + +struct din_connector_info { + uint32_t gpio_id; + bool gpio_tv_active_state; +}; + +/* Invalid channel mapping */ +enum { INVALID_DDI_CHANNEL_MAPPING = 0x0 }; + +/** + * DDI PHY channel mapping reflecting XBAR setting + */ +union ddi_channel_mapping { + struct mapping { + uint8_t lane0:2; /* Mapping for lane 0 */ + uint8_t lane1:2; /* Mapping for lane 1 */ + uint8_t lane2:2; /* Mapping for lane 2 */ + uint8_t lane3:2; /* Mapping for lane 3 */ + } mapping; + uint8_t raw; +}; + +/** +* Transmitter output configuration description +*/ +struct transmitter_configuration_info { + /* DDI PHY ID for the transmitter */ + enum transmitter transmitter_phy_id; + /* DDI PHY channel mapping reflecting crossbar setting */ + union ddi_channel_mapping output_channel_mapping; +}; + +struct transmitter_configuration { + /* Configuration for the primary transmitter */ + struct transmitter_configuration_info primary_transmitter_config; + /* Secondary transmitter configuration for Dual-link DVI */ + struct transmitter_configuration_info secondary_transmitter_config; +}; + +/* These size should be sufficient to store info coming from BIOS */ +#define NUMBER_OF_UCHAR_FOR_GUID 16 +#define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 +#define NUMBER_OF_CSR_M3_ARB 10 +#define NUMBER_OF_DISP_CLK_VOLTAGE 4 +#define NUMBER_OF_AVAILABLE_SCLK 5 + +struct i2c_reg_info { + unsigned char i2c_reg_index; + unsigned char i2c_reg_val; +}; + +struct ext_hdmi_settings { + unsigned char slv_addr; + unsigned char reg_num; + struct i2c_reg_info reg_settings[9]; + unsigned char reg_num_6g; + struct i2c_reg_info reg_settings_6g[3]; +}; + + +/* V6 */ +struct integrated_info { + struct clock_voltage_caps { + /* The Voltage Index indicated by FUSE, same voltage index + shared with SCLK DPM fuse table */ + uint32_t voltage_index; + /* Maximum clock supported with specified voltage index */ + uint32_t max_supported_clk; /* in KHz */ + } disp_clk_voltage[NUMBER_OF_DISP_CLK_VOLTAGE]; + + struct display_connection_info { + struct external_display_path { + /* A bit vector to show what devices are supported */ + uint32_t device_tag; + /* 16bit device ACPI id. */ + uint32_t device_acpi_enum; + /* A physical connector for displays to plug in, + using object connector definitions */ + struct graphics_object_id device_connector_id; + /* An index into external AUX/DDC channel LUT */ + uint8_t ext_aux_ddc_lut_index; + /* An index into external HPD pin LUT */ + uint8_t ext_hpd_pin_lut_index; + /* external encoder object id */ + struct graphics_object_id ext_encoder_obj_id; + /* XBAR mapping of the PHY channels */ + union ddi_channel_mapping channel_mapping; + + unsigned short caps; + } path[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; + + uint8_t gu_id[NUMBER_OF_UCHAR_FOR_GUID]; + uint8_t checksum; + } ext_disp_conn_info; /* exiting long long time */ + + struct available_s_clk_list { + /* Maximum clock supported with specified voltage index */ + uint32_t supported_s_clk; /* in KHz */ + /* The Voltage Index indicated by FUSE for specified SCLK */ + uint32_t voltage_index; + /* The Voltage ID indicated by FUSE for specified SCLK */ + uint32_t voltage_id; + } avail_s_clk[NUMBER_OF_AVAILABLE_SCLK]; + + uint8_t memory_type; + uint8_t ma_channel_number; + uint32_t boot_up_engine_clock; /* in KHz */ + uint32_t dentist_vco_freq; /* in KHz */ + uint32_t boot_up_uma_clock; /* in KHz */ + uint32_t boot_up_req_display_vector; + uint32_t other_display_misc; + uint32_t gpu_cap_info; + uint32_t sb_mmio_base_addr; + uint32_t system_config; + uint32_t cpu_cap_info; + uint32_t max_nb_voltage; + uint32_t min_nb_voltage; + uint32_t boot_up_nb_voltage; + uint32_t ext_disp_conn_info_offset; + uint32_t csr_m3_arb_cntl_default[NUMBER_OF_CSR_M3_ARB]; + uint32_t csr_m3_arb_cntl_uvd[NUMBER_OF_CSR_M3_ARB]; + uint32_t csr_m3_arb_cntl_fs3d[NUMBER_OF_CSR_M3_ARB]; + uint32_t gmc_restore_reset_time; + uint32_t minimum_n_clk; + uint32_t idle_n_clk; + uint32_t ddr_dll_power_up_time; + uint32_t ddr_pll_power_up_time; + /* start for V6 */ + uint32_t pcie_clk_ss_type; + uint32_t lvds_ss_percentage; + uint32_t lvds_sspread_rate_in_10hz; + uint32_t hdmi_ss_percentage; + uint32_t hdmi_sspread_rate_in_10hz; + uint32_t dvi_ss_percentage; + uint32_t dvi_sspread_rate_in_10_hz; + uint32_t sclk_dpm_boost_margin; + uint32_t sclk_dpm_throttle_margin; + uint32_t sclk_dpm_tdp_limit_pg; + uint32_t sclk_dpm_tdp_limit_boost; + uint32_t boost_engine_clock; + uint32_t boost_vid_2bit; + uint32_t enable_boost; + uint32_t gnb_tdp_limit; + /* Start from V7 */ + uint32_t max_lvds_pclk_freq_in_single_link; + uint32_t lvds_misc; + uint32_t lvds_pwr_on_seq_dig_on_to_de_in_4ms; + uint32_t lvds_pwr_on_seq_de_to_vary_bl_in_4ms; + uint32_t lvds_pwr_off_seq_vary_bl_to_de_in4ms; + uint32_t lvds_pwr_off_seq_de_to_dig_on_in4ms; + uint32_t lvds_off_to_on_delay_in_4ms; + uint32_t lvds_pwr_on_seq_vary_bl_to_blon_in_4ms; + uint32_t lvds_pwr_off_seq_blon_to_vary_bl_in_4ms; + uint32_t lvds_reserved1; + uint32_t lvds_bit_depth_control_val; + //Start from V9 + unsigned char dp0_ext_hdmi_slv_addr; + unsigned char dp0_ext_hdmi_reg_num; + struct i2c_reg_info dp0_ext_hdmi_reg_settings[9]; + unsigned char dp0_ext_hdmi_6g_reg_num; + struct i2c_reg_info dp0_ext_hdmi_6g_reg_settings[3]; + unsigned char dp1_ext_hdmi_slv_addr; + unsigned char dp1_ext_hdmi_reg_num; + struct i2c_reg_info dp1_ext_hdmi_reg_settings[9]; + unsigned char dp1_ext_hdmi_6g_reg_num; + struct i2c_reg_info dp1_ext_hdmi_6g_reg_settings[3]; + unsigned char dp2_ext_hdmi_slv_addr; + unsigned char dp2_ext_hdmi_reg_num; + struct i2c_reg_info dp2_ext_hdmi_reg_settings[9]; + unsigned char dp2_ext_hdmi_6g_reg_num; + struct i2c_reg_info dp2_ext_hdmi_6g_reg_settings[3]; + unsigned char dp3_ext_hdmi_slv_addr; + unsigned char dp3_ext_hdmi_reg_num; + struct i2c_reg_info dp3_ext_hdmi_reg_settings[9]; + unsigned char dp3_ext_hdmi_6g_reg_num; + struct i2c_reg_info dp3_ext_hdmi_6g_reg_settings[3]; +}; + +/** +* Power source ids. +*/ +enum power_source { + POWER_SOURCE_AC = 0, + POWER_SOURCE_DC, + POWER_SOURCE_LIMITED_POWER, + POWER_SOURCE_LIMITED_POWER_2, + POWER_SOURCE_MAX +}; + +struct bios_event_info { + uint32_t thermal_state; + uint32_t backlight_level; + enum power_source powerSource; + bool has_thermal_state_changed; + bool has_power_source_changed; + bool has_forced_mode_changed; + bool forced_mode; + bool backlight_changed; +}; + +enum { + HDMI_PIXEL_CLOCK_IN_KHZ_297 = 297000, + TMDS_PIXEL_CLOCK_IN_KHZ_165 = 165000 +}; + +/* + * DFS-bypass flag + */ +/* Copy of SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS from atombios.h */ +enum { + DFS_BYPASS_ENABLE = 0x10 +}; + +enum { + INVALID_BACKLIGHT = -1 +}; + +struct panel_backlight_boundaries { + uint32_t min_signal_level; + uint32_t max_signal_level; +}; + + +#endif diff --git a/drivers/gpu/drm/amd/display/include/grph_object_defs.h b/drivers/gpu/drm/amd/display/include/grph_object_defs.h new file mode 100644 index 000000000000..2941b882b0b6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/grph_object_defs.h @@ -0,0 +1,140 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GRPH_OBJECT_DEFS_H__ +#define __DAL_GRPH_OBJECT_DEFS_H__ + +#include "grph_object_id.h" + +/* ******************************************************************** + * ******************************************************************** + * + * These defines shared between All Graphics Objects + * + * ******************************************************************** + * ******************************************************************** + */ + +/* HPD unit id - HW direct translation */ +enum hpd_source_id { + HPD_SOURCEID1 = 0, + HPD_SOURCEID2, + HPD_SOURCEID3, + HPD_SOURCEID4, + HPD_SOURCEID5, + HPD_SOURCEID6, + + HPD_SOURCEID_COUNT, + HPD_SOURCEID_UNKNOWN +}; + +/* DDC unit id - HW direct translation */ +enum channel_id { + CHANNEL_ID_UNKNOWN = 0, + CHANNEL_ID_DDC1, + CHANNEL_ID_DDC2, + CHANNEL_ID_DDC3, + CHANNEL_ID_DDC4, + CHANNEL_ID_DDC5, + CHANNEL_ID_DDC6, + CHANNEL_ID_DDC_VGA, + CHANNEL_ID_I2C_PAD, + CHANNEL_ID_COUNT +}; + +#define DECODE_CHANNEL_ID(ch_id) \ + (ch_id) == CHANNEL_ID_DDC1 ? "CHANNEL_ID_DDC1" : \ + (ch_id) == CHANNEL_ID_DDC2 ? "CHANNEL_ID_DDC2" : \ + (ch_id) == CHANNEL_ID_DDC3 ? "CHANNEL_ID_DDC3" : \ + (ch_id) == CHANNEL_ID_DDC4 ? "CHANNEL_ID_DDC4" : \ + (ch_id) == CHANNEL_ID_DDC5 ? "CHANNEL_ID_DDC5" : \ + (ch_id) == CHANNEL_ID_DDC6 ? "CHANNEL_ID_DDC6" : \ + (ch_id) == CHANNEL_ID_DDC_VGA ? "CHANNEL_ID_DDC_VGA" : \ + (ch_id) == CHANNEL_ID_I2C_PAD ? "CHANNEL_ID_I2C_PAD" : "Invalid" + +enum transmitter { + TRANSMITTER_UNKNOWN = (-1L), + TRANSMITTER_UNIPHY_A, + TRANSMITTER_UNIPHY_B, + TRANSMITTER_UNIPHY_C, + TRANSMITTER_UNIPHY_D, + TRANSMITTER_UNIPHY_E, + TRANSMITTER_UNIPHY_F, + TRANSMITTER_NUTMEG_CRT, + TRANSMITTER_TRAVIS_CRT, + TRANSMITTER_TRAVIS_LCD, + TRANSMITTER_UNIPHY_G, + TRANSMITTER_COUNT +}; + +/* Generic source of the synchronisation input/output signal */ +/* Can be used for flow control, stereo sync, timing sync, frame sync, etc */ +enum sync_source { + SYNC_SOURCE_NONE = 0, + + /* Source based on controllers */ + SYNC_SOURCE_CONTROLLER0, + SYNC_SOURCE_CONTROLLER1, + SYNC_SOURCE_CONTROLLER2, + SYNC_SOURCE_CONTROLLER3, + SYNC_SOURCE_CONTROLLER4, + SYNC_SOURCE_CONTROLLER5, + + /* Source based on GSL group */ + SYNC_SOURCE_GSL_GROUP0, + SYNC_SOURCE_GSL_GROUP1, + SYNC_SOURCE_GSL_GROUP2, + + /* Source based on GSL IOs */ + /* These IOs normally used as GSL input/output */ + SYNC_SOURCE_GSL_IO_FIRST, + SYNC_SOURCE_GSL_IO_GENLOCK_CLOCK = SYNC_SOURCE_GSL_IO_FIRST, + SYNC_SOURCE_GSL_IO_GENLOCK_VSYNC, + SYNC_SOURCE_GSL_IO_SWAPLOCK_A, + SYNC_SOURCE_GSL_IO_SWAPLOCK_B, + SYNC_SOURCE_GSL_IO_LAST = SYNC_SOURCE_GSL_IO_SWAPLOCK_B, + + /* Source based on regular IOs */ + SYNC_SOURCE_IO_FIRST, + SYNC_SOURCE_IO_GENERIC_A = SYNC_SOURCE_IO_FIRST, + SYNC_SOURCE_IO_GENERIC_B, + SYNC_SOURCE_IO_GENERIC_C, + SYNC_SOURCE_IO_GENERIC_D, + SYNC_SOURCE_IO_GENERIC_E, + SYNC_SOURCE_IO_GENERIC_F, + SYNC_SOURCE_IO_HPD1, + SYNC_SOURCE_IO_HPD2, + SYNC_SOURCE_IO_HSYNC_A, + SYNC_SOURCE_IO_VSYNC_A, + SYNC_SOURCE_IO_HSYNC_B, + SYNC_SOURCE_IO_VSYNC_B, + SYNC_SOURCE_IO_LAST = SYNC_SOURCE_IO_VSYNC_B, + + /* Misc. flow control sources */ + SYNC_SOURCE_DUAL_GPU_PIN +}; + + +#endif diff --git a/drivers/gpu/drm/amd/display/include/grph_object_id.h b/drivers/gpu/drm/amd/display/include/grph_object_id.h new file mode 100644 index 000000000000..5eb2b4dc7b9c --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/grph_object_id.h @@ -0,0 +1,294 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_GRPH_OBJECT_ID_H__ +#define __DAL_GRPH_OBJECT_ID_H__ + +/* Types of graphics objects */ +enum object_type { + OBJECT_TYPE_UNKNOWN = 0, + + /* Direct ATOM BIOS translation */ + OBJECT_TYPE_GPU, + OBJECT_TYPE_ENCODER, + OBJECT_TYPE_CONNECTOR, + OBJECT_TYPE_ROUTER, + OBJECT_TYPE_GENERIC, + + /* Driver specific */ + OBJECT_TYPE_AUDIO, + OBJECT_TYPE_CONTROLLER, + OBJECT_TYPE_CLOCK_SOURCE, + OBJECT_TYPE_ENGINE, + + OBJECT_TYPE_COUNT +}; + +/* Enumeration inside one type of graphics objects */ +enum object_enum_id { + ENUM_ID_UNKNOWN = 0, + ENUM_ID_1, + ENUM_ID_2, + ENUM_ID_3, + ENUM_ID_4, + ENUM_ID_5, + ENUM_ID_6, + ENUM_ID_7, + + ENUM_ID_COUNT +}; + +/* Generic object ids */ +enum generic_id { + GENERIC_ID_UNKNOWN = 0, + GENERIC_ID_MXM_OPM, + GENERIC_ID_GLSYNC, + GENERIC_ID_STEREO, + + GENERIC_ID_COUNT +}; + +/* Controller object ids */ +enum controller_id { + CONTROLLER_ID_UNDEFINED = 0, + CONTROLLER_ID_D0, + CONTROLLER_ID_D1, + CONTROLLER_ID_D2, + CONTROLLER_ID_D3, + CONTROLLER_ID_D4, + CONTROLLER_ID_D5, + CONTROLLER_ID_UNDERLAY0, + CONTROLLER_ID_MAX = CONTROLLER_ID_UNDERLAY0 +}; + +#define IS_UNDERLAY_CONTROLLER(ctrlr_id) (ctrlr_id >= CONTROLLER_ID_UNDERLAY0) + +/* + * ClockSource object ids. + * We maintain the order matching (more or less) ATOM BIOS + * to improve optimized acquire + */ +enum clock_source_id { + CLOCK_SOURCE_ID_UNDEFINED = 0, + CLOCK_SOURCE_ID_PLL0, + CLOCK_SOURCE_ID_PLL1, + CLOCK_SOURCE_ID_PLL2, + CLOCK_SOURCE_ID_EXTERNAL, /* ID (Phy) ref. clk. for DP */ + CLOCK_SOURCE_ID_DCPLL, + CLOCK_SOURCE_ID_DFS, /* DENTIST */ + CLOCK_SOURCE_ID_VCE, /* VCE does not need a real PLL */ + /* Used to distinguish between programming pixel clock and ID (Phy) clock */ + CLOCK_SOURCE_ID_DP_DTO, + + CLOCK_SOURCE_COMBO_PHY_PLL0, /*combo PHY PLL defines (DC 11.2 and up)*/ + CLOCK_SOURCE_COMBO_PHY_PLL1, + CLOCK_SOURCE_COMBO_PHY_PLL2, + CLOCK_SOURCE_COMBO_PHY_PLL3, + CLOCK_SOURCE_COMBO_PHY_PLL4, + CLOCK_SOURCE_COMBO_PHY_PLL5, + CLOCK_SOURCE_COMBO_DISPLAY_PLL0 +}; + +/* Encoder object ids */ +enum encoder_id { + ENCODER_ID_UNKNOWN = 0, + + /* Radeon Class Display Hardware */ + ENCODER_ID_INTERNAL_LVDS, + ENCODER_ID_INTERNAL_TMDS1, + ENCODER_ID_INTERNAL_TMDS2, + ENCODER_ID_INTERNAL_DAC1, + ENCODER_ID_INTERNAL_DAC2, /* TV/CV DAC */ + + /* External Third Party Encoders */ + ENCODER_ID_INTERNAL_LVTM1, /* not used for Radeon */ + ENCODER_ID_INTERNAL_HDMI, + + /* Kaledisope (KLDSCP) Class Display Hardware */ + ENCODER_ID_INTERNAL_KLDSCP_TMDS1, + ENCODER_ID_INTERNAL_KLDSCP_DAC1, + ENCODER_ID_INTERNAL_KLDSCP_DAC2, /* Shared with CV/TV and CRT */ + /* External TMDS (dual link) */ + ENCODER_ID_EXTERNAL_MVPU_FPGA, /* MVPU FPGA chip */ + ENCODER_ID_INTERNAL_DDI, + ENCODER_ID_INTERNAL_UNIPHY, + ENCODER_ID_INTERNAL_KLDSCP_LVTMA, + ENCODER_ID_INTERNAL_UNIPHY1, + ENCODER_ID_INTERNAL_UNIPHY2, + ENCODER_ID_EXTERNAL_NUTMEG, + ENCODER_ID_EXTERNAL_TRAVIS, + + ENCODER_ID_INTERNAL_WIRELESS, /* Internal wireless display encoder */ + ENCODER_ID_INTERNAL_UNIPHY3, + ENCODER_ID_INTERNAL_VIRTUAL, +}; + +/* Connector object ids */ +enum connector_id { + CONNECTOR_ID_UNKNOWN = 0, + CONNECTOR_ID_SINGLE_LINK_DVII = 1, + CONNECTOR_ID_DUAL_LINK_DVII = 2, + CONNECTOR_ID_SINGLE_LINK_DVID = 3, + CONNECTOR_ID_DUAL_LINK_DVID = 4, + CONNECTOR_ID_VGA = 5, + CONNECTOR_ID_HDMI_TYPE_A = 12, + CONNECTOR_ID_LVDS = 14, + CONNECTOR_ID_PCIE = 16, + CONNECTOR_ID_HARDCODE_DVI = 18, + CONNECTOR_ID_DISPLAY_PORT = 19, + CONNECTOR_ID_EDP = 20, + CONNECTOR_ID_MXM = 21, + CONNECTOR_ID_WIRELESS = 22, + CONNECTOR_ID_MIRACAST = 23, + + CONNECTOR_ID_VIRTUAL = 100 +}; + +/* Audio object ids */ +enum audio_id { + AUDIO_ID_UNKNOWN = 0, + AUDIO_ID_INTERNAL_AZALIA +}; + +/* Engine object ids */ +enum engine_id { + ENGINE_ID_DIGA, + ENGINE_ID_DIGB, + ENGINE_ID_DIGC, + ENGINE_ID_DIGD, + ENGINE_ID_DIGE, + ENGINE_ID_DIGF, + ENGINE_ID_DIGG, + ENGINE_ID_DACA, + ENGINE_ID_DACB, + ENGINE_ID_VCE, /* wireless display pseudo-encoder */ + ENGINE_ID_VIRTUAL, + + ENGINE_ID_COUNT, + ENGINE_ID_UNKNOWN = (-1L) +}; + +enum transmitter_color_depth { + TRANSMITTER_COLOR_DEPTH_24 = 0, /* 8 bits */ + TRANSMITTER_COLOR_DEPTH_30, /* 10 bits */ + TRANSMITTER_COLOR_DEPTH_36, /* 12 bits */ + TRANSMITTER_COLOR_DEPTH_48 /* 16 bits */ +}; + +/* + ***************************************************************************** + * graphics_object_id struct + * + * graphics_object_id is a very simple struct wrapping 32bit Graphics + * Object identication + * + * This struct should stay very simple + * No dependencies at all (no includes) + * No debug messages or asserts + * No #ifndef and preprocessor directives + * No grow in space (no more data member) + ***************************************************************************** + */ + +struct graphics_object_id { + uint32_t id:8; + uint32_t enum_id:4; + uint32_t type:4; + uint32_t reserved:16; /* for padding. total size should be u32 */ +}; + +/* some simple functions for convenient graphics_object_id handle */ + +static inline struct graphics_object_id dal_graphics_object_id_init( + uint32_t id, + enum object_enum_id enum_id, + enum object_type type) +{ + struct graphics_object_id result = { + id, enum_id, type, 0 + }; + + return result; +} + +bool dal_graphics_object_id_is_equal( + struct graphics_object_id id1, + struct graphics_object_id id2); + +/* Based on internal data members memory layout */ +static inline uint32_t dal_graphics_object_id_to_uint( + struct graphics_object_id id) +{ + return id.id + (id.enum_id << 0x8) + (id.type << 0xc); +} + +static inline enum controller_id dal_graphics_object_id_get_controller_id( + struct graphics_object_id id) +{ + if (id.type == OBJECT_TYPE_CONTROLLER) + return id.id; + return CONTROLLER_ID_UNDEFINED; +} + +static inline enum clock_source_id dal_graphics_object_id_get_clock_source_id( + struct graphics_object_id id) +{ + if (id.type == OBJECT_TYPE_CLOCK_SOURCE) + return id.id; + return CLOCK_SOURCE_ID_UNDEFINED; +} + +static inline enum encoder_id dal_graphics_object_id_get_encoder_id( + struct graphics_object_id id) +{ + if (id.type == OBJECT_TYPE_ENCODER) + return id.id; + return ENCODER_ID_UNKNOWN; +} + +static inline enum connector_id dal_graphics_object_id_get_connector_id( + struct graphics_object_id id) +{ + if (id.type == OBJECT_TYPE_CONNECTOR) + return id.id; + return CONNECTOR_ID_UNKNOWN; +} + +static inline enum audio_id dal_graphics_object_id_get_audio_id( + struct graphics_object_id id) +{ + if (id.type == OBJECT_TYPE_AUDIO) + return id.id; + return AUDIO_ID_UNKNOWN; +} + +static inline enum engine_id dal_graphics_object_id_get_engine_id( + struct graphics_object_id id) +{ + if (id.type == OBJECT_TYPE_ENGINE) + return id.id; + return ENGINE_ID_UNKNOWN; +} +#endif diff --git a/drivers/gpu/drm/amd/display/include/i2caux_interface.h b/drivers/gpu/drm/amd/display/include/i2caux_interface.h new file mode 100644 index 000000000000..13a3c82d118f --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/i2caux_interface.h @@ -0,0 +1,92 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_I2CAUX_INTERFACE_H__ +#define __DAL_I2CAUX_INTERFACE_H__ + +#include "dc_types.h" +#include "gpio_service_interface.h" + + +#define DEFAULT_AUX_MAX_DATA_SIZE 16 +#define AUX_MAX_DEFER_WRITE_RETRY 20 + +struct aux_payload { + /* set following flag to read/write I2C data, + * reset it to read/write DPCD data */ + bool i2c_over_aux; + /* set following flag to write data, + * reset it to read data */ + bool write; + uint32_t address; + uint8_t length; + uint8_t *data; +}; + +struct aux_command { + struct aux_payload *payloads; + uint8_t number_of_payloads; + + /* expressed in milliseconds + * zero means "use default value" */ + uint32_t defer_delay; + + /* zero means "use default value" */ + uint32_t max_defer_write_retry; + + enum i2c_mot_mode mot; +}; + +union aux_config { + struct { + uint32_t ALLOW_AUX_WHEN_HPD_LOW:1; + } bits; + uint32_t raw; +}; + +struct i2caux; + +struct i2caux *dal_i2caux_create( + struct dc_context *ctx); + +bool dal_i2caux_submit_i2c_command( + struct i2caux *i2caux, + struct ddc *ddc, + struct i2c_command *cmd); + +bool dal_i2caux_submit_aux_command( + struct i2caux *i2caux, + struct ddc *ddc, + struct aux_command *cmd); + +void dal_i2caux_configure_aux( + struct i2caux *i2caux, + struct ddc *ddc, + union aux_config cfg); + +void dal_i2caux_destroy( + struct i2caux **ptr); + +#endif diff --git a/drivers/gpu/drm/amd/display/include/irq_service_interface.h b/drivers/gpu/drm/amd/display/include/irq_service_interface.h new file mode 100644 index 000000000000..d6ebed524daf --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/irq_service_interface.h @@ -0,0 +1,51 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_IRQ_SERVICE_INTERFACE_H__ +#define __DAL_IRQ_SERVICE_INTERFACE_H__ + +struct irq_service_init_data { + struct dc_context *ctx; +}; + +struct irq_service; + +void dal_irq_service_destroy(struct irq_service **irq_service); + +bool dal_irq_service_set( + struct irq_service *irq_service, + enum dc_irq_source source, + bool enable); + +bool dal_irq_service_ack( + struct irq_service *irq_service, + enum dc_irq_source source); + +enum dc_irq_source dal_irq_service_to_irq_source( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id); + +#endif diff --git a/drivers/gpu/drm/amd/display/include/link_service_types.h b/drivers/gpu/drm/amd/display/include/link_service_types.h new file mode 100644 index 000000000000..adea1a59f620 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/link_service_types.h @@ -0,0 +1,170 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_LINK_SERVICE_TYPES_H__ +#define __DAL_LINK_SERVICE_TYPES_H__ + +#include "grph_object_id.h" +#include "dal_types.h" +#include "irq_types.h" + +/*struct mst_mgr_callback_object;*/ +struct ddc; +struct irq_manager; + +enum { + MAX_CONTROLLER_NUM = 6 +}; + +enum dp_power_state { + DP_POWER_STATE_D0 = 1, + DP_POWER_STATE_D3 +}; + +enum edp_revision { + /* eDP version 1.1 or lower */ + EDP_REVISION_11 = 0x00, + /* eDP version 1.2 */ + EDP_REVISION_12 = 0x01, + /* eDP version 1.3 */ + EDP_REVISION_13 = 0x02 +}; + +enum { + LINK_RATE_REF_FREQ_IN_KHZ = 27000 /*27MHz*/ +}; + +enum link_training_result { + LINK_TRAINING_SUCCESS, + LINK_TRAINING_CR_FAIL, + /* CR DONE bit is cleared during EQ step */ + LINK_TRAINING_EQ_FAIL_CR, + /* other failure during EQ step */ + LINK_TRAINING_EQ_FAIL_EQ, +}; + +struct link_training_settings { + struct dc_link_settings link_settings; + struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]; + bool allow_invalid_msa_timing_param; +}; + +enum hw_dp_training_pattern { + HW_DP_TRAINING_PATTERN_1 = 0, + HW_DP_TRAINING_PATTERN_2, + HW_DP_TRAINING_PATTERN_3, + HW_DP_TRAINING_PATTERN_4 +}; + +/*TODO: Move this enum test harness*/ +/* Test patterns*/ +enum dp_test_pattern { + /* Input data is pass through Scrambler + * and 8b10b Encoder straight to output*/ + DP_TEST_PATTERN_VIDEO_MODE = 0, + + /* phy test patterns*/ + DP_TEST_PATTERN_PHY_PATTERN_BEGIN, + DP_TEST_PATTERN_D102 = DP_TEST_PATTERN_PHY_PATTERN_BEGIN, + DP_TEST_PATTERN_SYMBOL_ERROR, + DP_TEST_PATTERN_PRBS7, + DP_TEST_PATTERN_80BIT_CUSTOM, + DP_TEST_PATTERN_CP2520_1, + DP_TEST_PATTERN_CP2520_2, + DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE = DP_TEST_PATTERN_CP2520_2, + DP_TEST_PATTERN_CP2520_3, + + /* Link Training Patterns */ + DP_TEST_PATTERN_TRAINING_PATTERN1, + DP_TEST_PATTERN_TRAINING_PATTERN2, + DP_TEST_PATTERN_TRAINING_PATTERN3, + DP_TEST_PATTERN_TRAINING_PATTERN4, + DP_TEST_PATTERN_PHY_PATTERN_END = DP_TEST_PATTERN_TRAINING_PATTERN4, + + /* link test patterns*/ + DP_TEST_PATTERN_COLOR_SQUARES, + DP_TEST_PATTERN_COLOR_SQUARES_CEA, + DP_TEST_PATTERN_VERTICAL_BARS, + DP_TEST_PATTERN_HORIZONTAL_BARS, + DP_TEST_PATTERN_COLOR_RAMP, + + /* audio test patterns*/ + DP_TEST_PATTERN_AUDIO_OPERATOR_DEFINED, + DP_TEST_PATTERN_AUDIO_SAWTOOTH, + + DP_TEST_PATTERN_UNSUPPORTED +}; + +enum dp_panel_mode { + /* not required */ + DP_PANEL_MODE_DEFAULT, + /* standard mode for eDP */ + DP_PANEL_MODE_EDP, + /* external chips specific settings */ + DP_PANEL_MODE_SPECIAL +}; + +/* DPCD_ADDR_TRAINING_LANEx_SET registers value */ +union dpcd_training_lane_set { + struct { +#if defined(LITTLEENDIAN_CPU) + uint8_t VOLTAGE_SWING_SET:2; + uint8_t MAX_SWING_REACHED:1; + uint8_t PRE_EMPHASIS_SET:2; + uint8_t MAX_PRE_EMPHASIS_REACHED:1; + /* following is reserved in DP 1.1 */ + uint8_t POST_CURSOR2_SET:2; +#elif defined(BIGENDIAN_CPU) + uint8_t POST_CURSOR2_SET:2; + uint8_t MAX_PRE_EMPHASIS_REACHED:1; + uint8_t PRE_EMPHASIS_SET:2; + uint8_t MAX_SWING_REACHED:1; + uint8_t VOLTAGE_SWING_SET:2; +#else + #error ARCH not defined! +#endif + } bits; + + uint8_t raw; +}; + + +/* DP MST stream allocation (payload bandwidth number) */ +struct dp_mst_stream_allocation { + uint8_t vcp_id; + /* number of slots required for the DP stream in + * transport packet */ + uint8_t slot_count; +}; + +/* DP MST stream allocation table */ +struct dp_mst_stream_allocation_table { + /* number of DP video streams */ + int stream_count; + /* array of stream allocations */ + struct dp_mst_stream_allocation stream_allocations[MAX_CONTROLLER_NUM]; +}; + +#endif /*__DAL_LINK_SERVICE_TYPES_H__*/ diff --git a/drivers/gpu/drm/amd/display/include/logger_interface.h b/drivers/gpu/drm/amd/display/include/logger_interface.h new file mode 100644 index 000000000000..8e1fe70097be --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/logger_interface.h @@ -0,0 +1,188 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_LOGGER_INTERFACE_H__ +#define __DAL_LOGGER_INTERFACE_H__ + +#include "logger_types.h" + +struct dc_context; +struct dc_link; +struct dc_surface_update; +struct resource_context; +struct dc_state; + +/* + * + * DAL logger functionality + * + */ + +struct dal_logger *dal_logger_create(struct dc_context *ctx, uint32_t log_mask); + +uint32_t dal_logger_destroy(struct dal_logger **logger); + +void dm_logger_flush_buffer(struct dal_logger *logger, bool should_warn); + +void dm_logger_write( + struct dal_logger *logger, + enum dc_log_type log_type, + const char *msg, + ...); + +void dm_logger_append( + struct log_entry *entry, + const char *msg, + ...); + +void dm_logger_open( + struct dal_logger *logger, + struct log_entry *entry, + enum dc_log_type log_type); + +void dm_logger_close(struct log_entry *entry); + +void dc_conn_log(struct dc_context *ctx, + const struct dc_link *link, + uint8_t *hex_data, + int hex_data_count, + enum dc_log_type event, + const char *msg, + ...); + +void logger_write(struct dal_logger *logger, + enum dc_log_type log_type, + const char *msg, + void *paralist); + +void pre_surface_trace( + struct dc *dc, + const struct dc_plane_state *const *plane_states, + int surface_count); + +void update_surface_trace( + struct dc *dc, + const struct dc_surface_update *updates, + int surface_count); + +void post_surface_trace(struct dc *dc); + +void context_timing_trace( + struct dc *dc, + struct resource_context *res_ctx); + +void context_clock_trace( + struct dc *dc, + struct dc_state *context); + +/* Any function which is empty or have incomplete implementation should be + * marked by this macro. + * Note that the message will be printed exactly once for every function + * it is used in order to avoid repeating of the same message. */ +#define DAL_LOGGER_NOT_IMPL(fmt, ...) \ +{ \ + static bool print_not_impl = true; \ +\ + if (print_not_impl == true) { \ + print_not_impl = false; \ + dm_logger_write(ctx->logger, LOG_WARNING, \ + "DAL_NOT_IMPL: " fmt, ##__VA_ARGS__); \ + } \ +} + +/****************************************************************************** + * Convenience macros to save on typing. + *****************************************************************************/ + +#define DC_ERROR(...) \ + dm_logger_write(dc_ctx->logger, LOG_ERROR, \ + __VA_ARGS__) + +#define DC_SYNC_INFO(...) \ + dm_logger_write(dc_ctx->logger, LOG_SYNC, \ + __VA_ARGS__) + +/* Connectivity log format: + * [time stamp] [drm] [Major_minor] [connector name] message..... + * eg: + * [ 26.590965] [drm] [Conn_LKTN] [DP-1] HBRx4 pass VS=0, PE=0^ + * [ 26.881060] [drm] [Conn_Mode] [DP-1] {2560x1080, 2784x1111@185580Khz}^ + */ + +#define CONN_DATA_DETECT(link, hex_data, hex_len, ...) \ + dc_conn_log(link->ctx, link, hex_data, hex_len, \ + LOG_EVENT_DETECTION, ##__VA_ARGS__) + +#define CONN_DATA_LINK_LOSS(link, hex_data, hex_len, ...) \ + dc_conn_log(link->ctx, link, hex_data, hex_len, \ + LOG_EVENT_LINK_LOSS, ##__VA_ARGS__) + +#define CONN_MSG_LT(link, ...) \ + dc_conn_log(link->ctx, link, NULL, 0, \ + LOG_EVENT_LINK_TRAINING, ##__VA_ARGS__) + +#define CONN_MSG_MODE(link, ...) \ + dc_conn_log(link->ctx, link, NULL, 0, \ + LOG_EVENT_MODE_SET, ##__VA_ARGS__) + +/* + * Display Test Next logging + */ +#define DTN_INFO_BEGIN() \ + dm_dtn_log_begin(dc_ctx) + +#define DTN_INFO(msg, ...) \ + dm_dtn_log_append_v(dc_ctx, msg, ##__VA_ARGS__) + +#define DTN_INFO_END() \ + dm_dtn_log_end(dc_ctx) + +#define PERFORMANCE_TRACE_START() \ + unsigned long long perf_trc_start_stmp = dm_get_timestamp(dc->ctx); \ + unsigned long long perf_trc_start_log_msk = dc->ctx->logger->mask; \ + unsigned int perf_trc_start_log_flags = dc->ctx->logger->flags.value; \ + if (dc->debug.performance_trace) {\ + dm_logger_flush_buffer(dc->ctx->logger, false);\ + dc->ctx->logger->mask = 1<ctx->logger->flags.bits.ENABLE_CONSOLE = 0;\ + dc->ctx->logger->flags.bits.ENABLE_BUFFER = 1;\ + } + +#define PERFORMANCE_TRACE_END() do {\ + unsigned long long perf_trc_end_stmp = dm_get_timestamp(dc->ctx);\ + if (dc->debug.performance_trace) {\ + dm_logger_write(dc->ctx->logger, \ + LOG_PERF_TRACE, \ + "%s duration: %d ticks\n", __func__,\ + perf_trc_end_stmp - perf_trc_start_stmp); \ + if (perf_trc_start_log_msk != 1<ctx->logger->mask = perf_trc_start_log_msk;\ + dc->ctx->logger->flags.value = perf_trc_start_log_flags;\ + dm_logger_flush_buffer(dc->ctx->logger, false);\ + } \ + } \ +} while (0) + +#endif /* __DAL_LOGGER_INTERFACE_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/logger_types.h b/drivers/gpu/drm/amd/display/include/logger_types.h new file mode 100644 index 000000000000..e2ff8cd423d6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/logger_types.h @@ -0,0 +1,166 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_LOGGER_TYPES_H__ +#define __DAL_LOGGER_TYPES_H__ + +#include "os_types.h" + +#define MAX_NAME_LEN 32 + +struct dal_logger; + +enum dc_log_type { + LOG_ERROR = 0, + LOG_WARNING, + LOG_DEBUG, + LOG_DC, + LOG_DTN, + LOG_SURFACE, + LOG_HW_HOTPLUG, + LOG_HW_LINK_TRAINING, + LOG_HW_SET_MODE, + LOG_HW_RESUME_S3, + LOG_HW_AUDIO, + LOG_HW_HPD_IRQ, + LOG_MST, + LOG_SCALER, + LOG_BIOS, + LOG_BANDWIDTH_CALCS, + LOG_BANDWIDTH_VALIDATION, + LOG_I2C_AUX, + LOG_SYNC, + LOG_BACKLIGHT, + LOG_FEATURE_OVERRIDE, + LOG_DETECTION_EDID_PARSER, + LOG_DETECTION_DP_CAPS, + LOG_RESOURCE, + LOG_DML, + LOG_EVENT_MODE_SET, + LOG_EVENT_DETECTION, + LOG_EVENT_LINK_TRAINING, + LOG_EVENT_LINK_LOSS, + LOG_EVENT_UNDERFLOW, + LOG_IF_TRACE, + LOG_PERF_TRACE, + + LOG_SECTION_TOTAL_COUNT +}; + +#define DC_MIN_LOG_MASK ((1 << LOG_ERROR) | \ + (1 << LOG_DETECTION_EDID_PARSER)) + +#define DC_DEFAULT_LOG_MASK ((1 << LOG_ERROR) | \ + (1 << LOG_WARNING) | \ + (1 << LOG_EVENT_MODE_SET) | \ + (1 << LOG_EVENT_DETECTION) | \ + (1 << LOG_EVENT_LINK_TRAINING) | \ + (1 << LOG_EVENT_LINK_LOSS) | \ + (1 << LOG_EVENT_UNDERFLOW) | \ + (1 << LOG_RESOURCE) | \ + (1 << LOG_FEATURE_OVERRIDE) | \ + (1 << LOG_DETECTION_EDID_PARSER) | \ + (1 << LOG_DC) | \ + (1 << LOG_HW_HOTPLUG) | \ + (1 << LOG_HW_SET_MODE) | \ + (1 << LOG_HW_RESUME_S3) | \ + (1 << LOG_HW_HPD_IRQ) | \ + (1 << LOG_SYNC) | \ + (1 << LOG_BANDWIDTH_VALIDATION) | \ + (1 << LOG_MST) | \ + (1 << LOG_DETECTION_DP_CAPS) | \ + (1 << LOG_BACKLIGHT)) | \ + (1 << LOG_I2C_AUX) | \ + (1 << LOG_IF_TRACE) | \ + (1 << LOG_DTN) /* | \ + (1 << LOG_DEBUG) | \ + (1 << LOG_BIOS) | \ + (1 << LOG_SURFACE) | \ + (1 << LOG_SCALER) | \ + (1 << LOG_DML) | \ + (1 << LOG_HW_LINK_TRAINING) | \ + (1 << LOG_HW_AUDIO)| \ + (1 << LOG_BANDWIDTH_CALCS)*/ + +union logger_flags { + struct { + uint32_t ENABLE_CONSOLE:1; /* Print to console */ + uint32_t ENABLE_BUFFER:1; /* Print to buffer */ + uint32_t RESERVED:30; + } bits; + uint32_t value; +}; + +struct log_entry { + struct dal_logger *logger; + enum dc_log_type type; + + char *buf; + uint32_t buf_offset; + uint32_t max_buf_bytes; +}; + +/** +* Structure for enumerating log types +*/ +struct dc_log_type_info { + enum dc_log_type type; + char name[MAX_NAME_LEN]; +}; + +/* Structure for keeping track of offsets, buffer, etc */ + +#define DAL_LOGGER_BUFFER_MAX_SIZE 2048 + +/*Connectivity log needs to output EDID, which needs at lease 256x3 bytes, + * change log line size to 896 to meet the request. + */ +#define LOG_MAX_LINE_SIZE 896 + +struct dal_logger { + + /* How far into the circular buffer has been read by dsat + * Read offset should never cross write offset. Write \0's to + * read data just to be sure? + */ + uint32_t buffer_read_offset; + + /* How far into the circular buffer we have written + * Write offset should never cross read offset + */ + uint32_t buffer_write_offset; + + uint32_t open_count; + + char *log_buffer; /* Pointer to malloc'ed buffer */ + uint32_t log_buffer_size; /* Size of circular buffer */ + + uint32_t mask; /*array of masks for major elements*/ + + union logger_flags flags; + struct dc_context *ctx; +}; + +#endif /* __DAL_LOGGER_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/set_mode_types.h b/drivers/gpu/drm/amd/display/include/set_mode_types.h new file mode 100644 index 000000000000..fee2b6ffcfc1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/set_mode_types.h @@ -0,0 +1,107 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_SET_MODE_TYPES_H__ +#define __DAL_SET_MODE_TYPES_H__ + +#include "dc_types.h" +#include + +/* Info frame packet status */ +enum info_frame_flag { + INFO_PACKET_PACKET_INVALID = 0, + INFO_PACKET_PACKET_VALID = 1, + INFO_PACKET_PACKET_RESET = 2, + INFO_PACKET_PACKET_UPDATE_SCAN_TYPE = 8 +}; + +struct hdmi_info_frame_header { + uint8_t info_frame_type; + uint8_t version; + uint8_t length; +}; + +#pragma pack(push) +#pragma pack(1) + +struct info_packet_raw_data { + uint8_t hb0; + uint8_t hb1; + uint8_t hb2; + uint8_t sb[28]; /* sb0~sb27 */ +}; + +union hdmi_info_packet { + struct avi_info_frame { + struct hdmi_info_frame_header header; + + uint8_t CHECK_SUM:8; + + uint8_t S0_S1:2; + uint8_t B0_B1:2; + uint8_t A0:1; + uint8_t Y0_Y1_Y2:3; + + uint8_t R0_R3:4; + uint8_t M0_M1:2; + uint8_t C0_C1:2; + + uint8_t SC0_SC1:2; + uint8_t Q0_Q1:2; + uint8_t EC0_EC2:3; + uint8_t ITC:1; + + uint8_t VIC0_VIC7:8; + + uint8_t PR0_PR3:4; + uint8_t CN0_CN1:2; + uint8_t YQ0_YQ1:2; + + uint16_t bar_top; + uint16_t bar_bottom; + uint16_t bar_left; + uint16_t bar_right; + + uint8_t reserved[14]; + } bits; + + struct info_packet_raw_data packet_raw_data; +}; + +struct info_packet { + enum info_frame_flag flags; + union hdmi_info_packet info_packet_hdmi; +}; + +struct info_frame { + struct info_packet avi_info_packet; + struct info_packet gamut_packet; + struct info_packet vendor_info_packet; + struct info_packet spd_info_packet; +}; + +#pragma pack(pop) + +#endif /* __DAL_SET_MODE_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/signal_types.h b/drivers/gpu/drm/amd/display/include/signal_types.h new file mode 100644 index 000000000000..b5ebde642207 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/signal_types.h @@ -0,0 +1,95 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_SIGNAL_TYPES_H__ +#define __DC_SIGNAL_TYPES_H__ + +enum signal_type { + SIGNAL_TYPE_NONE = 0L, /* no signal */ + SIGNAL_TYPE_DVI_SINGLE_LINK = (1 << 0), + SIGNAL_TYPE_DVI_DUAL_LINK = (1 << 1), + SIGNAL_TYPE_HDMI_TYPE_A = (1 << 2), + SIGNAL_TYPE_LVDS = (1 << 3), + SIGNAL_TYPE_RGB = (1 << 4), + SIGNAL_TYPE_DISPLAY_PORT = (1 << 5), + SIGNAL_TYPE_DISPLAY_PORT_MST = (1 << 6), + SIGNAL_TYPE_EDP = (1 << 7), + SIGNAL_TYPE_VIRTUAL = (1 << 9), /* Virtual Display */ +}; + +/* help functions for signal types manipulation */ +static inline bool dc_is_hdmi_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_HDMI_TYPE_A); +} + +static inline bool dc_is_dp_sst_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_DISPLAY_PORT || + signal == SIGNAL_TYPE_EDP); +} + +static inline bool dc_is_dp_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_DISPLAY_PORT || + signal == SIGNAL_TYPE_EDP || + signal == SIGNAL_TYPE_DISPLAY_PORT_MST); +} + +static inline bool dc_is_embedded_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_EDP || signal == SIGNAL_TYPE_LVDS); +} + +static inline bool dc_is_dvi_signal(enum signal_type signal) +{ + switch (signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + return true; + break; + default: + return false; + } +} + +static inline bool dc_is_dvi_single_link_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_DVI_SINGLE_LINK); +} + +static inline bool dc_is_dual_link_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_DVI_DUAL_LINK); +} + +static inline bool dc_is_audio_capable_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_DISPLAY_PORT || + signal == SIGNAL_TYPE_DISPLAY_PORT_MST || + dc_is_hdmi_signal(signal)); +} + +#endif diff --git a/drivers/gpu/drm/amd/display/include/vector.h b/drivers/gpu/drm/amd/display/include/vector.h new file mode 100644 index 000000000000..8233b7c22a07 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/vector.h @@ -0,0 +1,150 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_VECTOR_H__ +#define __DAL_VECTOR_H__ + +struct vector { + uint8_t *container; + uint32_t struct_size; + uint32_t count; + uint32_t capacity; + struct dc_context *ctx; +}; + +bool dal_vector_construct( + struct vector *vector, + struct dc_context *ctx, + uint32_t capacity, + uint32_t struct_size); + +struct vector *dal_vector_create( + struct dc_context *ctx, + uint32_t capacity, + uint32_t struct_size); + +/* 'initial_value' is optional. If initial_value not supplied, + * each "structure" in the vector will contain zeros by default. */ +struct vector *dal_vector_presized_create( + struct dc_context *ctx, + uint32_t size, + void *initial_value, + uint32_t struct_size); + +void dal_vector_destruct( + struct vector *vector); + +void dal_vector_destroy( + struct vector **vector); + +uint32_t dal_vector_get_count( + const struct vector *vector); + +/* dal_vector_insert_at + * reallocate container if necessary + * then shell items at right and insert + * return if the container modified + * do not check that index belongs to container + * since the function is private and index is going to be calculated + * either with by function or as get_count+1 */ +bool dal_vector_insert_at( + struct vector *vector, + const void *what, + uint32_t position); + +bool dal_vector_append( + struct vector *vector, + const void *item); + +/* operator[] */ +void *dal_vector_at_index( + const struct vector *vector, + uint32_t index); + +void dal_vector_set_at_index( + const struct vector *vector, + const void *what, + uint32_t index); + +/* create a clone (copy) of a vector */ +struct vector *dal_vector_clone( + const struct vector *vector_other); + +/* dal_vector_remove_at_index + * Shifts elements on the right from remove position to the left, + * removing an element at position by overwrite means*/ +bool dal_vector_remove_at_index( + struct vector *vector, + uint32_t index); + +uint32_t dal_vector_capacity(const struct vector *vector); + +bool dal_vector_reserve(struct vector *vector, uint32_t capacity); + +void dal_vector_clear(struct vector *vector); + +/*************************************************************************** + * Macro definitions of TYPE-SAFE versions of vector set/get functions. + ***************************************************************************/ + +#define DAL_VECTOR_INSERT_AT(vector_type, type_t) \ + static bool vector_type##_vector_insert_at( \ + struct vector *vector, \ + type_t what, \ + uint32_t position) \ +{ \ + return dal_vector_insert_at(vector, what, position); \ +} + +#define DAL_VECTOR_APPEND(vector_type, type_t) \ + static bool vector_type##_vector_append( \ + struct vector *vector, \ + type_t item) \ +{ \ + return dal_vector_append(vector, item); \ +} + +/* Note: "type_t" is the ONLY token accepted by "checkpatch.pl" and by + * "checkcommit" as *return type*. + * For uniformity reasons "type_t" is used for all type-safe macro + * definitions here. */ +#define DAL_VECTOR_AT_INDEX(vector_type, type_t) \ + static type_t vector_type##_vector_at_index( \ + const struct vector *vector, \ + uint32_t index) \ +{ \ + return dal_vector_at_index(vector, index); \ +} + +#define DAL_VECTOR_SET_AT_INDEX(vector_type, type_t) \ + static void vector_type##_vector_set_at_index( \ + const struct vector *vector, \ + type_t what, \ + uint32_t index) \ +{ \ + dal_vector_set_at_index(vector, what, index); \ +} + +#endif /* __DAL_VECTOR_H__ */ diff --git a/drivers/gpu/drm/amd/display/modules/freesync/Makefile b/drivers/gpu/drm/amd/display/modules/freesync/Makefile new file mode 100644 index 000000000000..db8e0ff6d7a9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/freesync/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the 'freesync' sub-module of DAL. +# + +FREESYNC = freesync.o + +AMD_DAL_FREESYNC = $(addprefix $(AMDDALPATH)/modules/freesync/,$(FREESYNC)) +#$(info ************ DAL-FREE SYNC_MAKEFILE ************) + +AMD_DISPLAY_FILES += $(AMD_DAL_FREESYNC) diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c new file mode 100644 index 000000000000..4d7db4aa28e0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -0,0 +1,1483 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dc.h" +#include "mod_freesync.h" +#include "core_types.h" + +#define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 + +/* Refresh rate ramp at a fixed rate of 65 Hz/second */ +#define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) +/* Number of elements in the render times cache array */ +#define RENDER_TIMES_MAX_COUNT 20 +/* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ +#define BTR_EXIT_MARGIN 2000 +/* Number of consecutive frames to check before entering/exiting fixed refresh*/ +#define FIXED_REFRESH_ENTER_FRAME_COUNT 5 +#define FIXED_REFRESH_EXIT_FRAME_COUNT 5 + +#define FREESYNC_REGISTRY_NAME "freesync_v1" + +#define FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY "DalFreeSyncNoStaticForExternalDp" + +#define FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY "DalFreeSyncNoStaticForInternal" + +struct gradual_static_ramp { + bool ramp_is_active; + bool ramp_direction_is_up; + unsigned int ramp_current_frame_duration_in_ns; +}; + +struct time_cache { + /* video (48Hz feature) related */ + unsigned int update_duration_in_ns; + + /* BTR/fixed refresh related */ + unsigned int prev_time_stamp_in_us; + + unsigned int min_render_time_in_us; + unsigned int max_render_time_in_us; + + unsigned int render_times_index; + unsigned int render_times[RENDER_TIMES_MAX_COUNT]; +}; + +struct below_the_range { + bool btr_active; + bool program_btr; + + unsigned int mid_point_in_us; + + unsigned int inserted_frame_duration_in_us; + unsigned int frames_to_insert; + unsigned int frame_counter; +}; + +struct fixed_refresh { + bool fixed_active; + bool program_fixed; + unsigned int frame_counter; +}; + +struct freesync_range { + unsigned int min_refresh; + unsigned int max_frame_duration; + unsigned int vmax; + + unsigned int max_refresh; + unsigned int min_frame_duration; + unsigned int vmin; +}; + +struct freesync_state { + bool fullscreen; + bool static_screen; + bool video; + + unsigned int nominal_refresh_rate_in_micro_hz; + bool windowed_fullscreen; + + struct time_cache time; + + struct gradual_static_ramp static_ramp; + struct below_the_range btr; + struct fixed_refresh fixed_refresh; + struct freesync_range freesync_range; +}; + +struct freesync_entity { + struct dc_stream_state *stream; + struct mod_freesync_caps *caps; + struct freesync_state state; + struct mod_freesync_user_enable user_enable; +}; + +struct freesync_registry_options { + bool drr_external_supported; + bool drr_internal_supported; +}; + +struct core_freesync { + struct mod_freesync public; + struct dc *dc; + struct freesync_entity *map; + int num_entities; + struct freesync_registry_options opts; +}; + +#define MOD_FREESYNC_TO_CORE(mod_freesync)\ + container_of(mod_freesync, struct core_freesync, public) + +static bool check_dc_support(const struct dc *dc) +{ + if (dc->stream_funcs.adjust_vmin_vmax == NULL) + return false; + + return true; +} + +struct mod_freesync *mod_freesync_create(struct dc *dc) +{ + struct core_freesync *core_freesync = + kzalloc(sizeof(struct core_freesync), GFP_KERNEL); + + + struct persistent_data_flag flag; + + int i, data = 0; + + if (core_freesync == NULL) + goto fail_alloc_context; + + core_freesync->map = kzalloc(sizeof(struct freesync_entity) * MOD_FREESYNC_MAX_CONCURRENT_STREAMS, + GFP_KERNEL); + + if (core_freesync->map == NULL) + goto fail_alloc_map; + + for (i = 0; i < MOD_FREESYNC_MAX_CONCURRENT_STREAMS; i++) + core_freesync->map[i].stream = NULL; + + core_freesync->num_entities = 0; + + if (dc == NULL) + goto fail_construct; + + core_freesync->dc = dc; + + if (!check_dc_support(dc)) + goto fail_construct; + + /* Create initial module folder in registry for freesync enable data */ + flag.save_per_edid = true; + flag.save_per_link = false; + dm_write_persistent_data(dc->ctx, NULL, FREESYNC_REGISTRY_NAME, + NULL, NULL, 0, &flag); + flag.save_per_edid = false; + flag.save_per_link = false; + + if (dm_read_persistent_data(dc->ctx, NULL, NULL, + FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY, + &data, sizeof(data), &flag)) { + core_freesync->opts.drr_internal_supported = + (data & 1) ? false : true; + } + + if (dm_read_persistent_data(dc->ctx, NULL, NULL, + FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY, + &data, sizeof(data), &flag)) { + core_freesync->opts.drr_external_supported = + (data & 1) ? false : true; + } + + return &core_freesync->public; + +fail_construct: + kfree(core_freesync->map); + +fail_alloc_map: + kfree(core_freesync); + +fail_alloc_context: + return NULL; +} + +void mod_freesync_destroy(struct mod_freesync *mod_freesync) +{ + if (mod_freesync != NULL) { + int i; + struct core_freesync *core_freesync = + MOD_FREESYNC_TO_CORE(mod_freesync); + + for (i = 0; i < core_freesync->num_entities; i++) + if (core_freesync->map[i].stream) + dc_stream_release(core_freesync->map[i].stream); + + kfree(core_freesync->map); + + kfree(core_freesync); + } +} + +/* Given a specific dc_stream* this function finds its equivalent + * on the core_freesync->map and returns the corresponding index + */ +static unsigned int map_index_from_stream(struct core_freesync *core_freesync, + struct dc_stream_state *stream) +{ + unsigned int index = 0; + + for (index = 0; index < core_freesync->num_entities; index++) { + if (core_freesync->map[index].stream == stream) { + return index; + } + } + /* Could not find stream requested */ + ASSERT(false); + return index; +} + +bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, struct mod_freesync_caps *caps) +{ + struct dc *dc = NULL; + struct core_freesync *core_freesync = NULL; + int persistent_freesync_enable = 0; + struct persistent_data_flag flag; + unsigned int nom_refresh_rate_uhz; + unsigned long long temp; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + dc = core_freesync->dc; + + flag.save_per_edid = true; + flag.save_per_link = false; + + if (core_freesync->num_entities < MOD_FREESYNC_MAX_CONCURRENT_STREAMS) { + + dc_stream_retain(stream); + + temp = stream->timing.pix_clk_khz; + temp *= 1000ULL * 1000ULL * 1000ULL; + temp = div_u64(temp, stream->timing.h_total); + temp = div_u64(temp, stream->timing.v_total); + + nom_refresh_rate_uhz = (unsigned int) temp; + + core_freesync->map[core_freesync->num_entities].stream = stream; + core_freesync->map[core_freesync->num_entities].caps = caps; + + core_freesync->map[core_freesync->num_entities].state. + fullscreen = false; + core_freesync->map[core_freesync->num_entities].state. + static_screen = false; + core_freesync->map[core_freesync->num_entities].state. + video = false; + core_freesync->map[core_freesync->num_entities].state.time. + update_duration_in_ns = 0; + core_freesync->map[core_freesync->num_entities].state. + static_ramp.ramp_is_active = false; + + /* get persistent data from registry */ + if (dm_read_persistent_data(dc->ctx, stream->sink, + FREESYNC_REGISTRY_NAME, + "userenable", &persistent_freesync_enable, + sizeof(int), &flag)) { + core_freesync->map[core_freesync->num_entities].user_enable. + enable_for_gaming = + (persistent_freesync_enable & 1) ? true : false; + core_freesync->map[core_freesync->num_entities].user_enable. + enable_for_static = + (persistent_freesync_enable & 2) ? true : false; + core_freesync->map[core_freesync->num_entities].user_enable. + enable_for_video = + (persistent_freesync_enable & 4) ? true : false; + } else { + core_freesync->map[core_freesync->num_entities].user_enable. + enable_for_gaming = false; + core_freesync->map[core_freesync->num_entities].user_enable. + enable_for_static = false; + core_freesync->map[core_freesync->num_entities].user_enable. + enable_for_video = false; + } + + if (caps->supported && + nom_refresh_rate_uhz >= caps->min_refresh_in_micro_hz && + nom_refresh_rate_uhz <= caps->max_refresh_in_micro_hz) + stream->ignore_msa_timing_param = 1; + + core_freesync->num_entities++; + return true; + } + return false; +} + +bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream) +{ + int i = 0; + struct core_freesync *core_freesync = NULL; + unsigned int index = 0; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, stream); + + dc_stream_release(core_freesync->map[index].stream); + core_freesync->map[index].stream = NULL; + /* To remove this entity, shift everything after down */ + for (i = index; i < core_freesync->num_entities - 1; i++) + core_freesync->map[i] = core_freesync->map[i + 1]; + core_freesync->num_entities--; + return true; +} + +static void update_stream_freesync_context(struct core_freesync *core_freesync, + struct dc_stream_state *stream) +{ + unsigned int index; + struct freesync_context *ctx; + + ctx = &stream->freesync_ctx; + + index = map_index_from_stream(core_freesync, stream); + + ctx->supported = core_freesync->map[index].caps->supported; + ctx->enabled = (core_freesync->map[index].user_enable.enable_for_gaming || + core_freesync->map[index].user_enable.enable_for_video || + core_freesync->map[index].user_enable.enable_for_static); + ctx->active = (core_freesync->map[index].state.fullscreen || + core_freesync->map[index].state.video || + core_freesync->map[index].state.static_ramp.ramp_is_active); + ctx->min_refresh_in_micro_hz = + core_freesync->map[index].caps->min_refresh_in_micro_hz; + ctx->nominal_refresh_in_micro_hz = core_freesync-> + map[index].state.nominal_refresh_rate_in_micro_hz; + +} + +static void update_stream(struct core_freesync *core_freesync, + struct dc_stream_state *stream) +{ + unsigned int index = map_index_from_stream(core_freesync, stream); + if (core_freesync->map[index].caps->supported) { + stream->ignore_msa_timing_param = 1; + update_stream_freesync_context(core_freesync, stream); + } +} + +static void calc_freesync_range(struct core_freesync *core_freesync, + struct dc_stream_state *stream, + struct freesync_state *state, + unsigned int min_refresh_in_uhz, + unsigned int max_refresh_in_uhz) +{ + unsigned int min_frame_duration_in_ns = 0, max_frame_duration_in_ns = 0; + unsigned int index = map_index_from_stream(core_freesync, stream); + uint32_t vtotal = stream->timing.v_total; + + if ((min_refresh_in_uhz == 0) || (max_refresh_in_uhz == 0)) { + state->freesync_range.min_refresh = + state->nominal_refresh_rate_in_micro_hz; + state->freesync_range.max_refresh = + state->nominal_refresh_rate_in_micro_hz; + + state->freesync_range.max_frame_duration = 0; + state->freesync_range.min_frame_duration = 0; + + state->freesync_range.vmax = vtotal; + state->freesync_range.vmin = vtotal; + + return; + } + + min_frame_duration_in_ns = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + max_refresh_in_uhz))); + max_frame_duration_in_ns = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + min_refresh_in_uhz))); + + state->freesync_range.min_refresh = min_refresh_in_uhz; + state->freesync_range.max_refresh = max_refresh_in_uhz; + + state->freesync_range.max_frame_duration = max_frame_duration_in_ns; + state->freesync_range.min_frame_duration = min_frame_duration_in_ns; + + state->freesync_range.vmax = div64_u64(div64_u64(((unsigned long long)( + max_frame_duration_in_ns) * stream->timing.pix_clk_khz), + stream->timing.h_total), 1000000); + state->freesync_range.vmin = div64_u64(div64_u64(((unsigned long long)( + min_frame_duration_in_ns) * stream->timing.pix_clk_khz), + stream->timing.h_total), 1000000); + + /* vmin/vmax cannot be less than vtotal */ + if (state->freesync_range.vmin < vtotal) { + /* Error of 1 is permissible */ + ASSERT((state->freesync_range.vmin + 1) >= vtotal); + state->freesync_range.vmin = vtotal; + } + + if (state->freesync_range.vmax < vtotal) { + /* Error of 1 is permissible */ + ASSERT((state->freesync_range.vmax + 1) >= vtotal); + state->freesync_range.vmax = vtotal; + } + + /* Determine whether BTR can be supported */ + if (max_frame_duration_in_ns >= + 2 * min_frame_duration_in_ns) + core_freesync->map[index].caps->btr_supported = true; + else + core_freesync->map[index].caps->btr_supported = false; + + /* Cache the time variables */ + state->time.max_render_time_in_us = + max_frame_duration_in_ns / 1000; + state->time.min_render_time_in_us = + min_frame_duration_in_ns / 1000; + state->btr.mid_point_in_us = + (max_frame_duration_in_ns + + min_frame_duration_in_ns) / 2000; +} + +static void calc_v_total_from_duration(struct dc_stream_state *stream, + unsigned int duration_in_ns, int *v_total_nominal) +{ + *v_total_nominal = div64_u64(div64_u64(((unsigned long long)( + duration_in_ns) * stream->timing.pix_clk_khz), + stream->timing.h_total), 1000000); +} + +static void calc_v_total_for_static_ramp(struct core_freesync *core_freesync, + struct dc_stream_state *stream, + unsigned int index, int *v_total) +{ + unsigned int frame_duration = 0; + + struct gradual_static_ramp *static_ramp_variables = + &core_freesync->map[index].state.static_ramp; + + /* Calc ratio between new and current frame duration with 3 digit */ + unsigned int frame_duration_ratio = div64_u64(1000000, + (1000 + div64_u64(((unsigned long long)( + STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * + static_ramp_variables->ramp_current_frame_duration_in_ns), + 1000000000))); + + /* Calculate delta between new and current frame duration in ns */ + unsigned int frame_duration_delta = div64_u64(((unsigned long long)( + static_ramp_variables->ramp_current_frame_duration_in_ns) * + (1000 - frame_duration_ratio)), 1000); + + /* Adjust frame duration delta based on ratio between current and + * standard frame duration (frame duration at 60 Hz refresh rate). + */ + unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( + frame_duration_delta) * static_ramp_variables-> + ramp_current_frame_duration_in_ns), 16666666); + + /* Going to a higher refresh rate (lower frame duration) */ + if (static_ramp_variables->ramp_direction_is_up) { + /* reduce frame duration */ + static_ramp_variables->ramp_current_frame_duration_in_ns -= + ramp_rate_interpolated; + + /* min frame duration */ + frame_duration = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + core_freesync->map[index].state. + nominal_refresh_rate_in_micro_hz))); + + /* adjust for frame duration below min */ + if (static_ramp_variables->ramp_current_frame_duration_in_ns <= + frame_duration) { + + static_ramp_variables->ramp_is_active = false; + static_ramp_variables-> + ramp_current_frame_duration_in_ns = + frame_duration; + } + /* Going to a lower refresh rate (larger frame duration) */ + } else { + /* increase frame duration */ + static_ramp_variables->ramp_current_frame_duration_in_ns += + ramp_rate_interpolated; + + /* max frame duration */ + frame_duration = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + core_freesync->map[index].caps->min_refresh_in_micro_hz))); + + /* adjust for frame duration above max */ + if (static_ramp_variables->ramp_current_frame_duration_in_ns >= + frame_duration) { + + static_ramp_variables->ramp_is_active = false; + static_ramp_variables-> + ramp_current_frame_duration_in_ns = + frame_duration; + } + } + + calc_v_total_from_duration(stream, static_ramp_variables-> + ramp_current_frame_duration_in_ns, v_total); +} + +static void reset_freesync_state_variables(struct freesync_state* state) +{ + state->static_ramp.ramp_is_active = false; + if (state->nominal_refresh_rate_in_micro_hz) + state->static_ramp.ramp_current_frame_duration_in_ns = + ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + state->nominal_refresh_rate_in_micro_hz))); + + state->btr.btr_active = false; + state->btr.frame_counter = 0; + state->btr.frames_to_insert = 0; + state->btr.inserted_frame_duration_in_us = 0; + state->btr.program_btr = false; + + state->fixed_refresh.fixed_active = false; + state->fixed_refresh.program_fixed = false; +} +/* + * Sets freesync mode on a stream depending on current freesync state. + */ +static bool set_freesync_on_streams(struct core_freesync *core_freesync, + struct dc_stream_state **streams, int num_streams) +{ + int v_total_nominal = 0, v_total_min = 0, v_total_max = 0; + unsigned int stream_idx, map_index = 0; + struct freesync_state *state; + + if (num_streams == 0 || streams == NULL || num_streams > 1) + return false; + + for (stream_idx = 0; stream_idx < num_streams; stream_idx++) { + + map_index = map_index_from_stream(core_freesync, + streams[stream_idx]); + + state = &core_freesync->map[map_index].state; + + if (core_freesync->map[map_index].caps->supported) { + + /* Fullscreen has the topmost priority. If the + * fullscreen bit is set, we are in a fullscreen + * application where it should not matter if it is + * static screen. We should not check the static_screen + * or video bit. + * + * Special cases of fullscreen include btr and fixed + * refresh. We program btr on every flip and involves + * programming full range right before the last inserted frame. + * However, we do not want to program the full freesync range + * when fixed refresh is active, because we only program + * that logic once and this will override it. + */ + if (core_freesync->map[map_index].user_enable. + enable_for_gaming == true && + state->fullscreen == true && + state->fixed_refresh.fixed_active == false) { + /* Enable freesync */ + + v_total_min = state->freesync_range.vmin; + v_total_max = state->freesync_range.vmax; + + /* Update the freesync context for the stream */ + update_stream_freesync_context(core_freesync, + streams[stream_idx]); + + core_freesync->dc->stream_funcs. + adjust_vmin_vmax(core_freesync->dc, streams, + num_streams, v_total_min, + v_total_max); + + return true; + + } else if (core_freesync->map[map_index].user_enable. + enable_for_video && state->video == true) { + /* Enable 48Hz feature */ + + calc_v_total_from_duration(streams[stream_idx], + state->time.update_duration_in_ns, + &v_total_nominal); + + /* Program only if v_total_nominal is in range*/ + if (v_total_nominal >= + streams[stream_idx]->timing.v_total) { + + /* Update the freesync context for + * the stream + */ + update_stream_freesync_context( + core_freesync, + streams[stream_idx]); + + core_freesync->dc->stream_funcs. + adjust_vmin_vmax( + core_freesync->dc, streams, + num_streams, v_total_nominal, + v_total_nominal); + } + return true; + + } else { + /* Disable freesync */ + v_total_nominal = streams[stream_idx]-> + timing.v_total; + + /* Update the freesync context for + * the stream + */ + update_stream_freesync_context( + core_freesync, + streams[stream_idx]); + + core_freesync->dc->stream_funcs. + adjust_vmin_vmax( + core_freesync->dc, streams, + num_streams, v_total_nominal, + v_total_nominal); + + /* Reset the cached variables */ + reset_freesync_state_variables(state); + + return true; + } + } else { + /* Disable freesync */ + v_total_nominal = streams[stream_idx]-> + timing.v_total; + /* + * we have to reset drr always even sink does + * not support freesync because a former stream has + * be programmed + */ + core_freesync->dc->stream_funcs. + adjust_vmin_vmax( + core_freesync->dc, streams, + num_streams, v_total_nominal, + v_total_nominal); + /* Reset the cached variables */ + reset_freesync_state_variables(state); + } + + } + + return false; +} + +static void set_static_ramp_variables(struct core_freesync *core_freesync, + unsigned int index, bool enable_static_screen) +{ + unsigned int frame_duration = 0; + unsigned int nominal_refresh_rate = core_freesync->map[index].state. + nominal_refresh_rate_in_micro_hz; + unsigned int min_refresh_rate= core_freesync->map[index].caps-> + min_refresh_in_micro_hz; + struct gradual_static_ramp *static_ramp_variables = + &core_freesync->map[index].state.static_ramp; + + /* If we are ENABLING static screen, refresh rate should go DOWN. + * If we are DISABLING static screen, refresh rate should go UP. + */ + if (enable_static_screen) + static_ramp_variables->ramp_direction_is_up = false; + else + static_ramp_variables->ramp_direction_is_up = true; + + /* If ramp is not active, set initial frame duration depending on + * whether we are enabling/disabling static screen mode. If the ramp is + * already active, ramp should continue in the opposite direction + * starting with the current frame duration + */ + if (!static_ramp_variables->ramp_is_active) { + if (enable_static_screen == true) { + /* Going to lower refresh rate, so start from max + * refresh rate (min frame duration) + */ + frame_duration = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + nominal_refresh_rate))); + } else { + /* Going to higher refresh rate, so start from min + * refresh rate (max frame duration) + */ + frame_duration = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + min_refresh_rate))); + } + static_ramp_variables-> + ramp_current_frame_duration_in_ns = frame_duration; + + static_ramp_variables->ramp_is_active = true; + } +} + +void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams) +{ + unsigned int index, v_total, inserted_frame_v_total = 0; + unsigned int min_frame_duration_in_ns, vmax, vmin = 0; + struct freesync_state *state; + struct core_freesync *core_freesync = NULL; + struct dc_static_screen_events triggers = {0}; + + if (mod_freesync == NULL) + return; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + + if (core_freesync->num_entities == 0) + return; + + index = map_index_from_stream(core_freesync, + streams[0]); + + if (core_freesync->map[index].caps->supported == false) + return; + + state = &core_freesync->map[index].state; + + /* Below the Range Logic */ + + /* Only execute if in fullscreen mode */ + if (state->fullscreen == true && + core_freesync->map[index].user_enable.enable_for_gaming && + core_freesync->map[index].caps->btr_supported && + state->btr.btr_active) { + + /* TODO: pass in flag for Pre-DCE12 ASIC + * in order for frame variable duration to take affect, + * it needs to be done one VSYNC early, which is at + * frameCounter == 1. + * For DCE12 and newer updates to V_TOTAL_MIN/MAX + * will take affect on current frame + */ + if (state->btr.frames_to_insert == state->btr.frame_counter) { + + min_frame_duration_in_ns = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + state->nominal_refresh_rate_in_micro_hz))); + + vmin = state->freesync_range.vmin; + + inserted_frame_v_total = vmin; + + if (min_frame_duration_in_ns / 1000) + inserted_frame_v_total = + state->btr.inserted_frame_duration_in_us * + vmin / (min_frame_duration_in_ns / 1000); + + /* Set length of inserted frames as v_total_max*/ + vmax = inserted_frame_v_total; + vmin = inserted_frame_v_total; + + /* Program V_TOTAL */ + core_freesync->dc->stream_funcs.adjust_vmin_vmax( + core_freesync->dc, streams, + num_streams, vmin, vmax); + } + + if (state->btr.frame_counter > 0) + state->btr.frame_counter--; + + /* Restore FreeSync */ + if (state->btr.frame_counter == 0) + set_freesync_on_streams(core_freesync, streams, num_streams); + } + + /* If in fullscreen freesync mode or in video, do not program + * static screen ramp values + */ + if (state->fullscreen == true || state->video == true) { + + state->static_ramp.ramp_is_active = false; + + return; + } + + /* Gradual Static Screen Ramping Logic */ + + /* Execute if ramp is active and user enabled freesync static screen*/ + if (state->static_ramp.ramp_is_active && + core_freesync->map[index].user_enable.enable_for_static) { + + calc_v_total_for_static_ramp(core_freesync, streams[0], + index, &v_total); + + /* Update the freesync context for the stream */ + update_stream_freesync_context(core_freesync, streams[0]); + + /* Program static screen ramp values */ + core_freesync->dc->stream_funcs.adjust_vmin_vmax( + core_freesync->dc, streams, + num_streams, v_total, + v_total); + + triggers.overlay_update = true; + triggers.surface_update = true; + + core_freesync->dc->stream_funcs.set_static_screen_events( + core_freesync->dc, streams, num_streams, + &triggers); + } +} + +void mod_freesync_update_state(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams, + struct mod_freesync_params *freesync_params) +{ + bool freesync_program_required = false; + unsigned int stream_index; + struct freesync_state *state; + struct core_freesync *core_freesync = NULL; + struct dc_static_screen_events triggers = {0}; + + if (mod_freesync == NULL) + return; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + + if (core_freesync->num_entities == 0) + return; + + for(stream_index = 0; stream_index < num_streams; stream_index++) { + + unsigned int map_index = map_index_from_stream(core_freesync, + streams[stream_index]); + + bool is_embedded = dc_is_embedded_signal( + streams[stream_index]->sink->sink_signal); + + struct freesync_registry_options *opts = &core_freesync->opts; + + state = &core_freesync->map[map_index].state; + + switch (freesync_params->state){ + case FREESYNC_STATE_FULLSCREEN: + state->fullscreen = freesync_params->enable; + freesync_program_required = true; + state->windowed_fullscreen = + freesync_params->windowed_fullscreen; + break; + case FREESYNC_STATE_STATIC_SCREEN: + /* Static screen ramp is disabled by default, but can + * be enabled through regkey. + */ + if ((is_embedded && opts->drr_internal_supported) || + (!is_embedded && opts->drr_external_supported)) + + if (state->static_screen != + freesync_params->enable) { + + /* Change the state flag */ + state->static_screen = + freesync_params->enable; + + /* Update static screen ramp */ + set_static_ramp_variables(core_freesync, + map_index, + freesync_params->enable); + } + /* We program the ramp starting next VUpdate */ + break; + case FREESYNC_STATE_VIDEO: + /* Change core variables only if there is a change*/ + if(freesync_params->update_duration_in_ns != + state->time.update_duration_in_ns) { + + state->video = freesync_params->enable; + state->time.update_duration_in_ns = + freesync_params->update_duration_in_ns; + + freesync_program_required = true; + } + break; + case FREESYNC_STATE_NONE: + /* handle here to avoid warning */ + break; + } + } + + /* Update mask */ + triggers.overlay_update = true; + triggers.surface_update = true; + + core_freesync->dc->stream_funcs.set_static_screen_events( + core_freesync->dc, streams, num_streams, + &triggers); + + if (freesync_program_required) + /* Program freesync according to current state*/ + set_freesync_on_streams(core_freesync, streams, num_streams); +} + + +bool mod_freesync_get_state(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + struct mod_freesync_params *freesync_params) +{ + unsigned int index = 0; + struct core_freesync *core_freesync = NULL; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, stream); + + if (core_freesync->map[index].state.fullscreen) { + freesync_params->state = FREESYNC_STATE_FULLSCREEN; + freesync_params->enable = true; + } else if (core_freesync->map[index].state.static_screen) { + freesync_params->state = FREESYNC_STATE_STATIC_SCREEN; + freesync_params->enable = true; + } else if (core_freesync->map[index].state.video) { + freesync_params->state = FREESYNC_STATE_VIDEO; + freesync_params->enable = true; + } else { + freesync_params->state = FREESYNC_STATE_NONE; + freesync_params->enable = false; + } + + freesync_params->update_duration_in_ns = + core_freesync->map[index].state.time.update_duration_in_ns; + + freesync_params->windowed_fullscreen = + core_freesync->map[index].state.windowed_fullscreen; + + return true; +} + +bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams, + struct mod_freesync_user_enable *user_enable) +{ + unsigned int stream_index, map_index; + int persistent_data = 0; + struct persistent_data_flag flag; + struct dc *dc = NULL; + struct core_freesync *core_freesync = NULL; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + dc = core_freesync->dc; + + flag.save_per_edid = true; + flag.save_per_link = false; + + for(stream_index = 0; stream_index < num_streams; + stream_index++){ + + map_index = map_index_from_stream(core_freesync, + streams[stream_index]); + + core_freesync->map[map_index].user_enable = *user_enable; + + /* Write persistent data in registry*/ + if (core_freesync->map[map_index].user_enable. + enable_for_gaming) + persistent_data = persistent_data | 1; + if (core_freesync->map[map_index].user_enable. + enable_for_static) + persistent_data = persistent_data | 2; + if (core_freesync->map[map_index].user_enable. + enable_for_video) + persistent_data = persistent_data | 4; + + dm_write_persistent_data(dc->ctx, + streams[stream_index]->sink, + FREESYNC_REGISTRY_NAME, + "userenable", + &persistent_data, + sizeof(int), + &flag); + } + + set_freesync_on_streams(core_freesync, streams, num_streams); + + return true; +} + +bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + struct mod_freesync_user_enable *user_enable) +{ + unsigned int index = 0; + struct core_freesync *core_freesync = NULL; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, stream); + + *user_enable = core_freesync->map[index].user_enable; + + return true; +} + +bool mod_freesync_get_static_ramp_active(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + bool *is_ramp_active) +{ + unsigned int index = 0; + struct core_freesync *core_freesync = NULL; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, stream); + + *is_ramp_active = + core_freesync->map[index].state.static_ramp.ramp_is_active; + + return true; +} + +bool mod_freesync_override_min_max(struct mod_freesync *mod_freesync, + struct dc_stream_state *streams, + unsigned int min_refresh, + unsigned int max_refresh, + struct mod_freesync_caps *caps) +{ + unsigned int index = 0; + struct core_freesync *core_freesync; + struct freesync_state *state; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, streams); + state = &core_freesync->map[index].state; + + if (max_refresh == 0) + max_refresh = state->nominal_refresh_rate_in_micro_hz; + + if (min_refresh == 0) { + /* Restore defaults */ + calc_freesync_range(core_freesync, streams, state, + core_freesync->map[index].caps-> + min_refresh_in_micro_hz, + state->nominal_refresh_rate_in_micro_hz); + } else { + calc_freesync_range(core_freesync, streams, + state, + min_refresh, + max_refresh); + + /* Program vtotal min/max */ + core_freesync->dc->stream_funcs.adjust_vmin_vmax( + core_freesync->dc, &streams, 1, + state->freesync_range.vmin, + state->freesync_range.vmax); + } + + if (min_refresh != 0 && + dc_is_embedded_signal(streams->sink->sink_signal) && + (max_refresh - min_refresh >= 10000000)) { + caps->supported = true; + caps->min_refresh_in_micro_hz = min_refresh; + caps->max_refresh_in_micro_hz = max_refresh; + } + + /* Update the stream */ + update_stream(core_freesync, streams); + + return true; +} + +bool mod_freesync_get_min_max(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + unsigned int *min_refresh, + unsigned int *max_refresh) +{ + unsigned int index = 0; + struct core_freesync *core_freesync = NULL; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, stream); + + *min_refresh = + core_freesync->map[index].state.freesync_range.min_refresh; + *max_refresh = + core_freesync->map[index].state.freesync_range.max_refresh; + + return true; +} + +bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + unsigned int *vmin, + unsigned int *vmax) +{ + unsigned int index = 0; + struct core_freesync *core_freesync = NULL; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, stream); + + *vmin = + core_freesync->map[index].state.freesync_range.vmin; + *vmax = + core_freesync->map[index].state.freesync_range.vmax; + + return true; +} + +bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + unsigned int *nom_v_pos, + unsigned int *v_pos) +{ + unsigned int index = 0; + struct core_freesync *core_freesync = NULL; + struct crtc_position position; + + if (mod_freesync == NULL) + return false; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + index = map_index_from_stream(core_freesync, stream); + + if (core_freesync->dc->stream_funcs.get_crtc_position( + core_freesync->dc, &stream, 1, + &position.vertical_count, &position.nominal_vcount)) { + + *nom_v_pos = position.nominal_vcount; + *v_pos = position.vertical_count; + + return true; + } + + return false; +} + +void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams) +{ + unsigned int stream_index, map_index; + struct freesync_state *state; + struct core_freesync *core_freesync = NULL; + struct dc_static_screen_events triggers = {0}; + unsigned long long temp = 0; + + if (mod_freesync == NULL) + return; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + + for (stream_index = 0; stream_index < num_streams; stream_index++) { + map_index = map_index_from_stream(core_freesync, + streams[stream_index]); + + state = &core_freesync->map[map_index].state; + + /* Update the field rate for new timing */ + temp = streams[stream_index]->timing.pix_clk_khz; + temp *= 1000ULL * 1000ULL * 1000ULL; + temp = div_u64(temp, + streams[stream_index]->timing.h_total); + temp = div_u64(temp, + streams[stream_index]->timing.v_total); + state->nominal_refresh_rate_in_micro_hz = + (unsigned int) temp; + + if (core_freesync->map[map_index].caps->supported) { + + /* Update the stream */ + update_stream(core_freesync, streams[stream_index]); + + /* Calculate vmin/vmax and refresh rate for + * current mode + */ + calc_freesync_range(core_freesync, *streams, state, + core_freesync->map[map_index].caps-> + min_refresh_in_micro_hz, + state->nominal_refresh_rate_in_micro_hz); + + /* Update mask */ + triggers.overlay_update = true; + triggers.surface_update = true; + + core_freesync->dc->stream_funcs.set_static_screen_events( + core_freesync->dc, streams, num_streams, + &triggers); + } + } + + /* Program freesync according to current state*/ + set_freesync_on_streams(core_freesync, streams, num_streams); +} + +/* Add the timestamps to the cache and determine whether BTR programming + * is required, depending on the times calculated + */ +static void update_timestamps(struct core_freesync *core_freesync, + const struct dc_stream_state *stream, unsigned int map_index, + unsigned int last_render_time_in_us) +{ + struct freesync_state *state = &core_freesync->map[map_index].state; + + state->time.render_times[state->time.render_times_index] = + last_render_time_in_us; + state->time.render_times_index++; + + if (state->time.render_times_index >= RENDER_TIMES_MAX_COUNT) + state->time.render_times_index = 0; + + if (last_render_time_in_us + BTR_EXIT_MARGIN < + state->time.max_render_time_in_us) { + + /* Exit Below the Range */ + if (state->btr.btr_active) { + + state->btr.program_btr = true; + state->btr.btr_active = false; + state->btr.frame_counter = 0; + + /* Exit Fixed Refresh mode */ + } else if (state->fixed_refresh.fixed_active) { + + state->fixed_refresh.frame_counter++; + + if (state->fixed_refresh.frame_counter > + FIXED_REFRESH_EXIT_FRAME_COUNT) { + state->fixed_refresh.frame_counter = 0; + state->fixed_refresh.program_fixed = true; + state->fixed_refresh.fixed_active = false; + } + } + + } else if (last_render_time_in_us > state->time.max_render_time_in_us) { + + /* Enter Below the Range */ + if (!state->btr.btr_active && + core_freesync->map[map_index].caps->btr_supported) { + + state->btr.program_btr = true; + state->btr.btr_active = true; + + /* Enter Fixed Refresh mode */ + } else if (!state->fixed_refresh.fixed_active && + !core_freesync->map[map_index].caps->btr_supported) { + + state->fixed_refresh.frame_counter++; + + if (state->fixed_refresh.frame_counter > + FIXED_REFRESH_ENTER_FRAME_COUNT) { + state->fixed_refresh.frame_counter = 0; + state->fixed_refresh.program_fixed = true; + state->fixed_refresh.fixed_active = true; + } + } + } + + /* When Below the Range is active, must react on every frame */ + if (state->btr.btr_active) + state->btr.program_btr = true; +} + +static void apply_below_the_range(struct core_freesync *core_freesync, + struct dc_stream_state *stream, unsigned int map_index, + unsigned int last_render_time_in_us) +{ + unsigned int inserted_frame_duration_in_us = 0; + unsigned int mid_point_frames_ceil = 0; + unsigned int mid_point_frames_floor = 0; + unsigned int frame_time_in_us = 0; + unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; + unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; + unsigned int frames_to_insert = 0; + unsigned int min_frame_duration_in_ns = 0; + struct freesync_state *state = &core_freesync->map[map_index].state; + + if (!state->btr.program_btr) + return; + + state->btr.program_btr = false; + + min_frame_duration_in_ns = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + state->nominal_refresh_rate_in_micro_hz))); + + /* Program BTR */ + + /* BTR set to "not active" so disengage */ + if (!state->btr.btr_active) + + /* Restore FreeSync */ + set_freesync_on_streams(core_freesync, &stream, 1); + + /* BTR set to "active" so engage */ + else { + + /* Calculate number of midPoint frames that could fit within + * the render time interval- take ceil of this value + */ + mid_point_frames_ceil = (last_render_time_in_us + + state->btr.mid_point_in_us- 1) / + state->btr.mid_point_in_us; + + if (mid_point_frames_ceil > 0) { + + frame_time_in_us = last_render_time_in_us / + mid_point_frames_ceil; + delta_from_mid_point_in_us_1 = + (state->btr.mid_point_in_us > + frame_time_in_us) ? + (state->btr.mid_point_in_us - frame_time_in_us): + (frame_time_in_us - state->btr.mid_point_in_us); + } + + /* Calculate number of midPoint frames that could fit within + * the render time interval- take floor of this value + */ + mid_point_frames_floor = last_render_time_in_us / + state->btr.mid_point_in_us; + + if (mid_point_frames_floor > 0) { + + frame_time_in_us = last_render_time_in_us / + mid_point_frames_floor; + delta_from_mid_point_in_us_2 = + (state->btr.mid_point_in_us > + frame_time_in_us) ? + (state->btr.mid_point_in_us - frame_time_in_us): + (frame_time_in_us - state->btr.mid_point_in_us); + } + + /* Choose number of frames to insert based on how close it + * can get to the mid point of the variable range. + */ + if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) + frames_to_insert = mid_point_frames_ceil; + else + frames_to_insert = mid_point_frames_floor; + + /* Either we've calculated the number of frames to insert, + * or we need to insert min duration frames + */ + if (frames_to_insert > 0) + inserted_frame_duration_in_us = last_render_time_in_us / + frames_to_insert; + + if (inserted_frame_duration_in_us < + state->time.min_render_time_in_us) + + inserted_frame_duration_in_us = + state->time.min_render_time_in_us; + + /* Cache the calculated variables */ + state->btr.inserted_frame_duration_in_us = + inserted_frame_duration_in_us; + state->btr.frames_to_insert = frames_to_insert; + state->btr.frame_counter = frames_to_insert; + + } +} + +static void apply_fixed_refresh(struct core_freesync *core_freesync, + struct dc_stream_state *stream, unsigned int map_index) +{ + unsigned int vmin = 0, vmax = 0; + struct freesync_state *state = &core_freesync->map[map_index].state; + + if (!state->fixed_refresh.program_fixed) + return; + + state->fixed_refresh.program_fixed = false; + + /* Program Fixed Refresh */ + + /* Fixed Refresh set to "not active" so disengage */ + if (!state->fixed_refresh.fixed_active) { + set_freesync_on_streams(core_freesync, &stream, 1); + + /* Fixed Refresh set to "active" so engage (fix to max) */ + } else { + + vmin = state->freesync_range.vmin; + + vmax = vmin; + + core_freesync->dc->stream_funcs.adjust_vmin_vmax( + core_freesync->dc, &stream, + 1, vmin, + vmax); + } +} + +void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams, + unsigned int curr_time_stamp_in_us) +{ + unsigned int stream_index, map_index, last_render_time_in_us = 0; + struct core_freesync *core_freesync = NULL; + + if (mod_freesync == NULL) + return; + + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + + for (stream_index = 0; stream_index < num_streams; stream_index++) { + + map_index = map_index_from_stream(core_freesync, + streams[stream_index]); + + if (core_freesync->map[map_index].caps->supported) { + + last_render_time_in_us = curr_time_stamp_in_us - + core_freesync->map[map_index].state.time. + prev_time_stamp_in_us; + + /* Add the timestamps to the cache and determine + * whether BTR program is required + */ + update_timestamps(core_freesync, streams[stream_index], + map_index, last_render_time_in_us); + + if (core_freesync->map[map_index].state.fullscreen && + core_freesync->map[map_index].user_enable. + enable_for_gaming) { + + if (core_freesync->map[map_index].caps->btr_supported) { + + apply_below_the_range(core_freesync, + streams[stream_index], map_index, + last_render_time_in_us); + } else { + apply_fixed_refresh(core_freesync, + streams[stream_index], map_index); + } + } + + core_freesync->map[map_index].state.time. + prev_time_stamp_in_us = curr_time_stamp_in_us; + } + + } +} diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h new file mode 100644 index 000000000000..84b53425f2c8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h @@ -0,0 +1,167 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + + + +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef MOD_FREESYNC_H_ +#define MOD_FREESYNC_H_ + +#include "dm_services.h" + +struct mod_freesync *mod_freesync_create(struct dc *dc); +void mod_freesync_destroy(struct mod_freesync *mod_freesync); + +struct mod_freesync { + int dummy; +}; + +enum mod_freesync_state { + FREESYNC_STATE_NONE, + FREESYNC_STATE_FULLSCREEN, + FREESYNC_STATE_STATIC_SCREEN, + FREESYNC_STATE_VIDEO +}; + +enum mod_freesync_user_enable_mask { + FREESYNC_USER_ENABLE_STATIC = 0x1, + FREESYNC_USER_ENABLE_VIDEO = 0x2, + FREESYNC_USER_ENABLE_GAMING = 0x4 +}; + +struct mod_freesync_user_enable { + bool enable_for_static; + bool enable_for_video; + bool enable_for_gaming; +}; + +struct mod_freesync_caps { + bool supported; + unsigned int min_refresh_in_micro_hz; + unsigned int max_refresh_in_micro_hz; + + bool btr_supported; +}; + +struct mod_freesync_params { + enum mod_freesync_state state; + bool enable; + unsigned int update_duration_in_ns; + bool windowed_fullscreen; +}; + +/* + * Add stream to be tracked by module + */ +bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, struct mod_freesync_caps *caps); + +/* + * Remove stream to be tracked by module + */ +bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream); + +/* + * Update the freesync state flags for each display and program + * freesync accordingly + */ +void mod_freesync_update_state(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams, + struct mod_freesync_params *freesync_params); + +bool mod_freesync_get_state(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + struct mod_freesync_params *freesync_params); + +bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams, + struct mod_freesync_user_enable *user_enable); + +bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + struct mod_freesync_user_enable *user_enable); + +bool mod_freesync_get_static_ramp_active(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + bool *is_ramp_active); + +bool mod_freesync_override_min_max(struct mod_freesync *mod_freesync, + struct dc_stream_state *streams, + unsigned int min_refresh, + unsigned int max_refresh, + struct mod_freesync_caps *caps); + +bool mod_freesync_get_min_max(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + unsigned int *min_refresh, + unsigned int *max_refresh); + +bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + unsigned int *vmin, + unsigned int *vmax); + +bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, + struct dc_stream_state *stream, + unsigned int *nom_v_pos, + unsigned int *v_pos); + +void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams); + +void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams); + +void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, + struct dc_stream_state **streams, int num_streams, + unsigned int curr_time_stamp); + +#endif diff --git a/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_offset.h b/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_offset.h index b39fb6821faa..4ccf9681c45d 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_offset.h +++ b/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_offset.h @@ -2283,6 +2283,10 @@ #define mmDCHUBBUB_VLINE_SNAPSHOT_BASE_IDX 2 #define mmDCHUBBUB_SPARE 0x0534 #define mmDCHUBBUB_SPARE_BASE_IDX 2 +#define mmDCHUBBUB_TEST_DEBUG_INDEX 0x053a +#define mmDCHUBBUB_TEST_DEBUG_INDEX_BASE_IDX 2 +#define mmDCHUBBUB_TEST_DEBUG_DATA 0x053b +#define mmDCHUBBUB_TEST_DEBUG_DATA_BASE_IDX 2 // addressBlock: dce_dc_dchubbub_dchubbub_dcperfmon_dc_perfmon_dispdec @@ -10361,6 +10365,8 @@ #define mmUNIPHYG_CHANNEL_XBAR_CNTL_BASE_IDX 2 #define mmDCIO_WRCMD_DELAY 0x287e #define mmDCIO_WRCMD_DELAY_BASE_IDX 2 +#define mmDC_PINSTRAPS 0x2880 +#define mmDC_PINSTRAPS_BASE_IDX 2 #define mmDC_DVODATA_CONFIG 0x2882 #define mmDC_DVODATA_CONFIG_BASE_IDX 2 #define mmLVTMA_PWRSEQ_CNTL 0x2883 diff --git a/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_sh_mask.h index 1e98ce86ed19..b28d4b64c05d 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/raven1/DCN/dcn_1_0_sh_mask.h @@ -9361,12 +9361,14 @@ #define HUBPREQ0_DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH_C__SECONDARY_META_SURFACE_ADDRESS_HIGH_C__SHIFT 0x0 #define HUBPREQ0_DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH_C__SECONDARY_META_SURFACE_ADDRESS_HIGH_C_MASK 0x0000FFFFL //HUBPREQ0_DCSURF_SURFACE_CONTROL +#define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_TMZ__SHIFT 0x0 #define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_DCC_EN__SHIFT 0x1 #define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_DCC_IND_64B_BLK__SHIFT 0x2 #define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_DCC_IND_64B_BLK_C__SHIFT 0x5 #define HUBPREQ0_DCSURF_SURFACE_CONTROL__SECONDARY_SURFACE_DCC_EN__SHIFT 0x9 #define HUBPREQ0_DCSURF_SURFACE_CONTROL__SECONDARY_SURFACE_DCC_IND_64B_BLK__SHIFT 0xa #define HUBPREQ0_DCSURF_SURFACE_CONTROL__SECONDARY_SURFACE_DCC_IND_64B_BLK_C__SHIFT 0xd +#define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_TMZ_MASK 0x00000001L #define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_DCC_EN_MASK 0x00000002L #define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_DCC_IND_64B_BLK_MASK 0x00000004L #define HUBPREQ0_DCSURF_SURFACE_CONTROL__PRIMARY_SURFACE_DCC_IND_64B_BLK_C_MASK 0x00000020L @@ -39956,6 +39958,9 @@ #define DCIO_WRCMD_DELAY__DPHY_DELAY_MASK 0x00000F00L #define DCIO_WRCMD_DELAY__DCRXPHY_DELAY_MASK 0x0000F000L #define DCIO_WRCMD_DELAY__ZCAL_DELAY_MASK 0x000F0000L +//DC_PINSTRAPS +#define DC_PINSTRAPS__DC_PINSTRAPS_AUDIO__SHIFT 0xe +#define DC_PINSTRAPS__DC_PINSTRAPS_AUDIO_MASK 0x0000C000L //DC_DVODATA_CONFIG #define DC_DVODATA_CONFIG__VIP_MUX_EN__SHIFT 0x13 #define DC_DVODATA_CONFIG__VIP_ALTER_MAPPING_EN__SHIFT 0x14 diff --git a/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_offset.h b/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_offset.h index 75b660d57bdf..f730d0629020 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_offset.h +++ b/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_offset.h @@ -1841,6 +1841,10 @@ #define mmUNIPHYG_CHANNEL_XBAR_CNTL_BASE_IDX 2 #define mmDCIO_WRCMD_DELAY 0x2094 #define mmDCIO_WRCMD_DELAY_BASE_IDX 2 +#define mmDC_PINSTRAPS 0x2096 +#define mmDC_PINSTRAPS_BASE_IDX 2 +#define mmCC_DC_MISC_STRAPS 0x2097 +#define mmCC_DC_MISC_STRAPS_BASE_IDX 2 #define mmDC_DVODATA_CONFIG 0x2098 #define mmDC_DVODATA_CONFIG_BASE_IDX 2 #define mmLVTMA_PWRSEQ_CNTL 0x2099 diff --git a/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_sh_mask.h index d8ad862b3a74..6d3162c42957 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/vega10/DC/dce_12_0_sh_mask.h @@ -2447,6 +2447,14 @@ //DCCG_CBUS_WRCMD_DELAY #define DCCG_CBUS_WRCMD_DELAY__CBUS_PLL_WRCMD_DELAY__SHIFT 0x0 #define DCCG_CBUS_WRCMD_DELAY__CBUS_PLL_WRCMD_DELAY_MASK 0x0000000FL +//DC_PINSTRAPS +#define DC_PINSTRAPS__DC_PINSTRAPS_AUDIO__SHIFT 0xe +#define DC_PINSTRAPS__DC_PINSTRAPS_AUDIO_MASK 0x0000C000L +//CC_DC_MISC_STRAPS +#define CC_DC_MISC_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_MISC_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#define CC_DC_MISC_STRAPS__HDMI_DISABLE_MASK 0x00000040L +#define CC_DC_MISC_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x00000700L //DCCG_DS_DTO_INCR #define DCCG_DS_DTO_INCR__DCCG_DS_DTO_INCR__SHIFT 0x0 #define DCCG_DS_DTO_INCR__DCCG_DS_DTO_INCR_MASK 0xFFFFFFFFL diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c index a129bc5b1844..c6febbf0bf69 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c @@ -1486,7 +1486,7 @@ int atomctrl_get_leakage_vddc_base_on_leakage(struct pp_hwmgr *hwmgr, if (vddci_id_buf[i] == virtual_voltage_id) { for (j = 0; j < profile->ucLeakageBinNum; j++) { if (efuse_voltage_id <= leakage_bin[j]) { - *vddci = vddci_buf[j * profile->ucElbVDDC_Num + i]; + *vddci = vddci_buf[j * profile->ucElbVDDCI_Num + i]; break; } } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c index d1af1483c69b..a651ebcf44fd 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c @@ -830,9 +830,9 @@ static int init_over_drive_limits( const ATOM_Tonga_POWERPLAYTABLE *powerplay_table) { hwmgr->platform_descriptor.overdriveLimit.engineClock = - le16_to_cpu(powerplay_table->ulMaxODEngineClock); + le32_to_cpu(powerplay_table->ulMaxODEngineClock); hwmgr->platform_descriptor.overdriveLimit.memoryClock = - le16_to_cpu(powerplay_table->ulMaxODMemoryClock); + le32_to_cpu(powerplay_table->ulMaxODMemoryClock); hwmgr->platform_descriptor.minOverdriveVDDC = 0; hwmgr->platform_descriptor.maxOverdriveVDDC = 0; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c index 4466469cf8ab..e33ec7fc5d09 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c @@ -3778,7 +3778,7 @@ static int smu7_unfreeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr) "Trying to Unfreeze MCLK DPM when DPM is disabled", ); PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr, - PPSMC_MSG_SCLKDPM_UnfreezeLevel), + PPSMC_MSG_MCLKDPM_UnfreezeLevel), "Failed to unfreeze MCLK DPM during UnFreezeSclkMclkDPM Function!", return -EINVAL); } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c index 4f79c21f27ed..f8d838c2c8ee 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c @@ -753,6 +753,7 @@ static int vega10_hwmgr_backend_init(struct pp_hwmgr *hwmgr) uint32_t config_telemetry = 0; struct pp_atomfwctrl_voltage_table vol_table; struct cgs_system_info sys_info = {0}; + uint32_t reg; data = kzalloc(sizeof(struct vega10_hwmgr), GFP_KERNEL); if (data == NULL) @@ -859,6 +860,16 @@ static int vega10_hwmgr_backend_init(struct pp_hwmgr *hwmgr) advanceFanControlParameters.usFanPWMMinLimit * hwmgr->thermal_controller.fanInfo.ulMaxRPM / 100; + reg = soc15_get_register_offset(DF_HWID, 0, + mmDF_CS_AON0_DramBaseAddress0_BASE_IDX, + mmDF_CS_AON0_DramBaseAddress0); + data->mem_channels = (cgs_read_register(hwmgr->device, reg) & + DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK) >> + DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT; + PP_ASSERT_WITH_CODE(data->mem_channels < ARRAY_SIZE(channel_number), + "Mem Channel Index Exceeded maximum!", + return -EINVAL); + return result; } @@ -1777,7 +1788,7 @@ static int vega10_populate_all_memory_levels(struct pp_hwmgr *hwmgr) struct vega10_single_dpm_table *dpm_table = &(data->dpm_table.mem_table); int result = 0; - uint32_t i, j, reg, mem_channels; + uint32_t i, j; for (i = 0; i < dpm_table->count; i++) { result = vega10_populate_single_memory_level(hwmgr, @@ -1801,20 +1812,10 @@ static int vega10_populate_all_memory_levels(struct pp_hwmgr *hwmgr) i++; } - reg = soc15_get_register_offset(DF_HWID, 0, - mmDF_CS_AON0_DramBaseAddress0_BASE_IDX, - mmDF_CS_AON0_DramBaseAddress0); - mem_channels = (cgs_read_register(hwmgr->device, reg) & - DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK) >> - DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT; - PP_ASSERT_WITH_CODE(mem_channels < ARRAY_SIZE(channel_number), - "Mem Channel Index Exceeded maximum!", - return -1); - - pp_table->NumMemoryChannels = cpu_to_le16(mem_channels); + pp_table->NumMemoryChannels = (uint16_t)(data->mem_channels); pp_table->MemoryChannelWidth = - cpu_to_le16(HBM_MEMORY_CHANNEL_WIDTH * - channel_number[mem_channels]); + (uint16_t)(HBM_MEMORY_CHANNEL_WIDTH * + channel_number[data->mem_channels]); pp_table->LowestUclkReservedForUlv = (uint8_t)(data->lowest_uclk_reserved_for_ulv); diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.h index b4b461c3b8ee..8f7358cc3327 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.h @@ -389,6 +389,7 @@ struct vega10_hwmgr { uint32_t config_telemetry; uint32_t smu_version; uint32_t acg_loop_state; + uint32_t mem_channels; }; #define VEGA10_DPM2_NEAR_TDP_DEC 10 diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 704fc8934616..25f4b2e9a44f 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -234,6 +234,10 @@ int drm_connector_init(struct drm_device *dev, config->link_status_property, 0); + drm_object_attach_property(&connector->base, + config->non_desktop_property, + 0); + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); } @@ -763,6 +767,10 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, * value of link-status is "GOOD". If something fails during or after modeset, * the kernel driver may set this to "BAD" and issue a hotplug uevent. Drivers * should update this value using drm_mode_connector_set_link_status_property(). + * non_desktop: + * Indicates the output should be ignored for purposes of displaying a + * standard desktop environment or console. This is most likely because + * the output device is not rectilinear. * * Connectors also have one standardized atomic property: * @@ -811,6 +819,11 @@ int drm_connector_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.link_status_property = prop; + prop = drm_property_create_bool(dev, DRM_MODE_PROP_IMMUTABLE, "non-desktop"); + if (!prop) + return -ENOMEM; + dev->mode_config.non_desktop_property = prop; + return 0; } @@ -1194,6 +1207,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, if (edid) size = EDID_LENGTH * (1 + edid->extensions); + drm_object_property_set_value(&connector->base, + dev->mode_config.non_desktop_property, + connector->display_info.non_desktop); + ret = drm_property_replace_global_blob(dev, &connector->edid_blob_ptr, size, diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 00ddabfbf980..2e8fb51282ef 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -82,6 +82,8 @@ #define EDID_QUIRK_FORCE_6BPC (1 << 10) /* Force 10bpc */ #define EDID_QUIRK_FORCE_10BPC (1 << 11) +/* Non desktop display (i.e. HMD) */ +#define EDID_QUIRK_NON_DESKTOP (1 << 12) struct detailed_mode_closure { struct drm_connector *connector; @@ -157,6 +159,9 @@ static const struct edid_quirk { /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/ { "ETR", 13896, EDID_QUIRK_FORCE_8BPC }, + + /* HTC Vive VR Headset */ + { "HVR", 0xaa01, EDID_QUIRK_NON_DESKTOP }, }; /* @@ -4393,7 +4398,7 @@ static void drm_parse_cea_ext(struct drm_connector *connector, } static void drm_add_display_info(struct drm_connector *connector, - struct edid *edid) + struct edid *edid, u32 quirks) { struct drm_display_info *info = &connector->display_info; @@ -4407,6 +4412,8 @@ static void drm_add_display_info(struct drm_connector *connector, info->max_tmds_clock = 0; info->dvi_dual = false; + info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP); + if (edid->revision < 3) return; @@ -4627,7 +4634,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) * To avoid multiple parsing of same block, lets parse that map * from sink info, before parsing CEA modes. */ - drm_add_display_info(connector, edid); + drm_add_display_info(connector, edid, quirks); /* * EDID spec says modes should be preferred in this order: diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 116d1f1337c7..07374008f146 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -2033,6 +2033,9 @@ static bool drm_connector_enabled(struct drm_connector *connector, bool strict) { bool enable; + if (connector->display_info.non_desktop) + return false; + if (strict) enable = connector->status == connector_status_connected; else @@ -2052,7 +2055,8 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper, connector = fb_helper->connector_info[i]->connector; enabled[i] = drm_connector_enabled(connector, true); DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, - enabled[i] ? "yes" : "no"); + connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); + any_enabled |= enabled[i]; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 58e9e0601a61..faf17b83b910 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -210,7 +210,6 @@ static int fsl_dcu_drm_pm_suspend(struct device *dev) return PTR_ERR(fsl_dev->state); } - clk_disable_unprepare(fsl_dev->pix_clk); clk_disable_unprepare(fsl_dev->clk); return 0; @@ -233,6 +232,7 @@ static int fsl_dcu_drm_pm_resume(struct device *dev) if (fsl_dev->tcon) fsl_tcon_bypass_enable(fsl_dev->tcon); fsl_dcu_drm_init_planes(fsl_dev->drm); + enable_irq(fsl_dev->irq); drm_atomic_helper_resume(fsl_dev->drm, fsl_dev->state); console_lock(); @@ -240,7 +240,6 @@ static int fsl_dcu_drm_pm_resume(struct device *dev) console_unlock(); drm_kms_helper_poll_enable(fsl_dev->drm); - enable_irq(fsl_dev->irq); return 0; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index edd7d8127d19..c54806d08dd7 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -102,7 +102,6 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev, { struct drm_encoder *encoder = &fsl_dev->encoder; struct drm_connector *connector = &fsl_dev->connector.base; - struct drm_mode_config *mode_config = &fsl_dev->drm->mode_config; int ret; fsl_dev->connector.encoder = encoder; @@ -122,10 +121,6 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev, if (ret < 0) goto err_sysfs; - drm_object_property_set_value(&connector->base, - mode_config->dpms_property, - DRM_MODE_DPMS_OFF); - ret = drm_panel_attach(panel, connector); if (ret) { dev_err(fsl_dev->dev, "failed to attach panel\n"); diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 53e0b24beda6..9a9961802f5c 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -115,7 +115,7 @@ static void imx_drm_crtc_reset(struct drm_crtc *crtc) if (crtc->state) { if (crtc->state->mode_blob) - drm_property_unreference_blob(crtc->state->mode_blob); + drm_property_blob_put(crtc->state->mode_blob); state = to_imx_crtc_state(crtc->state); memset(state, 0, sizeof(*state)); diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 8def97d75030..aedecda9728a 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -183,7 +183,7 @@ static int imx_pd_register(struct drm_device *drm, &imx_pd_connector_helper_funcs); drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs, - DRM_MODE_CONNECTOR_VGA); + DRM_MODE_CONNECTOR_DPI); } if (imxpd->panel) diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h index 09143b840482..2de40d276116 100644 --- a/drivers/gpu/drm/r128/r128_drv.h +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -147,6 +147,10 @@ extern int r128_engine_reset(struct drm_device *dev, void *data, struct drm_file extern int r128_fullscreen(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int r128_cce_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_cce_stipple(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_cce_depth(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv); + extern void r128_freelist_reset(struct drm_device *dev); extern int r128_wait_ring(drm_r128_private_t *dev_priv, int n); diff --git a/drivers/gpu/drm/r128/r128_ioc32.c b/drivers/gpu/drm/r128/r128_ioc32.c index 663f38c63ba6..6589f9e0310e 100644 --- a/drivers/gpu/drm/r128/r128_ioc32.c +++ b/drivers/gpu/drm/r128/r128_ioc32.c @@ -63,39 +63,36 @@ static int compat_r128_init(struct file *file, unsigned int cmd, unsigned long arg) { drm_r128_init32_t init32; - drm_r128_init_t __user *init; + drm_r128_init_t init; if (copy_from_user(&init32, (void __user *)arg, sizeof(init32))) return -EFAULT; - init = compat_alloc_user_space(sizeof(*init)); - if (!access_ok(VERIFY_WRITE, init, sizeof(*init)) - || __put_user(init32.func, &init->func) - || __put_user(init32.sarea_priv_offset, &init->sarea_priv_offset) - || __put_user(init32.is_pci, &init->is_pci) - || __put_user(init32.cce_mode, &init->cce_mode) - || __put_user(init32.cce_secure, &init->cce_secure) - || __put_user(init32.ring_size, &init->ring_size) - || __put_user(init32.usec_timeout, &init->usec_timeout) - || __put_user(init32.fb_bpp, &init->fb_bpp) - || __put_user(init32.front_offset, &init->front_offset) - || __put_user(init32.front_pitch, &init->front_pitch) - || __put_user(init32.back_offset, &init->back_offset) - || __put_user(init32.back_pitch, &init->back_pitch) - || __put_user(init32.depth_bpp, &init->depth_bpp) - || __put_user(init32.depth_offset, &init->depth_offset) - || __put_user(init32.depth_pitch, &init->depth_pitch) - || __put_user(init32.span_offset, &init->span_offset) - || __put_user(init32.fb_offset, &init->fb_offset) - || __put_user(init32.mmio_offset, &init->mmio_offset) - || __put_user(init32.ring_offset, &init->ring_offset) - || __put_user(init32.ring_rptr_offset, &init->ring_rptr_offset) - || __put_user(init32.buffers_offset, &init->buffers_offset) - || __put_user(init32.agp_textures_offset, - &init->agp_textures_offset)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_R128_INIT, (unsigned long)init); + init.func = init32.func; + init.sarea_priv_offset = init32.sarea_priv_offset; + init.is_pci = init32.is_pci; + init.cce_mode = init32.cce_mode; + init.cce_secure = init32.cce_secure; + init.ring_size = init32.ring_size; + init.usec_timeout = init32.usec_timeout; + init.fb_bpp = init32.fb_bpp; + init.front_offset = init32.front_offset; + init.front_pitch = init32.front_pitch; + init.back_offset = init32.back_offset; + init.back_pitch = init32.back_pitch; + init.depth_bpp = init32.depth_bpp; + init.depth_offset = init32.depth_offset; + init.depth_pitch = init32.depth_pitch; + init.span_offset = init32.span_offset; + init.fb_offset = init32.fb_offset; + init.mmio_offset = init32.mmio_offset; + init.ring_offset = init32.ring_offset; + init.ring_rptr_offset = init32.ring_rptr_offset; + init.buffers_offset = init32.buffers_offset; + init.agp_textures_offset = init32.agp_textures_offset; + + return drm_ioctl_kernel(file, r128_cce_init, &init, + DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); } typedef struct drm_r128_depth32 { @@ -111,25 +108,19 @@ static int compat_r128_depth(struct file *file, unsigned int cmd, unsigned long arg) { drm_r128_depth32_t depth32; - drm_r128_depth_t __user *depth; + drm_r128_depth_t depth; if (copy_from_user(&depth32, (void __user *)arg, sizeof(depth32))) return -EFAULT; - depth = compat_alloc_user_space(sizeof(*depth)); - if (!access_ok(VERIFY_WRITE, depth, sizeof(*depth)) - || __put_user(depth32.func, &depth->func) - || __put_user(depth32.n, &depth->n) - || __put_user((int __user *)(unsigned long)depth32.x, &depth->x) - || __put_user((int __user *)(unsigned long)depth32.y, &depth->y) - || __put_user((unsigned int __user *)(unsigned long)depth32.buffer, - &depth->buffer) - || __put_user((unsigned char __user *)(unsigned long)depth32.mask, - &depth->mask)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_R128_DEPTH, (unsigned long)depth); + depth.func = depth32.func; + depth.n = depth32.n; + depth.x = compat_ptr(depth32.x); + depth.y = compat_ptr(depth32.y); + depth.buffer = compat_ptr(depth32.buffer); + depth.mask = compat_ptr(depth32.mask); + return drm_ioctl_kernel(file, r128_cce_depth, &depth, DRM_AUTH); } typedef struct drm_r128_stipple32 { @@ -140,18 +131,14 @@ static int compat_r128_stipple(struct file *file, unsigned int cmd, unsigned long arg) { drm_r128_stipple32_t stipple32; - drm_r128_stipple_t __user *stipple; + drm_r128_stipple_t stipple; if (copy_from_user(&stipple32, (void __user *)arg, sizeof(stipple32))) return -EFAULT; - stipple = compat_alloc_user_space(sizeof(*stipple)); - if (!access_ok(VERIFY_WRITE, stipple, sizeof(*stipple)) - || __put_user((unsigned int __user *)(unsigned long)stipple32.mask, - &stipple->mask)) - return -EFAULT; + stipple.mask = compat_ptr(stipple32.mask); - return drm_ioctl(file, DRM_IOCTL_R128_STIPPLE, (unsigned long)stipple); + return drm_ioctl_kernel(file, r128_cce_stipple, &stipple, DRM_AUTH); } typedef struct drm_r128_getparam32 { @@ -163,19 +150,15 @@ static int compat_r128_getparam(struct file *file, unsigned int cmd, unsigned long arg) { drm_r128_getparam32_t getparam32; - drm_r128_getparam_t __user *getparam; + drm_r128_getparam_t getparam; if (copy_from_user(&getparam32, (void __user *)arg, sizeof(getparam32))) return -EFAULT; - getparam = compat_alloc_user_space(sizeof(*getparam)); - if (!access_ok(VERIFY_WRITE, getparam, sizeof(*getparam)) - || __put_user(getparam32.param, &getparam->param) - || __put_user((void __user *)(unsigned long)getparam32.value, - &getparam->value)) - return -EFAULT; + getparam.param = getparam32.param; + getparam.value = compat_ptr(getparam32.value); - return drm_ioctl(file, DRM_IOCTL_R128_GETPARAM, (unsigned long)getparam); + return drm_ioctl_kernel(file, r128_getparam, &getparam, DRM_AUTH); } drm_ioctl_compat_t *r128_compat_ioctls[] = { diff --git a/drivers/gpu/drm/r128/r128_state.c b/drivers/gpu/drm/r128/r128_state.c index 8fd2d9f58f77..8fdc56c1c953 100644 --- a/drivers/gpu/drm/r128/r128_state.c +++ b/drivers/gpu/drm/r128/r128_state.c @@ -1460,7 +1460,7 @@ static int r128_cce_blit(struct drm_device *dev, void *data, struct drm_file *fi return ret; } -static int r128_cce_depth(struct drm_device *dev, void *data, struct drm_file *file_priv) +int r128_cce_depth(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_r128_private_t *dev_priv = dev->dev_private; drm_r128_depth_t *depth = data; @@ -1492,7 +1492,7 @@ static int r128_cce_depth(struct drm_device *dev, void *data, struct drm_file *f return ret; } -static int r128_cce_stipple(struct drm_device *dev, void *data, struct drm_file *file_priv) +int r128_cce_stipple(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_r128_private_t *dev_priv = dev->dev_private; drm_r128_stipple_t *stipple = data; @@ -1582,7 +1582,7 @@ static int r128_cce_indirect(struct drm_device *dev, void *data, struct drm_file return 0; } -static int r128_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv) +int r128_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_r128_private_t *dev_priv = dev->dev_private; drm_r128_getparam_t *param = data; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 2fcf805d3a16..33b821d6d018 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -245,7 +245,6 @@ static int radeonfb_create(struct drm_fb_helper *helper, } info->par = rfbdev; - info->skip_vt_switch = true; ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj); if (ret) { diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 4bcacd3f4861..b0a1dedac802 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -174,9 +174,9 @@ struct tegra_sor { struct reset_control *rst; struct clk *clk_parent; - struct clk *clk_brick; struct clk *clk_safe; - struct clk *clk_src; + struct clk *clk_out; + struct clk *clk_pad; struct clk *clk_dp; struct clk *clk; @@ -255,7 +255,7 @@ static int tegra_sor_set_parent_clock(struct tegra_sor *sor, struct clk *parent) clk_disable_unprepare(sor->clk); - err = clk_set_parent(sor->clk, parent); + err = clk_set_parent(sor->clk_out, parent); if (err < 0) return err; @@ -266,24 +266,24 @@ static int tegra_sor_set_parent_clock(struct tegra_sor *sor, struct clk *parent) return 0; } -struct tegra_clk_sor_brick { +struct tegra_clk_sor_pad { struct clk_hw hw; struct tegra_sor *sor; }; -static inline struct tegra_clk_sor_brick *to_brick(struct clk_hw *hw) +static inline struct tegra_clk_sor_pad *to_pad(struct clk_hw *hw) { - return container_of(hw, struct tegra_clk_sor_brick, hw); + return container_of(hw, struct tegra_clk_sor_pad, hw); } -static const char * const tegra_clk_sor_brick_parents[] = { +static const char * const tegra_clk_sor_pad_parents[] = { "pll_d2_out0", "pll_dp" }; -static int tegra_clk_sor_brick_set_parent(struct clk_hw *hw, u8 index) +static int tegra_clk_sor_pad_set_parent(struct clk_hw *hw, u8 index) { - struct tegra_clk_sor_brick *brick = to_brick(hw); - struct tegra_sor *sor = brick->sor; + struct tegra_clk_sor_pad *pad = to_pad(hw); + struct tegra_sor *sor = pad->sor; u32 value; value = tegra_sor_readl(sor, SOR_CLK_CNTRL); @@ -304,10 +304,10 @@ static int tegra_clk_sor_brick_set_parent(struct clk_hw *hw, u8 index) return 0; } -static u8 tegra_clk_sor_brick_get_parent(struct clk_hw *hw) +static u8 tegra_clk_sor_pad_get_parent(struct clk_hw *hw) { - struct tegra_clk_sor_brick *brick = to_brick(hw); - struct tegra_sor *sor = brick->sor; + struct tegra_clk_sor_pad *pad = to_pad(hw); + struct tegra_sor *sor = pad->sor; u8 parent = U8_MAX; u32 value; @@ -328,33 +328,33 @@ static u8 tegra_clk_sor_brick_get_parent(struct clk_hw *hw) return parent; } -static const struct clk_ops tegra_clk_sor_brick_ops = { - .set_parent = tegra_clk_sor_brick_set_parent, - .get_parent = tegra_clk_sor_brick_get_parent, +static const struct clk_ops tegra_clk_sor_pad_ops = { + .set_parent = tegra_clk_sor_pad_set_parent, + .get_parent = tegra_clk_sor_pad_get_parent, }; -static struct clk *tegra_clk_sor_brick_register(struct tegra_sor *sor, - const char *name) +static struct clk *tegra_clk_sor_pad_register(struct tegra_sor *sor, + const char *name) { - struct tegra_clk_sor_brick *brick; + struct tegra_clk_sor_pad *pad; struct clk_init_data init; struct clk *clk; - brick = devm_kzalloc(sor->dev, sizeof(*brick), GFP_KERNEL); - if (!brick) + pad = devm_kzalloc(sor->dev, sizeof(*pad), GFP_KERNEL); + if (!pad) return ERR_PTR(-ENOMEM); - brick->sor = sor; + pad->sor = sor; init.name = name; init.flags = 0; - init.parent_names = tegra_clk_sor_brick_parents; - init.num_parents = ARRAY_SIZE(tegra_clk_sor_brick_parents); - init.ops = &tegra_clk_sor_brick_ops; + init.parent_names = tegra_clk_sor_pad_parents; + init.num_parents = ARRAY_SIZE(tegra_clk_sor_pad_parents); + init.ops = &tegra_clk_sor_pad_ops; - brick->hw.init = &init; + pad->hw.init = &init; - clk = devm_clk_register(sor->dev, &brick->hw); + clk = devm_clk_register(sor->dev, &pad->hw); return clk; } @@ -998,8 +998,10 @@ static int tegra_sor_power_down(struct tegra_sor *sor) /* switch to safe parent clock */ err = tegra_sor_set_parent_clock(sor, sor->clk_safe); - if (err < 0) + if (err < 0) { dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); + return err; + } value = tegra_sor_readl(sor, SOR_DP_PADCTL0); value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | @@ -2007,8 +2009,10 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) /* switch to safe parent clock */ err = tegra_sor_set_parent_clock(sor, sor->clk_safe); - if (err < 0) + if (err < 0) { dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); + return; + } div = clk_get_rate(sor->clk) / 1000000 * 4; @@ -2111,13 +2115,17 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_XBAR_CTRL); /* switch to parent clock */ - err = clk_set_parent(sor->clk_src, sor->clk_parent); - if (err < 0) - dev_err(sor->dev, "failed to set source clock: %d\n", err); - - err = tegra_sor_set_parent_clock(sor, sor->clk_src); - if (err < 0) + err = clk_set_parent(sor->clk, sor->clk_parent); + if (err < 0) { dev_err(sor->dev, "failed to set parent clock: %d\n", err); + return; + } + + err = tegra_sor_set_parent_clock(sor, sor->clk_pad); + if (err < 0) { + dev_err(sor->dev, "failed to set pad clock: %d\n", err); + return; + } value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); @@ -2628,11 +2636,24 @@ static int tegra_sor_probe(struct platform_device *pdev) } if (sor->soc->supports_hdmi || sor->soc->supports_dp) { - sor->clk_src = devm_clk_get(&pdev->dev, "source"); - if (IS_ERR(sor->clk_src)) { - err = PTR_ERR(sor->clk_src); - dev_err(sor->dev, "failed to get source clock: %d\n", - err); + struct device_node *np = pdev->dev.of_node; + const char *name; + + /* + * For backwards compatibility with Tegra210 device trees, + * fall back to the old clock name "source" if the new "out" + * clock is not available. + */ + if (of_property_match_string(np, "clock-names", "out") < 0) + name = "source"; + else + name = "out"; + + sor->clk_out = devm_clk_get(&pdev->dev, name); + if (IS_ERR(sor->clk_out)) { + err = PTR_ERR(sor->clk_out); + dev_err(sor->dev, "failed to get %s clock: %d\n", + name, err); goto remove; } } @@ -2658,16 +2679,60 @@ static int tegra_sor_probe(struct platform_device *pdev) goto remove; } + /* + * Starting with Tegra186, the BPMP provides an implementation for + * the pad output clock, so we have to look it up from device tree. + */ + sor->clk_pad = devm_clk_get(&pdev->dev, "pad"); + if (IS_ERR(sor->clk_pad)) { + if (sor->clk_pad != ERR_PTR(-ENOENT)) { + err = PTR_ERR(sor->clk_pad); + goto remove; + } + + /* + * If the pad output clock is not available, then we assume + * we're on Tegra210 or earlier and have to provide our own + * implementation. + */ + sor->clk_pad = NULL; + } + + /* + * The bootloader may have set up the SOR such that it's module clock + * is sourced by one of the display PLLs. However, that doesn't work + * without properly having set up other bits of the SOR. + */ + err = clk_set_parent(sor->clk_out, sor->clk_safe); + if (err < 0) { + dev_err(&pdev->dev, "failed to use safe clock: %d\n", err); + goto remove; + } + platform_set_drvdata(pdev, sor); pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); - sor->clk_brick = tegra_clk_sor_brick_register(sor, "sor1_brick"); - pm_runtime_put(&pdev->dev); + /* + * On Tegra210 and earlier, provide our own implementation for the + * pad output clock. + */ + if (!sor->clk_pad) { + err = pm_runtime_get_sync(&pdev->dev); + if (err < 0) { + dev_err(&pdev->dev, "failed to get runtime PM: %d\n", + err); + goto remove; + } + + sor->clk_pad = tegra_clk_sor_pad_register(sor, + "sor1_pad_clkout"); + pm_runtime_put(&pdev->dev); + } - if (IS_ERR(sor->clk_brick)) { - err = PTR_ERR(sor->clk_brick); - dev_err(&pdev->dev, "failed to register SOR clock: %d\n", err); + if (IS_ERR(sor->clk_pad)) { + err = PTR_ERR(sor->clk_pad); + dev_err(&pdev->dev, "failed to register SOR pad clock: %d\n", + err); goto remove; } diff --git a/drivers/gpu/drm/tilcdc/Kconfig b/drivers/gpu/drm/tilcdc/Kconfig index 28fed7e206d0..81ac82455ce4 100644 --- a/drivers/gpu/drm/tilcdc/Kconfig +++ b/drivers/gpu/drm/tilcdc/Kconfig @@ -12,14 +12,3 @@ config DRM_TILCDC controller, for example AM33xx in beagle-bone, DA8xx, or OMAP-L1xx. This driver replaces the FB_DA8XX fbdev driver. -config DRM_TILCDC_SLAVE_COMPAT - bool "Support device tree blobs using TI LCDC Slave binding" - depends on DRM_TILCDC - default y - select OF_RESOLVE - select OF_OVERLAY - help - Choose this option if you need a kernel that is compatible - with device tree blobs using the obsolete "ti,tilcdc,slave" - binding. If you find "ti,tilcdc,slave"-string from your DTB, - you probably need this. Otherwise you do not. diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile index b9e1108e5b4e..87f9480e43b0 100644 --- a/drivers/gpu/drm/tilcdc/Makefile +++ b/drivers/gpu/drm/tilcdc/Makefile @@ -3,9 +3,6 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) ccflags-y += -Werror endif -obj-$(CONFIG_DRM_TILCDC_SLAVE_COMPAT) += tilcdc_slave_compat.o \ - tilcdc_slave_compat.dtb.o - tilcdc-y := \ tilcdc_plane.o \ tilcdc_crtc.o \ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c deleted file mode 100644 index d2b9e5f04724..000000000000 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2015 Texas Instruments - * Author: Jyri Sarha - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - */ - -/* - * To support the old "ti,tilcdc,slave" binding the binding has to be - * transformed to the new external encoder binding. - */ - -#include -#include -#include -#include -#include -#include - -#include "tilcdc_slave_compat.h" - -struct kfree_table { - int total; - int num; - void **table; -}; - -static int __init kfree_table_init(struct kfree_table *kft) -{ - kft->total = 32; - kft->num = 0; - kft->table = kmalloc(kft->total * sizeof(*kft->table), - GFP_KERNEL); - if (!kft->table) - return -ENOMEM; - - return 0; -} - -static int __init kfree_table_add(struct kfree_table *kft, void *p) -{ - if (kft->num == kft->total) { - void **old = kft->table; - - kft->total *= 2; - kft->table = krealloc(old, kft->total * sizeof(*kft->table), - GFP_KERNEL); - if (!kft->table) { - kft->table = old; - kfree(p); - return -ENOMEM; - } - } - kft->table[kft->num++] = p; - return 0; -} - -static void __init kfree_table_free(struct kfree_table *kft) -{ - int i; - - for (i = 0; i < kft->num; i++) - kfree(kft->table[i]); - - kfree(kft->table); -} - -static -struct property * __init tilcdc_prop_dup(const struct property *prop, - struct kfree_table *kft) -{ - struct property *nprop; - - nprop = kzalloc(sizeof(*nprop), GFP_KERNEL); - if (!nprop || kfree_table_add(kft, nprop)) - return NULL; - - nprop->name = kstrdup(prop->name, GFP_KERNEL); - if (!nprop->name || kfree_table_add(kft, nprop->name)) - return NULL; - - nprop->value = kmemdup(prop->value, prop->length, GFP_KERNEL); - if (!nprop->value || kfree_table_add(kft, nprop->value)) - return NULL; - - nprop->length = prop->length; - - return nprop; -} - -static void __init tilcdc_copy_props(struct device_node *from, - struct device_node *to, - const char * const props[], - struct kfree_table *kft) -{ - struct property *prop; - int i; - - for (i = 0; props[i]; i++) { - prop = of_find_property(from, props[i], NULL); - if (!prop) - continue; - - prop = tilcdc_prop_dup(prop, kft); - if (!prop) - continue; - - prop->next = to->properties; - to->properties = prop; - } -} - -static int __init tilcdc_prop_str_update(struct property *prop, - const char *str, - struct kfree_table *kft) -{ - prop->value = kstrdup(str, GFP_KERNEL); - if (kfree_table_add(kft, prop->value) || !prop->value) - return -ENOMEM; - prop->length = strlen(str)+1; - return 0; -} - -static void __init tilcdc_node_disable(struct device_node *node) -{ - struct property *prop; - - prop = kzalloc(sizeof(*prop), GFP_KERNEL); - if (!prop) - return; - - prop->name = "status"; - prop->value = "disabled"; - prop->length = strlen((char *)prop->value)+1; - - of_update_property(node, prop); -} - -static struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft) -{ - const int size = __dtb_tilcdc_slave_compat_end - - __dtb_tilcdc_slave_compat_begin; - static void *overlay_data; - struct device_node *overlay; - - if (!size) { - pr_warn("%s: No overlay data\n", __func__); - return NULL; - } - - overlay_data = kmemdup(__dtb_tilcdc_slave_compat_begin, - size, GFP_KERNEL); - if (!overlay_data || kfree_table_add(kft, overlay_data)) - return NULL; - - of_fdt_unflatten_tree(overlay_data, NULL, &overlay); - if (!overlay) { - pr_warn("%s: Unfattening overlay tree failed\n", __func__); - return NULL; - } - - return overlay; -} - -static const struct of_device_id tilcdc_slave_of_match[] __initconst = { - { .compatible = "ti,tilcdc,slave", }, - {}, -}; - -static const struct of_device_id tilcdc_of_match[] __initconst = { - { .compatible = "ti,am33xx-tilcdc", }, - {}, -}; - -static const struct of_device_id tilcdc_tda998x_of_match[] __initconst = { - { .compatible = "nxp,tda998x", }, - {}, -}; - -static const char * const tilcdc_slave_props[] __initconst = { - "pinctrl-names", - "pinctrl-0", - "pinctrl-1", - NULL -}; - -static void __init tilcdc_convert_slave_node(void) -{ - struct device_node *slave = NULL, *lcdc = NULL; - struct device_node *i2c = NULL, *fragment = NULL; - struct device_node *overlay, *encoder; - struct property *prop; - /* For all memory needed for the overlay tree. This memory can - be freed after the overlay has been applied. */ - struct kfree_table kft; - int ovcs_id, ret; - - if (kfree_table_init(&kft)) - return; - - lcdc = of_find_matching_node(NULL, tilcdc_of_match); - slave = of_find_matching_node(NULL, tilcdc_slave_of_match); - - if (!slave || !of_device_is_available(lcdc)) - goto out; - - i2c = of_parse_phandle(slave, "i2c", 0); - if (!i2c) { - pr_err("%s: Can't find i2c node trough phandle\n", __func__); - goto out; - } - - overlay = tilcdc_get_overlay(&kft); - if (!overlay) - goto out; - - encoder = of_find_matching_node(overlay, tilcdc_tda998x_of_match); - if (!encoder) { - pr_err("%s: Failed to find tda998x node\n", __func__); - goto out; - } - - tilcdc_copy_props(slave, encoder, tilcdc_slave_props, &kft); - - for_each_child_of_node(overlay, fragment) { - prop = of_find_property(fragment, "target-path", NULL); - if (!prop) - continue; - if (!strncmp("i2c", (char *)prop->value, prop->length)) - if (tilcdc_prop_str_update(prop, i2c->full_name, &kft)) - goto out; - if (!strncmp("lcdc", (char *)prop->value, prop->length)) - if (tilcdc_prop_str_update(prop, lcdc->full_name, &kft)) - goto out; - } - - tilcdc_node_disable(slave); - - ovcs_id = 0; - ret = of_overlay_apply(overlay, &ovcs_id); - if (ret) - pr_err("%s: Applying overlay changeset failed: %d\n", - __func__, ret); - else - pr_info("%s: ti,tilcdc,slave node successfully converted\n", - __func__); -out: - kfree_table_free(&kft); - of_node_put(i2c); - of_node_put(slave); - of_node_put(lcdc); - of_node_put(fragment); -} - -static int __init tilcdc_slave_compat_init(void) -{ - tilcdc_convert_slave_node(); - return 0; -} - -subsys_initcall(tilcdc_slave_compat_init); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts deleted file mode 100644 index 693f8b0aea2d..000000000000 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * DTS overlay for converting ti,tilcdc,slave binding to new binding. - * - * Copyright (C) 2015 Texas Instruments Inc. - * Author: Jyri Sarha - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -/* - * target-path property values are simple tags that are replaced with - * correct values in tildcdc_slave_compat.c. Some properties are also - * copied over from the ti,tilcdc,slave node. - */ - -/dts-v1/; -/ { - fragment@0 { - target-path = "i2c"; - __overlay__ { - #address-cells = <1>; - #size-cells = <0>; - tda19988 { - compatible = "nxp,tda998x"; - reg = <0x70>; - status = "okay"; - - port { - hdmi_0: endpoint@0 { - remote-endpoint = <&lcd_0>; - }; - }; - }; - }; - }; - - fragment@1 { - target-path = "lcdc"; - __overlay__ { - port { - lcd_0: endpoint@0 { - remote-endpoint = <&hdmi_0>; - }; - }; - }; - }; - - __local_fixups__ { - fragment@0 { - __overlay__ { - tda19988 { - port { - endpoint@0 { - remote-endpoint = <0>; - }; - }; - }; - }; - }; - fragment@1 { - __overlay__ { - port { - endpoint@0 { - remote-endpoint = <0>; - }; - }; - }; - }; - }; -}; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h deleted file mode 100644 index 403d35d87d0b..000000000000 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2015 Texas Instruments - * Author: Jyri Sarha - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ -/* This header declares the symbols defined in tilcdc_slave_compat.dts */ - -#ifndef __TILCDC_SLAVE_COMPAT_H__ -#define __TILCDC_SLAVE_COMPAT_H__ - -extern uint8_t __dtb_tilcdc_slave_compat_begin[]; -extern uint8_t __dtb_tilcdc_slave_compat_end[]; - -#endif /* __TILCDC_SLAVE_COMPAT_H__ */ diff --git a/drivers/gpu/drm/via/via_dmablit.c b/drivers/gpu/drm/via/via_dmablit.c index 98aae9809249..32c9938e1e1e 100644 --- a/drivers/gpu/drm/via/via_dmablit.c +++ b/drivers/gpu/drm/via/via_dmablit.c @@ -238,9 +238,9 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) vsg->pages = vzalloc(sizeof(struct page *) * vsg->num_pages); if (NULL == vsg->pages) return -ENOMEM; - ret = get_user_pages_unlocked((unsigned long)xfer->mem_addr, - vsg->num_pages, vsg->pages, - (vsg->direction == DMA_FROM_DEVICE) ? FOLL_WRITE : 0); + ret = get_user_pages_fast((unsigned long)xfer->mem_addr, + vsg->num_pages, vsg->direction == DMA_FROM_DEVICE, + vsg->pages); if (ret != vsg->num_pages) { if (ret < 0) return ret; diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c index 7a4b8362dda8..49bfe6e7d005 100644 --- a/drivers/gpu/ipu-v3/ipu-dc.c +++ b/drivers/gpu/ipu-v3/ipu-dc.c @@ -249,11 +249,8 @@ EXPORT_SYMBOL_GPL(ipu_dc_enable); void ipu_dc_enable_channel(struct ipu_dc *dc) { - int di; u32 reg; - di = dc->di; - reg = readl(dc->base + DC_WR_CH_CONF); reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; writel(reg, dc->base + DC_WR_CH_CONF); diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 46a54ed23410..0721e175664a 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -81,7 +81,7 @@ struct tctl_offset { }; static const struct tctl_offset tctl_offset_table[] = { - { 0x17, "AMD Ryzen 7 1600X", 20000 }, + { 0x17, "AMD Ryzen 5 1600X", 20000 }, { 0x17, "AMD Ryzen 7 1700X", 20000 }, { 0x17, "AMD Ryzen 7 1800X", 20000 }, { 0x17, "AMD Ryzen Threadripper 1950X", 27000 }, diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 246fb2365126..2b0f182daa87 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1246,10 +1246,8 @@ w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) exit_remove_files: w83781d_remove_files(dev); - if (data->lm75[0]) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1]) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); return err; } @@ -1262,10 +1260,8 @@ w83781d_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); w83781d_remove_files(dev); - if (data->lm75[0]) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1]) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); return 0; } diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 8af6081b4ab4..28fa3bd2c096 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1316,8 +1316,7 @@ static int w83791d_detect_subclients(struct i2c_client *client) /* Undo inits in case of errors */ error_sc_1: - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[0]); error_sc_0: return err; } @@ -1434,10 +1433,8 @@ error5: error4: sysfs_remove_group(&client->dev.kobj, &w83791d_group); error3: - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1] != NULL) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); return err; } @@ -1448,10 +1445,8 @@ static int w83791d_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &w83791d_group); - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1] != NULL) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); return 0; } diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index d764602d70db..76aa39e537e0 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -981,8 +981,7 @@ w83792d_detect_subclients(struct i2c_client *new_client) /* Undo inits in case of errors */ ERROR_SC_1: - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[0]); ERROR_SC_0: return err; } @@ -1456,10 +1455,8 @@ exit_remove_files: for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++) sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]); exit_i2c_unregister: - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1] != NULL) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); return err; } @@ -1475,10 +1472,8 @@ w83792d_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &w83792d_group_fan[i]); - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1] != NULL) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); return 0; } diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 5ba9d9f1daa1..0af0f6283b35 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1564,10 +1564,8 @@ static int w83793_remove(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) device_remove_file(dev, &w83793_temp[i].dev_attr); - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1] != NULL) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); /* Decrease data reference counter */ mutex_lock(&watchdog_data_mutex); @@ -1625,8 +1623,7 @@ w83793_detect_subclients(struct i2c_client *client) /* Undo inits in case of errors */ ERROR_SC_1: - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[0]); ERROR_SC_0: return err; } @@ -1962,10 +1959,8 @@ exit_remove: for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) device_remove_file(dev, &w83793_temp[i].dev_attr); - if (data->lm75[0] != NULL) - i2c_unregister_device(data->lm75[0]); - if (data->lm75[1] != NULL) - i2c_unregister_device(data->lm75[1]); + i2c_unregister_device(data->lm75[0]); + i2c_unregister_device(data->lm75[1]); free_mem: kfree(data); exit: diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index 6e0a5539a9ea..f0f467983960 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -3,7 +3,7 @@ # menuconfig HWSPINLOCK - tristate "Hardware Spinlock drivers" + bool "Hardware Spinlock drivers" config HWSPINLOCK_OMAP tristate "OMAP Hardware Spinlock device" diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 6f638bbc922d..2cab27a68479 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -35,6 +35,7 @@ #include #include #include +#include /* * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a @@ -238,46 +239,29 @@ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) } static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, - unsigned long arg) + unsigned nmsgs, struct i2c_msg *msgs) { - struct i2c_rdwr_ioctl_data rdwr_arg; - struct i2c_msg *rdwr_pa; u8 __user **data_ptrs; int i, res; - if (copy_from_user(&rdwr_arg, - (struct i2c_rdwr_ioctl_data __user *)arg, - sizeof(rdwr_arg))) - return -EFAULT; - - /* Put an arbitrary limit on the number of messages that can - * be sent at once */ - if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) - return -EINVAL; - - rdwr_pa = memdup_user(rdwr_arg.msgs, - rdwr_arg.nmsgs * sizeof(struct i2c_msg)); - if (IS_ERR(rdwr_pa)) - return PTR_ERR(rdwr_pa); - - data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL); + data_ptrs = kmalloc(nmsgs * sizeof(u8 __user *), GFP_KERNEL); if (data_ptrs == NULL) { - kfree(rdwr_pa); + kfree(msgs); return -ENOMEM; } res = 0; - for (i = 0; i < rdwr_arg.nmsgs; i++) { + for (i = 0; i < nmsgs; i++) { /* Limit the size of the message to a sane amount */ - if (rdwr_pa[i].len > 8192) { + if (msgs[i].len > 8192) { res = -EINVAL; break; } - data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; - rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len); - if (IS_ERR(rdwr_pa[i].buf)) { - res = PTR_ERR(rdwr_pa[i].buf); + data_ptrs[i] = (u8 __user *)msgs[i].buf; + msgs[i].buf = memdup_user(data_ptrs[i], msgs[i].len); + if (IS_ERR(msgs[i].buf)) { + res = PTR_ERR(msgs[i].buf); break; } @@ -292,121 +276,117 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, * greater (for example to account for a checksum byte at * the end of the message.) */ - if (rdwr_pa[i].flags & I2C_M_RECV_LEN) { - if (!(rdwr_pa[i].flags & I2C_M_RD) || - rdwr_pa[i].buf[0] < 1 || - rdwr_pa[i].len < rdwr_pa[i].buf[0] + + if (msgs[i].flags & I2C_M_RECV_LEN) { + if (!(msgs[i].flags & I2C_M_RD) || + msgs[i].buf[0] < 1 || + msgs[i].len < msgs[i].buf[0] + I2C_SMBUS_BLOCK_MAX) { res = -EINVAL; break; } - rdwr_pa[i].len = rdwr_pa[i].buf[0]; + msgs[i].len = msgs[i].buf[0]; } } if (res < 0) { int j; for (j = 0; j < i; ++j) - kfree(rdwr_pa[j].buf); + kfree(msgs[j].buf); kfree(data_ptrs); - kfree(rdwr_pa); + kfree(msgs); return res; } - res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs); + res = i2c_transfer(client->adapter, msgs, nmsgs); while (i-- > 0) { - if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) { - if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf, - rdwr_pa[i].len)) + if (res >= 0 && (msgs[i].flags & I2C_M_RD)) { + if (copy_to_user(data_ptrs[i], msgs[i].buf, + msgs[i].len)) res = -EFAULT; } - kfree(rdwr_pa[i].buf); + kfree(msgs[i].buf); } kfree(data_ptrs); - kfree(rdwr_pa); + kfree(msgs); return res; } static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, - unsigned long arg) + u8 read_write, u8 command, u32 size, + union i2c_smbus_data __user *data) { - struct i2c_smbus_ioctl_data data_arg; union i2c_smbus_data temp = {}; int datasize, res; - if (copy_from_user(&data_arg, - (struct i2c_smbus_ioctl_data __user *) arg, - sizeof(struct i2c_smbus_ioctl_data))) - return -EFAULT; - if ((data_arg.size != I2C_SMBUS_BYTE) && - (data_arg.size != I2C_SMBUS_QUICK) && - (data_arg.size != I2C_SMBUS_BYTE_DATA) && - (data_arg.size != I2C_SMBUS_WORD_DATA) && - (data_arg.size != I2C_SMBUS_PROC_CALL) && - (data_arg.size != I2C_SMBUS_BLOCK_DATA) && - (data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) && - (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && - (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { + if ((size != I2C_SMBUS_BYTE) && + (size != I2C_SMBUS_QUICK) && + (size != I2C_SMBUS_BYTE_DATA) && + (size != I2C_SMBUS_WORD_DATA) && + (size != I2C_SMBUS_PROC_CALL) && + (size != I2C_SMBUS_BLOCK_DATA) && + (size != I2C_SMBUS_I2C_BLOCK_BROKEN) && + (size != I2C_SMBUS_I2C_BLOCK_DATA) && + (size != I2C_SMBUS_BLOCK_PROC_CALL)) { dev_dbg(&client->adapter->dev, "size out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.size); + size); return -EINVAL; } /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, so the check is valid if size==I2C_SMBUS_QUICK too. */ - if ((data_arg.read_write != I2C_SMBUS_READ) && - (data_arg.read_write != I2C_SMBUS_WRITE)) { + if ((read_write != I2C_SMBUS_READ) && + (read_write != I2C_SMBUS_WRITE)) { dev_dbg(&client->adapter->dev, "read_write out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.read_write); + read_write); return -EINVAL; } /* Note that command values are always valid! */ - if ((data_arg.size == I2C_SMBUS_QUICK) || - ((data_arg.size == I2C_SMBUS_BYTE) && - (data_arg.read_write == I2C_SMBUS_WRITE))) + if ((size == I2C_SMBUS_QUICK) || + ((size == I2C_SMBUS_BYTE) && + (read_write == I2C_SMBUS_WRITE))) /* These are special: we do not use data */ return i2c_smbus_xfer(client->adapter, client->addr, - client->flags, data_arg.read_write, - data_arg.command, data_arg.size, NULL); + client->flags, read_write, + command, size, NULL); - if (data_arg.data == NULL) { + if (data == NULL) { dev_dbg(&client->adapter->dev, "data is NULL pointer in ioctl I2C_SMBUS.\n"); return -EINVAL; } - if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || - (data_arg.size == I2C_SMBUS_BYTE)) - datasize = sizeof(data_arg.data->byte); - else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || - (data_arg.size == I2C_SMBUS_PROC_CALL)) - datasize = sizeof(data_arg.data->word); + if ((size == I2C_SMBUS_BYTE_DATA) || + (size == I2C_SMBUS_BYTE)) + datasize = sizeof(data->byte); + else if ((size == I2C_SMBUS_WORD_DATA) || + (size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data->word); else /* size == smbus block, i2c block, or block proc. call */ - datasize = sizeof(data_arg.data->block); + datasize = sizeof(data->block); - if ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || - (data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) || - (data_arg.read_write == I2C_SMBUS_WRITE)) { - if (copy_from_user(&temp, data_arg.data, datasize)) + if ((size == I2C_SMBUS_PROC_CALL) || + (size == I2C_SMBUS_BLOCK_PROC_CALL) || + (size == I2C_SMBUS_I2C_BLOCK_DATA) || + (read_write == I2C_SMBUS_WRITE)) { + if (copy_from_user(&temp, data, datasize)) return -EFAULT; } - if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) { + if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) { /* Convert old I2C block commands to the new convention. This preserves binary compatibility. */ - data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA; - if (data_arg.read_write == I2C_SMBUS_READ) + size = I2C_SMBUS_I2C_BLOCK_DATA; + if (read_write == I2C_SMBUS_READ) temp.block[0] = I2C_SMBUS_BLOCK_MAX; } res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, - data_arg.read_write, data_arg.command, data_arg.size, &temp); - if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || - (data_arg.read_write == I2C_SMBUS_READ))) { - if (copy_to_user(data_arg.data, &temp, datasize)) + read_write, command, size, &temp); + if (!res && ((size == I2C_SMBUS_PROC_CALL) || + (size == I2C_SMBUS_BLOCK_PROC_CALL) || + (read_write == I2C_SMBUS_READ))) { + if (copy_to_user(data, &temp, datasize)) return -EFAULT; } return res; @@ -454,12 +434,39 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) funcs = i2c_get_functionality(client->adapter); return put_user(funcs, (unsigned long __user *)arg); - case I2C_RDWR: - return i2cdev_ioctl_rdwr(client, arg); + case I2C_RDWR: { + struct i2c_rdwr_ioctl_data rdwr_arg; + struct i2c_msg *rdwr_pa; + + if (copy_from_user(&rdwr_arg, + (struct i2c_rdwr_ioctl_data __user *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) + return -EINVAL; - case I2C_SMBUS: - return i2cdev_ioctl_smbus(client, arg); + rdwr_pa = memdup_user(rdwr_arg.msgs, + rdwr_arg.nmsgs * sizeof(struct i2c_msg)); + if (IS_ERR(rdwr_pa)) + return PTR_ERR(rdwr_pa); + + return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); + } + case I2C_SMBUS: { + struct i2c_smbus_ioctl_data data_arg; + if (copy_from_user(&data_arg, + (struct i2c_smbus_ioctl_data __user *) arg, + sizeof(struct i2c_smbus_ioctl_data))) + return -EFAULT; + return i2cdev_ioctl_smbus(client, data_arg.read_write, + data_arg.command, + data_arg.size, + data_arg.data); + } case I2C_RETRIES: client->adapter->retries = arg; break; @@ -480,6 +487,90 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; } +#ifdef CONFIG_COMPAT + +struct i2c_smbus_ioctl_data32 { + u8 read_write; + u8 command; + u32 size; + compat_caddr_t data; /* union i2c_smbus_data *data */ +}; + +struct i2c_msg32 { + u16 addr; + u16 flags; + u16 len; + compat_caddr_t buf; +}; + +struct i2c_rdwr_ioctl_data32 { + compat_caddr_t msgs; /* struct i2c_msg __user *msgs */ + u32 nmsgs; +}; + +static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; + unsigned long funcs; + switch (cmd) { + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, (compat_ulong_t __user *)arg); + case I2C_RDWR: { + struct i2c_rdwr_ioctl_data32 rdwr_arg; + struct i2c_msg32 *p; + struct i2c_msg *rdwr_pa; + int i; + + if (copy_from_user(&rdwr_arg, + (struct i2c_rdwr_ioctl_data32 __user *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + + if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) + return -EINVAL; + + rdwr_pa = kmalloc_array(rdwr_arg.nmsgs, sizeof(struct i2c_msg), + GFP_KERNEL); + if (!rdwr_pa) + return -ENOMEM; + + p = compat_ptr(rdwr_arg.msgs); + for (i = 0; i < rdwr_arg.nmsgs; i++) { + struct i2c_msg32 umsg; + if (copy_from_user(&umsg, p + i, sizeof(umsg))) { + kfree(rdwr_pa); + return -EFAULT; + } + rdwr_pa[i] = (struct i2c_msg) { + .addr = umsg.addr, + .flags = umsg.flags, + .len = umsg.len, + .buf = compat_ptr(umsg.buf) + }; + } + + return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); + } + case I2C_SMBUS: { + struct i2c_smbus_ioctl_data32 data32; + if (copy_from_user(&data32, + (void __user *) arg, + sizeof(data32))) + return -EFAULT; + return i2cdev_ioctl_smbus(client, data32.read_write, + data32.command, + data32.size, + compat_ptr(data32.data)); + } + default: + return i2cdev_ioctl(file, cmd, arg); + } +} +#else +#define compat_i2cdev_ioctl NULL +#endif + static int i2cdev_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); @@ -527,6 +618,7 @@ static const struct file_operations i2cdev_fops = { .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, + .compat_ioctl = compat_i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, }; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 6ff0be8cbdc9..7c3ed7c9af77 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1333,8 +1333,7 @@ static int ide_cdrom_prep_fs(struct request_queue *q, struct request *rq) unsigned long blocks = blk_rq_sectors(rq) / (hard_sect >> 9); struct scsi_request *req = scsi_req(rq); - scsi_req_init(req); - memset(req->cmd, 0, BLK_MAX_CDB); + q->initialize_rq_fn(rq); if (rq_data_dir(rq) == READ) req->cmd[0] = GPCMD_READ_10; diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index f5f2b62471da..859ddab9448f 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -22,7 +22,7 @@ #define DRV_NAME "ide-pnp" /* Add your devices here :)) */ -static struct pnp_device_id idepnp_devices[] = { +static const struct pnp_device_id idepnp_devices[] = { /* Generic ESDI/IDE/ATA compatible hard disk controller */ {.id = "PNP0600", .driver_data = 0}, {.id = ""} diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c index 62f9c43e2377..74c871495e81 100644 --- a/drivers/isdn/hisax/asuscom.c +++ b/drivers/isdn/hisax/asuscom.c @@ -348,7 +348,7 @@ int setup_asuscom(struct IsdnCard *card) } card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { + if (card->para[0] == -1 || !card->para[1]) { printk(KERN_ERR "AsusPnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index daf3742cdef6..a18b605fb4f2 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -805,7 +805,7 @@ static int avm_pnp_setup(struct IsdnCardState *cs) cs->hw.avm.cfg_reg = pnp_port_start(pnp_avm_d, 0); cs->irq = pnp_irq(pnp_avm_d, 0); - if (!cs->irq) { + if (cs->irq == -1) { printk(KERN_ERR "FritzPnP:No IRQ\n"); return (0); } diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 38bdd3f7b960..d23df7a7784d 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -1093,7 +1093,7 @@ static int setup_diva_isapnp(struct IsdnCard *card) } card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { + if (card->para[0] == -1 || !card->para[1]) { printk(KERN_ERR "Diva PnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index b21c05820f44..0754c0743790 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -945,7 +945,7 @@ static int setup_elsa_isapnp(struct IsdnCard *card) card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { + if (card->para[0] == -1 || !card->para[1]) { printk(KERN_ERR "Elsa PnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c index d925f579bc80..4d3b4b2f2612 100644 --- a/drivers/isdn/hisax/hfc_sx.c +++ b/drivers/isdn/hisax/hfc_sx.c @@ -1423,7 +1423,7 @@ int setup_hfcsx(struct IsdnCard *card) } card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { + if (card->para[0] == -1 || !card->para[1]) { printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c index 380bbeda9c74..91b5219499ca 100644 --- a/drivers/isdn/hisax/hfcscard.c +++ b/drivers/isdn/hisax/hfcscard.c @@ -196,7 +196,7 @@ int setup_hfcs(struct IsdnCard *card) } card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { + if (card->para[0] == -1 || !card->para[1]) { printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c index e4f7573ba9bf..7a7137d8664b 100644 --- a/drivers/isdn/hisax/hisax_fcpcipnp.c +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c @@ -940,6 +940,8 @@ static int fcpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) } adapter->io = pnp_port_start(pdev, 0); adapter->irq = pnp_irq(pdev, 0); + if (!adapter->io || adapter->irq == -1) + goto err_free; printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at IO %#x irq %d\n", (char *) dev_id->driver_data, adapter->io, adapter->irq); diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c index 1399ddd4f6cb..53e299be4304 100644 --- a/drivers/isdn/hisax/isurf.c +++ b/drivers/isdn/hisax/isurf.c @@ -238,7 +238,7 @@ int setup_isurf(struct IsdnCard *card) cs->hw.isurf.reset = pnp_port_start(pnp_d, 0); cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1); cs->irq = pnp_irq(pnp_d, 0); - if (!cs->irq || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) { + if (cs->irq == -1 || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) { printk(KERN_ERR "ISurfPnP:some resources are missing %d/%x/%lx\n", cs->irq, cs->hw.isurf.reset, cs->hw.isurf.phymem); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index 7ae39f5e865d..bfb79f3f0a49 100644 --- a/drivers/isdn/hisax/ix1_micro.c +++ b/drivers/isdn/hisax/ix1_micro.c @@ -256,7 +256,7 @@ int setup_ix1micro(struct IsdnCard *card) } card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { + if (card->para[0] == -1 || !card->para[1]) { printk(KERN_ERR "ITK PnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index e4c33cfe3ef4..dfbcd2eaa81a 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -261,7 +261,7 @@ int setup_niccy(struct IsdnCard *card) card->para[1] = pnp_port_start(pnp_d, 0); card->para[2] = pnp_port_start(pnp_d, 1); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1] || + if (card->para[0] == -1 || !card->para[1] || !card->para[2]) { printk(KERN_ERR "NiccyPnP:some resources are " "missing %ld/%lx/%lx\n", diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index f16a47bcef48..c0b97b893495 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -558,7 +558,7 @@ static int setup_sedlbauer_isapnp(struct IsdnCard *card, int *bytecnt) card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { + if (card->para[0] == -1 || !card->para[1]) { printk(KERN_ERR "Sedlbauer PnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c index 38fb2c1a3f0f..1eef693f04f0 100644 --- a/drivers/isdn/hisax/teles3.c +++ b/drivers/isdn/hisax/teles3.c @@ -306,7 +306,7 @@ int setup_teles3(struct IsdnCard *card) card->para[2] = pnp_port_start(pnp_d, 1); card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1] || !card->para[2]) { + if (card->para[0] == -1 || !card->para[1] || !card->para[2]) { printk(KERN_ERR "Teles PnP:some resources are missing %ld/%lx/%lx\n", card->para[0], card->para[1], card->para[2]); pnp_disable_dev(pnp_d); diff --git a/drivers/md/bcache/stats.c b/drivers/md/bcache/stats.c index d0831d5bcc87..be119326297b 100644 --- a/drivers/md/bcache/stats.c +++ b/drivers/md/bcache/stats.c @@ -147,9 +147,9 @@ static void scale_stats(struct cache_stats *stats, unsigned long rescale_at) } } -static void scale_accounting(unsigned long data) +static void scale_accounting(struct timer_list *t) { - struct cache_accounting *acc = (struct cache_accounting *) data; + struct cache_accounting *acc = from_timer(acc, t, timer); #define move_stat(name) do { \ unsigned t = atomic_xchg(&acc->collector.name, 0); \ @@ -234,9 +234,7 @@ void bch_cache_accounting_init(struct cache_accounting *acc, kobject_init(&acc->day.kobj, &bch_stats_ktype); closure_init(&acc->cl, parent); - init_timer(&acc->timer); + timer_setup(&acc->timer, scale_accounting, 0); acc->timer.expires = jiffies + accounting_delay; - acc->timer.data = (unsigned long) acc; - acc->timer.function = scale_accounting; add_timer(&acc->timer); } diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 33bb074d6941..b8ac591aaaa7 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -974,7 +974,8 @@ static void __get_memory_limit(struct dm_bufio_client *c, buffers = c->minimum_buffers; *limit_buffers = buffers; - *threshold_buffers = buffers * DM_BUFIO_WRITEBACK_PERCENT / 100; + *threshold_buffers = mult_frac(buffers, + DM_BUFIO_WRITEBACK_PERCENT, 100); } /* @@ -1910,19 +1911,15 @@ static int __init dm_bufio_init(void) memset(&dm_bufio_caches, 0, sizeof dm_bufio_caches); memset(&dm_bufio_cache_names, 0, sizeof dm_bufio_cache_names); - mem = (__u64)((totalram_pages - totalhigh_pages) * - DM_BUFIO_MEMORY_PERCENT / 100) << PAGE_SHIFT; + mem = (__u64)mult_frac(totalram_pages - totalhigh_pages, + DM_BUFIO_MEMORY_PERCENT, 100) << PAGE_SHIFT; if (mem > ULONG_MAX) mem = ULONG_MAX; #ifdef CONFIG_MMU - /* - * Get the size of vmalloc space the same way as VMALLOC_TOTAL - * in fs/proc/internal.h - */ - if (mem > (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100) - mem = (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100; + if (mem > mult_frac(VMALLOC_TOTAL, DM_BUFIO_VMALLOC_PERCENT, 100)) + mem = mult_frac(VMALLOC_TOTAL, DM_BUFIO_VMALLOC_PERCENT, 100); #endif dm_bufio_default_cache_size = mem; diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index 2209a9700acd..288386bfbfb5 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -44,9 +44,9 @@ struct dm_delay_info { static DEFINE_MUTEX(delayed_bios_lock); -static void handle_delayed_timer(unsigned long data) +static void handle_delayed_timer(struct timer_list *t) { - struct delay_c *dc = (struct delay_c *)data; + struct delay_c *dc = from_timer(dc, t, delay_timer); queue_work(dc->kdelayd_wq, &dc->flush_expired_bios); } @@ -195,7 +195,7 @@ out: goto bad_queue; } - setup_timer(&dc->delay_timer, handle_delayed_timer, (unsigned long)dc); + timer_setup(&dc->delay_timer, handle_delayed_timer, 0); INIT_WORK(&dc->flush_expired_bios, flush_expired_bios); INIT_LIST_HEAD(&dc->delayed_bios); diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index ba84b8d62cd0..73a5c198113a 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1513,7 +1513,6 @@ static int era_ctr(struct dm_target *ti, unsigned argc, char **argv) ti->flush_supported = true; ti->num_discard_bios = 1; - ti->discards_supported = true; era->callbacks.congested_fn = era_is_congested; dm_table_add_target_callbacks(ti->table, &era->callbacks); diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 61180783ef42..05c7bfd0c9d9 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -1094,9 +1094,9 @@ static void sleep_on_endio_wait(struct dm_integrity_c *ic) __remove_wait_queue(&ic->endio_wait, &wait); } -static void autocommit_fn(unsigned long data) +static void autocommit_fn(struct timer_list *t) { - struct dm_integrity_c *ic = (struct dm_integrity_c *)data; + struct dm_integrity_c *ic = from_timer(ic, t, autocommit_timer); if (likely(!dm_integrity_failed(ic))) queue_work(ic->commit_wq, &ic->commit_work); @@ -2942,7 +2942,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv) ic->autocommit_jiffies = msecs_to_jiffies(sync_msec); ic->autocommit_msec = sync_msec; - setup_timer(&ic->autocommit_timer, autocommit_fn, (unsigned long)ic); + timer_setup(&ic->autocommit_timer, autocommit_fn, 0); ic->io = dm_io_client_create(); if (IS_ERR(ic->io)) { diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 204606bef9e2..c8faa2b85842 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -499,8 +499,6 @@ static int multipath_clone_and_map(struct dm_target *ti, struct request *rq, if (IS_ERR(clone)) { /* EBUSY, ENODEV or EWOULDBLOCK: requeue */ bool queue_dying = blk_queue_dying(q); - DMERR_LIMIT("blk_get_request() returned %ld%s - requeuing", - PTR_ERR(clone), queue_dying ? " (path offline)" : ""); if (queue_dying) { atomic_inc(&m->pg_init_in_progress); activate_or_offline_path(pgpath); diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 366c625b9591..6319d846e0ad 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -2887,9 +2887,6 @@ static void configure_discard_support(struct raid_set *rs) bool raid456; struct dm_target *ti = rs->ti; - /* Assume discards not supported until after checks below. */ - ti->discards_supported = false; - /* * XXX: RAID level 4,5,6 require zeroing for safety. */ @@ -2914,9 +2911,6 @@ static void configure_discard_support(struct raid_set *rs) } } - /* All RAID members properly support discards */ - ti->discards_supported = true; - /* * RAID1 and RAID10 personalities require bio splitting, * RAID0/4/5/6 don't and process large discard bios properly. diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index c0b82136b2d1..580c49cc8079 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -94,9 +94,9 @@ static void wakeup_mirrord(void *context) queue_work(ms->kmirrord_wq, &ms->kmirrord_work); } -static void delayed_wake_fn(unsigned long data) +static void delayed_wake_fn(struct timer_list *t) { - struct mirror_set *ms = (struct mirror_set *) data; + struct mirror_set *ms = from_timer(ms, t, timer); clear_bit(0, &ms->timer_pending); wakeup_mirrord(ms); @@ -108,8 +108,6 @@ static void delayed_wake(struct mirror_set *ms) return; ms->timer.expires = jiffies + HZ / 5; - ms->timer.data = (unsigned long) ms; - ms->timer.function = delayed_wake_fn; add_timer(&ms->timer); } @@ -1133,7 +1131,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto err_free_context; } INIT_WORK(&ms->kmirrord_work, do_mirror); - init_timer(&ms->timer); + timer_setup(&ms->timer, delayed_wake_fn, 0); ms->timer_pending = 0; INIT_WORK(&ms->trigger_event, trigger_event); diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 433bd3f3014c..88130b5d95f9 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1758,13 +1758,12 @@ static bool dm_table_supports_write_zeroes(struct dm_table *t) return true; } - -static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev, - sector_t start, sector_t len, void *data) +static int device_not_discard_capable(struct dm_target *ti, struct dm_dev *dev, + sector_t start, sector_t len, void *data) { struct request_queue *q = bdev_get_queue(dev->bdev); - return q && blk_queue_discard(q); + return q && !blk_queue_discard(q); } static bool dm_table_supports_discards(struct dm_table *t) @@ -1772,28 +1771,24 @@ static bool dm_table_supports_discards(struct dm_table *t) struct dm_target *ti; unsigned i; - /* - * Unless any target used by the table set discards_supported, - * require at least one underlying device to support discards. - * t->devices includes internal dm devices such as mirror logs - * so we need to use iterate_devices here, which targets - * supporting discard selectively must provide. - */ for (i = 0; i < dm_table_get_num_targets(t); i++) { ti = dm_table_get_target(t, i); if (!ti->num_discard_bios) - continue; - - if (ti->discards_supported) - return true; + return false; - if (ti->type->iterate_devices && - ti->type->iterate_devices(ti, device_discard_capable, NULL)) - return true; + /* + * Either the target provides discard support (as implied by setting + * 'discards_supported') or it relies on _all_ data devices having + * discard support. + */ + if (!ti->discards_supported && + (!ti->type->iterate_devices || + ti->type->iterate_devices(ti, device_not_discard_capable, NULL))) + return false; } - return false; + return true; } void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, @@ -1806,9 +1801,15 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, */ q->limits = *limits; - if (!dm_table_supports_discards(t)) + if (!dm_table_supports_discards(t)) { queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q); - else + /* Must also clear discard limits... */ + q->limits.max_discard_sectors = 0; + q->limits.max_hw_discard_sectors = 0; + q->limits.discard_granularity = 0; + q->limits.discard_alignment = 0; + q->limits.discard_misaligned = 0; + } else queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); if (dm_table_supports_flush(t, (1UL << QUEUE_FLAG_WC))) { diff --git a/drivers/md/md.c b/drivers/md/md.c index c3dc134b9fb5..41c050b59ec4 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -541,7 +541,7 @@ static void mddev_put(struct mddev *mddev) bioset_free(sync_bs); } -static void md_safemode_timeout(unsigned long data); +static void md_safemode_timeout(struct timer_list *t); void mddev_init(struct mddev *mddev) { @@ -550,8 +550,7 @@ void mddev_init(struct mddev *mddev) mutex_init(&mddev->bitmap_info.mutex); INIT_LIST_HEAD(&mddev->disks); INIT_LIST_HEAD(&mddev->all_mddevs); - setup_timer(&mddev->safemode_timer, md_safemode_timeout, - (unsigned long) mddev); + timer_setup(&mddev->safemode_timer, md_safemode_timeout, 0); atomic_set(&mddev->active, 1); atomic_set(&mddev->openers, 0); atomic_set(&mddev->active_io, 0); @@ -5404,9 +5403,9 @@ static int add_named_array(const char *val, const struct kernel_param *kp) return -EINVAL; } -static void md_safemode_timeout(unsigned long data) +static void md_safemode_timeout(struct timer_list *t) { - struct mddev *mddev = (struct mddev *) data; + struct mddev *mddev = from_timer(mddev, t, safemode_timer); mddev->safemode = 1; if (mddev->external) diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index b0f7af872bb5..7eebbdfbcacd 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -63,9 +63,11 @@ void lkdtm_BUG(void) BUG(); } +static int warn_counter; + void lkdtm_WARNING(void) { - WARN_ON(1); + WARN(1, "Warning message trigger count: %d\n", warn_counter++); } void lkdtm_EXCEPTION(void) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5a2d71729b9a..2a8ac6829d42 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,6 +1,5 @@ menuconfig MTD tristate "Memory Technology Device (MTD) support" - depends on GENERIC_IO help Memory Technology Devices are flash, RAM and similar chips, often used for solid state file systems on embedded devices. This option diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index afb43d5e1782..1cd0fff0e940 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -20,8 +20,9 @@ static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch static int mapram_erase (struct mtd_info *, struct erase_info *); static void mapram_nop (struct mtd_info *); static struct mtd_info *map_ram_probe(struct map_info *map); -static unsigned long mapram_unmapped_area(struct mtd_info *, unsigned long, - unsigned long, unsigned long); +static int mapram_point (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, void **virt, resource_size_t *phys); +static int mapram_unpoint(struct mtd_info *mtd, loff_t from, size_t len); static struct mtd_chip_driver mapram_chipdrv = { @@ -65,11 +66,12 @@ static struct mtd_info *map_ram_probe(struct map_info *map) mtd->type = MTD_RAM; mtd->size = map->size; mtd->_erase = mapram_erase; - mtd->_get_unmapped_area = mapram_unmapped_area; mtd->_read = mapram_read; mtd->_write = mapram_write; mtd->_panic_write = mapram_write; + mtd->_point = mapram_point; mtd->_sync = mapram_nop; + mtd->_unpoint = mapram_unpoint; mtd->flags = MTD_CAP_RAM; mtd->writesize = 1; @@ -81,19 +83,23 @@ static struct mtd_info *map_ram_probe(struct map_info *map) return mtd; } - -/* - * Allow NOMMU mmap() to directly map the device (if not NULL) - * - return the address to which the offset maps - * - return -ENOSYS to indicate refusal to do the mapping - */ -static unsigned long mapram_unmapped_area(struct mtd_info *mtd, - unsigned long len, - unsigned long offset, - unsigned long flags) +static int mapram_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, void **virt, resource_size_t *phys) { struct map_info *map = mtd->priv; - return (unsigned long) map->virt + offset; + + if (!map->virt) + return -EINVAL; + *virt = map->virt + from; + if (phys) + *phys = map->phys + from; + *retlen = len; + return 0; +} + +static int mapram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +{ + return 0; } static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index e67f73ab44c9..20e3604b4d71 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -20,8 +20,10 @@ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch static void maprom_nop (struct mtd_info *); static struct mtd_info *map_rom_probe(struct map_info *map); static int maprom_erase (struct mtd_info *mtd, struct erase_info *info); -static unsigned long maprom_unmapped_area(struct mtd_info *, unsigned long, - unsigned long, unsigned long); +static int maprom_point (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, void **virt, resource_size_t *phys); +static int maprom_unpoint(struct mtd_info *mtd, loff_t from, size_t len); + static struct mtd_chip_driver maprom_chipdrv = { .probe = map_rom_probe, @@ -51,7 +53,8 @@ static struct mtd_info *map_rom_probe(struct map_info *map) mtd->name = map->name; mtd->type = MTD_ROM; mtd->size = map->size; - mtd->_get_unmapped_area = maprom_unmapped_area; + mtd->_point = maprom_point; + mtd->_unpoint = maprom_unpoint; mtd->_read = maprom_read; mtd->_write = maprom_write; mtd->_sync = maprom_nop; @@ -66,18 +69,23 @@ static struct mtd_info *map_rom_probe(struct map_info *map) } -/* - * Allow NOMMU mmap() to directly map the device (if not NULL) - * - return the address to which the offset maps - * - return -ENOSYS to indicate refusal to do the mapping - */ -static unsigned long maprom_unmapped_area(struct mtd_info *mtd, - unsigned long len, - unsigned long offset, - unsigned long flags) +static int maprom_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, void **virt, resource_size_t *phys) { struct map_info *map = mtd->priv; - return (unsigned long) map->virt + offset; + + if (!map->virt) + return -EINVAL; + *virt = map->virt + from; + if (phys) + *phys = map->phys + from; + *retlen = len; + return 0; +} + +static int maprom_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +{ + return 0; } static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 84b16133554b..0806f72102c0 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1814,8 +1814,13 @@ static void __init doc_dbg_register(struct mtd_info *floor) struct dentry *root = floor->dbg.dfs_dir; struct docg3 *docg3 = floor->priv; - if (IS_ERR_OR_NULL(root)) + if (IS_ERR_OR_NULL(root)) { + if (IS_ENABLED(CONFIG_DEBUG_FS) && + !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + dev_warn(floor->dev.parent, + "CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n"); return; + } debugfs_create_file("docg3_flashcontrol", S_IRUSR, root, docg3, &flashcontrol_fops); diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 268aae45b514..555b94406e0b 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -583,7 +583,7 @@ static struct mtd_erase_region_info erase_regions[] = { } }; -static struct mtd_partition lart_partitions[] = { +static const struct mtd_partition lart_partitions[] = { /* blob */ { .name = "blob", diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 00eea6fd379c..dbe6a1de2bb8 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -359,6 +359,7 @@ static const struct spi_device_id m25p_ids[] = { {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, /* Everspin MRAMs (non-JEDEC) */ + { "mr25h128" }, /* 128 Kib, 40 MHz */ { "mr25h256" }, /* 256 Kib, 40 MHz */ { "mr25h10" }, /* 1 Mib, 40 MHz */ { "mr25h40" }, /* 4 Mib, 40 MHz */ diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index cbd8547d7aad..0bf4aeaf0cb8 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,27 @@ static int ram_point(struct mtd_info *mtd, loff_t from, size_t len, { *virt = mtd->priv + from; *retlen = len; + + if (phys) { + /* limit retlen to the number of contiguous physical pages */ + unsigned long page_ofs = offset_in_page(*virt); + void *addr = *virt - page_ofs; + unsigned long pfn1, pfn0 = vmalloc_to_pfn(addr); + + *phys = __pfn_to_phys(pfn0) + page_ofs; + len += page_ofs; + while (len > PAGE_SIZE) { + len -= PAGE_SIZE; + addr += PAGE_SIZE; + pfn0++; + pfn1 = vmalloc_to_pfn(addr); + if (pfn1 != pfn0) { + *retlen = addr - *virt; + break; + } + } + } + return 0; } @@ -77,19 +99,6 @@ static int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) return 0; } -/* - * Allow NOMMU mmap() to directly map the device (if not NULL) - * - return the address to which the offset maps - * - return -ENOSYS to indicate refusal to do the mapping - */ -static unsigned long ram_get_unmapped_area(struct mtd_info *mtd, - unsigned long len, - unsigned long offset, - unsigned long flags) -{ - return (unsigned long) mtd->priv + offset; -} - static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -134,7 +143,6 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, mtd->_erase = ram_erase; mtd->_point = ram_point; mtd->_unpoint = ram_unpoint; - mtd->_get_unmapped_area = ram_get_unmapped_area; mtd->_read = ram_read; mtd->_write = ram_write; diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 8087c36dc693..0ec85f316d24 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -163,8 +163,9 @@ static int register_device(char *name, unsigned long start, unsigned long length } if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = - ioremap(start, length))) { - E("slram: ioremap failed\n"); + memremap(start, length, + MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) { + E("slram: memremap failed\n"); return -EIO; } ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = @@ -186,7 +187,7 @@ static int register_device(char *name, unsigned long start, unsigned long length if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) { E("slram: Failed to register new device\n"); - iounmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); + memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); kfree((*curmtd)->mtdinfo->priv); kfree((*curmtd)->mtdinfo); return(-EAGAIN); @@ -206,7 +207,7 @@ static void unregister_devices(void) while (slram_mtdlist) { nextitem = slram_mtdlist->next; mtd_device_unregister(slram_mtdlist->mtdinfo); - iounmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start); + memunmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start); kfree(slram_mtdlist->mtdinfo->priv); kfree(slram_mtdlist->mtdinfo); kfree(slram_mtdlist); diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index d504b3d1791d..70f488628464 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -61,7 +61,7 @@ static struct map_info flagadm_map = { .bankwidth = 2, }; -static struct mtd_partition flagadm_parts[] = { +static const struct mtd_partition flagadm_parts[] = { { .name = "Bootloader", .offset = FLASH_PARTITION0_ADDR, diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index 15bbda03be65..a0b8fa7849a9 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -47,7 +47,7 @@ static struct map_info impa7_map[NUM_FLASHBANKS] = { /* * MTD partitioning stuff */ -static struct mtd_partition partitions[] = +static const struct mtd_partition partitions[] = { { .name = "FileSystem", diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index 81dc2598bc0a..3528497f96c7 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -52,7 +52,7 @@ /* partition_info gives details on the logical partitions that the split the * single flash device into. If the size if zero we use up to the end of the * device. */ -static struct mtd_partition partition_info[]={ +static const struct mtd_partition partition_info[] = { { .name = "NetSc520 boot kernel", .offset = 0, diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index a577ef8553d0..729579fb654f 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -107,7 +107,7 @@ static struct map_info nettel_amd_map = { .bankwidth = AMD_BUSWIDTH, }; -static struct mtd_partition nettel_amd_partitions[] = { +static const struct mtd_partition nettel_amd_partitions[] = { { .name = "SnapGear BIOS config", .offset = 0x000e0000, diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 51572895c02c..6d9a4d6f9839 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -43,7 +43,6 @@ struct platram_info { struct device *dev; struct mtd_info *mtd; struct map_info map; - struct resource *area; struct platdata_mtd_ram *pdata; }; @@ -97,16 +96,6 @@ static int platram_remove(struct platform_device *pdev) platram_setrw(info, PLATRAM_RO); - /* release resources */ - - if (info->area) { - release_resource(info->area); - kfree(info->area); - } - - if (info->map.virt != NULL) - iounmap(info->map.virt); - kfree(info); return 0; @@ -147,12 +136,11 @@ static int platram_probe(struct platform_device *pdev) info->pdata = pdata; /* get the resource for the memory mapping */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - if (res == NULL) { - dev_err(&pdev->dev, "no memory resource specified\n"); - err = -ENOENT; + info->map.virt = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->map.virt)) { + err = PTR_ERR(info->map.virt); + dev_err(&pdev->dev, "failed to ioremap() region\n"); goto exit_free; } @@ -167,26 +155,8 @@ static int platram_probe(struct platform_device *pdev) (char *)pdata->mapname : (char *)pdev->name; info->map.bankwidth = pdata->bankwidth; - /* register our usage of the memory area */ - - info->area = request_mem_region(res->start, info->map.size, pdev->name); - if (info->area == NULL) { - dev_err(&pdev->dev, "failed to request memory region\n"); - err = -EIO; - goto exit_free; - } - - /* remap the memory area */ - - info->map.virt = ioremap(res->start, info->map.size); dev_dbg(&pdev->dev, "virt %p, %lu bytes\n", info->map.virt, info->map.size); - if (info->map.virt == NULL) { - dev_err(&pdev->dev, "failed to ioremap() region\n"); - err = -EIO; - goto exit_free; - } - simple_map_init(&info->map); dev_dbg(&pdev->dev, "initialised map, probing for mtd\n"); diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 556a2dfe94c5..4337d279ad83 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -87,7 +87,7 @@ static DEFINE_SPINLOCK(sbc_gxx_spin); /* partition_info gives details on the logical partitions that the split the * single flash device into. If the size if zero we use up to the end of the * device. */ -static struct mtd_partition partition_info[]={ +static const struct mtd_partition partition_info[] = { { .name = "SBC-GXx flash boot partition", .offset = 0, .size = BOOT_PARTITION_SIZE_KiB*1024 }, diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index 9969fedb1f13..8f177e0acb8c 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -43,7 +43,7 @@ static struct map_info ts5500_map = { .phys = WINDOW_ADDR }; -static struct mtd_partition ts5500_partitions[] = { +static const struct mtd_partition ts5500_partitions[] = { { .name = "Drive A", .offset = 0, diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 00a8190797ec..aef030ca8601 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -49,7 +49,7 @@ static struct mtd_info *uclinux_ram_mtdinfo; /****************************************************************************/ -static struct mtd_partition uclinux_romfs[] = { +static const struct mtd_partition uclinux_romfs[] = { { .name = "ROMfs" } }; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 3568294d4854..de8c902059b8 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -375,12 +375,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, return -EINVAL; if (!mtd->_write_oob) - ret = -EOPNOTSUPP; - else - ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT; - - if (ret) - return ret; + return -EOPNOTSUPP; ops.ooblen = length; ops.ooboffs = start & (mtd->writesize - 1); @@ -419,9 +414,6 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, if (length > 4096) return -EINVAL; - if (!access_ok(VERIFY_WRITE, ptr, length)) - return -EFAULT; - ops.ooblen = length; ops.ooboffs = start & (mtd->writesize - 1); ops.datbuf = NULL; @@ -618,9 +610,6 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, usr_data = (const void __user *)(uintptr_t)req.usr_data; usr_oob = (const void __user *)(uintptr_t)req.usr_oob; - if (!access_ok(VERIFY_READ, usr_data, req.len) || - !access_ok(VERIFY_READ, usr_oob, req.ooblen)) - return -EFAULT; if (!mtd->_write_oob) return -EOPNOTSUPP; @@ -662,21 +651,10 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) struct mtd_info *mtd = mfi->mtd; void __user *argp = (void __user *)arg; int ret = 0; - u_long size; struct mtd_info_user info; pr_debug("MTD_ioctl\n"); - size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; - if (cmd & IOC_IN) { - if (!access_ok(VERIFY_READ, argp, size)) - return -EFAULT; - } - if (cmd & IOC_OUT) { - if (!access_ok(VERIFY_WRITE, argp, size)) - return -EFAULT; - } - switch (cmd) { case MEMGETREGIONCOUNT: if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index d573606b91c2..60bf53df5454 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -643,32 +643,6 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) return err; } -/* - * try to support NOMMU mmaps on concatenated devices - * - we don't support subdev spanning as we can't guarantee it'll work - */ -static unsigned long concat_get_unmapped_area(struct mtd_info *mtd, - unsigned long len, - unsigned long offset, - unsigned long flags) -{ - struct mtd_concat *concat = CONCAT(mtd); - int i; - - for (i = 0; i < concat->num_subdev; i++) { - struct mtd_info *subdev = concat->subdev[i]; - - if (offset >= subdev->size) { - offset -= subdev->size; - continue; - } - - return mtd_get_unmapped_area(subdev, len, offset, flags); - } - - return (unsigned long) -ENOSYS; -} - /* * This function constructs a virtual MTD device by concatenating * num_devs MTD devices. A pointer to the new device object is @@ -790,7 +764,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd._unlock = concat_unlock; concat->mtd._suspend = concat_suspend; concat->mtd._resume = concat_resume; - concat->mtd._get_unmapped_area = concat_get_unmapped_area; /* * Combine the erase block size info of the subdevices: diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index e7ea842ba3db..f80e911b8843 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1022,11 +1022,18 @@ EXPORT_SYMBOL_GPL(mtd_unpoint); unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len, unsigned long offset, unsigned long flags) { - if (!mtd->_get_unmapped_area) - return -EOPNOTSUPP; - if (offset >= mtd->size || len > mtd->size - offset) - return -EINVAL; - return mtd->_get_unmapped_area(mtd, len, offset, flags); + size_t retlen; + void *virt; + int ret; + + ret = mtd_point(mtd, offset, len, &retlen, &virt, NULL); + if (ret) + return ret; + if (retlen != len) { + mtd_unpoint(mtd, offset, retlen); + return -ENOSYS; + } + return (unsigned long)virt; } EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); @@ -1093,6 +1100,39 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, } EXPORT_SYMBOL_GPL(mtd_panic_write); +static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs, + struct mtd_oob_ops *ops) +{ + /* + * Some users are setting ->datbuf or ->oobbuf to NULL, but are leaving + * ->len or ->ooblen uninitialized. Force ->len and ->ooblen to 0 in + * this case. + */ + if (!ops->datbuf) + ops->len = 0; + + if (!ops->oobbuf) + ops->ooblen = 0; + + if (offs < 0 || offs + ops->len >= mtd->size) + return -EINVAL; + + if (ops->ooblen) { + u64 maxooblen; + + if (ops->ooboffs >= mtd_oobavail(mtd, ops)) + return -EINVAL; + + maxooblen = ((mtd_div_by_ws(mtd->size, mtd) - + mtd_div_by_ws(offs, mtd)) * + mtd_oobavail(mtd, ops)) - ops->ooboffs; + if (ops->ooblen > maxooblen) + return -EINVAL; + } + + return 0; +} + int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int ret_code; @@ -1100,6 +1140,10 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) if (!mtd->_read_oob) return -EOPNOTSUPP; + ret_code = mtd_check_oob_ops(mtd, from, ops); + if (ret_code) + return ret_code; + ledtrig_mtd_activity(); /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics @@ -1119,11 +1163,18 @@ EXPORT_SYMBOL_GPL(mtd_read_oob); int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { + int ret; + ops->retlen = ops->oobretlen = 0; if (!mtd->_write_oob) return -EOPNOTSUPP; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + + ret = mtd_check_oob_ops(mtd, to, ops); + if (ret) + return ret; + ledtrig_mtd_activity(); return mtd->_write_oob(mtd, to, ops); } diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index a308e707392d..be088bccd593 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -101,18 +101,6 @@ static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) return part->parent->_unpoint(part->parent, from + part->offset, len); } -static unsigned long part_get_unmapped_area(struct mtd_info *mtd, - unsigned long len, - unsigned long offset, - unsigned long flags) -{ - struct mtd_part *part = mtd_to_part(mtd); - - offset += part->offset; - return part->parent->_get_unmapped_area(part->parent, len, offset, - flags); -} - static int part_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -458,8 +446,6 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, slave->mtd._unpoint = part_unpoint; } - if (parent->_get_unmapped_area) - slave->mtd._get_unmapped_area = part_get_unmapped_area; if (parent->_read_oob) slave->mtd._read_oob = part_read_oob; if (parent->_write_oob) diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index 7d9080e33865..f07492c6f4b2 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -50,7 +50,7 @@ * Number of free eraseblocks below which GC can also collect low frag * blocks. */ -#define LOW_FRAG_GC_TRESHOLD 5 +#define LOW_FRAG_GC_THRESHOLD 5 /* * Wear level cost amortization. We want to do wear leveling on the background @@ -805,7 +805,7 @@ static int __mtdswap_choose_gc_tree(struct mtdswap_dev *d) { int idx, stopat; - if (TREE_COUNT(d, CLEAN) < LOW_FRAG_GC_TRESHOLD) + if (TREE_COUNT(d, CLEAN) < LOW_FRAG_GC_THRESHOLD) stopat = MTDSWAP_LOWFRAG; else stopat = MTDSWAP_HIFRAG; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 3f2036f31da4..bb48aafed9a2 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -317,8 +317,11 @@ config MTD_NAND_PXA3xx tristate "NAND support on PXA3xx and Armada 370/XP" depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU help + This enables the driver for the NAND flash device found on - PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). + PXA3xx processors (NFCv1) and also on 32-bit Armada + platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada + platforms (7K, 8K) (NFCv2). config MTD_NAND_SLC_LPC32XX tristate "NXP LPC32xx SLC Controller" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 6e2db700d923..118a1349aad3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -59,7 +59,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o -obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o mtk_ecc.o +obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o nand-objs += nand_amd.o diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index dcec9cf4983f..d60ada45c549 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -41,7 +41,7 @@ static struct mtd_info *ams_delta_mtd = NULL; * Define partitions for flash devices */ -static struct mtd_partition partition_info[] = { +static const struct mtd_partition partition_info[] = { { .name = "Kernel", .offset = 0, .size = 3 * SZ_1M + SZ_512K }, diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c index f25eca79f4e5..90a71a56bc23 100644 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ b/drivers/mtd/nand/atmel/nand-controller.c @@ -718,8 +718,7 @@ static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column) nc->op.addrs[nc->op.naddrs++] = page; nc->op.addrs[nc->op.naddrs++] = page >> 8; - if ((mtd->writesize > 512 && chip->chipsize > SZ_128M) || - (mtd->writesize <= 512 && chip->chipsize > SZ_32M)) + if (chip->options & NAND_ROW_ADDR_3) nc->op.addrs[nc->op.naddrs++] = page >> 16; } } @@ -2530,6 +2529,9 @@ static __maybe_unused int atmel_nand_controller_resume(struct device *dev) struct atmel_nand_controller *nc = dev_get_drvdata(dev); struct atmel_nand *nand; + if (nc->pmecc) + atmel_pmecc_reset(nc->pmecc); + list_for_each_entry(nand, &nc->chips, node) { int i; @@ -2547,6 +2549,7 @@ static struct platform_driver atmel_nand_controller_driver = { .driver = { .name = "atmel-nand-controller", .of_match_table = of_match_ptr(atmel_nand_controller_of_ids), + .pm = &atmel_nand_controller_pm_ops, }, .probe = atmel_nand_controller_probe, .remove = atmel_nand_controller_remove, diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c index 8268636675ef..fcbe4fd6e684 100644 --- a/drivers/mtd/nand/atmel/pmecc.c +++ b/drivers/mtd/nand/atmel/pmecc.c @@ -765,6 +765,13 @@ void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, } EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes); +void atmel_pmecc_reset(struct atmel_pmecc *pmecc) +{ + writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); + writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_reset); + int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op) { struct atmel_pmecc *pmecc = user->pmecc; @@ -797,10 +804,7 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable); void atmel_pmecc_disable(struct atmel_pmecc_user *user) { - struct atmel_pmecc *pmecc = user->pmecc; - - writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); - writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); + atmel_pmecc_reset(user->pmecc); mutex_unlock(&user->pmecc->lock); } EXPORT_SYMBOL_GPL(atmel_pmecc_disable); @@ -855,10 +859,7 @@ static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev, /* Disable all interrupts before registering the PMECC handler. */ writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); - - /* Reset the ECC engine */ - writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); - writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); + atmel_pmecc_reset(pmecc); return pmecc; } diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h index a8ddbfca2ea5..817e0dd9fd15 100644 --- a/drivers/mtd/nand/atmel/pmecc.h +++ b/drivers/mtd/nand/atmel/pmecc.h @@ -61,6 +61,7 @@ atmel_pmecc_create_user(struct atmel_pmecc *pmecc, struct atmel_pmecc_user_req *req); void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user); +void atmel_pmecc_reset(struct atmel_pmecc *pmecc); int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op); void atmel_pmecc_disable(struct atmel_pmecc_user *user); int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user); diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 9d4a28fa6b73..8ab827edf94e 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -331,8 +331,7 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i ctx->write_byte(mtd, (u8)(page_addr >> 8)); - /* One more address cycle for devices > 32MiB */ - if (this->chipsize > (32 << 20)) + if (this->options & NAND_ROW_ADDR_3) ctx->write_byte(mtd, ((page_addr >> 16) & 0x0f)); } diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 1fc435f994e1..b01c9804590e 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -42,7 +42,7 @@ static void __iomem *cmx270_nand_io; /* * Define static partitions for flash device */ -static struct mtd_partition partition_info[] = { +static const struct mtd_partition partition_info[] = { [0] = { .name = "cmx270-0", .offset = 0, diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 3087b0ba7b7f..5124f8ae8c04 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -10,20 +10,18 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#include -#include + +#include +#include #include -#include -#include -#include +#include +#include #include +#include +#include #include +#include #include "denali.h" @@ -31,9 +29,9 @@ MODULE_LICENSE("GPL"); #define DENALI_NAND_NAME "denali-nand" -/* Host Data/Command Interface */ -#define DENALI_HOST_ADDR 0x00 -#define DENALI_HOST_DATA 0x10 +/* for Indexed Addressing */ +#define DENALI_INDEXED_CTRL 0x00 +#define DENALI_INDEXED_DATA 0x10 #define DENALI_MAP00 (0 << 26) /* direct access to buffer */ #define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ @@ -61,31 +59,55 @@ MODULE_LICENSE("GPL"); */ #define DENALI_CLK_X_MULT 6 -/* - * this macro allows us to convert from an MTD structure to our own - * device context (denali) structure. - */ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) { return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand); } -static void denali_host_write(struct denali_nand_info *denali, - uint32_t addr, uint32_t data) +/* + * Direct Addressing - the slave address forms the control information (command + * type, bank, block, and page address). The slave data is the actual data to + * be transferred. This mode requires 28 bits of address region allocated. + */ +static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr) +{ + return ioread32(denali->host + addr); +} + +static void denali_direct_write(struct denali_nand_info *denali, u32 addr, + u32 data) { - iowrite32(addr, denali->host + DENALI_HOST_ADDR); - iowrite32(data, denali->host + DENALI_HOST_DATA); + iowrite32(data, denali->host + addr); +} + +/* + * Indexed Addressing - address translation module intervenes in passing the + * control information. This mode reduces the required address range. The + * control information and transferred data are latched by the registers in + * the translation module. + */ +static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr) +{ + iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); + return ioread32(denali->host + DENALI_INDEXED_DATA); +} + +static void denali_indexed_write(struct denali_nand_info *denali, u32 addr, + u32 data) +{ + iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); + iowrite32(data, denali->host + DENALI_INDEXED_DATA); } /* * Use the configuration feature register to determine the maximum number of * banks that the hardware supports. */ -static void detect_max_banks(struct denali_nand_info *denali) +static void denali_detect_max_banks(struct denali_nand_info *denali) { uint32_t features = ioread32(denali->reg + FEATURES); - denali->max_banks = 1 << (features & FEATURES__N_BANKS); + denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features); /* the encoding changed from rev 5.0 to 5.1 */ if (denali->revision < 0x0501) @@ -189,7 +211,7 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, msecs_to_jiffies(1000)); if (!time_left) { dev_err(denali->dev, "timeout while waiting for irq 0x%x\n", - denali->irq_mask); + irq_mask); return 0; } @@ -208,73 +230,47 @@ static uint32_t denali_check_irq(struct denali_nand_info *denali) return irq_status; } -/* - * This helper function setups the registers for ECC and whether or not - * the spare area will be transferred. - */ -static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, - bool transfer_spare) -{ - int ecc_en_flag, transfer_spare_flag; - - /* set ECC, transfer spare bits if needed */ - ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0; - transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0; - - /* Enable spare area/ECC per user's request. */ - iowrite32(ecc_en_flag, denali->reg + ECC_ENABLE); - iowrite32(transfer_spare_flag, denali->reg + TRANSFER_SPARE_REG); -} - static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); int i; - iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), - denali->host + DENALI_HOST_ADDR); - for (i = 0; i < len; i++) - buf[i] = ioread32(denali->host + DENALI_HOST_DATA); + buf[i] = denali->host_read(denali, addr); } static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); int i; - iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), - denali->host + DENALI_HOST_ADDR); - for (i = 0; i < len; i++) - iowrite32(buf[i], denali->host + DENALI_HOST_DATA); + denali->host_write(denali, addr, buf[i]); } static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) { struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); uint16_t *buf16 = (uint16_t *)buf; int i; - iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), - denali->host + DENALI_HOST_ADDR); - for (i = 0; i < len / 2; i++) - buf16[i] = ioread32(denali->host + DENALI_HOST_DATA); + buf16[i] = denali->host_read(denali, addr); } static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); const uint16_t *buf16 = (const uint16_t *)buf; int i; - iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), - denali->host + DENALI_HOST_ADDR); - for (i = 0; i < len / 2; i++) - iowrite32(buf16[i], denali->host + DENALI_HOST_DATA); + denali->host_write(denali, addr, buf16[i]); } static uint8_t denali_read_byte(struct mtd_info *mtd) @@ -319,7 +315,7 @@ static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) if (ctrl & NAND_CTRL_CHANGE) denali_reset_irq(denali); - denali_host_write(denali, DENALI_BANK(denali) | type, dat); + denali->host_write(denali, DENALI_BANK(denali) | type, dat); } static int denali_dev_ready(struct mtd_info *mtd) @@ -389,7 +385,7 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd, return 0; } - max_bitflips = ecc_cor & ECC_COR_INFO__MAX_ERRORS; + max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor); /* * The register holds the maximum of per-sector corrected bitflips. @@ -402,13 +398,6 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd, return max_bitflips; } -#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) -#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) -#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) -#define ECC_ERROR_UNCORRECTABLE(x) ((x) & ERR_CORRECTION_INFO__ERROR_TYPE) -#define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8) -#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) - static int denali_sw_ecc_fixup(struct mtd_info *mtd, struct denali_nand_info *denali, unsigned long *uncor_ecc_flags, uint8_t *buf) @@ -426,18 +415,20 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd, do { err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS); - err_sector = ECC_SECTOR(err_addr); - err_byte = ECC_BYTE(err_addr); + err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr); + err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr); err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO); - err_cor_value = ECC_CORRECTION_VALUE(err_cor_info); - err_device = ECC_ERR_DEVICE(err_cor_info); + err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE, + err_cor_info); + err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE, + err_cor_info); /* reset the bitflip counter when crossing ECC sector */ if (err_sector != prev_sector) bitflips = 0; - if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) { + if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) { /* * Check later if this is a real ECC error, or * an erased sector. @@ -467,12 +458,11 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd, } prev_sector = err_sector; - } while (!ECC_LAST_ERR(err_cor_info)); + } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR)); /* - * Once handle all ecc errors, controller will trigger a - * ECC_TRANSACTION_DONE interrupt, so here just wait for - * a while for this interrupt + * Once handle all ECC errors, controller will trigger an + * ECC_TRANSACTION_DONE interrupt. */ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE); if (!(irq_status & INTR__ECC_TRANSACTION_DONE)) @@ -481,13 +471,6 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd, return max_bitflips; } -/* programs the controller to either enable/disable DMA transfers */ -static void denali_enable_dma(struct denali_nand_info *denali, bool en) -{ - iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->reg + DMA_ENABLE); - ioread32(denali->reg + DMA_ENABLE); -} - static void denali_setup_dma64(struct denali_nand_info *denali, dma_addr_t dma_addr, int page, int write) { @@ -502,14 +485,14 @@ static void denali_setup_dma64(struct denali_nand_info *denali, * 1. setup transfer type, interrupt when complete, * burst len = 64 bytes, the number of pages */ - denali_host_write(denali, mode, - 0x01002000 | (64 << 16) | (write << 8) | page_count); + denali->host_write(denali, mode, + 0x01002000 | (64 << 16) | (write << 8) | page_count); /* 2. set memory low address */ - denali_host_write(denali, mode, dma_addr); + denali->host_write(denali, mode, lower_32_bits(dma_addr)); /* 3. set memory high address */ - denali_host_write(denali, mode, (uint64_t)dma_addr >> 32); + denali->host_write(denali, mode, upper_32_bits(dma_addr)); } static void denali_setup_dma32(struct denali_nand_info *denali, @@ -523,32 +506,23 @@ static void denali_setup_dma32(struct denali_nand_info *denali, /* DMA is a four step process */ /* 1. setup transfer type and # of pages */ - denali_host_write(denali, mode | page, - 0x2000 | (write << 8) | page_count); + denali->host_write(denali, mode | page, + 0x2000 | (write << 8) | page_count); /* 2. set memory high address bits 23:8 */ - denali_host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); + denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); /* 3. set memory low address bits 23:8 */ - denali_host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); + denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); /* 4. interrupt when complete, burst len = 64 bytes */ - denali_host_write(denali, mode | 0x14000, 0x2400); -} - -static void denali_setup_dma(struct denali_nand_info *denali, - dma_addr_t dma_addr, int page, int write) -{ - if (denali->caps & DENALI_CAP_DMA_64BIT) - denali_setup_dma64(denali, dma_addr, page, write); - else - denali_setup_dma32(denali, dma_addr, page, write); + denali->host_write(denali, mode | 0x14000, 0x2400); } static int denali_pio_read(struct denali_nand_info *denali, void *buf, size_t size, int page, int raw) { - uint32_t addr = DENALI_BANK(denali) | page; + u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; uint32_t *buf32 = (uint32_t *)buf; uint32_t irq_status, ecc_err_mask; int i; @@ -560,9 +534,8 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf, denali_reset_irq(denali); - iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR); for (i = 0; i < size / 4; i++) - *buf32++ = ioread32(denali->host + DENALI_HOST_DATA); + *buf32++ = denali->host_read(denali, addr); irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC); if (!(irq_status & INTR__PAGE_XFER_INC)) @@ -577,16 +550,15 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf, static int denali_pio_write(struct denali_nand_info *denali, const void *buf, size_t size, int page, int raw) { - uint32_t addr = DENALI_BANK(denali) | page; + u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; const uint32_t *buf32 = (uint32_t *)buf; uint32_t irq_status; int i; denali_reset_irq(denali); - iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR); for (i = 0; i < size / 4; i++) - iowrite32(*buf32++, denali->host + DENALI_HOST_DATA); + denali->host_write(denali, addr, *buf32++); irq_status = denali_wait_for_irq(denali, INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL); @@ -635,19 +607,19 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, ecc_err_mask = INTR__ECC_ERR; } - denali_enable_dma(denali, true); + iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE); denali_reset_irq(denali); - denali_setup_dma(denali, dma_addr, page, write); + denali->setup_dma(denali, dma_addr, page, write); - /* wait for operation to complete */ irq_status = denali_wait_for_irq(denali, irq_mask); if (!(irq_status & INTR__DMA_CMD_COMP)) ret = -EIO; else if (irq_status & ecc_err_mask) ret = -EBADMSG; - denali_enable_dma(denali, false); + iowrite32(0, denali->reg + DMA_ENABLE); + dma_unmap_single(denali->dev, dma_addr, size, dir); if (irq_status & INTR__ERASED_PAGE) @@ -659,7 +631,9 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, static int denali_data_xfer(struct denali_nand_info *denali, void *buf, size_t size, int page, int raw, int write) { - setup_ecc_for_xfer(denali, !raw, raw); + iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); + iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0, + denali->reg + TRANSFER_SPARE_REG); if (denali->dma_avail) return denali_dma_xfer(denali, buf, size, page, raw, write); @@ -970,8 +944,8 @@ static int denali_erase(struct mtd_info *mtd, int page) denali_reset_irq(denali); - denali_host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, - DENALI_ERASE); + denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, + DENALI_ERASE); /* wait for erase to complete or failure to occur */ irq_status = denali_wait_for_irq(denali, @@ -1009,7 +983,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, tmp = ioread32(denali->reg + ACC_CLKS); tmp &= ~ACC_CLKS__VALUE; - tmp |= acc_clks; + tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks); iowrite32(tmp, denali->reg + ACC_CLKS); /* tRWH -> RE_2_WE */ @@ -1018,7 +992,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, tmp = ioread32(denali->reg + RE_2_WE); tmp &= ~RE_2_WE__VALUE; - tmp |= re_2_we; + tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we); iowrite32(tmp, denali->reg + RE_2_WE); /* tRHZ -> RE_2_RE */ @@ -1027,16 +1001,22 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, tmp = ioread32(denali->reg + RE_2_RE); tmp &= ~RE_2_RE__VALUE; - tmp |= re_2_re; + tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re); iowrite32(tmp, denali->reg + RE_2_RE); - /* tWHR -> WE_2_RE */ - we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk); + /* + * tCCS, tWHR -> WE_2_RE + * + * With WE_2_RE properly set, the Denali controller automatically takes + * care of the delay; the driver need not set NAND_WAIT_TCCS. + */ + we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), + t_clk); we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE; - tmp |= we_2_re; + tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re); iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE); /* tADL -> ADDR_2_DATA */ @@ -1050,8 +1030,8 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); - tmp &= ~addr_2_data_mask; - tmp |= addr_2_data; + tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; + tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data); iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA); /* tREH, tWH -> RDWR_EN_HI_CNT */ @@ -1061,7 +1041,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); tmp &= ~RDWR_EN_HI_CNT__VALUE; - tmp |= rdwr_en_hi; + tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi); iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT); /* tRP, tWP -> RDWR_EN_LO_CNT */ @@ -1075,7 +1055,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, tmp = ioread32(denali->reg + RDWR_EN_LO_CNT); tmp &= ~RDWR_EN_LO_CNT__VALUE; - tmp |= rdwr_en_lo; + tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo); iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT); /* tCS, tCEA -> CS_SETUP_CNT */ @@ -1086,7 +1066,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, tmp = ioread32(denali->reg + CS_SETUP_CNT); tmp &= ~CS_SETUP_CNT__VALUE; - tmp |= cs_setup; + tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup); iowrite32(tmp, denali->reg + CS_SETUP_CNT); return 0; @@ -1131,15 +1111,11 @@ static void denali_hw_init(struct denali_nand_info *denali) * if this value is 0, just let it be. */ denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES); - detect_max_banks(denali); + denali_detect_max_banks(denali); iowrite32(0x0F, denali->reg + RB_PIN_ENABLED); iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); - - /* Should set value for these registers when init */ - iowrite32(0, denali->reg + TWO_ROW_ADDR_CYCLES); - iowrite32(1, denali->reg + ECC_ENABLE); } int denali_calc_ecc_bytes(int step_size, int strength) @@ -1211,22 +1187,6 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = { .free = denali_ooblayout_free, }; -/* initialize driver data structures */ -static void denali_drv_init(struct denali_nand_info *denali) -{ - /* - * the completion object will be used to notify - * the callee that the interrupt is done - */ - init_completion(&denali->complete); - - /* - * the spinlock will be used to synchronize the ISR with any - * element that might be access shared data (interrupt status) - */ - spin_lock_init(&denali->irq_lock); -} - static int denali_multidev_fixup(struct denali_nand_info *denali) { struct nand_chip *chip = &denali->nand; @@ -1282,15 +1242,17 @@ int denali_init(struct denali_nand_info *denali) { struct nand_chip *chip = &denali->nand; struct mtd_info *mtd = nand_to_mtd(chip); + u32 features = ioread32(denali->reg + FEATURES); int ret; mtd->dev.parent = denali->dev; denali_hw_init(denali); - denali_drv_init(denali); + + init_completion(&denali->complete); + spin_lock_init(&denali->irq_lock); denali_clear_irq_all(denali); - /* Request IRQ after all the hardware initialization is finished */ ret = devm_request_irq(denali->dev, denali->irq, denali_isr, IRQF_SHARED, DENALI_NAND_NAME, denali); if (ret) { @@ -1308,7 +1270,6 @@ int denali_init(struct denali_nand_info *denali) if (!mtd->name) mtd->name = "denali-nand"; - /* register the driver with the NAND core subsystem */ chip->select_chip = denali_select_chip; chip->read_byte = denali_read_byte; chip->write_byte = denali_write_byte; @@ -1317,15 +1278,18 @@ int denali_init(struct denali_nand_info *denali) chip->dev_ready = denali_dev_ready; chip->waitfunc = denali_waitfunc; + if (features & FEATURES__INDEX_ADDR) { + denali->host_read = denali_indexed_read; + denali->host_write = denali_indexed_write; + } else { + denali->host_read = denali_direct_read; + denali->host_write = denali_direct_write; + } + /* clk rate info is needed for setup_data_interface */ if (denali->clk_x_rate) chip->setup_data_interface = denali_setup_data_interface; - /* - * scan for NAND devices attached to the controller - * this is the first stage in a two step process to register - * with the nand subsystem - */ ret = nand_scan_ident(mtd, denali->max_banks, NULL); if (ret) goto disable_irq; @@ -1347,20 +1311,15 @@ int denali_init(struct denali_nand_info *denali) if (denali->dma_avail) { chip->options |= NAND_USE_BOUNCE_BUFFER; chip->buf_align = 16; + if (denali->caps & DENALI_CAP_DMA_64BIT) + denali->setup_dma = denali_setup_dma64; + else + denali->setup_dma = denali_setup_dma32; } - /* - * second stage of the NAND scan - * this stage requires information regarding ECC and - * bad block management. - */ - chip->bbt_options |= NAND_BBT_USE_FLASH; chip->bbt_options |= NAND_BBT_NO_OOB; - chip->ecc.mode = NAND_ECC_HW_SYNDROME; - - /* no subpage writes on denali */ chip->options |= NAND_NO_SUBPAGE_WRITE; ret = denali_ecc_setup(mtd, chip, denali); @@ -1373,12 +1332,15 @@ int denali_init(struct denali_nand_info *denali) "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); - iowrite32(MAKE_ECC_CORRECTION(chip->ecc.strength, 1), + iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) | + FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength), denali->reg + ECC_CORRECTION); iowrite32(mtd->erasesize / mtd->writesize, denali->reg + PAGES_PER_BLOCK); iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0, denali->reg + DEVICE_WIDTH); + iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG, + denali->reg + TWO_ROW_ADDR_CYCLES); iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE); iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE); @@ -1441,7 +1403,6 @@ disable_irq: } EXPORT_SYMBOL(denali_init); -/* driver exit point */ void denali_remove(struct denali_nand_info *denali) { struct mtd_info *mtd = nand_to_mtd(&denali->nand); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 9239e6793e6e..2911066dacac 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -10,18 +10,16 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef __DENALI_H__ #define __DENALI_H__ #include +#include #include +#include +#include #define DEVICE_RESET 0x0 #define DEVICE_RESET__BANK(bank) BIT(bank) @@ -111,9 +109,6 @@ #define ECC_CORRECTION 0x1b0 #define ECC_CORRECTION__VALUE GENMASK(4, 0) #define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16) -#define MAKE_ECC_CORRECTION(val, thresh) \ - (((val) & (ECC_CORRECTION__VALUE)) | \ - (((thresh) << 16) & (ECC_CORRECTION__ERASE_THRESHOLD))) #define READ_MODE 0x1c0 #define READ_MODE__VALUE GENMASK(3, 0) @@ -255,13 +250,13 @@ #define ECC_ERROR_ADDRESS 0x630 #define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0) -#define ECC_ERROR_ADDRESS__SECTOR_NR GENMASK(15, 12) +#define ECC_ERROR_ADDRESS__SECTOR GENMASK(15, 12) #define ERR_CORRECTION_INFO 0x640 -#define ERR_CORRECTION_INFO__BYTEMASK GENMASK(7, 0) -#define ERR_CORRECTION_INFO__DEVICE_NR GENMASK(11, 8) -#define ERR_CORRECTION_INFO__ERROR_TYPE BIT(14) -#define ERR_CORRECTION_INFO__LAST_ERR_INFO BIT(15) +#define ERR_CORRECTION_INFO__BYTE GENMASK(7, 0) +#define ERR_CORRECTION_INFO__DEVICE GENMASK(11, 8) +#define ERR_CORRECTION_INFO__UNCOR BIT(14) +#define ERR_CORRECTION_INFO__LAST_ERR BIT(15) #define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10) #define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8) @@ -310,23 +305,24 @@ struct denali_nand_info { struct device *dev; void __iomem *reg; /* Register Interface */ void __iomem *host; /* Host Data/Command Interface */ - - /* elements used by ISR */ struct completion complete; - spinlock_t irq_lock; - uint32_t irq_mask; - uint32_t irq_status; + spinlock_t irq_lock; /* protect irq_mask and irq_status */ + u32 irq_mask; /* interrupts we are waiting for */ + u32 irq_status; /* interrupts that have happened */ int irq; - - void *buf; + void *buf; /* for syndrome layout conversion */ dma_addr_t dma_addr; - int dma_avail; + int dma_avail; /* can support DMA? */ int devs_per_cs; /* devices connected in parallel */ - int oob_skip_bytes; + int oob_skip_bytes; /* number of bytes reserved for BBM */ int max_banks; - unsigned int revision; - unsigned int caps; + unsigned int revision; /* IP revision */ + unsigned int caps; /* IP capability (or quirk) */ const struct nand_ecc_caps *ecc_caps; + u32 (*host_read)(struct denali_nand_info *denali, u32 addr); + void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data); + void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr, + int page, int write); }; #define DENALI_CAP_HW_ECC_FIXUP BIT(0) diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c index 56e2e177644d..cfd33e6ca77f 100644 --- a/drivers/mtd/nand/denali_dt.c +++ b/drivers/mtd/nand/denali_dt.c @@ -12,15 +12,16 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ + #include #include #include #include #include #include -#include #include #include +#include #include "denali.h" @@ -155,7 +156,6 @@ static struct platform_driver denali_dt_driver = { .of_match_table = denali_nand_dt_ids, }, }; - module_platform_driver(denali_dt_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c index 81370c79aa48..57fb7ae31412 100644 --- a/drivers/mtd/nand/denali_pci.c +++ b/drivers/mtd/nand/denali_pci.c @@ -11,6 +11,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ + +#include +#include #include #include #include @@ -106,7 +109,6 @@ failed_remap_reg: return ret; } -/* driver exit point */ static void denali_pci_remove(struct pci_dev *dev) { struct denali_nand_info *denali = pci_get_drvdata(dev); @@ -122,5 +124,4 @@ static struct pci_driver denali_pci_driver = { .probe = denali_pci_probe, .remove = denali_pci_remove, }; - module_pci_driver(denali_pci_driver); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index c3aa53caab5c..72671dc52e2e 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -705,8 +705,7 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu if (page_addr != -1) { WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress); WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); - /* One more address cycle for higher density devices */ - if (this->chipsize & 0x0c000000) { + if (this->options & NAND_ROW_ADDR_3) { WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); printk("high density\n"); } diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index fd3648952b5a..484f7fbc3f7d 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,12 +31,16 @@ #include #include #include -#include struct gpiomtd { void __iomem *io_sync; struct nand_chip nand_chip; struct gpio_nand_platdata plat; + struct gpio_desc *nce; /* Optional chip enable */ + struct gpio_desc *cle; + struct gpio_desc *ale; + struct gpio_desc *rdy; + struct gpio_desc *nwp; /* Optional write protection */ }; static inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd) @@ -78,11 +82,10 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) gpio_nand_dosync(gpiomtd); if (ctrl & NAND_CTRL_CHANGE) { - if (gpio_is_valid(gpiomtd->plat.gpio_nce)) - gpio_set_value(gpiomtd->plat.gpio_nce, - !(ctrl & NAND_NCE)); - gpio_set_value(gpiomtd->plat.gpio_cle, !!(ctrl & NAND_CLE)); - gpio_set_value(gpiomtd->plat.gpio_ale, !!(ctrl & NAND_ALE)); + if (gpiomtd->nce) + gpiod_set_value(gpiomtd->nce, !(ctrl & NAND_NCE)); + gpiod_set_value(gpiomtd->cle, !!(ctrl & NAND_CLE)); + gpiod_set_value(gpiomtd->ale, !!(ctrl & NAND_ALE)); gpio_nand_dosync(gpiomtd); } if (cmd == NAND_CMD_NONE) @@ -96,7 +99,7 @@ static int gpio_nand_devready(struct mtd_info *mtd) { struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - return gpio_get_value(gpiomtd->plat.gpio_rdy); + return gpiod_get_value(gpiomtd->rdy); } #ifdef CONFIG_OF @@ -123,12 +126,6 @@ static int gpio_nand_get_config_of(const struct device *dev, } } - plat->gpio_rdy = of_get_gpio(dev->of_node, 0); - plat->gpio_nce = of_get_gpio(dev->of_node, 1); - plat->gpio_ale = of_get_gpio(dev->of_node, 2); - plat->gpio_cle = of_get_gpio(dev->of_node, 3); - plat->gpio_nwp = of_get_gpio(dev->of_node, 4); - if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) plat->chip_delay = val; @@ -201,10 +198,11 @@ static int gpio_nand_remove(struct platform_device *pdev) nand_release(nand_to_mtd(&gpiomtd->nand_chip)); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_set_value(gpiomtd->plat.gpio_nwp, 0); - if (gpio_is_valid(gpiomtd->plat.gpio_nce)) - gpio_set_value(gpiomtd->plat.gpio_nce, 1); + /* Enable write protection and disable the chip */ + if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) + gpiod_set_value(gpiomtd->nwp, 0); + if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) + gpiod_set_value(gpiomtd->nce, 0); return 0; } @@ -215,66 +213,66 @@ static int gpio_nand_probe(struct platform_device *pdev) struct nand_chip *chip; struct mtd_info *mtd; struct resource *res; + struct device *dev = &pdev->dev; int ret = 0; - if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev)) + if (!dev->of_node && !dev_get_platdata(dev)) return -EINVAL; - gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); + gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL); if (!gpiomtd) return -ENOMEM; chip = &gpiomtd->nand_chip; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + chip->IO_ADDR_R = devm_ioremap_resource(dev, res); if (IS_ERR(chip->IO_ADDR_R)) return PTR_ERR(chip->IO_ADDR_R); res = gpio_nand_get_io_sync(pdev); if (res) { - gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); + gpiomtd->io_sync = devm_ioremap_resource(dev, res); if (IS_ERR(gpiomtd->io_sync)) return PTR_ERR(gpiomtd->io_sync); } - ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat); + ret = gpio_nand_get_config(dev, &gpiomtd->plat); if (ret) return ret; - if (gpio_is_valid(gpiomtd->plat.gpio_nce)) { - ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, - "NAND NCE"); - if (ret) - return ret; - gpio_direction_output(gpiomtd->plat.gpio_nce, 1); + /* Just enable the chip */ + gpiomtd->nce = devm_gpiod_get_optional(dev, "nce", GPIOD_OUT_HIGH); + if (IS_ERR(gpiomtd->nce)) + return PTR_ERR(gpiomtd->nce); + + /* We disable write protection once we know probe() will succeed */ + gpiomtd->nwp = devm_gpiod_get_optional(dev, "nwp", GPIOD_OUT_LOW); + if (IS_ERR(gpiomtd->nwp)) { + ret = PTR_ERR(gpiomtd->nwp); + goto out_ce; } - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { - ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, - "NAND NWP"); - if (ret) - return ret; + gpiomtd->nwp = devm_gpiod_get(dev, "ale", GPIOD_OUT_LOW); + if (IS_ERR(gpiomtd->nwp)) { + ret = PTR_ERR(gpiomtd->nwp); + goto out_ce; } - ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); - if (ret) - return ret; - gpio_direction_output(gpiomtd->plat.gpio_ale, 0); + gpiomtd->cle = devm_gpiod_get(dev, "cle", GPIOD_OUT_LOW); + if (IS_ERR(gpiomtd->cle)) { + ret = PTR_ERR(gpiomtd->cle); + goto out_ce; + } - ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); - if (ret) - return ret; - gpio_direction_output(gpiomtd->plat.gpio_cle, 0); - - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { - ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, - "NAND RDY"); - if (ret) - return ret; - gpio_direction_input(gpiomtd->plat.gpio_rdy); - chip->dev_ready = gpio_nand_devready; + gpiomtd->rdy = devm_gpiod_get_optional(dev, "rdy", GPIOD_IN); + if (IS_ERR(gpiomtd->rdy)) { + ret = PTR_ERR(gpiomtd->rdy); + goto out_ce; } + /* Using RDY pin */ + if (gpiomtd->rdy) + chip->dev_ready = gpio_nand_devready; nand_set_flash_node(chip, pdev->dev.of_node); chip->IO_ADDR_W = chip->IO_ADDR_R; @@ -285,12 +283,13 @@ static int gpio_nand_probe(struct platform_device *pdev) chip->cmd_ctrl = gpio_nand_cmd_ctrl; mtd = nand_to_mtd(chip); - mtd->dev.parent = &pdev->dev; + mtd->dev.parent = dev; platform_set_drvdata(pdev, gpiomtd); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); + /* Disable write protection, if wired up */ + if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) + gpiod_direction_output(gpiomtd->nwp, 1); ret = nand_scan(mtd, 1); if (ret) @@ -305,8 +304,11 @@ static int gpio_nand_probe(struct platform_device *pdev) return 0; err_wp: - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_set_value(gpiomtd->plat.gpio_nwp, 0); + if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) + gpiod_set_value(gpiomtd->nwp, 0); +out_ce: + if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) + gpiod_set_value(gpiomtd->nce, 0); return ret; } diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index d9ee1a7e6956..0897261c3e17 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -432,8 +432,7 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr) host->addr_value[0] |= (page_addr & 0xffff) << (host->addr_cycle * 8); host->addr_cycle += 2; - /* One more address cycle for devices > 128MiB */ - if (chip->chipsize > (128 << 20)) { + if (chip->options & NAND_ROW_ADDR_3) { host->addr_cycle += 1; if (host->command == NAND_CMD_ERASE1) host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16; diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c index 7f3b065b6b8f..c51d214d169e 100644 --- a/drivers/mtd/nand/mtk_ecc.c +++ b/drivers/mtd/nand/mtk_ecc.c @@ -115,6 +115,11 @@ static irqreturn_t mtk_ecc_irq(int irq, void *id) op = ECC_DECODE; dec = readw(ecc->regs + ECC_DECDONE); if (dec & ecc->sectors) { + /* + * Clear decode IRQ status once again to ensure that + * there will be no extra IRQ. + */ + readw(ecc->regs + ECC_DECIRQ_STA); ecc->sectors = 0; complete(&ecc->done); } else { @@ -130,8 +135,6 @@ static irqreturn_t mtk_ecc_irq(int irq, void *id) } } - writel(0, ecc->regs + ECC_IRQ_REG(op)); - return IRQ_HANDLED; } @@ -307,6 +310,12 @@ void mtk_ecc_disable(struct mtk_ecc *ecc) /* disable it */ mtk_ecc_wait_idle(ecc, op); + if (op == ECC_DECODE) + /* + * Clear decode IRQ status in case there is a timeout to wait + * decode IRQ. + */ + readw(ecc->regs + ECC_DECIRQ_STA); writew(0, ecc->regs + ECC_IRQ_REG(op)); writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op)); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 53e5e0337c3e..f3be0b2a8869 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -415,7 +415,7 @@ static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) * waits for completion. */ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) { - pr_debug("send_cmd(host, 0x%x, %d)\n", cmd, useirq); + dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); writew(cmd, NFC_V1_V2_FLASH_CMD); writew(NFC_CMD, NFC_V1_V2_CONFIG2); @@ -431,7 +431,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) udelay(1); } if (max_retries < 0) - pr_debug("%s: RESET failed\n", __func__); + dev_dbg(host->dev, "%s: RESET failed\n", __func__); } else { /* Wait for operation to complete */ wait_op_done(host, useirq); @@ -454,7 +454,7 @@ static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) * a NAND command. */ static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) { - pr_debug("send_addr(host, 0x%x %d)\n", addr, islast); + dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast); writew(addr, NFC_V1_V2_FLASH_ADDR); writew(NFC_ADDR, NFC_V1_V2_CONFIG2); @@ -607,7 +607,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, uint16_t ecc_status = get_ecc_status_v1(host); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { - pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + dev_dbg(host->dev, "HWECC uncorrectable 2-bit ECC error\n"); return -EBADMSG; } @@ -634,7 +634,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, do { err = ecc_stat & ecc_bit_mask; if (err > err_limit) { - printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); + dev_dbg(host->dev, "UnCorrectable RS-ECC Error\n"); return -EBADMSG; } else { ret += err; @@ -642,7 +642,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, ecc_stat >>= 4; } while (--no_subpages); - pr_debug("%d Symbol Correctable RS-ECC Error\n", ret); + dev_dbg(host->dev, "%d Symbol Correctable RS-ECC Error\n", ret); return ret; } @@ -673,7 +673,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) host->buf_start++; } - pr_debug("%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start); + dev_dbg(host->dev, "%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start); return ret; } @@ -859,8 +859,7 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) host->devtype_data->send_addr(host, (page_addr >> 8) & 0xff, true); } else { - /* One more address cycle for higher density devices */ - if (mtd->size >= 0x4000000) { + if (nand_chip->options & NAND_ROW_ADDR_3) { /* paddr_8 - paddr_15 */ host->devtype_data->send_addr(host, (page_addr >> 8) & 0xff, @@ -1212,7 +1211,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, struct nand_chip *nand_chip = mtd_to_nand(mtd); struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + dev_dbg(host->dev, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", command, column, page_addr); /* Reset command state information */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 12edaae17d81..6135d007a068 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -115,7 +115,7 @@ static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, struct nand_chip *chip = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; - if (section) + if (section || !ecc->total) return -ERANGE; oobregion->length = ecc->total; @@ -727,8 +727,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, chip->cmd_ctrl(mtd, page_addr, ctrl); ctrl &= ~NAND_CTRL_CHANGE; chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); - /* One more address cycle for devices > 32MiB */ - if (chip->chipsize > (32 << 20)) + if (chip->options & NAND_ROW_ADDR_3) chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); } chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); @@ -854,8 +853,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, chip->cmd_ctrl(mtd, page_addr, ctrl); chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE | NAND_ALE); - /* One more address cycle for devices > 128MiB */ - if (chip->chipsize > (128 << 20)) + if (chip->options & NAND_ROW_ADDR_3) chip->cmd_ctrl(mtd, page_addr >> 16, NAND_NCE | NAND_ALE); } @@ -1246,6 +1244,7 @@ int nand_reset(struct nand_chip *chip, int chipnr) return 0; } +EXPORT_SYMBOL_GPL(nand_reset); /** * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data @@ -2799,15 +2798,18 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd_to_nand(mtd); + int chipnr = (int)(to >> chip->chip_shift); struct mtd_oob_ops ops; int ret; - /* Wait for the device to get ready */ - panic_nand_wait(mtd, chip, 400); - /* Grab the device */ panic_nand_get_device(chip, mtd, FL_WRITING); + chip->select_chip(mtd, chipnr); + + /* Wait for the device to get ready */ + panic_nand_wait(mtd, chip, 400); + memset(&ops, 0, sizeof(ops)); ops.len = len; ops.datbuf = (uint8_t *)buf; @@ -3999,6 +4001,9 @@ ident_done: chip->chip_shift += 32 - 1; } + if (chip->chip_shift - chip->page_shift > 16) + chip->options |= NAND_ROW_ADDR_3; + chip->badblockbits = 8; chip->erase = single_erase; @@ -4700,6 +4705,19 @@ int nand_scan_tail(struct mtd_info *mtd) mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops); break; default: + /* + * Expose the whole OOB area to users if ECC_NONE + * is passed. We could do that for all kind of + * ->oobsize, but we must keep the old large/small + * page with ECC layout when ->oobsize <= 128 for + * compatibility reasons. + */ + if (ecc->mode == NAND_ECC_NONE) { + mtd_set_ooblayout(mtd, + &nand_ooblayout_lp_ops); + break; + } + WARN(1, "No oob scheme defined for oobsize %d\n", mtd->oobsize); ret = -EINVAL; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 246b4393118e..44322a363ba5 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -520,11 +520,16 @@ static int nandsim_debugfs_create(struct nandsim *dev) struct dentry *root = nsmtd->dbg.dfs_dir; struct dentry *dent; - if (!IS_ENABLED(CONFIG_DEBUG_FS)) + /* + * Just skip debugfs initialization when the debugfs directory is + * missing. + */ + if (IS_ERR_OR_NULL(root)) { + if (IS_ENABLED(CONFIG_DEBUG_FS) && + !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + NS_WARN("CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n"); return 0; - - if (IS_ERR_OR_NULL(root)) - return -1; + } dent = debugfs_create_file("nandsim_wear_report", S_IRUSR, root, dev, &dfs_fops); diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 7bb4d2ea9342..af5b32c9a791 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -154,7 +154,7 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, if (page_addr != -1) { write_addr_reg(nand, page_addr); - if (chip->chipsize > (128 << 20)) { + if (chip->options & NAND_ROW_ADDR_3) { write_addr_reg(nand, page_addr >> 8); write_addr_reg(nand, page_addr >> 16 | ENDADDR); } else { diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 54540c8fa1a2..dad438c4906a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1133,129 +1133,172 @@ static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, 0x97, 0x79, 0xe5, 0x24, 0xb5}; /** - * omap_calculate_ecc_bch - Generate bytes of ECC bytes + * _omap_calculate_ecc_bch - Generate ECC bytes for one sector * @mtd: MTD device structure * @dat: The pointer to data on which ecc is computed * @ecc_code: The ecc_code buffer + * @i: The sector number (for a multi sector page) * - * Support calculating of BCH4/8 ecc vectors for the page + * Support calculating of BCH4/8/16 ECC vectors for one sector + * within a page. Sector number is in @i. */ -static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_calc) +static int _omap_calculate_ecc_bch(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc, int i) { struct omap_nand_info *info = mtd_to_omap(mtd); int eccbytes = info->nand.ecc.bytes; struct gpmc_nand_regs *gpmc_regs = &info->reg; u8 *ecc_code; - unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4; + unsigned long bch_val1, bch_val2, bch_val3, bch_val4; u32 val; - int i, j; + int j; + + ecc_code = ecc_calc; + switch (info->ecc_opt) { + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH8_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); + bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); + bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); + *ecc_code++ = (bch_val4 & 0xFF); + *ecc_code++ = ((bch_val3 >> 24) & 0xFF); + *ecc_code++ = ((bch_val3 >> 16) & 0xFF); + *ecc_code++ = ((bch_val3 >> 8) & 0xFF); + *ecc_code++ = (bch_val3 & 0xFF); + *ecc_code++ = ((bch_val2 >> 24) & 0xFF); + *ecc_code++ = ((bch_val2 >> 16) & 0xFF); + *ecc_code++ = ((bch_val2 >> 8) & 0xFF); + *ecc_code++ = (bch_val2 & 0xFF); + *ecc_code++ = ((bch_val1 >> 24) & 0xFF); + *ecc_code++ = ((bch_val1 >> 16) & 0xFF); + *ecc_code++ = ((bch_val1 >> 8) & 0xFF); + *ecc_code++ = (bch_val1 & 0xFF); + break; + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH4_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); + *ecc_code++ = ((bch_val2 >> 12) & 0xFF); + *ecc_code++ = ((bch_val2 >> 4) & 0xFF); + *ecc_code++ = ((bch_val2 & 0xF) << 4) | + ((bch_val1 >> 28) & 0xF); + *ecc_code++ = ((bch_val1 >> 20) & 0xFF); + *ecc_code++ = ((bch_val1 >> 12) & 0xFF); + *ecc_code++ = ((bch_val1 >> 4) & 0xFF); + *ecc_code++ = ((bch_val1 & 0xF) << 4); + break; + case OMAP_ECC_BCH16_CODE_HW: + val = readl(gpmc_regs->gpmc_bch_result6[i]); + ecc_code[0] = ((val >> 8) & 0xFF); + ecc_code[1] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result5[i]); + ecc_code[2] = ((val >> 24) & 0xFF); + ecc_code[3] = ((val >> 16) & 0xFF); + ecc_code[4] = ((val >> 8) & 0xFF); + ecc_code[5] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result4[i]); + ecc_code[6] = ((val >> 24) & 0xFF); + ecc_code[7] = ((val >> 16) & 0xFF); + ecc_code[8] = ((val >> 8) & 0xFF); + ecc_code[9] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result3[i]); + ecc_code[10] = ((val >> 24) & 0xFF); + ecc_code[11] = ((val >> 16) & 0xFF); + ecc_code[12] = ((val >> 8) & 0xFF); + ecc_code[13] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result2[i]); + ecc_code[14] = ((val >> 24) & 0xFF); + ecc_code[15] = ((val >> 16) & 0xFF); + ecc_code[16] = ((val >> 8) & 0xFF); + ecc_code[17] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result1[i]); + ecc_code[18] = ((val >> 24) & 0xFF); + ecc_code[19] = ((val >> 16) & 0xFF); + ecc_code[20] = ((val >> 8) & 0xFF); + ecc_code[21] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result0[i]); + ecc_code[22] = ((val >> 24) & 0xFF); + ecc_code[23] = ((val >> 16) & 0xFF); + ecc_code[24] = ((val >> 8) & 0xFF); + ecc_code[25] = ((val >> 0) & 0xFF); + break; + default: + return -EINVAL; + } + + /* ECC scheme specific syndrome customizations */ + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back + */ + for (j = 0; j < eccbytes; j++) + ecc_calc[j] ^= bch4_polynomial[j]; + break; + case OMAP_ECC_BCH4_CODE_HW: + /* Set 8th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back + */ + for (j = 0; j < eccbytes; j++) + ecc_calc[j] ^= bch8_polynomial[j]; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* Set 14th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + case OMAP_ECC_BCH16_CODE_HW: + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used + * when SW based correction is required as ECC is required for one sector + * at a time. + */ +static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc) +{ + return _omap_calculate_ecc_bch(mtd, dat, ecc_calc, 0); +} + +/** + * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go. + */ +static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + int eccbytes = info->nand.ecc.bytes; + unsigned long nsectors; + int i, ret; nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; for (i = 0; i < nsectors; i++) { - ecc_code = ecc_calc; - switch (info->ecc_opt) { - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - case OMAP_ECC_BCH8_CODE_HW: - bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); - bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); - bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); - bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); - *ecc_code++ = (bch_val4 & 0xFF); - *ecc_code++ = ((bch_val3 >> 24) & 0xFF); - *ecc_code++ = ((bch_val3 >> 16) & 0xFF); - *ecc_code++ = ((bch_val3 >> 8) & 0xFF); - *ecc_code++ = (bch_val3 & 0xFF); - *ecc_code++ = ((bch_val2 >> 24) & 0xFF); - *ecc_code++ = ((bch_val2 >> 16) & 0xFF); - *ecc_code++ = ((bch_val2 >> 8) & 0xFF); - *ecc_code++ = (bch_val2 & 0xFF); - *ecc_code++ = ((bch_val1 >> 24) & 0xFF); - *ecc_code++ = ((bch_val1 >> 16) & 0xFF); - *ecc_code++ = ((bch_val1 >> 8) & 0xFF); - *ecc_code++ = (bch_val1 & 0xFF); - break; - case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: - case OMAP_ECC_BCH4_CODE_HW: - bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); - bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); - *ecc_code++ = ((bch_val2 >> 12) & 0xFF); - *ecc_code++ = ((bch_val2 >> 4) & 0xFF); - *ecc_code++ = ((bch_val2 & 0xF) << 4) | - ((bch_val1 >> 28) & 0xF); - *ecc_code++ = ((bch_val1 >> 20) & 0xFF); - *ecc_code++ = ((bch_val1 >> 12) & 0xFF); - *ecc_code++ = ((bch_val1 >> 4) & 0xFF); - *ecc_code++ = ((bch_val1 & 0xF) << 4); - break; - case OMAP_ECC_BCH16_CODE_HW: - val = readl(gpmc_regs->gpmc_bch_result6[i]); - ecc_code[0] = ((val >> 8) & 0xFF); - ecc_code[1] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result5[i]); - ecc_code[2] = ((val >> 24) & 0xFF); - ecc_code[3] = ((val >> 16) & 0xFF); - ecc_code[4] = ((val >> 8) & 0xFF); - ecc_code[5] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result4[i]); - ecc_code[6] = ((val >> 24) & 0xFF); - ecc_code[7] = ((val >> 16) & 0xFF); - ecc_code[8] = ((val >> 8) & 0xFF); - ecc_code[9] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result3[i]); - ecc_code[10] = ((val >> 24) & 0xFF); - ecc_code[11] = ((val >> 16) & 0xFF); - ecc_code[12] = ((val >> 8) & 0xFF); - ecc_code[13] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result2[i]); - ecc_code[14] = ((val >> 24) & 0xFF); - ecc_code[15] = ((val >> 16) & 0xFF); - ecc_code[16] = ((val >> 8) & 0xFF); - ecc_code[17] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result1[i]); - ecc_code[18] = ((val >> 24) & 0xFF); - ecc_code[19] = ((val >> 16) & 0xFF); - ecc_code[20] = ((val >> 8) & 0xFF); - ecc_code[21] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result0[i]); - ecc_code[22] = ((val >> 24) & 0xFF); - ecc_code[23] = ((val >> 16) & 0xFF); - ecc_code[24] = ((val >> 8) & 0xFF); - ecc_code[25] = ((val >> 0) & 0xFF); - break; - default: - return -EINVAL; - } - - /* ECC scheme specific syndrome customizations */ - switch (info->ecc_opt) { - case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: - /* Add constant polynomial to remainder, so that - * ECC of blank pages results in 0x0 on reading back */ - for (j = 0; j < eccbytes; j++) - ecc_calc[j] ^= bch4_polynomial[j]; - break; - case OMAP_ECC_BCH4_CODE_HW: - /* Set 8th ECC byte as 0x0 for ROM compatibility */ - ecc_calc[eccbytes - 1] = 0x0; - break; - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - /* Add constant polynomial to remainder, so that - * ECC of blank pages results in 0x0 on reading back */ - for (j = 0; j < eccbytes; j++) - ecc_calc[j] ^= bch8_polynomial[j]; - break; - case OMAP_ECC_BCH8_CODE_HW: - /* Set 14th ECC byte as 0x0 for ROM compatibility */ - ecc_calc[eccbytes - 1] = 0x0; - break; - case OMAP_ECC_BCH16_CODE_HW: - break; - default: - return -EINVAL; - } + ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i); + if (ret) + return ret; - ecc_calc += eccbytes; + ecc_calc += eccbytes; } return 0; @@ -1496,7 +1539,7 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, chip->write_buf(mtd, buf, mtd->writesize); /* Update ecc vector from GPMC result registers */ - chip->ecc.calculate(mtd, buf, &ecc_calc[0]); + omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]); ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, chip->ecc.total); @@ -1508,6 +1551,72 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +/** + * omap_write_subpage_bch - BCH hardware ECC based subpage write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @offset: column address of subpage within the page + * @data_len: data length + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * OMAP optimized subpage write method. + */ +static int omap_write_subpage_bch(struct mtd_info *mtd, + struct nand_chip *chip, u32 offset, + u32 data_len, const u8 *buf, + int oob_required, int page) +{ + u8 *ecc_calc = chip->buffers->ecccalc; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int ecc_steps = chip->ecc.steps; + u32 start_step = offset / ecc_size; + u32 end_step = (offset + data_len - 1) / ecc_size; + int step, ret = 0; + + /* + * Write entire page at one go as it would be optimal + * as ECC is calculated by hardware. + * ECC is calculated for all subpages but we choose + * only what we want. + */ + + /* Enable GPMC ECC engine */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* Write data */ + chip->write_buf(mtd, buf, mtd->writesize); + + for (step = 0; step < ecc_steps; step++) { + /* mask ECC of un-touched subpages by padding 0xFF */ + if (step < start_step || step > end_step) + memset(ecc_calc, 0xff, ecc_bytes); + else + ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step); + + if (ret) + return ret; + + buf += ecc_size; + ecc_calc += ecc_bytes; + } + + /* copy calculated ECC for whole page to chip->buffer->oob */ + /* this include masked-value(0xFF) for unwritten subpages */ + ecc_calc = chip->buffers->ecccalc; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + /* write OOB buffer to NAND device */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + /** * omap_read_page_bch - BCH ecc based page read function for entire page * @mtd: mtd info structure @@ -1544,7 +1653,7 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, chip->ecc.total); /* Calculate ecc bytes */ - chip->ecc.calculate(mtd, buf, ecc_calc); + omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc); ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, chip->ecc.total); @@ -1588,8 +1697,7 @@ static bool is_elm_present(struct omap_nand_info *info, return true; } -static bool omap2_nand_ecc_check(struct omap_nand_info *info, - struct omap_nand_platform_data *pdata) +static bool omap2_nand_ecc_check(struct omap_nand_info *info) { bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm; @@ -1804,7 +1912,6 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; - struct omap_nand_platform_data *pdata = NULL; struct mtd_info *mtd; struct nand_chip *nand_chip; int err; @@ -1821,29 +1928,10 @@ static int omap_nand_probe(struct platform_device *pdev) info->pdev = pdev; - if (dev->of_node) { - if (omap_get_dt_info(dev, info)) - return -EINVAL; - } else { - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "platform data missing\n"); - return -EINVAL; - } - - info->gpmc_cs = pdata->cs; - info->reg = pdata->reg; - info->ecc_opt = pdata->ecc_opt; - if (pdata->dev_ready) - dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n"); - - info->xfer_type = pdata->xfer_type; - info->devsize = pdata->devsize; - info->elm_of_node = pdata->elm_of_node; - info->flash_bbt = pdata->flash_bbt; - } + err = omap_get_dt_info(dev, info); + if (err) + return err; - platform_set_drvdata(pdev, info); info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); if (!info->ops) { dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); @@ -2002,7 +2090,7 @@ static int omap_nand_probe(struct platform_device *pdev) goto return_error; } - if (!omap2_nand_ecc_check(info, pdata)) { + if (!omap2_nand_ecc_check(info)) { err = -EINVAL; goto return_error; } @@ -2044,7 +2132,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 4; nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch; + nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ oobbytes_per_step = nand_chip->ecc.bytes + 1; @@ -2066,9 +2154,9 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 4; nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; + nand_chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); oobbytes_per_step = nand_chip->ecc.bytes; @@ -2087,7 +2175,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 8; nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch; + nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ oobbytes_per_step = nand_chip->ecc.bytes + 1; @@ -2109,9 +2197,9 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 8; nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; + nand_chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); oobbytes_per_step = nand_chip->ecc.bytes; @@ -2131,9 +2219,9 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 16; nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; + nand_chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); oobbytes_per_step = nand_chip->ecc.bytes; @@ -2167,10 +2255,9 @@ scan_tail: if (err) goto return_error; - if (dev->of_node) - mtd_device_register(mtd, NULL, 0); - else - mtd_device_register(mtd, pdata->parts, pdata->nr_parts); + err = mtd_device_register(mtd, NULL, 0); + if (err) + goto return_error; platform_set_drvdata(pdev, mtd); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 85cff68643e0..90b9a9ccbe60 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) #define NAND_STOP_DELAY msecs_to_jiffies(40) @@ -45,6 +47,10 @@ */ #define INIT_BUFFER_SIZE 2048 +/* System control register and bit to enable NAND on some SoCs */ +#define GENCONF_SOC_DEVICE_MUX 0x208 +#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) + /* registers and bit definitions */ #define NDCR (0x00) /* Control register */ #define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ @@ -174,6 +180,7 @@ enum { enum pxa3xx_nand_variant { PXA3XX_NAND_VARIANT_PXA, PXA3XX_NAND_VARIANT_ARMADA370, + PXA3XX_NAND_VARIANT_ARMADA_8K, }; struct pxa3xx_nand_host { @@ -425,6 +432,10 @@ static const struct of_device_id pxa3xx_nand_dt_ids[] = { .compatible = "marvell,armada370-nand", .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, }, + { + .compatible = "marvell,armada-8k-nand", + .data = (void *)PXA3XX_NAND_VARIANT_ARMADA_8K, + }, {} }; MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); @@ -825,7 +836,8 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) info->retcode = ERR_UNCORERR; if (status & NDSR_CORERR) { info->retcode = ERR_CORERR; - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 && + if ((info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) && info->ecc_bch) info->ecc_err_cnt = NDSR_ERR_CNT(status); else @@ -888,7 +900,8 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) nand_writel(info, NDCB0, info->ndcb2); /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) nand_writel(info, NDCB0, info->ndcb3); } @@ -1671,7 +1684,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) chip->options |= NAND_BUSWIDTH_16; /* Device detection must be done with ECC disabled */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) nand_writel(info, NDECCCTRL, 0x0); if (pdata->flash_bbt) @@ -1709,7 +1723,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) * (aka splitted) command handling, */ if (mtd->writesize > PAGE_CHUNK_SIZE) { - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) { + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) { chip->cmdfunc = nand_cmdfunc_extended; } else { dev_err(&info->pdev->dev, @@ -1928,6 +1943,24 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev) if (!of_id) return 0; + /* + * Some SoCs like A7k/A8k need to enable manually the NAND + * controller to avoid being bootloader dependent. This is done + * through the use of a single bit in the System Functions registers. + */ + if (pxa3xx_nand_get_variant(pdev) == PXA3XX_NAND_VARIANT_ARMADA_8K) { + struct regmap *sysctrl_base = syscon_regmap_lookup_by_phandle( + pdev->dev.of_node, "marvell,system-controller"); + u32 reg; + + if (IS_ERR(sysctrl_base)) + return PTR_ERR(sysctrl_base); + + regmap_read(sysctrl_base, GENCONF_SOC_DEVICE_MUX, ®); + reg |= GENCONF_SOC_DEVICE_MUX_NFC_EN; + regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); + } + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index 3baddfc997d1..2656c1ac5646 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -22,6 +22,7 @@ #include #include #include +#include /* NANDc reg offsets */ #define NAND_FLASH_CMD 0x00 @@ -199,6 +200,15 @@ nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ */ #define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) +/* Returns the NAND register physical address */ +#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) + +/* Returns the dma address for reg read buffer */ +#define reg_buf_dma_addr(chip, vaddr) \ + ((chip)->reg_read_dma + \ + ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) + +#define QPIC_PER_CW_CMD_ELEMENTS 32 #define QPIC_PER_CW_CMD_SGL 32 #define QPIC_PER_CW_DATA_SGL 8 @@ -221,8 +231,13 @@ nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ /* * This data type corresponds to the BAM transaction which will be used for all * NAND transfers. + * @bam_ce - the array of BAM command elements * @cmd_sgl - sgl for NAND BAM command pipe * @data_sgl - sgl for NAND BAM consumer/producer pipe + * @bam_ce_pos - the index in bam_ce which is available for next sgl + * @bam_ce_start - the index in bam_ce which marks the start position ce + * for current sgl. It will be used for size calculation + * for current sgl * @cmd_sgl_pos - current index in command sgl. * @cmd_sgl_start - start index in command sgl. * @tx_sgl_pos - current index in data sgl for tx. @@ -231,8 +246,11 @@ nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ * @rx_sgl_start - start index in data sgl for rx. */ struct bam_transaction { + struct bam_cmd_element *bam_ce; struct scatterlist *cmd_sgl; struct scatterlist *data_sgl; + u32 bam_ce_pos; + u32 bam_ce_start; u32 cmd_sgl_pos; u32 cmd_sgl_start; u32 tx_sgl_pos; @@ -307,7 +325,8 @@ struct nandc_regs { * controller * @dev: parent device * @base: MMIO base - * @base_dma: physical base address of controller registers + * @base_phys: physical base address of controller registers + * @base_dma: dma base address of controller registers * @core_clk: controller clock * @aon_clk: another controller clock * @@ -340,6 +359,7 @@ struct qcom_nand_controller { struct device *dev; void __iomem *base; + phys_addr_t base_phys; dma_addr_t base_dma; struct clk *core_clk; @@ -462,7 +482,8 @@ alloc_bam_transaction(struct qcom_nand_controller *nandc) bam_txn_size = sizeof(*bam_txn) + num_cw * - ((sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + + ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + + (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); @@ -472,6 +493,10 @@ alloc_bam_transaction(struct qcom_nand_controller *nandc) bam_txn = bam_txn_buf; bam_txn_buf += sizeof(*bam_txn); + bam_txn->bam_ce = bam_txn_buf; + bam_txn_buf += + sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; + bam_txn->cmd_sgl = bam_txn_buf; bam_txn_buf += sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; @@ -489,6 +514,8 @@ static void clear_bam_transaction(struct qcom_nand_controller *nandc) if (!nandc->props->is_bam) return; + bam_txn->bam_ce_pos = 0; + bam_txn->bam_ce_start = 0; bam_txn->cmd_sgl_pos = 0; bam_txn->cmd_sgl_start = 0; bam_txn->tx_sgl_pos = 0; @@ -733,6 +760,66 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, return 0; } +/* + * Prepares the command descriptor for BAM DMA which will be used for NAND + * register reads and writes. The command descriptor requires the command + * to be formed in command element type so this function uses the command + * element from bam transaction ce array and fills the same with required + * data. A single SGL can contain multiple command elements so + * NAND_BAM_NEXT_SGL will be used for starting the separate SGL + * after the current command element. + */ +static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, + int reg_off, const void *vaddr, + int size, unsigned int flags) +{ + int bam_ce_size; + int i, ret; + struct bam_cmd_element *bam_ce_buffer; + struct bam_transaction *bam_txn = nandc->bam_txn; + + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; + + /* fill the command desc */ + for (i = 0; i < size; i++) { + if (read) + bam_prep_ce(&bam_ce_buffer[i], + nandc_reg_phys(nandc, reg_off + 4 * i), + BAM_READ_COMMAND, + reg_buf_dma_addr(nandc, + (__le32 *)vaddr + i)); + else + bam_prep_ce_le32(&bam_ce_buffer[i], + nandc_reg_phys(nandc, reg_off + 4 * i), + BAM_WRITE_COMMAND, + *((__le32 *)vaddr + i)); + } + + bam_txn->bam_ce_pos += size; + + /* use the separate sgl after this command */ + if (flags & NAND_BAM_NEXT_SGL) { + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; + bam_ce_size = (bam_txn->bam_ce_pos - + bam_txn->bam_ce_start) * + sizeof(struct bam_cmd_element); + sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], + bam_ce_buffer, bam_ce_size); + bam_txn->cmd_sgl_pos++; + bam_txn->bam_ce_start = bam_txn->bam_ce_pos; + + if (flags & NAND_BAM_NWD) { + ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, + DMA_PREP_FENCE | + DMA_PREP_CMD); + if (ret) + return ret; + } + } + + return 0; +} + /* * Prepares the data descriptor for BAM DMA which will be used for NAND * data reads and writes. @@ -851,19 +938,22 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first, { bool flow_control = false; void *vaddr; - int size; - if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) - flow_control = true; + vaddr = nandc->reg_read_buf + nandc->reg_read_pos; + nandc->reg_read_pos += num_regs; if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) first = dev_cmd_reg_addr(nandc, first); - size = num_regs * sizeof(u32); - vaddr = nandc->reg_read_buf + nandc->reg_read_pos; - nandc->reg_read_pos += num_regs; + if (nandc->props->is_bam) + return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, + num_regs, flags); + + if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) + flow_control = true; - return prep_adm_dma_desc(nandc, true, first, vaddr, size, flow_control); + return prep_adm_dma_desc(nandc, true, first, vaddr, + num_regs * sizeof(u32), flow_control); } /* @@ -880,13 +970,9 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, bool flow_control = false; struct nandc_regs *regs = nandc->regs; void *vaddr; - int size; vaddr = offset_to_nandc_reg(regs, first); - if (first == NAND_FLASH_CMD) - flow_control = true; - if (first == NAND_ERASED_CW_DETECT_CFG) { if (flags & NAND_ERASED_CW_SET) vaddr = ®s->erased_cw_detect_cfg_set; @@ -903,10 +989,15 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); - size = num_regs * sizeof(u32); + if (nandc->props->is_bam) + return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, + num_regs, flags); + + if (first == NAND_FLASH_CMD) + flow_control = true; - return prep_adm_dma_desc(nandc, false, first, vaddr, size, - flow_control); + return prep_adm_dma_desc(nandc, false, first, vaddr, + num_regs * sizeof(u32), flow_control); } /* @@ -1170,7 +1261,8 @@ static int submit_descs(struct qcom_nand_controller *nandc) } if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { - r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 0); + r = prepare_bam_async_desc(nandc, nandc->cmd_chan, + DMA_PREP_CMD); if (r) return r; } @@ -2705,6 +2797,7 @@ static int qcom_nandc_probe(struct platform_device *pdev) if (IS_ERR(nandc->base)) return PTR_ERR(nandc->base); + nandc->base_phys = res->start; nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start); nandc->core_clk = devm_clk_get(dev, "core"); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e7f3c98487e6..3c5008a4f5f3 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -1094,14 +1094,11 @@ MODULE_DEVICE_TABLE(of, of_flctl_match); static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) { - const struct of_device_id *match; - struct flctl_soc_config *config; + const struct flctl_soc_config *config; struct sh_flctl_platform_data *pdata; - match = of_match_device(of_flctl_match, dev); - if (match) - config = (struct flctl_soc_config *)match->data; - else { + config = of_device_get_match_data(dev); + if (!config) { dev_err(dev, "%s: no OF configuration attached\n", __func__); return NULL; } diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index d206b3c533bc..ee5ab994132f 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -6,3 +6,11 @@ config MTD_PARSER_TRX may contain up to 3/4 partitions (depending on the version). This driver will parse TRX header and report at least two partitions: kernel and rootfs. + +config MTD_SHARPSL_PARTS + tristate "Sharp SL Series NAND flash partition parser" + depends on MTD_NAND_SHARPSL || MTD_NAND_TMIO || COMPILE_TEST + help + This provides the read-only FTL logic necessary to read the partition + table from the NAND flash of Sharp SL Series (Zaurus) and the MTD + partition parser using this code. diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile index 4d9024e0be3b..5b1bcc3d90d9 100644 --- a/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o +obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o diff --git a/drivers/mtd/parsers/sharpslpart.c b/drivers/mtd/parsers/sharpslpart.c new file mode 100644 index 000000000000..5fe0079ea5ed --- /dev/null +++ b/drivers/mtd/parsers/sharpslpart.c @@ -0,0 +1,398 @@ +/* + * sharpslpart.c - MTD partition parser for NAND flash using the SHARP FTL + * for logical addressing, as used on the PXA models of the SHARP SL Series. + * + * Copyright (C) 2017 Andrea Adami + * + * Based on SHARP GPL 2.4 sources: + * http://support.ezaurus.com/developer/source/source_dl.asp + * drivers/mtd/nand/sharp_sl_logical.c + * linux/include/asm-arm/sharp_nand_logical.h + * + * Copyright (C) 2002 SHARP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* oob structure */ +#define NAND_NOOB_LOGADDR_00 8 +#define NAND_NOOB_LOGADDR_01 9 +#define NAND_NOOB_LOGADDR_10 10 +#define NAND_NOOB_LOGADDR_11 11 +#define NAND_NOOB_LOGADDR_20 12 +#define NAND_NOOB_LOGADDR_21 13 + +#define BLOCK_IS_RESERVED 0xffff +#define BLOCK_UNMASK_COMPLEMENT 1 + +/* factory defaults */ +#define SHARPSL_NAND_PARTS 3 +#define SHARPSL_FTL_PART_SIZE (7 * SZ_1M) +#define SHARPSL_PARTINFO1_LADDR 0x00060000 +#define SHARPSL_PARTINFO2_LADDR 0x00064000 + +#define BOOT_MAGIC 0x424f4f54 +#define FSRO_MAGIC 0x4653524f +#define FSRW_MAGIC 0x46535257 + +/** + * struct sharpsl_ftl - Sharp FTL Logical Table + * @logmax: number of logical blocks + * @log2phy: the logical-to-physical table + * + * Structure containing the logical-to-physical translation table + * used by the SHARP SL FTL. + */ +struct sharpsl_ftl { + unsigned int logmax; + unsigned int *log2phy; +}; + +/* verify that the OOB bytes 8 to 15 are free and available for the FTL */ +static int sharpsl_nand_check_ooblayout(struct mtd_info *mtd) +{ + u8 freebytes = 0; + int section = 0; + + while (true) { + struct mtd_oob_region oobfree = { }; + int ret, i; + + ret = mtd_ooblayout_free(mtd, section++, &oobfree); + if (ret) + break; + + if (!oobfree.length || oobfree.offset > 15 || + (oobfree.offset + oobfree.length) < 8) + continue; + + i = oobfree.offset >= 8 ? oobfree.offset : 8; + for (; i < oobfree.offset + oobfree.length && i < 16; i++) + freebytes |= BIT(i - 8); + + if (freebytes == 0xff) + return 0; + } + + return -ENOTSUPP; +} + +static int sharpsl_nand_read_oob(struct mtd_info *mtd, loff_t offs, u8 *buf) +{ + struct mtd_oob_ops ops = { }; + int ret; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + + ret = mtd_read_oob(mtd, offs, &ops); + if (ret != 0 || mtd->oobsize != ops.oobretlen) + return -1; + + return 0; +} + +/* + * The logical block number assigned to a physical block is stored in the OOB + * of the first page, in 3 16-bit copies with the following layout: + * + * 01234567 89abcdef + * -------- -------- + * ECC BB xyxyxy + * + * When reading we check that the first two copies agree. + * In case of error, matching is tried using the following pairs. + * Reserved values 0xffff mean the block is kept for wear leveling. + * + * 01234567 89abcdef + * -------- -------- + * ECC BB xyxy oob[8]==oob[10] && oob[9]==oob[11] -> byte0=8 byte1=9 + * ECC BB xyxy oob[10]==oob[12] && oob[11]==oob[13] -> byte0=10 byte1=11 + * ECC BB xy xy oob[12]==oob[8] && oob[13]==oob[9] -> byte0=12 byte1=13 + */ +static int sharpsl_nand_get_logical_num(u8 *oob) +{ + u16 us; + int good0, good1; + + if (oob[NAND_NOOB_LOGADDR_00] == oob[NAND_NOOB_LOGADDR_10] && + oob[NAND_NOOB_LOGADDR_01] == oob[NAND_NOOB_LOGADDR_11]) { + good0 = NAND_NOOB_LOGADDR_00; + good1 = NAND_NOOB_LOGADDR_01; + } else if (oob[NAND_NOOB_LOGADDR_10] == oob[NAND_NOOB_LOGADDR_20] && + oob[NAND_NOOB_LOGADDR_11] == oob[NAND_NOOB_LOGADDR_21]) { + good0 = NAND_NOOB_LOGADDR_10; + good1 = NAND_NOOB_LOGADDR_11; + } else if (oob[NAND_NOOB_LOGADDR_20] == oob[NAND_NOOB_LOGADDR_00] && + oob[NAND_NOOB_LOGADDR_21] == oob[NAND_NOOB_LOGADDR_01]) { + good0 = NAND_NOOB_LOGADDR_20; + good1 = NAND_NOOB_LOGADDR_21; + } else { + return -EINVAL; + } + + us = oob[good0] | oob[good1] << 8; + + /* parity check */ + if (hweight16(us) & BLOCK_UNMASK_COMPLEMENT) + return -EINVAL; + + /* reserved */ + if (us == BLOCK_IS_RESERVED) + return BLOCK_IS_RESERVED; + + return (us >> 1) & GENMASK(9, 0); +} + +static int sharpsl_nand_init_ftl(struct mtd_info *mtd, struct sharpsl_ftl *ftl) +{ + unsigned int block_num, log_num, phymax; + loff_t block_adr; + u8 *oob; + int i, ret; + + oob = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!oob) + return -ENOMEM; + + phymax = mtd_div_by_eb(SHARPSL_FTL_PART_SIZE, mtd); + + /* FTL reserves 5% of the blocks + 1 spare */ + ftl->logmax = ((phymax * 95) / 100) - 1; + + ftl->log2phy = kmalloc_array(ftl->logmax, sizeof(*ftl->log2phy), + GFP_KERNEL); + if (!ftl->log2phy) { + ret = -ENOMEM; + goto exit; + } + + /* initialize ftl->log2phy */ + for (i = 0; i < ftl->logmax; i++) + ftl->log2phy[i] = UINT_MAX; + + /* create physical-logical table */ + for (block_num = 0; block_num < phymax; block_num++) { + block_adr = block_num * mtd->erasesize; + + if (mtd_block_isbad(mtd, block_adr)) + continue; + + if (sharpsl_nand_read_oob(mtd, block_adr, oob)) + continue; + + /* get logical block */ + log_num = sharpsl_nand_get_logical_num(oob); + + /* cut-off errors and skip the out-of-range values */ + if (log_num > 0 && log_num < ftl->logmax) { + if (ftl->log2phy[log_num] == UINT_MAX) + ftl->log2phy[log_num] = block_num; + } + } + + pr_info("Sharp SL FTL: %d blocks used (%d logical, %d reserved)\n", + phymax, ftl->logmax, phymax - ftl->logmax); + + ret = 0; +exit: + kfree(oob); + return ret; +} + +void sharpsl_nand_cleanup_ftl(struct sharpsl_ftl *ftl) +{ + kfree(ftl->log2phy); +} + +static int sharpsl_nand_read_laddr(struct mtd_info *mtd, + loff_t from, + size_t len, + void *buf, + struct sharpsl_ftl *ftl) +{ + unsigned int log_num, final_log_num; + unsigned int block_num; + loff_t block_adr; + loff_t block_ofs; + size_t retlen; + int err; + + log_num = mtd_div_by_eb((u32)from, mtd); + final_log_num = mtd_div_by_eb(((u32)from + len - 1), mtd); + + if (len <= 0 || log_num >= ftl->logmax || final_log_num > log_num) + return -EINVAL; + + block_num = ftl->log2phy[log_num]; + block_adr = block_num * mtd->erasesize; + block_ofs = mtd_mod_by_eb((u32)from, mtd); + + err = mtd_read(mtd, block_adr + block_ofs, len, &retlen, buf); + /* Ignore corrected ECC errors */ + if (mtd_is_bitflip(err)) + err = 0; + + if (!err && retlen != len) + err = -EIO; + + if (err) + pr_err("sharpslpart: error, read failed at %#llx\n", + block_adr + block_ofs); + + return err; +} + +/* + * MTD Partition Parser + * + * Sample values read from SL-C860 + * + * # cat /proc/mtd + * dev: size erasesize name + * mtd0: 006d0000 00020000 "Filesystem" + * mtd1: 00700000 00004000 "smf" + * mtd2: 03500000 00004000 "root" + * mtd3: 04400000 00004000 "home" + * + * PARTITIONINFO1 + * 0x00060000: 00 00 00 00 00 00 70 00 42 4f 4f 54 00 00 00 00 ......p.BOOT.... + * 0x00060010: 00 00 70 00 00 00 c0 03 46 53 52 4f 00 00 00 00 ..p.....FSRO.... + * 0x00060020: 00 00 c0 03 00 00 00 04 46 53 52 57 00 00 00 00 ........FSRW.... + */ +struct sharpsl_nand_partinfo { + __le32 start; + __le32 end; + __be32 magic; + u32 reserved; +}; + +static int sharpsl_nand_read_partinfo(struct mtd_info *master, + loff_t from, + size_t len, + struct sharpsl_nand_partinfo *buf, + struct sharpsl_ftl *ftl) +{ + int ret; + + ret = sharpsl_nand_read_laddr(master, from, len, buf, ftl); + if (ret) + return ret; + + /* check for magics */ + if (be32_to_cpu(buf[0].magic) != BOOT_MAGIC || + be32_to_cpu(buf[1].magic) != FSRO_MAGIC || + be32_to_cpu(buf[2].magic) != FSRW_MAGIC) { + pr_err("sharpslpart: magic values mismatch\n"); + return -EINVAL; + } + + /* fixup for hardcoded value 64 MiB (for older models) */ + buf[2].end = cpu_to_le32(master->size); + + /* extra sanity check */ + if (le32_to_cpu(buf[0].end) <= le32_to_cpu(buf[0].start) || + le32_to_cpu(buf[1].start) < le32_to_cpu(buf[0].end) || + le32_to_cpu(buf[1].end) <= le32_to_cpu(buf[1].start) || + le32_to_cpu(buf[2].start) < le32_to_cpu(buf[1].end) || + le32_to_cpu(buf[2].end) <= le32_to_cpu(buf[2].start)) { + pr_err("sharpslpart: partition sizes mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int sharpsl_parse_mtd_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + struct sharpsl_ftl ftl; + struct sharpsl_nand_partinfo buf[SHARPSL_NAND_PARTS]; + struct mtd_partition *sharpsl_nand_parts; + int err; + + /* check that OOB bytes 8 to 15 used by the FTL are actually free */ + err = sharpsl_nand_check_ooblayout(master); + if (err) + return err; + + /* init logical mgmt (FTL) */ + err = sharpsl_nand_init_ftl(master, &ftl); + if (err) + return err; + + /* read and validate first partition table */ + pr_info("sharpslpart: try reading first partition table\n"); + err = sharpsl_nand_read_partinfo(master, + SHARPSL_PARTINFO1_LADDR, + sizeof(buf), buf, &ftl); + if (err) { + /* fallback: read second partition table */ + pr_warn("sharpslpart: first partition table is invalid, retry using the second\n"); + err = sharpsl_nand_read_partinfo(master, + SHARPSL_PARTINFO2_LADDR, + sizeof(buf), buf, &ftl); + } + + /* cleanup logical mgmt (FTL) */ + sharpsl_nand_cleanup_ftl(&ftl); + + if (err) { + pr_err("sharpslpart: both partition tables are invalid\n"); + return err; + } + + sharpsl_nand_parts = kzalloc(sizeof(*sharpsl_nand_parts) * + SHARPSL_NAND_PARTS, GFP_KERNEL); + if (!sharpsl_nand_parts) + return -ENOMEM; + + /* original names */ + sharpsl_nand_parts[0].name = "smf"; + sharpsl_nand_parts[0].offset = le32_to_cpu(buf[0].start); + sharpsl_nand_parts[0].size = le32_to_cpu(buf[0].end) - + le32_to_cpu(buf[0].start); + + sharpsl_nand_parts[1].name = "root"; + sharpsl_nand_parts[1].offset = le32_to_cpu(buf[1].start); + sharpsl_nand_parts[1].size = le32_to_cpu(buf[1].end) - + le32_to_cpu(buf[1].start); + + sharpsl_nand_parts[2].name = "home"; + sharpsl_nand_parts[2].offset = le32_to_cpu(buf[2].start); + sharpsl_nand_parts[2].size = le32_to_cpu(buf[2].end) - + le32_to_cpu(buf[2].start); + + *pparts = sharpsl_nand_parts; + return SHARPSL_NAND_PARTS; +} + +static struct mtd_part_parser sharpsl_mtd_parser = { + .parse_fn = sharpsl_parse_mtd_partitions, + .name = "sharpslpart", +}; +module_mtd_part_parser(sharpsl_mtd_parser); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrea Adami "); +MODULE_DESCRIPTION("MTD partitioning for NAND flash on Sharp SL Series"); diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 69c638dd0484..89da88e59121 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -50,7 +50,7 @@ config SPI_ATMEL_QUADSPI config SPI_CADENCE_QUADSPI tristate "Cadence Quad SPI controller" - depends on OF && (ARM || COMPILE_TEST) + depends on OF && (ARM || ARM64 || COMPILE_TEST) help Enable support for the Cadence Quad SPI Flash controller. @@ -90,7 +90,7 @@ config SPI_INTEL_SPI tristate config SPI_INTEL_SPI_PCI - tristate "Intel PCH/PCU SPI flash PCI driver" if EXPERT + tristate "Intel PCH/PCU SPI flash PCI driver" depends on X86 && PCI select SPI_INTEL_SPI help @@ -106,7 +106,7 @@ config SPI_INTEL_SPI_PCI will be called intel-spi-pci. config SPI_INTEL_SPI_PLATFORM - tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT + tristate "Intel PCH/PCU SPI flash platform driver" depends on X86 select SPI_INTEL_SPI help diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index 53c7d8e0327a..75a2bc447a99 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,9 @@ #define CQSPI_NAME "cadence-qspi" #define CQSPI_MAX_CHIPSELECT 16 +/* Quirks */ +#define CQSPI_NEEDS_WR_DELAY BIT(0) + struct cqspi_st; struct cqspi_flash_pdata { @@ -75,7 +79,9 @@ struct cqspi_st { bool is_decoded_cs; u32 fifo_depth; u32 fifo_width; + bool rclk_en; u32 trigger_address; + u32 wr_delay; struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT]; }; @@ -608,6 +614,15 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, reinit_completion(&cqspi->transfer_complete); writel(CQSPI_REG_INDIRECTWR_START_MASK, reg_base + CQSPI_REG_INDIRECTWR); + /* + * As per 66AK2G02 TRM SPRUHY8F section 11.15.5.3 Indirect Access + * Controller programming sequence, couple of cycles of + * QSPI_REF_CLK delay is required for the above bit to + * be internally synchronized by the QSPI module. Provide 5 + * cycles of delay. + */ + if (cqspi->wr_delay) + ndelay(cqspi->wr_delay); while (remaining > 0) { write_bytes = remaining > page_size ? page_size : remaining; @@ -775,7 +790,7 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi) } static void cqspi_readdata_capture(struct cqspi_st *cqspi, - const unsigned int bypass, + const bool bypass, const unsigned int delay) { void __iomem *reg_base = cqspi->iobase; @@ -839,7 +854,8 @@ static void cqspi_configure(struct spi_nor *nor) cqspi->sclk = sclk; cqspi_config_baudrate_div(cqspi); cqspi_delay(nor); - cqspi_readdata_capture(cqspi, 1, f_pdata->read_delay); + cqspi_readdata_capture(cqspi, !cqspi->rclk_en, + f_pdata->read_delay); } if (switch_cs || switch_ck) @@ -1036,6 +1052,8 @@ static int cqspi_of_get_pdata(struct platform_device *pdev) return -ENXIO; } + cqspi->rclk_en = of_property_read_bool(np, "cdns,rclk-en"); + return 0; } @@ -1156,6 +1174,7 @@ static int cqspi_probe(struct platform_device *pdev) struct cqspi_st *cqspi; struct resource *res; struct resource *res_ahb; + unsigned long data; int ret; int irq; @@ -1206,13 +1225,24 @@ static int cqspi_probe(struct platform_device *pdev) return -ENXIO; } + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + ret = clk_prepare_enable(cqspi->clk); if (ret) { dev_err(dev, "Cannot enable QSPI clock.\n"); - return ret; + goto probe_clk_failed; } cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); + data = (unsigned long)of_device_get_match_data(dev); + if (data & CQSPI_NEEDS_WR_DELAY) + cqspi->wr_delay = 5 * DIV_ROUND_UP(NSEC_PER_SEC, + cqspi->master_ref_clk_hz); ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0, pdev->name, cqspi); @@ -1233,10 +1263,13 @@ static int cqspi_probe(struct platform_device *pdev) } return ret; -probe_irq_failed: - cqspi_controller_enable(cqspi, 0); probe_setup_failed: + cqspi_controller_enable(cqspi, 0); +probe_irq_failed: clk_disable_unprepare(cqspi->clk); +probe_clk_failed: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); return ret; } @@ -1253,6 +1286,9 @@ static int cqspi_remove(struct platform_device *pdev) clk_disable_unprepare(cqspi->clk); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; } @@ -1284,7 +1320,14 @@ static const struct dev_pm_ops cqspi__dev_pm_ops = { #endif static const struct of_device_id cqspi_dt_ids[] = { - {.compatible = "cdns,qspi-nor",}, + { + .compatible = "cdns,qspi-nor", + .data = (void *)0, + }, + { + .compatible = "ti,k2g-qspi", + .data = (void *)CQSPI_NEEDS_WR_DELAY, + }, { /* end of table */ } }; diff --git a/drivers/mtd/spi-nor/intel-spi-pci.c b/drivers/mtd/spi-nor/intel-spi-pci.c index e82652335ede..c0976f2e3dd1 100644 --- a/drivers/mtd/spi-nor/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/intel-spi-pci.c @@ -63,7 +63,10 @@ static void intel_spi_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id intel_spi_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, { }, }; MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c index 8a596bfeddff..ef034d898a23 100644 --- a/drivers/mtd/spi-nor/intel-spi.c +++ b/drivers/mtd/spi-nor/intel-spi.c @@ -67,8 +67,6 @@ #define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT) #define PR_RPE BIT(15) #define PR_BASE_MASK 0x3fff -/* Last PR is GPR0 */ -#define PR_NUM (5 + 1) /* Offsets are from @ispi->sregs */ #define SSFSTS_CTL 0x00 @@ -90,20 +88,35 @@ #define OPMENU0 0x08 #define OPMENU1 0x0c +#define OPTYPE_READ_NO_ADDR 0 +#define OPTYPE_WRITE_NO_ADDR 1 +#define OPTYPE_READ_WITH_ADDR 2 +#define OPTYPE_WRITE_WITH_ADDR 3 + /* CPU specifics */ #define BYT_PR 0x74 #define BYT_SSFSTS_CTL 0x90 #define BYT_BCR 0xfc #define BYT_BCR_WPD BIT(0) #define BYT_FREG_NUM 5 +#define BYT_PR_NUM 5 #define LPT_PR 0x74 #define LPT_SSFSTS_CTL 0x90 #define LPT_FREG_NUM 5 +#define LPT_PR_NUM 5 #define BXT_PR 0x84 #define BXT_SSFSTS_CTL 0xa0 #define BXT_FREG_NUM 12 +#define BXT_PR_NUM 6 + +#define LVSCC 0xc4 +#define UVSCC 0xc8 +#define ERASE_OPCODE_SHIFT 8 +#define ERASE_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT) +#define ERASE_64K_OPCODE_SHIFT 16 +#define ERASE_64K_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT) #define INTEL_SPI_TIMEOUT 5000 /* ms */ #define INTEL_SPI_FIFO_SZ 64 @@ -117,8 +130,11 @@ * @pregs: Start of protection registers * @sregs: Start of software sequencer registers * @nregions: Maximum number of regions + * @pr_num: Maximum number of protected range registers * @writeable: Is the chip writeable - * @swseq: Use SW sequencer in register reads/writes + * @locked: Is SPI setting locked + * @swseq_reg: Use SW sequencer in register reads/writes + * @swseq_erase: Use SW sequencer in erase operation * @erase_64k: 64k erase supported * @opcodes: Opcodes which are supported. This are programmed by BIOS * before it locks down the controller. @@ -132,8 +148,11 @@ struct intel_spi { void __iomem *pregs; void __iomem *sregs; size_t nregions; + size_t pr_num; bool writeable; - bool swseq; + bool locked; + bool swseq_reg; + bool swseq_erase; bool erase_64k; u8 opcodes[8]; u8 preopcodes[2]; @@ -167,7 +186,7 @@ static void intel_spi_dump_regs(struct intel_spi *ispi) for (i = 0; i < ispi->nregions; i++) dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i, readl(ispi->base + FREG(i))); - for (i = 0; i < PR_NUM; i++) + for (i = 0; i < ispi->pr_num; i++) dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i, readl(ispi->pregs + PR(i))); @@ -181,8 +200,11 @@ static void intel_spi_dump_regs(struct intel_spi *ispi) if (ispi->info->type == INTEL_SPI_BYT) dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR)); + dev_dbg(ispi->dev, "LVSCC=0x%08x\n", readl(ispi->base + LVSCC)); + dev_dbg(ispi->dev, "UVSCC=0x%08x\n", readl(ispi->base + UVSCC)); + dev_dbg(ispi->dev, "Protected regions:\n"); - for (i = 0; i < PR_NUM; i++) { + for (i = 0; i < ispi->pr_num; i++) { u32 base, limit; value = readl(ispi->pregs + PR(i)); @@ -214,7 +236,9 @@ static void intel_spi_dump_regs(struct intel_spi *ispi) } dev_dbg(ispi->dev, "Using %cW sequencer for register access\n", - ispi->swseq ? 'S' : 'H'); + ispi->swseq_reg ? 'S' : 'H'); + dev_dbg(ispi->dev, "Using %cW sequencer for erase operation\n", + ispi->swseq_erase ? 'S' : 'H'); } /* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */ @@ -278,7 +302,7 @@ static int intel_spi_wait_sw_busy(struct intel_spi *ispi) static int intel_spi_init(struct intel_spi *ispi) { - u32 opmenu0, opmenu1, val; + u32 opmenu0, opmenu1, lvscc, uvscc, val; int i; switch (ispi->info->type) { @@ -286,6 +310,8 @@ static int intel_spi_init(struct intel_spi *ispi) ispi->sregs = ispi->base + BYT_SSFSTS_CTL; ispi->pregs = ispi->base + BYT_PR; ispi->nregions = BYT_FREG_NUM; + ispi->pr_num = BYT_PR_NUM; + ispi->swseq_reg = true; if (writeable) { /* Disable write protection */ @@ -305,12 +331,15 @@ static int intel_spi_init(struct intel_spi *ispi) ispi->sregs = ispi->base + LPT_SSFSTS_CTL; ispi->pregs = ispi->base + LPT_PR; ispi->nregions = LPT_FREG_NUM; + ispi->pr_num = LPT_PR_NUM; + ispi->swseq_reg = true; break; case INTEL_SPI_BXT: ispi->sregs = ispi->base + BXT_SSFSTS_CTL; ispi->pregs = ispi->base + BXT_PR; ispi->nregions = BXT_FREG_NUM; + ispi->pr_num = BXT_PR_NUM; ispi->erase_64k = true; break; @@ -318,42 +347,64 @@ static int intel_spi_init(struct intel_spi *ispi) return -EINVAL; } - /* Disable #SMI generation */ + /* Disable #SMI generation from HW sequencer */ val = readl(ispi->base + HSFSTS_CTL); val &= ~HSFSTS_CTL_FSMIE; writel(val, ispi->base + HSFSTS_CTL); /* - * BIOS programs allowed opcodes and then locks down the register. - * So read back what opcodes it decided to support. That's the set - * we are going to support as well. + * Determine whether erase operation should use HW or SW sequencer. + * + * The HW sequencer has a predefined list of opcodes, with only the + * erase opcode being programmable in LVSCC and UVSCC registers. + * If these registers don't contain a valid erase opcode, erase + * cannot be done using HW sequencer. */ - opmenu0 = readl(ispi->sregs + OPMENU0); - opmenu1 = readl(ispi->sregs + OPMENU1); + lvscc = readl(ispi->base + LVSCC); + uvscc = readl(ispi->base + UVSCC); + if (!(lvscc & ERASE_OPCODE_MASK) || !(uvscc & ERASE_OPCODE_MASK)) + ispi->swseq_erase = true; + /* SPI controller on Intel BXT supports 64K erase opcode */ + if (ispi->info->type == INTEL_SPI_BXT && !ispi->swseq_erase) + if (!(lvscc & ERASE_64K_OPCODE_MASK) || + !(uvscc & ERASE_64K_OPCODE_MASK)) + ispi->erase_64k = false; /* * Some controllers can only do basic operations using hardware * sequencer. All other operations are supposed to be carried out - * using software sequencer. If we find that BIOS has programmed - * opcodes for the software sequencer we use that over the hardware - * sequencer. + * using software sequencer. */ - if (opmenu0 && opmenu1) { - for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) { - ispi->opcodes[i] = opmenu0 >> i * 8; - ispi->opcodes[i + 4] = opmenu1 >> i * 8; - } - - val = readl(ispi->sregs + PREOP_OPTYPE); - ispi->preopcodes[0] = val; - ispi->preopcodes[1] = val >> 8; - + if (ispi->swseq_reg) { /* Disable #SMI generation from SW sequencer */ val = readl(ispi->sregs + SSFSTS_CTL); val &= ~SSFSTS_CTL_FSMIE; writel(val, ispi->sregs + SSFSTS_CTL); + } + + /* Check controller's lock status */ + val = readl(ispi->base + HSFSTS_CTL); + ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN); + + if (ispi->locked) { + /* + * BIOS programs allowed opcodes and then locks down the + * register. So read back what opcodes it decided to support. + * That's the set we are going to support as well. + */ + opmenu0 = readl(ispi->sregs + OPMENU0); + opmenu1 = readl(ispi->sregs + OPMENU1); - ispi->swseq = true; + if (opmenu0 && opmenu1) { + for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) { + ispi->opcodes[i] = opmenu0 >> i * 8; + ispi->opcodes[i + 4] = opmenu1 >> i * 8; + } + + val = readl(ispi->sregs + PREOP_OPTYPE); + ispi->preopcodes[0] = val; + ispi->preopcodes[1] = val >> 8; + } } intel_spi_dump_regs(ispi); @@ -361,18 +412,28 @@ static int intel_spi_init(struct intel_spi *ispi) return 0; } -static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode) +static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype) { int i; + int preop; - for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) - if (ispi->opcodes[i] == opcode) - return i; - return -EINVAL; + if (ispi->locked) { + for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) + if (ispi->opcodes[i] == opcode) + return i; + + return -EINVAL; + } + + /* The lock is off, so just use index 0 */ + writel(opcode, ispi->sregs + OPMENU0); + preop = readw(ispi->sregs + PREOP_OPTYPE); + writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE); + + return 0; } -static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, - int len) +static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, int len) { u32 val, status; int ret; @@ -394,6 +455,9 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, return -EINVAL; } + if (len > INTEL_SPI_FIFO_SZ) + return -EINVAL; + val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT; val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; val |= HSFSTS_CTL_FGO; @@ -412,27 +476,39 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, return 0; } -static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, - int len) +static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len, + int optype) { - u32 val, status; + u32 val = 0, status; + u16 preop; int ret; - ret = intel_spi_opcode_index(ispi, opcode); + ret = intel_spi_opcode_index(ispi, opcode, optype); if (ret < 0) return ret; - val = (len << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS; + if (len > INTEL_SPI_FIFO_SZ) + return -EINVAL; + + /* Only mark 'Data Cycle' bit when there is data to be transferred */ + if (len > 0) + val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS; val |= ret << SSFSTS_CTL_COP_SHIFT; val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE; val |= SSFSTS_CTL_SCGO; + preop = readw(ispi->sregs + PREOP_OPTYPE); + if (preop) { + val |= SSFSTS_CTL_ACS; + if (preop >> 8) + val |= SSFSTS_CTL_SPOP; + } writel(val, ispi->sregs + SSFSTS_CTL); ret = intel_spi_wait_sw_busy(ispi); if (ret) return ret; - status = readl(ispi->base + SSFSTS_CTL); + status = readl(ispi->sregs + SSFSTS_CTL); if (status & SSFSTS_CTL_FCERR) return -EIO; else if (status & SSFSTS_CTL_AEL) @@ -449,10 +525,11 @@ static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) /* Address of the first chip */ writel(0, ispi->base + FADDR); - if (ispi->swseq) - ret = intel_spi_sw_cycle(ispi, opcode, buf, len); + if (ispi->swseq_reg) + ret = intel_spi_sw_cycle(ispi, opcode, len, + OPTYPE_READ_NO_ADDR); else - ret = intel_spi_hw_cycle(ispi, opcode, buf, len); + ret = intel_spi_hw_cycle(ispi, opcode, len); if (ret) return ret; @@ -467,10 +544,15 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) /* * This is handled with atomic operation and preop code in Intel - * controller so skip it here now. + * controller so skip it here now. If the controller is not locked, + * program the opcode to the PREOP register for later use. */ - if (opcode == SPINOR_OP_WREN) + if (opcode == SPINOR_OP_WREN) { + if (!ispi->locked) + writel(opcode, ispi->sregs + PREOP_OPTYPE); + return 0; + } writel(0, ispi->base + FADDR); @@ -479,9 +561,10 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) if (ret) return ret; - if (ispi->swseq) - return intel_spi_sw_cycle(ispi, opcode, buf, len); - return intel_spi_hw_cycle(ispi, opcode, buf, len); + if (ispi->swseq_reg) + return intel_spi_sw_cycle(ispi, opcode, len, + OPTYPE_WRITE_NO_ADDR); + return intel_spi_hw_cycle(ispi, opcode, len); } static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len, @@ -561,12 +644,6 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len, val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT; val |= HSFSTS_CTL_FCYCLE_WRITE; - /* Write enable */ - if (ispi->preopcodes[1] == SPINOR_OP_WREN) - val |= SSFSTS_CTL_SPOP; - val |= SSFSTS_CTL_ACS; - writel(val, ispi->base + HSFSTS_CTL); - ret = intel_spi_write_block(ispi, write_buf, block_size); if (ret) { dev_err(ispi->dev, "failed to write block\n"); @@ -574,8 +651,8 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len, } /* Start the write now */ - val = readl(ispi->base + HSFSTS_CTL); - writel(val | HSFSTS_CTL_FGO, ispi->base + HSFSTS_CTL); + val |= HSFSTS_CTL_FGO; + writel(val, ispi->base + HSFSTS_CTL); ret = intel_spi_wait_hw_busy(ispi); if (ret) { @@ -620,6 +697,22 @@ static int intel_spi_erase(struct spi_nor *nor, loff_t offs) erase_size = SZ_4K; } + if (ispi->swseq_erase) { + while (len > 0) { + writel(offs, ispi->base + FADDR); + + ret = intel_spi_sw_cycle(ispi, nor->erase_opcode, + 0, OPTYPE_WRITE_WITH_ADDR); + if (ret) + return ret; + + offs += erase_size; + len -= erase_size; + } + + return 0; + } + while (len > 0) { writel(offs, ispi->base + FADDR); @@ -652,7 +745,7 @@ static bool intel_spi_is_protected(const struct intel_spi *ispi, { int i; - for (i = 0; i < PR_NUM; i++) { + for (i = 0; i < ispi->pr_num; i++) { u32 pr_base, pr_limit, pr_value; pr_value = readl(ispi->pregs + PR(i)); diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c index c258c7adf1c5..abe455ccd68b 100644 --- a/drivers/mtd/spi-nor/mtk-quadspi.c +++ b/drivers/mtd/spi-nor/mtk-quadspi.c @@ -404,6 +404,29 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, return ret; } +static void mt8173_nor_disable_clk(struct mt8173_nor *mt8173_nor) +{ + clk_disable_unprepare(mt8173_nor->spi_clk); + clk_disable_unprepare(mt8173_nor->nor_clk); +} + +static int mt8173_nor_enable_clk(struct mt8173_nor *mt8173_nor) +{ + int ret; + + ret = clk_prepare_enable(mt8173_nor->spi_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(mt8173_nor->nor_clk); + if (ret) { + clk_disable_unprepare(mt8173_nor->spi_clk); + return ret; + } + + return 0; +} + static int mtk_nor_init(struct mt8173_nor *mt8173_nor, struct device_node *flash_node) { @@ -468,15 +491,11 @@ static int mtk_nor_drv_probe(struct platform_device *pdev) return PTR_ERR(mt8173_nor->nor_clk); mt8173_nor->dev = &pdev->dev; - ret = clk_prepare_enable(mt8173_nor->spi_clk); + + ret = mt8173_nor_enable_clk(mt8173_nor); if (ret) return ret; - ret = clk_prepare_enable(mt8173_nor->nor_clk); - if (ret) { - clk_disable_unprepare(mt8173_nor->spi_clk); - return ret; - } /* only support one attached flash */ flash_np = of_get_next_available_child(pdev->dev.of_node, NULL); if (!flash_np) { @@ -487,10 +506,9 @@ static int mtk_nor_drv_probe(struct platform_device *pdev) ret = mtk_nor_init(mt8173_nor, flash_np); nor_free: - if (ret) { - clk_disable_unprepare(mt8173_nor->spi_clk); - clk_disable_unprepare(mt8173_nor->nor_clk); - } + if (ret) + mt8173_nor_disable_clk(mt8173_nor); + return ret; } @@ -498,11 +516,38 @@ static int mtk_nor_drv_remove(struct platform_device *pdev) { struct mt8173_nor *mt8173_nor = platform_get_drvdata(pdev); - clk_disable_unprepare(mt8173_nor->spi_clk); - clk_disable_unprepare(mt8173_nor->nor_clk); + mt8173_nor_disable_clk(mt8173_nor); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_nor_suspend(struct device *dev) +{ + struct mt8173_nor *mt8173_nor = dev_get_drvdata(dev); + + mt8173_nor_disable_clk(mt8173_nor); + return 0; } +static int mtk_nor_resume(struct device *dev) +{ + struct mt8173_nor *mt8173_nor = dev_get_drvdata(dev); + + return mt8173_nor_enable_clk(mt8173_nor); +} + +static const struct dev_pm_ops mtk_nor_dev_pm_ops = { + .suspend = mtk_nor_suspend, + .resume = mtk_nor_resume, +}; + +#define MTK_NOR_DEV_PM_OPS (&mtk_nor_dev_pm_ops) +#else +#define MTK_NOR_DEV_PM_OPS NULL +#endif + static const struct of_device_id mtk_nor_of_ids[] = { { .compatible = "mediatek,mt8173-nor"}, { /* sentinel */ } @@ -514,6 +559,7 @@ static struct platform_driver mtk_nor_driver = { .remove = mtk_nor_drv_remove, .driver = { .name = "mtk-nor", + .pm = MTK_NOR_DEV_PM_OPS, .of_match_table = mtk_nor_of_ids, }, }; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 19c000722cbc..bc266f70a15b 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -89,6 +89,8 @@ struct flash_info { #define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ #define USE_CLSR BIT(14) /* use CLSR command */ + + int (*quad_enable)(struct spi_nor *nor); }; #define JEDEC_MFR(info) ((info)->id[0]) @@ -870,6 +872,8 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) return ret; } +static int macronix_quad_enable(struct spi_nor *nor); + /* Used when the "_ext_id" is two bytes at most */ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ .id = { \ @@ -964,6 +968,7 @@ static const struct flash_info spi_nor_ids[] = { { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) }, /* Everspin */ + { "mr25h128", CAT25_INFO( 16 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "mr25h40", CAT25_INFO(512 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, @@ -982,6 +987,11 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "gd25lq32", INFO(0xc86016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | @@ -997,6 +1007,12 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + .quad_enable = macronix_quad_enable, + }, /* Intel/Numonyx -- xxxs33b */ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, @@ -1024,7 +1040,7 @@ static const struct flash_info spi_nor_ids[] = { { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, @@ -1137,6 +1153,11 @@ static const struct flash_info spi_nor_ids[] = { { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { + "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) }, { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) }, @@ -2288,8 +2309,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, /* Check the SFDP header version. */ if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || - header.major != SFDP_JESD216_MAJOR || - header.minor < SFDP_JESD216_MINOR) + header.major != SFDP_JESD216_MAJOR) return -EINVAL; /* @@ -2427,6 +2447,15 @@ static int spi_nor_init_params(struct spi_nor *nor, params->quad_enable = spansion_quad_enable; break; } + + /* + * Some manufacturer like GigaDevice may use different + * bit to set QE on different memories, so the MFR can't + * indicate the quad_enable method for this case, we need + * set it in flash info list. + */ + if (info->quad_enable) + params->quad_enable = info->quad_enable; } /* Override the parameters with data read from SFDP tables. */ @@ -2630,17 +2659,60 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, /* Enable Quad I/O if needed. */ enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 || spi_nor_get_protocol_width(nor->write_proto) == 4); - if (enable_quad_io && params->quad_enable) { - err = params->quad_enable(nor); + if (enable_quad_io && params->quad_enable) + nor->quad_enable = params->quad_enable; + else + nor->quad_enable = NULL; + + return 0; +} + +static int spi_nor_init(struct spi_nor *nor) +{ + int err; + + /* + * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up + * with the software protection bits set + */ + if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL || + JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || + JEDEC_MFR(nor->info) == SNOR_MFR_SST || + nor->info->flags & SPI_NOR_HAS_LOCK) { + write_enable(nor); + write_sr(nor, 0); + spi_nor_wait_till_ready(nor); + } + + if (nor->quad_enable) { + err = nor->quad_enable(nor); if (err) { dev_err(nor->dev, "quad mode not supported\n"); return err; } } + if ((nor->addr_width == 4) && + (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) && + !(nor->info->flags & SPI_NOR_4B_OPCODES)) + set_4byte(nor, nor->info, 1); + return 0; } +/* mtd resume handler */ +static void spi_nor_resume(struct mtd_info *mtd) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + struct device *dev = nor->dev; + int ret; + + /* re-initialize the nor chip */ + ret = spi_nor_init(nor); + if (ret) + dev_err(dev, "resume() failed\n"); +} + int spi_nor_scan(struct spi_nor *nor, const char *name, const struct spi_nor_hwcaps *hwcaps) { @@ -2708,20 +2780,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (ret) return ret; - /* - * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up - * with the software protection bits set - */ - - if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || - JEDEC_MFR(info) == SNOR_MFR_INTEL || - JEDEC_MFR(info) == SNOR_MFR_SST || - info->flags & SPI_NOR_HAS_LOCK) { - write_enable(nor); - write_sr(nor, 0); - spi_nor_wait_till_ready(nor); - } - if (!mtd->name) mtd->name = dev_name(dev); mtd->priv = nor; @@ -2731,6 +2789,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->size = params.size; mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; + mtd->_resume = spi_nor_resume; /* NOR protection support for STmicro/Micron chips and similar */ if (JEDEC_MFR(info) == SNOR_MFR_MICRON || @@ -2804,8 +2863,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (JEDEC_MFR(info) == SNOR_MFR_SPANSION || info->flags & SPI_NOR_4B_OPCODES) spi_nor_set_4byte_opcodes(nor, info); - else - set_4byte(nor, info, 1); } else { nor->addr_width = 3; } @@ -2822,6 +2879,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, return ret; } + /* Send all the required SPI flash commands to initialize device */ + nor->info = info; + ret = spi_nor_init(nor); + if (ret) + return ret; + dev_info(dev, "%s (%lld Kbytes)\n", info->name, (long long)mtd->size >> 10); diff --git a/drivers/mtd/spi-nor/stm32-quadspi.c b/drivers/mtd/spi-nor/stm32-quadspi.c index 86c0931543c5..b3c7f6addba7 100644 --- a/drivers/mtd/spi-nor/stm32-quadspi.c +++ b/drivers/mtd/spi-nor/stm32-quadspi.c @@ -1,9 +1,22 @@ /* - * stm32_quadspi.c + * Driver for stm32 quadspi controller * - * Copyright (C) 2017, Ludovic Barre + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Ludovic Barre author . * - * License terms: GNU General Public License (GPL), version 2 + * License terms: GPL V2.0. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * This program. If not, see . */ #include #include @@ -113,6 +126,7 @@ #define STM32_MAX_MMAP_SZ SZ_256M #define STM32_MAX_NORCHIP 2 +#define STM32_QSPI_FIFO_SZ 32 #define STM32_QSPI_FIFO_TIMEOUT_US 30000 #define STM32_QSPI_BUSY_TIMEOUT_US 100000 @@ -124,6 +138,7 @@ struct stm32_qspi_flash { u32 presc; u32 read_mode; bool registered; + u32 prefetch_limit; }; struct stm32_qspi { @@ -240,12 +255,12 @@ static int stm32_qspi_tx_poll(struct stm32_qspi *qspi, STM32_QSPI_FIFO_TIMEOUT_US); if (ret) { dev_err(qspi->dev, "fifo timeout (stat:%#x)\n", sr); - break; + return ret; } tx_fifo(buf++, qspi->io_base + QUADSPI_DR); } - return ret; + return 0; } static int stm32_qspi_tx_mm(struct stm32_qspi *qspi, @@ -272,6 +287,7 @@ static int stm32_qspi_send(struct stm32_qspi_flash *flash, { struct stm32_qspi *qspi = flash->qspi; u32 ccr, dcr, cr; + u32 last_byte; int err; err = stm32_qspi_wait_nobusy(qspi); @@ -314,6 +330,10 @@ static int stm32_qspi_send(struct stm32_qspi_flash *flash, if (err) goto abort; writel_relaxed(FCR_CTCF, qspi->io_base + QUADSPI_FCR); + } else { + last_byte = cmd->addr + cmd->len; + if (last_byte > flash->prefetch_limit) + goto abort; } return err; @@ -322,7 +342,9 @@ abort: cr = readl_relaxed(qspi->io_base + QUADSPI_CR) | CR_ABORT; writel_relaxed(cr, qspi->io_base + QUADSPI_CR); - dev_err(qspi->dev, "%s abort err:%d\n", __func__, err); + if (err) + dev_err(qspi->dev, "%s abort err:%d\n", __func__, err); + return err; } @@ -550,6 +572,7 @@ static int stm32_qspi_flash_setup(struct stm32_qspi *qspi, } flash->fsize = FSIZE_VAL(mtd->size); + flash->prefetch_limit = mtd->size - STM32_QSPI_FIFO_SZ; flash->read_mode = CCR_FMODE_MM; if (mtd->size > qspi->mm_size) diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 93faa1fed6f2..ea01f24f15e7 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -95,7 +95,7 @@ static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable) reg = reg_readl(priv, REG_SPHY_CNTRL); if (enable) { reg |= PHY_RESET; - reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS | CK25_DIS); + reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS | IDDQ_GLOBAL_PWR | CK25_DIS); reg_writel(priv, reg, REG_SPHY_CNTRL); udelay(21); reg = reg_readl(priv, REG_SPHY_CNTRL); diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 7451922c209d..1c1ddd891ca3 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -2579,6 +2579,7 @@ static int ena_restore_device(struct ena_adapter *adapter) bool wd_state; int rc; + set_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags); rc = ena_device_init(ena_dev, adapter->pdev, &get_feat_ctx, &wd_state); if (rc) { dev_err(&pdev->dev, "Can not initialize device\n"); @@ -2592,6 +2593,11 @@ static int ena_restore_device(struct ena_adapter *adapter) goto err_device_destroy; } + clear_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags); + /* Make sure we don't have a race with AENQ Links state handler */ + if (test_bit(ENA_FLAG_LINK_UP, &adapter->flags)) + netif_carrier_on(adapter->netdev); + rc = ena_enable_msix_and_set_admin_interrupts(adapter, adapter->num_queues); if (rc) { @@ -2618,7 +2624,7 @@ err_device_destroy: ena_com_admin_destroy(ena_dev); err: clear_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags); - + clear_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags); dev_err(&pdev->dev, "Reset attempt failed. Can not reset the device\n"); @@ -3495,7 +3501,8 @@ static void ena_update_on_link_change(void *adapter_data, if (status) { netdev_dbg(adapter->netdev, "%s\n", __func__); set_bit(ENA_FLAG_LINK_UP, &adapter->flags); - netif_carrier_on(adapter->netdev); + if (!test_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags)) + netif_carrier_on(adapter->netdev); } else { clear_bit(ENA_FLAG_LINK_UP, &adapter->flags); netif_carrier_off(adapter->netdev); diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.h b/drivers/net/ethernet/amazon/ena/ena_netdev.h index ed8bd0a579c4..3bbc003871de 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.h +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.h @@ -272,7 +272,8 @@ enum ena_flags_t { ENA_FLAG_DEV_UP, ENA_FLAG_LINK_UP, ENA_FLAG_MSIX_ENABLED, - ENA_FLAG_TRIGGER_RESET + ENA_FLAG_TRIGGER_RESET, + ENA_FLAG_ONGOING_RESET }; /* adapter specific private data structure */ diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c index 23053919ebf5..ae55da60ed0e 100644 --- a/drivers/net/ethernet/fealnx.c +++ b/drivers/net/ethernet/fealnx.c @@ -257,8 +257,8 @@ enum rx_desc_status_bits { RXFSD = 0x00000800, /* first descriptor */ RXLSD = 0x00000400, /* last descriptor */ ErrorSummary = 0x80, /* error summary */ - RUNT = 0x40, /* runt packet received */ - LONG = 0x20, /* long packet received */ + RUNTPKT = 0x40, /* runt packet received */ + LONGPKT = 0x20, /* long packet received */ FAE = 0x10, /* frame align error */ CRC = 0x08, /* crc error */ RXER = 0x04, /* receive error */ @@ -1628,7 +1628,7 @@ static int netdev_rx(struct net_device *dev) dev->name, rx_status); dev->stats.rx_errors++; /* end of a packet. */ - if (rx_status & (LONG | RUNT)) + if (rx_status & (LONGPKT | RUNTPKT)) dev->stats.rx_length_errors++; if (rx_status & RXER) dev->stats.rx_frame_errors++; diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 04aaacbc3d45..1dc4aef37d3a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -849,7 +849,6 @@ static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; union ibmvnic_crq crq; - dma_addr_t dma_addr; int len = 0; if (adapter->vpd->buff) @@ -879,7 +878,7 @@ static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter) adapter->vpd->dma_addr = dma_map_single(dev, adapter->vpd->buff, adapter->vpd->len, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dma_addr)) { + if (dma_mapping_error(dev, adapter->vpd->dma_addr)) { dev_err(dev, "Could not map VPD buffer\n"); kfree(adapter->vpd->buff); return -ENOMEM; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index dbd69310f263..538b42d5c187 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1231,7 +1231,7 @@ static bool fm10k_clean_tx_irq(struct fm10k_q_vector *q_vector, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->flags & FM10K_TXD_FLAG_DONE)) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 5829715fa342..e019baa905c5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -90,7 +90,6 @@ #define I40E_AQ_LEN 256 #define I40E_AQ_WORK_LIMIT 66 /* max number of VFs + a little */ #define I40E_MAX_USER_PRIORITY 8 -#define I40E_MAX_QUEUES_PER_CH 64 #define I40E_DEFAULT_TRAFFIC_CLASS BIT(0) #define I40E_DEFAULT_MSG_ENABLE 4 #define I40E_QUEUE_WAIT_RETRY_LIMIT 10 diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 9dcb2a961197..9af74253c3f7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -613,6 +613,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) hw->flags |= I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE; } + /* Newer versions of firmware require lock when reading the NVM */ + if (hw->aq.api_maj_ver > 1 || + (hw->aq.api_maj_ver == 1 && + hw->aq.api_min_ver >= 5)) + hw->flags |= I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK; + /* The ability to RX (not drop) 802.1ad frames was added in API 1.7 */ if (hw->aq.api_maj_ver > 1 || (hw->aq.api_maj_ver == 1 && diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 0203665cb53c..095965f268bd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -948,7 +948,8 @@ i40e_status i40e_init_shared_code(struct i40e_hw *hw) hw->pf_id = (u8)(func_rid & 0x7); if (hw->mac.type == I40E_MAC_X722) - hw->flags |= I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE; + hw->flags |= I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE | + I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK; status = i40e_init_nvm(hw); return status; @@ -1268,6 +1269,7 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) * we don't need to do the PF Reset */ if (!cnt) { + u32 reg2 = 0; if (hw->revision_id == 0) cnt = I40E_PF_RESET_WAIT_COUNT_A0; else @@ -1279,6 +1281,12 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) reg = rd32(hw, I40E_PFGEN_CTRL); if (!(reg & I40E_PFGEN_CTRL_PFSWR_MASK)) break; + reg2 = rd32(hw, I40E_GLGEN_RSTAT); + if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK) { + hw_dbg(hw, "Core reset upcoming. Skipping PF reset request.\n"); + hw_dbg(hw, "I40E_GLGEN_RSTAT = 0x%x\n", reg2); + return I40E_ERR_NOT_READY; + } usleep_range(1000, 2000); } if (reg & I40E_PFGEN_CTRL_PFSWR_MASK) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4a964d6e4a9e..4c08cc86463e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2166,6 +2166,73 @@ i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name, return aq_ret; } +/** + * i40e_set_promiscuous - set promiscuous mode + * @pf: board private structure + * @promisc: promisc on or off + * + * There are different ways of setting promiscuous mode on a PF depending on + * what state/environment we're in. This identifies and sets it appropriately. + * Returns 0 on success. + **/ +static int i40e_set_promiscuous(struct i40e_pf *pf, bool promisc) +{ + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + struct i40e_hw *hw = &pf->hw; + i40e_status aq_ret; + + if (vsi->type == I40E_VSI_MAIN && + pf->lan_veb != I40E_NO_VEB && + !(pf->flags & I40E_FLAG_MFP_ENABLED)) { + /* set defport ON for Main VSI instead of true promisc + * this way we will get all unicast/multicast and VLAN + * promisc behavior but will not get VF or VMDq traffic + * replicated on the Main VSI. + */ + if (promisc) + aq_ret = i40e_aq_set_default_vsi(hw, + vsi->seid, + NULL); + else + aq_ret = i40e_aq_clear_default_vsi(hw, + vsi->seid, + NULL); + if (aq_ret) { + dev_info(&pf->pdev->dev, + "Set default VSI failed, err %s, aq_err %s\n", + i40e_stat_str(hw, aq_ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); + } + } else { + aq_ret = i40e_aq_set_vsi_unicast_promiscuous( + hw, + vsi->seid, + promisc, NULL, + true); + if (aq_ret) { + dev_info(&pf->pdev->dev, + "set unicast promisc failed, err %s, aq_err %s\n", + i40e_stat_str(hw, aq_ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); + } + aq_ret = i40e_aq_set_vsi_multicast_promiscuous( + hw, + vsi->seid, + promisc, NULL); + if (aq_ret) { + dev_info(&pf->pdev->dev, + "set multicast promisc failed, err %s, aq_err %s\n", + i40e_stat_str(hw, aq_ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); + } + } + + if (!aq_ret) + pf->cur_promisc = promisc; + + return aq_ret; +} + /** * i40e_sync_vsi_filters - Update the VSI filter list to the HW * @vsi: ptr to the VSI @@ -2467,81 +2534,16 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) || test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state)); - if ((vsi->type == I40E_VSI_MAIN) && - (pf->lan_veb != I40E_NO_VEB) && - !(pf->flags & I40E_FLAG_MFP_ENABLED)) { - /* set defport ON for Main VSI instead of true promisc - * this way we will get all unicast/multicast and VLAN - * promisc behavior but will not get VF or VMDq traffic - * replicated on the Main VSI. - */ - if (pf->cur_promisc != cur_promisc) { - pf->cur_promisc = cur_promisc; - if (cur_promisc) - aq_ret = - i40e_aq_set_default_vsi(hw, - vsi->seid, - NULL); - else - aq_ret = - i40e_aq_clear_default_vsi(hw, - vsi->seid, - NULL); - if (aq_ret) { - retval = i40e_aq_rc_to_posix(aq_ret, - hw->aq.asq_last_status); - dev_info(&pf->pdev->dev, - "Set default VSI failed on %s, err %s, aq_err %s\n", - vsi_name, - i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, - hw->aq.asq_last_status)); - } - } - } else { - aq_ret = i40e_aq_set_vsi_unicast_promiscuous( - hw, - vsi->seid, - cur_promisc, NULL, - true); - if (aq_ret) { - retval = - i40e_aq_rc_to_posix(aq_ret, - hw->aq.asq_last_status); - dev_info(&pf->pdev->dev, - "set unicast promisc failed on %s, err %s, aq_err %s\n", - vsi_name, - i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, - hw->aq.asq_last_status)); - } - aq_ret = i40e_aq_set_vsi_multicast_promiscuous( - hw, - vsi->seid, - cur_promisc, NULL); - if (aq_ret) { - retval = - i40e_aq_rc_to_posix(aq_ret, - hw->aq.asq_last_status); - dev_info(&pf->pdev->dev, - "set multicast promisc failed on %s, err %s, aq_err %s\n", - vsi_name, - i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, - hw->aq.asq_last_status)); - } - } - aq_ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw, - vsi->seid, - cur_promisc, NULL); + aq_ret = i40e_set_promiscuous(pf, cur_promisc); if (aq_ret) { retval = i40e_aq_rc_to_posix(aq_ret, - pf->hw.aq.asq_last_status); + hw->aq.asq_last_status); dev_info(&pf->pdev->dev, - "set brdcast promisc failed, err %s, aq_err %s\n", - i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, - hw->aq.asq_last_status)); + "Setting promiscuous %s failed on %s, err %s aq_err %s\n", + cur_promisc ? "on" : "off", + vsi_name, + i40e_stat_str(hw, aq_ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); } } out: @@ -3964,7 +3966,7 @@ static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if the descriptor isn't done, no work yet to do */ if (!(eop_desc->cmd_type_offset_bsz & @@ -5629,14 +5631,6 @@ static int i40e_validate_num_queues(struct i40e_pf *pf, int num_queues, return -EINVAL; *reconfig_rss = false; - - if (num_queues > I40E_MAX_QUEUES_PER_CH) { - dev_err(&pf->pdev->dev, - "Failed to create VMDq VSI. User requested num_queues (%d) > I40E_MAX_QUEUES_PER_VSI (%u)\n", - num_queues, I40E_MAX_QUEUES_PER_CH); - return -EINVAL; - } - if (vsi->current_rss_size) { if (num_queues > vsi->current_rss_size) { dev_dbg(&pf->pdev->dev, @@ -9429,6 +9423,15 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) if (!lock_acquired) rtnl_unlock(); + /* Restore promiscuous settings */ + ret = i40e_set_promiscuous(pf, pf->cur_promisc); + if (ret) + dev_warn(&pf->pdev->dev, + "Failed to restore promiscuous setting: %s, err %s aq_err %s\n", + pf->cur_promisc ? "on" : "off", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + i40e_reset_all_vfs(pf, true); /* tell the firmware that we're starting */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 0ccab0a5d717..7689c2ee0d46 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -328,15 +328,17 @@ static i40e_status __i40e_read_nvm_word(struct i40e_hw *hw, i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, u16 *data) { - i40e_status ret_code; + i40e_status ret_code = 0; - ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (hw->flags & I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK) + ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (ret_code) return ret_code; ret_code = __i40e_read_nvm_word(hw, offset, data); - i40e_release_nvm(hw); + if (hw->flags & I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK) + i40e_release_nvm(hw); return ret_code; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index d6d352a6e6ea..4566d66ffc7c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -759,7 +759,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); i40e_trace(clean_tx_irq, tx_ring, tx_desc, tx_buf); /* we have caught up to head, no work left to do */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 00d4833e9925..0e8568719b4e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -629,6 +629,7 @@ struct i40e_hw { #define I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE BIT_ULL(0) #define I40E_HW_FLAG_802_1AD_CAPABLE BIT_ULL(1) #define I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE BIT_ULL(2) +#define I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK BIT_ULL(3) u64 flags; /* Used in set switch config AQ command */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index f8a794b72462..a3dc9b932946 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2218,18 +2218,19 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) struct i40e_mac_filter *f; f = i40e_find_mac(vsi, al->list[i].addr); - if (!f) + if (!f) { f = i40e_add_mac_filter(vsi, al->list[i].addr); - if (!f) { - dev_err(&pf->pdev->dev, - "Unable to add MAC filter %pM for VF %d\n", - al->list[i].addr, vf->vf_id); - ret = I40E_ERR_PARAM; - spin_unlock_bh(&vsi->mac_filter_hash_lock); - goto error_param; - } else { - vf->num_mac++; + if (!f) { + dev_err(&pf->pdev->dev, + "Unable to add MAC filter %pM for VF %d\n", + al->list[i].addr, vf->vf_id); + ret = I40E_ERR_PARAM; + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_param; + } else { + vf->num_mac++; + } } } spin_unlock_bh(&vsi->mac_filter_hash_lock); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index fe817e2b6fef..50864f99446d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -179,7 +179,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); i40e_trace(clean_tx_irq, tx_ring, tx_desc, tx_buf); /* if the descriptor isn't done, no work yet to do */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_client.c b/drivers/net/ethernet/intel/i40evf/i40evf_client.c index d8131139565e..da60ce12b33d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_client.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_client.c @@ -25,6 +25,26 @@ static struct i40e_ops i40evf_lan_ops = { .setup_qvlist = i40evf_client_setup_qvlist, }; +/** + * i40evf_client_get_params - retrieve relevant client parameters + * @vsi: VSI with parameters + * @params: client param struct + **/ +static +void i40evf_client_get_params(struct i40e_vsi *vsi, struct i40e_params *params) +{ + int i; + + memset(params, 0, sizeof(struct i40e_params)); + params->mtu = vsi->netdev->mtu; + params->link_up = vsi->back->link_up; + + for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { + params->qos.prio_qos[i].tc = 0; + params->qos.prio_qos[i].qs_handle = vsi->qs_handle; + } +} + /** * i40evf_notify_client_message - call the client message receive callback * @vsi: the VSI associated with this client @@ -66,10 +86,6 @@ void i40evf_notify_client_l2_params(struct i40e_vsi *vsi) return; cinst = vsi->back->cinst; - memset(¶ms, 0, sizeof(params)); - params.mtu = vsi->netdev->mtu; - params.link_up = vsi->back->link_up; - params.qos.prio_qos[0].qs_handle = vsi->qs_handle; if (!cinst || !cinst->client || !cinst->client->ops || !cinst->client->ops->l2_param_change) { @@ -77,6 +93,8 @@ void i40evf_notify_client_l2_params(struct i40e_vsi *vsi) "Cannot locate client instance l2_param_change function\n"); return; } + i40evf_client_get_params(vsi, ¶ms); + cinst->lan_info.params = params; cinst->client->ops->l2_param_change(&cinst->lan_info, cinst->client, ¶ms); } @@ -166,9 +184,9 @@ static struct i40e_client_instance * i40evf_client_add_instance(struct i40evf_adapter *adapter) { struct i40e_client_instance *cinst = NULL; - struct netdev_hw_addr *mac = NULL; struct i40e_vsi *vsi = &adapter->vsi; - int i; + struct netdev_hw_addr *mac = NULL; + struct i40e_params params; if (!vf_registered_client) goto out; @@ -192,18 +210,14 @@ i40evf_client_add_instance(struct i40evf_adapter *adapter) cinst->lan_info.version.major = I40EVF_CLIENT_VERSION_MAJOR; cinst->lan_info.version.minor = I40EVF_CLIENT_VERSION_MINOR; cinst->lan_info.version.build = I40EVF_CLIENT_VERSION_BUILD; + i40evf_client_get_params(vsi, ¶ms); + cinst->lan_info.params = params; set_bit(__I40E_CLIENT_INSTANCE_NONE, &cinst->state); cinst->lan_info.msix_count = adapter->num_iwarp_msix; cinst->lan_info.msix_entries = &adapter->msix_entries[adapter->iwarp_base_vector]; - for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { - cinst->lan_info.params.qos.prio_qos[i].tc = 0; - cinst->lan_info.params.qos.prio_qos[i].qs_handle = - vsi->qs_handle; - } - mac = list_first_entry(&cinst->lan_info.netdev->dev_addrs.list, struct netdev_hw_addr, list); if (mac) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index ca2ebdbd24d7..7b2a4eba92e2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2110,6 +2110,11 @@ static void i40evf_client_task(struct work_struct *work) adapter->flags &= ~I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; goto out; } + if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS) { + i40evf_notify_client_l2_params(&adapter->vsi); + adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS; + goto out; + } if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_CLOSE) { i40evf_notify_client_close(&adapter->vsi, false); adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_CLOSE; @@ -2118,11 +2123,6 @@ static void i40evf_client_task(struct work_struct *work) if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_OPEN) { i40evf_notify_client_open(&adapter->vsi); adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_OPEN; - goto out; - } - if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS) { - i40evf_notify_client_l2_params(&adapter->vsi); - adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS; } out: clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index e94d3c256667..c208753ff5b7 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -7317,7 +7317,7 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector, int napi_budget) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(E1000_TXD_STAT_DD))) diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 713e8df23744..4214c1519a87 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -810,7 +810,7 @@ static bool igbvf_clean_tx_irq(struct igbvf_ring *tx_ring) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(E1000_TXD_STAT_DD))) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index ca06c3cc2ca8..62a18914f00f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1192,7 +1192,7 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD))) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index feed11bc9ddf..1f4a69134ade 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -326,7 +326,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD))) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 2d46ec84ebdf..2d0897b7d860 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3142,13 +3142,17 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp->ports) return -ENOMEM; - mlxsw_sp->port_to_module = kcalloc(max_ports, sizeof(u8), GFP_KERNEL); + mlxsw_sp->port_to_module = kmalloc_array(max_ports, sizeof(int), + GFP_KERNEL); if (!mlxsw_sp->port_to_module) { err = -ENOMEM; goto err_port_to_module_alloc; } for (i = 1; i < max_ports; i++) { + /* Mark as invalid */ + mlxsw_sp->port_to_module[i] = -1; + err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, &module, &width, &lane); if (err) @@ -3216,6 +3220,8 @@ static void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < count; i++) { local_port = base_port + i * 2; + if (mlxsw_sp->port_to_module[local_port] < 0) + continue; module = mlxsw_sp->port_to_module[local_port]; mlxsw_sp_port_create(mlxsw_sp, local_port, false, module, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 58cf222fb985..432ab9b12b7f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -152,7 +152,7 @@ struct mlxsw_sp { const struct mlxsw_bus_info *bus_info; unsigned char base_mac[ETH_ALEN]; struct mlxsw_sp_upper *lags; - u8 *port_to_module; + int *port_to_module; struct mlxsw_sp_sb *sb; struct mlxsw_sp_bridge *bridge; struct mlxsw_sp_router *router; diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index fe7e0e1dd01d..b2299f2b2155 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -1530,7 +1530,7 @@ static int vxge_reset_vpath(struct vxgedev *vdev, int vp_id) vxge_debug_init(VXGE_ERR, "vxge_hw_vpath_reset failed for" "vpath:%d", vp_id); - return status; + return status; } } else return VXGE_HW_FAIL; @@ -1950,19 +1950,19 @@ static enum vxge_hw_status vxge_rth_configure(struct vxgedev *vdev) * for all VPATHs. The h/w only uses the lowest numbered VPATH * when steering frames. */ - for (index = 0; index < vdev->no_of_vpath; index++) { + for (index = 0; index < vdev->no_of_vpath; index++) { status = vxge_hw_vpath_rts_rth_set( vdev->vpaths[index].handle, vdev->config.rth_algorithm, &hash_types, vdev->config.rth_bkt_sz); - if (status != VXGE_HW_OK) { + if (status != VXGE_HW_OK) { vxge_debug_init(VXGE_ERR, "RTH configuration failed for vpath:%d", vdev->vpaths[index].device_id); return status; - } - } + } + } return status; } @@ -1991,7 +1991,7 @@ static enum vxge_hw_status vxge_reset_all_vpaths(struct vxgedev *vdev) vxge_debug_init(VXGE_ERR, "vxge_hw_vpath_reset failed for " "vpath:%d", i); - return status; + return status; } } } @@ -2474,32 +2474,31 @@ static int vxge_add_isr(struct vxgedev *vdev) switch (msix_idx) { case 0: snprintf(vdev->desc[intr_cnt], VXGE_INTR_STRLEN, - "%s:vxge:MSI-X %d - Tx - fn:%d vpath:%d", + "%s:vxge:MSI-X %d - Tx - fn:%d vpath:%d", vdev->ndev->name, vdev->entries[intr_cnt].entry, pci_fun, vp_idx); ret = request_irq( - vdev->entries[intr_cnt].vector, + vdev->entries[intr_cnt].vector, vxge_tx_msix_handle, 0, vdev->desc[intr_cnt], &vdev->vpaths[vp_idx].fifo); - vdev->vxge_entries[intr_cnt].arg = + vdev->vxge_entries[intr_cnt].arg = &vdev->vpaths[vp_idx].fifo; irq_req = 1; break; case 1: snprintf(vdev->desc[intr_cnt], VXGE_INTR_STRLEN, - "%s:vxge:MSI-X %d - Rx - fn:%d vpath:%d", + "%s:vxge:MSI-X %d - Rx - fn:%d vpath:%d", vdev->ndev->name, vdev->entries[intr_cnt].entry, pci_fun, vp_idx); ret = request_irq( - vdev->entries[intr_cnt].vector, - vxge_rx_msix_napi_handle, - 0, + vdev->entries[intr_cnt].vector, + vxge_rx_msix_napi_handle, 0, vdev->desc[intr_cnt], &vdev->vpaths[vp_idx].ring); - vdev->vxge_entries[intr_cnt].arg = + vdev->vxge_entries[intr_cnt].arg = &vdev->vpaths[vp_idx].ring; irq_req = 1; break; @@ -2512,9 +2511,9 @@ static int vxge_add_isr(struct vxgedev *vdev) vxge_rem_msix_isr(vdev); vdev->config.intr_type = INTA; vxge_debug_init(VXGE_ERR, - "%s: Defaulting to INTA" - , vdev->ndev->name); - goto INTA_MODE; + "%s: Defaulting to INTA", + vdev->ndev->name); + goto INTA_MODE; } if (irq_req) { @@ -4505,8 +4504,8 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre) if (status != VXGE_HW_OK) { vxge_debug_init(VXGE_ERR, "Failed to initialize device (%d)", status); - ret = -EINVAL; - goto _exit3; + ret = -EINVAL; + goto _exit3; } if (VXGE_FW_VER(ll_config->device_hw_info.fw_version.major, diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index b6cee71f49d3..bc879aeb62d4 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -214,8 +214,14 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog, { int err; - if (prog && !prog->aux->offload) - return -EINVAL; + if (prog) { + struct bpf_dev_offload *offload = prog->aux->offload; + + if (!offload) + return -EINVAL; + if (offload->netdev != nn->dp.netdev) + return -EINVAL; + } if (prog && old_prog) { u8 cap; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index e0283bb24f06..8fcc90c0d2d3 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -125,6 +125,21 @@ nfp_flower_repr_netdev_stop(struct nfp_app *app, struct nfp_repr *repr) return nfp_flower_cmsg_portmod(repr, false); } +static int +nfp_flower_repr_netdev_init(struct nfp_app *app, struct net_device *netdev) +{ + return tc_setup_cb_egdev_register(netdev, + nfp_flower_setup_tc_egress_cb, + netdev_priv(netdev)); +} + +static void +nfp_flower_repr_netdev_clean(struct nfp_app *app, struct net_device *netdev) +{ + tc_setup_cb_egdev_unregister(netdev, nfp_flower_setup_tc_egress_cb, + netdev_priv(netdev)); +} + static void nfp_flower_sriov_disable(struct nfp_app *app) { struct nfp_flower_priv *priv = app->priv; @@ -452,6 +467,9 @@ const struct nfp_app_type app_flower = { .vnic_init = nfp_flower_vnic_init, .vnic_clean = nfp_flower_vnic_clean, + .repr_init = nfp_flower_repr_netdev_init, + .repr_clean = nfp_flower_repr_netdev_clean, + .repr_open = nfp_flower_repr_netdev_open, .repr_stop = nfp_flower_repr_netdev_stop, diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index c90e72b7ff5a..e6b26c5ae6e0 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -52,8 +52,7 @@ struct nfp_app; #define NFP_FLOWER_MASK_ELEMENT_RS 1 #define NFP_FLOWER_MASK_HASH_BITS 10 -#define NFP_FL_META_FLAG_NEW_MASK 128 -#define NFP_FL_META_FLAG_LAST_MASK 1 +#define NFP_FL_META_FLAG_MANAGE_MASK BIT(7) #define NFP_FL_MASK_REUSE_TIME_NS 40000 #define NFP_FL_MASK_ID_LOCATION 1 @@ -197,5 +196,7 @@ void nfp_tunnel_del_ipv4_off(struct nfp_app *app, __be32 ipv4); void nfp_tunnel_add_ipv4_off(struct nfp_app *app, __be32 ipv4); void nfp_tunnel_request_route(struct nfp_app *app, struct sk_buff *skb); void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb); +int nfp_flower_setup_tc_egress_cb(enum tc_setup_type type, void *type_data, + void *cb_priv); #endif diff --git a/drivers/net/ethernet/netronome/nfp/flower/metadata.c b/drivers/net/ethernet/netronome/nfp/flower/metadata.c index 193520ef23f0..db977cf8e933 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/metadata.c +++ b/drivers/net/ethernet/netronome/nfp/flower/metadata.c @@ -282,7 +282,7 @@ nfp_check_mask_add(struct nfp_app *app, char *mask_data, u32 mask_len, id = nfp_add_mask_table(app, mask_data, mask_len); if (id < 0) return false; - *meta_flags |= NFP_FL_META_FLAG_NEW_MASK; + *meta_flags |= NFP_FL_META_FLAG_MANAGE_MASK; } *mask_id = id; @@ -299,6 +299,9 @@ nfp_check_mask_remove(struct nfp_app *app, char *mask_data, u32 mask_len, if (!mask_entry) return false; + if (meta_flags) + *meta_flags &= ~NFP_FL_META_FLAG_MANAGE_MASK; + *mask_id = mask_entry->mask_id; mask_entry->ref_cnt--; if (!mask_entry->ref_cnt) { @@ -306,7 +309,7 @@ nfp_check_mask_remove(struct nfp_app *app, char *mask_data, u32 mask_len, nfp_release_mask_id(app, *mask_id); kfree(mask_entry); if (meta_flags) - *meta_flags |= NFP_FL_META_FLAG_LAST_MASK; + *meta_flags |= NFP_FL_META_FLAG_MANAGE_MASK; } return true; diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index cdbb5464b790..553f94f55dce 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -131,7 +131,8 @@ static bool nfp_flower_check_higher_than_mac(struct tc_cls_flower_offload *f) static int nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls, - struct tc_cls_flower_offload *flow) + struct tc_cls_flower_offload *flow, + bool egress) { struct flow_dissector_key_basic *mask_basic = NULL; struct flow_dissector_key_basic *key_basic = NULL; @@ -167,6 +168,9 @@ nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls, skb_flow_dissector_target(flow->dissector, FLOW_DISSECTOR_KEY_ENC_CONTROL, flow->key); + if (!egress) + return -EOPNOTSUPP; + if (mask_enc_ctl->addr_type != 0xffff || enc_ctl->addr_type != FLOW_DISSECTOR_KEY_IPV4_ADDRS) return -EOPNOTSUPP; @@ -194,6 +198,9 @@ nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls, key_layer |= NFP_FLOWER_LAYER_VXLAN; key_size += sizeof(struct nfp_flower_vxlan); + } else if (egress) { + /* Reject non tunnel matches offloaded to egress repr. */ + return -EOPNOTSUPP; } if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) { @@ -308,6 +315,7 @@ err_free_flow: * @app: Pointer to the APP handle * @netdev: netdev structure. * @flow: TC flower classifier offload structure. + * @egress: NFP netdev is the egress. * * Adds a new flow to the repeated hash structure and action payload. * @@ -315,7 +323,7 @@ err_free_flow: */ static int nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev, - struct tc_cls_flower_offload *flow) + struct tc_cls_flower_offload *flow, bool egress) { struct nfp_flower_priv *priv = app->priv; struct nfp_fl_payload *flow_pay; @@ -326,7 +334,7 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev, if (!key_layer) return -ENOMEM; - err = nfp_flower_calculate_key_layers(key_layer, flow); + err = nfp_flower_calculate_key_layers(key_layer, flow, egress); if (err) goto err_free_key_ls; @@ -447,7 +455,7 @@ nfp_flower_get_stats(struct nfp_app *app, struct tc_cls_flower_offload *flow) static int nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, - struct tc_cls_flower_offload *flower) + struct tc_cls_flower_offload *flower, bool egress) { if (!eth_proto_is_802_3(flower->common.protocol) || flower->common.chain_index) @@ -455,7 +463,7 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, switch (flower->command) { case TC_CLSFLOWER_REPLACE: - return nfp_flower_add_offload(app, netdev, flower); + return nfp_flower_add_offload(app, netdev, flower, egress); case TC_CLSFLOWER_DESTROY: return nfp_flower_del_offload(app, netdev, flower); case TC_CLSFLOWER_STATS: @@ -465,6 +473,23 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, return -EOPNOTSUPP; } +int nfp_flower_setup_tc_egress_cb(enum tc_setup_type type, void *type_data, + void *cb_priv) +{ + struct nfp_repr *repr = cb_priv; + + if (!tc_can_offload(repr->netdev)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return nfp_flower_repr_offload(repr->app, repr->netdev, + type_data, true); + default: + return -EOPNOTSUPP; + } +} + static int nfp_flower_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { @@ -476,7 +501,7 @@ static int nfp_flower_setup_tc_block_cb(enum tc_setup_type type, switch (type) { case TC_SETUP_CLSFLOWER: return nfp_flower_repr_offload(repr->app, repr->netdev, - type_data); + type_data, false); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 54b67c9b8d5b..0e5e0305ad1c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -76,6 +76,8 @@ extern const struct nfp_app_type app_flower; * @vnic_free: free up app's vNIC state * @vnic_init: vNIC netdev was registered * @vnic_clean: vNIC netdev about to be unregistered + * @repr_init: representor about to be registered + * @repr_clean: representor about to be unregistered * @repr_open: representor netdev open callback * @repr_stop: representor netdev stop callback * @start: start application logic @@ -109,6 +111,9 @@ struct nfp_app_type { int (*vnic_init)(struct nfp_app *app, struct nfp_net *nn); void (*vnic_clean)(struct nfp_app *app, struct nfp_net *nn); + int (*repr_init)(struct nfp_app *app, struct net_device *netdev); + void (*repr_clean)(struct nfp_app *app, struct net_device *netdev); + int (*repr_open)(struct nfp_app *app, struct nfp_repr *repr); int (*repr_stop)(struct nfp_app *app, struct nfp_repr *repr); @@ -212,6 +217,21 @@ static inline int nfp_app_repr_stop(struct nfp_app *app, struct nfp_repr *repr) return app->type->repr_stop(app, repr); } +static inline int +nfp_app_repr_init(struct nfp_app *app, struct net_device *netdev) +{ + if (!app->type->repr_init) + return 0; + return app->type->repr_init(app, netdev); +} + +static inline void +nfp_app_repr_clean(struct nfp_app *app, struct net_device *netdev) +{ + if (app->type->repr_clean) + app->type->repr_clean(app, netdev); +} + static inline int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl) { app->ctrl = ctrl; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 60c8d733a37d..2801ecd09eab 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -104,7 +104,7 @@ static const struct nfp_et_stat nfp_mac_et_stats[] = { { "rx_frame_too_long_errors", NFP_MAC_STATS_RX_FRAME_TOO_LONG_ERRORS, }, { "rx_range_length_errors", NFP_MAC_STATS_RX_RANGE_LENGTH_ERRORS, }, - { "rx_vlan_reveive_ok", NFP_MAC_STATS_RX_VLAN_REVEIVE_OK, }, + { "rx_vlan_received_ok", NFP_MAC_STATS_RX_VLAN_RECEIVED_OK, }, { "rx_errors", NFP_MAC_STATS_RX_IN_ERRORS, }, { "rx_broadcast_pkts", NFP_MAC_STATS_RX_IN_BROADCAST_PKTS, }, { "rx_drop_events", NFP_MAC_STATS_RX_DROP_EVENTS, }, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 1bce8c131bb9..924a05e05da0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -258,6 +258,7 @@ const struct net_device_ops nfp_repr_netdev_ops = { static void nfp_repr_clean(struct nfp_repr *repr) { unregister_netdev(repr->netdev); + nfp_app_repr_clean(repr->app, repr->netdev); dst_release((struct dst_entry *)repr->dst); nfp_port_free(repr->port); } @@ -297,6 +298,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev, netdev->netdev_ops = &nfp_repr_netdev_ops; netdev->ethtool_ops = &nfp_port_ethtool_ops; + netdev->max_mtu = pf_netdev->max_mtu; + SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops); if (nfp_app_has_tc(app)) { @@ -304,12 +307,18 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev, netdev->hw_features |= NETIF_F_HW_TC; } - err = register_netdev(netdev); + err = nfp_app_repr_init(app, netdev); if (err) goto err_clean; + err = register_netdev(netdev); + if (err) + goto err_repr_clean; + return 0; +err_repr_clean: + nfp_app_repr_clean(app, netdev); err_clean: dst_release((struct dst_entry *)repr->dst); return err; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 51dcb9c603ee..21bd4aa32646 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -157,7 +157,7 @@ void nfp_devlink_port_unregister(struct nfp_port *port); /* unused 0x008 */ #define NFP_MAC_STATS_RX_FRAME_TOO_LONG_ERRORS (NFP_MAC_STATS_BASE + 0x010) #define NFP_MAC_STATS_RX_RANGE_LENGTH_ERRORS (NFP_MAC_STATS_BASE + 0x018) -#define NFP_MAC_STATS_RX_VLAN_REVEIVE_OK (NFP_MAC_STATS_BASE + 0x020) +#define NFP_MAC_STATS_RX_VLAN_RECEIVED_OK (NFP_MAC_STATS_BASE + 0x020) #define NFP_MAC_STATS_RX_IN_ERRORS (NFP_MAC_STATS_BASE + 0x028) #define NFP_MAC_STATS_RX_IN_BROADCAST_PKTS (NFP_MAC_STATS_BASE + 0x030) #define NFP_MAC_STATS_RX_DROP_EVENTS (NFP_MAC_STATS_BASE + 0x038) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 6e15d3c10ebf..fe7c1f230028 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -1277,11 +1277,10 @@ static struct qed_dcbx_get *qed_dcbnl_get_dcbx(struct qed_hwfn *hwfn, { struct qed_dcbx_get *dcbx_info; - dcbx_info = kmalloc(sizeof(*dcbx_info), GFP_ATOMIC); + dcbx_info = kzalloc(sizeof(*dcbx_info), GFP_ATOMIC); if (!dcbx_info) return NULL; - memset(dcbx_info, 0, sizeof(*dcbx_info)); if (qed_dcbx_query_params(hwfn, dcbx_info, type)) { kfree(dcbx_info); return NULL; diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index dcb8c39382e7..fc0d5fa65ad4 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -2030,21 +2030,6 @@ out: return ret; } -static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct rtl8169_private *tp = netdev_priv(dev); - int ret; - - del_timer_sync(&tp->timer); - - rtl_lock_work(tp); - ret = rtl8169_set_speed(dev, cmd->autoneg, ethtool_cmd_speed(cmd), - cmd->duplex, cmd->advertising); - rtl_unlock_work(tp); - - return ret; -} - static netdev_features_t rtl8169_fix_features(struct net_device *dev, netdev_features_t features) { @@ -2171,6 +2156,27 @@ static int rtl8169_get_link_ksettings(struct net_device *dev, return rc; } +static int rtl8169_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int rc; + u32 advertising; + + if (!ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising)) + return -EINVAL; + + del_timer_sync(&tp->timer); + + rtl_lock_work(tp); + rc = rtl8169_set_speed(dev, cmd->base.autoneg, cmd->base.speed, + cmd->base.duplex, advertising); + rtl_unlock_work(tp); + + return rc; +} + static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { @@ -2591,7 +2597,6 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_link = ethtool_op_get_link, .get_coalesce = rtl_get_coalesce, .set_coalesce = rtl_set_coalesce, - .set_settings = rtl8169_set_settings, .get_msglevel = rtl8169_get_msglevel, .set_msglevel = rtl8169_set_msglevel, .get_regs = rtl8169_get_regs, @@ -2603,6 +2608,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_ts_info = ethtool_op_get_ts_info, .nway_reset = rtl8169_nway_reset, .get_link_ksettings = rtl8169_get_link_ksettings, + .set_link_ksettings = rtl8169_set_link_ksettings, }; static void rtl8169_get_mac_version(struct rtl8169_private *tp, @@ -3789,27 +3795,32 @@ static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp) rtl_writephy(tp, 0x1f, 0x0000); /* EEE setting */ - rtl_w0w1_eri(tp, 0x1b0, ERIAR_MASK_1111, 0x0000, 0x0003, ERIAR_EXGMAC); + rtl_w0w1_eri(tp, 0x1b0, ERIAR_MASK_1111, 0x0003, 0x0000, ERIAR_EXGMAC); rtl_writephy(tp, 0x1f, 0x0005); rtl_writephy(tp, 0x05, 0x8b85); - rtl_w0w1_phy(tp, 0x06, 0x0000, 0x2000); + rtl_w0w1_phy(tp, 0x06, 0x2000, 0x0000); rtl_writephy(tp, 0x1f, 0x0004); rtl_writephy(tp, 0x1f, 0x0007); rtl_writephy(tp, 0x1e, 0x0020); - rtl_w0w1_phy(tp, 0x15, 0x0000, 0x0100); + rtl_w0w1_phy(tp, 0x15, 0x0100, 0x0000); rtl_writephy(tp, 0x1f, 0x0002); rtl_writephy(tp, 0x1f, 0x0000); rtl_writephy(tp, 0x0d, 0x0007); rtl_writephy(tp, 0x0e, 0x003c); rtl_writephy(tp, 0x0d, 0x4007); - rtl_writephy(tp, 0x0e, 0x0000); + rtl_writephy(tp, 0x0e, 0x0006); rtl_writephy(tp, 0x0d, 0x0000); /* Green feature */ rtl_writephy(tp, 0x1f, 0x0003); - rtl_w0w1_phy(tp, 0x19, 0x0000, 0x0001); - rtl_w0w1_phy(tp, 0x10, 0x0000, 0x0400); + rtl_w0w1_phy(tp, 0x19, 0x0001, 0x0000); + rtl_w0w1_phy(tp, 0x10, 0x0400, 0x0000); + rtl_writephy(tp, 0x1f, 0x0000); + rtl_writephy(tp, 0x1f, 0x0005); + rtl_w0w1_phy(tp, 0x01, 0x0100, 0x0000); rtl_writephy(tp, 0x1f, 0x0000); + /* soft-reset phy */ + rtl_writephy(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART); /* Broken BIOS workaround: feed GigaMAC registers with MAC address. */ rtl_rar_exgmac_set(tp, tp->dev->dev_addr); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index db8a4bcfc6c7..a73600dceb8b 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -119,8 +119,8 @@ do { \ #define CPDMA_RXCP 0x60 #define CPSW_POLL_WEIGHT 64 -#define CPSW_MIN_PACKET_SIZE 60 -#define CPSW_MAX_PACKET_SIZE (1500 + 14 + 4 + 4) +#define CPSW_MIN_PACKET_SIZE (VLAN_ETH_ZLEN) +#define CPSW_MAX_PACKET_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) #define RX_PRIORITY_MAPPING 0x76543210 #define TX_PRIORITY_MAPPING 0x33221100 diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 4e16d839c311..b718a02a6bb6 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1337,21 +1337,33 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], } if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { +#if IS_ENABLED(CONFIG_IPV6) if (changelink) { attrtype = IFLA_GENEVE_UDP_ZERO_CSUM6_TX; goto change_notsup; } if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) info->key.tun_flags &= ~TUNNEL_CSUM; +#else + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX], + "IPv6 support not enabled in the kernel"); + return -EPFNOSUPPORT; +#endif } if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { +#if IS_ENABLED(CONFIG_IPV6) if (changelink) { attrtype = IFLA_GENEVE_UDP_ZERO_CSUM6_RX; goto change_notsup; } if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) *use_udp6_rx_checksums = false; +#else + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX], + "IPv6 support not enabled in the kernel"); + return -EPFNOSUPPORT; +#endif } return 0; @@ -1527,11 +1539,13 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) goto nla_put_failure; if (metadata && nla_put_flag(skb, IFLA_GENEVE_COLLECT_METADATA)) - goto nla_put_failure; + goto nla_put_failure; +#if IS_ENABLED(CONFIG_IPV6) if (nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, !geneve->use_udp6_rx_checksums)) goto nla_put_failure; +#endif return 0; diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 4958bb6b7376..88ddfb92122b 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -646,6 +646,10 @@ struct nvsp_message { #define NETVSC_RECEIVE_BUFFER_ID 0xcafe #define NETVSC_SEND_BUFFER_ID 0 +#define NETVSC_SUPPORTED_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | \ + NETIF_F_TSO | NETIF_F_IPV6_CSUM | \ + NETIF_F_TSO6) + #define VRSS_SEND_TAB_SIZE 16 /* must be power of 2 */ #define VRSS_CHANNEL_MAX 64 #define VRSS_CHANNEL_DEFAULT 8 diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index da216ca4f2b2..5129647d420c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -2011,7 +2011,7 @@ static int netvsc_probe(struct hv_device *dev, memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); - /* hw_features computed in rndis_filter_device_add */ + /* hw_features computed in rndis_netdev_set_hwcaps() */ net->features = net->hw_features | NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 8b1242b8d8ef..7b637c7dd1e5 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1131,69 +1131,20 @@ unlock: rtnl_unlock(); } -struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, - struct netvsc_device_info *device_info) +static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device, + struct netvsc_device *nvdev) { - struct net_device *net = hv_get_drvdata(dev); + struct net_device *net = rndis_device->ndev; struct net_device_context *net_device_ctx = netdev_priv(net); - struct netvsc_device *net_device; - struct rndis_device *rndis_device; struct ndis_offload hwcaps; struct ndis_offload_params offloads; - struct ndis_recv_scale_cap rsscap; - u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); unsigned int gso_max_size = GSO_MAX_SIZE; - u32 mtu, size; - const struct cpumask *node_cpu_mask; - u32 num_possible_rss_qs; - int i, ret; - - rndis_device = get_rndis_device(); - if (!rndis_device) - return ERR_PTR(-ENODEV); - - /* - * Let the inner driver handle this first to create the netvsc channel - * NOTE! Once the channel is created, we may get a receive callback - * (RndisFilterOnReceive()) before this call is completed - */ - net_device = netvsc_device_add(dev, device_info); - if (IS_ERR(net_device)) { - kfree(rndis_device); - return net_device; - } - - /* Initialize the rndis device */ - net_device->max_chn = 1; - net_device->num_chn = 1; - - net_device->extension = rndis_device; - rndis_device->ndev = net; - - /* Send the rndis initialization message */ - ret = rndis_filter_init_device(rndis_device, net_device); - if (ret != 0) - goto err_dev_remv; - - /* Get the MTU from the host */ - size = sizeof(u32); - ret = rndis_filter_query_device(rndis_device, net_device, - RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, - &mtu, &size); - if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) - net->mtu = mtu; - - /* Get the mac address */ - ret = rndis_filter_query_device_mac(rndis_device, net_device); - if (ret != 0) - goto err_dev_remv; - - memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); + int ret; /* Find HW offload capabilities */ - ret = rndis_query_hwcaps(rndis_device, net_device, &hwcaps); + ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps); if (ret != 0) - goto err_dev_remv; + return ret; /* A value of zero means "no change"; now turn on what we want. */ memset(&offloads, 0, sizeof(struct ndis_offload_params)); @@ -1201,8 +1152,12 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, /* Linux does not care about IP checksum, always does in kernel */ offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED; + /* Reset previously set hw_features flags */ + net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES; + net_device_ctx->tx_checksum_mask = 0; + /* Compute tx offload settings based on hw capabilities */ - net->hw_features = NETIF_F_RXCSUM; + net->hw_features |= NETIF_F_RXCSUM; if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) { /* Can checksum TCP */ @@ -1246,10 +1201,75 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, } } + /* In case some hw_features disappeared we need to remove them from + * net->features list as they're no longer supported. + */ + net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features; + netif_set_gso_max_size(net, gso_max_size); - ret = rndis_filter_set_offload_params(net, net_device, &offloads); - if (ret) + ret = rndis_filter_set_offload_params(net, nvdev, &offloads); + + return ret; +} + +struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, + struct netvsc_device_info *device_info) +{ + struct net_device *net = hv_get_drvdata(dev); + struct netvsc_device *net_device; + struct rndis_device *rndis_device; + struct ndis_recv_scale_cap rsscap; + u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); + u32 mtu, size; + const struct cpumask *node_cpu_mask; + u32 num_possible_rss_qs; + int i, ret; + + rndis_device = get_rndis_device(); + if (!rndis_device) + return ERR_PTR(-ENODEV); + + /* Let the inner driver handle this first to create the netvsc channel + * NOTE! Once the channel is created, we may get a receive callback + * (RndisFilterOnReceive()) before this call is completed + */ + net_device = netvsc_device_add(dev, device_info); + if (IS_ERR(net_device)) { + kfree(rndis_device); + return net_device; + } + + /* Initialize the rndis device */ + net_device->max_chn = 1; + net_device->num_chn = 1; + + net_device->extension = rndis_device; + rndis_device->ndev = net; + + /* Send the rndis initialization message */ + ret = rndis_filter_init_device(rndis_device, net_device); + if (ret != 0) + goto err_dev_remv; + + /* Get the MTU from the host */ + size = sizeof(u32); + ret = rndis_filter_query_device(rndis_device, net_device, + RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, + &mtu, &size); + if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) + net->mtu = mtu; + + /* Get the mac address */ + ret = rndis_filter_query_device_mac(rndis_device, net_device); + if (ret != 0) + goto err_dev_remv; + + memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); + + /* Query and set hardware capabilities */ + ret = rndis_netdev_set_hwcaps(rndis_device, net_device); + if (ret != 0) goto err_dev_remv; rndis_filter_query_device_link_status(rndis_device, net_device); diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index f2a7e929316e..11c1e7950fe5 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -116,7 +116,7 @@ bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6) return false; } -static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type) +static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type) { void *lyr3h = NULL; @@ -124,7 +124,7 @@ static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type) case htons(ETH_P_ARP): { struct arphdr *arph; - if (unlikely(!pskb_may_pull(skb, sizeof(*arph)))) + if (unlikely(!pskb_may_pull(skb, arp_hdr_len(port->dev)))) return NULL; arph = arp_hdr(skb); @@ -165,8 +165,26 @@ static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type) /* Only Neighbour Solicitation pkts need different treatment */ if (ipv6_addr_any(&ip6h->saddr) && ip6h->nexthdr == NEXTHDR_ICMP) { + struct icmp6hdr *icmph; + + if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h) + sizeof(*icmph)))) + return NULL; + + ip6h = ipv6_hdr(skb); + icmph = (struct icmp6hdr *)(ip6h + 1); + + if (icmph->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) { + /* Need to access the ipv6 address in body */ + if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h) + sizeof(*icmph) + + sizeof(struct in6_addr)))) + return NULL; + + ip6h = ipv6_hdr(skb); + icmph = (struct icmp6hdr *)(ip6h + 1); + } + *type = IPVL_ICMPV6; - lyr3h = ip6h + 1; + lyr3h = icmph; } break; } @@ -510,7 +528,7 @@ static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev) struct ipvl_addr *addr; int addr_type; - lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); + lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type); if (!lyr3h) goto out; @@ -539,7 +557,7 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev) if (!ipvlan_is_vepa(ipvlan->port) && ether_addr_equal(eth->h_dest, eth->h_source)) { - lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); + lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type); if (lyr3h) { addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true); if (addr) { @@ -606,7 +624,7 @@ static bool ipvlan_external_frame(struct sk_buff *skb, struct ipvl_port *port) int addr_type; if (ether_addr_equal(eth->h_source, skb->dev->dev_addr)) { - lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); + lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type); if (!lyr3h) return true; @@ -627,7 +645,7 @@ static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb, struct sk_buff *skb = *pskb; rx_handler_result_t ret = RX_HANDLER_PASS; - lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); + lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type); if (!lyr3h) goto out; @@ -666,7 +684,7 @@ static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb, } else { struct ipvl_addr *addr; - lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); + lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type); if (!lyr3h) return ret; @@ -717,7 +735,7 @@ static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb, if (!port || port->mode != IPVLAN_MODE_L3S) goto out; - lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); + lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type); if (!lyr3h) goto out; diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index a266aa435d4d..30cb803e2fe5 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -107,16 +107,6 @@ static int ipvlan_port_create(struct net_device *dev) struct ipvl_port *port; int err, idx; - if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) { - netdev_err(dev, "Master is either lo or non-ether device\n"); - return -EINVAL; - } - - if (netdev_is_rx_handler_busy(dev)) { - netdev_err(dev, "Device is already in use.\n"); - return -EBUSY; - } - port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL); if (!port) return -ENOMEM; @@ -179,8 +169,9 @@ static void ipvlan_port_destroy(struct net_device *dev) static int ipvlan_init(struct net_device *dev) { struct ipvl_dev *ipvlan = netdev_priv(dev); - const struct net_device *phy_dev = ipvlan->phy_dev; - struct ipvl_port *port = ipvlan->port; + struct net_device *phy_dev = ipvlan->phy_dev; + struct ipvl_port *port; + int err; dev->state = (dev->state & ~IPVLAN_STATE_MASK) | (phy_dev->state & IPVLAN_STATE_MASK); @@ -196,18 +187,27 @@ static int ipvlan_init(struct net_device *dev) if (!ipvlan->pcpu_stats) return -ENOMEM; + if (!netif_is_ipvlan_port(phy_dev)) { + err = ipvlan_port_create(phy_dev); + if (err < 0) { + free_percpu(ipvlan->pcpu_stats); + return err; + } + } + port = ipvlan_port_get_rtnl(phy_dev); port->count += 1; - return 0; } static void ipvlan_uninit(struct net_device *dev) { struct ipvl_dev *ipvlan = netdev_priv(dev); - struct ipvl_port *port = ipvlan->port; + struct net_device *phy_dev = ipvlan->phy_dev; + struct ipvl_port *port; free_percpu(ipvlan->pcpu_stats); + port = ipvlan_port_get_rtnl(phy_dev); port->count -= 1; if (!port->count) ipvlan_port_destroy(port->dev); @@ -554,7 +554,6 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, struct net_device *phy_dev; int err; u16 mode = IPVLAN_MODE_L3; - bool create = false; if (!tb[IFLA_LINK]) return -EINVAL; @@ -568,28 +567,41 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, phy_dev = tmp->phy_dev; } else if (!netif_is_ipvlan_port(phy_dev)) { - err = ipvlan_port_create(phy_dev); - if (err < 0) - return err; - create = true; - } + /* Exit early if the underlying link is invalid or busy */ + if (phy_dev->type != ARPHRD_ETHER || + phy_dev->flags & IFF_LOOPBACK) { + netdev_err(phy_dev, + "Master is either lo or non-ether device\n"); + return -EINVAL; + } - if (data && data[IFLA_IPVLAN_MODE]) - mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); + if (netdev_is_rx_handler_busy(phy_dev)) { + netdev_err(phy_dev, "Device is already in use.\n"); + return -EBUSY; + } + } - port = ipvlan_port_get_rtnl(phy_dev); ipvlan->phy_dev = phy_dev; ipvlan->dev = dev; - ipvlan->port = port; ipvlan->sfeatures = IPVLAN_FEATURES; ipvlan_adjust_mtu(ipvlan, phy_dev); INIT_LIST_HEAD(&ipvlan->addrs); - /* Flags are per port and latest update overrides. User has - * to be consistent in setting it just like the mode attribute. + /* TODO Probably put random address here to be presented to the + * world but keep using the physical-dev address for the outgoing + * packets. */ - if (data && data[IFLA_IPVLAN_FLAGS]) - ipvlan->port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); + memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); + + dev->priv_flags |= IFF_IPVLAN_SLAVE; + + err = register_netdevice(dev); + if (err < 0) + return err; + + /* ipvlan_init() would have created the port, if required */ + port = ipvlan_port_get_rtnl(phy_dev); + ipvlan->port = port; /* If the port-id base is at the MAX value, then wrap it around and * begin from 0x1 again. This may be due to a busy system where lots @@ -609,31 +621,28 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, err = ida_simple_get(&port->ida, 0x1, port->dev_id_start, GFP_KERNEL); if (err < 0) - goto destroy_ipvlan_port; + goto unregister_netdev; dev->dev_id = err; + /* Increment id-base to the next slot for the future assignment */ port->dev_id_start = err + 1; - /* TODO Probably put random address here to be presented to the - * world but keep using the physical-dev address for the outgoing - * packets. - */ - memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); + err = netdev_upper_dev_link(phy_dev, dev, extack); + if (err) + goto remove_ida; - dev->priv_flags |= IFF_IPVLAN_SLAVE; + /* Flags are per port and latest update overrides. User has + * to be consistent in setting it just like the mode attribute. + */ + if (data && data[IFLA_IPVLAN_FLAGS]) + port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); - err = register_netdevice(dev); - if (err < 0) - goto remove_ida; + if (data && data[IFLA_IPVLAN_MODE]) + mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); - err = netdev_upper_dev_link(phy_dev, dev, extack); - if (err) { - goto unregister_netdev; - } err = ipvlan_set_port_mode(port, mode); - if (err) { + if (err) goto unlink_netdev; - } list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); netif_stacked_transfer_operstate(phy_dev, dev); @@ -641,13 +650,10 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, unlink_netdev: netdev_upper_dev_unlink(phy_dev, dev); -unregister_netdev: - unregister_netdevice(dev); remove_ida: ida_simple_remove(&port->ida, dev->dev_id); -destroy_ipvlan_port: - if (create) - ipvlan_port_destroy(phy_dev); +unregister_netdev: + unregister_netdevice(dev); return err; } EXPORT_SYMBOL_GPL(ipvlan_link_new); diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 2c98152d1e1b..1d025ab9568f 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -2411,7 +2411,7 @@ static int dump_secy(struct macsec_secy *secy, struct net_device *dev, if (!hdr) return -EMSGSIZE; - genl_dump_check_consistent(cb, hdr, &macsec_fam); + genl_dump_check_consistent(cb, hdr); if (nla_put_u32(skb, MACSEC_ATTR_IFINDEX, dev->ifindex)) goto nla_put_failure; diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index 72f4228a63bb..9442db221834 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -116,3 +116,7 @@ static struct mdio_device_id __maybe_unused cortina_tbl[] = { }; MODULE_DEVICE_TABLE(mdio, cortina_tbl); + +MODULE_DESCRIPTION("Cortina EDC CDR 10G Ethernet PHY driver"); +MODULE_AUTHOR("NXP"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/tap.c b/drivers/net/tap.c index b13890953ebb..e9489b88407c 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1077,7 +1077,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd, case TUNSETOFFLOAD: /* let the user check for future flags */ if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | - TUN_F_TSO_ECN)) + TUN_F_TSO_ECN | TUN_F_UFO)) return -EINVAL; rtnl_lock(); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 6bb1e604aadd..6a7bde9bc4b2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1485,6 +1485,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, err = xdp_do_redirect(tun->dev, &xdp, xdp_prog); if (err) goto err_redirect; + rcu_read_unlock(); return NULL; case XDP_TX: xdp_xmit = true; @@ -1517,7 +1518,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, if (xdp_xmit) { skb->dev = tun->dev; generic_xdp_tx(skb, xdp_prog); - rcu_read_lock(); + rcu_read_unlock(); return NULL; } @@ -2369,6 +2370,8 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |= NETIF_F_TSO6; arg &= ~(TUN_F_TSO4|TUN_F_TSO6); } + + arg &= ~TUN_F_UFO; } /* This gives the user a way to test for new features in future by diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 42d7edcc3106..981c931a7a1f 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -76,7 +76,6 @@ #define MOD_AUTHOR "Option Wireless" #define MOD_DESCRIPTION "USB High Speed Option driver" -#define MOD_LICENSE "GPL" #define HSO_MAX_NET_DEVICES 10 #define HSO__MAX_MTU 2048 @@ -3286,7 +3285,7 @@ module_exit(hso_exit); MODULE_AUTHOR(MOD_AUTHOR); MODULE_DESCRIPTION(MOD_DESCRIPTION); -MODULE_LICENSE(MOD_LICENSE); +MODULE_LICENSE("GPL"); /* change the debug level (eg: insmod hso.ko debug=0x04) */ MODULE_PARM_DESC(debug, "debug level mask [0x01 | 0x02 | 0x04 | 0x08 | 0x10]"); diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index ca71f6c03859..7275761a1177 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -291,12 +291,15 @@ static void ipheth_sndbulk_callback(struct urb *urb) static int ipheth_carrier_set(struct ipheth_device *dev) { - struct usb_device *udev = dev->udev; + struct usb_device *udev; int retval; + if (!dev) return 0; if (!dev->confirmed_pairing) return 0; + + udev = dev->udev; retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), IPHETH_CMD_CARRIER_CHECK, /* request */ diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 720a3a248070..c750cf7c042b 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1239,6 +1239,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */ {QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */ + {QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/virtio_net. b/drivers/net/virtio_net. deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index e31438541ee1..7d295ee71534 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -566,18 +566,16 @@ static int ath10k_htt_rx_crypto_param_len(struct ath10k *ar, #define MICHAEL_MIC_LEN 8 -static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar, - enum htt_rx_mpdu_encrypt_type type) +static int ath10k_htt_rx_crypto_mic_len(struct ath10k *ar, + enum htt_rx_mpdu_encrypt_type type) { switch (type) { case HTT_RX_MPDU_ENCRYPT_NONE: - return 0; case HTT_RX_MPDU_ENCRYPT_WEP40: case HTT_RX_MPDU_ENCRYPT_WEP104: - return IEEE80211_WEP_ICV_LEN; case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC: case HTT_RX_MPDU_ENCRYPT_TKIP_WPA: - return IEEE80211_TKIP_ICV_LEN; + return 0; case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2: return IEEE80211_CCMP_MIC_LEN; case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2: @@ -594,6 +592,31 @@ static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar, return 0; } +static int ath10k_htt_rx_crypto_icv_len(struct ath10k *ar, + enum htt_rx_mpdu_encrypt_type type) +{ + switch (type) { + case HTT_RX_MPDU_ENCRYPT_NONE: + case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2: + case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2: + case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2: + case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2: + return 0; + case HTT_RX_MPDU_ENCRYPT_WEP40: + case HTT_RX_MPDU_ENCRYPT_WEP104: + return IEEE80211_WEP_ICV_LEN; + case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC: + case HTT_RX_MPDU_ENCRYPT_TKIP_WPA: + return IEEE80211_TKIP_ICV_LEN; + case HTT_RX_MPDU_ENCRYPT_WEP128: + case HTT_RX_MPDU_ENCRYPT_WAPI: + break; + } + + ath10k_warn(ar, "unsupported encryption type %d\n", type); + return 0; +} + struct amsdu_subframe_hdr { u8 dst[ETH_ALEN]; u8 src[ETH_ALEN]; @@ -1063,25 +1086,27 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, /* Tail */ if (status->flag & RX_FLAG_IV_STRIPPED) { skb_trim(msdu, msdu->len - - ath10k_htt_rx_crypto_tail_len(ar, enctype)); + ath10k_htt_rx_crypto_mic_len(ar, enctype)); + + skb_trim(msdu, msdu->len - + ath10k_htt_rx_crypto_icv_len(ar, enctype)); } else { /* MIC */ - if ((status->flag & RX_FLAG_MIC_STRIPPED) && - enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) - skb_trim(msdu, msdu->len - 8); + if (status->flag & RX_FLAG_MIC_STRIPPED) + skb_trim(msdu, msdu->len - + ath10k_htt_rx_crypto_mic_len(ar, enctype)); /* ICV */ - if (status->flag & RX_FLAG_ICV_STRIPPED && - enctype != HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) + if (status->flag & RX_FLAG_ICV_STRIPPED) skb_trim(msdu, msdu->len - - ath10k_htt_rx_crypto_tail_len(ar, enctype)); + ath10k_htt_rx_crypto_icv_len(ar, enctype)); } /* MMIC */ if ((status->flag & RX_FLAG_MMIC_STRIPPED) && !ieee80211_has_morefrags(hdr->frame_control) && enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) - skb_trim(msdu, msdu->len - 8); + skb_trim(msdu, msdu->len - MICHAEL_MIC_LEN); /* Head */ if (status->flag & RX_FLAG_IV_STRIPPED) { diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 71812a2dd513..f7d228b5ba93 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1233,7 +1233,7 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, } /* External RF module */ - iris_node = of_find_node_by_name(mmio_node, "iris"); + iris_node = of_get_child_by_name(mmio_node, "iris"); if (iris_node) { if (of_device_is_compatible(iris_node, "qcom,wcn3620")) wcn->rf_id = RF_IRIS_WCN3620; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index af7c4f36b66f..e7e75b458005 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -72,18 +72,21 @@ #define IWL9000_SMEM_OFFSET 0x400000 #define IWL9000_SMEM_LEN 0x68000 -#define IWL9000_FW_PRE "iwlwifi-9000-pu-a0-jf-a0-" +#define IWL9000A_FW_PRE "iwlwifi-9000-pu-a0-jf-a0-" +#define IWL9000B_FW_PRE "iwlwifi-9000-pu-b0-jf-b0-" #define IWL9000RFB_FW_PRE "iwlwifi-9000-pu-a0-jf-b0-" #define IWL9260A_FW_PRE "iwlwifi-9260-th-a0-jf-a0-" #define IWL9260B_FW_PRE "iwlwifi-9260-th-b0-jf-b0-" -#define IWL9000_MODULE_FIRMWARE(api) \ - IWL9000_FW_PRE "-" __stringify(api) ".ucode" +#define IWL9000A_MODULE_FIRMWARE(api) \ + IWL9000A_FW_PRE __stringify(api) ".ucode" +#define IWL9000B_MODULE_FIRMWARE(api) \ + IWL9000B_FW_PRE __stringify(api) ".ucode" #define IWL9000RFB_MODULE_FIRMWARE(api) \ - IWL9000RFB_FW_PRE "-" __stringify(api) ".ucode" + IWL9000RFB_FW_PRE __stringify(api) ".ucode" #define IWL9260A_MODULE_FIRMWARE(api) \ - IWL9260A_FW_PRE "-" __stringify(api) ".ucode" + IWL9260A_FW_PRE __stringify(api) ".ucode" #define IWL9260B_MODULE_FIRMWARE(api) \ - IWL9260B_FW_PRE "-" __stringify(api) ".ucode" + IWL9260B_FW_PRE __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_9000 10 @@ -194,7 +197,48 @@ const struct iwl_cfg iwl9460_2ac_cfg = { .nvm_ver = IWL9000_NVM_VERSION, .nvm_calib_ver = IWL9000_TX_POWER_VERSION, .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + +const struct iwl_cfg iwl9460_2ac_cfg_soc = { + .name = "Intel(R) Dual Band Wireless AC 9460", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, .integrated = true, + .soc_latency = 5000, +}; + +const struct iwl_cfg iwl9461_2ac_cfg_soc = { + .name = "Intel(R) Dual Band Wireless AC 9461", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, + .soc_latency = 5000, +}; + +const struct iwl_cfg iwl9462_2ac_cfg_soc = { + .name = "Intel(R) Dual Band Wireless AC 9462", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, + .soc_latency = 5000, }; const struct iwl_cfg iwl9560_2ac_cfg = { @@ -206,10 +250,23 @@ const struct iwl_cfg iwl9560_2ac_cfg = { .nvm_ver = IWL9000_NVM_VERSION, .nvm_calib_ver = IWL9000_TX_POWER_VERSION, .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, - .integrated = true, }; -MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); +const struct iwl_cfg iwl9560_2ac_cfg_soc = { + .name = "Intel(R) Dual Band Wireless AC 9560", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, + .soc_latency = 5000, +}; +MODULE_FIRMWARE(IWL9000A_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL9000B_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9000RFB_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9260A_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9260B_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c index ea8206515171..705f83b02e13 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/a000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/a000.c @@ -80,15 +80,15 @@ #define IWL_A000_HR_A0_FW_PRE "iwlwifi-QuQnj-a0-hr-a0-" #define IWL_A000_HR_MODULE_FIRMWARE(api) \ - IWL_A000_HR_FW_PRE "-" __stringify(api) ".ucode" + IWL_A000_HR_FW_PRE __stringify(api) ".ucode" #define IWL_A000_JF_MODULE_FIRMWARE(api) \ - IWL_A000_JF_FW_PRE "-" __stringify(api) ".ucode" + IWL_A000_JF_FW_PRE __stringify(api) ".ucode" #define IWL_A000_HR_F0_QNJ_MODULE_FIRMWARE(api) \ - IWL_A000_HR_F0_FW_PRE "-" __stringify(api) ".ucode" + IWL_A000_HR_F0_FW_PRE __stringify(api) ".ucode" #define IWL_A000_JF_B0_QNJ_MODULE_FIRMWARE(api) \ - IWL_A000_JF_B0_FW_PRE "-" __stringify(api) ".ucode" + IWL_A000_JF_B0_FW_PRE __stringify(api) ".ucode" #define IWL_A000_HR_A0_QNJ_MODULE_FIRMWARE(api) \ - IWL_A000_HR_A0_FW_PRE "-" __stringify(api) ".ucode" + IWL_A000_HR_A0_FW_PRE __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_A000 10 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 5a40092febfb..3bfc657f6b42 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -531,6 +531,8 @@ struct iwl_scan_config_v1 { } __packed; /* SCAN_CONFIG_DB_CMD_API_S */ #define SCAN_TWO_LMACS 2 +#define SCAN_LB_LMAC_IDX 0 +#define SCAN_HB_LMAC_IDX 1 struct iwl_scan_config { __le32 flags; @@ -578,6 +580,7 @@ enum iwl_umac_scan_general_flags { IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9), IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10), IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED = BIT(11), + IWL_UMAC_SCAN_GEN_FLAGS_ADAPTIVE_DWELL = BIT(13), }; /** @@ -631,12 +634,17 @@ struct iwl_scan_req_umac_tail { * @uid: scan id, &enum iwl_umac_scan_uid_offsets * @ooc_priority: out of channel priority - &enum iwl_scan_priority * @general_flags: &enum iwl_umac_scan_general_flags - * @reserved2: for future use and alignment * @scan_start_mac_id: report the scan start TSF time according to this mac TSF * @extended_dwell: dwell time for channels 1, 6 and 11 * @active_dwell: dwell time for active scan * @passive_dwell: dwell time for passive scan * @fragmented_dwell: dwell time for fragmented passive scan + * @adwell_default_n_aps: for adaptive dwell the default number of APs + * per channel + * @adwell_default_n_aps_social: for adaptive dwell the default + * number of APs per social (1,6,11) channel + * @adwell_max_budget: for adaptive dwell the maximal budget of TU to be added + * to total scan time * @max_out_time: max out of serving channel time, per LMAC - for CDB there * are 2 LMACs * @suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs @@ -644,6 +652,8 @@ struct iwl_scan_req_umac_tail { * @channel_flags: &enum iwl_scan_channel_flags * @n_channels: num of channels in scan request * @reserved: for future use and alignment + * @reserved2: for future use and alignment + * @reserved3: for future use and alignment * @data: &struct iwl_scan_channel_cfg_umac and * &struct iwl_scan_req_umac_tail */ @@ -651,41 +661,64 @@ struct iwl_scan_req_umac { __le32 flags; __le32 uid; __le32 ooc_priority; - /* SCAN_GENERAL_PARAMS_API_S_VER_4 */ __le16 general_flags; - u8 reserved2; + u8 reserved; u8 scan_start_mac_id; - u8 extended_dwell; - u8 active_dwell; - u8 passive_dwell; - u8 fragmented_dwell; union { struct { + u8 extended_dwell; + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; __le32 max_out_time; __le32 suspend_time; __le32 scan_priority; - /* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ + /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ u8 channel_flags; u8 n_channels; - __le16 reserved; + __le16 reserved2; u8 data[]; } v1; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ struct { + u8 extended_dwell; + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; __le32 max_out_time[SCAN_TWO_LMACS]; __le32 suspend_time[SCAN_TWO_LMACS]; __le32 scan_priority; - /* SCAN_CHANNEL_PARAMS_API_S_VER_4 */ + /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ u8 channel_flags; u8 n_channels; - __le16 reserved; + __le16 reserved2; u8 data[]; } v6; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_6 */ + struct { + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; + u8 adwell_default_n_aps; + u8 adwell_default_n_aps_social; + u8 reserved3; + __le16 adwell_max_budget; + __le32 max_out_time[SCAN_TWO_LMACS]; + __le32 suspend_time[SCAN_TWO_LMACS]; + __le32 scan_priority; + /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ + u8 channel_flags; + u8 n_channels; + __le16 reserved2; + u8 data[]; + } v7; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_7 */ }; } __packed; -#define IWL_SCAN_REQ_UMAC_SIZE sizeof(struct iwl_scan_req_umac) +#define IWL_SCAN_REQ_UMAC_SIZE_V7 sizeof(struct iwl_scan_req_umac) +#define IWL_SCAN_REQ_UMAC_SIZE_V6 (sizeof(struct iwl_scan_req_umac) - \ + 2 * sizeof(u8) - sizeof(__le16)) #define IWL_SCAN_REQ_UMAC_SIZE_V1 (sizeof(struct iwl_scan_req_umac) - \ - 2 * sizeof(__le32)) + 2 * sizeof(__le32) - 2 * sizeof(u8) - \ + sizeof(__le16)) /** * struct iwl_umac_scan_abort diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 740d97093d1c..37a5c5b4eda6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -264,6 +264,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_STA_TYPE = (__force iwl_ucode_tlv_api_t)30, IWL_UCODE_TLV_API_NAN2_VER2 = (__force iwl_ucode_tlv_api_t)31, /* API Set 1 */ + IWL_UCODE_TLV_API_ADAPTIVE_DWELL = (__force iwl_ucode_tlv_api_t)32, IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE = (__force iwl_ucode_tlv_api_t)34, IWL_UCODE_TLV_API_NEW_RX_STATS = (__force iwl_ucode_tlv_api_t)35, IWL_UCODE_TLV_API_COEX_ATS_EXTERNAL = (__force iwl_ucode_tlv_api_t)37, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index d1263a554420..e21e46cf6f9a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -366,6 +366,7 @@ struct iwl_cfg { u32 dccm2_len; u32 smem_offset; u32 smem_len; + u32 soc_latency; u16 nvm_ver; u16 nvm_calib_ver; u16 rx_with_siso_diversity:1, @@ -472,6 +473,10 @@ extern const struct iwl_cfg iwl9260_2ac_cfg; extern const struct iwl_cfg iwl9270_2ac_cfg; extern const struct iwl_cfg iwl9460_2ac_cfg; extern const struct iwl_cfg iwl9560_2ac_cfg; +extern const struct iwl_cfg iwl9460_2ac_cfg_soc; +extern const struct iwl_cfg iwl9461_2ac_cfg_soc; +extern const struct iwl_cfg iwl9462_2ac_cfg_soc; +extern const struct iwl_cfg iwl9560_2ac_cfg_soc; extern const struct iwl_cfg iwla000_2ac_cfg_hr; extern const struct iwl_cfg iwla000_2ac_cfg_hr_cdb; extern const struct iwl_cfg iwla000_2ac_cfg_jf; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 0e18c5066f04..4575595ab022 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1142,6 +1142,12 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } +static inline bool iwl_mvm_is_adaptive_dwell_supported(struct iwl_mvm *mvm) +{ + return fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_ADAPTIVE_DWELL); +} + static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) { /* For now we only use this mode to differentiate between diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 774122fed454..e4fd476e9ccb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -130,6 +130,19 @@ struct iwl_mvm_scan_params { u32 measurement_dwell; }; +static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm) +{ + struct iwl_scan_req_umac *cmd = mvm->scan_cmd; + + if (iwl_mvm_is_adaptive_dwell_supported(mvm)) + return (void *)&cmd->v7.data; + + if (iwl_mvm_has_new_tx_api(mvm)) + return (void *)&cmd->v6.data; + + return (void *)&cmd->v1.data; +} + static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) { if (mvm->scan_rx_ant != ANT_NONE) @@ -1075,25 +1088,57 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, { struct iwl_mvm_scan_timing_params *timing = &scan_timing[params->type]; + if (iwl_mvm_is_regular_scan(params)) + cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + else + cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2); + + if (iwl_mvm_is_adaptive_dwell_supported(mvm)) { + if (params->measurement_dwell) { + cmd->v7.active_dwell = params->measurement_dwell; + cmd->v7.passive_dwell = params->measurement_dwell; + } else { + cmd->v7.active_dwell = IWL_SCAN_DWELL_ACTIVE; + cmd->v7.passive_dwell = IWL_SCAN_DWELL_PASSIVE; + } + cmd->v7.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; + + cmd->v7.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + cmd->v7.max_out_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(timing->max_out_time); + cmd->v7.suspend_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(timing->suspend_time); + if (iwl_mvm_is_cdb_supported(mvm)) { + cmd->v7.max_out_time[SCAN_HB_LMAC_IDX] = + cpu_to_le32(timing->max_out_time); + cmd->v7.suspend_time[SCAN_HB_LMAC_IDX] = + cpu_to_le32(timing->suspend_time); + } + + return; + } + if (params->measurement_dwell) { - cmd->active_dwell = params->measurement_dwell; - cmd->passive_dwell = params->measurement_dwell; - cmd->extended_dwell = params->measurement_dwell; + cmd->v1.active_dwell = params->measurement_dwell; + cmd->v1.passive_dwell = params->measurement_dwell; + cmd->v1.extended_dwell = params->measurement_dwell; } else { - cmd->active_dwell = IWL_SCAN_DWELL_ACTIVE; - cmd->passive_dwell = IWL_SCAN_DWELL_PASSIVE; - cmd->extended_dwell = IWL_SCAN_DWELL_EXTENDED; + cmd->v1.active_dwell = IWL_SCAN_DWELL_ACTIVE; + cmd->v1.passive_dwell = IWL_SCAN_DWELL_PASSIVE; + cmd->v1.extended_dwell = IWL_SCAN_DWELL_EXTENDED; } - cmd->fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; + cmd->v1.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; if (iwl_mvm_has_new_tx_api(mvm)) { cmd->v6.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); - cmd->v6.max_out_time[0] = cpu_to_le32(timing->max_out_time); - cmd->v6.suspend_time[0] = cpu_to_le32(timing->suspend_time); + cmd->v6.max_out_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(timing->max_out_time); + cmd->v6.suspend_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(timing->suspend_time); if (iwl_mvm_is_cdb_supported(mvm)) { - cmd->v6.max_out_time[1] = + cmd->v6.max_out_time[SCAN_HB_LMAC_IDX] = cpu_to_le32(timing->max_out_time); - cmd->v6.suspend_time[1] = + cmd->v6.suspend_time[SCAN_HB_LMAC_IDX] = cpu_to_le32(timing->suspend_time); } } else { @@ -1102,11 +1147,6 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cmd->v1.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); } - - if (iwl_mvm_is_regular_scan(params)) - cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); - else - cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2); } static void @@ -1178,8 +1218,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int type) { struct iwl_scan_req_umac *cmd = mvm->scan_cmd; - void *cmd_data = iwl_mvm_has_new_tx_api(mvm) ? - (void *)&cmd->v6.data : (void *)&cmd->v1.data; + void *cmd_data = iwl_mvm_get_scan_req_umac_data(mvm); struct iwl_scan_req_umac_tail *sec_part = cmd_data + sizeof(struct iwl_scan_channel_cfg_umac) * mvm->fw->ucode_capa.n_scan_channels; @@ -1216,7 +1255,10 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; - if (iwl_mvm_has_new_tx_api(mvm)) { + if (iwl_mvm_is_adaptive_dwell_supported(mvm)) { + cmd->v7.channel_flags = channel_flags; + cmd->v7.n_channels = params->n_channels; + } else if (iwl_mvm_has_new_tx_api(mvm)) { cmd->v6.channel_flags = channel_flags; cmd->v6.n_channels = params->n_channels; } else { @@ -1661,8 +1703,10 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm) { int base_size = IWL_SCAN_REQ_UMAC_SIZE_V1; - if (iwl_mvm_has_new_tx_api(mvm)) - base_size = IWL_SCAN_REQ_UMAC_SIZE; + if (iwl_mvm_is_adaptive_dwell_supported(mvm)) + base_size = IWL_SCAN_REQ_UMAC_SIZE_V7; + else if (iwl_mvm_has_new_tx_api(mvm)) + base_size = IWL_SCAN_REQ_UMAC_SIZE_V6; if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) return base_size + diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 4a21c12276d7..f21fe59faccf 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -535,47 +535,121 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x2526, 0x0238, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x023C, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0260, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0264, iwl9461_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2526, 0x02A0, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x02A4, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x1010, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x1030, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x1210, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x1410, iwl9270_2ac_cfg)}, - {IWL_PCI_DEVICE(0x2526, 0x1420, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x1420, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2526, 0x1610, iwl9270_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x4010, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x4030, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x40A4, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x42A4, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x271B, 0x0010, iwl9160_2ac_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0014, iwl9160_2ac_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0210, iwl9160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x30DC, 0x0060, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x31DC, 0x0034, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0030, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0034, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0038, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x003C, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0060, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0310, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0410, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0xA370, 0x0030, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0xA370, 0x0034, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0xA370, 0x0038, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0xA370, 0x003C, iwl9560_2ac_cfg)}, - {IWL_PCI_DEVICE(0xA370, 0x0060, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0xA370, 0x1030, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x271B, 0x0214, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x0034, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x0038, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x003C, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x0060, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x0230, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x0234, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x0238, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x023C, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x4030, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0060, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x023C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x003C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0060, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x023C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0310, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0410, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x003C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0060, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x023C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x40A4, iwl9462_2ac_cfg_soc)}, /* a000 Series */ {IWL_PCI_DEVICE(0x2720, 0x0A10, iwla000_2ac_cfg_hr_cdb)}, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 07a49f58070a..10b075a46b26 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2805,7 +2805,7 @@ static int mac80211_hwsim_get_radio(struct sk_buff *skb, return -EMSGSIZE; if (cb) - genl_dump_check_consistent(cb, hdr, &hwsim_genl_family); + genl_dump_check_consistent(cb, hdr); if (data->alpha2[0] && data->alpha2[1]) param.reg_alpha2 = data->alpha2; @@ -3108,6 +3108,7 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) { struct hwsim_new_radio_params param = { 0 }; const char *hwname = NULL; + int ret; param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG]; param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE]; @@ -3147,7 +3148,9 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) param.regd = hwsim_world_regdom_custom[idx]; } - return mac80211_hwsim_new_radio(info, ¶m); + ret = mac80211_hwsim_new_radio(info, ¶m); + kfree(hwname); + return ret; } static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 08730227cd18..8f8443833348 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -162,13 +162,13 @@ static int rsi_usb_reg_read(struct usb_device *usbdev, u8 *buf; int status = -ENOMEM; + if (len > RSI_USB_CTRL_BUF_SIZE) + return -EINVAL; + buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); if (!buf) return status; - if (len > RSI_USB_CTRL_BUF_SIZE) - return -EINVAL; - status = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), USB_VENDOR_REGISTER_READ, @@ -207,13 +207,13 @@ static int rsi_usb_reg_write(struct usb_device *usbdev, u8 *usb_reg_buf; int status = -ENOMEM; + if (len > RSI_USB_CTRL_BUF_SIZE) + return -EINVAL; + usb_reg_buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); if (!usb_reg_buf) return status; - if (len > RSI_USB_CTRL_BUF_SIZE) - return -EINVAL; - usb_reg_buf[0] = (value & 0x00ff); usb_reg_buf[1] = (value & 0xff00) >> 8; usb_reg_buf[2] = 0x0; diff --git a/drivers/ntb/hw/Kconfig b/drivers/ntb/hw/Kconfig index a89243c9fdd3..e51b581fd102 100644 --- a/drivers/ntb/hw/Kconfig +++ b/drivers/ntb/hw/Kconfig @@ -1,3 +1,4 @@ source "drivers/ntb/hw/amd/Kconfig" source "drivers/ntb/hw/idt/Kconfig" source "drivers/ntb/hw/intel/Kconfig" +source "drivers/ntb/hw/mscc/Kconfig" diff --git a/drivers/ntb/hw/Makefile b/drivers/ntb/hw/Makefile index 87332c3905f0..923c442db750 100644 --- a/drivers/ntb/hw/Makefile +++ b/drivers/ntb/hw/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_NTB_AMD) += amd/ obj-$(CONFIG_NTB_IDT) += idt/ obj-$(CONFIG_NTB_INTEL) += intel/ +obj-$(CONFIG_NTB_SWITCHTEC) += mscc/ diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index d44d7ef38fe8..0cd79f367f7c 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -2628,35 +2628,35 @@ static void idt_pci_remove(struct pci_dev *pdev) /* * IDT PCIe-switch models ports configuration structures */ -static struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = { +static const struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = { .name = "89HPES24NT6AG2", .port_cnt = 6, .ports = {0, 2, 4, 6, 8, 12} }; -static struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = { .name = "89HPES32NT8AG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = { .name = "89HPES32NT8BG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes12nt12g2_config = { +static const struct idt_89hpes_cfg idt_89hpes12nt12g2_config = { .name = "89HPES12NT12G2", .port_cnt = 3, .ports = {0, 8, 16} }; -static struct idt_89hpes_cfg idt_89hpes16nt16g2_config = { +static const struct idt_89hpes_cfg idt_89hpes16nt16g2_config = { .name = "89HPES16NT16G2", .port_cnt = 4, .ports = {0, 8, 12, 16} }; -static struct idt_89hpes_cfg idt_89hpes24nt24g2_config = { +static const struct idt_89hpes_cfg idt_89hpes24nt24g2_config = { .name = "89HPES24NT24G2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = { .name = "89HPES32NT24AG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = { .name = "89HPES32NT24BG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.c b/drivers/ntb/hw/intel/ntb_hw_intel.c index 2557e2c05b90..4de074a86073 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.c +++ b/drivers/ntb/hw/intel/ntb_hw_intel.c @@ -1742,89 +1742,18 @@ static int skx_setup_b2b_mw(struct intel_ntb_dev *ndev, { struct pci_dev *pdev; void __iomem *mmio; - resource_size_t bar_size; phys_addr_t bar_addr; - int b2b_bar; - u8 bar_sz; pdev = ndev->ntb.pdev; mmio = ndev->self_mmio; - if (ndev->b2b_idx == UINT_MAX) { - dev_dbg(&pdev->dev, "not using b2b mw\n"); - b2b_bar = 0; - ndev->b2b_off = 0; - } else { - b2b_bar = ndev_mw_to_bar(ndev, ndev->b2b_idx); - if (b2b_bar < 0) - return -EIO; - - dev_dbg(&pdev->dev, "using b2b mw bar %d\n", b2b_bar); - - bar_size = pci_resource_len(ndev->ntb.pdev, b2b_bar); - - dev_dbg(&pdev->dev, "b2b bar size %#llx\n", bar_size); - - if (b2b_mw_share && ((bar_size >> 1) >= XEON_B2B_MIN_SIZE)) { - dev_dbg(&pdev->dev, "b2b using first half of bar\n"); - ndev->b2b_off = bar_size >> 1; - } else if (bar_size >= XEON_B2B_MIN_SIZE) { - dev_dbg(&pdev->dev, "b2b using whole bar\n"); - ndev->b2b_off = 0; - --ndev->mw_count; - } else { - dev_dbg(&pdev->dev, "b2b bar size is too small\n"); - return -EIO; - } - } - - /* - * Reset the secondary bar sizes to match the primary bar sizes, - * except disable or halve the size of the b2b secondary bar. - */ - pci_read_config_byte(pdev, SKX_IMBAR1SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "IMBAR1SZ %#x\n", bar_sz); - if (b2b_bar == 1) { - if (ndev->b2b_off) - bar_sz -= 1; - else - bar_sz = 0; - } - - pci_write_config_byte(pdev, SKX_EMBAR1SZ_OFFSET, bar_sz); - pci_read_config_byte(pdev, SKX_EMBAR1SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "EMBAR1SZ %#x\n", bar_sz); - - pci_read_config_byte(pdev, SKX_IMBAR2SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "IMBAR2SZ %#x\n", bar_sz); - if (b2b_bar == 2) { - if (ndev->b2b_off) - bar_sz -= 1; - else - bar_sz = 0; - } - - pci_write_config_byte(pdev, SKX_EMBAR2SZ_OFFSET, bar_sz); - pci_read_config_byte(pdev, SKX_EMBAR2SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "EMBAR2SZ %#x\n", bar_sz); - - /* SBAR01 hit by first part of the b2b bar */ - if (b2b_bar == 0) - bar_addr = addr->bar0_addr; - else if (b2b_bar == 1) - bar_addr = addr->bar2_addr64; - else if (b2b_bar == 2) - bar_addr = addr->bar4_addr64; - else - return -EIO; - /* setup incoming bar limits == base addrs (zero length windows) */ - bar_addr = addr->bar2_addr64 + (b2b_bar == 1 ? ndev->b2b_off : 0); + bar_addr = addr->bar2_addr64; iowrite64(bar_addr, mmio + SKX_IMBAR1XLMT_OFFSET); bar_addr = ioread64(mmio + SKX_IMBAR1XLMT_OFFSET); dev_dbg(&pdev->dev, "IMBAR1XLMT %#018llx\n", bar_addr); - bar_addr = addr->bar4_addr64 + (b2b_bar == 2 ? ndev->b2b_off : 0); + bar_addr = addr->bar4_addr64; iowrite64(bar_addr, mmio + SKX_IMBAR2XLMT_OFFSET); bar_addr = ioread64(mmio + SKX_IMBAR2XLMT_OFFSET); dev_dbg(&pdev->dev, "IMBAR2XLMT %#018llx\n", bar_addr); diff --git a/drivers/ntb/hw/mscc/Kconfig b/drivers/ntb/hw/mscc/Kconfig new file mode 100644 index 000000000000..013ed6716438 --- /dev/null +++ b/drivers/ntb/hw/mscc/Kconfig @@ -0,0 +1,9 @@ +config NTB_SWITCHTEC + tristate "MicroSemi Switchtec Non-Transparent Bridge Support" + select PCI_SW_SWITCHTEC + help + Enables NTB support for Switchtec PCI switches. This also + selects the Switchtec management driver as they share the same + hardware interface. + + If unsure, say N. diff --git a/drivers/ntb/hw/mscc/Makefile b/drivers/ntb/hw/mscc/Makefile new file mode 100644 index 000000000000..064686ead1ba --- /dev/null +++ b/drivers/ntb/hw/mscc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_NTB_SWITCHTEC) += ntb_hw_switchtec.o diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c new file mode 100644 index 000000000000..afe8ed6f3b23 --- /dev/null +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -0,0 +1,1216 @@ +/* + * Microsemi Switchtec(tm) PCIe Management Driver + * Copyright (c) 2017, Microsemi Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Microsemi Switchtec(tm) NTB Driver"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Microsemi Corporation"); + +static ulong max_mw_size = SZ_2M; +module_param(max_mw_size, ulong, 0644); +MODULE_PARM_DESC(max_mw_size, + "Max memory window size reported to the upper layer"); + +static bool use_lut_mws; +module_param(use_lut_mws, bool, 0644); +MODULE_PARM_DESC(use_lut_mws, + "Enable the use of the LUT based memory windows"); + +#ifndef ioread64 +#ifdef readq +#define ioread64 readq +#else +#define ioread64 _ioread64 +static inline u64 _ioread64(void __iomem *mmio) +{ + u64 low, high; + + low = ioread32(mmio); + high = ioread32(mmio + sizeof(u32)); + return low | (high << 32); +} +#endif +#endif + +#ifndef iowrite64 +#ifdef writeq +#define iowrite64 writeq +#else +#define iowrite64 _iowrite64 +static inline void _iowrite64(u64 val, void __iomem *mmio) +{ + iowrite32(val, mmio); + iowrite32(val >> 32, mmio + sizeof(u32)); +} +#endif +#endif + +#define SWITCHTEC_NTB_MAGIC 0x45CC0001 +#define MAX_MWS 128 + +struct shared_mw { + u32 magic; + u32 link_sta; + u32 partition_id; + u64 mw_sizes[MAX_MWS]; + u32 spad[128]; +}; + +#define MAX_DIRECT_MW ARRAY_SIZE(((struct ntb_ctrl_regs *)(0))->bar_entry) +#define LUT_SIZE SZ_64K + +struct switchtec_ntb { + struct ntb_dev ntb; + struct switchtec_dev *stdev; + + int self_partition; + int peer_partition; + + int doorbell_irq; + int message_irq; + + struct ntb_info_regs __iomem *mmio_ntb; + struct ntb_ctrl_regs __iomem *mmio_ctrl; + struct ntb_dbmsg_regs __iomem *mmio_dbmsg; + struct ntb_ctrl_regs __iomem *mmio_self_ctrl; + struct ntb_ctrl_regs __iomem *mmio_peer_ctrl; + struct ntb_dbmsg_regs __iomem *mmio_self_dbmsg; + + struct shared_mw *self_shared; + struct shared_mw __iomem *peer_shared; + dma_addr_t self_shared_dma; + + u64 db_mask; + u64 db_valid_mask; + int db_shift; + int db_peer_shift; + + /* synchronize rmw access of db_mask and hw reg */ + spinlock_t db_mask_lock; + + int nr_direct_mw; + int nr_lut_mw; + int direct_mw_to_bar[MAX_DIRECT_MW]; + + int peer_nr_direct_mw; + int peer_nr_lut_mw; + int peer_direct_mw_to_bar[MAX_DIRECT_MW]; + + bool link_is_up; + enum ntb_speed link_speed; + enum ntb_width link_width; +}; + +static struct switchtec_ntb *ntb_sndev(struct ntb_dev *ntb) +{ + return container_of(ntb, struct switchtec_ntb, ntb); +} + +static int switchtec_ntb_part_op(struct switchtec_ntb *sndev, + struct ntb_ctrl_regs __iomem *ctl, + u32 op, int wait_status) +{ + static const char * const op_text[] = { + [NTB_CTRL_PART_OP_LOCK] = "lock", + [NTB_CTRL_PART_OP_CFG] = "configure", + [NTB_CTRL_PART_OP_RESET] = "reset", + }; + + int i; + u32 ps; + int status; + + switch (op) { + case NTB_CTRL_PART_OP_LOCK: + status = NTB_CTRL_PART_STATUS_LOCKING; + break; + case NTB_CTRL_PART_OP_CFG: + status = NTB_CTRL_PART_STATUS_CONFIGURING; + break; + case NTB_CTRL_PART_OP_RESET: + status = NTB_CTRL_PART_STATUS_RESETTING; + break; + default: + return -EINVAL; + } + + iowrite32(op, &ctl->partition_op); + + for (i = 0; i < 1000; i++) { + if (msleep_interruptible(50) != 0) { + iowrite32(NTB_CTRL_PART_OP_RESET, &ctl->partition_op); + return -EINTR; + } + + ps = ioread32(&ctl->partition_status) & 0xFFFF; + + if (ps != status) + break; + } + + if (ps == wait_status) + return 0; + + if (ps == status) { + dev_err(&sndev->stdev->dev, + "Timed out while peforming %s (%d). (%08x)", + op_text[op], op, + ioread32(&ctl->partition_status)); + + return -ETIMEDOUT; + } + + return -EIO; +} + +static int switchtec_ntb_send_msg(struct switchtec_ntb *sndev, int idx, + u32 val) +{ + if (idx < 0 || idx >= ARRAY_SIZE(sndev->mmio_self_dbmsg->omsg)) + return -EINVAL; + + iowrite32(val, &sndev->mmio_self_dbmsg->omsg[idx].msg); + + return 0; +} + +static int switchtec_ntb_mw_count(struct ntb_dev *ntb, int pidx) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + int nr_direct_mw = sndev->peer_nr_direct_mw; + int nr_lut_mw = sndev->peer_nr_lut_mw - 1; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + if (!use_lut_mws) + nr_lut_mw = 0; + + return nr_direct_mw + nr_lut_mw; +} + +static int lut_index(struct switchtec_ntb *sndev, int mw_idx) +{ + return mw_idx - sndev->nr_direct_mw + 1; +} + +static int peer_lut_index(struct switchtec_ntb *sndev, int mw_idx) +{ + return mw_idx - sndev->peer_nr_direct_mw + 1; +} + +static int switchtec_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, + int widx, resource_size_t *addr_align, + resource_size_t *size_align, + resource_size_t *size_max) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + int lut; + resource_size_t size; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + lut = widx >= sndev->peer_nr_direct_mw; + size = ioread64(&sndev->peer_shared->mw_sizes[widx]); + + if (size == 0) + return -EINVAL; + + if (addr_align) + *addr_align = lut ? size : SZ_4K; + + if (size_align) + *size_align = lut ? size : SZ_4K; + + if (size_max) + *size_max = size; + + return 0; +} + +static void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + int bar = sndev->peer_direct_mw_to_bar[idx]; + u32 ctl_val; + + ctl_val = ioread32(&ctl->bar_entry[bar].ctl); + ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN; + iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); + iowrite32(0, &ctl->bar_entry[bar].win_size); + iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr); +} + +static void switchtec_ntb_mw_clr_lut(struct switchtec_ntb *sndev, int idx) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + + iowrite64(0, &ctl->lut_entry[peer_lut_index(sndev, idx)]); +} + +static void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx, + dma_addr_t addr, resource_size_t size) +{ + int xlate_pos = ilog2(size); + int bar = sndev->peer_direct_mw_to_bar[idx]; + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + u32 ctl_val; + + ctl_val = ioread32(&ctl->bar_entry[bar].ctl); + ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN; + + iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); + iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size); + iowrite64(sndev->self_partition | addr, + &ctl->bar_entry[bar].xlate_addr); +} + +static void switchtec_ntb_mw_set_lut(struct switchtec_ntb *sndev, int idx, + dma_addr_t addr, resource_size_t size) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + + iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | addr), + &ctl->lut_entry[peer_lut_index(sndev, idx)]); +} + +static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, + dma_addr_t addr, resource_size_t size) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + int xlate_pos = ilog2(size); + int nr_direct_mw = sndev->peer_nr_direct_mw; + int rc; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + dev_dbg(&sndev->stdev->dev, "MW %d: part %d addr %pad size %pap", + widx, pidx, &addr, &size); + + if (widx >= switchtec_ntb_mw_count(ntb, pidx)) + return -EINVAL; + + if (xlate_pos < 12) + return -EINVAL; + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, + NTB_CTRL_PART_STATUS_LOCKED); + if (rc) + return rc; + + if (addr == 0 || size == 0) { + if (widx < nr_direct_mw) + switchtec_ntb_mw_clr_direct(sndev, widx); + else + switchtec_ntb_mw_clr_lut(sndev, widx); + } else { + if (widx < nr_direct_mw) + switchtec_ntb_mw_set_direct(sndev, widx, addr, size); + else + switchtec_ntb_mw_set_lut(sndev, widx, addr, size); + } + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + + if (rc == -EIO) { + dev_err(&sndev->stdev->dev, + "Hardware reported an error configuring mw %d: %08x", + widx, ioread32(&ctl->bar_error)); + + if (widx < nr_direct_mw) + switchtec_ntb_mw_clr_direct(sndev, widx); + else + switchtec_ntb_mw_clr_lut(sndev, widx); + + switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + } + + return rc; +} + +static int switchtec_ntb_peer_mw_count(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return sndev->nr_direct_mw + (use_lut_mws ? sndev->nr_lut_mw - 1 : 0); +} + +static int switchtec_ntb_direct_get_addr(struct switchtec_ntb *sndev, + int idx, phys_addr_t *base, + resource_size_t *size) +{ + int bar = sndev->direct_mw_to_bar[idx]; + size_t offset = 0; + + if (bar < 0) + return -EINVAL; + + if (idx == 0) { + /* + * This is the direct BAR shared with the LUTs + * which means the actual window will be offset + * by the size of all the LUT entries. + */ + + offset = LUT_SIZE * sndev->nr_lut_mw; + } + + if (base) + *base = pci_resource_start(sndev->ntb.pdev, bar) + offset; + + if (size) { + *size = pci_resource_len(sndev->ntb.pdev, bar) - offset; + if (offset && *size > offset) + *size = offset; + + if (*size > max_mw_size) + *size = max_mw_size; + } + + return 0; +} + +static int switchtec_ntb_lut_get_addr(struct switchtec_ntb *sndev, + int idx, phys_addr_t *base, + resource_size_t *size) +{ + int bar = sndev->direct_mw_to_bar[0]; + int offset; + + offset = LUT_SIZE * lut_index(sndev, idx); + + if (base) + *base = pci_resource_start(sndev->ntb.pdev, bar) + offset; + + if (size) + *size = LUT_SIZE; + + return 0; +} + +static int switchtec_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int idx, + phys_addr_t *base, + resource_size_t *size) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (idx < sndev->nr_direct_mw) + return switchtec_ntb_direct_get_addr(sndev, idx, base, size); + else if (idx < switchtec_ntb_peer_mw_count(ntb)) + return switchtec_ntb_lut_get_addr(sndev, idx, base, size); + else + return -EINVAL; +} + +static void switchtec_ntb_part_link_speed(struct switchtec_ntb *sndev, + int partition, + enum ntb_speed *speed, + enum ntb_width *width) +{ + struct switchtec_dev *stdev = sndev->stdev; + + u32 pff = ioread32(&stdev->mmio_part_cfg[partition].vep_pff_inst_id); + u32 linksta = ioread32(&stdev->mmio_pff_csr[pff].pci_cap_region[13]); + + if (speed) + *speed = (linksta >> 16) & 0xF; + + if (width) + *width = (linksta >> 20) & 0x3F; +} + +static void switchtec_ntb_set_link_speed(struct switchtec_ntb *sndev) +{ + enum ntb_speed self_speed, peer_speed; + enum ntb_width self_width, peer_width; + + if (!sndev->link_is_up) { + sndev->link_speed = NTB_SPEED_NONE; + sndev->link_width = NTB_WIDTH_NONE; + return; + } + + switchtec_ntb_part_link_speed(sndev, sndev->self_partition, + &self_speed, &self_width); + switchtec_ntb_part_link_speed(sndev, sndev->peer_partition, + &peer_speed, &peer_width); + + sndev->link_speed = min(self_speed, peer_speed); + sndev->link_width = min(self_width, peer_width); +} + +enum { + LINK_MESSAGE = 0, + MSG_LINK_UP = 1, + MSG_LINK_DOWN = 2, + MSG_CHECK_LINK = 3, +}; + +static void switchtec_ntb_check_link(struct switchtec_ntb *sndev) +{ + int link_sta; + int old = sndev->link_is_up; + + link_sta = sndev->self_shared->link_sta; + if (link_sta) { + u64 peer = ioread64(&sndev->peer_shared->magic); + + if ((peer & 0xFFFFFFFF) == SWITCHTEC_NTB_MAGIC) + link_sta = peer >> 32; + else + link_sta = 0; + } + + sndev->link_is_up = link_sta; + switchtec_ntb_set_link_speed(sndev); + + if (link_sta != old) { + switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_CHECK_LINK); + ntb_link_event(&sndev->ntb); + dev_info(&sndev->stdev->dev, "ntb link %s", + link_sta ? "up" : "down"); + } +} + +static void switchtec_ntb_link_notification(struct switchtec_dev *stdev) +{ + struct switchtec_ntb *sndev = stdev->sndev; + + switchtec_ntb_check_link(sndev); +} + +static u64 switchtec_ntb_link_is_up(struct ntb_dev *ntb, + enum ntb_speed *speed, + enum ntb_width *width) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (speed) + *speed = sndev->link_speed; + if (width) + *width = sndev->link_width; + + return sndev->link_is_up; +} + +static int switchtec_ntb_link_enable(struct ntb_dev *ntb, + enum ntb_speed max_speed, + enum ntb_width max_width) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + dev_dbg(&sndev->stdev->dev, "enabling link"); + + sndev->self_shared->link_sta = 1; + switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP); + + switchtec_ntb_check_link(sndev); + + return 0; +} + +static int switchtec_ntb_link_disable(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + dev_dbg(&sndev->stdev->dev, "disabling link"); + + sndev->self_shared->link_sta = 0; + switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP); + + switchtec_ntb_check_link(sndev); + + return 0; +} + +static u64 switchtec_ntb_db_valid_mask(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return sndev->db_valid_mask; +} + +static int switchtec_ntb_db_vector_count(struct ntb_dev *ntb) +{ + return 1; +} + +static u64 switchtec_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (db_vector < 0 || db_vector > 1) + return 0; + + return sndev->db_valid_mask; +} + +static u64 switchtec_ntb_db_read(struct ntb_dev *ntb) +{ + u64 ret; + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + ret = ioread64(&sndev->mmio_self_dbmsg->idb) >> sndev->db_shift; + + return ret & sndev->db_valid_mask; +} + +static int switchtec_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + iowrite64(db_bits << sndev->db_shift, &sndev->mmio_self_dbmsg->idb); + + return 0; +} + +static int switchtec_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits) +{ + unsigned long irqflags; + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (db_bits & ~sndev->db_valid_mask) + return -EINVAL; + + spin_lock_irqsave(&sndev->db_mask_lock, irqflags); + + sndev->db_mask |= db_bits << sndev->db_shift; + iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); + + spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags); + + return 0; +} + +static int switchtec_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) +{ + unsigned long irqflags; + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (db_bits & ~sndev->db_valid_mask) + return -EINVAL; + + spin_lock_irqsave(&sndev->db_mask_lock, irqflags); + + sndev->db_mask &= ~(db_bits << sndev->db_shift); + iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); + + spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags); + + return 0; +} + +static u64 switchtec_ntb_db_read_mask(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return (sndev->db_mask >> sndev->db_shift) & sndev->db_valid_mask; +} + +static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb, + phys_addr_t *db_addr, + resource_size_t *db_size) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + unsigned long offset; + + offset = (unsigned long)sndev->mmio_self_dbmsg->odb - + (unsigned long)sndev->stdev->mmio; + + offset += sndev->db_shift / 8; + + if (db_addr) + *db_addr = pci_resource_start(ntb->pdev, 0) + offset; + if (db_size) + *db_size = sizeof(u32); + + return 0; +} + +static int switchtec_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + iowrite64(db_bits << sndev->db_peer_shift, + &sndev->mmio_self_dbmsg->odb); + + return 0; +} + +static int switchtec_ntb_spad_count(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return ARRAY_SIZE(sndev->self_shared->spad); +} + +static u32 switchtec_ntb_spad_read(struct ntb_dev *ntb, int idx) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad)) + return 0; + + if (!sndev->self_shared) + return 0; + + return sndev->self_shared->spad[idx]; +} + +static int switchtec_ntb_spad_write(struct ntb_dev *ntb, int idx, u32 val) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad)) + return -EINVAL; + + if (!sndev->self_shared) + return -EIO; + + sndev->self_shared->spad[idx] = val; + + return 0; +} + +static u32 switchtec_ntb_peer_spad_read(struct ntb_dev *ntb, int pidx, + int sidx) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad)) + return 0; + + if (!sndev->peer_shared) + return 0; + + return ioread32(&sndev->peer_shared->spad[sidx]); +} + +static int switchtec_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx, + int sidx, u32 val) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad)) + return -EINVAL; + + if (!sndev->peer_shared) + return -EIO; + + iowrite32(val, &sndev->peer_shared->spad[sidx]); + + return 0; +} + +static int switchtec_ntb_peer_spad_addr(struct ntb_dev *ntb, int pidx, + int sidx, phys_addr_t *spad_addr) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + unsigned long offset; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + offset = (unsigned long)&sndev->peer_shared->spad[sidx] - + (unsigned long)sndev->stdev->mmio; + + if (spad_addr) + *spad_addr = pci_resource_start(ntb->pdev, 0) + offset; + + return 0; +} + +static const struct ntb_dev_ops switchtec_ntb_ops = { + .mw_count = switchtec_ntb_mw_count, + .mw_get_align = switchtec_ntb_mw_get_align, + .mw_set_trans = switchtec_ntb_mw_set_trans, + .peer_mw_count = switchtec_ntb_peer_mw_count, + .peer_mw_get_addr = switchtec_ntb_peer_mw_get_addr, + .link_is_up = switchtec_ntb_link_is_up, + .link_enable = switchtec_ntb_link_enable, + .link_disable = switchtec_ntb_link_disable, + .db_valid_mask = switchtec_ntb_db_valid_mask, + .db_vector_count = switchtec_ntb_db_vector_count, + .db_vector_mask = switchtec_ntb_db_vector_mask, + .db_read = switchtec_ntb_db_read, + .db_clear = switchtec_ntb_db_clear, + .db_set_mask = switchtec_ntb_db_set_mask, + .db_clear_mask = switchtec_ntb_db_clear_mask, + .db_read_mask = switchtec_ntb_db_read_mask, + .peer_db_addr = switchtec_ntb_peer_db_addr, + .peer_db_set = switchtec_ntb_peer_db_set, + .spad_count = switchtec_ntb_spad_count, + .spad_read = switchtec_ntb_spad_read, + .spad_write = switchtec_ntb_spad_write, + .peer_spad_read = switchtec_ntb_peer_spad_read, + .peer_spad_write = switchtec_ntb_peer_spad_write, + .peer_spad_addr = switchtec_ntb_peer_spad_addr, +}; + +static void switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) +{ + u64 part_map; + + sndev->ntb.pdev = sndev->stdev->pdev; + sndev->ntb.topo = NTB_TOPO_SWITCH; + sndev->ntb.ops = &switchtec_ntb_ops; + + sndev->self_partition = sndev->stdev->partition; + + sndev->mmio_ntb = sndev->stdev->mmio_ntb; + part_map = ioread64(&sndev->mmio_ntb->ep_map); + part_map &= ~(1 << sndev->self_partition); + sndev->peer_partition = ffs(part_map) - 1; + + dev_dbg(&sndev->stdev->dev, "Partition ID %d of %d (%llx)", + sndev->self_partition, sndev->stdev->partition_count, + part_map); + + sndev->mmio_ctrl = (void * __iomem)sndev->mmio_ntb + + SWITCHTEC_NTB_REG_CTRL_OFFSET; + sndev->mmio_dbmsg = (void * __iomem)sndev->mmio_ntb + + SWITCHTEC_NTB_REG_DBMSG_OFFSET; + + sndev->mmio_self_ctrl = &sndev->mmio_ctrl[sndev->self_partition]; + sndev->mmio_peer_ctrl = &sndev->mmio_ctrl[sndev->peer_partition]; + sndev->mmio_self_dbmsg = &sndev->mmio_dbmsg[sndev->self_partition]; +} + +static int map_bars(int *map, struct ntb_ctrl_regs __iomem *ctrl) +{ + int i; + int cnt = 0; + + for (i = 0; i < ARRAY_SIZE(ctrl->bar_entry); i++) { + u32 r = ioread32(&ctrl->bar_entry[i].ctl); + + if (r & NTB_CTRL_BAR_VALID) + map[cnt++] = i; + } + + return cnt; +} + +static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev) +{ + sndev->nr_direct_mw = map_bars(sndev->direct_mw_to_bar, + sndev->mmio_self_ctrl); + + sndev->nr_lut_mw = ioread16(&sndev->mmio_self_ctrl->lut_table_entries); + sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw); + + dev_dbg(&sndev->stdev->dev, "MWs: %d direct, %d lut", + sndev->nr_direct_mw, sndev->nr_lut_mw); + + sndev->peer_nr_direct_mw = map_bars(sndev->peer_direct_mw_to_bar, + sndev->mmio_peer_ctrl); + + sndev->peer_nr_lut_mw = + ioread16(&sndev->mmio_peer_ctrl->lut_table_entries); + sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw); + + dev_dbg(&sndev->stdev->dev, "Peer MWs: %d direct, %d lut", + sndev->peer_nr_direct_mw, sndev->peer_nr_lut_mw); + +} + +/* + * There are 64 doorbells in the switch hardware but this is + * shared among all partitions. So we must split them in half + * (32 for each partition). However, the message interrupts are + * also shared with the top 4 doorbells so we just limit this to + * 28 doorbells per partition + */ +static void switchtec_ntb_init_db(struct switchtec_ntb *sndev) +{ + sndev->db_valid_mask = 0x0FFFFFFF; + + if (sndev->self_partition < sndev->peer_partition) { + sndev->db_shift = 0; + sndev->db_peer_shift = 32; + } else { + sndev->db_shift = 32; + sndev->db_peer_shift = 0; + } + + sndev->db_mask = 0x0FFFFFFFFFFFFFFFULL; + iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); + iowrite64(sndev->db_valid_mask << sndev->db_peer_shift, + &sndev->mmio_self_dbmsg->odb_mask); +} + +static void switchtec_ntb_init_msgs(struct switchtec_ntb *sndev) +{ + int i; + u32 msg_map = 0; + + for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) { + int m = i | sndev->peer_partition << 2; + + msg_map |= m << i * 8; + } + + iowrite32(msg_map, &sndev->mmio_self_dbmsg->msg_map); + + for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) + iowrite64(NTB_DBMSG_IMSG_STATUS | NTB_DBMSG_IMSG_MASK, + &sndev->mmio_self_dbmsg->imsg[i]); +} + +static int switchtec_ntb_init_req_id_table(struct switchtec_ntb *sndev) +{ + int rc = 0; + u16 req_id; + u32 error; + + req_id = ioread16(&sndev->mmio_ntb->requester_id); + + if (ioread32(&sndev->mmio_self_ctrl->req_id_table_size) < 2) { + dev_err(&sndev->stdev->dev, + "Not enough requester IDs available."); + return -EFAULT; + } + + rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl, + NTB_CTRL_PART_OP_LOCK, + NTB_CTRL_PART_STATUS_LOCKED); + if (rc) + return rc; + + iowrite32(NTB_PART_CTRL_ID_PROT_DIS, + &sndev->mmio_self_ctrl->partition_ctrl); + + /* + * Root Complex Requester ID (which is 0:00.0) + */ + iowrite32(0 << 16 | NTB_CTRL_REQ_ID_EN, + &sndev->mmio_self_ctrl->req_id_table[0]); + + /* + * Host Bridge Requester ID (as read from the mmap address) + */ + iowrite32(req_id << 16 | NTB_CTRL_REQ_ID_EN, + &sndev->mmio_self_ctrl->req_id_table[1]); + + rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl, + NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + if (rc == -EIO) { + error = ioread32(&sndev->mmio_self_ctrl->req_id_error); + dev_err(&sndev->stdev->dev, + "Error setting up the requester ID table: %08x", + error); + } + + return rc; +} + +static void switchtec_ntb_init_shared(struct switchtec_ntb *sndev) +{ + int i; + + memset(sndev->self_shared, 0, LUT_SIZE); + sndev->self_shared->magic = SWITCHTEC_NTB_MAGIC; + sndev->self_shared->partition_id = sndev->stdev->partition; + + for (i = 0; i < sndev->nr_direct_mw; i++) { + int bar = sndev->direct_mw_to_bar[i]; + resource_size_t sz = pci_resource_len(sndev->stdev->pdev, bar); + + if (i == 0) + sz = min_t(resource_size_t, sz, + LUT_SIZE * sndev->nr_lut_mw); + + sndev->self_shared->mw_sizes[i] = sz; + } + + for (i = 0; i < sndev->nr_lut_mw; i++) { + int idx = sndev->nr_direct_mw + i; + + sndev->self_shared->mw_sizes[idx] = LUT_SIZE; + } +} + +static int switchtec_ntb_init_shared_mw(struct switchtec_ntb *sndev) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + int bar = sndev->direct_mw_to_bar[0]; + u32 ctl_val; + int rc; + + sndev->self_shared = dma_zalloc_coherent(&sndev->stdev->pdev->dev, + LUT_SIZE, + &sndev->self_shared_dma, + GFP_KERNEL); + if (!sndev->self_shared) { + dev_err(&sndev->stdev->dev, + "unable to allocate memory for shared mw"); + return -ENOMEM; + } + + switchtec_ntb_init_shared(sndev); + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, + NTB_CTRL_PART_STATUS_LOCKED); + if (rc) + goto unalloc_and_exit; + + ctl_val = ioread32(&ctl->bar_entry[bar].ctl); + ctl_val &= 0xFF; + ctl_val |= NTB_CTRL_BAR_LUT_WIN_EN; + ctl_val |= ilog2(LUT_SIZE) << 8; + ctl_val |= (sndev->nr_lut_mw - 1) << 14; + iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); + + iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | + sndev->self_shared_dma), + &ctl->lut_entry[0]); + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + if (rc) { + u32 bar_error, lut_error; + + bar_error = ioread32(&ctl->bar_error); + lut_error = ioread32(&ctl->lut_error); + dev_err(&sndev->stdev->dev, + "Error setting up shared MW: %08x / %08x", + bar_error, lut_error); + goto unalloc_and_exit; + } + + sndev->peer_shared = pci_iomap(sndev->stdev->pdev, bar, LUT_SIZE); + if (!sndev->peer_shared) { + rc = -ENOMEM; + goto unalloc_and_exit; + } + + dev_dbg(&sndev->stdev->dev, "Shared MW Ready"); + return 0; + +unalloc_and_exit: + dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE, + sndev->self_shared, sndev->self_shared_dma); + + return rc; +} + +static void switchtec_ntb_deinit_shared_mw(struct switchtec_ntb *sndev) +{ + if (sndev->peer_shared) + pci_iounmap(sndev->stdev->pdev, sndev->peer_shared); + + if (sndev->self_shared) + dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE, + sndev->self_shared, + sndev->self_shared_dma); +} + +static irqreturn_t switchtec_ntb_doorbell_isr(int irq, void *dev) +{ + struct switchtec_ntb *sndev = dev; + + dev_dbg(&sndev->stdev->dev, "doorbell\n"); + + ntb_db_event(&sndev->ntb, 0); + + return IRQ_HANDLED; +} + +static irqreturn_t switchtec_ntb_message_isr(int irq, void *dev) +{ + int i; + struct switchtec_ntb *sndev = dev; + + for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) { + u64 msg = ioread64(&sndev->mmio_self_dbmsg->imsg[i]); + + if (msg & NTB_DBMSG_IMSG_STATUS) { + dev_dbg(&sndev->stdev->dev, "message: %d %08x\n", i, + (u32)msg); + iowrite8(1, &sndev->mmio_self_dbmsg->imsg[i].status); + + if (i == LINK_MESSAGE) + switchtec_ntb_check_link(sndev); + } + } + + return IRQ_HANDLED; +} + +static int switchtec_ntb_init_db_msg_irq(struct switchtec_ntb *sndev) +{ + int i; + int rc; + int doorbell_irq = 0; + int message_irq = 0; + int event_irq; + int idb_vecs = sizeof(sndev->mmio_self_dbmsg->idb_vec_map); + + event_irq = ioread32(&sndev->stdev->mmio_part_cfg->vep_vector_number); + + while (doorbell_irq == event_irq) + doorbell_irq++; + while (message_irq == doorbell_irq || + message_irq == event_irq) + message_irq++; + + dev_dbg(&sndev->stdev->dev, "irqs - event: %d, db: %d, msgs: %d", + event_irq, doorbell_irq, message_irq); + + for (i = 0; i < idb_vecs - 4; i++) + iowrite8(doorbell_irq, + &sndev->mmio_self_dbmsg->idb_vec_map[i]); + + for (; i < idb_vecs; i++) + iowrite8(message_irq, + &sndev->mmio_self_dbmsg->idb_vec_map[i]); + + sndev->doorbell_irq = pci_irq_vector(sndev->stdev->pdev, doorbell_irq); + sndev->message_irq = pci_irq_vector(sndev->stdev->pdev, message_irq); + + rc = request_irq(sndev->doorbell_irq, + switchtec_ntb_doorbell_isr, 0, + "switchtec_ntb_doorbell", sndev); + if (rc) + return rc; + + rc = request_irq(sndev->message_irq, + switchtec_ntb_message_isr, 0, + "switchtec_ntb_message", sndev); + if (rc) { + free_irq(sndev->doorbell_irq, sndev); + return rc; + } + + return 0; +} + +static void switchtec_ntb_deinit_db_msg_irq(struct switchtec_ntb *sndev) +{ + free_irq(sndev->doorbell_irq, sndev); + free_irq(sndev->message_irq, sndev); +} + +static int switchtec_ntb_add(struct device *dev, + struct class_interface *class_intf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + struct switchtec_ntb *sndev; + int rc; + + stdev->sndev = NULL; + + if (stdev->pdev->class != MICROSEMI_NTB_CLASSCODE) + return -ENODEV; + + if (stdev->partition_count != 2) + dev_warn(dev, "ntb driver only supports 2 partitions"); + + sndev = kzalloc_node(sizeof(*sndev), GFP_KERNEL, dev_to_node(dev)); + if (!sndev) + return -ENOMEM; + + sndev->stdev = stdev; + switchtec_ntb_init_sndev(sndev); + switchtec_ntb_init_mw(sndev); + switchtec_ntb_init_db(sndev); + switchtec_ntb_init_msgs(sndev); + + rc = switchtec_ntb_init_req_id_table(sndev); + if (rc) + goto free_and_exit; + + rc = switchtec_ntb_init_shared_mw(sndev); + if (rc) + goto free_and_exit; + + rc = switchtec_ntb_init_db_msg_irq(sndev); + if (rc) + goto deinit_shared_and_exit; + + rc = ntb_register_device(&sndev->ntb); + if (rc) + goto deinit_and_exit; + + stdev->sndev = sndev; + stdev->link_notifier = switchtec_ntb_link_notification; + dev_info(dev, "NTB device registered"); + + return 0; + +deinit_and_exit: + switchtec_ntb_deinit_db_msg_irq(sndev); +deinit_shared_and_exit: + switchtec_ntb_deinit_shared_mw(sndev); +free_and_exit: + kfree(sndev); + dev_err(dev, "failed to register ntb device: %d", rc); + return rc; +} + +void switchtec_ntb_remove(struct device *dev, + struct class_interface *class_intf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + struct switchtec_ntb *sndev = stdev->sndev; + + if (!sndev) + return; + + stdev->link_notifier = NULL; + stdev->sndev = NULL; + ntb_unregister_device(&sndev->ntb); + switchtec_ntb_deinit_db_msg_irq(sndev); + switchtec_ntb_deinit_shared_mw(sndev); + kfree(sndev); + dev_info(dev, "ntb device unregistered"); +} + +static struct class_interface switchtec_interface = { + .add_dev = switchtec_ntb_add, + .remove_dev = switchtec_ntb_remove, +}; + +static int __init switchtec_ntb_init(void) +{ + switchtec_interface.class = switchtec_class; + return class_interface_register(&switchtec_interface); +} +module_init(switchtec_ntb_init); + +static void __exit switchtec_ntb_exit(void) +{ + class_interface_unregister(&switchtec_interface); +} +module_exit(switchtec_ntb_exit); diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index f58d8e305323..045e3dd4750e 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -191,8 +191,6 @@ struct ntb_transport_qp { struct ntb_transport_mw { phys_addr_t phys_addr; resource_size_t phys_size; - resource_size_t xlat_align; - resource_size_t xlat_align_size; void __iomem *vbase; size_t xlat_size; size_t buff_size; @@ -687,13 +685,20 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; struct pci_dev *pdev = nt->ndev->pdev; size_t xlat_size, buff_size; + resource_size_t xlat_align; + resource_size_t xlat_align_size; int rc; if (!size) return -EINVAL; - xlat_size = round_up(size, mw->xlat_align_size); - buff_size = round_up(size, mw->xlat_align); + rc = ntb_mw_get_align(nt->ndev, PIDX, num_mw, &xlat_align, + &xlat_align_size, NULL); + if (rc) + return rc; + + xlat_size = round_up(size, xlat_align_size); + buff_size = round_up(size, xlat_align); /* No need to re-setup */ if (mw->xlat_size == xlat_size) @@ -722,7 +727,7 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, * is a requirement of the hardware. It is recommended to setup CMA * for BAR sizes equal or greater than 4MB. */ - if (!IS_ALIGNED(mw->dma_addr, mw->xlat_align)) { + if (!IS_ALIGNED(mw->dma_addr, xlat_align)) { dev_err(&pdev->dev, "DMA memory %pad is not aligned\n", &mw->dma_addr); ntb_free_mw(nt, num_mw); @@ -1104,11 +1109,6 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) for (i = 0; i < mw_count; i++) { mw = &nt->mw_vec[i]; - rc = ntb_mw_get_align(ndev, PIDX, i, &mw->xlat_align, - &mw->xlat_align_size, NULL); - if (rc) - goto err1; - rc = ntb_peer_mw_get_addr(ndev, i, &mw->phys_addr, &mw->phys_size); if (rc) diff --git a/drivers/ntb/test/ntb_perf.c b/drivers/ntb/test/ntb_perf.c index 759f772fa00c..427112cf101a 100644 --- a/drivers/ntb/test/ntb_perf.c +++ b/drivers/ntb/test/ntb_perf.c @@ -108,8 +108,6 @@ MODULE_PARM_DESC(on_node, "Run threads only on NTB device node (default: true)") struct perf_mw { phys_addr_t phys_addr; resource_size_t phys_size; - resource_size_t xlat_align; - resource_size_t xlat_align_size; void __iomem *vbase; size_t xlat_size; size_t buf_size; @@ -472,13 +470,20 @@ static int perf_set_mw(struct perf_ctx *perf, resource_size_t size) { struct perf_mw *mw = &perf->mw; size_t xlat_size, buf_size; + resource_size_t xlat_align; + resource_size_t xlat_align_size; int rc; if (!size) return -EINVAL; - xlat_size = round_up(size, mw->xlat_align_size); - buf_size = round_up(size, mw->xlat_align); + rc = ntb_mw_get_align(perf->ntb, PIDX, 0, &xlat_align, + &xlat_align_size, NULL); + if (rc) + return rc; + + xlat_size = round_up(size, xlat_align_size); + buf_size = round_up(size, xlat_align); if (mw->xlat_size == xlat_size) return 0; @@ -567,11 +572,6 @@ static int perf_setup_mw(struct ntb_dev *ntb, struct perf_ctx *perf) mw = &perf->mw; - rc = ntb_mw_get_align(ntb, PIDX, 0, &mw->xlat_align, - &mw->xlat_align_size, NULL); - if (rc) - return rc; - rc = ntb_peer_mw_get_addr(ntb, 0, &mw->phys_addr, &mw->phys_size); if (rc) return rc; diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index a69815c45ce6..91526a986caa 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c @@ -753,9 +753,9 @@ static ssize_t tool_peer_mw_trans_read(struct file *filep, phys_addr_t base; resource_size_t mw_size; - resource_size_t align_addr; - resource_size_t align_size; - resource_size_t max_size; + resource_size_t align_addr = 0; + resource_size_t align_size = 0; + resource_size_t max_size = 0; buf_size = min_t(size_t, size, 512); diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index 447e0e14f3b6..70d5f3ad9909 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile @@ -21,6 +21,7 @@ libnvdimm-y += region_devs.o libnvdimm-y += region.o libnvdimm-y += namespace_devs.o libnvdimm-y += label.o +libnvdimm-y += badrange.o libnvdimm-$(CONFIG_ND_CLAIM) += claim.o libnvdimm-$(CONFIG_BTT) += btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o diff --git a/drivers/nvdimm/badrange.c b/drivers/nvdimm/badrange.c new file mode 100644 index 000000000000..e068d72b4357 --- /dev/null +++ b/drivers/nvdimm/badrange.c @@ -0,0 +1,293 @@ +/* + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nd-core.h" +#include "nd.h" + +void badrange_init(struct badrange *badrange) +{ + INIT_LIST_HEAD(&badrange->list); + spin_lock_init(&badrange->lock); +} +EXPORT_SYMBOL_GPL(badrange_init); + +static void append_badrange_entry(struct badrange *badrange, + struct badrange_entry *bre, u64 addr, u64 length) +{ + lockdep_assert_held(&badrange->lock); + bre->start = addr; + bre->length = length; + list_add_tail(&bre->list, &badrange->list); +} + +static int alloc_and_append_badrange_entry(struct badrange *badrange, + u64 addr, u64 length, gfp_t flags) +{ + struct badrange_entry *bre; + + bre = kzalloc(sizeof(*bre), flags); + if (!bre) + return -ENOMEM; + + append_badrange_entry(badrange, bre, addr, length); + return 0; +} + +static int add_badrange(struct badrange *badrange, u64 addr, u64 length) +{ + struct badrange_entry *bre, *bre_new; + + spin_unlock(&badrange->lock); + bre_new = kzalloc(sizeof(*bre_new), GFP_KERNEL); + spin_lock(&badrange->lock); + + if (list_empty(&badrange->list)) { + if (!bre_new) + return -ENOMEM; + append_badrange_entry(badrange, bre_new, addr, length); + return 0; + } + + /* + * There is a chance this is a duplicate, check for those first. + * This will be the common case as ARS_STATUS returns all known + * errors in the SPA space, and we can't query it per region + */ + list_for_each_entry(bre, &badrange->list, list) + if (bre->start == addr) { + /* If length has changed, update this list entry */ + if (bre->length != length) + bre->length = length; + kfree(bre_new); + return 0; + } + + /* + * If not a duplicate or a simple length update, add the entry as is, + * as any overlapping ranges will get resolved when the list is consumed + * and converted to badblocks + */ + if (!bre_new) + return -ENOMEM; + append_badrange_entry(badrange, bre_new, addr, length); + + return 0; +} + +int badrange_add(struct badrange *badrange, u64 addr, u64 length) +{ + int rc; + + spin_lock(&badrange->lock); + rc = add_badrange(badrange, addr, length); + spin_unlock(&badrange->lock); + + return rc; +} +EXPORT_SYMBOL_GPL(badrange_add); + +void badrange_forget(struct badrange *badrange, phys_addr_t start, + unsigned int len) +{ + struct list_head *badrange_list = &badrange->list; + u64 clr_end = start + len - 1; + struct badrange_entry *bre, *next; + + spin_lock(&badrange->lock); + + /* + * [start, clr_end] is the badrange interval being cleared. + * [bre->start, bre_end] is the badrange_list entry we're comparing + * the above interval against. The badrange list entry may need + * to be modified (update either start or length), deleted, or + * split into two based on the overlap characteristics + */ + + list_for_each_entry_safe(bre, next, badrange_list, list) { + u64 bre_end = bre->start + bre->length - 1; + + /* Skip intervals with no intersection */ + if (bre_end < start) + continue; + if (bre->start > clr_end) + continue; + /* Delete completely overlapped badrange entries */ + if ((bre->start >= start) && (bre_end <= clr_end)) { + list_del(&bre->list); + kfree(bre); + continue; + } + /* Adjust start point of partially cleared entries */ + if ((start <= bre->start) && (clr_end > bre->start)) { + bre->length -= clr_end - bre->start + 1; + bre->start = clr_end + 1; + continue; + } + /* Adjust bre->length for partial clearing at the tail end */ + if ((bre->start < start) && (bre_end <= clr_end)) { + /* bre->start remains the same */ + bre->length = start - bre->start; + continue; + } + /* + * If clearing in the middle of an entry, we split it into + * two by modifying the current entry to represent one half of + * the split, and adding a new entry for the second half. + */ + if ((bre->start < start) && (bre_end > clr_end)) { + u64 new_start = clr_end + 1; + u64 new_len = bre_end - new_start + 1; + + /* Add new entry covering the right half */ + alloc_and_append_badrange_entry(badrange, new_start, + new_len, GFP_NOWAIT); + /* Adjust this entry to cover the left half */ + bre->length = start - bre->start; + continue; + } + } + spin_unlock(&badrange->lock); +} +EXPORT_SYMBOL_GPL(badrange_forget); + +static void set_badblock(struct badblocks *bb, sector_t s, int num) +{ + dev_dbg(bb->dev, "Found a bad range (0x%llx, 0x%llx)\n", + (u64) s * 512, (u64) num * 512); + /* this isn't an error as the hardware will still throw an exception */ + if (badblocks_set(bb, s, num, 1)) + dev_info_once(bb->dev, "%s: failed for sector %llx\n", + __func__, (u64) s); +} + +/** + * __add_badblock_range() - Convert a physical address range to bad sectors + * @bb: badblocks instance to populate + * @ns_offset: namespace offset where the error range begins (in bytes) + * @len: number of bytes of badrange to be added + * + * This assumes that the range provided with (ns_offset, len) is within + * the bounds of physical addresses for this namespace, i.e. lies in the + * interval [ns_start, ns_start + ns_size) + */ +static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len) +{ + const unsigned int sector_size = 512; + sector_t start_sector, end_sector; + u64 num_sectors; + u32 rem; + + start_sector = div_u64(ns_offset, sector_size); + end_sector = div_u64_rem(ns_offset + len, sector_size, &rem); + if (rem) + end_sector++; + num_sectors = end_sector - start_sector; + + if (unlikely(num_sectors > (u64)INT_MAX)) { + u64 remaining = num_sectors; + sector_t s = start_sector; + + while (remaining) { + int done = min_t(u64, remaining, INT_MAX); + + set_badblock(bb, s, done); + remaining -= done; + s += done; + } + } else + set_badblock(bb, start_sector, num_sectors); +} + +static void badblocks_populate(struct badrange *badrange, + struct badblocks *bb, const struct resource *res) +{ + struct badrange_entry *bre; + + if (list_empty(&badrange->list)) + return; + + list_for_each_entry(bre, &badrange->list, list) { + u64 bre_end = bre->start + bre->length - 1; + + /* Discard intervals with no intersection */ + if (bre_end < res->start) + continue; + if (bre->start > res->end) + continue; + /* Deal with any overlap after start of the namespace */ + if (bre->start >= res->start) { + u64 start = bre->start; + u64 len; + + if (bre_end <= res->end) + len = bre->length; + else + len = res->start + resource_size(res) + - bre->start; + __add_badblock_range(bb, start - res->start, len); + continue; + } + /* + * Deal with overlap for badrange starting before + * the namespace. + */ + if (bre->start < res->start) { + u64 len; + + if (bre_end < res->end) + len = bre->start + bre->length - res->start; + else + len = resource_size(res); + __add_badblock_range(bb, 0, len); + } + } +} + +/** + * nvdimm_badblocks_populate() - Convert a list of badranges to badblocks + * @region: parent region of the range to interrogate + * @bb: badblocks instance to populate + * @res: resource range to consider + * + * The badrange list generated during bus initialization may contain + * multiple, possibly overlapping physical address ranges. Compare each + * of these ranges to the resource range currently being initialized, + * and add badblocks entries for all matching sub-ranges + */ +void nvdimm_badblocks_populate(struct nd_region *nd_region, + struct badblocks *bb, const struct resource *res) +{ + struct nvdimm_bus *nvdimm_bus; + + if (!is_memory(&nd_region->dev)) { + dev_WARN_ONCE(&nd_region->dev, 1, + "%s only valid for pmem regions\n", __func__); + return; + } + nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); + + nvdimm_bus_lock(&nvdimm_bus->dev); + badblocks_populate(&nvdimm_bus->badrange, bb, res); + nvdimm_bus_unlock(&nvdimm_bus->dev); +} +EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index baf283986a7e..0a5e6cd758fe 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -11,6 +11,7 @@ * General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -221,7 +222,7 @@ static void nvdimm_account_cleared_poison(struct nvdimm_bus *nvdimm_bus, phys_addr_t phys, u64 cleared) { if (cleared > 0) - nvdimm_forget_poison(nvdimm_bus, phys, cleared); + badrange_forget(&nvdimm_bus->badrange, phys, cleared); if (cleared > 0 && cleared / 512) nvdimm_clear_badblocks_regions(nvdimm_bus, phys, cleared); @@ -344,11 +345,10 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent, return NULL; INIT_LIST_HEAD(&nvdimm_bus->list); INIT_LIST_HEAD(&nvdimm_bus->mapping_list); - INIT_LIST_HEAD(&nvdimm_bus->poison_list); init_waitqueue_head(&nvdimm_bus->probe_wait); nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); mutex_init(&nvdimm_bus->reconfig_mutex); - spin_lock_init(&nvdimm_bus->poison_lock); + badrange_init(&nvdimm_bus->badrange); if (nvdimm_bus->id < 0) { kfree(nvdimm_bus); return NULL; @@ -395,15 +395,15 @@ static int child_unregister(struct device *dev, void *data) return 0; } -static void free_poison_list(struct list_head *poison_list) +static void free_badrange_list(struct list_head *badrange_list) { - struct nd_poison *pl, *next; + struct badrange_entry *bre, *next; - list_for_each_entry_safe(pl, next, poison_list, list) { - list_del(&pl->list); - kfree(pl); + list_for_each_entry_safe(bre, next, badrange_list, list) { + list_del(&bre->list); + kfree(bre); } - list_del_init(poison_list); + list_del_init(badrange_list); } static int nd_bus_remove(struct device *dev) @@ -417,9 +417,9 @@ static int nd_bus_remove(struct device *dev) nd_synchronize(); device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); - spin_lock(&nvdimm_bus->poison_lock); - free_poison_list(&nvdimm_bus->poison_list); - spin_unlock(&nvdimm_bus->poison_lock); + spin_lock(&nvdimm_bus->badrange.lock); + free_badrange_list(&nvdimm_bus->badrange.list); + spin_unlock(&nvdimm_bus->badrange.lock); nvdimm_bus_destroy_ndctl(nvdimm_bus); diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index bb71f0cf8f5d..1dc527660637 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -398,265 +398,11 @@ struct attribute_group nvdimm_bus_attribute_group = { }; EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); -static void set_badblock(struct badblocks *bb, sector_t s, int num) +int nvdimm_bus_add_badrange(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) { - dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n", - (u64) s * 512, (u64) num * 512); - /* this isn't an error as the hardware will still throw an exception */ - if (badblocks_set(bb, s, num, 1)) - dev_info_once(bb->dev, "%s: failed for sector %llx\n", - __func__, (u64) s); + return badrange_add(&nvdimm_bus->badrange, addr, length); } - -/** - * __add_badblock_range() - Convert a physical address range to bad sectors - * @bb: badblocks instance to populate - * @ns_offset: namespace offset where the error range begins (in bytes) - * @len: number of bytes of poison to be added - * - * This assumes that the range provided with (ns_offset, len) is within - * the bounds of physical addresses for this namespace, i.e. lies in the - * interval [ns_start, ns_start + ns_size) - */ -static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len) -{ - const unsigned int sector_size = 512; - sector_t start_sector, end_sector; - u64 num_sectors; - u32 rem; - - start_sector = div_u64(ns_offset, sector_size); - end_sector = div_u64_rem(ns_offset + len, sector_size, &rem); - if (rem) - end_sector++; - num_sectors = end_sector - start_sector; - - if (unlikely(num_sectors > (u64)INT_MAX)) { - u64 remaining = num_sectors; - sector_t s = start_sector; - - while (remaining) { - int done = min_t(u64, remaining, INT_MAX); - - set_badblock(bb, s, done); - remaining -= done; - s += done; - } - } else - set_badblock(bb, start_sector, num_sectors); -} - -static void badblocks_populate(struct list_head *poison_list, - struct badblocks *bb, const struct resource *res) -{ - struct nd_poison *pl; - - if (list_empty(poison_list)) - return; - - list_for_each_entry(pl, poison_list, list) { - u64 pl_end = pl->start + pl->length - 1; - - /* Discard intervals with no intersection */ - if (pl_end < res->start) - continue; - if (pl->start > res->end) - continue; - /* Deal with any overlap after start of the namespace */ - if (pl->start >= res->start) { - u64 start = pl->start; - u64 len; - - if (pl_end <= res->end) - len = pl->length; - else - len = res->start + resource_size(res) - - pl->start; - __add_badblock_range(bb, start - res->start, len); - continue; - } - /* Deal with overlap for poison starting before the namespace */ - if (pl->start < res->start) { - u64 len; - - if (pl_end < res->end) - len = pl->start + pl->length - res->start; - else - len = resource_size(res); - __add_badblock_range(bb, 0, len); - } - } -} - -/** - * nvdimm_badblocks_populate() - Convert a list of poison ranges to badblocks - * @region: parent region of the range to interrogate - * @bb: badblocks instance to populate - * @res: resource range to consider - * - * The poison list generated during bus initialization may contain - * multiple, possibly overlapping physical address ranges. Compare each - * of these ranges to the resource range currently being initialized, - * and add badblocks entries for all matching sub-ranges - */ -void nvdimm_badblocks_populate(struct nd_region *nd_region, - struct badblocks *bb, const struct resource *res) -{ - struct nvdimm_bus *nvdimm_bus; - struct list_head *poison_list; - - if (!is_memory(&nd_region->dev)) { - dev_WARN_ONCE(&nd_region->dev, 1, - "%s only valid for pmem regions\n", __func__); - return; - } - nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); - poison_list = &nvdimm_bus->poison_list; - - nvdimm_bus_lock(&nvdimm_bus->dev); - badblocks_populate(poison_list, bb, res); - nvdimm_bus_unlock(&nvdimm_bus->dev); -} -EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); - -static void append_poison_entry(struct nvdimm_bus *nvdimm_bus, - struct nd_poison *pl, u64 addr, u64 length) -{ - lockdep_assert_held(&nvdimm_bus->poison_lock); - pl->start = addr; - pl->length = length; - list_add_tail(&pl->list, &nvdimm_bus->poison_list); -} - -static int add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length, - gfp_t flags) -{ - struct nd_poison *pl; - - pl = kzalloc(sizeof(*pl), flags); - if (!pl) - return -ENOMEM; - - append_poison_entry(nvdimm_bus, pl, addr, length); - return 0; -} - -static int bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) -{ - struct nd_poison *pl, *pl_new; - - spin_unlock(&nvdimm_bus->poison_lock); - pl_new = kzalloc(sizeof(*pl_new), GFP_KERNEL); - spin_lock(&nvdimm_bus->poison_lock); - - if (list_empty(&nvdimm_bus->poison_list)) { - if (!pl_new) - return -ENOMEM; - append_poison_entry(nvdimm_bus, pl_new, addr, length); - return 0; - } - - /* - * There is a chance this is a duplicate, check for those first. - * This will be the common case as ARS_STATUS returns all known - * errors in the SPA space, and we can't query it per region - */ - list_for_each_entry(pl, &nvdimm_bus->poison_list, list) - if (pl->start == addr) { - /* If length has changed, update this list entry */ - if (pl->length != length) - pl->length = length; - kfree(pl_new); - return 0; - } - - /* - * If not a duplicate or a simple length update, add the entry as is, - * as any overlapping ranges will get resolved when the list is consumed - * and converted to badblocks - */ - if (!pl_new) - return -ENOMEM; - append_poison_entry(nvdimm_bus, pl_new, addr, length); - - return 0; -} - -int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) -{ - int rc; - - spin_lock(&nvdimm_bus->poison_lock); - rc = bus_add_poison(nvdimm_bus, addr, length); - spin_unlock(&nvdimm_bus->poison_lock); - - return rc; -} -EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); - -void nvdimm_forget_poison(struct nvdimm_bus *nvdimm_bus, phys_addr_t start, - unsigned int len) -{ - struct list_head *poison_list = &nvdimm_bus->poison_list; - u64 clr_end = start + len - 1; - struct nd_poison *pl, *next; - - spin_lock(&nvdimm_bus->poison_lock); - WARN_ON_ONCE(list_empty(poison_list)); - - /* - * [start, clr_end] is the poison interval being cleared. - * [pl->start, pl_end] is the poison_list entry we're comparing - * the above interval against. The poison list entry may need - * to be modified (update either start or length), deleted, or - * split into two based on the overlap characteristics - */ - - list_for_each_entry_safe(pl, next, poison_list, list) { - u64 pl_end = pl->start + pl->length - 1; - - /* Skip intervals with no intersection */ - if (pl_end < start) - continue; - if (pl->start > clr_end) - continue; - /* Delete completely overlapped poison entries */ - if ((pl->start >= start) && (pl_end <= clr_end)) { - list_del(&pl->list); - kfree(pl); - continue; - } - /* Adjust start point of partially cleared entries */ - if ((start <= pl->start) && (clr_end > pl->start)) { - pl->length -= clr_end - pl->start + 1; - pl->start = clr_end + 1; - continue; - } - /* Adjust pl->length for partial clearing at the tail end */ - if ((pl->start < start) && (pl_end <= clr_end)) { - /* pl->start remains the same */ - pl->length = start - pl->start; - continue; - } - /* - * If clearing in the middle of an entry, we split it into - * two by modifying the current entry to represent one half of - * the split, and adding a new entry for the second half. - */ - if ((pl->start < start) && (pl_end > clr_end)) { - u64 new_start = clr_end + 1; - u64 new_len = pl_end - new_start + 1; - - /* Add new entry covering the right half */ - add_poison(nvdimm_bus, new_start, new_len, GFP_NOWAIT); - /* Adjust this entry to cover the left half */ - pl->length = start - pl->start; - continue; - } - } - spin_unlock(&nvdimm_bus->poison_lock); -} -EXPORT_SYMBOL_GPL(nvdimm_forget_poison); +EXPORT_SYMBOL_GPL(nvdimm_bus_add_badrange); #ifdef CONFIG_BLK_DEV_INTEGRITY int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index e0f0e3ce1a32..f8913b8124b6 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c @@ -55,6 +55,8 @@ static int nvdimm_probe(struct device *dev) goto err; rc = nvdimm_init_config_data(ndd); + if (rc == -EACCES) + nvdimm_set_locked(dev); if (rc) goto err; @@ -68,6 +70,7 @@ static int nvdimm_probe(struct device *dev) rc = nd_label_reserve_dpa(ndd); if (ndd->ns_current >= 0) nvdimm_set_aliasing(dev); + nvdimm_clear_locked(dev); nvdimm_bus_unlock(dev); if (rc) diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index f0d1b7e5de01..097794d9f786 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -200,6 +200,13 @@ void nvdimm_set_locked(struct device *dev) set_bit(NDD_LOCKED, &nvdimm->flags); } +void nvdimm_clear_locked(struct device *dev) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + + clear_bit(NDD_LOCKED, &nvdimm->flags); +} + static void nvdimm_release(struct device *dev) { struct nvdimm *nvdimm = to_nvdimm(dev); @@ -324,6 +331,17 @@ static ssize_t commands_show(struct device *dev, } static DEVICE_ATTR_RO(commands); +static ssize_t flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + + return sprintf(buf, "%s%s\n", + test_bit(NDD_ALIASING, &nvdimm->flags) ? "alias " : "", + test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : ""); +} +static DEVICE_ATTR_RO(flags); + static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -365,6 +383,7 @@ static DEVICE_ATTR_RO(available_slots); static struct attribute *nvdimm_attributes[] = { &dev_attr_state.attr, + &dev_attr_flags.attr, &dev_attr_commands.attr, &dev_attr_available_slots.attr, NULL, diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index 9c5f108910e3..de66c02f6140 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -1050,7 +1050,7 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels) nsindex = to_namespace_index(ndd, 0); memset(nsindex, 0, ndd->nsarea.config_size); for (i = 0; i < 2; i++) { - int rc = nd_label_write_index(ndd, i, i*2, ND_NSINDEX_INIT); + int rc = nd_label_write_index(ndd, i, 3 - i, ND_NSINDEX_INIT); if (rc) return rc; diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 3e4d1e7998da..bb3ba8cf24d4 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -1620,7 +1620,7 @@ static umode_t namespace_visible(struct kobject *kobj, if (a == &dev_attr_resource.attr) { if (is_namespace_blk(dev)) return 0; - return a->mode; + return 0400; } if (is_namespace_pmem(dev) || is_namespace_blk(dev)) { @@ -1875,7 +1875,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) * @nspm: target namespace to create * @nd_label: target pmem namespace label to evaluate */ -struct device *create_namespace_pmem(struct nd_region *nd_region, +static struct device *create_namespace_pmem(struct nd_region *nd_region, struct nd_namespace_index *nsindex, struct nd_namespace_label *nd_label) { @@ -2186,7 +2186,7 @@ static int add_namespace_resource(struct nd_region *nd_region, return i; } -struct device *create_namespace_blk(struct nd_region *nd_region, +static struct device *create_namespace_blk(struct nd_region *nd_region, struct nd_namespace_label *nd_label, int count) { diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 86bc19ae30da..79274ead54fb 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -29,10 +29,9 @@ struct nvdimm_bus { struct list_head list; struct device dev; int id, probe_active; - struct list_head poison_list; struct list_head mapping_list; struct mutex reconfig_mutex; - spinlock_t poison_lock; + struct badrange badrange; }; struct nvdimm { diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 9c758a91372b..e958f3724c41 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -34,12 +34,6 @@ enum { NVDIMM_IO_ATOMIC = 1, }; -struct nd_poison { - u64 start; - u64 length; - struct list_head list; -}; - struct nvdimm_drvdata { struct device *dev; int nslabel_size; @@ -254,6 +248,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, unsigned int len); void nvdimm_set_aliasing(struct device *dev); void nvdimm_set_locked(struct device *dev); +void nvdimm_clear_locked(struct device *dev); struct nd_btt *to_nd_btt(struct device *dev); struct nd_gen_sb { diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 9576c444f0ab..65cc171c721d 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -282,8 +282,16 @@ static struct attribute *nd_pfn_attributes[] = { NULL, }; +static umode_t pfn_visible(struct kobject *kobj, struct attribute *a, int n) +{ + if (a == &dev_attr_resource.attr) + return 0400; + return a->mode; +} + struct attribute_group nd_pfn_attribute_group = { .attrs = nd_pfn_attributes, + .is_visible = pfn_visible, }; static const struct attribute_group *nd_pfn_attribute_groups[] = { diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 829d760f651c..abaf38c61220 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -562,8 +562,12 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n) if (!is_nd_pmem(dev) && a == &dev_attr_badblocks.attr) return 0; - if (!is_nd_pmem(dev) && a == &dev_attr_resource.attr) - return 0; + if (a == &dev_attr_resource.attr) { + if (is_nd_pmem(dev)) + return 0400; + else + return 0; + } if (a == &dev_attr_deep_flush.attr) { int has_flush = nvdimm_has_flush(nd_region); diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c index 739b8feadc7d..664d3013f68f 100644 --- a/drivers/nvme/target/fc.c +++ b/drivers/nvme/target/fc.c @@ -2144,6 +2144,7 @@ nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, struct nvmet_fc_fcp_iod *fod) { struct nvme_fc_cmd_iu *cmdiu = &fod->cmdiubuf; + u32 xfrlen = be32_to_cpu(cmdiu->data_len); int ret; /* @@ -2157,7 +2158,6 @@ nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, fod->fcpreq->done = nvmet_fc_xmt_fcp_op_done; - fod->req.transfer_len = be32_to_cpu(cmdiu->data_len); if (cmdiu->flags & FCNVME_CMD_FLAGS_WRITE) { fod->io_dir = NVMET_FCP_WRITE; if (!nvme_is_write(&cmdiu->sqe)) @@ -2168,7 +2168,7 @@ nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, goto transport_error; } else { fod->io_dir = NVMET_FCP_NODATA; - if (fod->req.transfer_len) + if (xfrlen) goto transport_error; } @@ -2192,6 +2192,8 @@ nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, return; } + fod->req.transfer_len = xfrlen; + /* keep a running counter of tail position */ atomic_inc(&fod->queue->sqtail); diff --git a/drivers/of/base.c b/drivers/of/base.c index f2e649ff746f..26618ba8f92a 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -761,10 +761,10 @@ EXPORT_SYMBOL(of_find_node_opts_by_path); /** * of_find_node_by_name - Find a node by its "name" property - * @from: The node to start searching from or NULL, the node + * @from: The node to start searching from or NULL; the node * you pass will not be searched, only the next one - * will; typically, you pass what the previous call - * returned. of_node_put() will be called on it + * will. Typically, you pass what the previous call + * returned. of_node_put() will be called on @from. * @name: The name string to match against * * Returns a node pointer with refcount incremented, use diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index e9ec931f5b9a..a7b1cb6c2f65 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -374,7 +374,7 @@ int of_pci_map_rid(struct device_node *np, u32 rid, pr_debug("%pOF: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n", np, map_name, map_mask, rid_base, out_base, - rid_len, rid, *id_out); + rid_len, rid, masked_rid - rid_base + out_base); return 0; } diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile index 3031fc2f18f6..32389acfa616 100644 --- a/drivers/of/unittest-data/Makefile +++ b/drivers/of/unittest-data/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +DTC_FLAGS_testcases := -Wno-interrupts_property obj-y += testcases.dtb.o targets += testcases.dtb testcases.dtb.S diff --git a/drivers/of/unittest-data/testcases.dts b/drivers/of/unittest-data/testcases.dts index ce49463d9d32..55fe0ee20109 100644 --- a/drivers/of/unittest-data/testcases.dts +++ b/drivers/of/unittest-data/testcases.dts @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /dts-v1/; +/plugin/; + / { testcase-data { changeset { @@ -15,66 +17,3 @@ #include "tests-match.dtsi" #include "tests-platform.dtsi" #include "tests-overlay.dtsi" - -/* - * phandle fixup data - generated by dtc patches that aren't upstream. - * This data must be regenerated whenever phandle references are modified in - * the testdata tree. - * - * The format of this data may be subject to change. For the time being consider - * this a kernel-internal data format. - */ -/ { __local_fixups__ { - testcase-data { - phandle-tests { - consumer-a { - phandle-list = <0x00000000 0x00000008 - 0x00000018 0x00000028 - 0x00000034 0x00000038>; - phandle-list-bad-args = <0x00000000 0x0000000c>; - }; - }; - interrupts { - intmap0 { - interrupt-map = <0x00000004 0x00000010 - 0x00000024 0x00000034>; - }; - intmap1 { - interrupt-map = <0x0000000c>; - }; - interrupts0 { - interrupt-parent = <0x00000000>; - }; - interrupts1 { - interrupt-parent = <0x00000000>; - }; - interrupts-extended0 { - interrupts-extended = <0x00000000 0x00000008 - 0x00000018 0x00000024 - 0x0000002c 0x00000034 - 0x0000003c>; - }; - }; - testcase-device1 { - interrupt-parent = <0x00000000>; - }; - testcase-device2 { - interrupt-parent = <0x00000000>; - }; - overlay2 { - fragment@0 { - target = <0x00000000>; - }; - }; - overlay3 { - fragment@0 { - target = <0x00000000>; - }; - }; - overlay4 { - fragment@0 { - target = <0x00000000>; - }; - }; - }; -}; }; diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index da45dbea20ce..730cc897b94d 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -13,6 +13,7 @@ * */ +#include #include #include @@ -20,8 +21,6 @@ #include #include #include -#include -#include #include MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); @@ -34,265 +33,10 @@ module_param(max_devices, int, 0644); MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); static dev_t switchtec_devt; -static struct class *switchtec_class; static DEFINE_IDA(switchtec_minor_ida); -#define MICROSEMI_VENDOR_ID 0x11f8 -#define MICROSEMI_NTB_CLASSCODE 0x068000 -#define MICROSEMI_MGMT_CLASSCODE 0x058000 - -#define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024 -#define SWITCHTEC_MAX_PFF_CSR 48 - -#define SWITCHTEC_EVENT_OCCURRED BIT(0) -#define SWITCHTEC_EVENT_CLEAR BIT(0) -#define SWITCHTEC_EVENT_EN_LOG BIT(1) -#define SWITCHTEC_EVENT_EN_CLI BIT(2) -#define SWITCHTEC_EVENT_EN_IRQ BIT(3) -#define SWITCHTEC_EVENT_FATAL BIT(4) - -enum { - SWITCHTEC_GAS_MRPC_OFFSET = 0x0000, - SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000, - SWITCHTEC_GAS_SW_EVENT_OFFSET = 0x1800, - SWITCHTEC_GAS_SYS_INFO_OFFSET = 0x2000, - SWITCHTEC_GAS_FLASH_INFO_OFFSET = 0x2200, - SWITCHTEC_GAS_PART_CFG_OFFSET = 0x4000, - SWITCHTEC_GAS_NTB_OFFSET = 0x10000, - SWITCHTEC_GAS_PFF_CSR_OFFSET = 0x134000, -}; - -struct mrpc_regs { - u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; - u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; - u32 cmd; - u32 status; - u32 ret_value; -} __packed; - -enum mrpc_status { - SWITCHTEC_MRPC_STATUS_INPROGRESS = 1, - SWITCHTEC_MRPC_STATUS_DONE = 2, - SWITCHTEC_MRPC_STATUS_ERROR = 0xFF, - SWITCHTEC_MRPC_STATUS_INTERRUPTED = 0x100, -}; - -struct sw_event_regs { - u64 event_report_ctrl; - u64 reserved1; - u64 part_event_bitmap; - u64 reserved2; - u32 global_summary; - u32 reserved3[3]; - u32 stack_error_event_hdr; - u32 stack_error_event_data; - u32 reserved4[4]; - u32 ppu_error_event_hdr; - u32 ppu_error_event_data; - u32 reserved5[4]; - u32 isp_error_event_hdr; - u32 isp_error_event_data; - u32 reserved6[4]; - u32 sys_reset_event_hdr; - u32 reserved7[5]; - u32 fw_exception_hdr; - u32 reserved8[5]; - u32 fw_nmi_hdr; - u32 reserved9[5]; - u32 fw_non_fatal_hdr; - u32 reserved10[5]; - u32 fw_fatal_hdr; - u32 reserved11[5]; - u32 twi_mrpc_comp_hdr; - u32 twi_mrpc_comp_data; - u32 reserved12[4]; - u32 twi_mrpc_comp_async_hdr; - u32 twi_mrpc_comp_async_data; - u32 reserved13[4]; - u32 cli_mrpc_comp_hdr; - u32 cli_mrpc_comp_data; - u32 reserved14[4]; - u32 cli_mrpc_comp_async_hdr; - u32 cli_mrpc_comp_async_data; - u32 reserved15[4]; - u32 gpio_interrupt_hdr; - u32 gpio_interrupt_data; - u32 reserved16[4]; -} __packed; - -enum { - SWITCHTEC_CFG0_RUNNING = 0x04, - SWITCHTEC_CFG1_RUNNING = 0x05, - SWITCHTEC_IMG0_RUNNING = 0x03, - SWITCHTEC_IMG1_RUNNING = 0x07, -}; - -struct sys_info_regs { - u32 device_id; - u32 device_version; - u32 firmware_version; - u32 reserved1; - u32 vendor_table_revision; - u32 table_format_version; - u32 partition_id; - u32 cfg_file_fmt_version; - u16 cfg_running; - u16 img_running; - u32 reserved2[57]; - char vendor_id[8]; - char product_id[16]; - char product_revision[4]; - char component_vendor[8]; - u16 component_id; - u8 component_revision; -} __packed; - -struct flash_info_regs { - u32 flash_part_map_upd_idx; - - struct active_partition_info { - u32 address; - u32 build_version; - u32 build_string; - } active_img; - - struct active_partition_info active_cfg; - struct active_partition_info inactive_img; - struct active_partition_info inactive_cfg; - - u32 flash_length; - - struct partition_info { - u32 address; - u32 length; - } cfg0; - - struct partition_info cfg1; - struct partition_info img0; - struct partition_info img1; - struct partition_info nvlog; - struct partition_info vendor[8]; -}; - -struct ntb_info_regs { - u8 partition_count; - u8 partition_id; - u16 reserved1; - u64 ep_map; - u16 requester_id; -} __packed; - -struct part_cfg_regs { - u32 status; - u32 state; - u32 port_cnt; - u32 usp_port_mode; - u32 usp_pff_inst_id; - u32 vep_pff_inst_id; - u32 dsp_pff_inst_id[47]; - u32 reserved1[11]; - u16 vep_vector_number; - u16 usp_vector_number; - u32 port_event_bitmap; - u32 reserved2[3]; - u32 part_event_summary; - u32 reserved3[3]; - u32 part_reset_hdr; - u32 part_reset_data[5]; - u32 mrpc_comp_hdr; - u32 mrpc_comp_data[5]; - u32 mrpc_comp_async_hdr; - u32 mrpc_comp_async_data[5]; - u32 dyn_binding_hdr; - u32 dyn_binding_data[5]; - u32 reserved4[159]; -} __packed; - -enum { - SWITCHTEC_PART_CFG_EVENT_RESET = 1 << 0, - SWITCHTEC_PART_CFG_EVENT_MRPC_CMP = 1 << 1, - SWITCHTEC_PART_CFG_EVENT_MRPC_ASYNC_CMP = 1 << 2, - SWITCHTEC_PART_CFG_EVENT_DYN_PART_CMP = 1 << 3, -}; - -struct pff_csr_regs { - u16 vendor_id; - u16 device_id; - u32 pci_cfg_header[15]; - u32 pci_cap_region[48]; - u32 pcie_cap_region[448]; - u32 indirect_gas_window[128]; - u32 indirect_gas_window_off; - u32 reserved[127]; - u32 pff_event_summary; - u32 reserved2[3]; - u32 aer_in_p2p_hdr; - u32 aer_in_p2p_data[5]; - u32 aer_in_vep_hdr; - u32 aer_in_vep_data[5]; - u32 dpc_hdr; - u32 dpc_data[5]; - u32 cts_hdr; - u32 cts_data[5]; - u32 reserved3[6]; - u32 hotplug_hdr; - u32 hotplug_data[5]; - u32 ier_hdr; - u32 ier_data[5]; - u32 threshold_hdr; - u32 threshold_data[5]; - u32 power_mgmt_hdr; - u32 power_mgmt_data[5]; - u32 tlp_throttling_hdr; - u32 tlp_throttling_data[5]; - u32 force_speed_hdr; - u32 force_speed_data[5]; - u32 credit_timeout_hdr; - u32 credit_timeout_data[5]; - u32 link_state_hdr; - u32 link_state_data[5]; - u32 reserved4[174]; -} __packed; - -struct switchtec_dev { - struct pci_dev *pdev; - struct device dev; - struct cdev cdev; - - int partition; - int partition_count; - int pff_csr_count; - char pff_local[SWITCHTEC_MAX_PFF_CSR]; - - void __iomem *mmio; - struct mrpc_regs __iomem *mmio_mrpc; - struct sw_event_regs __iomem *mmio_sw_event; - struct sys_info_regs __iomem *mmio_sys_info; - struct flash_info_regs __iomem *mmio_flash_info; - struct ntb_info_regs __iomem *mmio_ntb; - struct part_cfg_regs __iomem *mmio_part_cfg; - struct part_cfg_regs __iomem *mmio_part_cfg_all; - struct pff_csr_regs __iomem *mmio_pff_csr; - - /* - * The mrpc mutex must be held when accessing the other - * mrpc_ fields, alive flag and stuser->state field - */ - struct mutex mrpc_mutex; - struct list_head mrpc_queue; - int mrpc_busy; - struct work_struct mrpc_work; - struct delayed_work mrpc_timeout; - bool alive; - - wait_queue_head_t event_wq; - atomic_t event_cnt; -}; - -static struct switchtec_dev *to_stdev(struct device *dev) -{ - return container_of(dev, struct switchtec_dev, dev); -} +struct class *switchtec_class; +EXPORT_SYMBOL_GPL(switchtec_class); enum mrpc_state { MRPC_IDLE = 0, @@ -1234,6 +978,49 @@ static const struct file_operations switchtec_fops = { .compat_ioctl = switchtec_dev_ioctl, }; +static void link_event_work(struct work_struct *work) +{ + struct switchtec_dev *stdev; + + stdev = container_of(work, struct switchtec_dev, link_event_work); + + if (stdev->link_notifier) + stdev->link_notifier(stdev); +} + +static void check_link_state_events(struct switchtec_dev *stdev) +{ + int idx; + u32 reg; + int count; + int occurred = 0; + + for (idx = 0; idx < stdev->pff_csr_count; idx++) { + reg = ioread32(&stdev->mmio_pff_csr[idx].link_state_hdr); + dev_dbg(&stdev->dev, "link_state: %d->%08x\n", idx, reg); + count = (reg >> 5) & 0xFF; + + if (count != stdev->link_event_count[idx]) { + occurred = 1; + stdev->link_event_count[idx] = count; + } + } + + if (occurred) + schedule_work(&stdev->link_event_work); +} + +static void enable_link_state_events(struct switchtec_dev *stdev) +{ + int idx; + + for (idx = 0; idx < stdev->pff_csr_count; idx++) { + iowrite32(SWITCHTEC_EVENT_CLEAR | + SWITCHTEC_EVENT_EN_IRQ, + &stdev->mmio_pff_csr[idx].link_state_hdr); + } +} + static void stdev_release(struct device *dev) { struct switchtec_dev *stdev = to_stdev(dev); @@ -1286,6 +1073,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev) stdev->mrpc_busy = 0; INIT_WORK(&stdev->mrpc_work, mrpc_event_work); INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work); + INIT_WORK(&stdev->link_event_work, link_event_work); init_waitqueue_head(&stdev->event_wq); atomic_set(&stdev->event_cnt, 0); @@ -1329,6 +1117,9 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx) if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) return 0; + if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE) + return 0; + dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr); hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED); iowrite32(hdr, hdr_reg); @@ -1348,6 +1139,7 @@ static int mask_all_events(struct switchtec_dev *stdev, int eid) for (idx = 0; idx < stdev->pff_csr_count; idx++) { if (!stdev->pff_local[idx]) continue; + count += mask_event(stdev, eid, idx); } } else { @@ -1372,6 +1164,8 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev) iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); } + check_link_state_events(stdev); + for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) event_count += mask_all_events(stdev, eid); @@ -1481,6 +1275,9 @@ static int switchtec_pci_probe(struct pci_dev *pdev, struct switchtec_dev *stdev; int rc; + if (pdev->class == MICROSEMI_NTB_CLASSCODE) + request_module_nowait("ntb_hw_switchtec"); + stdev = stdev_create(pdev); if (IS_ERR(stdev)) return PTR_ERR(stdev); @@ -1498,6 +1295,7 @@ static int switchtec_pci_probe(struct pci_dev *pdev, iowrite32(SWITCHTEC_EVENT_CLEAR | SWITCHTEC_EVENT_EN_IRQ, &stdev->mmio_part_cfg->mrpc_comp_hdr); + enable_link_state_events(stdev); rc = cdev_device_add(&stdev->cdev, &stdev->dev); if (rc) diff --git a/drivers/pcmcia/sa1111_badge4.c b/drivers/pcmcia/sa1111_badge4.c index 2f490930430d..93a5c7423d80 100644 --- a/drivers/pcmcia/sa1111_badge4.c +++ b/drivers/pcmcia/sa1111_badge4.c @@ -144,6 +144,7 @@ int pcmcia_badge4_init(struct sa1111_dev *dev) sa11xx_drv_pcmcia_add_one); } +#ifndef MODULE static int __init pcmv_setup(char *s) { int v[4]; @@ -158,3 +159,4 @@ static int __init pcmv_setup(char *s) } __setup("pcmv=", pcmv_setup); +#endif diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 09dac11337d1..2c745e8ccad6 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -93,12 +93,31 @@ config ASUS_LAPTOP config DELL_SMBIOS tristate - select DCDBAS + +config DELL_SMBIOS_WMI + tristate "Dell SMBIOS calling interface (WMI implementation)" + depends on ACPI_WMI + select DELL_WMI_DESCRIPTOR + select DELL_SMBIOS + ---help--- + This provides an implementation for the Dell SMBIOS calling interface + communicated over ACPI-WMI. + + If you have a Dell computer from >2007 you should say Y or M here. + If you aren't sure and this module doesn't work for your computer + it just won't load. + +config DELL_SMBIOS_SMM + tristate "Dell SMBIOS calling interface (SMM implementation)" + depends on DCDBAS + select DELL_SMBIOS ---help--- - This module provides common functions for kernel modules using - Dell SMBIOS. + This provides an implementation for the Dell SMBIOS calling interface + communicated over SMI/SMM. - If you have a Dell laptop, say Y or M here. + If you have a Dell computer from <=2017 you should say Y or M here. + If you aren't sure and this module doesn't work for your computer + it just won't load. config DELL_LAPTOP tristate "Dell Laptop Extras" @@ -116,11 +135,12 @@ config DELL_LAPTOP laptops (except for some models covered by the Compal driver). config DELL_WMI - tristate "Dell WMI extras" + tristate "Dell WMI notifications" depends on ACPI_WMI depends on DMI depends on INPUT depends on ACPI_VIDEO || ACPI_VIDEO = n + select DELL_WMI_DESCRIPTOR select DELL_SMBIOS select INPUT_SPARSEKMAP ---help--- @@ -129,6 +149,10 @@ config DELL_WMI To compile this driver as a module, choose M here: the module will be called dell-wmi. +config DELL_WMI_DESCRIPTOR + tristate + depends on ACPI_WMI + config DELL_WMI_AIO tristate "WMI Hotkeys for Dell All-In-One series" depends on ACPI_WMI @@ -426,7 +450,6 @@ config THINKPAD_ACPI_ALSA_SUPPORT config THINKPAD_ACPI_DEBUGFACILITIES bool "Maintainer debug facilities" depends on THINKPAD_ACPI - default n ---help--- Enables extra stuff in the thinkpad-acpi which is completely useless for normal use. Read the driver source to find out what it does. @@ -437,7 +460,6 @@ config THINKPAD_ACPI_DEBUGFACILITIES config THINKPAD_ACPI_DEBUG bool "Verbose debug mode" depends on THINKPAD_ACPI - default n ---help--- Enables extra debugging information, at the expense of a slightly increase in driver size. @@ -447,7 +469,6 @@ config THINKPAD_ACPI_DEBUG config THINKPAD_ACPI_UNSAFE_LEDS bool "Allow control of important LEDs (unsafe)" depends on THINKPAD_ACPI - default n ---help--- Overriding LED state on ThinkPads can mask important firmware alerts (like critical battery condition), or misled @@ -515,7 +536,6 @@ config SENSORS_HDAPS tristate "Thinkpad Hard Drive Active Protection System (hdaps)" depends on INPUT select INPUT_POLLDEV - default n help This driver provides support for the IBM Hard Drive Active Protection System (hdaps), which provides an accelerometer and other misc. data. @@ -658,6 +678,18 @@ config WMI_BMOF To compile this driver as a module, choose M here: the module will be called wmi-bmof. +config INTEL_WMI_THUNDERBOLT + tristate "Intel WMI thunderbolt force power driver" + depends on ACPI_WMI + ---help--- + Say Y here if you want to be able to use the WMI interface on select + systems to force the power control of Intel Thunderbolt controllers. + This is useful for updating the firmware when devices are not plugged + into the controller. + + To compile this driver as a module, choose M here: the module will + be called intel-wmi-thunderbolt. + config MSI_WMI tristate "MSI WMI extras" depends on ACPI_WMI @@ -763,7 +795,6 @@ config TOSHIBA_HAPS config TOSHIBA_WMI tristate "Toshiba WMI Hotkeys Driver (EXPERIMENTAL)" - default n depends on ACPI_WMI depends on INPUT select INPUT_SPARSEKMAP @@ -785,7 +816,6 @@ config ACPI_CMPC depends on RFKILL || RFKILL=n select INPUT select BACKLIGHT_CLASS_DEVICE - default n help Support for Intel Classmate PC ACPI devices, including some keys as input device, backlight device, tablet and accelerometer @@ -793,7 +823,7 @@ config ACPI_CMPC config INTEL_CHT_INT33FE tristate "Intel Cherry Trail ACPI INT33FE Driver" - depends on X86 && ACPI && I2C + depends on X86 && ACPI && I2C && REGULATOR ---help--- This driver add support for the INT33FE ACPI device found on some Intel Cherry Trail devices. @@ -804,6 +834,10 @@ config INTEL_CHT_INT33FE This driver instantiates i2c-clients for these, so that standard i2c drivers for these chips can bind to the them. + If you enable this driver it is advised to also select + CONFIG_TYPEC_FUSB302=m, CONFIG_CHARGER_BQ24190=m and + CONFIG_BATTERY_MAX17042=m. + config INTEL_INT0002_VGPIO tristate "Intel ACPI INT0002 Virtual GPIO driver" depends on GPIOLIB && ACPI @@ -892,7 +926,6 @@ config INTEL_IPS config INTEL_IMR bool "Intel Isolated Memory Region support" - default n depends on X86_INTEL_QUARK && IOSF_MBI ---help--- This option provides a means to manipulate Isolated Memory Regions. @@ -1088,7 +1121,6 @@ config INTEL_PUNIT_IPC config INTEL_TELEMETRY tristate "Intel SoC Telemetry Driver" - default n depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64 ---help--- This driver provides interfaces to configure and use @@ -1111,7 +1143,6 @@ config MLX_PLATFORM config MLX_CPLD_PLATFORM tristate "Mellanox platform hotplug driver support" - default n select HWMON select I2C ---help--- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index f9e3ae683bbe..c32b34a72467 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -13,8 +13,11 @@ obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o +obj-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o +obj-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o +obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o @@ -40,6 +43,7 @@ obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o +obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o # toshiba_acpi must link after wmi to ensure that wmi devices are found # before toshiba_acpi initializes diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 48e1541dc8d4..a32c5c00e0e7 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -119,6 +119,7 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ +#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 @@ -148,6 +149,7 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 +#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 @@ -222,10 +224,13 @@ struct asus_wmi { int tpd_led_wk; struct led_classdev kbd_led; int kbd_led_wk; + struct led_classdev lightbar_led; + int lightbar_led_wk; struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; struct work_struct kbd_led_work; struct work_struct wlan_led_work; + struct work_struct lightbar_led_work; struct asus_rfkill wlan; struct asus_rfkill bluetooth; @@ -567,6 +572,48 @@ static enum led_brightness wlan_led_get(struct led_classdev *led_cdev) return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK; } +static void lightbar_led_update(struct work_struct *work) +{ + struct asus_wmi *asus; + int ctrl_param; + + asus = container_of(work, struct asus_wmi, lightbar_led_work); + + ctrl_param = asus->lightbar_led_wk; + asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL); +} + +static void lightbar_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct asus_wmi *asus; + + asus = container_of(led_cdev, struct asus_wmi, lightbar_led); + + asus->lightbar_led_wk = !!value; + queue_work(asus->led_workqueue, &asus->lightbar_led_work); +} + +static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev) +{ + struct asus_wmi *asus; + u32 result; + + asus = container_of(led_cdev, struct asus_wmi, lightbar_led); + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); + + return result & ASUS_WMI_DSTS_LIGHTBAR_MASK; +} + +static int lightbar_led_presence(struct asus_wmi *asus) +{ + u32 result; + + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); + + return result & ASUS_WMI_DSTS_PRESENCE_BIT; +} + static void asus_wmi_led_exit(struct asus_wmi *asus) { if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) @@ -575,6 +622,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus) led_classdev_unregister(&asus->tpd_led); if (!IS_ERR_OR_NULL(asus->wlan_led.dev)) led_classdev_unregister(&asus->wlan_led); + if (!IS_ERR_OR_NULL(asus->lightbar_led.dev)) + led_classdev_unregister(&asus->lightbar_led); if (asus->led_workqueue) destroy_workqueue(asus->led_workqueue); } @@ -630,6 +679,20 @@ static int asus_wmi_led_init(struct asus_wmi *asus) rv = led_classdev_register(&asus->platform_device->dev, &asus->wlan_led); + if (rv) + goto error; + } + + if (lightbar_led_presence(asus)) { + INIT_WORK(&asus->lightbar_led_work, lightbar_led_update); + + asus->lightbar_led.name = "asus::lightbar"; + asus->lightbar_led.brightness_set = lightbar_led_set; + asus->lightbar_led.brightness_get = lightbar_led_get; + asus->lightbar_led.max_brightness = 1; + + rv = led_classdev_register(&asus->platform_device->dev, + &asus->lightbar_led); } error: diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index f42159fd2031..bf897b1832b1 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -35,18 +35,6 @@ #include "dell-rbtn.h" #include "dell-smbios.h" -#define BRIGHTNESS_TOKEN 0x7d -#define KBD_LED_OFF_TOKEN 0x01E1 -#define KBD_LED_ON_TOKEN 0x01E2 -#define KBD_LED_AUTO_TOKEN 0x01E3 -#define KBD_LED_AUTO_25_TOKEN 0x02EA -#define KBD_LED_AUTO_50_TOKEN 0x02EB -#define KBD_LED_AUTO_75_TOKEN 0x02EC -#define KBD_LED_AUTO_100_TOKEN 0x02F6 -#define GLOBAL_MIC_MUTE_ENABLE 0x0364 -#define GLOBAL_MIC_MUTE_DISABLE 0x0365 -#define KBD_LED_AC_TOKEN 0x0451 - struct quirk_entry { u8 touchpad_led; @@ -85,6 +73,7 @@ static struct platform_driver platform_driver = { } }; +static struct calling_interface_buffer *buffer; static struct platform_device *platform_device; static struct backlight_device *dell_backlight_device; static struct rfkill *wifi_rfkill; @@ -283,6 +272,27 @@ static const struct dmi_system_id dell_quirks[] __initconst = { { } }; +void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3) +{ + memset(buffer, 0, sizeof(struct calling_interface_buffer)); + buffer->input[0] = arg0; + buffer->input[1] = arg1; + buffer->input[2] = arg2; + buffer->input[3] = arg3; +} + +int dell_send_request(u16 class, u16 select) +{ + int ret; + + buffer->cmd_class = class; + buffer->cmd_select = select; + ret = dell_smbios_call(buffer); + if (ret != 0) + return ret; + return dell_smbios_error(buffer->output[0]); +} + /* * Derived from information in smbios-wireless-ctl: * @@ -405,7 +415,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = { static int dell_rfkill_set(void *data, bool blocked) { - struct calling_interface_buffer *buffer; int disable = blocked ? 1 : 0; unsigned long radio = (unsigned long)data; int hwswitch_bit = (unsigned long)data - 1; @@ -413,20 +422,16 @@ static int dell_rfkill_set(void *data, bool blocked) int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (ret) + return ret; status = buffer->output[1]; - if (ret != 0) - goto out; - - dell_smbios_clear_buffer(); - - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0x2, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (ret) + return ret; hwswitch = buffer->output[1]; /* If the hardware switch controls this radio, and the hardware @@ -435,28 +440,19 @@ static int dell_rfkill_set(void *data, bool blocked) (status & BIT(0)) && !(status & BIT(16))) disable = 1; - dell_smbios_clear_buffer(); - - buffer->input[0] = (1 | (radio<<8) | (disable << 16)); - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; - - out: - dell_smbios_release_buffer(); - return dell_smbios_error(ret); + dell_set_arguments(1 | (radio<<8) | (disable << 16), 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + return ret; } -/* Must be called with the buffer held */ static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio, - int status, - struct calling_interface_buffer *buffer) + int status) { if (status & BIT(0)) { /* Has hw-switch, sync sw_state to BIOS */ int block = rfkill_blocked(rfkill); - dell_smbios_clear_buffer(); - buffer->input[0] = (1 | (radio << 8) | (block << 16)); - dell_smbios_send_request(17, 11); + dell_set_arguments(1 | (radio << 8) | (block << 16), 0, 0, 0); + dell_send_request(CLASS_INFO, SELECT_RFKILL); } else { /* No hw-switch, sync BIOS state to sw_state */ rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16))); @@ -472,32 +468,23 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio, static void dell_rfkill_query(struct rfkill *rfkill, void *data) { - struct calling_interface_buffer *buffer; int radio = ((unsigned long)data & 0xF); int hwswitch; int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); status = buffer->output[1]; if (ret != 0 || !(status & BIT(0))) { - dell_smbios_release_buffer(); return; } - dell_smbios_clear_buffer(); - - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0x2, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); hwswitch = buffer->output[1]; - dell_smbios_release_buffer(); - if (ret != 0) return; @@ -513,27 +500,23 @@ static struct dentry *dell_laptop_dir; static int dell_debugfs_show(struct seq_file *s, void *data) { - struct calling_interface_buffer *buffer; int hwswitch_state; int hwswitch_ret; int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (ret) + return ret; status = buffer->output[1]; - dell_smbios_clear_buffer(); - - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - hwswitch_ret = buffer->output[0]; + dell_set_arguments(0, 0x2, 0, 0); + hwswitch_ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (hwswitch_ret) + return hwswitch_ret; hwswitch_state = buffer->output[1]; - dell_smbios_release_buffer(); - seq_printf(s, "return:\t%d\n", ret); seq_printf(s, "status:\t0x%X\n", status); seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", @@ -613,46 +596,36 @@ static const struct file_operations dell_debugfs_fops = { static void dell_update_rfkill(struct work_struct *ignored) { - struct calling_interface_buffer *buffer; int hwswitch = 0; int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); status = buffer->output[1]; if (ret != 0) - goto out; - - dell_smbios_clear_buffer(); + return; - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0x2, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); if (ret == 0 && (status & BIT(0))) hwswitch = buffer->output[1]; if (wifi_rfkill) { dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch); - dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer); + dell_rfkill_update_sw_state(wifi_rfkill, 1, status); } if (bluetooth_rfkill) { dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status, hwswitch); - dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status, - buffer); + dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status); } if (wwan_rfkill) { dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch); - dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer); + dell_rfkill_update_sw_state(wwan_rfkill, 3, status); } - - out: - dell_smbios_release_buffer(); } static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); @@ -696,7 +669,6 @@ static struct notifier_block dell_laptop_rbtn_notifier = { static int __init dell_setup_rfkill(void) { - struct calling_interface_buffer *buffer; int status, ret, whitelisted; const char *product; @@ -712,11 +684,9 @@ static int __init dell_setup_rfkill(void) if (!force_rfkill && !whitelisted) return 0; - buffer = dell_smbios_get_buffer(); - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); status = buffer->output[1]; - dell_smbios_release_buffer(); /* dell wireless info smbios call is not supported */ if (ret != 0) @@ -869,7 +839,6 @@ static void dell_cleanup_rfkill(void) static int dell_send_intensity(struct backlight_device *bd) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; @@ -877,24 +846,17 @@ static int dell_send_intensity(struct backlight_device *bd) if (!token) return -ENODEV; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - buffer->input[1] = bd->props.brightness; - + dell_set_arguments(token->location, bd->props.brightness, 0, 0); if (power_supply_is_system_supplied() > 0) - dell_smbios_send_request(1, 2); + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_AC); else - dell_smbios_send_request(1, 1); - - ret = dell_smbios_error(buffer->output[0]); + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT); - dell_smbios_release_buffer(); return ret; } static int dell_get_intensity(struct backlight_device *bd) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; @@ -902,20 +864,14 @@ static int dell_get_intensity(struct backlight_device *bd) if (!token) return -ENODEV; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - + dell_set_arguments(token->location, 0, 0, 0); if (power_supply_is_system_supplied() > 0) - dell_smbios_send_request(0, 2); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC); else - dell_smbios_send_request(0, 1); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_BAT); - if (buffer->output[0]) - ret = dell_smbios_error(buffer->output[0]); - else + if (ret == 0) ret = buffer->output[1]; - - dell_smbios_release_buffer(); return ret; } @@ -1179,20 +1135,13 @@ static DEFINE_MUTEX(kbd_led_mutex); static int kbd_get_info(struct kbd_info *info) { - struct calling_interface_buffer *buffer; u8 units; int ret; - buffer = dell_smbios_get_buffer(); - - buffer->input[0] = 0x0; - dell_smbios_send_request(4, 11); - ret = buffer->output[0]; - - if (ret) { - ret = dell_smbios_error(ret); - goto out; - } + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); + if (ret) + return ret; info->modes = buffer->output[1] & 0xFFFF; info->type = (buffer->output[1] >> 24) & 0xFF; @@ -1209,8 +1158,6 @@ static int kbd_get_info(struct kbd_info *info) if (units & BIT(3)) info->days = (buffer->output[3] >> 24) & 0xFF; - out: - dell_smbios_release_buffer(); return ret; } @@ -1269,19 +1216,12 @@ static int kbd_set_level(struct kbd_state *state, u8 level) static int kbd_get_state(struct kbd_state *state) { - struct calling_interface_buffer *buffer; int ret; - buffer = dell_smbios_get_buffer(); - - buffer->input[0] = 0x1; - dell_smbios_send_request(4, 11); - ret = buffer->output[0]; - - if (ret) { - ret = dell_smbios_error(ret); - goto out; - } + dell_set_arguments(0x1, 0, 0, 0); + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); + if (ret) + return ret; state->mode_bit = ffs(buffer->output[1] & 0xFFFF); if (state->mode_bit != 0) @@ -1296,31 +1236,27 @@ static int kbd_get_state(struct kbd_state *state) state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F; state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3; - out: - dell_smbios_release_buffer(); return ret; } static int kbd_set_state(struct kbd_state *state) { - struct calling_interface_buffer *buffer; int ret; + u32 input1; + u32 input2; + + input1 = BIT(state->mode_bit) & 0xFFFF; + input1 |= (state->triggers & 0xFF) << 16; + input1 |= (state->timeout_value & 0x3F) << 24; + input1 |= (state->timeout_unit & 0x3) << 30; + input2 = state->als_setting & 0xFF; + input2 |= (state->level & 0xFF) << 16; + input2 |= (state->timeout_value_ac & 0x3F) << 24; + input2 |= (state->timeout_unit_ac & 0x3) << 30; + dell_set_arguments(0x2, input1, input2, 0); + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); - buffer = dell_smbios_get_buffer(); - buffer->input[0] = 0x2; - buffer->input[1] = BIT(state->mode_bit) & 0xFFFF; - buffer->input[1] |= (state->triggers & 0xFF) << 16; - buffer->input[1] |= (state->timeout_value & 0x3F) << 24; - buffer->input[1] |= (state->timeout_unit & 0x3) << 30; - buffer->input[2] = state->als_setting & 0xFF; - buffer->input[2] |= (state->level & 0xFF) << 16; - buffer->input[2] |= (state->timeout_value_ac & 0x3F) << 24; - buffer->input[2] |= (state->timeout_unit_ac & 0x3) << 30; - dell_smbios_send_request(4, 11); - ret = buffer->output[0]; - dell_smbios_release_buffer(); - - return dell_smbios_error(ret); + return ret; } static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) @@ -1345,7 +1281,6 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) static int kbd_set_token_bit(u8 bit) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; @@ -1356,19 +1291,14 @@ static int kbd_set_token_bit(u8 bit) if (!token) return -EINVAL; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - buffer->input[1] = token->value; - dell_smbios_send_request(1, 0); - ret = buffer->output[0]; - dell_smbios_release_buffer(); + dell_set_arguments(token->location, token->value, 0, 0); + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); - return dell_smbios_error(ret); + return ret; } static int kbd_get_token_bit(u8 bit) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; int val; @@ -1380,15 +1310,12 @@ static int kbd_get_token_bit(u8 bit) if (!token) return -EINVAL; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - dell_smbios_send_request(0, 0); - ret = buffer->output[0]; + dell_set_arguments(token->location, 0, 0, 0); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_STD); val = buffer->output[1]; - dell_smbios_release_buffer(); if (ret) - return dell_smbios_error(ret); + return ret; return (val == token->value); } @@ -2102,7 +2029,6 @@ static struct notifier_block dell_laptop_notifier = { int dell_micmute_led_set(int state) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; if (state == 0) @@ -2115,11 +2041,8 @@ int dell_micmute_led_set(int state) if (!token) return -ENODEV; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - buffer->input[1] = token->value; - dell_smbios_send_request(1, 0); - dell_smbios_release_buffer(); + dell_set_arguments(token->location, token->value, 0, 0); + dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); return state; } @@ -2127,7 +2050,6 @@ EXPORT_SYMBOL_GPL(dell_micmute_led_set); static int __init dell_init(void) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int max_intensity = 0; int ret; @@ -2151,6 +2073,13 @@ static int __init dell_init(void) if (ret) goto fail_platform_device2; + buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto fail_buffer; + } + + ret = dell_setup_rfkill(); if (ret) { @@ -2175,12 +2104,10 @@ static int __init dell_init(void) token = dell_smbios_find_token(BRIGHTNESS_TOKEN); if (token) { - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - dell_smbios_send_request(0, 2); - if (buffer->output[0] == 0) + dell_set_arguments(token->location, 0, 0, 0); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC); + if (ret) max_intensity = buffer->output[3]; - dell_smbios_release_buffer(); } if (max_intensity) { @@ -2214,6 +2141,8 @@ static int __init dell_init(void) fail_get_brightness: backlight_device_unregister(dell_backlight_device); fail_backlight: + kfree(buffer); +fail_buffer: dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); @@ -2233,6 +2162,7 @@ static void __exit dell_exit(void) touchpad_led_exit(); kbd_led_exit(); backlight_device_unregister(dell_backlight_device); + kfree(buffer); dell_cleanup_rfkill(); if (platform_device) { platform_device_unregister(platform_device); diff --git a/drivers/platform/x86/dell-smbios-smm.c b/drivers/platform/x86/dell-smbios-smm.c new file mode 100644 index 000000000000..89f65c4651a0 --- /dev/null +++ b/drivers/platform/x86/dell-smbios-smm.c @@ -0,0 +1,196 @@ +/* + * SMI methods for use with dell-smbios + * + * Copyright (c) Red Hat + * Copyright (c) 2014 Gabriele Mazzotta + * Copyright (c) 2014 Pali Rohár + * Copyright (c) 2017 Dell Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "../../firmware/dcdbas.h" +#include "dell-smbios.h" + +static int da_command_address; +static int da_command_code; +static struct calling_interface_buffer *buffer; +struct platform_device *platform_device; +static DEFINE_MUTEX(smm_mutex); + +static const struct dmi_system_id dell_device_table[] __initconst = { + { + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ + }, + }, + { + .ident = "Dell Computer Corporation", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), + }, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, dell_device_table); + +static void __init parse_da_table(const struct dmi_header *dm) +{ + struct calling_interface_structure *table = + container_of(dm, struct calling_interface_structure, header); + + /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least + * 6 bytes of entry + */ + if (dm->length < 17) + return; + + da_command_address = table->cmdIOAddress; + da_command_code = table->cmdIOCode; +} + +static void __init find_cmd_address(const struct dmi_header *dm, void *dummy) +{ + switch (dm->type) { + case 0xda: /* Calling interface */ + parse_da_table(dm); + break; + } +} + +int dell_smbios_smm_call(struct calling_interface_buffer *input) +{ + struct smi_cmd command; + size_t size; + + size = sizeof(struct calling_interface_buffer); + command.magic = SMI_CMD_MAGIC; + command.command_address = da_command_address; + command.command_code = da_command_code; + command.ebx = virt_to_phys(buffer); + command.ecx = 0x42534931; + + mutex_lock(&smm_mutex); + memcpy(buffer, input, size); + dcdbas_smi_request(&command); + memcpy(input, buffer, size); + mutex_unlock(&smm_mutex); + return 0; +} + +/* When enabled this indicates that SMM won't work */ +static bool test_wsmt_enabled(void) +{ + struct calling_interface_token *wsmt; + + /* if token doesn't exist, SMM will work */ + wsmt = dell_smbios_find_token(WSMT_EN_TOKEN); + if (!wsmt) + return false; + + /* If token exists, try to access over SMM but set a dummy return. + * - If WSMT disabled it will be overwritten by SMM + * - If WSMT enabled then dummy value will remain + */ + buffer->cmd_class = CLASS_TOKEN_READ; + buffer->cmd_select = SELECT_TOKEN_STD; + memset(buffer, 0, sizeof(struct calling_interface_buffer)); + buffer->input[0] = wsmt->location; + buffer->output[0] = 99; + dell_smbios_smm_call(buffer); + if (buffer->output[0] == 99) + return true; + + return false; +} + +static int __init dell_smbios_smm_init(void) +{ + int ret; + /* + * Allocate buffer below 4GB for SMI data--only 32-bit physical addr + * is passed to SMI handler. + */ + buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); + if (!buffer) + return -ENOMEM; + + dmi_walk(find_cmd_address, NULL); + + if (test_wsmt_enabled()) { + pr_debug("Disabling due to WSMT enabled\n"); + ret = -ENODEV; + goto fail_wsmt; + } + + platform_device = platform_device_alloc("dell-smbios", 1); + if (!platform_device) { + ret = -ENOMEM; + goto fail_platform_device_alloc; + } + + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device_add; + + ret = dell_smbios_register_device(&platform_device->dev, + &dell_smbios_smm_call); + if (ret) + goto fail_register; + + return 0; + +fail_register: + platform_device_del(platform_device); + +fail_platform_device_add: + platform_device_put(platform_device); + +fail_wsmt: +fail_platform_device_alloc: + free_page((unsigned long)buffer); + return ret; +} + +static void __exit dell_smbios_smm_exit(void) +{ + if (platform_device) { + dell_smbios_unregister_device(&platform_device->dev); + platform_device_unregister(platform_device); + free_page((unsigned long)buffer); + } +} + +subsys_initcall(dell_smbios_smm_init); +module_exit(dell_smbios_smm_exit); + +MODULE_AUTHOR("Matthew Garrett "); +MODULE_AUTHOR("Gabriele Mazzotta "); +MODULE_AUTHOR("Pali Rohár "); +MODULE_AUTHOR("Mario Limonciello "); +MODULE_DESCRIPTION("Dell SMBIOS communications over SMI"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c new file mode 100644 index 000000000000..609557aa5868 --- /dev/null +++ b/drivers/platform/x86/dell-smbios-wmi.c @@ -0,0 +1,285 @@ +/* + * WMI methods for use with dell-smbios + * + * Copyright (c) 2017 Dell Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "dell-smbios.h" +#include "dell-wmi-descriptor.h" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); +static int wmi_supported; + +struct misc_bios_flags_structure { + struct dmi_header header; + u16 flags0; +} __packed; +#define FLAG_HAS_ACPI_WMI 0x02 + +#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" + +struct wmi_smbios_priv { + struct dell_wmi_smbios_buffer *buf; + struct list_head list; + struct wmi_device *wdev; + struct device *child; + u32 req_buf_size; +}; +static LIST_HEAD(wmi_list); + +static inline struct wmi_smbios_priv *get_first_smbios_priv(void) +{ + return list_first_entry_or_null(&wmi_list, + struct wmi_smbios_priv, + list); +} + +static int run_smbios_call(struct wmi_device *wdev) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + struct wmi_smbios_priv *priv; + struct acpi_buffer input; + union acpi_object *obj; + acpi_status status; + + priv = dev_get_drvdata(&wdev->dev); + input.length = priv->req_buf_size - sizeof(u64); + input.pointer = &priv->buf->std; + + dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n", + priv->buf->std.cmd_class, priv->buf->std.cmd_select, + priv->buf->std.input[0], priv->buf->std.input[1], + priv->buf->std.input[2], priv->buf->std.input[3]); + + status = wmidev_evaluate_method(wdev, 0, 1, &input, &output); + if (ACPI_FAILURE(status)) + return -EIO; + obj = (union acpi_object *)output.pointer; + if (obj->type != ACPI_TYPE_BUFFER) { + dev_dbg(&wdev->dev, "received type: %d\n", obj->type); + if (obj->type == ACPI_TYPE_INTEGER) + dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n", + obj->integer.value); + return -EIO; + } + memcpy(&priv->buf->std, obj->buffer.pointer, obj->buffer.length); + dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n", + priv->buf->std.output[0], priv->buf->std.output[1], + priv->buf->std.output[2], priv->buf->std.output[3]); + + return 0; +} + +int dell_smbios_wmi_call(struct calling_interface_buffer *buffer) +{ + struct wmi_smbios_priv *priv; + size_t difference; + size_t size; + int ret; + + mutex_lock(&call_mutex); + priv = get_first_smbios_priv(); + if (!priv) { + ret = -ENODEV; + goto out_wmi_call; + } + + size = sizeof(struct calling_interface_buffer); + difference = priv->req_buf_size - sizeof(u64) - size; + + memset(&priv->buf->ext, 0, difference); + memcpy(&priv->buf->std, buffer, size); + ret = run_smbios_call(priv->wdev); + memcpy(buffer, &priv->buf->std, size); +out_wmi_call: + mutex_unlock(&call_mutex); + + return ret; +} + +static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd, + struct wmi_ioctl_buffer *arg) +{ + struct wmi_smbios_priv *priv; + int ret = 0; + + switch (cmd) { + case DELL_WMI_SMBIOS_CMD: + mutex_lock(&call_mutex); + priv = dev_get_drvdata(&wdev->dev); + if (!priv) { + ret = -ENODEV; + goto fail_smbios_cmd; + } + memcpy(priv->buf, arg, priv->req_buf_size); + if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) { + dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n", + priv->buf->std.cmd_class, + priv->buf->std.cmd_select, + priv->buf->std.input[0]); + ret = -EFAULT; + goto fail_smbios_cmd; + } + ret = run_smbios_call(priv->wdev); + if (ret) + goto fail_smbios_cmd; + memcpy(arg, priv->buf, priv->req_buf_size); +fail_smbios_cmd: + mutex_unlock(&call_mutex); + break; + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static int dell_smbios_wmi_probe(struct wmi_device *wdev) +{ + struct wmi_driver *wdriver = + container_of(wdev->dev.driver, struct wmi_driver, driver); + struct wmi_smbios_priv *priv; + u32 hotfix; + int count; + int ret; + + ret = dell_wmi_get_descriptor_valid(); + if (ret) + return ret; + + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* WMI buffer size will be either 4k or 32k depending on machine */ + if (!dell_wmi_get_size(&priv->req_buf_size)) + return -EPROBE_DEFER; + + /* some SMBIOS calls fail unless BIOS contains hotfix */ + if (!dell_wmi_get_hotfix(&hotfix)) + return -EPROBE_DEFER; + if (!hotfix) { + dev_warn(&wdev->dev, + "WMI SMBIOS userspace interface not supported(%u), try upgrading to a newer BIOS\n", + hotfix); + wdriver->filter_callback = NULL; + } + + /* add in the length object we will use internally with ioctl */ + priv->req_buf_size += sizeof(u64); + ret = set_required_buffer_size(wdev, priv->req_buf_size); + if (ret) + return ret; + + count = get_order(priv->req_buf_size); + priv->buf = (void *)__get_free_pages(GFP_KERNEL, count); + if (!priv->buf) + return -ENOMEM; + + /* ID is used by dell-smbios to set priority of drivers */ + wdev->dev.id = 1; + ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call); + if (ret) + goto fail_register; + + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + mutex_lock(&list_mutex); + list_add_tail(&priv->list, &wmi_list); + mutex_unlock(&list_mutex); + + return 0; + +fail_register: + free_pages((unsigned long)priv->buf, count); + return ret; +} + +static int dell_smbios_wmi_remove(struct wmi_device *wdev) +{ + struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev); + int count; + + mutex_lock(&call_mutex); + mutex_lock(&list_mutex); + list_del(&priv->list); + mutex_unlock(&list_mutex); + dell_smbios_unregister_device(&wdev->dev); + count = get_order(priv->req_buf_size); + free_pages((unsigned long)priv->buf, count); + mutex_unlock(&call_mutex); + return 0; +} + +static const struct wmi_device_id dell_smbios_wmi_id_table[] = { + { .guid_string = DELL_WMI_SMBIOS_GUID }, + { }, +}; + +static void __init parse_b1_table(const struct dmi_header *dm) +{ + struct misc_bios_flags_structure *flags = + container_of(dm, struct misc_bios_flags_structure, header); + + /* 4 bytes header, 8 bytes flags */ + if (dm->length < 12) + return; + if (dm->handle != 0xb100) + return; + if ((flags->flags0 & FLAG_HAS_ACPI_WMI)) + wmi_supported = 1; +} + +static void __init find_b1(const struct dmi_header *dm, void *dummy) +{ + switch (dm->type) { + case 0xb1: /* misc bios flags */ + parse_b1_table(dm); + break; + } +} + +static struct wmi_driver dell_smbios_wmi_driver = { + .driver = { + .name = "dell-smbios", + }, + .probe = dell_smbios_wmi_probe, + .remove = dell_smbios_wmi_remove, + .id_table = dell_smbios_wmi_id_table, + .filter_callback = dell_smbios_wmi_filter, +}; + +static int __init init_dell_smbios_wmi(void) +{ + dmi_walk(find_b1, NULL); + + if (!wmi_supported) + return -ENODEV; + + return wmi_driver_register(&dell_smbios_wmi_driver); +} + +static void __exit exit_dell_smbios_wmi(void) +{ + wmi_driver_unregister(&dell_smbios_wmi_driver); +} + +module_init(init_dell_smbios_wmi); +module_exit(exit_dell_smbios_wmi); + +MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID); +MODULE_AUTHOR("Mario Limonciello "); +MODULE_DESCRIPTION("Dell SMBIOS communications over WMI"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c index 0a5723468bff..6a60db515bda 100644 --- a/drivers/platform/x86/dell-smbios.c +++ b/drivers/platform/x86/dell-smbios.c @@ -12,33 +12,119 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include +#include #include #include -#include #include +#include #include -#include -#include "../../firmware/dcdbas.h" #include "dell-smbios.h" -struct calling_interface_structure { - struct dmi_header header; - u16 cmdIOAddress; - u8 cmdIOCode; - u32 supportedCmds; - struct calling_interface_token tokens[]; -} __packed; - -static struct calling_interface_buffer *buffer; -static DEFINE_MUTEX(buffer_mutex); - -static int da_command_address; -static int da_command_code; +static u32 da_supported_commands; static int da_num_tokens; +static struct platform_device *platform_device; static struct calling_interface_token *da_tokens; +static struct device_attribute *token_location_attrs; +static struct device_attribute *token_value_attrs; +static struct attribute **token_attrs; +static DEFINE_MUTEX(smbios_mutex); + +struct smbios_device { + struct list_head list; + struct device *device; + int (*call_fn)(struct calling_interface_buffer *); +}; + +struct smbios_call { + u32 need_capability; + int cmd_class; + int cmd_select; +}; + +/* calls that are whitelisted for given capabilities */ +static struct smbios_call call_whitelist[] = { + /* generally tokens are allowed, but may be further filtered or + * restricted by token blacklist or whitelist + */ + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD}, + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC}, + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT}, + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD}, + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC}, + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT}, + /* used by userspace: fwupdate */ + {CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP}, + /* used by userspace: fwupd */ + {CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK}, + {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE}, +}; + +/* calls that are explicitly blacklisted */ +static struct smbios_call call_blacklist[] = { + {0x0000, 01, 07}, /* manufacturing use */ + {0x0000, 06, 05}, /* manufacturing use */ + {0x0000, 11, 03}, /* write once */ + {0x0000, 11, 07}, /* write once */ + {0x0000, 11, 11}, /* write once */ + {0x0000, 19, -1}, /* diagnostics */ + /* handled by kernel: dell-laptop */ + {0x0000, CLASS_INFO, SELECT_RFKILL}, + {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT}, +}; + +struct token_range { + u32 need_capability; + u16 min; + u16 max; +}; + +/* tokens that are whitelisted for given capabilities */ +static struct token_range token_whitelist[] = { + /* used by userspace: fwupdate */ + {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN}, + /* can indicate to userspace that WMI is needed */ + {0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN} +}; + +/* tokens that are explicitly blacklisted */ +static struct token_range token_blacklist[] = { + {0x0000, 0x0058, 0x0059}, /* ME use */ + {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */ + {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */ + {0x0000, 0x0175, 0x0176}, /* write once */ + {0x0000, 0x0195, 0x0197}, /* diagnostics */ + {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */ + {0x0000, 0x027D, 0x0284}, /* diagnostics */ + {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */ + {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */ + {0x0000, 0x0300, 0x0302}, /* manufacturing use */ + {0x0000, 0x0325, 0x0326}, /* manufacturing use */ + {0x0000, 0x0332, 0x0335}, /* fan control */ + {0x0000, 0x0350, 0x0350}, /* manufacturing use */ + {0x0000, 0x0363, 0x0363}, /* manufacturing use */ + {0x0000, 0x0368, 0x0368}, /* manufacturing use */ + {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */ + {0x0000, 0x049E, 0x049F}, /* manufacturing use */ + {0x0000, 0x04A0, 0x04A3}, /* disagnostics */ + {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */ + {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */ + {0x0000, 0x9000, 0x9001}, /* internal BIOS use */ + {0x0000, 0xA000, 0xBFFF}, /* write only */ + {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */ + /* handled by kernel: dell-laptop */ + {0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN}, + {0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN}, + {0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN}, + {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN}, + {0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN}, + {0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE}, +}; + +static LIST_HEAD(smbios_device_list); int dell_smbios_error(int value) { @@ -55,42 +141,175 @@ int dell_smbios_error(int value) } EXPORT_SYMBOL_GPL(dell_smbios_error); -struct calling_interface_buffer *dell_smbios_get_buffer(void) +int dell_smbios_register_device(struct device *d, void *call_fn) { - mutex_lock(&buffer_mutex); - dell_smbios_clear_buffer(); - return buffer; + struct smbios_device *priv; + + priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL); + if (!priv) + return -ENOMEM; + get_device(d); + priv->device = d; + priv->call_fn = call_fn; + mutex_lock(&smbios_mutex); + list_add_tail(&priv->list, &smbios_device_list); + mutex_unlock(&smbios_mutex); + dev_dbg(d, "Added device: %s\n", d->driver->name); + return 0; } -EXPORT_SYMBOL_GPL(dell_smbios_get_buffer); +EXPORT_SYMBOL_GPL(dell_smbios_register_device); -void dell_smbios_clear_buffer(void) +void dell_smbios_unregister_device(struct device *d) { - memset(buffer, 0, sizeof(struct calling_interface_buffer)); + struct smbios_device *priv; + + mutex_lock(&smbios_mutex); + list_for_each_entry(priv, &smbios_device_list, list) { + if (priv->device == d) { + list_del(&priv->list); + put_device(d); + break; + } + } + mutex_unlock(&smbios_mutex); + dev_dbg(d, "Remove device: %s\n", d->driver->name); } -EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer); +EXPORT_SYMBOL_GPL(dell_smbios_unregister_device); -void dell_smbios_release_buffer(void) +int dell_smbios_call_filter(struct device *d, + struct calling_interface_buffer *buffer) { - mutex_unlock(&buffer_mutex); + u16 t = 0; + int i; + + /* can't make calls over 30 */ + if (buffer->cmd_class > 30) { + dev_dbg(d, "class too big: %u\n", buffer->cmd_class); + return -EINVAL; + } + + /* supported calls on the particular system */ + if (!(da_supported_commands & (1 << buffer->cmd_class))) { + dev_dbg(d, "invalid command, supported commands: 0x%8x\n", + da_supported_commands); + return -EINVAL; + } + + /* match against call blacklist */ + for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) { + if (buffer->cmd_class != call_blacklist[i].cmd_class) + continue; + if (buffer->cmd_select != call_blacklist[i].cmd_select && + call_blacklist[i].cmd_select != -1) + continue; + dev_dbg(d, "blacklisted command: %u/%u\n", + buffer->cmd_class, buffer->cmd_select); + return -EINVAL; + } + + /* if a token call, find token ID */ + + if ((buffer->cmd_class == CLASS_TOKEN_READ || + buffer->cmd_class == CLASS_TOKEN_WRITE) && + buffer->cmd_select < 3) { + /* find the matching token ID */ + for (i = 0; i < da_num_tokens; i++) { + if (da_tokens[i].location != buffer->input[0]) + continue; + t = da_tokens[i].tokenID; + break; + } + + /* token call; but token didn't exist */ + if (!t) { + dev_dbg(d, "token at location %04x doesn't exist\n", + buffer->input[0]); + return -EINVAL; + } + + /* match against token blacklist */ + for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) { + if (!token_blacklist[i].min || !token_blacklist[i].max) + continue; + if (t >= token_blacklist[i].min && + t <= token_blacklist[i].max) + return -EINVAL; + } + + /* match against token whitelist */ + for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) { + if (!token_whitelist[i].min || !token_whitelist[i].max) + continue; + if (t < token_whitelist[i].min || + t > token_whitelist[i].max) + continue; + if (!token_whitelist[i].need_capability || + capable(token_whitelist[i].need_capability)) { + dev_dbg(d, "whitelisted token: %x\n", t); + return 0; + } + + } + } + /* match against call whitelist */ + for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) { + if (buffer->cmd_class != call_whitelist[i].cmd_class) + continue; + if (buffer->cmd_select != call_whitelist[i].cmd_select) + continue; + if (!call_whitelist[i].need_capability || + capable(call_whitelist[i].need_capability)) { + dev_dbg(d, "whitelisted capable command: %u/%u\n", + buffer->cmd_class, buffer->cmd_select); + return 0; + } + dev_dbg(d, "missing capability %d for %u/%u\n", + call_whitelist[i].need_capability, + buffer->cmd_class, buffer->cmd_select); + + } + + /* not in a whitelist, only allow processes with capabilities */ + if (capable(CAP_SYS_RAWIO)) { + dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n", + buffer->cmd_class, buffer->cmd_select); + return 0; + } + + return -EACCES; } -EXPORT_SYMBOL_GPL(dell_smbios_release_buffer); +EXPORT_SYMBOL_GPL(dell_smbios_call_filter); -void dell_smbios_send_request(int class, int select) +int dell_smbios_call(struct calling_interface_buffer *buffer) { - struct smi_cmd command; + int (*call_fn)(struct calling_interface_buffer *) = NULL; + struct device *selected_dev = NULL; + struct smbios_device *priv; + int ret; - command.magic = SMI_CMD_MAGIC; - command.command_address = da_command_address; - command.command_code = da_command_code; - command.ebx = virt_to_phys(buffer); - command.ecx = 0x42534931; + mutex_lock(&smbios_mutex); + list_for_each_entry(priv, &smbios_device_list, list) { + if (!selected_dev || priv->device->id >= selected_dev->id) { + dev_dbg(priv->device, "Trying device ID: %d\n", + priv->device->id); + call_fn = priv->call_fn; + selected_dev = priv->device; + } + } + + if (!selected_dev) { + ret = -ENODEV; + pr_err("No dell-smbios drivers are loaded\n"); + goto out_smbios_call; + } - buffer->class = class; - buffer->select = select; + ret = call_fn(buffer); - dcdbas_smi_request(&command); +out_smbios_call: + mutex_unlock(&smbios_mutex); + return ret; } -EXPORT_SYMBOL_GPL(dell_smbios_send_request); +EXPORT_SYMBOL_GPL(dell_smbios_call); struct calling_interface_token *dell_smbios_find_token(int tokenid) { @@ -139,8 +358,7 @@ static void __init parse_da_table(const struct dmi_header *dm) if (dm->length < 17) return; - da_command_address = table->cmdIOAddress; - da_command_code = table->cmdIOCode; + da_supported_commands = table->supportedCmds; new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * sizeof(struct calling_interface_token), @@ -156,6 +374,27 @@ static void __init parse_da_table(const struct dmi_header *dm) da_num_tokens += tokens; } +static void zero_duplicates(struct device *dev) +{ + int i, j; + + for (i = 0; i < da_num_tokens; i++) { + if (da_tokens[i].tokenID == 0) + continue; + for (j = i+1; j < da_num_tokens; j++) { + if (da_tokens[j].tokenID == 0) + continue; + if (da_tokens[i].tokenID == da_tokens[j].tokenID) { + dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n", + da_tokens[j].tokenID, + da_tokens[j].location, + da_tokens[j].value); + da_tokens[j].tokenID = 0; + } + } + } +} + static void __init find_tokens(const struct dmi_header *dm, void *dummy) { switch (dm->type) { @@ -169,10 +408,160 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) } } +static int match_attribute(struct device *dev, + struct device_attribute *attr) +{ + int i; + + for (i = 0; i < da_num_tokens * 2; i++) { + if (!token_attrs[i]) + continue; + if (strcmp(token_attrs[i]->name, attr->attr.name) == 0) + return i/2; + } + dev_dbg(dev, "couldn't match: %s\n", attr->attr.name); + return -EINVAL; +} + +static ssize_t location_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + i = match_attribute(dev, attr); + if (i > 0) + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location); + return 0; +} + +static ssize_t value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + i = match_attribute(dev, attr); + if (i > 0) + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value); + return 0; +} + +static struct attribute_group smbios_attribute_group = { + .name = "tokens" +}; + +static struct platform_driver platform_driver = { + .driver = { + .name = "dell-smbios", + }, +}; + +static int build_tokens_sysfs(struct platform_device *dev) +{ + char *location_name; + char *value_name; + size_t size; + int ret; + int i, j; + + /* (number of tokens + 1 for null terminated */ + size = sizeof(struct device_attribute) * (da_num_tokens + 1); + token_location_attrs = kzalloc(size, GFP_KERNEL); + if (!token_location_attrs) + return -ENOMEM; + token_value_attrs = kzalloc(size, GFP_KERNEL); + if (!token_value_attrs) + goto out_allocate_value; + + /* need to store both location and value + terminator*/ + size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1); + token_attrs = kzalloc(size, GFP_KERNEL); + if (!token_attrs) + goto out_allocate_attrs; + + for (i = 0, j = 0; i < da_num_tokens; i++) { + /* skip empty */ + if (da_tokens[i].tokenID == 0) + continue; + /* add location */ + location_name = kasprintf(GFP_KERNEL, "%04x_location", + da_tokens[i].tokenID); + if (location_name == NULL) + goto out_unwind_strings; + sysfs_attr_init(&token_location_attrs[i].attr); + token_location_attrs[i].attr.name = location_name; + token_location_attrs[i].attr.mode = 0444; + token_location_attrs[i].show = location_show; + token_attrs[j++] = &token_location_attrs[i].attr; + + /* add value */ + value_name = kasprintf(GFP_KERNEL, "%04x_value", + da_tokens[i].tokenID); + if (value_name == NULL) + goto loop_fail_create_value; + sysfs_attr_init(&token_value_attrs[i].attr); + token_value_attrs[i].attr.name = value_name; + token_value_attrs[i].attr.mode = 0444; + token_value_attrs[i].show = value_show; + token_attrs[j++] = &token_value_attrs[i].attr; + continue; + +loop_fail_create_value: + kfree(value_name); + goto out_unwind_strings; + } + smbios_attribute_group.attrs = token_attrs; + + ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group); + if (ret) + goto out_unwind_strings; + return 0; + +out_unwind_strings: + for (i = i-1; i > 0; i--) { + kfree(token_location_attrs[i].attr.name); + kfree(token_value_attrs[i].attr.name); + } + kfree(token_attrs); +out_allocate_attrs: + kfree(token_value_attrs); +out_allocate_value: + kfree(token_location_attrs); + + return -ENOMEM; +} + +static void free_group(struct platform_device *pdev) +{ + int i; + + sysfs_remove_group(&pdev->dev.kobj, + &smbios_attribute_group); + for (i = 0; i < da_num_tokens; i++) { + kfree(token_location_attrs[i].attr.name); + kfree(token_value_attrs[i].attr.name); + } + kfree(token_attrs); + kfree(token_value_attrs); + kfree(token_location_attrs); +} + static int __init dell_smbios_init(void) { + const struct dmi_device *valid; int ret; + valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL); + if (!valid) { + pr_err("Unable to run on non-Dell system\n"); + return -ENODEV; + } + dmi_walk(find_tokens, NULL); if (!da_tokens) { @@ -180,27 +569,52 @@ static int __init dell_smbios_init(void) return -ENODEV; } - /* - * Allocate buffer below 4GB for SMI data--only 32-bit physical addr - * is passed to SMI handler. - */ - buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); - if (!buffer) { + ret = platform_driver_register(&platform_driver); + if (ret) + goto fail_platform_driver; + + platform_device = platform_device_alloc("dell-smbios", 0); + if (!platform_device) { ret = -ENOMEM; - goto fail_buffer; + goto fail_platform_device_alloc; } + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device_add; + + /* duplicate tokens will cause problems building sysfs files */ + zero_duplicates(&platform_device->dev); + + ret = build_tokens_sysfs(platform_device); + if (ret) + goto fail_create_group; return 0; -fail_buffer: +fail_create_group: + platform_device_del(platform_device); + +fail_platform_device_add: + platform_device_put(platform_device); + +fail_platform_device_alloc: + platform_driver_unregister(&platform_driver); + +fail_platform_driver: kfree(da_tokens); return ret; } static void __exit dell_smbios_exit(void) { + mutex_lock(&smbios_mutex); + if (platform_device) { + free_group(platform_device); + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); + } kfree(da_tokens); - free_page((unsigned long)buffer); + mutex_unlock(&smbios_mutex); } subsys_initcall(dell_smbios_init); diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h index 45cbc2292cd3..138d478d9adc 100644 --- a/drivers/platform/x86/dell-smbios.h +++ b/drivers/platform/x86/dell-smbios.h @@ -16,17 +16,29 @@ #ifndef _DELL_SMBIOS_H_ #define _DELL_SMBIOS_H_ -struct notifier_block; +#include +#include -/* This structure will be modified by the firmware when we enter - * system management mode, hence the volatiles */ +/* Classes and selects used only in kernel drivers */ +#define CLASS_KBD_BACKLIGHT 4 +#define SELECT_KBD_BACKLIGHT 11 -struct calling_interface_buffer { - u16 class; - u16 select; - volatile u32 input[4]; - volatile u32 output[4]; -} __packed; +/* Tokens used in kernel drivers, any of these + * should be filtered from userspace access + */ +#define BRIGHTNESS_TOKEN 0x007d +#define KBD_LED_AC_TOKEN 0x0451 +#define KBD_LED_OFF_TOKEN 0x01E1 +#define KBD_LED_ON_TOKEN 0x01E2 +#define KBD_LED_AUTO_TOKEN 0x01E3 +#define KBD_LED_AUTO_25_TOKEN 0x02EA +#define KBD_LED_AUTO_50_TOKEN 0x02EB +#define KBD_LED_AUTO_75_TOKEN 0x02EC +#define KBD_LED_AUTO_100_TOKEN 0x02F6 +#define GLOBAL_MIC_MUTE_ENABLE 0x0364 +#define GLOBAL_MIC_MUTE_DISABLE 0x0365 + +struct notifier_block; struct calling_interface_token { u16 tokenID; @@ -37,12 +49,21 @@ struct calling_interface_token { }; }; -int dell_smbios_error(int value); +struct calling_interface_structure { + struct dmi_header header; + u16 cmdIOAddress; + u8 cmdIOCode; + u32 supportedCmds; + struct calling_interface_token tokens[]; +} __packed; -struct calling_interface_buffer *dell_smbios_get_buffer(void); -void dell_smbios_clear_buffer(void); -void dell_smbios_release_buffer(void); -void dell_smbios_send_request(int class, int select); +int dell_smbios_register_device(struct device *d, void *call_fn); +void dell_smbios_unregister_device(struct device *d); + +int dell_smbios_error(int value); +int dell_smbios_call_filter(struct device *d, + struct calling_interface_buffer *buffer); +int dell_smbios_call(struct calling_interface_buffer *buffer); struct calling_interface_token *dell_smbios_find_token(int tokenid); diff --git a/drivers/platform/x86/dell-smo8800.c b/drivers/platform/x86/dell-smo8800.c index 37e646034ef8..1d87237bc731 100644 --- a/drivers/platform/x86/dell-smo8800.c +++ b/drivers/platform/x86/dell-smo8800.c @@ -90,7 +90,7 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf, struct smo8800_device, miscdev); u32 data = 0; - unsigned char byte_data = 0; + unsigned char byte_data; ssize_t retval = 1; if (count < 1) @@ -103,7 +103,6 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf, if (retval) return retval; - byte_data = 1; retval = 1; if (data < 255) diff --git a/drivers/platform/x86/dell-wmi-descriptor.c b/drivers/platform/x86/dell-wmi-descriptor.c new file mode 100644 index 000000000000..072821aa47fc --- /dev/null +++ b/drivers/platform/x86/dell-wmi-descriptor.c @@ -0,0 +1,213 @@ +/* + * Dell WMI descriptor driver + * + * Copyright (C) 2017 Dell Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "dell-wmi-descriptor.h" + +#define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" + +struct descriptor_priv { + struct list_head list; + u32 interface_version; + u32 size; + u32 hotfix; +}; +static int descriptor_valid = -EPROBE_DEFER; +static LIST_HEAD(wmi_list); +static DEFINE_MUTEX(list_mutex); + +int dell_wmi_get_descriptor_valid(void) +{ + if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID)) + return -ENODEV; + + return descriptor_valid; +} +EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid); + +bool dell_wmi_get_interface_version(u32 *version) +{ + struct descriptor_priv *priv; + bool ret = false; + + mutex_lock(&list_mutex); + priv = list_first_entry_or_null(&wmi_list, + struct descriptor_priv, + list); + if (priv) { + *version = priv->interface_version; + ret = true; + } + mutex_unlock(&list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version); + +bool dell_wmi_get_size(u32 *size) +{ + struct descriptor_priv *priv; + bool ret = false; + + mutex_lock(&list_mutex); + priv = list_first_entry_or_null(&wmi_list, + struct descriptor_priv, + list); + if (priv) { + *size = priv->size; + ret = true; + } + mutex_unlock(&list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(dell_wmi_get_size); + +bool dell_wmi_get_hotfix(u32 *hotfix) +{ + struct descriptor_priv *priv; + bool ret = false; + + mutex_lock(&list_mutex); + priv = list_first_entry_or_null(&wmi_list, + struct descriptor_priv, + list); + if (priv) { + *hotfix = priv->hotfix; + ret = true; + } + mutex_unlock(&list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix); + +/* + * Descriptor buffer is 128 byte long and contains: + * + * Name Offset Length Value + * Vendor Signature 0 4 "DELL" + * Object Signature 4 4 " WMI" + * WMI Interface Version 8 4 + * WMI buffer length 12 4 + * WMI hotfix number 16 4 + */ +static int dell_wmi_descriptor_probe(struct wmi_device *wdev) +{ + union acpi_object *obj = NULL; + struct descriptor_priv *priv; + u32 *buffer; + int ret; + + obj = wmidev_block_query(wdev, 0); + if (!obj) { + dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); + ret = -EIO; + goto out; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); + ret = -EINVAL; + descriptor_valid = ret; + goto out; + } + + /* Although it's not technically a failure, this would lead to + * unexpected behavior + */ + if (obj->buffer.length != 128) { + dev_err(&wdev->dev, + "Dell descriptor buffer has unexpected length (%d)\n", + obj->buffer.length); + ret = -EINVAL; + descriptor_valid = ret; + goto out; + } + + buffer = (u32 *)obj->buffer.pointer; + + if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) { + dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n", + buffer); + ret = -EINVAL; + descriptor_valid = ret; + goto out; + } + descriptor_valid = 0; + + if (buffer[2] != 0 && buffer[2] != 1) + dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n", + (unsigned long) buffer[2]); + + priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv), + GFP_KERNEL); + + if (!priv) { + ret = -ENOMEM; + goto out; + } + + priv->interface_version = buffer[2]; + priv->size = buffer[3]; + priv->hotfix = buffer[4]; + ret = 0; + dev_set_drvdata(&wdev->dev, priv); + mutex_lock(&list_mutex); + list_add_tail(&priv->list, &wmi_list); + mutex_unlock(&list_mutex); + + dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu, buffer size %lu, hotfix %lu\n", + (unsigned long) priv->interface_version, + (unsigned long) priv->size, + (unsigned long) priv->hotfix); + +out: + kfree(obj); + return ret; +} + +static int dell_wmi_descriptor_remove(struct wmi_device *wdev) +{ + struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev); + + mutex_lock(&list_mutex); + list_del(&priv->list); + mutex_unlock(&list_mutex); + return 0; +} + +static const struct wmi_device_id dell_wmi_descriptor_id_table[] = { + { .guid_string = DELL_WMI_DESCRIPTOR_GUID }, + { }, +}; + +static struct wmi_driver dell_wmi_descriptor_driver = { + .driver = { + .name = "dell-wmi-descriptor", + }, + .probe = dell_wmi_descriptor_probe, + .remove = dell_wmi_descriptor_remove, + .id_table = dell_wmi_descriptor_id_table, +}; + +module_wmi_driver(dell_wmi_descriptor_driver); + +MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID); +MODULE_AUTHOR("Mario Limonciello "); +MODULE_DESCRIPTION("Dell WMI descriptor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-wmi-descriptor.h b/drivers/platform/x86/dell-wmi-descriptor.h new file mode 100644 index 000000000000..a6123a4d06a7 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-descriptor.h @@ -0,0 +1,28 @@ +/* + * Dell WMI descriptor driver + * + * Copyright (c) 2017 Dell Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DELL_WMI_DESCRIPTOR_H_ +#define _DELL_WMI_DESCRIPTOR_H_ + +#include + +/* possible return values: + * -ENODEV: Descriptor GUID missing from WMI bus + * -EPROBE_DEFER: probing for dell-wmi-descriptor not yet run + * 0: valid descriptor, successfully probed + * < 0: invalid descriptor, don't probe dependent devices + */ +int dell_wmi_get_descriptor_valid(void); + +bool dell_wmi_get_interface_version(u32 *version); +bool dell_wmi_get_size(u32 *size); +bool dell_wmi_get_hotfix(u32 *hotfix); + +#endif diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 28d9f8696081..39d2f4518483 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -39,6 +39,7 @@ #include #include #include "dell-smbios.h" +#include "dell-wmi-descriptor.h" MODULE_AUTHOR("Matthew Garrett "); MODULE_AUTHOR("Pali Rohár "); @@ -46,12 +47,10 @@ MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); MODULE_LICENSE("GPL"); #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" -#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" static bool wmi_requires_smbios_request; MODULE_ALIAS("wmi:"DELL_EVENT_GUID); -MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID); struct dell_wmi_priv { struct input_dev *input_dev; @@ -618,78 +617,6 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev) input_unregister_device(priv->input_dev); } -/* - * Descriptor buffer is 128 byte long and contains: - * - * Name Offset Length Value - * Vendor Signature 0 4 "DELL" - * Object Signature 4 4 " WMI" - * WMI Interface Version 8 4 - * WMI buffer length 12 4 4096 - */ -static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev) -{ - struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); - union acpi_object *obj = NULL; - struct wmi_device *desc_dev; - u32 *buffer; - int ret; - - desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID); - if (!desc_dev) { - dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n"); - return -ENODEV; - } - - obj = wmidev_block_query(desc_dev, 0); - if (!obj) { - dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); - ret = -EIO; - goto out; - } - - if (obj->type != ACPI_TYPE_BUFFER) { - dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); - ret = -EINVAL; - goto out; - } - - if (obj->buffer.length != 128) { - dev_err(&wdev->dev, - "Dell descriptor buffer has invalid length (%d)\n", - obj->buffer.length); - if (obj->buffer.length < 16) { - ret = -EINVAL; - goto out; - } - } - - buffer = (u32 *)obj->buffer.pointer; - - if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720) - dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n", - 8, buffer); - - if (buffer[2] != 0 && buffer[2] != 1) - dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n", - buffer[2]); - - if (buffer[3] != 4096) - dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n", - buffer[3]); - - priv->interface_version = buffer[2]; - ret = 0; - - dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n", - priv->interface_version); - -out: - kfree(obj); - put_device(&desc_dev->dev); - return ret; -} - /* * According to Dell SMBIOS documentation: * @@ -711,13 +638,16 @@ static int dell_wmi_events_set_enabled(bool enable) struct calling_interface_buffer *buffer; int ret; - buffer = dell_smbios_get_buffer(); + buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); + buffer->cmd_class = CLASS_INFO; + buffer->cmd_select = SELECT_APP_REGISTRATION; buffer->input[0] = 0x10000; buffer->input[1] = 0x51534554; buffer->input[3] = enable; - dell_smbios_send_request(17, 3); - ret = buffer->output[0]; - dell_smbios_release_buffer(); + ret = dell_smbios_call(buffer); + if (ret == 0) + ret = buffer->output[0]; + kfree(buffer); return dell_smbios_error(ret); } @@ -725,7 +655,11 @@ static int dell_wmi_events_set_enabled(bool enable) static int dell_wmi_probe(struct wmi_device *wdev) { struct dell_wmi_priv *priv; - int err; + int ret; + + ret = dell_wmi_get_descriptor_valid(); + if (ret) + return ret; priv = devm_kzalloc( &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL); @@ -733,9 +667,8 @@ static int dell_wmi_probe(struct wmi_device *wdev) return -ENOMEM; dev_set_drvdata(&wdev->dev, priv); - err = dell_wmi_check_descriptor_buffer(wdev); - if (err) - return err; + if (!dell_wmi_get_interface_version(&priv->interface_version)) + return -EPROBE_DEFER; return dell_wmi_input_setup(wdev); } diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 56a8195096a2..2cfbd3fa5136 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -691,6 +691,7 @@ static enum led_brightness eco_led_get(struct led_classdev *cdev) static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) { + struct fujitsu_laptop *priv = acpi_driver_data(device); struct led_classdev *led; int result; @@ -724,12 +725,15 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) } /* - * BTNI bit 24 seems to indicate the presence of a radio toggle - * button in place of a slide switch, and all such machines appear - * to also have an RF LED. Therefore use bit 24 as an indicator - * that an RF LED is present. + * Some Fujitsu laptops have a radio toggle button in place of a slide + * switch and all such machines appear to also have an RF LED. Based on + * comparing DSDT tables of four Fujitsu Lifebook models (E744, E751, + * S7110, S8420; the first one has a radio toggle button, the other + * three have slide switches), bit 17 of flags_supported (the value + * returned by method S000 of ACPI device FUJ02E3) seems to indicate + * whether given model has a radio toggle button. */ - if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { + if (priv->flags_supported & BIT(17)) { led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index b4ed3dc983d5..b4224389febe 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -297,7 +297,7 @@ static int hp_wmi_hw_state(int mask) if (state < 0) return state; - return state & 0x1; + return !!(state & mask); } static int __init hp_wmi_bios_2008_later(void) diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 493d8910a74e..7b12abe86b94 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -240,6 +240,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), + AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index fe98d4ac0df3..53ab4e0f8962 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1166,6 +1166,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"), }, }, + { + .ident = "Lenovo YOGA 920-13IKB", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"), + }, + }, {} }; diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index e34fd70b67af..f470279c4c10 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -226,6 +226,24 @@ wakeup: return; } + /* + * Needed for suspend to work on some platforms that don't expose + * the 5-button array, but still send notifies with power button + * event code to this device object on power button actions. + * + * Report the power button press; catch and ignore the button release. + */ + if (!priv->array) { + if (event == 0xce) { + input_report_key(priv->input_dev, KEY_POWER, 1); + input_sync(priv->input_dev); + return; + } + + if (event == 0xcf) + return; + } + /* 0xC0 is for HID events, other values are for 5 button array */ if (event != 0xc0) { if (!priv->array || diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c new file mode 100644 index 000000000000..c2257bd06f18 --- /dev/null +++ b/drivers/platform/x86/intel-wmi-thunderbolt.c @@ -0,0 +1,98 @@ +/* + * WMI Thunderbolt driver + * + * Copyright (C) 2017 Dell Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTEL_WMI_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341" + +static ssize_t force_power_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_buffer input; + acpi_status status; + u8 mode; + + input.length = sizeof(u8); + input.pointer = &mode; + mode = hex_to_bin(buf[0]); + if (mode == 0 || mode == 1) { + status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1, + &input, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + } else { + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR_WO(force_power); + +static struct attribute *tbt_attrs[] = { + &dev_attr_force_power.attr, + NULL +}; + +static const struct attribute_group tbt_attribute_group = { + .attrs = tbt_attrs, +}; + +static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev) +{ + int ret; + + ret = sysfs_create_group(&wdev->dev.kobj, &tbt_attribute_group); + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); + return ret; +} + +static int intel_wmi_thunderbolt_remove(struct wmi_device *wdev) +{ + sysfs_remove_group(&wdev->dev.kobj, &tbt_attribute_group); + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); + return 0; +} + +static const struct wmi_device_id intel_wmi_thunderbolt_id_table[] = { + { .guid_string = INTEL_WMI_THUNDERBOLT_GUID }, + { }, +}; + +static struct wmi_driver intel_wmi_thunderbolt_driver = { + .driver = { + .name = "intel-wmi-thunderbolt", + }, + .probe = intel_wmi_thunderbolt_probe, + .remove = intel_wmi_thunderbolt_remove, + .id_table = intel_wmi_thunderbolt_id_table, +}; + +module_wmi_driver(intel_wmi_thunderbolt_driver); + +MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID); +MODULE_AUTHOR("Mario Limonciello "); +MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index da706e2c4232..380ef7ec094f 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #define EXPECTED_PTYPE 4 @@ -34,6 +35,42 @@ struct cht_int33fe_data { struct i2c_client *pi3usb30532; }; +/* + * Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates + * the max17047 both through the INT33FE ACPI device (it is right there + * in the resources table) as well as through a separate MAX17047 device. + * + * These helpers are used to work around this by checking if an i2c-client + * for the max17047 has already been registered. + */ +static int cht_int33fe_check_for_max17047(struct device *dev, void *data) +{ + struct i2c_client **max17047 = data; + struct acpi_device *adev; + const char *hid; + + adev = ACPI_COMPANION(dev); + if (!adev) + return 0; + + hid = acpi_device_hid(adev); + + /* The MAX17047 ACPI node doesn't have an UID, so we don't check that */ + if (strcmp(hid, "MAX17047")) + return 0; + + *max17047 = to_i2c_client(dev); + return 1; +} + +static struct i2c_client *cht_int33fe_find_max17047(void) +{ + struct i2c_client *max17047 = NULL; + + i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047); + return max17047; +} + static const char * const max17047_suppliers[] = { "bq24190-charger" }; static const struct property_entry max17047_props[] = { @@ -41,14 +78,25 @@ static const struct property_entry max17047_props[] = { { } }; +static const struct property_entry fusb302_props[] = { + PROPERTY_ENTRY_STRING("fcs,extcon-name", "cht_wcove_pwrsrc"), + PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000), + PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000), + PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000), + { } +}; + static int cht_int33fe_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct i2c_board_info board_info; struct cht_int33fe_data *data; + struct i2c_client *max17047; + struct regulator *regulator; unsigned long long ptyp; acpi_status status; int fusb302_irq; + int ret; status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp); if (ACPI_FAILURE(status)) { @@ -63,6 +111,34 @@ static int cht_int33fe_probe(struct i2c_client *client) if (ptyp != EXPECTED_PTYPE) return -ENODEV; + /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */ + if (!acpi_dev_present("INT34D3", "1", 3)) { + dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n", + EXPECTED_PTYPE); + return -ENODEV; + } + + /* + * We expect the WC PMIC to be paired with a TI bq24292i charger-IC. + * We check for the bq24292i vbus regulator here, this has 2 purposes: + * 1) The bq24292i allows charging with up to 12V, setting the fusb302's + * max-snk voltage to 12V with another charger-IC is not good. + * 2) For the fusb302 driver to get the bq24292i vbus regulator, the + * regulator-map, which is part of the bq24292i regulator_init_data, + * must be registered before the fusb302 is instantiated, otherwise + * it will end up with a dummy-regulator. + * Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data + * which is defined in i2c-cht-wc.c from where the bq24292i i2c-client + * gets instantiated. We use regulator_get_optional here so that we + * don't end up getting a dummy-regulator ourselves. + */ + regulator = regulator_get_optional(dev, "cht_wc_usb_typec_vbus"); + if (IS_ERR(regulator)) { + ret = PTR_ERR(regulator); + return (ret == -ENODEV) ? -EPROBE_DEFER : ret; + } + regulator_put(regulator); + /* The FUSB302 uses the irq at index 1 and is the only irq user */ fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1); if (fusb302_irq < 0) { @@ -75,16 +151,31 @@ static int cht_int33fe_probe(struct i2c_client *client) if (!data) return -ENOMEM; - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); - board_info.properties = max17047_props; - - data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); - if (!data->max17047) - return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ + /* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */ + max17047 = cht_int33fe_find_max17047(); + if (max17047) { + /* Pre-existing i2c-client for the max17047, add device-props */ + ret = device_add_properties(&max17047->dev, max17047_props); + if (ret) + return ret; + /* And re-probe to get the new device-props applied. */ + ret = device_reprobe(&max17047->dev); + if (ret) + dev_warn(dev, "Reprobing max17047 error: %d\n", ret); + } else { + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); + board_info.dev_name = "max17047"; + board_info.properties = max17047_props; + data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); + if (!data->max17047) + return -EPROBE_DEFER; /* Wait for i2c-adapter to load */ + } memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "fusb302", I2C_NAME_SIZE); + strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE); + board_info.dev_name = "fusb302"; + board_info.properties = fusb302_props; board_info.irq = fusb302_irq; data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info); @@ -92,6 +183,7 @@ static int cht_int33fe_probe(struct i2c_client *client) goto out_unregister_max17047; memset(&board_info, 0, sizeof(board_info)); + board_info.dev_name = "pi3usb30532"; strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE); data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info); @@ -106,7 +198,8 @@ out_unregister_fusb302: i2c_unregister_device(data->fusb302); out_unregister_max17047: - i2c_unregister_device(data->max17047); + if (data->max17047) + i2c_unregister_device(data->max17047); return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ } @@ -117,7 +210,8 @@ static int cht_int33fe_remove(struct i2c_client *i2c) i2c_unregister_device(data->pi3usb30532); i2c_unregister_device(data->fusb302); - i2c_unregister_device(data->max17047); + if (data->max17047) + i2c_unregister_device(data->max17047); return 0; } diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 58dcee562d64..a0c95853fd3f 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -10,10 +10,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -259,8 +255,6 @@ static const int IPS_SAMPLE_WINDOW = 5000; /* 5s moving window of samples */ /* Per-SKU limits */ struct ips_mcp_limits { - int cpu_family; - int cpu_model; /* includes extended model... */ int mcp_power_limit; /* mW units */ int core_power_limit; int mch_power_limit; @@ -295,11 +289,14 @@ static struct ips_mcp_limits ips_ulv_limits = { }; struct ips_driver { - struct pci_dev *dev; - void *regmap; + struct device *dev; + void __iomem *regmap; + int irq; + struct task_struct *monitor; struct task_struct *adjust; struct dentry *debug_root; + struct timer_list timer; /* Average CPU core temps (all averages in .01 degrees C for precision) */ u16 ctv1_avg_temp; @@ -594,7 +591,7 @@ static void ips_disable_gpu_turbo(struct ips_driver *ips) return; if (!ips->gpu_turbo_disable()) - dev_err(&ips->dev->dev, "failed to disable graphics turbo\n"); + dev_err(ips->dev, "failed to disable graphics turbo\n"); else ips->__gpu_turbo_on = false; } @@ -649,8 +646,7 @@ static bool cpu_exceeded(struct ips_driver *ips, int cpu) spin_unlock_irqrestore(&ips->turbo_status_lock, flags); if (ret) - dev_info(&ips->dev->dev, - "CPU power or thermal limit exceeded\n"); + dev_info(ips->dev, "CPU power or thermal limit exceeded\n"); return ret; } @@ -769,7 +765,7 @@ static int ips_adjust(void *data) struct ips_driver *ips = data; unsigned long flags; - dev_dbg(&ips->dev->dev, "starting ips-adjust thread\n"); + dev_dbg(ips->dev, "starting ips-adjust thread\n"); /* * Adjust CPU and GPU clamps every 5s if needed. Doing it more @@ -816,7 +812,7 @@ sleep: schedule_timeout_interruptible(msecs_to_jiffies(IPS_ADJUST_PERIOD)); } while (!kthread_should_stop()); - dev_dbg(&ips->dev->dev, "ips-adjust thread stopped\n"); + dev_dbg(ips->dev, "ips-adjust thread stopped\n"); return 0; } @@ -942,9 +938,10 @@ static u32 calc_avg_power(struct ips_driver *ips, u32 *array) return avg; } -static void monitor_timeout(unsigned long arg) +static void monitor_timeout(struct timer_list *t) { - wake_up_process((struct task_struct *)arg); + struct ips_driver *ips = from_timer(ips, t, timer); + wake_up_process(ips->monitor); } /** @@ -961,7 +958,6 @@ static void monitor_timeout(unsigned long arg) static int ips_monitor(void *data) { struct ips_driver *ips = data; - struct timer_list timer; unsigned long seqno_timestamp, expire, last_msecs, last_sample_period; int i; u32 *cpu_samples, *mchp_samples, old_cpu_power; @@ -976,7 +972,7 @@ static int ips_monitor(void *data) mchp_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL); if (!mcp_samples || !ctv1_samples || !ctv2_samples || !mch_samples || !cpu_samples || !mchp_samples) { - dev_err(&ips->dev->dev, + dev_err(ips->dev, "failed to allocate sample array, ips disabled\n"); kfree(mcp_samples); kfree(ctv1_samples); @@ -1049,8 +1045,7 @@ static int ips_monitor(void *data) schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); last_sample_period = IPS_SAMPLE_PERIOD; - setup_deferrable_timer_on_stack(&timer, monitor_timeout, - (unsigned long)current); + timer_setup(&ips->timer, monitor_timeout, TIMER_DEFERRABLE); do { u32 cpu_val, mch_val; u16 val; @@ -1097,7 +1092,8 @@ static int ips_monitor(void *data) ITV_ME_SEQNO_SHIFT; if (cur_seqno == last_seqno && time_after(jiffies, seqno_timestamp + HZ)) { - dev_warn(&ips->dev->dev, "ME failed to update for more than 1s, likely hung\n"); + dev_warn(ips->dev, + "ME failed to update for more than 1s, likely hung\n"); } else { seqno_timestamp = get_jiffies_64(); last_seqno = cur_seqno; @@ -1107,7 +1103,7 @@ static int ips_monitor(void *data) expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); __set_current_state(TASK_INTERRUPTIBLE); - mod_timer(&timer, expire); + mod_timer(&ips->timer, expire); schedule(); /* Calculate actual sample period for power averaging */ @@ -1116,10 +1112,9 @@ static int ips_monitor(void *data) last_sample_period = 1; } while (!kthread_should_stop()); - del_timer_sync(&timer); - destroy_timer_on_stack(&timer); + del_timer_sync(&ips->timer); - dev_dbg(&ips->dev->dev, "ips-monitor thread stopped\n"); + dev_dbg(ips->dev, "ips-monitor thread stopped\n"); return 0; } @@ -1128,17 +1123,17 @@ static int ips_monitor(void *data) #define THM_DUMPW(reg) \ { \ u16 val = thm_readw(reg); \ - dev_dbg(&ips->dev->dev, #reg ": 0x%04x\n", val); \ + dev_dbg(ips->dev, #reg ": 0x%04x\n", val); \ } #define THM_DUMPL(reg) \ { \ u32 val = thm_readl(reg); \ - dev_dbg(&ips->dev->dev, #reg ": 0x%08x\n", val); \ + dev_dbg(ips->dev, #reg ": 0x%08x\n", val); \ } #define THM_DUMPQ(reg) \ { \ u64 val = thm_readq(reg); \ - dev_dbg(&ips->dev->dev, #reg ": 0x%016x\n", val); \ + dev_dbg(ips->dev, #reg ": 0x%016x\n", val); \ } static void dump_thermal_info(struct ips_driver *ips) @@ -1146,7 +1141,7 @@ static void dump_thermal_info(struct ips_driver *ips) u16 ptl; ptl = thm_readw(THM_PTL); - dev_dbg(&ips->dev->dev, "Processor temp limit: %d\n", ptl); + dev_dbg(ips->dev, "Processor temp limit: %d\n", ptl); THM_DUMPW(THM_CTA); THM_DUMPW(THM_TRC); @@ -1175,8 +1170,8 @@ static irqreturn_t ips_irq_handler(int irq, void *arg) if (!tses && !tes) return IRQ_NONE; - dev_info(&ips->dev->dev, "TSES: 0x%02x\n", tses); - dev_info(&ips->dev->dev, "TES: 0x%02x\n", tes); + dev_info(ips->dev, "TSES: 0x%02x\n", tses); + dev_info(ips->dev, "TES: 0x%02x\n", tes); /* STS update from EC? */ if (tes & 1) { @@ -1214,8 +1209,8 @@ static irqreturn_t ips_irq_handler(int irq, void *arg) /* Thermal trip */ if (tses) { - dev_warn(&ips->dev->dev, - "thermal trip occurred, tses: 0x%04x\n", tses); + dev_warn(ips->dev, "thermal trip occurred, tses: 0x%04x\n", + tses); thm_writeb(THM_TSES, tses); } @@ -1330,8 +1325,7 @@ static void ips_debugfs_init(struct ips_driver *ips) ips->debug_root = debugfs_create_dir("ips", NULL); if (!ips->debug_root) { - dev_err(&ips->dev->dev, - "failed to create debugfs entries: %ld\n", + dev_err(ips->dev, "failed to create debugfs entries: %ld\n", PTR_ERR(ips->debug_root)); return; } @@ -1345,8 +1339,7 @@ static void ips_debugfs_init(struct ips_driver *ips) ips->debug_root, node, &ips_debugfs_ops); if (!ent) { - dev_err(&ips->dev->dev, - "failed to create debug file: %ld\n", + dev_err(ips->dev, "failed to create debug file: %ld\n", PTR_ERR(ent)); goto err_cleanup; } @@ -1373,8 +1366,8 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) u16 tdp; if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) { - dev_info(&ips->dev->dev, "Non-IPS CPU detected.\n"); - goto out; + dev_info(ips->dev, "Non-IPS CPU detected.\n"); + return NULL; } rdmsrl(IA32_MISC_ENABLE, misc_en); @@ -1395,8 +1388,8 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) else if (strstr(boot_cpu_data.x86_model_id, "CPU U")) limits = &ips_ulv_limits; else { - dev_info(&ips->dev->dev, "No CPUID match found.\n"); - goto out; + dev_info(ips->dev, "No CPUID match found.\n"); + return NULL; } rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_power); @@ -1404,12 +1397,12 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) /* Sanity check TDP against CPU */ if (limits->core_power_limit != (tdp / 8) * 1000) { - dev_info(&ips->dev->dev, "CPU TDP doesn't match expected value (found %d, expected %d)\n", + dev_info(ips->dev, + "CPU TDP doesn't match expected value (found %d, expected %d)\n", tdp / 8, limits->core_power_limit / 1000); limits->core_power_limit = (tdp / 8) * 1000; } -out: return limits; } @@ -1459,7 +1452,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips) { if (!ips->gpu_busy && late_i915_load) { if (ips_get_i915_syms(ips)) { - dev_info(&ips->dev->dev, + dev_info(ips->dev, "i915 driver attached, reenabling gpu turbo\n"); ips->gpu_turbo_enabled = !(thm_readl(THM_HTS) & HTS_GTD_DIS); } @@ -1480,8 +1473,7 @@ ips_link_to_i915_driver(void) EXPORT_SYMBOL_GPL(ips_link_to_i915_driver); static const struct pci_device_id ips_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, { 0, } }; @@ -1517,62 +1509,45 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) if (dmi_check_system(ips_blacklist)) return -ENODEV; - ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL); + ips = devm_kzalloc(&dev->dev, sizeof(*ips), GFP_KERNEL); if (!ips) return -ENOMEM; - pci_set_drvdata(dev, ips); - ips->dev = dev; + spin_lock_init(&ips->turbo_status_lock); + ips->dev = &dev->dev; ips->limits = ips_detect_cpu(ips); if (!ips->limits) { dev_info(&dev->dev, "IPS not supported on this CPU\n"); - ret = -ENXIO; - goto error_free; + return -ENXIO; } - spin_lock_init(&ips->turbo_status_lock); - - ret = pci_enable_device(dev); + ret = pcim_enable_device(dev); if (ret) { dev_err(&dev->dev, "can't enable PCI device, aborting\n"); - goto error_free; + return ret; } - if (!pci_resource_start(dev, 0)) { - dev_err(&dev->dev, "TBAR not assigned, aborting\n"); - ret = -ENXIO; - goto error_free; - } - - ret = pci_request_regions(dev, "ips thermal sensor"); + ret = pcim_iomap_regions(dev, 1 << 0, pci_name(dev)); if (ret) { - dev_err(&dev->dev, "thermal resource busy, aborting\n"); - goto error_free; - } - - - ips->regmap = ioremap(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); - if (!ips->regmap) { dev_err(&dev->dev, "failed to map thermal regs, aborting\n"); - ret = -EBUSY; - goto error_release; + return ret; } + ips->regmap = pcim_iomap_table(dev)[0]; + + pci_set_drvdata(dev, ips); tse = thm_readb(THM_TSE); if (tse != TSE_EN) { dev_err(&dev->dev, "thermal device not enabled (0x%02x), aborting\n", tse); - ret = -ENXIO; - goto error_unmap; + return -ENXIO; } trc = thm_readw(THM_TRC); trc_required_mask = TRC_CORE1_EN | TRC_CORE_PWR | TRC_MCH_EN; if ((trc & trc_required_mask) != trc_required_mask) { dev_err(&dev->dev, "thermal reporting for required devices not enabled, aborting\n"); - ret = -ENXIO; - goto error_unmap; + return -ENXIO; } if (trc & TRC_CORE2_EN) @@ -1602,20 +1577,23 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) rdmsrl(PLATFORM_INFO, platform_info); if (!(platform_info & PLATFORM_TDP)) { dev_err(&dev->dev, "platform indicates TDP override unavailable, aborting\n"); - ret = -ENODEV; - goto error_unmap; + return -ENODEV; } /* * IRQ handler for ME interaction * Note: don't use MSI here as the PCH has bugs. */ - pci_disable_msi(dev); - ret = request_irq(dev->irq, ips_irq_handler, IRQF_SHARED, "ips", - ips); + ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); + if (ret < 0) + return ret; + + ips->irq = pci_irq_vector(dev, 0); + + ret = request_irq(ips->irq, ips_irq_handler, IRQF_SHARED, "ips", ips); if (ret) { dev_err(&dev->dev, "request irq failed, aborting\n"); - goto error_unmap; + return ret; } /* Enable aux, hot & critical interrupts */ @@ -1672,13 +1650,8 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) error_thread_cleanup: kthread_stop(ips->adjust); error_free_irq: - free_irq(ips->dev->irq, ips); -error_unmap: - iounmap(ips->regmap); -error_release: - pci_release_regions(dev); -error_free: - kfree(ips); + free_irq(ips->irq, ips); + pci_free_irq_vectors(dev); return ret; } @@ -1709,27 +1682,20 @@ static void ips_remove(struct pci_dev *dev) wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); wrmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit); - free_irq(ips->dev->irq, ips); + free_irq(ips->irq, ips); + pci_free_irq_vectors(dev); if (ips->adjust) kthread_stop(ips->adjust); if (ips->monitor) kthread_stop(ips->monitor); - iounmap(ips->regmap); - pci_release_regions(dev); - kfree(ips); dev_dbg(&dev->dev, "IPS driver removed\n"); } -static void ips_shutdown(struct pci_dev *dev) -{ -} - static struct pci_driver ips_pci_driver = { .name = "intel ips", .id_table = ips_id_table, .probe = ips_probe, .remove = ips_remove, - .shutdown = ips_shutdown, }; module_pci_driver(ips_pci_driver); diff --git a/drivers/platform/x86/intel_ips.h b/drivers/platform/x86/intel_ips.h index 73299beff5b3..60f4e3ddbe9f 100644 --- a/drivers/platform/x86/intel_ips.h +++ b/drivers/platform/x86/intel_ips.h @@ -10,10 +10,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". */ diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index a47a41fc10ad..b5b890127479 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -252,28 +252,28 @@ static int intel_punit_get_bars(struct platform_device *pdev) * - GTDRIVER_IPC BASE_IFACE */ res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr; } res = platform_get_resource(pdev, IORESOURCE_MEM, 3); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr; } res = platform_get_resource(pdev, IORESOURCE_MEM, 4); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr; } res = platform_get_resource(pdev, IORESOURCE_MEM, 5); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr; diff --git a/drivers/platform/x86/intel_telemetry_core.c b/drivers/platform/x86/intel_telemetry_core.c index 0d4c3808a6d8..f378621b5fe9 100644 --- a/drivers/platform/x86/intel_telemetry_core.c +++ b/drivers/platform/x86/intel_telemetry_core.c @@ -15,9 +15,8 @@ * Telemetry Framework provides platform related PM and performance statistics. * This file provides the core telemetry API implementation. */ -#include -#include #include +#include #include diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index d4fc42b4cbeb..4249e8267bbc 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -21,14 +21,12 @@ * /sys/kernel/debug/telemetry/ioss_race_verbosity: Write and Change Tracing * Verbosity via firmware */ -#include -#include -#include #include -#include +#include #include -#include +#include #include +#include #include #include @@ -76,8 +74,6 @@ #define TELEM_IOSS_DX_D0IX_EVTS 25 #define TELEM_IOSS_PG_EVTS 30 -#define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0])) - #define TELEM_DEBUGFS_CPU(model, data) \ { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data} @@ -304,13 +300,13 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = { .ioss_d0ix_data = telem_apl_ioss_d0ix_data, .ioss_pg_data = telem_apl_ioss_pg_data, - .pss_idle_evts = TELEM_EVT_LEN(telem_apl_pss_idle_data), - .pcs_idle_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_idle_blkd_data), - .pcs_s0ix_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_s0ix_blkd_data), - .pss_ltr_evts = TELEM_EVT_LEN(telem_apl_pss_ltr_data), - .pss_wakeup_evts = TELEM_EVT_LEN(telem_apl_pss_wakeup), - .ioss_d0ix_evts = TELEM_EVT_LEN(telem_apl_ioss_d0ix_data), - .ioss_pg_evts = TELEM_EVT_LEN(telem_apl_ioss_pg_data), + .pss_idle_evts = ARRAY_SIZE(telem_apl_pss_idle_data), + .pcs_idle_blkd_evts = ARRAY_SIZE(telem_apl_pcs_idle_blkd_data), + .pcs_s0ix_blkd_evts = ARRAY_SIZE(telem_apl_pcs_s0ix_blkd_data), + .pss_ltr_evts = ARRAY_SIZE(telem_apl_pss_ltr_data), + .pss_wakeup_evts = ARRAY_SIZE(telem_apl_pss_wakeup), + .ioss_d0ix_evts = ARRAY_SIZE(telem_apl_ioss_d0ix_data), + .ioss_pg_evts = ARRAY_SIZE(telem_apl_ioss_pg_data), .pstates_id = TELEM_APL_PSS_PSTATES_ID, .pss_idle_id = TELEM_APL_PSS_IDLE_ID, diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index e0424d5a795a..2f889d6c270e 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -16,15 +16,9 @@ * It used the PUNIT and PMC IPC interfaces for configuring the counters. * The accumulated results are fetched from SRAM. */ -#include -#include -#include -#include -#include + #include -#include -#include -#include +#include #include #include @@ -256,7 +250,7 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit, break; default: - pr_err("Unknown Telemetry action Specified %d\n", action); + pr_err("Unknown Telemetry action specified %d\n", action); return -EINVAL; } @@ -659,7 +653,7 @@ static int telemetry_setup(struct platform_device *pdev) ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_RESET); if (ret) { - dev_err(&pdev->dev, "TELEMTRY Setup Failed\n"); + dev_err(&pdev->dev, "TELEMETRY Setup Failed\n"); return ret; } return 0; @@ -685,7 +679,7 @@ static int telemetry_plt_update_events(struct telemetry_evtconfig pss_evtconfig, ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_UPDATE); if (ret) - pr_err("TELEMTRY Config Failed\n"); + pr_err("TELEMETRY Config Failed\n"); return ret; } @@ -822,7 +816,7 @@ static int telemetry_plt_reset_events(void) ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_RESET); if (ret) - pr_err("TELEMTRY Reset Failed\n"); + pr_err("TELEMETRY Reset Failed\n"); return ret; } @@ -885,7 +879,7 @@ static int telemetry_plt_add_events(u8 num_pss_evts, u8 num_ioss_evts, ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_ADD); if (ret) - pr_err("TELEMTRY ADD Failed\n"); + pr_err("TELEMETRY ADD Failed\n"); return ret; } @@ -1195,7 +1189,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev) ret = telemetry_set_pltdata(&telm_pltops, telm_conf); if (ret) { - dev_err(&pdev->dev, "TELEMTRY Set Pltops Failed.\n"); + dev_err(&pdev->dev, "TELEMETRY Set Pltops Failed.\n"); goto out; } @@ -1210,7 +1204,7 @@ out: iounmap(telm_conf->pss_config.regmap); if (telm_conf->ioss_config.regmap) iounmap(telm_conf->ioss_config.regmap); - dev_err(&pdev->dev, "TELEMTRY Setup Failed.\n"); + dev_err(&pdev->dev, "TELEMETRY Setup Failed.\n"); return ret; } @@ -1234,7 +1228,6 @@ static struct platform_driver telemetry_soc_driver = { static int __init telemetry_module_init(void) { - pr_info(DRIVER_NAME ": version %s loaded\n", DRIVER_VERSION); return platform_driver_register(&telemetry_soc_driver); } diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c index 4f60d8e32a0a..d4ea01805879 100644 --- a/drivers/platform/x86/intel_turbo_max_3.c +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -125,6 +125,7 @@ static int itmt_legacy_cpu_online(unsigned int cpu) static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { ICPU(INTEL_FAM6_BROADWELL_X), + ICPU(INTEL_FAM6_SKYLAKE_X), {} }; diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 4f3de2a8c4df..504256c3660d 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -216,8 +216,8 @@ static struct resource mlxplat_mlxcpld_resources[] = { [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), }; -struct platform_device *mlxplat_dev; -struct mlxcpld_hotplug_platform_data *mlxplat_hotplug; +static struct platform_device *mlxplat_dev; +static struct mlxcpld_hotplug_platform_data *mlxplat_hotplug; static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) { diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c index bc98ef95514a..9b9e1f39bbfb 100644 --- a/drivers/platform/x86/peaq-wmi.c +++ b/drivers/platform/x86/peaq-wmi.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -64,8 +65,23 @@ static void peaq_wmi_poll(struct input_polled_dev *dev) } } +/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ +static const struct dmi_system_id peaq_dmi_table[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), + DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), + }, + }, + {} +}; + static int __init peaq_wmi_init(void) { + /* WMI GUID is not unique, also check for a DMI match */ + if (!dmi_check_system(peaq_dmi_table)) + return -ENODEV; + if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) return -ENODEV; @@ -86,9 +102,6 @@ static int __init peaq_wmi_init(void) static void __exit peaq_wmi_exit(void) { - if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) - return; - input_unregister_polled_device(peaq_poll_dev); } diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c index 1157a7b646d6..266535c2a72f 100644 --- a/drivers/platform/x86/silead_dmi.c +++ b/drivers/platform/x86/silead_dmi.c @@ -58,6 +58,7 @@ static const struct property_entry dexp_ursus_7w_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 630), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -72,6 +73,7 @@ static const struct property_entry surftab_wintron70_st70416_6_props[] = { PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-surftab-wintron70-st70416-6.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -83,6 +85,8 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = { static const struct property_entry gp_electronic_t701_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 960), PROPERTY_ENTRY_U32("touchscreen-size-y", 640), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-gp-electronic-t701.fw"), { } @@ -114,6 +118,7 @@ static const struct property_entry pov_mobii_wintab_p800w_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p800w.fw"), + PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -136,6 +141,36 @@ static const struct silead_ts_dmi_data itworks_tw891_data = { .properties = itworks_tw891_props, }; +static const struct property_entry chuwi_hi8_pro_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1728), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct silead_ts_dmi_data chuwi_hi8_pro_data = { + .acpi_name = "MSSL1680:00", + .properties = chuwi_hi8_pro_props, +}; + +static const struct property_entry digma_citi_e200_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl1686-digma_citi_e200.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct silead_ts_dmi_data digma_citi_e200_data = { + .acpi_name = "MSSL1680:00", + .properties = digma_citi_e200_props, +}; + static const struct dmi_system_id silead_ts_dmi_table[] = { { /* CUBE iwork8 Air */ @@ -219,6 +254,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "TW891"), }, }, + { + /* Chuwi Hi8 Pro */ + .driver_data = (void *)&chuwi_hi8_pro_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"), + }, + }, + { + /* Digma Citi E200 */ + .driver_data = (void *)&digma_citi_e200_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Digma"), + DMI_MATCH(DMI_PRODUCT_NAME, "CITI E200"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + }, + }, { }, }; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index a16cea2be9c3..62aa2c37b8d2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1627,7 +1627,7 @@ static const struct rfkill_ops sony_rfkill_ops = { static int sony_nc_setup_rfkill(struct acpi_device *device, enum sony_nc_rfkill nc_type) { - int err = 0; + int err; struct rfkill *rfk; enum rfkill_type type; const char *name; @@ -1660,17 +1660,19 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, if (!rfk) return -ENOMEM; - if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) { + err = sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); + if (err < 0) { rfkill_destroy(rfk); - return -1; + return err; } hwblock = !(result & 0x1); - if (sony_call_snc_handle(sony_rfkill_handle, - sony_rfkill_address[nc_type], - &result) < 0) { + err = sony_call_snc_handle(sony_rfkill_handle, + sony_rfkill_address[nc_type], + &result); + if (err < 0) { rfkill_destroy(rfk); - return -1; + return err; } swblock = !(result & 0x2); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3887dfeafc96..117be48ff4de 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -310,8 +310,7 @@ static struct { enum { TP_HOTKEY_TABLET_NONE = 0, TP_HOTKEY_TABLET_USES_MHKG, - /* X1 Yoga 2016, seen on BIOS N1FET44W */ - TP_HOTKEY_TABLET_USES_CMMD, + TP_HOTKEY_TABLET_USES_GMMS, } hotkey_tablet; u32 kbdlight:1; u32 light:1; @@ -2044,8 +2043,28 @@ static void hotkey_poll_setup(const bool may_warn); /* HKEY.MHKG() return bits */ #define TP_HOTKEY_TABLET_MASK (1 << 3) -/* ThinkPad X1 Yoga (2016) */ -#define TP_EC_CMMD_TABLET_MODE 0x6 +enum { + TP_ACPI_MULTI_MODE_INVALID = 0, + TP_ACPI_MULTI_MODE_UNKNOWN = 1 << 0, + TP_ACPI_MULTI_MODE_LAPTOP = 1 << 1, + TP_ACPI_MULTI_MODE_TABLET = 1 << 2, + TP_ACPI_MULTI_MODE_FLAT = 1 << 3, + TP_ACPI_MULTI_MODE_STAND = 1 << 4, + TP_ACPI_MULTI_MODE_TENT = 1 << 5, + TP_ACPI_MULTI_MODE_STAND_TENT = 1 << 6, +}; + +enum { + /* The following modes are considered tablet mode for the purpose of + * reporting the status to userspace. i.e. in all these modes it makes + * sense to disable the laptop input devices such as touchpad and + * keyboard. + */ + TP_ACPI_MULTI_MODE_TABLET_LIKE = TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT | + TP_ACPI_MULTI_MODE_STAND_TENT, +}; static int hotkey_get_wlsw(void) { @@ -2066,6 +2085,90 @@ static int hotkey_get_wlsw(void) return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; } +static int hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode) +{ + int type = (s >> 16) & 0xffff; + int value = s & 0xffff; + int mode = TP_ACPI_MULTI_MODE_INVALID; + int valid_modes = 0; + + if (has_tablet_mode) + *has_tablet_mode = 0; + + switch (type) { + case 1: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND_TENT; + break; + case 2: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_FLAT | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT; + break; + case 3: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_FLAT; + break; + case 4: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT; + break; + case 5: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_FLAT | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT; + break; + default: + pr_err("Unknown multi mode status type %d with value 0x%04X, please report this to %s\n", + type, value, TPACPI_MAIL); + return 0; + } + + if (has_tablet_mode && (valid_modes & TP_ACPI_MULTI_MODE_TABLET_LIKE)) + *has_tablet_mode = 1; + + switch (value) { + case 1: + mode = TP_ACPI_MULTI_MODE_LAPTOP; + break; + case 2: + mode = TP_ACPI_MULTI_MODE_FLAT; + break; + case 3: + mode = TP_ACPI_MULTI_MODE_TABLET; + break; + case 4: + if (type == 1) + mode = TP_ACPI_MULTI_MODE_STAND_TENT; + else + mode = TP_ACPI_MULTI_MODE_STAND; + break; + case 5: + mode = TP_ACPI_MULTI_MODE_TENT; + break; + default: + if (type == 5 && value == 0xffff) { + pr_warn("Multi mode status is undetected, assuming laptop\n"); + return 0; + } + } + + if (!(mode & valid_modes)) { + pr_err("Unknown/reserved multi mode value 0x%04X for type %d, please report this to %s\n", + value, type, TPACPI_MAIL); + return 0; + } + + return !!(mode & TP_ACPI_MULTI_MODE_TABLET_LIKE); +} + static int hotkey_get_tablet_mode(int *status) { int s; @@ -2077,11 +2180,11 @@ static int hotkey_get_tablet_mode(int *status) *status = ((s & TP_HOTKEY_TABLET_MASK) != 0); break; - case TP_HOTKEY_TABLET_USES_CMMD: - if (!acpi_evalf(ec_handle, &s, "CMMD", "d")) + case TP_HOTKEY_TABLET_USES_GMMS: + if (!acpi_evalf(hkey_handle, &s, "GMMS", "dd", 0)) return -EIO; - *status = (s == TP_EC_CMMD_TABLET_MODE); + *status = hotkey_gmms_get_tablet_mode(s, NULL); break; default: break; @@ -3113,16 +3216,19 @@ static int hotkey_init_tablet_mode(void) int in_tablet_mode = 0, res; char *type = NULL; - if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) { + if (acpi_evalf(hkey_handle, &res, "GMMS", "qdd", 0)) { + int has_tablet_mode; + + in_tablet_mode = hotkey_gmms_get_tablet_mode(res, + &has_tablet_mode); + if (has_tablet_mode) + tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_GMMS; + type = "GMMS"; + } else if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) { /* For X41t, X60t, X61t Tablets... */ tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_MHKG; in_tablet_mode = !!(res & TP_HOTKEY_TABLET_MASK); type = "MHKG"; - } else if (acpi_evalf(ec_handle, &res, "CMMD", "qd")) { - /* For X1 Yoga (2016) */ - tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_CMMD; - in_tablet_mode = res == TP_EC_CMMD_TABLET_MODE; - type = "CMMD"; } if (!tp_features.hotkey_tablet) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 0765b1797d4c..791449a2370f 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -33,17 +33,20 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include +#include #include +#include +#include #include -#include -#include +#include #include #include -#include +#include +#include +#include #include +#include +#include ACPI_MODULE_NAME("wmi"); MODULE_AUTHOR("Carlos Corbacho"); @@ -69,9 +72,12 @@ struct wmi_block { struct wmi_device dev; struct list_head list; struct guid_block gblock; + struct miscdevice char_dev; + struct mutex char_mutex; struct acpi_device *acpi_device; wmi_notify_handler handler; void *handler_data; + u64 req_buf_size; bool read_takes_no_args; }; @@ -188,6 +194,25 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) /* * Exported WMI functions */ + +/** + * set_required_buffer_size - Sets the buffer size needed for performing IOCTL + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * + * Allocates memory needed for buffer, stores the buffer size in that memory + */ +int set_required_buffer_size(struct wmi_device *wdev, u64 length) +{ + struct wmi_block *wblock; + + wblock = container_of(wdev, struct wmi_block, dev); + wblock->req_buf_size = length; + + return 0; +} +EXPORT_SYMBOL_GPL(set_required_buffer_size); + /** * wmi_evaluate_method - Evaluate a WMI method * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -200,6 +225,28 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) */ acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) +{ + struct wmi_block *wblock = NULL; + + if (!find_guid(guid_string, &wblock)) + return AE_ERROR; + return wmidev_evaluate_method(&wblock->dev, instance, method_id, + in, out); +} +EXPORT_SYMBOL_GPL(wmi_evaluate_method); + +/** + * wmidev_evaluate_method - Evaluate a WMI method + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @method_id: Method ID to call + * &in: Buffer containing input for the method call + * &out: Empty buffer to return the method results + * + * Call an ACPI-WMI method + */ +acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, + u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) { struct guid_block *block = NULL; struct wmi_block *wblock = NULL; @@ -209,9 +256,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) union acpi_object params[3]; char method[5] = "WM"; - if (!find_guid(guid_string, &wblock)) - return AE_ERROR; - + wblock = container_of(wdev, struct wmi_block, dev); block = &wblock->gblock; handle = wblock->acpi_device->handle; @@ -246,7 +291,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) return status; } -EXPORT_SYMBOL_GPL(wmi_evaluate_method); +EXPORT_SYMBOL_GPL(wmidev_evaluate_method); static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) @@ -348,23 +393,6 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) } EXPORT_SYMBOL_GPL(wmidev_block_query); -struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, - const char *guid_string) -{ - struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev); - struct wmi_block *other_wb; - - if (!find_guid(guid_string, &other_wb)) - return NULL; - - if (other_wb->acpi_device != this_wb->acpi_device) - return NULL; - - get_device(&other_wb->dev.dev); - return &other_wb->dev; -} -EXPORT_SYMBOL_GPL(wmidev_get_other_guid); - /** * wmi_set_block - Write to a WMI block * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -761,6 +789,113 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) return 0; } +static int wmi_char_open(struct inode *inode, struct file *filp) +{ + const char *driver_name = filp->f_path.dentry->d_iname; + struct wmi_block *wblock = NULL; + struct wmi_block *next = NULL; + + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + if (!wblock->dev.dev.driver) + continue; + if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) { + filp->private_data = wblock; + break; + } + } + + if (!filp->private_data) + return -ENODEV; + + return nonseekable_open(inode, filp); +} + +static ssize_t wmi_char_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) +{ + struct wmi_block *wblock = filp->private_data; + + return simple_read_from_buffer(buffer, length, offset, + &wblock->req_buf_size, + sizeof(wblock->req_buf_size)); +} + +static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct wmi_ioctl_buffer __user *input = + (struct wmi_ioctl_buffer __user *) arg; + struct wmi_block *wblock = filp->private_data; + struct wmi_ioctl_buffer *buf = NULL; + struct wmi_driver *wdriver = NULL; + int ret; + + if (_IOC_TYPE(cmd) != WMI_IOC) + return -ENOTTY; + + /* make sure we're not calling a higher instance than exists*/ + if (_IOC_NR(cmd) >= wblock->gblock.instance_count) + return -EINVAL; + + mutex_lock(&wblock->char_mutex); + buf = wblock->handler_data; + if (get_user(buf->length, &input->length)) { + dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); + ret = -EFAULT; + goto out_ioctl; + } + /* if it's too small, abort */ + if (buf->length < wblock->req_buf_size) { + dev_err(&wblock->dev.dev, + "Buffer %lld too small, need at least %lld\n", + buf->length, wblock->req_buf_size); + ret = -EINVAL; + goto out_ioctl; + } + /* if it's too big, warn, driver will only use what is needed */ + if (buf->length > wblock->req_buf_size) + dev_warn(&wblock->dev.dev, + "Buffer %lld is bigger than required %lld\n", + buf->length, wblock->req_buf_size); + + /* copy the structure from userspace */ + if (copy_from_user(buf, input, wblock->req_buf_size)) { + dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", + wblock->req_buf_size); + ret = -EFAULT; + goto out_ioctl; + } + + /* let the driver do any filtering and do the call */ + wdriver = container_of(wblock->dev.dev.driver, + struct wmi_driver, driver); + if (!try_module_get(wdriver->driver.owner)) { + ret = -EBUSY; + goto out_ioctl; + } + ret = wdriver->filter_callback(&wblock->dev, cmd, buf); + module_put(wdriver->driver.owner); + if (ret) + goto out_ioctl; + + /* return the result (only up to our internal buffer size) */ + if (copy_to_user(input, buf, wblock->req_buf_size)) { + dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", + wblock->req_buf_size); + ret = -EFAULT; + } + +out_ioctl: + mutex_unlock(&wblock->char_mutex); + return ret; +} + +static const struct file_operations wmi_fops = { + .owner = THIS_MODULE, + .read = wmi_char_read, + .open = wmi_char_open, + .unlocked_ioctl = wmi_ioctl, + .compat_ioctl = wmi_ioctl, +}; static int wmi_dev_probe(struct device *dev) { @@ -768,16 +903,63 @@ static int wmi_dev_probe(struct device *dev) struct wmi_driver *wdriver = container_of(dev->driver, struct wmi_driver, driver); int ret = 0; + int count; + char *buf; if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) dev_warn(dev, "failed to enable device -- probing anyway\n"); if (wdriver->probe) { ret = wdriver->probe(dev_to_wdev(dev)); - if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) - dev_warn(dev, "failed to disable device\n"); + if (ret != 0) + goto probe_failure; + } + + /* driver wants a character device made */ + if (wdriver->filter_callback) { + /* check that required buffer size declared by driver or MOF */ + if (!wblock->req_buf_size) { + dev_err(&wblock->dev.dev, + "Required buffer size not set\n"); + ret = -EINVAL; + goto probe_failure; + } + + count = get_order(wblock->req_buf_size); + wblock->handler_data = (void *)__get_free_pages(GFP_KERNEL, + count); + if (!wblock->handler_data) { + ret = -ENOMEM; + goto probe_failure; + } + + buf = kmalloc(strlen(wdriver->driver.name) + 4, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto probe_string_failure; + } + sprintf(buf, "wmi/%s", wdriver->driver.name); + wblock->char_dev.minor = MISC_DYNAMIC_MINOR; + wblock->char_dev.name = buf; + wblock->char_dev.fops = &wmi_fops; + wblock->char_dev.mode = 0444; + ret = misc_register(&wblock->char_dev); + if (ret) { + dev_warn(dev, "failed to register char dev: %d", ret); + ret = -ENOMEM; + goto probe_misc_failure; + } } + return 0; + +probe_misc_failure: + kfree(buf); +probe_string_failure: + kfree(wblock->handler_data); +probe_failure: + if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) + dev_warn(dev, "failed to disable device\n"); return ret; } @@ -788,6 +970,13 @@ static int wmi_dev_remove(struct device *dev) container_of(dev->driver, struct wmi_driver, driver); int ret = 0; + if (wdriver->filter_callback) { + misc_deregister(&wblock->char_dev); + kfree(wblock->char_dev.name); + free_pages((unsigned long)wblock->handler_data, + get_order(wblock->req_buf_size)); + } + if (wdriver->remove) ret = wdriver->remove(dev_to_wdev(dev)); @@ -844,6 +1033,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, if (gblock->flags & ACPI_WMI_METHOD) { wblock->dev.dev.type = &wmi_type_method; + mutex_init(&wblock->char_mutex); goto out_init; } @@ -1145,7 +1335,7 @@ static int acpi_wmi_remove(struct platform_device *device) acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); wmi_free_devices(acpi_device); - device_unregister((struct device *)dev_get_drvdata(&device->dev)); + device_destroy(&wmi_bus_class, MKDEV(0, 0)); return 0; } @@ -1199,7 +1389,7 @@ static int acpi_wmi_probe(struct platform_device *device) return 0; err_remove_busdev: - device_unregister(wmi_bus_dev); + device_destroy(&wmi_bus_class, MKDEV(0, 0)); err_remove_notify_handler: acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, @@ -1264,8 +1454,8 @@ err_unreg_class: static void __exit acpi_wmi_exit(void) { platform_driver_unregister(&acpi_wmi_driver); - class_unregister(&wmi_bus_class); bus_unregister(&wmi_bus_type); + class_unregister(&wmi_bus_class); } subsys_initcall(acpi_wmi_init); diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 75db585a2a94..acd3ce8ecf3f 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device { unsigned period; /* PWM period expressed in clk cycles */ }; +struct atmel_tcb_channel { + u32 enabled; + u32 cmr; + u32 ra; + u32 rb; + u32 rc; +}; + struct atmel_tcb_pwm_chip { struct pwm_chip chip; spinlock_t lock; struct atmel_tc *tc; struct atmel_tcb_pwm_device *pwms[NPWM]; + struct atmel_tcb_channel bkup[NPWM / 2]; }; static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) @@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) * Use software trigger to apply the new setting. * If both PWM devices in this group are disabled we stop the clock. */ - if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) + if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) { __raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(group, CCR)); - else + tcbpwmc->bkup[group].enabled = 1; + } else { __raw_writel(ATMEL_TC_SWTRG, regs + ATMEL_TC_REG(group, CCR)); + tcbpwmc->bkup[group].enabled = 0; + } spin_unlock(&tcbpwmc->lock); } @@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) /* Use software trigger to apply the new setting */ __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs + ATMEL_TC_REG(group, CCR)); + tcbpwmc->bkup[group].enabled = 1; spin_unlock(&tcbpwmc->lock); return 0; } @@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); +#ifdef CONFIG_PM_SLEEP +static int atmel_tcb_pwm_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); + void __iomem *base = tcbpwm->tc->regs; + int i; + + for (i = 0; i < (NPWM / 2); i++) { + struct atmel_tcb_channel *chan = &tcbpwm->bkup[i]; + + chan->cmr = readl(base + ATMEL_TC_REG(i, CMR)); + chan->ra = readl(base + ATMEL_TC_REG(i, RA)); + chan->rb = readl(base + ATMEL_TC_REG(i, RB)); + chan->rc = readl(base + ATMEL_TC_REG(i, RC)); + } + return 0; +} + +static int atmel_tcb_pwm_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); + void __iomem *base = tcbpwm->tc->regs; + int i; + + for (i = 0; i < (NPWM / 2); i++) { + struct atmel_tcb_channel *chan = &tcbpwm->bkup[i]; + + writel(chan->cmr, base + ATMEL_TC_REG(i, CMR)); + writel(chan->ra, base + ATMEL_TC_REG(i, RA)); + writel(chan->rb, base + ATMEL_TC_REG(i, RB)); + writel(chan->rc, base + ATMEL_TC_REG(i, RC)); + if (chan->enabled) { + writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, + base + ATMEL_TC_REG(i, CCR)); + } + } + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, + atmel_tcb_pwm_resume); + static struct platform_driver atmel_tcb_pwm_driver = { .driver = { .name = "atmel-tcb-pwm", .of_match_table = atmel_tcb_pwm_dt_ids, + .pm = &atmel_tcb_pwm_pm_ops, }, .probe = atmel_tcb_pwm_probe, .remove = atmel_tcb_pwm_remove, diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index 2fb30deee345..815f5333bb8f 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,8 @@ #define PERIP_PWM_PDM_CONTROL_CH_MASK 0x1 #define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch) ((ch) * 4) +#define IMG_PWM_PM_TIMEOUT 1000 /* ms */ + /* * PWM period is specified with a timebase register, * in number of step periods. The PWM duty cycle is also @@ -52,6 +55,8 @@ */ #define MIN_TMBASE_STEPS 16 +#define IMG_PWM_NPWM 4 + struct img_pwm_soc_data { u32 max_timebase; }; @@ -66,6 +71,8 @@ struct img_pwm_chip { int max_period_ns; int min_period_ns; const struct img_pwm_soc_data *data; + u32 suspend_ctrl_cfg; + u32 suspend_ch_cfg[IMG_PWM_NPWM]; }; static inline struct img_pwm_chip *to_img_pwm_chip(struct pwm_chip *chip) @@ -92,6 +99,7 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long mul, output_clk_hz, input_clk_hz; struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); unsigned int max_timebase = pwm_chip->data->max_timebase; + int ret; if (period_ns < pwm_chip->min_period_ns || period_ns > pwm_chip->max_period_ns) { @@ -123,6 +131,10 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty = DIV_ROUND_UP(timebase * duty_ns, period_ns); + ret = pm_runtime_get_sync(chip->dev); + if (ret < 0) + return ret; + val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm)); val |= (div & PWM_CTRL_CFG_DIV_MASK) << @@ -133,6 +145,9 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, (timebase << PWM_CH_CFG_TMBASE_SHIFT); img_pwm_writel(pwm_chip, PWM_CH_CFG(pwm->hwpwm), val); + pm_runtime_mark_last_busy(chip->dev); + pm_runtime_put_autosuspend(chip->dev); + return 0; } @@ -140,6 +155,11 @@ static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { u32 val; struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); + int ret; + + ret = pm_runtime_get_sync(chip->dev); + if (ret < 0) + return ret; val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); val |= BIT(pwm->hwpwm); @@ -160,6 +180,9 @@ static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); val &= ~BIT(pwm->hwpwm); img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); + + pm_runtime_mark_last_busy(chip->dev); + pm_runtime_put_autosuspend(chip->dev); } static const struct pwm_ops img_pwm_ops = { @@ -182,6 +205,37 @@ static const struct of_device_id img_pwm_of_match[] = { }; MODULE_DEVICE_TABLE(of, img_pwm_of_match); +static int img_pwm_runtime_suspend(struct device *dev) +{ + struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); + + clk_disable_unprepare(pwm_chip->pwm_clk); + clk_disable_unprepare(pwm_chip->sys_clk); + + return 0; +} + +static int img_pwm_runtime_resume(struct device *dev) +{ + struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(pwm_chip->sys_clk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable sys clock\n"); + return ret; + } + + ret = clk_prepare_enable(pwm_chip->pwm_clk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable pwm clock\n"); + clk_disable_unprepare(pwm_chip->sys_clk); + return ret; + } + + return 0; +} + static int img_pwm_probe(struct platform_device *pdev) { int ret; @@ -224,23 +278,20 @@ static int img_pwm_probe(struct platform_device *pdev) return PTR_ERR(pwm->pwm_clk); } - ret = clk_prepare_enable(pwm->sys_clk); - if (ret < 0) { - dev_err(&pdev->dev, "could not prepare or enable sys clock\n"); - return ret; - } - - ret = clk_prepare_enable(pwm->pwm_clk); - if (ret < 0) { - dev_err(&pdev->dev, "could not prepare or enable pwm clock\n"); - goto disable_sysclk; + pm_runtime_set_autosuspend_delay(&pdev->dev, IMG_PWM_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_pwm_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; } clk_rate = clk_get_rate(pwm->pwm_clk); if (!clk_rate) { dev_err(&pdev->dev, "pwm clock has no frequency\n"); ret = -EINVAL; - goto disable_pwmclk; + goto err_suspend; } /* The maximum input clock divider is 512 */ @@ -255,21 +306,23 @@ static int img_pwm_probe(struct platform_device *pdev) pwm->chip.dev = &pdev->dev; pwm->chip.ops = &img_pwm_ops; pwm->chip.base = -1; - pwm->chip.npwm = 4; + pwm->chip.npwm = IMG_PWM_NPWM; ret = pwmchip_add(&pwm->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); - goto disable_pwmclk; + goto err_suspend; } platform_set_drvdata(pdev, pwm); return 0; -disable_pwmclk: - clk_disable_unprepare(pwm->pwm_clk); -disable_sysclk: - clk_disable_unprepare(pwm->sys_clk); +err_suspend: + if (!pm_runtime_enabled(&pdev->dev)) + img_pwm_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); return ret; } @@ -278,6 +331,11 @@ static int img_pwm_remove(struct platform_device *pdev) struct img_pwm_chip *pwm_chip = platform_get_drvdata(pdev); u32 val; unsigned int i; + int ret; + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + return ret; for (i = 0; i < pwm_chip->chip.npwm; i++) { val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); @@ -285,15 +343,79 @@ static int img_pwm_remove(struct platform_device *pdev) img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); } - clk_disable_unprepare(pwm_chip->pwm_clk); - clk_disable_unprepare(pwm_chip->sys_clk); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_pwm_runtime_suspend(&pdev->dev); return pwmchip_remove(&pwm_chip->chip); } +#ifdef CONFIG_PM_SLEEP +static int img_pwm_suspend(struct device *dev) +{ + struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); + int i, ret; + + if (pm_runtime_status_suspended(dev)) { + ret = img_pwm_runtime_resume(dev); + if (ret) + return ret; + } + + for (i = 0; i < pwm_chip->chip.npwm; i++) + pwm_chip->suspend_ch_cfg[i] = img_pwm_readl(pwm_chip, + PWM_CH_CFG(i)); + + pwm_chip->suspend_ctrl_cfg = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + + img_pwm_runtime_suspend(dev); + + return 0; +} + +static int img_pwm_resume(struct device *dev) +{ + struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); + int ret; + int i; + + ret = img_pwm_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < pwm_chip->chip.npwm; i++) + img_pwm_writel(pwm_chip, PWM_CH_CFG(i), + pwm_chip->suspend_ch_cfg[i]); + + img_pwm_writel(pwm_chip, PWM_CTRL_CFG, pwm_chip->suspend_ctrl_cfg); + + for (i = 0; i < pwm_chip->chip.npwm; i++) + if (pwm_chip->suspend_ctrl_cfg & BIT(i)) + regmap_update_bits(pwm_chip->periph_regs, + PERIP_PWM_PDM_CONTROL, + PERIP_PWM_PDM_CONTROL_CH_MASK << + PERIP_PWM_PDM_CONTROL_CH_SHIFT(i), + 0); + + if (pm_runtime_status_suspended(dev)) + img_pwm_runtime_suspend(dev); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops img_pwm_pm_ops = { + SET_RUNTIME_PM_OPS(img_pwm_runtime_suspend, + img_pwm_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_pwm_suspend, img_pwm_resume) +}; + static struct platform_driver img_pwm_driver = { .driver = { .name = "img-pwm", + .pm = &img_pwm_pm_ops, .of_match_table = img_pwm_of_match, }, .probe = img_pwm_probe, diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index b52f3afb2ba1..f5d97e0ad52b 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -40,11 +41,19 @@ enum { MTK_CLK_PWM3, MTK_CLK_PWM4, MTK_CLK_PWM5, + MTK_CLK_PWM6, + MTK_CLK_PWM7, + MTK_CLK_PWM8, MTK_CLK_MAX, }; -static const char * const mtk_pwm_clk_name[] = { - "main", "top", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5" +static const char * const mtk_pwm_clk_name[MTK_CLK_MAX] = { + "main", "top", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5", "pwm6", "pwm7", + "pwm8" +}; + +struct mtk_pwm_platform_data { + unsigned int num_pwms; }; /** @@ -59,6 +68,10 @@ struct mtk_pwm_chip { struct clk *clks[MTK_CLK_MAX]; }; +static const unsigned int mtk_pwm_reg_offset[] = { + 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220 +}; + static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) { return container_of(chip, struct mtk_pwm_chip, chip); @@ -103,14 +116,14 @@ static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm) static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, unsigned int offset) { - return readl(chip->regs + 0x10 + (num * 0x40) + offset); + return readl(chip->regs + mtk_pwm_reg_offset[num] + offset); } static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip, unsigned int num, unsigned int offset, u32 value) { - writel(value, chip->regs + 0x10 + (num * 0x40) + offset); + writel(value, chip->regs + mtk_pwm_reg_offset[num] + offset); } static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -185,6 +198,7 @@ static const struct pwm_ops mtk_pwm_ops = { static int mtk_pwm_probe(struct platform_device *pdev) { + const struct mtk_pwm_platform_data *data; struct mtk_pwm_chip *pc; struct resource *res; unsigned int i; @@ -194,15 +208,22 @@ static int mtk_pwm_probe(struct platform_device *pdev) if (!pc) return -ENOMEM; + data = of_device_get_match_data(&pdev->dev); + if (data == NULL) + return -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pc->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pc->regs)) return PTR_ERR(pc->regs); - for (i = 0; i < MTK_CLK_MAX; i++) { + for (i = 0; i < data->num_pwms + 2; i++) { pc->clks[i] = devm_clk_get(&pdev->dev, mtk_pwm_clk_name[i]); - if (IS_ERR(pc->clks[i])) + if (IS_ERR(pc->clks[i])) { + dev_err(&pdev->dev, "clock: %s fail: %ld\n", + mtk_pwm_clk_name[i], PTR_ERR(pc->clks[i])); return PTR_ERR(pc->clks[i]); + } } platform_set_drvdata(pdev, pc); @@ -210,7 +231,7 @@ static int mtk_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &mtk_pwm_ops; pc->chip.base = -1; - pc->chip.npwm = 5; + pc->chip.npwm = data->num_pwms; ret = pwmchip_add(&pc->chip); if (ret < 0) { @@ -228,9 +249,23 @@ static int mtk_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } +static const struct mtk_pwm_platform_data mt2712_pwm_data = { + .num_pwms = 8, +}; + +static const struct mtk_pwm_platform_data mt7622_pwm_data = { + .num_pwms = 6, +}; + +static const struct mtk_pwm_platform_data mt7623_pwm_data = { + .num_pwms = 5, +}; + static const struct of_device_id mtk_pwm_of_match[] = { - { .compatible = "mediatek,mt7623-pwm" }, - { } + { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data }, + { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data }, + { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data }, + { }, }; MODULE_DEVICE_TABLE(of, mtk_pwm_of_match); diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c index 9793b296108f..1ac9e4384142 100644 --- a/drivers/pwm/pwm-stm32-lp.c +++ b/drivers/pwm/pwm-stm32-lp.c @@ -219,8 +219,7 @@ static int stm32_pwm_lp_remove(struct platform_device *pdev) unsigned int i; for (i = 0; i < priv->chip.npwm; i++) - if (pwm_is_enabled(&priv->chip.pwms[i])) - pwm_disable(&priv->chip.pwms[i]); + pwm_disable(&priv->chip.pwms[i]); return pwmchip_remove(&priv->chip); } diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 6d23f1d1c9b7..334199c58f1d 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -368,14 +368,15 @@ static int sun4i_pwm_probe(struct platform_device *pdev) struct sun4i_pwm_chip *pwm; struct resource *res; int ret; - const struct of_device_id *match; - - match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev); pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); if (!pwm) return -ENOMEM; + pwm->data = of_device_get_match_data(&pdev->dev); + if (!pwm->data) + return -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pwm->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pwm->base)) @@ -385,7 +386,6 @@ static int sun4i_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm->clk)) return PTR_ERR(pwm->clk); - pwm->data = match->data; pwm->chip.dev = &pdev->dev; pwm->chip.ops = &sun4i_pwm_ops; pwm->chip.base = -1; diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c index 5c1b6388122a..ec4bc1515f0d 100644 --- a/drivers/rapidio/devices/rio_mport_cdev.c +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -889,11 +889,9 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode, goto err_req; } - pinned = get_user_pages_unlocked( + pinned = get_user_pages_fast( (unsigned long)xfer->loc_addr & PAGE_MASK, - nr_pages, - page_list, - dir == DMA_FROM_DEVICE ? FOLL_WRITE : 0); + nr_pages, dir == DMA_FROM_DEVICE, page_list); if (pinned != nr_pages) { if (pinned < 0) { @@ -961,9 +959,10 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode, nents = dma_map_sg(chan->device->dev, req->sgt.sgl, req->sgt.nents, dir); - if (nents == -EFAULT) { + if (nents == 0) { rmcd_error("Failed to map SG list"); - return -EFAULT; + ret = -EFAULT; + goto err_pg; } ret = do_dma_request(req, xfer, sync, nents); diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c index e67b923b1ca6..4931ed790428 100644 --- a/drivers/rapidio/switches/idt_gen2.c +++ b/drivers/rapidio/switches/idt_gen2.c @@ -458,7 +458,7 @@ static void idtg2_remove(struct rio_dev *rdev) idtg2_sysfs(rdev, false); } -static struct rio_device_id idtg2_id_table[] = { +static const struct rio_device_id idtg2_id_table[] = { {RIO_DEVICE(RIO_DID_IDTCPS1848, RIO_VID_IDT)}, {RIO_DEVICE(RIO_DID_IDTCPS1616, RIO_VID_IDT)}, {RIO_DEVICE(RIO_DID_IDTVPS1616, RIO_VID_IDT)}, diff --git a/drivers/rapidio/switches/idt_gen3.c b/drivers/rapidio/switches/idt_gen3.c index c5923a547bed..85a3908294d9 100644 --- a/drivers/rapidio/switches/idt_gen3.c +++ b/drivers/rapidio/switches/idt_gen3.c @@ -348,7 +348,7 @@ static void idtg3_shutdown(struct rio_dev *rdev) } } -static struct rio_device_id idtg3_id_table[] = { +static const struct rio_device_id idtg3_id_table[] = { {RIO_DEVICE(RIO_DID_IDTRXS1632, RIO_VID_IDT)}, {RIO_DEVICE(RIO_DID_IDTRXS2448, RIO_VID_IDT)}, { 0, } /* terminate list */ diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c index 7fbb60d31796..4058ce2c76fa 100644 --- a/drivers/rapidio/switches/idtcps.c +++ b/drivers/rapidio/switches/idtcps.c @@ -168,7 +168,7 @@ static void idtcps_remove(struct rio_dev *rdev) spin_unlock(&rdev->rswitch->lock); } -static struct rio_device_id idtcps_id_table[] = { +static const struct rio_device_id idtcps_id_table[] = { {RIO_DEVICE(RIO_DID_IDTCPS6Q, RIO_VID_IDT)}, {RIO_DEVICE(RIO_DID_IDTCPS8, RIO_VID_IDT)}, {RIO_DEVICE(RIO_DID_IDTCPS10Q, RIO_VID_IDT)}, diff --git a/drivers/rapidio/switches/tsi568.c b/drivers/rapidio/switches/tsi568.c index 8a43561b9d17..1214628b7ded 100644 --- a/drivers/rapidio/switches/tsi568.c +++ b/drivers/rapidio/switches/tsi568.c @@ -169,7 +169,7 @@ static void tsi568_remove(struct rio_dev *rdev) spin_unlock(&rdev->rswitch->lock); } -static struct rio_device_id tsi568_id_table[] = { +static const struct rio_device_id tsi568_id_table[] = { {RIO_DEVICE(RIO_DID_TSI568, RIO_VID_TUNDRA)}, { 0, } /* terminate list */ }; diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c index 2700d15f7584..9f063e214836 100644 --- a/drivers/rapidio/switches/tsi57x.c +++ b/drivers/rapidio/switches/tsi57x.c @@ -336,7 +336,7 @@ static void tsi57x_remove(struct rio_dev *rdev) spin_unlock(&rdev->rswitch->lock); } -static struct rio_device_id tsi57x_id_table[] = { +static const struct rio_device_id tsi57x_id_table[] = { {RIO_DEVICE(RIO_DID_TSI572, RIO_VID_TUNDRA)}, {RIO_DEVICE(RIO_DID_TSI574, RIO_VID_TUNDRA)}, {RIO_DEVICE(RIO_DID_TSI577, RIO_VID_TUNDRA)}, diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index bf04479456a0..b609e1d3654b 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -28,7 +28,6 @@ config OMAP_REMOTEPROC depends on OMAP_IOMMU select MAILBOX select OMAP2PLUS_MBOX - select RPMSG_VIRTIO help Say y here to support OMAP's remote processors (dual M3 and DSP on OMAP4) via the remote processor framework. @@ -58,7 +57,6 @@ config DA8XX_REMOTEPROC tristate "DA8xx/OMAP-L13x remoteproc support" depends on ARCH_DAVINCI_DA8XX depends on DMA_CMA - select RPMSG_VIRTIO help Say y here to support DA8xx/OMAP-L13x remote processors via the remote processor framework. @@ -79,7 +77,6 @@ config DA8XX_REMOTEPROC config KEYSTONE_REMOTEPROC tristate "Keystone Remoteproc support" depends on ARCH_KEYSTONE - select RPMSG_VIRTIO help Say Y here here to support Keystone remote processors (DSP) via the remote processor framework. @@ -135,7 +132,6 @@ config ST_REMOTEPROC depends on ARCH_STI select MAILBOX select STI_MBOX - select RPMSG_VIRTIO help Say y here to support ST's adjunct processors via the remote processor framework. diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 2d3d5ac92c06..8a3fa2bcc9f6 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "remoteproc_internal.h" #include "qcom_common.h" @@ -64,6 +65,8 @@ #define QDSP6SS_RESET_REG 0x014 #define QDSP6SS_GFMUX_CTL_REG 0x020 #define QDSP6SS_PWR_CTL_REG 0x030 +#define QDSP6SS_MEM_PWR_CTL 0x0B0 +#define QDSP6SS_STRAP_ACC 0x110 /* AXI Halt Register Offsets */ #define AXI_HALTREQ_REG 0x0 @@ -92,6 +95,15 @@ #define QDSS_BHS_ON BIT(21) #define QDSS_LDO_BYP BIT(22) +/* QDSP6v56 parameters */ +#define QDSP6v56_LDO_BYP BIT(25) +#define QDSP6v56_BHS_ON BIT(24) +#define QDSP6v56_CLAMP_WL BIT(21) +#define QDSP6v56_CLAMP_QMC_MEM BIT(22) +#define HALT_CHECK_MAX_LOOPS 200 +#define QDSP6SS_XO_CBCR 0x0038 +#define QDSP6SS_ACC_OVERRIDE_VAL 0x20 + struct reg_info { struct regulator *reg; int uV; @@ -110,6 +122,8 @@ struct rproc_hexagon_res { struct qcom_mss_reg_res *active_supply; char **proxy_clk_names; char **active_clk_names; + int version; + bool need_mem_protection; }; struct q6v5 { @@ -154,6 +168,16 @@ struct q6v5 { struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; + bool need_mem_protection; + int mpss_perm; + int mba_perm; + int version; +}; + +enum { + MSS_MSM8916, + MSS_MSM8974, + MSS_MSM8996, }; static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, @@ -289,6 +313,26 @@ static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc, return &table; } +static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, + bool remote_owner, phys_addr_t addr, + size_t size) +{ + struct qcom_scm_vmperm next; + + if (!qproc->need_mem_protection) + return 0; + if (remote_owner && *current_perm == BIT(QCOM_SCM_VMID_MSS_MSA)) + return 0; + if (!remote_owner && *current_perm == BIT(QCOM_SCM_VMID_HLOS)) + return 0; + + next.vmid = remote_owner ? QCOM_SCM_VMID_MSS_MSA : QCOM_SCM_VMID_HLOS; + next.perm = remote_owner ? QCOM_SCM_PERM_RW : QCOM_SCM_PERM_RWX; + + return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), + current_perm, &next, 1); +} + static int q6v5_load(struct rproc *rproc, const struct firmware *fw) { struct q6v5 *qproc = rproc->priv; @@ -353,33 +397,98 @@ static int q6v5proc_reset(struct q6v5 *qproc) { u32 val; int ret; + int i; - /* Assert resets, stop core */ - val = readl(qproc->reg_base + QDSP6SS_RESET_REG); - val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE); - writel(val, qproc->reg_base + QDSP6SS_RESET_REG); - /* Enable power block headswitch, and wait for it to stabilize */ - val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= QDSS_BHS_ON | QDSS_LDO_BYP; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - udelay(1); - - /* - * Turn on memories. L2 banks should be done individually - * to minimize inrush current. - */ - val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N | - Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_L2DATA_SLP_NRET_N_2; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_L2DATA_SLP_NRET_N_1; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_L2DATA_SLP_NRET_N_0; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + if (qproc->version == MSS_MSM8996) { + /* Override the ACC value if required */ + writel(QDSP6SS_ACC_OVERRIDE_VAL, + qproc->reg_base + QDSP6SS_STRAP_ACC); + /* Assert resets, stop core */ + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); + val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); + + /* BHS require xo cbcr to be enabled */ + val = readl(qproc->reg_base + QDSP6SS_XO_CBCR); + val |= 0x1; + writel(val, qproc->reg_base + QDSP6SS_XO_CBCR); + + /* Read CLKOFF bit to go low indicating CLK is enabled */ + ret = readl_poll_timeout(qproc->reg_base + QDSP6SS_XO_CBCR, + val, !(val & BIT(31)), 1, + HALT_CHECK_MAX_LOOPS); + if (ret) { + dev_err(qproc->dev, + "xo cbcr enabling timed out (rc:%d)\n", ret); + return ret; + } + /* Enable power block headswitch and wait for it to stabilize */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= QDSP6v56_BHS_ON; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + udelay(1); + + /* Put LDO in bypass mode */ + val |= QDSP6v56_LDO_BYP; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Deassert QDSP6 compiler memory clamp */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val &= ~QDSP6v56_CLAMP_QMC_MEM; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Deassert memory peripheral sleep and L2 memory standby */ + val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Turn on L1, L2, ETB and JU memories 1 at a time */ + val = readl(qproc->reg_base + QDSP6SS_MEM_PWR_CTL); + for (i = 19; i >= 0; i--) { + val |= BIT(i); + writel(val, qproc->reg_base + + QDSP6SS_MEM_PWR_CTL); + /* + * Read back value to ensure the write is done then + * wait for 1us for both memory peripheral and data + * array to turn on. + */ + val |= readl(qproc->reg_base + QDSP6SS_MEM_PWR_CTL); + udelay(1); + } + /* Remove word line clamp */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val &= ~QDSP6v56_CLAMP_WL; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + } else { + /* Assert resets, stop core */ + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); + val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); + + /* Enable power block headswitch and wait for it to stabilize */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= QDSS_BHS_ON | QDSS_LDO_BYP; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + udelay(1); + /* + * Turn on memories. L2 banks should be done individually + * to minimize inrush current. + */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N | + Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_L2DATA_SLP_NRET_N_2; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_L2DATA_SLP_NRET_N_1; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_L2DATA_SLP_NRET_N_0; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + } /* Remove IO clamp */ val &= ~Q6SS_CLAMP_IO; writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); @@ -451,6 +560,8 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) { unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; dma_addr_t phys; + int mdata_perm; + int xferop_ret; void *ptr; int ret; @@ -462,6 +573,17 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) memcpy(ptr, fw->data, fw->size); + /* Hypervisor mapping to access metadata by modem */ + mdata_perm = BIT(QCOM_SCM_VMID_HLOS); + ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, + true, phys, fw->size); + if (ret) { + dev_err(qproc->dev, + "assigning Q6 access to metadata failed: %d\n", ret); + ret = -EAGAIN; + goto free_dma_attrs; + } + writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG); writel(RMB_CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); @@ -471,6 +593,14 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) else if (ret < 0) dev_err(qproc->dev, "MPSS header authentication failed: %d\n", ret); + /* Metadata authentication done, remove modem access */ + xferop_ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, + false, phys, fw->size); + if (xferop_ret) + dev_warn(qproc->dev, + "mdt buffer not reclaimed system may become unstable\n"); + +free_dma_attrs: dma_free_attrs(qproc->dev, fw->size, ptr, phys, dma_attrs); return ret < 0 ? ret : 0; @@ -504,7 +634,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) bool relocate = false; char seg_name[10]; ssize_t offset; - size_t size; + size_t size = 0; void *ptr; int ret; int i; @@ -542,7 +672,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) } mpss_reloc = relocate ? min_addr : qproc->mpss_phys; - + /* Load firmware segments */ for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; @@ -575,18 +705,24 @@ static int q6v5_mpss_load(struct q6v5 *qproc) memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); } - - size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); - if (!size) { - boot_addr = relocate ? qproc->mpss_phys : min_addr; - writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); - writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); - } - size += phdr->p_memsz; - writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); } + /* Transfer ownership of modem ddr region to q6 */ + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, + qproc->mpss_phys, qproc->mpss_size); + if (ret) { + dev_err(qproc->dev, + "assigning Q6 access to mpss memory failed: %d\n", ret); + ret = -EAGAIN; + goto release_firmware; + } + + boot_addr = relocate ? qproc->mpss_phys : min_addr; + writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); + writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); + writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); + ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_AUTH_COMPLETE, 10000); if (ret == -ETIMEDOUT) dev_err(qproc->dev, "MPSS authentication timed out\n"); @@ -602,6 +738,7 @@ release_firmware: static int q6v5_start(struct rproc *rproc) { struct q6v5 *qproc = (struct q6v5 *)rproc->priv; + int xfermemop_ret; int ret; ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, @@ -637,11 +774,22 @@ static int q6v5_start(struct rproc *rproc) goto assert_reset; } + /* Assign MBA image access in DDR to q6 */ + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, + qproc->mba_phys, + qproc->mba_size); + if (xfermemop_ret) { + dev_err(qproc->dev, + "assigning Q6 access to mba memory failed: %d\n", + xfermemop_ret); + goto disable_active_clks; + } + writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); ret = q6v5proc_reset(qproc); if (ret) - goto halt_axi_ports; + goto reclaim_mba; ret = q6v5_rmb_mba_wait(qproc, 0, 5000); if (ret == -ETIMEDOUT) { @@ -658,16 +806,22 @@ static int q6v5_start(struct rproc *rproc) ret = q6v5_mpss_load(qproc); if (ret) - goto halt_axi_ports; + goto reclaim_mpss; ret = wait_for_completion_timeout(&qproc->start_done, msecs_to_jiffies(5000)); if (ret == 0) { dev_err(qproc->dev, "start timed out\n"); ret = -ETIMEDOUT; - goto halt_axi_ports; + goto reclaim_mpss; } + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, + qproc->mba_phys, + qproc->mba_size); + if (xfermemop_ret) + dev_err(qproc->dev, + "Failed to reclaim mba buffer system may become unstable\n"); qproc->running = true; q6v5_clk_disable(qproc->dev, qproc->proxy_clks, @@ -677,12 +831,30 @@ static int q6v5_start(struct rproc *rproc) return 0; +reclaim_mpss: + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, + false, qproc->mpss_phys, + qproc->mpss_size); + WARN_ON(xfermemop_ret); + halt_axi_ports: q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); + +reclaim_mba: + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, + qproc->mba_phys, + qproc->mba_size); + if (xfermemop_ret) { + dev_err(qproc->dev, + "Failed to reclaim mba buffer, system may become unstable\n"); + } + +disable_active_clks: q6v5_clk_disable(qproc->dev, qproc->active_clks, qproc->active_clk_count); + assert_reset: reset_control_assert(qproc->mss_restart); disable_vdd: @@ -702,6 +874,7 @@ static int q6v5_stop(struct rproc *rproc) { struct q6v5 *qproc = (struct q6v5 *)rproc->priv; int ret; + u32 val; qproc->running = false; @@ -718,6 +891,20 @@ static int q6v5_stop(struct rproc *rproc) q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); + if (qproc->version == MSS_MSM8996) { + /* + * To avoid high MX current during LPASS/MSS restart. + */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_CLAMP_IO | QDSP6v56_CLAMP_WL | + QDSP6v56_CLAMP_QMC_MEM; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + } + + + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, false, + qproc->mpss_phys, qproc->mpss_size); + WARN_ON(ret); reset_control_assert(qproc->mss_restart); q6v5_clk_disable(qproc->dev, qproc->active_clks, @@ -1017,6 +1204,8 @@ static int q6v5_probe(struct platform_device *pdev) if (ret) goto free_rproc; + qproc->version = desc->version; + qproc->need_mem_protection = desc->need_mem_protection; ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt); if (ret < 0) goto free_rproc; @@ -1038,7 +1227,8 @@ static int q6v5_probe(struct platform_device *pdev) ret = PTR_ERR(qproc->state); goto free_rproc; } - + qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); + qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); @@ -1067,6 +1257,24 @@ static int q6v5_remove(struct platform_device *pdev) return 0; } +static const struct rproc_hexagon_res msm8996_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_clk_names = (char*[]){ + "xo", + "pnoc", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + "gpll0_mss_clk", + NULL + }, + .need_mem_protection = true, + .version = MSS_MSM8996, +}; + static const struct rproc_hexagon_res msm8916_mss = { .hexagon_mba_image = "mba.mbn", .proxy_supply = (struct qcom_mss_reg_res[]) { @@ -1094,6 +1302,8 @@ static const struct rproc_hexagon_res msm8916_mss = { "mem", NULL }, + .need_mem_protection = false, + .version = MSS_MSM8916, }; static const struct rproc_hexagon_res msm8974_mss = { @@ -1131,12 +1341,15 @@ static const struct rproc_hexagon_res msm8974_mss = { "mem", NULL }, + .need_mem_protection = false, + .version = MSS_MSM8974, }; static const struct of_device_id q6v5_of_match[] = { { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss}, { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss}, { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss}, + { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss}, { }, }; MODULE_DEVICE_TABLE(of, q6v5_of_match); diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 1c122e230cec..a20488336aa0 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -155,6 +155,132 @@ static const struct file_operations rproc_recovery_ops = { .llseek = generic_file_llseek, }; +/* Expose resource table content via debugfs */ +static int rproc_rsc_table_show(struct seq_file *seq, void *p) +{ + static const char * const types[] = {"carveout", "devmem", "trace", "vdev"}; + struct rproc *rproc = seq->private; + struct resource_table *table = rproc->table_ptr; + struct fw_rsc_carveout *c; + struct fw_rsc_devmem *d; + struct fw_rsc_trace *t; + struct fw_rsc_vdev *v; + int i, j; + + if (!table) { + seq_puts(seq, "No resource table found\n"); + return 0; + } + + for (i = 0; i < table->num; i++) { + int offset = table->offset[i]; + struct fw_rsc_hdr *hdr = (void *)table + offset; + void *rsc = (void *)hdr + sizeof(*hdr); + + switch (hdr->type) { + case RSC_CARVEOUT: + c = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + seq_printf(seq, " Device Address 0x%x\n", c->da); + seq_printf(seq, " Physical Address 0x%x\n", c->pa); + seq_printf(seq, " Length 0x%x Bytes\n", c->len); + seq_printf(seq, " Flags 0x%x\n", c->flags); + seq_printf(seq, " Reserved (should be zero) [%d]\n", c->reserved); + seq_printf(seq, " Name %s\n\n", c->name); + break; + case RSC_DEVMEM: + d = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + seq_printf(seq, " Device Address 0x%x\n", d->da); + seq_printf(seq, " Physical Address 0x%x\n", d->pa); + seq_printf(seq, " Length 0x%x Bytes\n", d->len); + seq_printf(seq, " Flags 0x%x\n", d->flags); + seq_printf(seq, " Reserved (should be zero) [%d]\n", d->reserved); + seq_printf(seq, " Name %s\n\n", d->name); + break; + case RSC_TRACE: + t = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + seq_printf(seq, " Device Address 0x%x\n", t->da); + seq_printf(seq, " Length 0x%x Bytes\n", t->len); + seq_printf(seq, " Reserved (should be zero) [%d]\n", t->reserved); + seq_printf(seq, " Name %s\n\n", t->name); + break; + case RSC_VDEV: + v = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + + seq_printf(seq, " ID %d\n", v->id); + seq_printf(seq, " Notify ID %d\n", v->notifyid); + seq_printf(seq, " Device features 0x%x\n", v->dfeatures); + seq_printf(seq, " Guest features 0x%x\n", v->gfeatures); + seq_printf(seq, " Config length 0x%x\n", v->config_len); + seq_printf(seq, " Status 0x%x\n", v->status); + seq_printf(seq, " Number of vrings %d\n", v->num_of_vrings); + seq_printf(seq, " Reserved (should be zero) [%d][%d]\n\n", + v->reserved[0], v->reserved[1]); + + for (j = 0; j < v->num_of_vrings; j++) { + seq_printf(seq, " Vring %d\n", j); + seq_printf(seq, " Device Address 0x%x\n", v->vring[j].da); + seq_printf(seq, " Alignment %d\n", v->vring[j].align); + seq_printf(seq, " Number of buffers %d\n", v->vring[j].num); + seq_printf(seq, " Notify ID %d\n", v->vring[j].notifyid); + seq_printf(seq, " Physical Address 0x%x\n\n", + v->vring[j].pa); + } + break; + default: + seq_printf(seq, "Unknown resource type found: %d [hdr: %p]\n", + hdr->type, hdr); + break; + } + } + + return 0; +} + +static int rproc_rsc_table_open(struct inode *inode, struct file *file) +{ + return single_open(file, rproc_rsc_table_show, inode->i_private); +} + +static const struct file_operations rproc_rsc_table_ops = { + .open = rproc_rsc_table_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Expose carveout content via debugfs */ +static int rproc_carveouts_show(struct seq_file *seq, void *p) +{ + struct rproc *rproc = seq->private; + struct rproc_mem_entry *carveout; + + list_for_each_entry(carveout, &rproc->carveouts, node) { + seq_puts(seq, "Carveout memory entry:\n"); + seq_printf(seq, "\tVirtual address: %p\n", carveout->va); + seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); + seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); + seq_printf(seq, "\tLength: 0x%x Bytes\n\n", carveout->len); + } + + return 0; +} + +static int rproc_carveouts_open(struct inode *inode, struct file *file) +{ + return single_open(file, rproc_carveouts_show, inode->i_private); +} + +static const struct file_operations rproc_carveouts_ops = { + .open = rproc_carveouts_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + void rproc_remove_trace_file(struct dentry *tfile) { debugfs_remove(tfile); @@ -198,6 +324,10 @@ void rproc_create_debug_dir(struct rproc *rproc) rproc, &rproc_name_ops); debugfs_create_file("recovery", 0400, rproc->dbg_dir, rproc, &rproc_recovery_ops); + debugfs_create_file("resource_table", 0400, rproc->dbg_dir, + rproc, &rproc_rsc_table_ops); + debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, + rproc, &rproc_carveouts_ops); } void __init rproc_init_debugfs(void) diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index 0fe6eac46512..65a9f6b892f0 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -47,7 +47,8 @@ config RPMSG_QCOM_SMD platforms. config RPMSG_VIRTIO - tristate + tristate "Virtio RPMSG bus driver" + depends on HAS_DMA select RPMSG select VIRTIO diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 5dcc9bf1c5bc..40d76d2a5eff 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -227,6 +227,7 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, init_completion(&channel->open_req); init_completion(&channel->open_ack); + init_completion(&channel->intent_req_comp); INIT_LIST_HEAD(&channel->done_intents); INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); @@ -1148,19 +1149,38 @@ static struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev, static int qcom_glink_announce_create(struct rpmsg_device *rpdev) { struct glink_channel *channel = to_glink_channel(rpdev->ept); - struct glink_core_rx_intent *intent; + struct device_node *np = rpdev->dev.of_node; struct qcom_glink *glink = channel->glink; - int num_intents = glink->intentless ? 0 : 5; + struct glink_core_rx_intent *intent; + const struct property *prop = NULL; + __be32 defaults[] = { cpu_to_be32(SZ_1K), cpu_to_be32(5) }; + int num_intents; + int num_groups = 1; + __be32 *val = defaults; + int size; + + if (glink->intentless) + return 0; + + prop = of_find_property(np, "qcom,intents", NULL); + if (prop) { + val = prop->value; + num_groups = prop->length / sizeof(u32) / 2; + } /* Channel is now open, advertise base set of intents */ - while (num_intents--) { - intent = qcom_glink_alloc_intent(glink, channel, SZ_1K, true); - if (!intent) - break; + while (num_groups--) { + size = be32_to_cpup(val++); + num_intents = be32_to_cpup(val++); + while (num_intents--) { + intent = qcom_glink_alloc_intent(glink, channel, size, + true); + if (!intent) + break; - qcom_glink_advertise_intent(glink, channel, intent); + qcom_glink_advertise_intent(glink, channel, intent); + } } - return 0; } @@ -1237,11 +1257,16 @@ static int __qcom_glink_send(struct glink_channel *channel, spin_lock_irqsave(&channel->intent_lock, flags); idr_for_each_entry(&channel->riids, tmp, iid) { if (tmp->size >= len && !tmp->in_use) { - tmp->in_use = true; - intent = tmp; - break; + if (!intent) + intent = tmp; + else if (intent->size > tmp->size) + intent = tmp; + if (intent->size == len) + break; } } + if (intent) + intent->in_use = true; spin_unlock_irqrestore(&channel->intent_lock, flags); /* We found an available intent */ @@ -1551,6 +1576,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, idr_init(&glink->rcids); glink->mbox_client.dev = dev; + glink->mbox_client.knows_txdone = true; glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); if (IS_ERR(glink->mbox_chan)) { if (PTR_ERR(glink->mbox_chan) != -EPROBE_DEFER) @@ -1616,3 +1642,6 @@ void qcom_glink_native_unregister(struct qcom_glink *glink) device_unregister(glink->dev); } EXPORT_SYMBOL_GPL(qcom_glink_native_unregister); + +MODULE_DESCRIPTION("Qualcomm GLINK driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e0e58f3b1420..b59a31b079a5 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -433,6 +433,19 @@ config RTC_DRV_PCF85063 This driver can also be built as a module. If so, the module will be called rtc-pcf85063. +config RTC_DRV_PCF85363 + tristate "NXP PCF85363" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the PCF85363 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf85363. + + The nvmem interface will be named pcf85363-#, where # is the + zero-based instance number. + config RTC_DRV_PCF8563 tristate "Philips PCF8563/Epson RTC8564" help @@ -1174,6 +1187,17 @@ config RTC_DRV_WM8350 This driver can also be built as a module. If so, the module will be called "rtc-wm8350". +config RTC_DRV_SC27XX + tristate "Spreadtrum SC27xx RTC" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + help + If you say Y here you will get support for the RTC subsystem + of the Spreadtrum SC27xx series PMICs. The SC27xx series PMICs + includes the SC2720, SC2721, SC2723, SC2730 and SC2731 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-sc27xx. + config RTC_DRV_SPEAR tristate "SPEAR ST RTC" depends on PLAT_SPEAR || COMPILE_TEST @@ -1706,14 +1730,24 @@ config RTC_DRV_MOXART will be called rtc-moxart config RTC_DRV_MT6397 - tristate "Mediatek Real Time Clock driver" + tristate "MediaTek PMIC based RTC" depends on MFD_MT6397 || (COMPILE_TEST && IRQ_DOMAIN) help - This selects the Mediatek(R) RTC driver. RTC is part of Mediatek + This selects the MediaTek(R) RTC driver. RTC is part of MediaTek MT6397 PMIC. You should enable MT6397 PMIC MFD before select - Mediatek(R) RTC driver. + MediaTek(R) RTC driver. + + If you want to use MediaTek(R) RTC interface, select Y or M here. - If you want to use Mediatek(R) RTC interface, select Y or M here. +config RTC_DRV_MT7622 + tristate "MediaTek SoC based RTC" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This enables support for the real time clock built in the MediaTek + SoCs. + + This drive can also be built as a module. If so, the module + will be called rtc-mt7622. config RTC_DRV_XGENE tristate "APM X-Gene RTC" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0bf1fc02b82c..f2f50c11dc38 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o +obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o @@ -114,6 +115,7 @@ obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o +obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o @@ -144,6 +146,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o +obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 8cec9a02c0b8..672b192f8153 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -779,7 +779,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) } timerqueue_add(&rtc->timerqueue, &timer->node); - if (!next) { + if (!next || ktime_before(timer->node.expires, next->expires)) { struct rtc_wkalrm alarm; int err; alarm.time = rtc_ktime_to_tm(timer->node.expires); @@ -1004,6 +1004,10 @@ int rtc_read_offset(struct rtc_device *rtc, long *offset) * to compensate for differences in the actual clock rate due to temperature, * the crystal, capacitor, etc. * + * The adjustment applied is as follows: + * t = t0 * (1 + offset * 1e-9) + * where t0 is the measured length of 1 RTC second with offset = 0 + * * Kernel interface to adjust an rtc clock offset. * Return 0 on success, or a negative number on error. * If the rtc offset is not setable (or not implemented), return -EINVAL diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index fea9a60b06cf..b033bc556f5d 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -614,12 +614,12 @@ static int abx80x_probe(struct i2c_client *client, if (err) return err; - rtc = devm_rtc_device_register(&client->dev, "abx8xx", - &abx80x_rtc_ops, THIS_MODULE); - + rtc = devm_rtc_allocate_device(&client->dev); if (IS_ERR(rtc)) return PTR_ERR(rtc); + rtc->ops = &abx80x_rtc_ops; + i2c_set_clientdata(client, rtc); if (client->irq > 0) { @@ -646,10 +646,14 @@ static int abx80x_probe(struct i2c_client *client, err = devm_add_action_or_reset(&client->dev, rtc_calib_remove_sysfs_group, &client->dev); - if (err) + if (err) { dev_err(&client->dev, "Failed to add sysfs cleanup action: %d\n", err); + return err; + } + + err = rtc_register_device(rtc); return err; } diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c index 21f355c37eab..1e4978c96ffd 100644 --- a/drivers/rtc/rtc-armada38x.c +++ b/drivers/rtc/rtc-armada38x.c @@ -28,6 +28,8 @@ #define RTC_IRQ_AL_EN BIT(0) #define RTC_IRQ_FREQ_EN BIT(1) #define RTC_IRQ_FREQ_1HZ BIT(2) +#define RTC_CCR 0x18 +#define RTC_CCR_MODE BIT(15) #define RTC_TIME 0xC #define RTC_ALARM1 0x10 @@ -343,18 +345,117 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) return IRQ_HANDLED; } +/* + * The information given in the Armada 388 functional spec is complex. + * They give two different formulas for calculating the offset value, + * but when considering "Offset" as an 8-bit signed integer, they both + * reduce down to (we shall rename "Offset" as "val" here): + * + * val = (f_ideal / f_measured - 1) / resolution where f_ideal = 32768 + * + * Converting to time, f = 1/t: + * val = (t_measured / t_ideal - 1) / resolution where t_ideal = 1/32768 + * + * => t_measured / t_ideal = val * resolution + 1 + * + * "offset" in the RTC interface is defined as: + * t = t0 * (1 + offset * 1e-9) + * where t is the desired period, t0 is the measured period with a zero + * offset, which is t_measured above. With t0 = t_measured and t = t_ideal, + * offset = (t_ideal / t_measured - 1) / 1e-9 + * + * => t_ideal / t_measured = offset * 1e-9 + 1 + * + * so: + * + * offset * 1e-9 + 1 = 1 / (val * resolution + 1) + * + * We want "resolution" to be an integer, so resolution = R * 1e-9, giving + * offset = 1e18 / (val * R + 1e9) - 1e9 + * val = (1e18 / (offset + 1e9) - 1e9) / R + * with a common transformation: + * f(x) = 1e18 / (x + 1e9) - 1e9 + * offset = f(val * R) + * val = f(offset) / R + * + * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb). + */ +static long armada38x_ppb_convert(long ppb) +{ + long div = ppb + 1000000000L; + + return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L; +} + +static int armada38x_rtc_read_offset(struct device *dev, long *offset) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long ccr, flags; + long ppb_cor; + + spin_lock_irqsave(&rtc->lock, flags); + ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR); + spin_unlock_irqrestore(&rtc->lock, flags); + + ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr; + /* ppb_cor + 1000000000L can never be zero */ + *offset = armada38x_ppb_convert(ppb_cor); + + return 0; +} + +static int armada38x_rtc_set_offset(struct device *dev, long offset) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long ccr = 0; + long ppb_cor, off; + + /* + * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we + * need to clamp the input. This equates to -484270 .. 488558. + * Not only is this to stop out of range "off" but also to + * avoid the division by zero in armada38x_ppb_convert(). + */ + offset = clamp(offset, -484270L, 488558L); + + ppb_cor = armada38x_ppb_convert(offset); + + /* + * Use low update mode where possible, which gives a better + * resolution of correction. + */ + off = DIV_ROUND_CLOSEST(ppb_cor, 954); + if (off > 127 || off < -128) { + ccr = RTC_CCR_MODE; + off = DIV_ROUND_CLOSEST(ppb_cor, 3815); + } + + /* + * Armada 388 requires a bit pattern in bits 14..8 depending on + * the sign bit: { 0, ~S, S, S, S, S, S } + */ + ccr |= (off & 0x3fff) ^ 0x2000; + rtc_delayed_write(ccr, rtc, RTC_CCR); + + return 0; +} + static const struct rtc_class_ops armada38x_rtc_ops = { .read_time = armada38x_rtc_read_time, .set_time = armada38x_rtc_set_time, .read_alarm = armada38x_rtc_read_alarm, .set_alarm = armada38x_rtc_set_alarm, .alarm_irq_enable = armada38x_rtc_alarm_irq_enable, + .read_offset = armada38x_rtc_read_offset, + .set_offset = armada38x_rtc_set_offset, }; static const struct rtc_class_ops armada38x_rtc_ops_noirq = { .read_time = armada38x_rtc_read_time, .set_time = armada38x_rtc_set_time, .read_alarm = armada38x_rtc_read_alarm, + .read_offset = armada38x_rtc_read_offset, + .set_offset = armada38x_rtc_set_offset, }; static const struct armada38x_rtc_data armada38x_data = { diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index e221b78b6f10..de81ecedd571 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -42,8 +42,6 @@ #define at91_rtc_write(field, val) \ writel_relaxed((val), at91_rtc_regs + field) -#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */ - struct at91_rtc_config { bool use_shadow_imr; }; @@ -51,7 +49,6 @@ struct at91_rtc_config { static const struct at91_rtc_config *at91_rtc_config; static DECLARE_COMPLETION(at91_rtc_updated); static DECLARE_COMPLETION(at91_rtc_upd_rdy); -static unsigned int at91_alarm_year = AT91_RTC_EPOCH; static void __iomem *at91_rtc_regs; static int irq; static DEFINE_SPINLOCK(at91_rtc_lock); @@ -131,8 +128,7 @@ static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg, /* * The Calendar Alarm register does not have a field for - * the year - so these will return an invalid value. When an - * alarm is set, at91_alarm_year will store the current year. + * the year - so these will return an invalid value. */ tm->tm_year = bcd2bin(date & AT91_RTC_CENT) * 100; /* century */ tm->tm_year += bcd2bin((date & AT91_RTC_YEAR) >> 8); /* year */ @@ -208,15 +204,14 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) struct rtc_time *tm = &alrm->time; at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm); - tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); - tm->tm_year = at91_alarm_year - 1900; + tm->tm_year = -1; alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM) ? 1 : 0; - dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, - 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + dev_dbg(dev, "%s(): %02d-%02d %02d:%02d:%02d %sabled\n", __func__, + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + alrm->enabled ? "en" : "dis"); return 0; } @@ -230,8 +225,6 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm); - at91_alarm_year = tm.tm_year; - tm.tm_mon = alrm->time.tm_mon; tm.tm_mday = alrm->time.tm_mday; tm.tm_hour = alrm->time.tm_hour; @@ -255,7 +248,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) } dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, - at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, + tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); return 0; diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 72b22935eb62..d8df2e9e14ad 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -514,56 +514,43 @@ static void msg_init(struct spi_message *m, struct spi_transfer *x, spi_message_add_tail(x, m); } -static ssize_t -ds1305_nvram_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int ds1305_nvram_read(void *priv, unsigned int off, void *buf, + size_t count) { - struct spi_device *spi; + struct ds1305 *ds1305 = priv; + struct spi_device *spi = ds1305->spi; u8 addr; struct spi_message m; struct spi_transfer x[2]; - int status; - - spi = to_spi_device(kobj_to_dev(kobj)); addr = DS1305_NVRAM + off; msg_init(&m, x, &addr, count, NULL, buf); - status = spi_sync(spi, &m); - if (status < 0) - dev_err(&spi->dev, "nvram %s error %d\n", "read", status); - return (status < 0) ? status : count; + return spi_sync(spi, &m); } -static ssize_t -ds1305_nvram_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int ds1305_nvram_write(void *priv, unsigned int off, void *buf, + size_t count) { - struct spi_device *spi; + struct ds1305 *ds1305 = priv; + struct spi_device *spi = ds1305->spi; u8 addr; struct spi_message m; struct spi_transfer x[2]; - int status; - - spi = to_spi_device(kobj_to_dev(kobj)); addr = (DS1305_WRITE | DS1305_NVRAM) + off; msg_init(&m, x, &addr, count, buf, NULL); - status = spi_sync(spi, &m); - if (status < 0) - dev_err(&spi->dev, "nvram %s error %d\n", "write", status); - return (status < 0) ? status : count; + return spi_sync(spi, &m); } -static struct bin_attribute nvram = { - .attr.name = "nvram", - .attr.mode = S_IRUGO | S_IWUSR, - .read = ds1305_nvram_read, - .write = ds1305_nvram_write, - .size = DS1305_NVRAM_LEN, +static struct nvmem_config ds1305_nvmem_cfg = { + .name = "ds1305_nvram", + .word_size = 1, + .stride = 1, + .size = DS1305_NVRAM_LEN, + .reg_read = ds1305_nvram_read, + .reg_write = ds1305_nvram_write, }; /*----------------------------------------------------------------------*/ @@ -708,10 +695,19 @@ static int ds1305_probe(struct spi_device *spi) dev_dbg(&spi->dev, "AM/PM\n"); /* register RTC ... from here on, ds1305->ctrl needs locking */ - ds1305->rtc = devm_rtc_device_register(&spi->dev, "ds1305", - &ds1305_ops, THIS_MODULE); + ds1305->rtc = devm_rtc_allocate_device(&spi->dev); if (IS_ERR(ds1305->rtc)) { - status = PTR_ERR(ds1305->rtc); + return PTR_ERR(ds1305->rtc); + } + + ds1305->rtc->ops = &ds1305_ops; + + ds1305_nvmem_cfg.priv = ds1305; + ds1305->rtc->nvmem_config = &ds1305_nvmem_cfg; + ds1305->rtc->nvram_old_abi = true; + + status = rtc_register_device(ds1305->rtc); + if (status) { dev_dbg(&spi->dev, "register rtc --> %d\n", status); return status; } @@ -734,12 +730,6 @@ static int ds1305_probe(struct spi_device *spi) } } - /* export NVRAM */ - status = sysfs_create_bin_file(&spi->dev.kobj, &nvram); - if (status < 0) { - dev_err(&spi->dev, "register nvram --> %d\n", status); - } - return 0; } @@ -747,8 +737,6 @@ static int ds1305_remove(struct spi_device *spi) { struct ds1305 *ds1305 = spi_get_drvdata(spi); - sysfs_remove_bin_file(&spi->dev.kobj, &nvram); - /* carefully shut down irq and workqueue, if present */ if (spi->irq) { set_bit(FLAG_EXITING, &ds1305->flags); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index e7d9215c9201..923dde912f60 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -325,6 +325,10 @@ static const struct of_device_id ds1307_of_match[] = { .compatible = "isil,isl12057", .data = (void *)ds_1337 }, + { + .compatible = "epson,rx8130", + .data = (void *)rx_8130 + }, { } }; MODULE_DEVICE_TABLE(of, ds1307_of_match); @@ -348,6 +352,7 @@ static const struct acpi_device_id ds1307_acpi_ids[] = { { .id = "PT7C4338", .driver_data = ds_1307 }, { .id = "RX8025", .driver_data = rx_8025 }, { .id = "ISL12057", .driver_data = ds_1337 }, + { .id = "RX8130", .driver_data = rx_8130 }, { } }; MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids); @@ -787,8 +792,6 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) * Alarm support for mcp794xx devices. */ -#define MCP794XX_REG_WEEKDAY 0x3 -#define MCP794XX_REG_WEEKDAY_WDAY_MASK 0x7 #define MCP794XX_REG_CONTROL 0x07 # define MCP794XX_BIT_ALM0_EN 0x10 # define MCP794XX_BIT_ALM1_EN 0x20 @@ -877,15 +880,38 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) return 0; } +/* + * We may have a random RTC weekday, therefore calculate alarm weekday based + * on current weekday we read from the RTC timekeeping regs + */ +static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm) +{ + struct rtc_time tm_now; + int days_now, days_alarm, ret; + + ret = ds1307_get_time(dev, &tm_now); + if (ret) + return ret; + + days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60); + days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60); + + return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1; +} + static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); unsigned char regs[10]; - int ret; + int wday, ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; + wday = mcp794xx_alm_weekday(dev, &t->time); + if (wday < 0) + return wday; + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " "enabled=%d pending=%d\n", __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, @@ -902,7 +928,7 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) regs[3] = bin2bcd(t->time.tm_sec); regs[4] = bin2bcd(t->time.tm_min); regs[5] = bin2bcd(t->time.tm_hour); - regs[6] = bin2bcd(t->time.tm_wday + 1); + regs[6] = wday; regs[7] = bin2bcd(t->time.tm_mday); regs[8] = bin2bcd(t->time.tm_mon + 1); @@ -1354,14 +1380,12 @@ static int ds1307_probe(struct i2c_client *client, { struct ds1307 *ds1307; int err = -ENODEV; - int tmp, wday; + int tmp; const struct chip_desc *chip; bool want_irq; bool ds1307_can_wakeup_device = false; unsigned char regs[8]; struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); - struct rtc_time tm; - unsigned long timestamp; u8 trickle_charger_setup = 0; ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL); @@ -1641,25 +1665,6 @@ read_rtc: bin2bcd(tmp)); } - /* - * Some IPs have weekday reset value = 0x1 which might not correct - * hence compute the wday using the current date/month/year values - */ - ds1307_get_time(ds1307->dev, &tm); - wday = tm.tm_wday; - timestamp = rtc_tm_to_time64(&tm); - rtc_time64_to_tm(timestamp, &tm); - - /* - * Check if reset wday is different from the computed wday - * If different then set the wday which we computed using - * timestamp - */ - if (wday != tm.tm_wday) - regmap_update_bits(ds1307->regmap, MCP794XX_REG_WEEKDAY, - MCP794XX_REG_WEEKDAY_WDAY_MASK, - tm.tm_wday + 1); - if (want_irq || ds1307_can_wakeup_device) { device_set_wakeup_capable(ds1307->dev, true); set_bit(HAS_ALARM, &ds1307->flags); diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index aa0d2c6f1edc..4d5b007d7fc6 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c @@ -216,9 +216,16 @@ static int ds1390_probe(struct spi_device *spi) return res; } +static const struct of_device_id ds1390_of_match[] = { + { .compatible = "dallas,ds1390" }, + {} +}; +MODULE_DEVICE_TABLE(of, ds1390_of_match); + static struct spi_driver ds1390_driver = { .driver = { .name = "rtc-ds1390", + .of_match_table = of_match_ptr(ds1390_of_match), }, .probe = ds1390_probe, }; diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 1b2dcb58c0ab..1e95312a6f2e 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -398,42 +398,37 @@ static const struct rtc_class_ops ds1511_rtc_ops = { .alarm_irq_enable = ds1511_rtc_alarm_irq_enable, }; -static ssize_t -ds1511_nvram_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *ba, - char *buf, loff_t pos, size_t size) +static int ds1511_nvram_read(void *priv, unsigned int pos, void *buf, + size_t size) { - ssize_t count; + int i; rtc_write(pos, DS1511_RAMADDR_LSB); - for (count = 0; count < size; count++) - *buf++ = rtc_read(DS1511_RAMDATA); + for (i = 0; i < size; i++) + *(char *)buf++ = rtc_read(DS1511_RAMDATA); - return count; + return 0; } -static ssize_t -ds1511_nvram_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t pos, size_t size) +static int ds1511_nvram_write(void *priv, unsigned int pos, void *buf, + size_t size) { - ssize_t count; + int i; rtc_write(pos, DS1511_RAMADDR_LSB); - for (count = 0; count < size; count++) - rtc_write(*buf++, DS1511_RAMDATA); + for (i = 0; i < size; i++) + rtc_write(*(char *)buf++, DS1511_RAMDATA); - return count; + return 0; } -static struct bin_attribute ds1511_nvram_attr = { - .attr = { - .name = "nvram", - .mode = S_IRUGO | S_IWUSR, - }, +static struct nvmem_config ds1511_nvmem_cfg = { + .name = "ds1511_nvram", + .word_size = 1, + .stride = 1, .size = DS1511_RAM_MAX, - .read = ds1511_nvram_read, - .write = ds1511_nvram_write, + .reg_read = ds1511_nvram_read, + .reg_write = ds1511_nvram_write, }; static int ds1511_rtc_probe(struct platform_device *pdev) @@ -477,11 +472,20 @@ static int ds1511_rtc_probe(struct platform_device *pdev) spin_lock_init(&pdata->lock); platform_set_drvdata(pdev, pdata); - pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &ds1511_rtc_ops, THIS_MODULE); + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(pdata->rtc)) return PTR_ERR(pdata->rtc); + pdata->rtc->ops = &ds1511_rtc_ops; + + ds1511_nvmem_cfg.priv = &pdev->dev; + pdata->rtc->nvmem_config = &ds1511_nvmem_cfg; + pdata->rtc->nvram_old_abi = true; + + ret = rtc_register_device(pdata->rtc); + if (ret) + return ret; + /* * if the platform has an interrupt in mind for this device, * then by all means, set it @@ -496,26 +500,6 @@ static int ds1511_rtc_probe(struct platform_device *pdev) } } - ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr); - if (ret) - dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n", - ds1511_nvram_attr.attr.name); - - return 0; -} - -static int ds1511_rtc_remove(struct platform_device *pdev) -{ - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - - sysfs_remove_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr); - if (pdata->irq > 0) { - /* - * disable the alarm interrupt - */ - rtc_write(rtc_read(RTC_CMD) & ~RTC_TIE, RTC_CMD); - rtc_read(RTC_CMD1); - } return 0; } @@ -524,7 +508,6 @@ MODULE_ALIAS("platform:ds1511"); static struct platform_driver ds1511_rtc_driver = { .probe = ds1511_rtc_probe, - .remove = ds1511_rtc_remove, .driver = { .name = "ds1511", }, diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 64989afffa3d..ff65a7d2b9c9 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -82,7 +82,7 @@ static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) { uint32_t ctrl; - int timeout = 1000; + int timeout = 10000; do { ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); @@ -94,7 +94,7 @@ static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) { uint32_t ctrl; - int ret, timeout = 1000; + int ret, timeout = 10000; ret = jz4740_rtc_wait_write_ready(rtc); if (ret != 0) @@ -368,7 +368,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev) ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0); if (ret) { - dev_err(&pdev->dev, "Could not write write to RTC registers\n"); + dev_err(&pdev->dev, "Could not write to RTC registers\n"); return ret; } } diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index f4c070ea8384..c90fba3ed861 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -154,6 +154,8 @@ struct m41t80_data { struct rtc_device *rtc; #ifdef CONFIG_COMMON_CLK struct clk_hw sqw; + unsigned long freq; + unsigned int sqwe; #endif }; @@ -443,43 +445,40 @@ static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume); #ifdef CONFIG_COMMON_CLK #define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw) -static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) +static unsigned long m41t80_decode_freq(int setting) +{ + return (setting == 0) ? 0 : (setting == 1) ? M41T80_SQW_MAX_FREQ : + M41T80_SQW_MAX_FREQ >> setting; +} + +static unsigned long m41t80_get_freq(struct m41t80_data *m41t80) { - struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); struct i2c_client *client = m41t80->client; int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ? M41T80_REG_WDAY : M41T80_REG_SQW; int ret = i2c_smbus_read_byte_data(client, reg_sqw); - unsigned long val = M41T80_SQW_MAX_FREQ; if (ret < 0) return 0; + return m41t80_decode_freq(ret >> 4); +} - ret >>= 4; - if (ret == 0) - val = 0; - else if (ret > 1) - val = val / (1 << ret); - - return val; +static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return sqw_to_m41t80_data(hw)->freq; } static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { - int i, freq = M41T80_SQW_MAX_FREQ; - - if (freq <= rate) - return freq; - - for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) { - freq /= 1 << i; - if (freq <= rate) - return freq; - } - - return 0; + if (rate >= M41T80_SQW_MAX_FREQ) + return M41T80_SQW_MAX_FREQ; + if (rate >= M41T80_SQW_MAX_FREQ / 4) + return M41T80_SQW_MAX_FREQ / 4; + if (!rate) + return 0; + return 1 << ilog2(rate); } static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate, @@ -491,17 +490,12 @@ static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate, M41T80_REG_WDAY : M41T80_REG_SQW; int reg, ret, val = 0; - if (rate) { - if (!is_power_of_2(rate)) - return -EINVAL; - val = ilog2(rate); - if (val == ilog2(M41T80_SQW_MAX_FREQ)) - val = 1; - else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1)) - val = ilog2(M41T80_SQW_MAX_FREQ) - val; - else - return -EINVAL; - } + if (rate >= M41T80_SQW_MAX_FREQ) + val = 1; + else if (rate >= M41T80_SQW_MAX_FREQ / 4) + val = 2; + else if (rate) + val = 15 - ilog2(rate); reg = i2c_smbus_read_byte_data(client, reg_sqw); if (reg < 0) @@ -510,10 +504,9 @@ static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate, reg = (reg & 0x0f) | (val << 4); ret = i2c_smbus_write_byte_data(client, reg_sqw, reg); - if (ret < 0) - return ret; - - return -EINVAL; + if (!ret) + m41t80->freq = m41t80_decode_freq(val); + return ret; } static int m41t80_sqw_control(struct clk_hw *hw, bool enable) @@ -530,7 +523,10 @@ static int m41t80_sqw_control(struct clk_hw *hw, bool enable) else ret &= ~M41T80_ALMON_SQWE; - return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret); + ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret); + if (!ret) + m41t80->sqwe = enable; + return ret; } static int m41t80_sqw_prepare(struct clk_hw *hw) @@ -545,14 +541,7 @@ static void m41t80_sqw_unprepare(struct clk_hw *hw) static int m41t80_sqw_is_prepared(struct clk_hw *hw) { - struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); - struct i2c_client *client = m41t80->client; - int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); - - if (ret < 0) - return ret; - - return !!(ret & M41T80_ALMON_SQWE); + return sqw_to_m41t80_data(hw)->sqwe; } static const struct clk_ops m41t80_sqw_ops = { @@ -587,6 +576,7 @@ static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80) init.parent_names = NULL; init.num_parents = 0; m41t80->sqw.init = &init; + m41t80->freq = m41t80_get_freq(m41t80); /* optional override of the clockname */ of_property_read_string(node, "clock-output-names", &init.name); diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 02af045305dd..d9aea9b6d9cd 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -163,35 +163,30 @@ static const struct rtc_class_ops m48t86_rtc_ops = { .proc = m48t86_rtc_proc, }; -static ssize_t m48t86_nvram_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int m48t86_nvram_read(void *priv, unsigned int off, void *buf, + size_t count) { - struct device *dev = kobj_to_dev(kobj); + struct device *dev = priv; unsigned int i; for (i = 0; i < count; i++) - buf[i] = m48t86_readb(dev, M48T86_NVRAM(off + i)); + ((u8 *)buf)[i] = m48t86_readb(dev, M48T86_NVRAM(off + i)); - return count; + return 0; } -static ssize_t m48t86_nvram_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int m48t86_nvram_write(void *priv, unsigned int off, void *buf, + size_t count) { - struct device *dev = kobj_to_dev(kobj); + struct device *dev = priv; unsigned int i; for (i = 0; i < count; i++) - m48t86_writeb(dev, buf[i], M48T86_NVRAM(off + i)); + m48t86_writeb(dev, ((u8 *)buf)[i], M48T86_NVRAM(off + i)); - return count; + return 0; } -static BIN_ATTR(nvram, 0644, m48t86_nvram_read, m48t86_nvram_write, - M48T86_NVRAM_LEN); - /* * The RTC is an optional feature at purchase time on some Technologic Systems * boards. Verify that it actually exists by checking if the last two bytes @@ -223,11 +218,21 @@ static bool m48t86_verify_chip(struct platform_device *pdev) return false; } +static struct nvmem_config m48t86_nvmem_cfg = { + .name = "m48t86_nvram", + .word_size = 1, + .stride = 1, + .size = M48T86_NVRAM_LEN, + .reg_read = m48t86_nvram_read, + .reg_write = m48t86_nvram_write, +}; + static int m48t86_rtc_probe(struct platform_device *pdev) { struct m48t86_rtc_info *info; struct resource *res; unsigned char reg; + int err; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -254,25 +259,25 @@ static int m48t86_rtc_probe(struct platform_device *pdev) return -ENODEV; } - info->rtc = devm_rtc_device_register(&pdev->dev, "m48t86", - &m48t86_rtc_ops, THIS_MODULE); + info->rtc = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(info->rtc)) return PTR_ERR(info->rtc); + info->rtc->ops = &m48t86_rtc_ops; + + m48t86_nvmem_cfg.priv = &pdev->dev; + info->rtc->nvmem_config = &m48t86_nvmem_cfg; + info->rtc->nvram_old_abi = true; + + err = rtc_register_device(info->rtc); + if (err) + return err; + /* read battery status */ reg = m48t86_readb(&pdev->dev, M48T86_D); dev_info(&pdev->dev, "battery %s\n", (reg & M48T86_D_VRT) ? "ok" : "exhausted"); - if (device_create_bin_file(&pdev->dev, &bin_attr_nvram)) - dev_err(&pdev->dev, "failed to create nvram sysfs entry\n"); - - return 0; -} - -static int m48t86_rtc_remove(struct platform_device *pdev) -{ - device_remove_bin_file(&pdev->dev, &bin_attr_nvram); return 0; } @@ -281,7 +286,6 @@ static struct platform_driver m48t86_rtc_platform_driver = { .name = "rtc-m48t86", }, .probe = m48t86_rtc_probe, - .remove = m48t86_rtc_remove, }; module_platform_driver(m48t86_rtc_platform_driver); diff --git a/drivers/rtc/rtc-mt7622.c b/drivers/rtc/rtc-mt7622.c new file mode 100644 index 000000000000..d79b9ae4d237 --- /dev/null +++ b/drivers/rtc/rtc-mt7622.c @@ -0,0 +1,422 @@ +/* + * Driver for MediaTek SoC based RTC + * + * Copyright (C) 2017 Sean Wang + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include + +#define MTK_RTC_DEV KBUILD_MODNAME + +#define MTK_RTC_PWRCHK1 0x4 +#define RTC_PWRCHK1_MAGIC 0xc6 + +#define MTK_RTC_PWRCHK2 0x8 +#define RTC_PWRCHK2_MAGIC 0x9a + +#define MTK_RTC_KEY 0xc +#define RTC_KEY_MAGIC 0x59 + +#define MTK_RTC_PROT1 0x10 +#define RTC_PROT1_MAGIC 0xa3 + +#define MTK_RTC_PROT2 0x14 +#define RTC_PROT2_MAGIC 0x57 + +#define MTK_RTC_PROT3 0x18 +#define RTC_PROT3_MAGIC 0x67 + +#define MTK_RTC_PROT4 0x1c +#define RTC_PROT4_MAGIC 0xd2 + +#define MTK_RTC_CTL 0x20 +#define RTC_RC_STOP BIT(0) + +#define MTK_RTC_DEBNCE 0x2c +#define RTC_DEBNCE_MASK GENMASK(2, 0) + +#define MTK_RTC_INT 0x30 +#define RTC_INT_AL_STA BIT(4) + +/* + * Ranges from 0x40 to 0x78 provide RTC time setup for year, month, + * day of month, day of week, hour, minute and second. + */ +#define MTK_RTC_TREG(_t, _f) (0x40 + (0x4 * (_f)) + ((_t) * 0x20)) + +#define MTK_RTC_AL_CTL 0x7c +#define RTC_AL_EN BIT(0) +#define RTC_AL_ALL GENMASK(7, 0) + +/* + * The offset is used in the translation for the year between in struct + * rtc_time and in hardware register MTK_RTC_TREG(x,MTK_YEA) + */ +#define MTK_RTC_TM_YR_OFFSET 100 + +/* + * The lowest value for the valid tm_year. RTC hardware would take incorrectly + * tm_year 100 as not a leap year and thus it is also required being excluded + * from the valid options. + */ +#define MTK_RTC_TM_YR_L (MTK_RTC_TM_YR_OFFSET + 1) + +/* + * The most year the RTC can hold is 99 and the next to 99 in year register + * would be wraparound to 0, for MT7622. + */ +#define MTK_RTC_HW_YR_LIMIT 99 + +/* The highest value for the valid tm_year */ +#define MTK_RTC_TM_YR_H (MTK_RTC_TM_YR_OFFSET + MTK_RTC_HW_YR_LIMIT) + +/* Simple macro helps to check whether the hardware supports the tm_year */ +#define MTK_RTC_TM_YR_VALID(_y) ((_y) >= MTK_RTC_TM_YR_L && \ + (_y) <= MTK_RTC_TM_YR_H) + +/* Types of the function the RTC provides are time counter and alarm. */ +enum { + MTK_TC, + MTK_AL, +}; + +/* Indexes are used for the pointer to relevant registers in MTK_RTC_TREG */ +enum { + MTK_YEA, + MTK_MON, + MTK_DOM, + MTK_DOW, + MTK_HOU, + MTK_MIN, + MTK_SEC +}; + +struct mtk_rtc { + struct rtc_device *rtc; + void __iomem *base; + int irq; + struct clk *clk; +}; + +static void mtk_w32(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + writel_relaxed(val, rtc->base + reg); +} + +static u32 mtk_r32(struct mtk_rtc *rtc, u32 reg) +{ + return readl_relaxed(rtc->base + reg); +} + +static void mtk_rmw(struct mtk_rtc *rtc, u32 reg, u32 mask, u32 set) +{ + u32 val; + + val = mtk_r32(rtc, reg); + val &= ~mask; + val |= set; + mtk_w32(rtc, reg, val); +} + +static void mtk_set(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + mtk_rmw(rtc, reg, 0, val); +} + +static void mtk_clr(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + mtk_rmw(rtc, reg, val, 0); +} + +static void mtk_rtc_hw_init(struct mtk_rtc *hw) +{ + /* The setup of the init sequence is for allowing RTC got to work */ + mtk_w32(hw, MTK_RTC_PWRCHK1, RTC_PWRCHK1_MAGIC); + mtk_w32(hw, MTK_RTC_PWRCHK2, RTC_PWRCHK2_MAGIC); + mtk_w32(hw, MTK_RTC_KEY, RTC_KEY_MAGIC); + mtk_w32(hw, MTK_RTC_PROT1, RTC_PROT1_MAGIC); + mtk_w32(hw, MTK_RTC_PROT2, RTC_PROT2_MAGIC); + mtk_w32(hw, MTK_RTC_PROT3, RTC_PROT3_MAGIC); + mtk_w32(hw, MTK_RTC_PROT4, RTC_PROT4_MAGIC); + mtk_rmw(hw, MTK_RTC_DEBNCE, RTC_DEBNCE_MASK, 0); + mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); +} + +static void mtk_rtc_get_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, + int time_alarm) +{ + u32 year, mon, mday, wday, hour, min, sec; + + /* + * Read again until the field of the second is not changed which + * ensures all fields in the consistent state. Note that MTK_SEC must + * be read first. In this way, it guarantees the others remain not + * changed when the results for two MTK_SEC consecutive reads are same. + */ + do { + sec = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC)); + min = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN)); + hour = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU)); + wday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW)); + mday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM)); + mon = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MON)); + year = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA)); + } while (sec != mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC))); + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour = hour; + tm->tm_wday = wday; + tm->tm_mday = mday; + tm->tm_mon = mon - 1; + + /* Rebase to the absolute year which userspace queries */ + tm->tm_year = year + MTK_RTC_TM_YR_OFFSET; +} + +static void mtk_rtc_set_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, + int time_alarm) +{ + u32 year; + + /* Rebase to the relative year which RTC hardware requires */ + year = tm->tm_year - MTK_RTC_TM_YR_OFFSET; + + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA), year); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MON), tm->tm_mon + 1); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW), tm->tm_wday); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM), tm->tm_mday); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU), tm->tm_hour); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN), tm->tm_min); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC), tm->tm_sec); +} + +static irqreturn_t mtk_rtc_alarmirq(int irq, void *id) +{ + struct mtk_rtc *hw = (struct mtk_rtc *)id; + u32 irq_sta; + + irq_sta = mtk_r32(hw, MTK_RTC_INT); + if (irq_sta & RTC_INT_AL_STA) { + /* Stop alarm also implicitly disables the alarm interrupt */ + mtk_w32(hw, MTK_RTC_AL_CTL, 0); + rtc_update_irq(hw->rtc, 1, RTC_IRQF | RTC_AF); + + /* Ack alarm interrupt status */ + mtk_w32(hw, MTK_RTC_INT, RTC_INT_AL_STA); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mtk_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + mtk_rtc_get_alarm_or_time(hw, tm, MTK_TC); + + return rtc_valid_tm(tm); +} + +static int mtk_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (!MTK_RTC_TM_YR_VALID(tm->tm_year)) + return -EINVAL; + + /* Stop time counter before setting a new one*/ + mtk_set(hw, MTK_RTC_CTL, RTC_RC_STOP); + + mtk_rtc_set_alarm_or_time(hw, tm, MTK_TC); + + /* Restart the time counter */ + mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); + + return 0; +} + +static int mtk_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + + mtk_rtc_get_alarm_or_time(hw, alrm_tm, MTK_AL); + + wkalrm->enabled = !!(mtk_r32(hw, MTK_RTC_AL_CTL) & RTC_AL_EN); + wkalrm->pending = !!(mtk_r32(hw, MTK_RTC_INT) & RTC_INT_AL_STA); + + return 0; +} + +static int mtk_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + + if (!MTK_RTC_TM_YR_VALID(alrm_tm->tm_year)) + return -EINVAL; + + /* + * Stop the alarm also implicitly including disables interrupt before + * setting a new one. + */ + mtk_clr(hw, MTK_RTC_AL_CTL, RTC_AL_EN); + + /* + * Avoid contention between mtk_rtc_setalarm and IRQ handler so that + * disabling the interrupt and awaiting for pending IRQ handler to + * complete. + */ + synchronize_irq(hw->irq); + + mtk_rtc_set_alarm_or_time(hw, alrm_tm, MTK_AL); + + /* Restart the alarm with the new setup */ + mtk_w32(hw, MTK_RTC_AL_CTL, RTC_AL_ALL); + + return 0; +} + +static const struct rtc_class_ops mtk_rtc_ops = { + .read_time = mtk_rtc_gettime, + .set_time = mtk_rtc_settime, + .read_alarm = mtk_rtc_getalarm, + .set_alarm = mtk_rtc_setalarm, +}; + +static const struct of_device_id mtk_rtc_match[] = { + { .compatible = "mediatek,mt7622-rtc" }, + { .compatible = "mediatek,soc-rtc" }, + {}, +}; + +static int mtk_rtc_probe(struct platform_device *pdev) +{ + struct mtk_rtc *hw; + struct resource *res; + int ret; + + hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + platform_set_drvdata(pdev, hw); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hw->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw->base)) + return PTR_ERR(hw->base); + + hw->clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(hw->clk)) { + dev_err(&pdev->dev, "No clock\n"); + return PTR_ERR(hw->clk); + } + + ret = clk_prepare_enable(hw->clk); + if (ret) + return ret; + + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + ret = hw->irq; + goto err; + } + + ret = devm_request_irq(&pdev->dev, hw->irq, mtk_rtc_alarmirq, + 0, dev_name(&pdev->dev), hw); + if (ret) { + dev_err(&pdev->dev, "Can't request IRQ\n"); + goto err; + } + + mtk_rtc_hw_init(hw); + + device_init_wakeup(&pdev->dev, true); + + hw->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &mtk_rtc_ops, THIS_MODULE); + if (IS_ERR(hw->rtc)) { + ret = PTR_ERR(hw->rtc); + dev_err(&pdev->dev, "Unable to register device\n"); + goto err; + } + + return 0; +err: + clk_disable_unprepare(hw->clk); + + return ret; +} + +static int mtk_rtc_remove(struct platform_device *pdev) +{ + struct mtk_rtc *hw = platform_get_drvdata(pdev); + + clk_disable_unprepare(hw->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_rtc_suspend(struct device *dev) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(hw->irq); + + return 0; +} + +static int mtk_rtc_resume(struct device *dev) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(hw->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume); + +#define MTK_RTC_PM_OPS (&mtk_rtc_pm_ops) +#else /* CONFIG_PM */ +#define MTK_RTC_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct platform_driver mtk_rtc_driver = { + .probe = mtk_rtc_probe, + .remove = mtk_rtc_remove, + .driver = { + .name = MTK_RTC_DEV, + .of_match_table = mtk_rtc_match, + .pm = MTK_RTC_PM_OPS, + }, +}; + +module_platform_driver(mtk_rtc_driver); + +MODULE_DESCRIPTION("MediaTek SoC based RTC Driver"); +MODULE_AUTHOR("Sean Wang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 13f7cd11c07e..1d666ac9ef70 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -70,6 +70,10 @@ #define OMAP_RTC_COMP_MSB_REG 0x50 #define OMAP_RTC_OSC_REG 0x54 +#define OMAP_RTC_SCRATCH0_REG 0x60 +#define OMAP_RTC_SCRATCH1_REG 0x64 +#define OMAP_RTC_SCRATCH2_REG 0x68 + #define OMAP_RTC_KICK0_REG 0x6c #define OMAP_RTC_KICK1_REG 0x70 @@ -667,6 +671,45 @@ static struct pinctrl_desc rtc_pinctrl_desc = { .owner = THIS_MODULE, }; +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val, + size_t bytes) +{ + struct omap_rtc *rtc = priv; + u32 *val = _val; + int i; + + for (i = 0; i < bytes / 4; i++) + val[i] = rtc_readl(rtc, + OMAP_RTC_SCRATCH0_REG + offset + (i * 4)); + + return 0; +} + +static int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val, + size_t bytes) +{ + struct omap_rtc *rtc = priv; + u32 *val = _val; + int i; + + rtc->type->unlock(rtc); + for (i = 0; i < bytes / 4; i++) + rtc_writel(rtc, + OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]); + rtc->type->lock(rtc); + + return 0; +} + +static struct nvmem_config omap_rtc_nvmem_config = { + .name = "omap_rtc_scratch", + .word_size = 4, + .stride = 4, + .size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG, + .reg_read = omap_rtc_scratch_read, + .reg_write = omap_rtc_scratch_write, +}; + static int omap_rtc_probe(struct platform_device *pdev) { struct omap_rtc *rtc; @@ -797,13 +840,16 @@ static int omap_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, true); - rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &omap_rtc_ops, THIS_MODULE); + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(rtc->rtc)) { ret = PTR_ERR(rtc->rtc); goto err; } + rtc->rtc->ops = &omap_rtc_ops; + omap_rtc_nvmem_config.priv = rtc; + rtc->rtc->nvmem_config = &omap_rtc_nvmem_config; + /* handle periodic and alarm irqs */ ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0, dev_name(&rtc->rtc->dev), rtc); @@ -830,9 +876,14 @@ static int omap_rtc_probe(struct platform_device *pdev) rtc->pctldev = pinctrl_register(&rtc_pinctrl_desc, &pdev->dev, rtc); if (IS_ERR(rtc->pctldev)) { dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); - return PTR_ERR(rtc->pctldev); + ret = PTR_ERR(rtc->pctldev); + goto err; } + ret = rtc_register_device(rtc->rtc); + if (ret) + goto err; + return 0; err: diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 28c48b3c1946..c312af0db729 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -35,6 +35,9 @@ #define REG_MONTHS 0x08 #define REG_YEARS 0x09 +#define REG_OFFSET 0x0e +#define REG_OFFSET_MODE BIT(7) + struct pcf8523 { struct rtc_device *rtc; }; @@ -272,10 +275,47 @@ static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd, #define pcf8523_rtc_ioctl NULL #endif +static int pcf8523_rtc_read_offset(struct device *dev, long *offset) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + u8 value; + s8 val; + + err = pcf8523_read(client, REG_OFFSET, &value); + if (err < 0) + return err; + + /* sign extend the 7-bit offset value */ + val = value << 1; + *offset = (value & REG_OFFSET_MODE ? 4069 : 4340) * (val >> 1); + + return 0; +} + +static int pcf8523_rtc_set_offset(struct device *dev, long offset) +{ + struct i2c_client *client = to_i2c_client(dev); + long reg_m0, reg_m1; + u8 value; + + reg_m0 = clamp(DIV_ROUND_CLOSEST(offset, 4340), -64L, 63L); + reg_m1 = clamp(DIV_ROUND_CLOSEST(offset, 4069), -64L, 63L); + + if (abs(reg_m0 * 4340 - offset) < abs(reg_m1 * 4069 - offset)) + value = reg_m0 & 0x7f; + else + value = (reg_m1 & 0x7f) | REG_OFFSET_MODE; + + return pcf8523_write(client, REG_OFFSET, value); +} + static const struct rtc_class_ops pcf8523_rtc_ops = { .read_time = pcf8523_rtc_read_time, .set_time = pcf8523_rtc_set_time, .ioctl = pcf8523_rtc_ioctl, + .read_offset = pcf8523_rtc_read_offset, + .set_offset = pcf8523_rtc_set_offset, }; static int pcf8523_probe(struct i2c_client *client, diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c new file mode 100644 index 000000000000..ea04e9f0930b --- /dev/null +++ b/drivers/rtc/rtc-pcf85363.c @@ -0,0 +1,220 @@ +/* + * drivers/rtc/rtc-pcf85363.c + * + * Driver for NXP PCF85363 real-time clock. + * + * Copyright (C) 2017 Eric Nelson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based loosely on rtc-8583 by Russell King, Wolfram Sang and Juergen Beisert + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Date/Time registers + */ +#define DT_100THS 0x00 +#define DT_SECS 0x01 +#define DT_MINUTES 0x02 +#define DT_HOURS 0x03 +#define DT_DAYS 0x04 +#define DT_WEEKDAYS 0x05 +#define DT_MONTHS 0x06 +#define DT_YEARS 0x07 + +/* + * Alarm registers + */ +#define DT_SECOND_ALM1 0x08 +#define DT_MINUTE_ALM1 0x09 +#define DT_HOUR_ALM1 0x0a +#define DT_DAY_ALM1 0x0b +#define DT_MONTH_ALM1 0x0c +#define DT_MINUTE_ALM2 0x0d +#define DT_HOUR_ALM2 0x0e +#define DT_WEEKDAY_ALM2 0x0f +#define DT_ALARM_EN 0x10 + +/* + * Time stamp registers + */ +#define DT_TIMESTAMP1 0x11 +#define DT_TIMESTAMP2 0x17 +#define DT_TIMESTAMP3 0x1d +#define DT_TS_MODE 0x23 + +/* + * control registers + */ +#define CTRL_OFFSET 0x24 +#define CTRL_OSCILLATOR 0x25 +#define CTRL_BATTERY 0x26 +#define CTRL_PIN_IO 0x27 +#define CTRL_FUNCTION 0x28 +#define CTRL_INTA_EN 0x29 +#define CTRL_INTB_EN 0x2a +#define CTRL_FLAGS 0x2b +#define CTRL_RAMBYTE 0x2c +#define CTRL_WDOG 0x2d +#define CTRL_STOP_EN 0x2e +#define CTRL_RESETS 0x2f +#define CTRL_RAM 0x40 + +#define NVRAM_SIZE 0x40 + +static struct i2c_driver pcf85363_driver; + +struct pcf85363 { + struct device *dev; + struct rtc_device *rtc; + struct nvmem_config nvmem_cfg; + struct regmap *regmap; +}; + +static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_YEARS + 1]; + int ret, len = sizeof(buf); + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len); + if (ret) { + dev_err(dev, "%s: error %d\n", __func__, ret); + return ret; + } + + tm->tm_year = bcd2bin(buf[DT_YEARS]); + /* adjust for 1900 base of rtc_time */ + tm->tm_year += 100; + + tm->tm_wday = buf[DT_WEEKDAYS] & 7; + buf[DT_SECS] &= 0x7F; + tm->tm_sec = bcd2bin(buf[DT_SECS]); + buf[DT_MINUTES] &= 0x7F; + tm->tm_min = bcd2bin(buf[DT_MINUTES]); + tm->tm_hour = bcd2bin(buf[DT_HOURS]); + tm->tm_mday = bcd2bin(buf[DT_DAYS]); + tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1; + + return 0; +} + +static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_YEARS + 1]; + int len = sizeof(buf); + + buf[DT_100THS] = 0; + buf[DT_SECS] = bin2bcd(tm->tm_sec); + buf[DT_MINUTES] = bin2bcd(tm->tm_min); + buf[DT_HOURS] = bin2bcd(tm->tm_hour); + buf[DT_DAYS] = bin2bcd(tm->tm_mday); + buf[DT_WEEKDAYS] = tm->tm_wday; + buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1); + buf[DT_YEARS] = bin2bcd(tm->tm_year % 100); + + return regmap_bulk_write(pcf85363->regmap, DT_100THS, + buf, len); +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = pcf85363_rtc_read_time, + .set_time = pcf85363_rtc_set_time, +}; + +static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + + return regmap_bulk_read(pcf85363->regmap, CTRL_RAM + offset, + val, bytes); +} + +static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + + return regmap_bulk_write(pcf85363->regmap, CTRL_RAM + offset, + val, bytes); +} + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int pcf85363_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf85363 *pcf85363; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363), + GFP_KERNEL); + if (!pcf85363) + return -ENOMEM; + + pcf85363->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(pcf85363->regmap)) { + dev_err(&client->dev, "regmap allocation failed\n"); + return PTR_ERR(pcf85363->regmap); + } + + pcf85363->dev = &client->dev; + i2c_set_clientdata(client, pcf85363); + + pcf85363->rtc = devm_rtc_allocate_device(pcf85363->dev); + if (IS_ERR(pcf85363->rtc)) + return PTR_ERR(pcf85363->rtc); + + pcf85363->nvmem_cfg.name = "pcf85363-"; + pcf85363->nvmem_cfg.word_size = 1; + pcf85363->nvmem_cfg.stride = 1; + pcf85363->nvmem_cfg.size = NVRAM_SIZE; + pcf85363->nvmem_cfg.reg_read = pcf85363_nvram_read; + pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write; + pcf85363->nvmem_cfg.priv = pcf85363; + pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg; + pcf85363->rtc->ops = &rtc_ops; + + return rtc_register_device(pcf85363->rtc); +} + +static const struct of_device_id dev_ids[] = { + { .compatible = "nxp,pcf85363" }, + {} +}; +MODULE_DEVICE_TABLE(of, dev_ids); + +static struct i2c_driver pcf85363_driver = { + .driver = { + .name = "pcf85363", + .of_match_table = of_match_ptr(dev_ids), + }, + .probe = pcf85363_probe, +}; + +module_i2c_driver(pcf85363_driver); + +MODULE_AUTHOR("Eric Nelson"); +MODULE_DESCRIPTION("pcf85363 I2C RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index cea6ea4df970..3efc86c25d27 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -387,7 +387,7 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) if (err) return err; - return pcf8563_set_alarm_mode(client, 1); + return pcf8563_set_alarm_mode(client, !!tm->enabled); } static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) @@ -422,7 +422,7 @@ static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw, return 0; buf &= PCF8563_REG_CLKO_F_MASK; - return clkout_rates[ret]; + return clkout_rates[buf]; } static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index e1687e19c59f..82eb7da2c478 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -308,10 +308,9 @@ static int pl031_remove(struct amba_device *adev) dev_pm_clear_wake_irq(&adev->dev); device_init_wakeup(&adev->dev, false); - free_irq(adev->irq[0], ldata); + if (adev->irq[0]) + free_irq(adev->irq[0], ldata); rtc_device_unregister(ldata->rtc); - iounmap(ldata->base); - kfree(ldata); amba_release_regions(adev); return 0; @@ -322,25 +321,28 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) int ret; struct pl031_local *ldata; struct pl031_vendor_data *vendor = id->data; - struct rtc_class_ops *ops = &vendor->ops; + struct rtc_class_ops *ops; unsigned long time, data; ret = amba_request_regions(adev, NULL); if (ret) goto err_req; - ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL); - if (!ldata) { + ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local), + GFP_KERNEL); + ops = devm_kmemdup(&adev->dev, &vendor->ops, sizeof(vendor->ops), + GFP_KERNEL); + if (!ldata || !ops) { ret = -ENOMEM; goto out; } - ldata->vendor = vendor; - - ldata->base = ioremap(adev->res.start, resource_size(&adev->res)); + ldata->vendor = vendor; + ldata->base = devm_ioremap(&adev->dev, adev->res.start, + resource_size(&adev->res)); if (!ldata->base) { ret = -ENOMEM; - goto out_no_remap; + goto out; } amba_set_drvdata(adev, ldata); @@ -373,28 +375,32 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) } } + if (!adev->irq[0]) { + /* When there's no interrupt, no point in exposing the alarm */ + ops->read_alarm = NULL; + ops->set_alarm = NULL; + ops->alarm_irq_enable = NULL; + } + device_init_wakeup(&adev->dev, true); ldata->rtc = rtc_device_register("pl031", &adev->dev, ops, THIS_MODULE); if (IS_ERR(ldata->rtc)) { ret = PTR_ERR(ldata->rtc); - goto out_no_rtc; + goto out; } - if (request_irq(adev->irq[0], pl031_interrupt, - vendor->irqflags, "rtc-pl031", ldata)) { - ret = -EIO; - goto out_no_irq; + if (adev->irq[0]) { + ret = request_irq(adev->irq[0], pl031_interrupt, + vendor->irqflags, "rtc-pl031", ldata); + if (ret) + goto out_no_irq; + dev_pm_set_wake_irq(&adev->dev, adev->irq[0]); } - dev_pm_set_wake_irq(&adev->dev, adev->irq[0]); return 0; out_no_irq: rtc_device_unregister(ldata->rtc); -out_no_rtc: - iounmap(ldata->base); -out_no_remap: - kfree(ldata); out: amba_release_regions(adev); err_req: @@ -446,7 +452,7 @@ static struct pl031_vendor_data stv2_pl031 = { .irqflags = IRQF_SHARED | IRQF_COND_SUSPEND, }; -static struct amba_id pl031_ids[] = { +static const struct amba_id pl031_ids[] = { { .id = 0x00041031, .mask = 0x000fffff, diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index aa09771de04f..3d6174eb32f6 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -282,13 +282,13 @@ static int rv3029_eeprom_read(struct device *dev, u8 reg, static int rv3029_eeprom_write(struct device *dev, u8 reg, u8 const buf[], size_t len) { - int ret, err; + int ret; size_t i; u8 tmp; - err = rv3029_eeprom_enter(dev); - if (err < 0) - return err; + ret = rv3029_eeprom_enter(dev); + if (ret < 0) + return ret; for (i = 0; i < len; i++, reg++) { ret = rv3029_read_regs(dev, reg, &tmp, 1); @@ -304,11 +304,11 @@ static int rv3029_eeprom_write(struct device *dev, u8 reg, break; } - err = rv3029_eeprom_exit(dev); - if (err < 0) - return err; + ret = rv3029_eeprom_exit(dev); + if (ret < 0) + return ret; - return ret; + return 0; } static int rv3029_eeprom_update_bits(struct device *dev, @@ -876,6 +876,8 @@ static const struct i2c_device_id rv3029_id[] = { MODULE_DEVICE_TABLE(i2c, rv3029_id); static const struct of_device_id rv3029_of_match[] = { + { .compatible = "microcrystal,rv3029" }, + /* Backward compatibility only, do not use compatibles below: */ { .compatible = "rv3029" }, { .compatible = "rv3029c2" }, { .compatible = "mc,rv3029c2" }, diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c index 1ed3403ff8ac..5c5938ab3d86 100644 --- a/drivers/rtc/rtc-rx8010.c +++ b/drivers/rtc/rtc-rx8010.c @@ -24,7 +24,6 @@ #define RX8010_MDAY 0x14 #define RX8010_MONTH 0x15 #define RX8010_YEAR 0x16 -#define RX8010_YEAR 0x16 #define RX8010_RESV17 0x17 #define RX8010_ALMIN 0x18 #define RX8010_ALHOUR 0x19 @@ -36,7 +35,7 @@ #define RX8010_CTRL 0x1F /* 0x20 to 0x2F are user registers */ #define RX8010_RESV30 0x30 -#define RX8010_RESV31 0x32 +#define RX8010_RESV31 0x31 #define RX8010_IRQ 0x32 #define RX8010_EXT_WADA BIT(3) @@ -248,7 +247,7 @@ static int rx8010_init_client(struct i2c_client *client) rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST); - return err; + return 0; } static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t) @@ -277,7 +276,7 @@ static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE); t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled; - return err; + return 0; } static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t) diff --git a/drivers/rtc/rtc-sc27xx.c b/drivers/rtc/rtc-sc27xx.c new file mode 100644 index 000000000000..d544d5268757 --- /dev/null +++ b/drivers/rtc/rtc-sc27xx.c @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2017 Spreadtrum Communications Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPRD_RTC_SEC_CNT_VALUE 0x0 +#define SPRD_RTC_MIN_CNT_VALUE 0x4 +#define SPRD_RTC_HOUR_CNT_VALUE 0x8 +#define SPRD_RTC_DAY_CNT_VALUE 0xc +#define SPRD_RTC_SEC_CNT_UPD 0x10 +#define SPRD_RTC_MIN_CNT_UPD 0x14 +#define SPRD_RTC_HOUR_CNT_UPD 0x18 +#define SPRD_RTC_DAY_CNT_UPD 0x1c +#define SPRD_RTC_SEC_ALM_UPD 0x20 +#define SPRD_RTC_MIN_ALM_UPD 0x24 +#define SPRD_RTC_HOUR_ALM_UPD 0x28 +#define SPRD_RTC_DAY_ALM_UPD 0x2c +#define SPRD_RTC_INT_EN 0x30 +#define SPRD_RTC_INT_RAW_STS 0x34 +#define SPRD_RTC_INT_CLR 0x38 +#define SPRD_RTC_INT_MASK_STS 0x3C +#define SPRD_RTC_SEC_ALM_VALUE 0x40 +#define SPRD_RTC_MIN_ALM_VALUE 0x44 +#define SPRD_RTC_HOUR_ALM_VALUE 0x48 +#define SPRD_RTC_DAY_ALM_VALUE 0x4c +#define SPRD_RTC_SPG_VALUE 0x50 +#define SPRD_RTC_SPG_UPD 0x54 +#define SPRD_RTC_SEC_AUXALM_UPD 0x60 +#define SPRD_RTC_MIN_AUXALM_UPD 0x64 +#define SPRD_RTC_HOUR_AUXALM_UPD 0x68 +#define SPRD_RTC_DAY_AUXALM_UPD 0x6c + +/* BIT & MASK definition for SPRD_RTC_INT_* registers */ +#define SPRD_RTC_SEC_EN BIT(0) +#define SPRD_RTC_MIN_EN BIT(1) +#define SPRD_RTC_HOUR_EN BIT(2) +#define SPRD_RTC_DAY_EN BIT(3) +#define SPRD_RTC_ALARM_EN BIT(4) +#define SPRD_RTC_HRS_FORMAT_EN BIT(5) +#define SPRD_RTC_AUXALM_EN BIT(6) +#define SPRD_RTC_SPG_UPD_EN BIT(7) +#define SPRD_RTC_SEC_UPD_EN BIT(8) +#define SPRD_RTC_MIN_UPD_EN BIT(9) +#define SPRD_RTC_HOUR_UPD_EN BIT(10) +#define SPRD_RTC_DAY_UPD_EN BIT(11) +#define SPRD_RTC_ALMSEC_UPD_EN BIT(12) +#define SPRD_RTC_ALMMIN_UPD_EN BIT(13) +#define SPRD_RTC_ALMHOUR_UPD_EN BIT(14) +#define SPRD_RTC_ALMDAY_UPD_EN BIT(15) +#define SPRD_RTC_INT_MASK GENMASK(15, 0) + +#define SPRD_RTC_TIME_INT_MASK \ + (SPRD_RTC_SEC_UPD_EN | SPRD_RTC_MIN_UPD_EN | \ + SPRD_RTC_HOUR_UPD_EN | SPRD_RTC_DAY_UPD_EN) + +#define SPRD_RTC_ALMTIME_INT_MASK \ + (SPRD_RTC_ALMSEC_UPD_EN | SPRD_RTC_ALMMIN_UPD_EN | \ + SPRD_RTC_ALMHOUR_UPD_EN | SPRD_RTC_ALMDAY_UPD_EN) + +#define SPRD_RTC_ALM_INT_MASK \ + (SPRD_RTC_SEC_EN | SPRD_RTC_MIN_EN | \ + SPRD_RTC_HOUR_EN | SPRD_RTC_DAY_EN | \ + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN) + +/* second/minute/hour/day values mask definition */ +#define SPRD_RTC_SEC_MASK GENMASK(5, 0) +#define SPRD_RTC_MIN_MASK GENMASK(5, 0) +#define SPRD_RTC_HOUR_MASK GENMASK(4, 0) +#define SPRD_RTC_DAY_MASK GENMASK(15, 0) + +/* alarm lock definition for SPRD_RTC_SPG_UPD register */ +#define SPRD_RTC_ALMLOCK_MASK GENMASK(7, 0) +#define SPRD_RTC_ALM_UNLOCK 0xa5 +#define SPRD_RTC_ALM_LOCK (~SPRD_RTC_ALM_UNLOCK & \ + SPRD_RTC_ALMLOCK_MASK) + +/* SPG values definition for SPRD_RTC_SPG_UPD register */ +#define SPRD_RTC_POWEROFF_ALM_FLAG BIT(8) +#define SPRD_RTC_POWER_RESET_FLAG BIT(9) + +/* timeout of synchronizing time and alarm registers (us) */ +#define SPRD_RTC_POLL_TIMEOUT 200000 +#define SPRD_RTC_POLL_DELAY_US 20000 + +struct sprd_rtc { + struct rtc_device *rtc; + struct regmap *regmap; + struct device *dev; + u32 base; + int irq; + bool valid; +}; + +/* + * The Spreadtrum RTC controller has 3 groups registers, including time, normal + * alarm and auxiliary alarm. The time group registers are used to set RTC time, + * the normal alarm registers are used to set normal alarm, and the auxiliary + * alarm registers are used to set auxiliary alarm. Both alarm event and + * auxiliary alarm event can wake up system from deep sleep, but only alarm + * event can power up system from power down status. + */ +enum sprd_rtc_reg_types { + SPRD_RTC_TIME, + SPRD_RTC_ALARM, + SPRD_RTC_AUX_ALARM, +}; + +static int sprd_rtc_clear_alarm_ints(struct sprd_rtc *rtc) +{ + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_ALM_INT_MASK); +} + +static int sprd_rtc_disable_ints(struct sprd_rtc *rtc) +{ + int ret; + + ret = regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_INT_MASK, 0); + if (ret) + return ret; + + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_INT_MASK); +} + +static int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock) +{ + int ret; + u32 val; + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val); + if (ret) + return ret; + + val &= ~(SPRD_RTC_ALMLOCK_MASK | SPRD_RTC_POWEROFF_ALM_FLAG); + if (lock) + val |= SPRD_RTC_ALM_LOCK; + else + val |= SPRD_RTC_ALM_UNLOCK | SPRD_RTC_POWEROFF_ALM_FLAG; + + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_SPG_UPD, val); + if (ret) + return ret; + + /* wait until the SPG value is updated successfully */ + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->base + SPRD_RTC_INT_RAW_STS, val, + (val & SPRD_RTC_SPG_UPD_EN), + SPRD_RTC_POLL_DELAY_US, + SPRD_RTC_POLL_TIMEOUT); + if (ret) { + dev_err(rtc->dev, "failed to update SPG value:%d\n", ret); + return ret; + } + + return 0; +} + +static int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, + time64_t *secs) +{ + u32 sec_reg, min_reg, hour_reg, day_reg; + u32 val, sec, min, hour, day; + int ret; + + switch (type) { + case SPRD_RTC_TIME: + sec_reg = SPRD_RTC_SEC_CNT_VALUE; + min_reg = SPRD_RTC_MIN_CNT_VALUE; + hour_reg = SPRD_RTC_HOUR_CNT_VALUE; + day_reg = SPRD_RTC_DAY_CNT_VALUE; + break; + case SPRD_RTC_ALARM: + sec_reg = SPRD_RTC_SEC_ALM_VALUE; + min_reg = SPRD_RTC_MIN_ALM_VALUE; + hour_reg = SPRD_RTC_HOUR_ALM_VALUE; + day_reg = SPRD_RTC_DAY_ALM_VALUE; + break; + case SPRD_RTC_AUX_ALARM: + sec_reg = SPRD_RTC_SEC_AUXALM_UPD; + min_reg = SPRD_RTC_MIN_AUXALM_UPD; + hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; + day_reg = SPRD_RTC_DAY_AUXALM_UPD; + break; + default: + return -EINVAL; + } + + ret = regmap_read(rtc->regmap, rtc->base + sec_reg, &val); + if (ret) + return ret; + + sec = val & SPRD_RTC_SEC_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + min_reg, &val); + if (ret) + return ret; + + min = val & SPRD_RTC_MIN_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + hour_reg, &val); + if (ret) + return ret; + + hour = val & SPRD_RTC_HOUR_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + day_reg, &val); + if (ret) + return ret; + + day = val & SPRD_RTC_DAY_MASK; + *secs = (((time64_t)(day * 24) + hour) * 60 + min) * 60 + sec; + return 0; +} + +static int sprd_rtc_set_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, + time64_t secs) +{ + u32 sec_reg, min_reg, hour_reg, day_reg, sts_mask; + u32 sec, min, hour, day, val; + int ret, rem; + + /* convert seconds to RTC time format */ + day = div_s64_rem(secs, 86400, &rem); + hour = rem / 3600; + rem -= hour * 3600; + min = rem / 60; + sec = rem - min * 60; + + switch (type) { + case SPRD_RTC_TIME: + sec_reg = SPRD_RTC_SEC_CNT_UPD; + min_reg = SPRD_RTC_MIN_CNT_UPD; + hour_reg = SPRD_RTC_HOUR_CNT_UPD; + day_reg = SPRD_RTC_DAY_CNT_UPD; + sts_mask = SPRD_RTC_TIME_INT_MASK; + break; + case SPRD_RTC_ALARM: + sec_reg = SPRD_RTC_SEC_ALM_UPD; + min_reg = SPRD_RTC_MIN_ALM_UPD; + hour_reg = SPRD_RTC_HOUR_ALM_UPD; + day_reg = SPRD_RTC_DAY_ALM_UPD; + sts_mask = SPRD_RTC_ALMTIME_INT_MASK; + break; + case SPRD_RTC_AUX_ALARM: + sec_reg = SPRD_RTC_SEC_AUXALM_UPD; + min_reg = SPRD_RTC_MIN_AUXALM_UPD; + hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; + day_reg = SPRD_RTC_DAY_AUXALM_UPD; + sts_mask = 0; + break; + default: + return -EINVAL; + } + + ret = regmap_write(rtc->regmap, rtc->base + sec_reg, sec); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + min_reg, min); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + hour_reg, hour); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + day_reg, day); + if (ret) + return ret; + + if (type == SPRD_RTC_AUX_ALARM) + return 0; + + /* + * Since the time and normal alarm registers are put in always-power-on + * region supplied by VDDRTC, then these registers changing time will + * be very long, about 125ms. Thus here we should wait until all + * values are updated successfully. + */ + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->base + SPRD_RTC_INT_RAW_STS, val, + ((val & sts_mask) == sts_mask), + SPRD_RTC_POLL_DELAY_US, + SPRD_RTC_POLL_TIMEOUT); + if (ret < 0) { + dev_err(rtc->dev, "set time/alarm values timeout\n"); + return ret; + } + + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + sts_mask); +} + +static int sprd_rtc_read_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + u32 val; + int ret; + + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_AUX_ALARM, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, &alrm->time); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & SPRD_RTC_AUXALM_EN); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val); + if (ret) + return ret; + + alrm->pending = !!(val & SPRD_RTC_AUXALM_EN); + return 0; +} + +static int sprd_rtc_set_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(&alrm->time); + int ret; + + /* clear the auxiliary alarm interrupt status */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_AUXALM_EN); + if (ret) + return ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_AUX_ALARM, secs); + if (ret) + return ret; + + if (alrm->enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_AUXALM_EN, + SPRD_RTC_AUXALM_EN); + } else { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_AUXALM_EN, 0); + } + + return ret; +} + +static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + int ret; + + if (!rtc->valid) { + dev_warn(dev, "RTC values are invalid\n"); + return -EINVAL; + } + + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, tm); + return rtc_valid_tm(tm); +} + +static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(tm); + u32 val; + int ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs); + if (ret) + return ret; + + if (!rtc->valid) { + /* + * Set SPRD_RTC_POWER_RESET_FLAG to indicate now RTC has valid + * time values. + */ + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_SPG_UPD, + SPRD_RTC_POWER_RESET_FLAG, + SPRD_RTC_POWER_RESET_FLAG); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->base + SPRD_RTC_INT_RAW_STS, + val, (val & SPRD_RTC_SPG_UPD_EN), + SPRD_RTC_POLL_DELAY_US, + SPRD_RTC_POLL_TIMEOUT); + if (ret) { + dev_err(rtc->dev, "failed to update SPG value:%d\n", + ret); + return ret; + } + + rtc->valid = true; + } + + return 0; +} + +static int sprd_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + int ret; + u32 val; + + /* + * If aie_timer is enabled, we should get the normal alarm time. + * Otherwise we should get auxiliary alarm time. + */ + if (rtc->rtc && rtc->rtc->aie_timer.enabled == 0) + return sprd_rtc_read_aux_alarm(dev, alrm); + + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, &alrm->time); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & SPRD_RTC_ALARM_EN); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val); + if (ret) + return ret; + + alrm->pending = !!(val & SPRD_RTC_ALARM_EN); + return 0; +} + +static int sprd_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(&alrm->time); + struct rtc_time aie_time = + rtc_ktime_to_tm(rtc->rtc->aie_timer.node.expires); + int ret; + + /* + * We have 2 groups alarms: normal alarm and auxiliary alarm. Since + * both normal alarm event and auxiliary alarm event can wake up system + * from deep sleep, but only alarm event can power up system from power + * down status. Moreover we do not need to poll about 125ms when + * updating auxiliary alarm registers. Thus we usually set auxiliary + * alarm when wake up system from deep sleep, and for other scenarios, + * we should set normal alarm with polling status. + * + * So here we check if the alarm time is set by aie_timer, if yes, we + * should set normal alarm, if not, we should set auxiliary alarm which + * means it is just a wake event. + */ + if (!rtc->rtc->aie_timer.enabled || rtc_tm_sub(&aie_time, &alrm->time)) + return sprd_rtc_set_aux_alarm(dev, alrm); + + /* clear the alarm interrupt status firstly */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_ALARM_EN); + if (ret) + return ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_ALARM, secs); + if (ret) + return ret; + + if (alrm->enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN, + SPRD_RTC_ALARM_EN); + if (ret) + return ret; + + /* unlock the alarm to enable the alarm function. */ + ret = sprd_rtc_lock_alarm(rtc, false); + } else { + regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN, 0); + + /* + * Lock the alarm function in case fake alarm event will power + * up systems. + */ + ret = sprd_rtc_lock_alarm(rtc, true); + } + + return ret; +} + +static int sprd_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + int ret; + + if (enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN); + if (ret) + return ret; + + ret = sprd_rtc_lock_alarm(rtc, false); + } else { + regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 0); + + ret = sprd_rtc_lock_alarm(rtc, true); + } + + return ret; +} + +static const struct rtc_class_ops sprd_rtc_ops = { + .read_time = sprd_rtc_read_time, + .set_time = sprd_rtc_set_time, + .read_alarm = sprd_rtc_read_alarm, + .set_alarm = sprd_rtc_set_alarm, + .alarm_irq_enable = sprd_rtc_alarm_irq_enable, +}; + +static irqreturn_t sprd_rtc_handler(int irq, void *dev_id) +{ + struct sprd_rtc *rtc = dev_id; + int ret; + + ret = sprd_rtc_clear_alarm_ints(rtc); + if (ret) + return IRQ_RETVAL(ret); + + rtc_update_irq(rtc->rtc, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; +} + +static int sprd_rtc_check_power_down(struct sprd_rtc *rtc) +{ + u32 val; + int ret; + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val); + if (ret) + return ret; + + /* + * If the SPRD_RTC_POWER_RESET_FLAG was not set, which means the RTC has + * been powered down, so the RTC time values are invalid. + */ + rtc->valid = (val & SPRD_RTC_POWER_RESET_FLAG) ? true : false; + return 0; +} + +static int sprd_rtc_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct sprd_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc->regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", &rtc->base); + if (ret) { + dev_err(&pdev->dev, "failed to get RTC base address\n"); + return ret; + } + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) { + dev_err(&pdev->dev, "failed to get RTC irq number\n"); + return rtc->irq; + } + + rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, rtc); + + /* clear all RTC interrupts and disable all RTC interrupts */ + ret = sprd_rtc_disable_ints(rtc); + if (ret) { + dev_err(&pdev->dev, "failed to disable RTC interrupts\n"); + return ret; + } + + /* check if RTC time values are valid */ + ret = sprd_rtc_check_power_down(rtc); + if (ret) { + dev_err(&pdev->dev, "failed to check RTC time values\n"); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + sprd_rtc_handler, + IRQF_ONESHOT | IRQF_EARLY_RESUME, + pdev->name, rtc); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request RTC irq\n"); + return ret; + } + + rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &sprd_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + device_init_wakeup(&pdev->dev, 1); + return 0; +} + +static int sprd_rtc_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + return 0; +} + +static const struct of_device_id sprd_rtc_of_match[] = { + { .compatible = "sprd,sc2731-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sprd_rtc_of_match); + +static struct platform_driver sprd_rtc_driver = { + .driver = { + .name = "sprd-rtc", + .of_match_table = sprd_rtc_of_match, + }, + .probe = sprd_rtc_probe, + .remove = sprd_rtc_remove, +}; +module_platform_driver(sprd_rtc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Spreadtrum RTC Device Driver"); +MODULE_AUTHOR("Baolin Wang "); diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index e364550eb9a7..92ff2edb86a6 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -72,9 +72,10 @@ since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf) retval = rtc_read_time(to_rtc_device(dev), &tm); if (retval == 0) { - unsigned long time; - rtc_tm_to_time(&tm, &time); - retval = sprintf(buf, "%lu\n", time); + time64_t time; + + time = rtc_tm_to_time64(&tm); + retval = sprintf(buf, "%lld\n", time); } return retval; @@ -132,7 +133,7 @@ static ssize_t wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t retval; - unsigned long alarm; + time64_t alarm; struct rtc_wkalrm alm; /* Don't show disabled alarms. For uniformity, RTC alarms are @@ -145,8 +146,8 @@ wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf) */ retval = rtc_read_alarm(to_rtc_device(dev), &alm); if (retval == 0 && alm.enabled) { - rtc_tm_to_time(&alm.time, &alarm); - retval = sprintf(buf, "%lu\n", alarm); + alarm = rtc_tm_to_time64(&alm.time); + retval = sprintf(buf, "%lld\n", alarm); } return retval; @@ -157,8 +158,8 @@ wakealarm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { ssize_t retval; - unsigned long now, alarm; - unsigned long push = 0; + time64_t now, alarm; + time64_t push = 0; struct rtc_wkalrm alm; struct rtc_device *rtc = to_rtc_device(dev); const char *buf_ptr; @@ -170,7 +171,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr, retval = rtc_read_time(rtc, &alm.time); if (retval < 0) return retval; - rtc_tm_to_time(&alm.time, &now); + now = rtc_tm_to_time64(&alm.time); buf_ptr = buf; if (*buf_ptr == '+') { @@ -181,7 +182,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr, } else adjust = 1; } - retval = kstrtoul(buf_ptr, 0, &alarm); + retval = kstrtos64(buf_ptr, 0, &alarm); if (retval) return retval; if (adjust) { @@ -197,7 +198,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr, return retval; if (alm.enabled) { if (push) { - rtc_tm_to_time(&alm.time, &push); + push = rtc_tm_to_time64(&alm.time); alarm += push; } else return -EBUSY; @@ -212,7 +213,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr, */ alarm = now + 300; } - rtc_time_to_tm(alarm, &alm.time); + rtc_time64_to_tm(alarm, &alm.time); retval = rtc_set_alarm(rtc, &alm); return (retval < 0) ? retval : n; diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c index 65b432a096fe..0c34d3b81279 100644 --- a/drivers/rtc/rtc-xgene.c +++ b/drivers/rtc/rtc-xgene.c @@ -52,6 +52,7 @@ struct xgene_rtc_dev { void __iomem *csr_base; struct clk *clk; unsigned int irq_wake; + unsigned int irq_enabled; }; static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -104,15 +105,19 @@ static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled) return 0; } +static int xgene_rtc_alarm_irq_enabled(struct device *dev) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + return readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE ? 1 : 0; +} + static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); - unsigned long rtc_time; unsigned long alarm_time; - rtc_time = readl(pdata->csr_base + RTC_CCVR); rtc_tm_to_time(&alrm->time, &alarm_time); - pdata->alarm_time = alarm_time; writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR); @@ -180,12 +185,18 @@ static int xgene_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Couldn't get the clock for RTC\n"); return -ENODEV; } - clk_prepare_enable(pdata->clk); + ret = clk_prepare_enable(pdata->clk); + if (ret) + return ret; /* Turn on the clock and the crystal */ writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR); - device_init_wakeup(&pdev->dev, 1); + ret = device_init_wakeup(&pdev->dev, 1); + if (ret) { + clk_disable_unprepare(pdata->clk); + return ret; + } pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &xgene_rtc_ops, THIS_MODULE); @@ -210,45 +221,55 @@ static int xgene_rtc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int xgene_rtc_suspend(struct device *dev) +static int __maybe_unused xgene_rtc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); int irq; irq = platform_get_irq(pdev, 0); + + /* + * If this RTC alarm will be used for waking the system up, + * don't disable it of course. Else we just disable the alarm + * and await suspension. + */ if (device_may_wakeup(&pdev->dev)) { if (!enable_irq_wake(irq)) pdata->irq_wake = 1; } else { + pdata->irq_enabled = xgene_rtc_alarm_irq_enabled(dev); xgene_rtc_alarm_irq_enable(dev, 0); - clk_disable(pdata->clk); + clk_disable_unprepare(pdata->clk); } - return 0; } -static int xgene_rtc_resume(struct device *dev) +static int __maybe_unused xgene_rtc_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); int irq; + int rc; irq = platform_get_irq(pdev, 0); + if (device_may_wakeup(&pdev->dev)) { if (pdata->irq_wake) { disable_irq_wake(irq); pdata->irq_wake = 0; } } else { - clk_enable(pdata->clk); - xgene_rtc_alarm_irq_enable(dev, 1); + rc = clk_prepare_enable(pdata->clk); + if (rc) { + dev_err(dev, "Unable to enable clock error %d\n", rc); + return rc; + } + xgene_rtc_alarm_irq_enable(dev, pdata->irq_enabled); } return 0; } -#endif static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 353f0bebcf8c..8c9d412b6d33 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -282,9 +282,9 @@ static void raw3215_start_io(struct raw3215_info *raw) /* * Function to start a delayed output after RAW3215_TIMEOUT seconds */ -static void raw3215_timeout(unsigned long __data) +static void raw3215_timeout(struct timer_list *t) { - struct raw3215_info *raw = (struct raw3215_info *) __data; + struct raw3215_info *raw = from_timer(raw, t, timer); unsigned long flags; spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); @@ -670,7 +670,7 @@ static struct raw3215_info *raw3215_alloc_info(void) return NULL; } - setup_timer(&info->timer, raw3215_timeout, (unsigned long)info); + timer_setup(&info->timer, raw3215_timeout, 0); init_waitqueue_head(&info->empty_wait); tasklet_init(&info->tlet, raw3215_wakeup, (unsigned long)info); tty_port_init(&info->port); diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index be3e3c1206c2..fd2146bcc0ad 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -69,7 +69,7 @@ static struct con3270 *condev; #define CON_UPDATE_STATUS 4 /* Update status line. */ #define CON_UPDATE_ALL 8 /* Recreate screen. */ -static void con3270_update(struct con3270 *); +static void con3270_update(struct timer_list *); /* * Setup timeout for a device. On timeout trigger an update. @@ -205,8 +205,9 @@ con3270_write_callback(struct raw3270_request *rq, void *data) * Update console display. */ static void -con3270_update(struct con3270 *cp) +con3270_update(struct timer_list *t) { + struct con3270 *cp = from_timer(cp, t, timer); struct raw3270_request *wrq; char wcc, prolog[6]; unsigned long flags; @@ -552,7 +553,7 @@ con3270_flush(void) con3270_update_status(cp); while (cp->update_flags != 0) { spin_unlock_irqrestore(&cp->view.lock, flags); - con3270_update(cp); + con3270_update(&cp->timer); spin_lock_irqsave(&cp->view.lock, flags); con3270_wait_write(cp); } @@ -623,8 +624,7 @@ con3270_init(void) INIT_LIST_HEAD(&condev->lines); INIT_LIST_HEAD(&condev->update); - setup_timer(&condev->timer, (void (*)(unsigned long)) con3270_update, - (unsigned long) condev); + timer_setup(&condev->timer, con3270_update, 0); tasklet_init(&condev->readlet, (void (*)(unsigned long)) con3270_read_tasklet, (unsigned long) condev->read); diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 41d8aa96801f..9b4c61c1e309 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -136,6 +136,7 @@ static enum sclp_suspend_state_t { #define SCLP_BUSY_INTERVAL 10 #define SCLP_RETRY_INTERVAL 30 +static void sclp_request_timeout(bool force_restart); static void sclp_process_queue(void); static void __sclp_make_read_req(void); static int sclp_init_mask(int calculate); @@ -154,25 +155,32 @@ __sclp_queue_read_req(void) /* Set up request retry timer. Called while sclp_lock is locked. */ static inline void -__sclp_set_request_timer(unsigned long time, void (*function)(unsigned long), - unsigned long data) +__sclp_set_request_timer(unsigned long time, void (*cb)(struct timer_list *)) { del_timer(&sclp_request_timer); - sclp_request_timer.function = function; - sclp_request_timer.data = data; + sclp_request_timer.function = (TIMER_FUNC_TYPE)cb; sclp_request_timer.expires = jiffies + time; add_timer(&sclp_request_timer); } -/* Request timeout handler. Restart the request queue. If DATA is non-zero, +static void sclp_request_timeout_restart(struct timer_list *unused) +{ + sclp_request_timeout(true); +} + +static void sclp_request_timeout_normal(struct timer_list *unused) +{ + sclp_request_timeout(false); +} + +/* Request timeout handler. Restart the request queue. If force_restart, * force restart of running request. */ -static void -sclp_request_timeout(unsigned long data) +static void sclp_request_timeout(bool force_restart) { unsigned long flags; spin_lock_irqsave(&sclp_lock, flags); - if (data) { + if (force_restart) { if (sclp_running_state == sclp_running_state_running) { /* Break running state and queue NOP read event request * to get a defined interface state. */ @@ -181,7 +189,7 @@ sclp_request_timeout(unsigned long data) } } else { __sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ, - sclp_request_timeout, 0); + sclp_request_timeout_normal); } spin_unlock_irqrestore(&sclp_lock, flags); sclp_process_queue(); @@ -239,7 +247,7 @@ out: * invokes callback. This timer can be set per request in situations where * waiting too long would be harmful to the system, e.g. during SE reboot. */ -static void sclp_req_queue_timeout(unsigned long data) +static void sclp_req_queue_timeout(struct timer_list *unused) { unsigned long flags, expires_next; struct sclp_req *req; @@ -276,12 +284,12 @@ __sclp_start_request(struct sclp_req *req) req->status = SCLP_REQ_RUNNING; sclp_running_state = sclp_running_state_running; __sclp_set_request_timer(SCLP_RETRY_INTERVAL * HZ, - sclp_request_timeout, 1); + sclp_request_timeout_restart); return 0; } else if (rc == -EBUSY) { /* Try again later */ __sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ, - sclp_request_timeout, 0); + sclp_request_timeout_normal); return 0; } /* Request failed */ @@ -315,7 +323,7 @@ sclp_process_queue(void) /* Cannot abort already submitted request - could still * be active at the SCLP */ __sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ, - sclp_request_timeout, 0); + sclp_request_timeout_normal); break; } do_post: @@ -558,7 +566,7 @@ sclp_sync_wait(void) if (timer_pending(&sclp_request_timer) && get_tod_clock_fast() > timeout && del_timer(&sclp_request_timer)) - sclp_request_timer.function(sclp_request_timer.data); + sclp_request_timer.function((TIMER_DATA_TYPE)&sclp_request_timer); cpu_relax(); } local_irq_disable(); @@ -915,7 +923,7 @@ static void sclp_check_handler(struct ext_code ext_code, /* Initial init mask request timed out. Modify request state to failed. */ static void -sclp_check_timeout(unsigned long data) +sclp_check_timeout(struct timer_list *unused) { unsigned long flags; @@ -954,7 +962,7 @@ sclp_check_interface(void) sclp_init_req.status = SCLP_REQ_RUNNING; sclp_running_state = sclp_running_state_running; __sclp_set_request_timer(SCLP_RETRY_INTERVAL * HZ, - sclp_check_timeout, 0); + sclp_check_timeout); spin_unlock_irqrestore(&sclp_lock, flags); /* Enable service-signal interruption - needs to happen * with IRQs enabled. */ @@ -1159,9 +1167,8 @@ sclp_init(void) INIT_LIST_HEAD(&sclp_req_queue); INIT_LIST_HEAD(&sclp_reg_list); list_add(&sclp_state_change_event.list, &sclp_reg_list); - init_timer(&sclp_request_timer); - init_timer(&sclp_queue_timer); - sclp_queue_timer.function = sclp_req_queue_timeout; + timer_setup(&sclp_request_timer, NULL, 0); + timer_setup(&sclp_queue_timer, sclp_req_queue_timeout, 0); /* Check interface */ spin_unlock_irqrestore(&sclp_lock, flags); rc = sclp_check_interface(); diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index 7027e61a6931..8966a1c1b548 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -125,7 +125,7 @@ static void sclp_console_sync_queue(void) * temporary write buffer without further waiting on a final new line. */ static void -sclp_console_timeout(unsigned long data) +sclp_console_timeout(struct timer_list *unused) { sclp_conbuf_emit(); } @@ -211,7 +211,6 @@ sclp_console_write(struct console *console, const char *message, /* Setup timer to output current console buffer after 1/10 second */ if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 && !timer_pending(&sclp_con_timer)) { - setup_timer(&sclp_con_timer, sclp_console_timeout, 0UL); mod_timer(&sclp_con_timer, jiffies + HZ / 10); } out: @@ -332,7 +331,7 @@ sclp_console_init(void) INIT_LIST_HEAD(&sclp_con_outqueue); spin_lock_init(&sclp_con_lock); sclp_conbuf = NULL; - init_timer(&sclp_con_timer); + timer_setup(&sclp_con_timer, sclp_console_timeout, 0); /* Set output format */ if (MACHINE_IS_VM) diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 1cceefdc03e0..9f7b87d6d434 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -151,7 +151,7 @@ __sclp_ttybuf_emit(struct sclp_buffer *buffer) * temporary write buffer. */ static void -sclp_tty_timeout(unsigned long data) +sclp_tty_timeout(struct timer_list *unused) { unsigned long flags; struct sclp_buffer *buf; @@ -218,7 +218,6 @@ static int sclp_tty_write_string(const unsigned char *str, int count, int may_fa /* Setup timer to output current console buffer after 1/10 second */ if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) && !timer_pending(&sclp_tty_timer)) { - setup_timer(&sclp_tty_timer, sclp_tty_timeout, 0UL); mod_timer(&sclp_tty_timer, jiffies + HZ / 10); } spin_unlock_irqrestore(&sclp_tty_lock, flags); @@ -526,7 +525,7 @@ sclp_tty_init(void) } INIT_LIST_HEAD(&sclp_tty_outqueue); spin_lock_init(&sclp_tty_lock); - init_timer(&sclp_tty_timer); + timer_setup(&sclp_tty_timer, sclp_tty_timeout, 0); sclp_ttybuf = NULL; sclp_tty_buffer_count = 0; if (MACHINE_IS_VM) { diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index e84395d71389..3f9a6ef650fa 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -357,7 +357,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request, * Emit buffer after having waited long enough for more data to arrive. */ static void -sclp_vt220_timeout(unsigned long data) +sclp_vt220_timeout(struct timer_list *unused) { sclp_vt220_emit_current(); } @@ -454,8 +454,6 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, /* Setup timer to output current console buffer after some time */ if (sclp_vt220_current_request != NULL && !timer_pending(&sclp_vt220_timer) && do_schedule) { - sclp_vt220_timer.function = sclp_vt220_timeout; - sclp_vt220_timer.data = 0UL; sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY; add_timer(&sclp_vt220_timer); } @@ -699,7 +697,7 @@ static int __init __sclp_vt220_init(int num_pages) spin_lock_init(&sclp_vt220_lock); INIT_LIST_HEAD(&sclp_vt220_empty); INIT_LIST_HEAD(&sclp_vt220_outqueue); - init_timer(&sclp_vt220_timer); + timer_setup(&sclp_vt220_timer, sclp_vt220_timeout, 0); tty_port_init(&sclp_vt220_port); sclp_vt220_current_request = NULL; sclp_vt220_buffered_chars = 0; diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 9dd4534823b3..32503a60ee85 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -32,7 +32,7 @@ static void __tape_do_irq (struct ccw_device *, unsigned long, struct irb *); static void tape_delayed_next_request(struct work_struct *); -static void tape_long_busy_timeout(unsigned long data); +static void tape_long_busy_timeout(struct timer_list *t); /* * One list to contain all tape devices of all disciplines, so @@ -381,8 +381,7 @@ tape_generic_online(struct tape_device *device, return -EINVAL; } - init_timer(&device->lb_timeout); - device->lb_timeout.function = tape_long_busy_timeout; + timer_setup(&device->lb_timeout, tape_long_busy_timeout, 0); /* Let the discipline have a go at the device. */ device->discipline = discipline; @@ -867,18 +866,16 @@ tape_delayed_next_request(struct work_struct *work) spin_unlock_irq(get_ccwdev_lock(device->cdev)); } -static void tape_long_busy_timeout(unsigned long data) +static void tape_long_busy_timeout(struct timer_list *t) { + struct tape_device *device = from_timer(device, t, lb_timeout); struct tape_request *request; - struct tape_device *device; - device = (struct tape_device *) data; spin_lock_irq(get_ccwdev_lock(device->cdev)); request = list_entry(device->req_queue.next, struct tape_request, list); BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY); DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id); __tape_start_next_request(device); - device->lb_timeout.data = 0UL; tape_put_device(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); } @@ -1157,7 +1154,6 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) if (req->status == TAPE_REQUEST_LONG_BUSY) { DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id); if (del_timer(&device->lb_timeout)) { - device->lb_timeout.data = 0UL; tape_put_device(device); __tape_start_next_request(device); } @@ -1212,8 +1208,6 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) case TAPE_IO_PENDING: break; case TAPE_IO_LONG_BUSY: - device->lb_timeout.data = - (unsigned long) tape_get_device(device); device->lb_timeout.expires = jiffies + LONG_BUSY_TIMEOUT * HZ; DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id); diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index e5ebe2fbee23..e417ccd9e299 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -118,7 +118,7 @@ struct tty3270 { #define TTY_UPDATE_STATUS 8 /* Update status line. */ #define TTY_UPDATE_ALL 16 /* Recreate screen. */ -static void tty3270_update(struct tty3270 *); +static void tty3270_update(struct timer_list *); static void tty3270_resize_work(struct work_struct *work); /* @@ -361,8 +361,9 @@ tty3270_write_callback(struct raw3270_request *rq, void *data) * Update 3270 display. */ static void -tty3270_update(struct tty3270 *tp) +tty3270_update(struct timer_list *t) { + struct tty3270 *tp = from_timer(tp, t, timer); static char invalid_sba[2] = { 0xff, 0xff }; struct raw3270_request *wrq; unsigned long updated; @@ -748,8 +749,7 @@ tty3270_alloc_view(void) goto out_reset; tty_port_init(&tp->port); - setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, - (unsigned long) tp); + timer_setup(&tp->timer, tty3270_update, 0); tasklet_init(&tp->readlet, (void (*)(unsigned long)) tty3270_read_tasklet, (unsigned long) tp->read); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e5c32f4b5287..318d8269f5de 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -142,7 +142,7 @@ static void io_subchannel_shutdown(struct subchannel *); static int io_subchannel_sch_event(struct subchannel *, int); static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, int); -static void recovery_func(unsigned long data); +static void recovery_func(struct timer_list *unused); static struct css_device_id io_subchannel_ids[] = { { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, }, @@ -194,7 +194,7 @@ int __init io_subchannel_init(void) { int ret; - setup_timer(&recovery_timer, recovery_func, 0); + timer_setup(&recovery_timer, recovery_func, 0); ret = bus_register(&ccw_bus_type); if (ret) return ret; @@ -726,7 +726,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, INIT_WORK(&priv->todo_work, ccw_device_todo); INIT_LIST_HEAD(&priv->cmb_list); init_waitqueue_head(&priv->wait_q); - init_timer(&priv->timer); + timer_setup(&priv->timer, ccw_device_timeout, 0); atomic_set(&priv->onoff, 0); cdev->ccwlock = sch->lock; @@ -1271,7 +1271,7 @@ static void recovery_work_func(struct work_struct *unused) static DECLARE_WORK(recovery_work, recovery_work_func); -static void recovery_func(unsigned long data) +static void recovery_func(struct timer_list *unused) { /* * We can't do our recovery in softirq context and it's not diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index b37c22adcc7a..f5c427ec24b1 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -134,6 +135,7 @@ int ccw_device_notify(struct ccw_device *, int); void ccw_device_set_disconnected(struct ccw_device *cdev); void ccw_device_set_notoper(struct ccw_device *cdev); +void ccw_device_timeout(struct timer_list *t); void ccw_device_set_timeout(struct ccw_device *, int); void ccw_device_schedule_recovery(void); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index f98ea674c3d8..dd7d79d30edc 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -91,12 +91,12 @@ static void ccw_timeout_log(struct ccw_device *cdev) /* * Timeout function. It just triggers a DEV_EVENT_TIMEOUT. */ -static void -ccw_device_timeout(unsigned long data) +void +ccw_device_timeout(struct timer_list *t) { - struct ccw_device *cdev; + struct ccw_device_private *priv = from_timer(priv, t, timer); + struct ccw_device *cdev = priv->cdev; - cdev = (struct ccw_device *) data; spin_lock_irq(cdev->ccwlock); if (timeout_log_enabled) ccw_timeout_log(cdev); @@ -118,8 +118,6 @@ ccw_device_set_timeout(struct ccw_device *cdev, int expires) if (mod_timer(&cdev->private->timer, jiffies + expires)) return; } - cdev->private->timer.function = ccw_device_timeout; - cdev->private->timer.data = (unsigned long) cdev; cdev->private->timer.expires = jiffies + expires; add_timer(&cdev->private->timer); } diff --git a/drivers/s390/cio/eadm_sch.c b/drivers/s390/cio/eadm_sch.c index d14795f7110b..ce16e4f45d44 100644 --- a/drivers/s390/cio/eadm_sch.c +++ b/drivers/s390/cio/eadm_sch.c @@ -94,9 +94,10 @@ static int eadm_subchannel_clear(struct subchannel *sch) return 0; } -static void eadm_subchannel_timeout(unsigned long data) +static void eadm_subchannel_timeout(struct timer_list *t) { - struct subchannel *sch = (struct subchannel *) data; + struct eadm_private *private = from_timer(private, t, timer); + struct subchannel *sch = private->sch; spin_lock_irq(sch->lock); EADM_LOG(1, "timeout"); @@ -118,8 +119,6 @@ static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires) if (mod_timer(&private->timer, jiffies + expires)) return; } - private->timer.function = eadm_subchannel_timeout; - private->timer.data = (unsigned long) sch; private->timer.expires = jiffies + expires; add_timer(&private->timer); } @@ -224,7 +223,7 @@ static int eadm_subchannel_probe(struct subchannel *sch) return -ENOMEM; INIT_LIST_HEAD(&private->head); - init_timer(&private->timer); + timer_setup(&private->timer, eadm_subchannel_timeout, 0); spin_lock_irq(sch->lock); set_eadm_private(sch, private); diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 29d6b5222f1c..a6f7c2986b94 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -393,7 +393,7 @@ int test_nonshared_ind(struct qdio_irq *); /* prototypes for setup */ void qdio_inbound_processing(unsigned long data); void qdio_outbound_processing(unsigned long data); -void qdio_outbound_timer(unsigned long data); +void qdio_outbound_timer(struct timer_list *t); void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb); int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs, diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index a4ad39ba3873..ed4852fab44b 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -894,9 +894,9 @@ void qdio_outbound_processing(unsigned long data) __qdio_outbound_processing(q); } -void qdio_outbound_timer(unsigned long data) +void qdio_outbound_timer(struct timer_list *t) { - struct qdio_q *q = (struct qdio_q *)data; + struct qdio_q *q = from_timer(q, t, u.out.timer); qdio_tasklet_schedule(q); } diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 48b3866a9ded..9ae1380cbc31 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -252,8 +252,7 @@ static void setup_queues(struct qdio_irq *irq_ptr, tasklet_init(&q->tasklet, qdio_outbound_processing, (unsigned long) q); - setup_timer(&q->u.out.timer, (void(*)(unsigned long)) - &qdio_outbound_timer, (unsigned long)q); + timer_setup(&q->u.out.timer, qdio_outbound_timer, 0); } } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 8b5658b0bec3..faeba9db3d95 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -374,13 +374,13 @@ void ap_wait(enum ap_wait wait) /** * ap_request_timeout(): Handling of request timeouts - * @data: Holds the AP device. + * @t: timer making this callback * * Handles request timeouts. */ -void ap_request_timeout(unsigned long data) +void ap_request_timeout(struct timer_list *t) { - struct ap_queue *aq = (struct ap_queue *) data; + struct ap_queue *aq = from_timer(aq, t, timeout); if (ap_suspend_flag) return; @@ -1203,7 +1203,7 @@ out: mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ); } -static void ap_config_timeout(unsigned long ptr) +static void ap_config_timeout(struct timer_list *unused) { if (ap_suspend_flag) return; @@ -1306,7 +1306,7 @@ int __init ap_module_init(void) goto out_bus; /* Setup the AP bus rescan timer. */ - setup_timer(&ap_config_timer, ap_config_timeout, 0); + timer_setup(&ap_config_timer, ap_config_timeout, 0); /* * Setup the high resultion poll timer. diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 3a0e19d87e7c..7e45c4d08cad 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -241,7 +241,7 @@ void ap_flush_queue(struct ap_queue *aq); void *ap_airq_ptr(void); void ap_wait(enum ap_wait wait); -void ap_request_timeout(unsigned long data); +void ap_request_timeout(struct timer_list *t); void ap_bus_force_rescan(void); void ap_queue_init_reply(struct ap_queue *aq, struct ap_message *ap_msg); diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index a550d40921e7..ba3a2e13b0eb 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -634,7 +634,7 @@ struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type) INIT_LIST_HEAD(&aq->list); INIT_LIST_HEAD(&aq->pendingq); INIT_LIST_HEAD(&aq->requestq); - setup_timer(&aq->timeout, ap_request_timeout, (unsigned long) aq); + timer_setup(&aq->timeout, ap_request_timeout, 0); return aq; } diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index cbb8156bf5e0..1d91a32db08e 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -564,21 +564,24 @@ void zfcp_erp_notify(struct zfcp_erp_action *erp_action, unsigned long set_mask) * zfcp_erp_timeout_handler - Trigger ERP action from timed out ERP request * @data: ERP action (from timer data) */ -void zfcp_erp_timeout_handler(unsigned long data) +void zfcp_erp_timeout_handler(struct timer_list *t) { - struct zfcp_erp_action *act = (struct zfcp_erp_action *) data; + struct zfcp_fsf_req *fsf_req = from_timer(fsf_req, t, timer); + struct zfcp_erp_action *act = fsf_req->erp_action; + zfcp_erp_notify(act, ZFCP_STATUS_ERP_TIMEDOUT); } -static void zfcp_erp_memwait_handler(unsigned long data) +static void zfcp_erp_memwait_handler(struct timer_list *t) { - zfcp_erp_notify((struct zfcp_erp_action *)data, 0); + struct zfcp_erp_action *act = from_timer(act, t, timer); + + zfcp_erp_notify(act, 0); } static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action) { - setup_timer(&erp_action->timer, zfcp_erp_memwait_handler, - (unsigned long) erp_action); + timer_setup(&erp_action->timer, zfcp_erp_memwait_handler, 0); erp_action->timer.expires = jiffies + HZ; add_timer(&erp_action->timer); } diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 8ca2ab7deaa9..bf8ea4df2bb8 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -69,7 +69,7 @@ extern int zfcp_erp_thread_setup(struct zfcp_adapter *); extern void zfcp_erp_thread_kill(struct zfcp_adapter *); extern void zfcp_erp_wait(struct zfcp_adapter *); extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long); -extern void zfcp_erp_timeout_handler(unsigned long); +extern void zfcp_erp_timeout_handler(struct timer_list *t); /* zfcp_fc.c */ extern struct kmem_cache *zfcp_fc_req_cache; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 00fb98f7b2cd..51b81c0a0652 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -21,9 +21,11 @@ struct kmem_cache *zfcp_fsf_qtcb_cache; -static void zfcp_fsf_request_timeout_handler(unsigned long data) +static void zfcp_fsf_request_timeout_handler(struct timer_list *t) { - struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; + struct zfcp_fsf_req *fsf_req = from_timer(fsf_req, t, timer); + struct zfcp_adapter *adapter = fsf_req->adapter; + zfcp_qdio_siosl(adapter); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, "fsrth_1"); @@ -32,8 +34,7 @@ static void zfcp_fsf_request_timeout_handler(unsigned long data) static void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, unsigned long timeout) { - fsf_req->timer.function = zfcp_fsf_request_timeout_handler; - fsf_req->timer.data = (unsigned long) fsf_req->adapter; + fsf_req->timer.function = (TIMER_FUNC_TYPE)zfcp_fsf_request_timeout_handler; fsf_req->timer.expires = jiffies + timeout; add_timer(&fsf_req->timer); } @@ -41,8 +42,7 @@ static void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, static void zfcp_fsf_start_erp_timer(struct zfcp_fsf_req *fsf_req) { BUG_ON(!fsf_req->erp_action); - fsf_req->timer.function = zfcp_erp_timeout_handler; - fsf_req->timer.data = (unsigned long) fsf_req->erp_action; + fsf_req->timer.function = (TIMER_FUNC_TYPE)zfcp_erp_timeout_handler; fsf_req->timer.expires = jiffies + 30 * HZ; add_timer(&fsf_req->timer); } @@ -692,7 +692,7 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio, adapter->req_no++; INIT_LIST_HEAD(&req->list); - init_timer(&req->timer); + timer_setup(&req->timer, NULL, 0); init_completion(&req->completion); req->adapter = adapter; diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index f32765d3cbd8..5c8ed7350a04 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -22,7 +22,6 @@ #include -#define D7S_MINOR 193 #define DRIVER_NAME "d7s" #define PFX DRIVER_NAME ": " diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 5b6153f23f01..8e2f767147cb 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1084,24 +1084,35 @@ static int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req) { struct bnx2fc_rport *tgt = io_req->tgt; int rc = SUCCESS; + unsigned int time_left; io_req->wait_for_comp = 1; bnx2fc_initiate_cleanup(io_req); spin_unlock_bh(&tgt->tgt_lock); - wait_for_completion(&io_req->tm_done); - + /* + * Can't wait forever on cleanup response lest we let the SCSI error + * handler wait forever + */ + time_left = wait_for_completion_timeout(&io_req->tm_done, + BNX2FC_FW_TIMEOUT); io_req->wait_for_comp = 0; + if (!time_left) + BNX2FC_IO_DBG(io_req, "%s(): Wait for cleanup timed out.\n", + __func__); + /* - * release the reference taken in eh_abort to allow the - * target to re-login after flushing IOs + * Release reference held by SCSI command the cleanup completion + * hits the BNX2FC_CLEANUP case in bnx2fc_process_cq_compl() and + * thus the SCSI command is not returnedi by bnx2fc_scsi_done(). */ kref_put(&io_req->refcount, bnx2fc_cmd_release); spin_lock_bh(&tgt->tgt_lock); return rc; } + /** * bnx2fc_eh_abort - eh_abort_handler api to abort an outstanding * SCSI command @@ -1118,6 +1129,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) struct fc_lport *lport; struct bnx2fc_rport *tgt; int rc; + unsigned int time_left; rc = fc_block_scsi_eh(sc_cmd); if (rc) @@ -1194,6 +1206,11 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) if (cancel_delayed_work(&io_req->timeout_work)) kref_put(&io_req->refcount, bnx2fc_cmd_release); /* drop timer hold */ + /* + * We don't want to hold off the upper layer timer so simply + * cleanup the command and return that I/O was successfully + * aborted. + */ rc = bnx2fc_abts_cleanup(io_req); /* This only occurs when an task abort was requested while ABTS is in progress. Setting the IO_CLEANUP flag will skip the @@ -1201,7 +1218,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) was a result from the ABTS request rather than the CLEANUP request */ set_bit(BNX2FC_FLAG_IO_CLEANUP, &io_req->req_flags); - goto out; + goto done; } /* Cancel the current timer running on this io_req */ @@ -1221,7 +1238,11 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) } spin_unlock_bh(&tgt->tgt_lock); - wait_for_completion(&io_req->tm_done); + /* Wait 2 * RA_TOV + 1 to be sure timeout function hasn't fired */ + time_left = wait_for_completion_timeout(&io_req->tm_done, + (2 * rp->r_a_tov + 1) * HZ); + if (time_left) + BNX2FC_IO_DBG(io_req, "Timed out in eh_abort waiting for tm_done"); spin_lock_bh(&tgt->tgt_lock); io_req->wait_for_comp = 0; @@ -1233,8 +1254,12 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) /* Let the scsi-ml try to recover this command */ printk(KERN_ERR PFX "abort failed, xid = 0x%x\n", io_req->xid); + /* + * Cleanup firmware residuals before returning control back + * to SCSI ML. + */ rc = bnx2fc_abts_cleanup(io_req); - goto out; + goto done; } else { /* * We come here even when there was a race condition @@ -1249,7 +1274,6 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) done: /* release the reference taken in eh_abort */ kref_put(&io_req->refcount, bnx2fc_cmd_release); -out: spin_unlock_bh(&tgt->tgt_lock); return rc; } diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 617802855233..38b3a9c84fd1 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -3393,12 +3393,6 @@ static int cxlflash_afu_debug(struct cxlflash_cfg *cfg, goto out; } - if (unlikely(!access_ok(is_write ? VERIFY_READ : VERIFY_WRITE, - ubuf, ulen))) { - rc = -EFAULT; - goto out; - } - buf = kmalloc(ulen + cache_line_size() - 1, GFP_KERNEL); if (unlikely(!buf)) { rc = -ENOMEM; diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index fe5a9ea27b5e..78d4aa8df675 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -22,7 +22,7 @@ struct scsi_dev_info_list { struct list_head dev_info_list; char vendor[8]; char model[16]; - unsigned flags; + blist_flags_t flags; unsigned compatible; /* for use with scsi_static_device_list entries */ }; @@ -35,7 +35,7 @@ struct scsi_dev_info_list_table { static const char spaces[] = " "; /* 16 of them */ -static unsigned scsi_default_dev_flags; +static blist_flags_t scsi_default_dev_flags; static LIST_HEAD(scsi_dev_info_list); static char scsi_dev_flags[256]; @@ -52,7 +52,7 @@ static struct { char *vendor; char *model; char *revision; /* revision known to be bad, unused */ - unsigned flags; + blist_flags_t flags; } scsi_static_device_list[] __initdata = { /* * The following devices are known not to tolerate a lun != 0 scan @@ -335,7 +335,7 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length, * Returns: 0 OK, -error on failure. **/ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, - char *strflags, int flags) + char *strflags, blist_flags_t flags) { return scsi_dev_info_list_add_keyed(compatible, vendor, model, strflags, flags, @@ -361,7 +361,7 @@ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, * Returns: 0 OK, -error on failure. **/ int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, - char *strflags, int flags, int key) + char *strflags, blist_flags_t flags, int key) { struct scsi_dev_info_list *devinfo; struct scsi_dev_info_list_table *devinfo_table = @@ -571,9 +571,9 @@ static int scsi_dev_info_list_add_str(char *dev_list) * matching flags value, else return the host or global default * settings. Called during scan time. **/ -int scsi_get_device_flags(struct scsi_device *sdev, - const unsigned char *vendor, - const unsigned char *model) +blist_flags_t scsi_get_device_flags(struct scsi_device *sdev, + const unsigned char *vendor, + const unsigned char *model) { return scsi_get_device_flags_keyed(sdev, vendor, model, SCSI_DEVINFO_GLOBAL); @@ -593,7 +593,7 @@ int scsi_get_device_flags(struct scsi_device *sdev, * flags value, else return the host or global default settings. * Called during scan time. **/ -int scsi_get_device_flags_keyed(struct scsi_device *sdev, +blist_flags_t scsi_get_device_flags_keyed(struct scsi_device *sdev, const unsigned char *vendor, const unsigned char *model, int key) diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index df1368aea9a3..a5946cd64caa 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -50,15 +50,16 @@ enum { SCSI_DEVINFO_SPI, }; -extern int scsi_get_device_flags(struct scsi_device *sdev, - const unsigned char *vendor, - const unsigned char *model); -extern int scsi_get_device_flags_keyed(struct scsi_device *sdev, - const unsigned char *vendor, - const unsigned char *model, int key); +extern blist_flags_t scsi_get_device_flags(struct scsi_device *sdev, + const unsigned char *vendor, + const unsigned char *model); +extern blist_flags_t scsi_get_device_flags_keyed(struct scsi_device *sdev, + const unsigned char *vendor, + const unsigned char *model, + int key); extern int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, char *strflags, - int flags, int key); + blist_flags_t flags, int key); extern int scsi_dev_info_list_del_keyed(char *vendor, char *model, int key); extern int scsi_dev_info_add_list(int key, const char *name); extern int scsi_dev_info_remove_list(int key); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a0f2a20ea9e9..be5e919db0e8 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -566,7 +566,7 @@ EXPORT_SYMBOL(scsi_sanitize_inquiry_string); * are copied to the scsi_device any flags value is stored in *@bflags. **/ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, - int result_len, int *bflags) + int result_len, blist_flags_t *bflags) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; int first_inquiry_len, try_inquiry_len, next_inquiry_len; diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 94e402ed30f6..b141d7641a2e 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -4920,11 +4920,7 @@ static int sgl_map_user_pages(struct st_buffer *STbp, /* Try to fault in all of the necessary pages */ /* rw==READ means read from drive, write into memory area */ - res = get_user_pages_unlocked( - uaddr, - nr_pages, - pages, - rw == READ ? FOLL_WRITE : 0); /* don't force */ + res = get_user_pages_fast(uaddr, nr_pages, rw == READ, pages); /* Errors and no page mapped should return here */ if (res < nr_pages) diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c index f8ea523863ba..986c2a40d978 100644 --- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c +++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c @@ -1683,10 +1683,10 @@ ksocknal_destroy_conn(struct ksock_conn *conn) case SOCKNAL_RX_LNET_PAYLOAD: last_rcv = conn->ksnc_rx_deadline - cfs_time_seconds(*ksocknal_tunables.ksnd_timeout); - CERROR("Completing partial receive from %s[%d], ip %pI4h:%d, with error, wanted: %d, left: %d, last alive is %ld secs ago\n", + CERROR("Completing partial receive from %s[%d], ip %pI4h:%d, with error, wanted: %zd, left: %d, last alive is %ld secs ago\n", libcfs_id2str(conn->ksnc_peer->ksnp_id), conn->ksnc_type, &conn->ksnc_ipaddr, conn->ksnc_port, - conn->ksnc_rx_nob_wanted, conn->ksnc_rx_nob_left, + iov_iter_count(&conn->ksnc_rx_to), conn->ksnc_rx_nob_left, cfs_duration_sec(cfs_time_sub(cfs_time_current(), last_rcv))); lnet_finalize(conn->ksnc_peer->ksnp_ni, diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h index 35a7b396def4..d50ebdf863fa 100644 --- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h +++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h @@ -358,11 +358,7 @@ struct ksock_conn { __u8 ksnc_rx_scheduled; /* being progressed */ __u8 ksnc_rx_state; /* what is being read */ int ksnc_rx_nob_left; /* # bytes to next hdr/body */ - int ksnc_rx_nob_wanted;/* bytes actually wanted */ - int ksnc_rx_niov; /* # iovec frags */ - struct kvec *ksnc_rx_iov; /* the iovec frags */ - int ksnc_rx_nkiov; /* # page frags */ - struct bio_vec *ksnc_rx_kiov; /* the page frags */ + struct iov_iter ksnc_rx_to; /* copy destination */ union ksock_rxiovspace ksnc_rx_iov_space; /* space for frag descriptors */ __u32 ksnc_rx_csum; /* partial checksum for incoming * data @@ -701,8 +697,7 @@ int ksocknal_lib_setup_sock(struct socket *so); int ksocknal_lib_send_iov(struct ksock_conn *conn, struct ksock_tx *tx); int ksocknal_lib_send_kiov(struct ksock_conn *conn, struct ksock_tx *tx); void ksocknal_lib_eager_ack(struct ksock_conn *conn); -int ksocknal_lib_recv_iov(struct ksock_conn *conn); -int ksocknal_lib_recv_kiov(struct ksock_conn *conn); +int ksocknal_lib_recv(struct ksock_conn *conn); int ksocknal_lib_get_conn_tunables(struct ksock_conn *conn, int *txmem, int *rxmem, int *nagle); diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c index a5f2ecb966fa..27c56d5ae4e5 100644 --- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c +++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c @@ -250,66 +250,16 @@ ksocknal_transmit(struct ksock_conn *conn, struct ksock_tx *tx) } static int -ksocknal_recv_iov(struct ksock_conn *conn) +ksocknal_recv_iter(struct ksock_conn *conn) { - struct kvec *iov = conn->ksnc_rx_iov; int nob; int rc; - LASSERT(conn->ksnc_rx_niov > 0); - - /* - * Never touch conn->ksnc_rx_iov or change connection - * status inside ksocknal_lib_recv_iov - */ - rc = ksocknal_lib_recv_iov(conn); - - if (rc <= 0) - return rc; - - /* received something... */ - nob = rc; - - conn->ksnc_peer->ksnp_last_alive = cfs_time_current(); - conn->ksnc_rx_deadline = - cfs_time_shift(*ksocknal_tunables.ksnd_timeout); - mb(); /* order with setting rx_started */ - conn->ksnc_rx_started = 1; - - conn->ksnc_rx_nob_wanted -= nob; - conn->ksnc_rx_nob_left -= nob; - - do { - LASSERT(conn->ksnc_rx_niov > 0); - - if (nob < (int)iov->iov_len) { - iov->iov_len -= nob; - iov->iov_base += nob; - return -EAGAIN; - } - - nob -= iov->iov_len; - conn->ksnc_rx_iov = ++iov; - conn->ksnc_rx_niov--; - } while (nob); - - return rc; -} - -static int -ksocknal_recv_kiov(struct ksock_conn *conn) -{ - struct bio_vec *kiov = conn->ksnc_rx_kiov; - int nob; - int rc; - - LASSERT(conn->ksnc_rx_nkiov > 0); - /* - * Never touch conn->ksnc_rx_kiov or change connection - * status inside ksocknal_lib_recv_iov + * Never touch conn->ksnc_rx_to or change connection + * status inside ksocknal_lib_recv */ - rc = ksocknal_lib_recv_kiov(conn); + rc = ksocknal_lib_recv(conn); if (rc <= 0) return rc; @@ -323,22 +273,11 @@ ksocknal_recv_kiov(struct ksock_conn *conn) mb(); /* order with setting rx_started */ conn->ksnc_rx_started = 1; - conn->ksnc_rx_nob_wanted -= nob; conn->ksnc_rx_nob_left -= nob; - do { - LASSERT(conn->ksnc_rx_nkiov > 0); - - if (nob < (int)kiov->bv_len) { - kiov->bv_offset += nob; - kiov->bv_len -= nob; - return -EAGAIN; - } - - nob -= kiov->bv_len; - conn->ksnc_rx_kiov = ++kiov; - conn->ksnc_rx_nkiov--; - } while (nob); + iov_iter_advance(&conn->ksnc_rx_to, nob); + if (iov_iter_count(&conn->ksnc_rx_to)) + return -EAGAIN; return 1; } @@ -348,7 +287,7 @@ ksocknal_receive(struct ksock_conn *conn) { /* * Return 1 on success, 0 on EOF, < 0 on error. - * Caller checks ksnc_rx_nob_wanted to determine + * Caller checks ksnc_rx_to to determine * progress/completion. */ int rc; @@ -365,11 +304,7 @@ ksocknal_receive(struct ksock_conn *conn) } for (;;) { - if (conn->ksnc_rx_niov) - rc = ksocknal_recv_iov(conn); - else - rc = ksocknal_recv_kiov(conn); - + rc = ksocknal_recv_iter(conn); if (rc <= 0) { /* error/EOF or partial receive */ if (rc == -EAGAIN) { @@ -383,7 +318,7 @@ ksocknal_receive(struct ksock_conn *conn) /* Completed a fragment */ - if (!conn->ksnc_rx_nob_wanted) { + if (!iov_iter_count(&conn->ksnc_rx_to)) { rc = 1; break; } @@ -1051,6 +986,7 @@ int ksocknal_new_packet(struct ksock_conn *conn, int nob_to_skip) { static char ksocknal_slop_buffer[4096]; + struct kvec *kvec = (struct kvec *)&conn->ksnc_rx_iov_space; int nob; unsigned int niov; @@ -1071,32 +1007,26 @@ ksocknal_new_packet(struct ksock_conn *conn, int nob_to_skip) case KSOCK_PROTO_V2: case KSOCK_PROTO_V3: conn->ksnc_rx_state = SOCKNAL_RX_KSM_HEADER; - conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space; - conn->ksnc_rx_iov[0].iov_base = &conn->ksnc_msg; - - conn->ksnc_rx_nob_wanted = offsetof(struct ksock_msg, ksm_u); + kvec->iov_base = &conn->ksnc_msg; + kvec->iov_len = offsetof(struct ksock_msg, ksm_u); conn->ksnc_rx_nob_left = offsetof(struct ksock_msg, ksm_u); - conn->ksnc_rx_iov[0].iov_len = offsetof(struct ksock_msg, ksm_u); + iov_iter_kvec(&conn->ksnc_rx_to, READ|ITER_KVEC, kvec, + 1, offsetof(struct ksock_msg, ksm_u)); break; case KSOCK_PROTO_V1: /* Receiving bare struct lnet_hdr */ conn->ksnc_rx_state = SOCKNAL_RX_LNET_HEADER; - conn->ksnc_rx_nob_wanted = sizeof(struct lnet_hdr); + kvec->iov_base = &conn->ksnc_msg.ksm_u.lnetmsg; + kvec->iov_len = sizeof(struct lnet_hdr); conn->ksnc_rx_nob_left = sizeof(struct lnet_hdr); - - conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space; - conn->ksnc_rx_iov[0].iov_base = &conn->ksnc_msg.ksm_u.lnetmsg; - conn->ksnc_rx_iov[0].iov_len = sizeof(struct lnet_hdr); + iov_iter_kvec(&conn->ksnc_rx_to, READ|ITER_KVEC, kvec, + 1, sizeof(struct lnet_hdr)); break; default: LBUG(); } - conn->ksnc_rx_niov = 1; - - conn->ksnc_rx_kiov = NULL; - conn->ksnc_rx_nkiov = 0; conn->ksnc_rx_csum = ~0; return 1; } @@ -1107,15 +1037,14 @@ ksocknal_new_packet(struct ksock_conn *conn, int nob_to_skip) */ conn->ksnc_rx_state = SOCKNAL_RX_SLOP; conn->ksnc_rx_nob_left = nob_to_skip; - conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space; skipped = 0; niov = 0; do { nob = min_t(int, nob_to_skip, sizeof(ksocknal_slop_buffer)); - conn->ksnc_rx_iov[niov].iov_base = ksocknal_slop_buffer; - conn->ksnc_rx_iov[niov].iov_len = nob; + kvec[niov].iov_base = ksocknal_slop_buffer; + kvec[niov].iov_len = nob; niov++; skipped += nob; nob_to_skip -= nob; @@ -1123,16 +1052,14 @@ ksocknal_new_packet(struct ksock_conn *conn, int nob_to_skip) } while (nob_to_skip && /* mustn't overflow conn's rx iov */ niov < sizeof(conn->ksnc_rx_iov_space) / sizeof(struct iovec)); - conn->ksnc_rx_niov = niov; - conn->ksnc_rx_kiov = NULL; - conn->ksnc_rx_nkiov = 0; - conn->ksnc_rx_nob_wanted = skipped; + iov_iter_kvec(&conn->ksnc_rx_to, READ|ITER_KVEC, kvec, niov, skipped); return 0; } static int ksocknal_process_receive(struct ksock_conn *conn) { + struct kvec *kvec = (struct kvec *)&conn->ksnc_rx_iov_space; struct lnet_hdr *lhdr; struct lnet_process_id *id; int rc; @@ -1146,7 +1073,7 @@ ksocknal_process_receive(struct ksock_conn *conn) conn->ksnc_rx_state == SOCKNAL_RX_LNET_HEADER || conn->ksnc_rx_state == SOCKNAL_RX_SLOP); again: - if (conn->ksnc_rx_nob_wanted) { + if (iov_iter_count(&conn->ksnc_rx_to)) { rc = ksocknal_receive(conn); if (rc <= 0) { @@ -1171,7 +1098,7 @@ ksocknal_process_receive(struct ksock_conn *conn) return (!rc ? -ESHUTDOWN : rc); } - if (conn->ksnc_rx_nob_wanted) { + if (iov_iter_count(&conn->ksnc_rx_to)) { /* short read */ return -EAGAIN; } @@ -1234,16 +1161,13 @@ ksocknal_process_receive(struct ksock_conn *conn) } conn->ksnc_rx_state = SOCKNAL_RX_LNET_HEADER; - conn->ksnc_rx_nob_wanted = sizeof(struct ksock_lnet_msg); conn->ksnc_rx_nob_left = sizeof(struct ksock_lnet_msg); - conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space; - conn->ksnc_rx_iov[0].iov_base = &conn->ksnc_msg.ksm_u.lnetmsg; - conn->ksnc_rx_iov[0].iov_len = sizeof(struct ksock_lnet_msg); + kvec->iov_base = &conn->ksnc_msg.ksm_u.lnetmsg; + kvec->iov_len = sizeof(struct ksock_lnet_msg); - conn->ksnc_rx_niov = 1; - conn->ksnc_rx_kiov = NULL; - conn->ksnc_rx_nkiov = 0; + iov_iter_kvec(&conn->ksnc_rx_to, READ|ITER_KVEC, kvec, + 1, sizeof(struct ksock_lnet_msg)); goto again; /* read lnet header now */ @@ -1345,26 +1269,9 @@ ksocknal_recv(struct lnet_ni *ni, void *private, struct lnet_msg *msg, LASSERT(to->nr_segs <= LNET_MAX_IOV); conn->ksnc_cookie = msg; - conn->ksnc_rx_nob_wanted = iov_iter_count(to); conn->ksnc_rx_nob_left = rlen; - if (to->type & ITER_KVEC) { - conn->ksnc_rx_nkiov = 0; - conn->ksnc_rx_kiov = NULL; - conn->ksnc_rx_iov = conn->ksnc_rx_iov_space.iov; - conn->ksnc_rx_niov = - lnet_extract_iov(LNET_MAX_IOV, conn->ksnc_rx_iov, - to->nr_segs, to->kvec, - to->iov_offset, iov_iter_count(to)); - } else { - conn->ksnc_rx_niov = 0; - conn->ksnc_rx_iov = NULL; - conn->ksnc_rx_kiov = conn->ksnc_rx_iov_space.kiov; - conn->ksnc_rx_nkiov = - lnet_extract_kiov(LNET_MAX_IOV, conn->ksnc_rx_kiov, - to->nr_segs, to->bvec, - to->iov_offset, iov_iter_count(to)); - } + conn->ksnc_rx_to = *to; LASSERT(conn->ksnc_rx_scheduled); @@ -2329,12 +2236,12 @@ ksocknal_find_timed_out_conn(struct ksock_peer *peer) conn->ksnc_rx_deadline)) { /* Timed out incomplete incoming message */ ksocknal_conn_addref(conn); - CNETERR("Timeout receiving from %s (%pI4h:%d), state %d wanted %d left %d\n", + CNETERR("Timeout receiving from %s (%pI4h:%d), state %d wanted %zd left %d\n", libcfs_id2str(peer->ksnp_id), &conn->ksnc_ipaddr, conn->ksnc_port, conn->ksnc_rx_state, - conn->ksnc_rx_nob_wanted, + iov_iter_count(&conn->ksnc_rx_to), conn->ksnc_rx_nob_left); return conn; } diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c index 970140f09258..cb28dd2baf2f 100644 --- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c +++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c @@ -162,94 +162,39 @@ ksocknal_lib_eager_ack(struct ksock_conn *conn) sizeof(opt)); } -int -ksocknal_lib_recv_iov(struct ksock_conn *conn) +static int lustre_csum(struct kvec *v, void *context) { - unsigned int niov = conn->ksnc_rx_niov; - struct kvec *iov = conn->ksnc_rx_iov; - struct msghdr msg = { - .msg_flags = 0 - }; - int nob; - int i; - int rc; - int fragnob; - int sum; - __u32 saved_csum; - - LASSERT(niov > 0); - - for (nob = i = 0; i < niov; i++) - nob += iov[i].iov_len; - - LASSERT(nob <= conn->ksnc_rx_nob_wanted); - - iov_iter_kvec(&msg.msg_iter, READ | ITER_KVEC, iov, niov, nob); - rc = sock_recvmsg(conn->ksnc_sock, &msg, MSG_DONTWAIT); - - saved_csum = 0; - if (conn->ksnc_proto == &ksocknal_protocol_v2x) { - saved_csum = conn->ksnc_msg.ksm_csum; - conn->ksnc_msg.ksm_csum = 0; - } - - if (saved_csum) { - /* accumulate checksum */ - for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) { - LASSERT(i < niov); - - fragnob = iov[i].iov_len; - if (fragnob > sum) - fragnob = sum; - - conn->ksnc_rx_csum = crc32_le(conn->ksnc_rx_csum, - iov[i].iov_base, - fragnob); - } - conn->ksnc_msg.ksm_csum = saved_csum; - } - - return rc; + struct ksock_conn *conn = context; + conn->ksnc_rx_csum = crc32_le(conn->ksnc_rx_csum, + v->iov_base, v->iov_len); + return 0; } int -ksocknal_lib_recv_kiov(struct ksock_conn *conn) +ksocknal_lib_recv(struct ksock_conn *conn) { - unsigned int niov = conn->ksnc_rx_nkiov; - struct bio_vec *kiov = conn->ksnc_rx_kiov; - struct msghdr msg = { - .msg_flags = 0 - }; - int nob; - int i; + struct msghdr msg = { .msg_iter = conn->ksnc_rx_to }; + __u32 saved_csum; int rc; - void *base; - int sum; - int fragnob; - for (nob = i = 0; i < niov; i++) - nob += kiov[i].bv_len; - - LASSERT(nob <= conn->ksnc_rx_nob_wanted); - - iov_iter_bvec(&msg.msg_iter, READ | ITER_BVEC, kiov, niov, nob); rc = sock_recvmsg(conn->ksnc_sock, &msg, MSG_DONTWAIT); + if (rc <= 0) + return rc; - if (conn->ksnc_msg.ksm_csum) { - for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) { - LASSERT(i < niov); - - base = kmap(kiov[i].bv_page) + kiov[i].bv_offset; - fragnob = kiov[i].bv_len; - if (fragnob > sum) - fragnob = sum; + saved_csum = conn->ksnc_msg.ksm_csum; + if (!saved_csum) + return rc; - conn->ksnc_rx_csum = crc32_le(conn->ksnc_rx_csum, - base, fragnob); + /* header is included only in V2 - V3 checksums only the bulk data */ + if (!(conn->ksnc_rx_to.type & ITER_BVEC) && + conn->ksnc_proto != &ksocknal_protocol_v2x) + return rc; + + /* accumulate checksum */ + conn->ksnc_msg.ksm_csum = 0; + iov_iter_for_each_range(&conn->ksnc_rx_to, rc, lustre_csum, conn); + conn->ksnc_msg.ksm_csum = saved_csum; - kunmap(kiov[i].bv_page); - } - } return rc; } diff --git a/drivers/staging/lustre/lnet/lnet/lib-move.c b/drivers/staging/lustre/lnet/lnet/lib-move.c index 27848cd69564..68d16ffec980 100644 --- a/drivers/staging/lustre/lnet/lnet/lib-move.c +++ b/drivers/staging/lustre/lnet/lnet/lib-move.c @@ -890,7 +890,7 @@ lnet_return_rx_credits_locked(struct lnet_msg *msg) */ LASSERT(msg->msg_kiov); - rb = list_entry(msg->msg_kiov, struct lnet_rtrbuf, rb_kiov[0]); + rb = container_of(msg->msg_kiov, struct lnet_rtrbuf, rb_kiov[0]); rbp = rb->rb_pool; msg->msg_kiov = NULL; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm_bo.c index 6e2dce7a5a2d..79bd540d7882 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm_bo.c @@ -1020,10 +1020,8 @@ static int alloc_user_pages(struct hmm_buffer_object *bo, } else { /*Handle frame buffer allocated in user space*/ mutex_unlock(&bo->mutex); - down_read(¤t->mm->mmap_sem); - page_nr = get_user_pages((unsigned long)userptr, - (int)(bo->pgnr), 1, pages, NULL); - up_read(¤t->mm->mmap_sem); + page_nr = get_user_pages_fast((unsigned long)userptr, + (int)(bo->pgnr), 1, pages); mutex_lock(&bo->mutex); bo->mem_type = HMM_BO_MEM_TYPE_USER; } diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c index d946838450d4..2a205c6173dc 100644 --- a/drivers/staging/pi433/pi433_if.c +++ b/drivers/staging/pi433/pi433_if.c @@ -762,32 +762,15 @@ abort: static long pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int err = 0; int retval = 0; struct pi433_instance *instance; struct pi433_device *device; - u32 tmp; + void __user *argp = (void __user *)arg; /* Check type and command number */ if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC) return -ENOTTY; - /* Check access direction once here; don't repeat below. - * IOC_DIR is from the user perspective, while access_ok is - * from the kernel perspective; so they look reversed. - */ - if (_IOC_DIR(cmd) & _IOC_READ) - err = !access_ok(VERIFY_WRITE, - (void __user *)arg, - _IOC_SIZE(cmd)); - - if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) - err = !access_ok(VERIFY_READ, - (void __user *)arg, - _IOC_SIZE(cmd)); - if (err) - return -EFAULT; - /* TODO? guard against device removal before, or while, * we issue this ioctl. --> device_get() */ @@ -799,78 +782,33 @@ pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case PI433_IOC_RD_TX_CFG: - tmp = _IOC_SIZE(cmd); - if ((tmp == 0) || ((tmp % sizeof(struct pi433_tx_cfg)) != 0)) { - retval = -EINVAL; - break; - } - - if (__copy_to_user((void __user *)arg, - &instance->tx_cfg, - tmp)) - { - retval = -EFAULT; - break; - } - + if (copy_to_user(argp, &instance->tx_cfg, + sizeof(struct pi433_tx_cfg))) + return -EFAULT; break; case PI433_IOC_WR_TX_CFG: - tmp = _IOC_SIZE(cmd); - if ((tmp == 0) || ((tmp % sizeof(struct pi433_tx_cfg)) != 0)) { - retval = -EINVAL; - break; - } - - if (__copy_from_user(&instance->tx_cfg, - (void __user *)arg, - tmp)) - { - retval = -EFAULT; - break; - } - + if (copy_from_user(&instance->tx_cfg, argp, + sizeof(struct pi433_tx_cfg))) + return -EFAULT; break; - case PI433_IOC_RD_RX_CFG: - tmp = _IOC_SIZE(cmd); - if ( (tmp == 0) || ((tmp % sizeof(struct pi433_rx_cfg)) != 0) ) { - retval = -EINVAL; - break; - } - - if (__copy_to_user((void __user *)arg, - &device->rx_cfg, - tmp)) - { - retval = -EFAULT; - break; - } - + if (copy_to_user(argp, &device->rx_cfg, + sizeof(struct pi433_rx_cfg))) + return -EFAULT; break; case PI433_IOC_WR_RX_CFG: - tmp = _IOC_SIZE(cmd); mutex_lock(&device->rx_lock); /* during pendig read request, change of config not allowed */ if (device->rx_active) { - retval = -EAGAIN; - mutex_unlock(&device->rx_lock); - break; - } - - if ( (tmp == 0) || ((tmp % sizeof(struct pi433_rx_cfg)) != 0) ) { - retval = -EINVAL; mutex_unlock(&device->rx_lock); - break; + return -EAGAIN; } - if (__copy_from_user(&device->rx_cfg, - (void __user *)arg, - tmp)) - { - retval = -EFAULT; + if (copy_from_user(&device->rx_cfg, argp, + sizeof(struct pi433_rx_cfg))) { mutex_unlock(&device->rx_lock); - break; + return -EFAULT; } mutex_unlock(&device->rx_lock); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index eea2d78b0ec6..315b49c1de3b 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -90,8 +90,7 @@ static irqreturn_t vchiq_doorbell_irq(int irq, void *dev_id); static struct vchiq_pagelist_info * -create_pagelist(char __user *buf, size_t count, unsigned short type, - struct task_struct *task); +create_pagelist(char __user *buf, size_t count, unsigned short type); static void free_pagelist(struct vchiq_pagelist_info *pagelistinfo, @@ -255,8 +254,7 @@ vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, pagelistinfo = create_pagelist((char __user *)offset, size, (dir == VCHIQ_BULK_RECEIVE) ? PAGELIST_READ - : PAGELIST_WRITE, - current); + : PAGELIST_WRITE); if (!pagelistinfo) return VCHIQ_ERROR; @@ -395,8 +393,7 @@ cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo) */ static struct vchiq_pagelist_info * -create_pagelist(char __user *buf, size_t count, unsigned short type, - struct task_struct *task) +create_pagelist(char __user *buf, size_t count, unsigned short type) { PAGELIST_T *pagelist; struct vchiq_pagelist_info *pagelistinfo; @@ -476,14 +473,11 @@ create_pagelist(char __user *buf, size_t count, unsigned short type, } /* do not try and release vmalloc pages */ } else { - down_read(&task->mm->mmap_sem); - actual_pages = get_user_pages( - (unsigned long)buf & PAGE_MASK, + actual_pages = get_user_pages_fast( + (unsigned long)buf & PAGE_MASK, num_pages, - (type == PAGELIST_READ) ? FOLL_WRITE : 0, - pages, - NULL /*vmas */); - up_read(&task->mm->mmap_sem); + type == PAGELIST_READ, + pages); if (actual_pages != num_pages) { vchiq_log_info(vchiq_arm_log_level, diff --git a/drivers/target/iscsi/cxgbit/cxgbit.h b/drivers/target/iscsi/cxgbit/cxgbit.h index 90388698c222..417b9e66b0cd 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit.h +++ b/drivers/target/iscsi/cxgbit/cxgbit.h @@ -165,6 +165,7 @@ enum cxgbit_csk_flags { CSK_LOGIN_PDU_DONE, CSK_LOGIN_DONE, CSK_DDP_ENABLE, + CSK_ABORT_RPL_WAIT, }; struct cxgbit_sock_common { @@ -321,6 +322,7 @@ int cxgbit_setup_np(struct iscsi_np *, struct sockaddr_storage *); int cxgbit_setup_conn_digest(struct cxgbit_sock *); int cxgbit_accept_np(struct iscsi_np *, struct iscsi_conn *); void cxgbit_free_np(struct iscsi_np *); +void cxgbit_abort_conn(struct cxgbit_sock *csk); void cxgbit_free_conn(struct iscsi_conn *); extern cxgbit_cplhandler_func cxgbit_cplhandlers[NUM_CPL_CMDS]; int cxgbit_get_login_rx(struct iscsi_conn *, struct iscsi_login *); diff --git a/drivers/target/iscsi/cxgbit/cxgbit_cm.c b/drivers/target/iscsi/cxgbit/cxgbit_cm.c index d4fa41be80f9..92eb57e2adaf 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_cm.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_cm.c @@ -665,6 +665,46 @@ static int cxgbit_send_abort_req(struct cxgbit_sock *csk) return cxgbit_l2t_send(csk->com.cdev, skb, csk->l2t); } +static void +__cxgbit_abort_conn(struct cxgbit_sock *csk, struct sk_buff *skb) +{ + __kfree_skb(skb); + + if (csk->com.state != CSK_STATE_ESTABLISHED) + goto no_abort; + + set_bit(CSK_ABORT_RPL_WAIT, &csk->com.flags); + csk->com.state = CSK_STATE_ABORTING; + + cxgbit_send_abort_req(csk); + + return; + +no_abort: + cxgbit_wake_up(&csk->com.wr_wait, __func__, CPL_ERR_NONE); + cxgbit_put_csk(csk); +} + +void cxgbit_abort_conn(struct cxgbit_sock *csk) +{ + struct sk_buff *skb = alloc_skb(0, GFP_KERNEL | __GFP_NOFAIL); + + cxgbit_get_csk(csk); + cxgbit_init_wr_wait(&csk->com.wr_wait); + + spin_lock_bh(&csk->lock); + if (csk->lock_owner) { + cxgbit_skcb_rx_backlog_fn(skb) = __cxgbit_abort_conn; + __skb_queue_tail(&csk->backlogq, skb); + } else { + __cxgbit_abort_conn(csk, skb); + } + spin_unlock_bh(&csk->lock); + + cxgbit_wait_for_reply(csk->com.cdev, &csk->com.wr_wait, + csk->tid, 600, __func__); +} + void cxgbit_free_conn(struct iscsi_conn *conn) { struct cxgbit_sock *csk = conn->context; @@ -1709,12 +1749,17 @@ rel_skb: static void cxgbit_abort_rpl_rss(struct cxgbit_sock *csk, struct sk_buff *skb) { + struct cpl_abort_rpl_rss *rpl = cplhdr(skb); + pr_debug("%s: csk %p; tid %u; state %d\n", __func__, csk, csk->tid, csk->com.state); switch (csk->com.state) { case CSK_STATE_ABORTING: csk->com.state = CSK_STATE_DEAD; + if (test_bit(CSK_ABORT_RPL_WAIT, &csk->com.flags)) + cxgbit_wake_up(&csk->com.wr_wait, __func__, + rpl->status); cxgbit_put_csk(csk); break; default: diff --git a/drivers/target/iscsi/cxgbit/cxgbit_ddp.c b/drivers/target/iscsi/cxgbit/cxgbit_ddp.c index 5fdb57cac968..768cce0ccb80 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_ddp.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_ddp.c @@ -275,6 +275,14 @@ void cxgbit_release_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd) struct cxgbit_device *cdev = csk->com.cdev; struct cxgbi_ppm *ppm = cdev2ppm(cdev); + /* Abort the TCP conn if DDP is not complete to + * avoid any possibility of DDP after freeing + * the cmd. + */ + if (unlikely(cmd->write_data_done != + cmd->se_cmd.data_length)) + cxgbit_abort_conn(csk); + cxgbi_ppm_ppod_release(ppm, ttinfo->idx); dma_unmap_sg(&ppm->pdev->dev, ttinfo->sgl, diff --git a/drivers/target/iscsi/cxgbit/cxgbit_main.c b/drivers/target/iscsi/cxgbit/cxgbit_main.c index 4fd775ace541..f3f8856bfb68 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_main.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_main.c @@ -446,6 +446,7 @@ cxgbit_uld_lro_rx_handler(void *hndl, const __be64 *rsp, case CPL_RX_ISCSI_DDP: case CPL_FW4_ACK: lro_flush = false; + /* fall through */ case CPL_ABORT_RPL_RSS: case CPL_PASS_ESTABLISH: case CPL_PEER_CLOSE: diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 9e67c7678c86..9eb10d34682c 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -502,7 +502,7 @@ void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd) EXPORT_SYMBOL(iscsit_aborted_task); static void iscsit_do_crypto_hash_buf(struct ahash_request *, const void *, - u32, u32, u8 *, u8 *); + u32, u32, const void *, void *); static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *); static int @@ -523,7 +523,7 @@ iscsit_xmit_nondatain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, iscsit_do_crypto_hash_buf(conn->conn_tx_hash, hdr, ISCSI_HDR_LEN, 0, NULL, - (u8 *)header_digest); + header_digest); iov[0].iov_len += ISCSI_CRC_LEN; tx_size += ISCSI_CRC_LEN; @@ -550,9 +550,8 @@ iscsit_xmit_nondatain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (conn->conn_ops->DataDigest) { iscsit_do_crypto_hash_buf(conn->conn_tx_hash, data_buf, data_buf_len, - padding, - (u8 *)&cmd->pad_bytes, - (u8 *)&cmd->data_crc); + padding, &cmd->pad_bytes, + &cmd->data_crc); iov[niov].iov_base = &cmd->data_crc; iov[niov++].iov_len = ISCSI_CRC_LEN; @@ -597,7 +596,7 @@ iscsit_xmit_datain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, iscsit_do_crypto_hash_buf(conn->conn_tx_hash, cmd->pdu, ISCSI_HDR_LEN, 0, NULL, - (u8 *)header_digest); + header_digest); iov[0].iov_len += ISCSI_CRC_LEN; tx_size += ISCSI_CRC_LEN; @@ -836,6 +835,7 @@ static int iscsit_add_reject_from_cmd( unsigned char *buf) { struct iscsi_conn *conn; + const bool do_put = cmd->se_cmd.se_tfo != NULL; if (!cmd->conn) { pr_err("cmd->conn is NULL for ITT: 0x%08x\n", @@ -866,7 +866,7 @@ static int iscsit_add_reject_from_cmd( * Perform the kref_put now if se_cmd has already been setup by * scsit_setup_scsi_cmd() */ - if (cmd->se_cmd.se_tfo != NULL) { + if (do_put) { pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n"); target_put_sess_cmd(&cmd->se_cmd); } @@ -1410,13 +1410,9 @@ static u32 iscsit_do_crypto_hash_sg( return data_crc; } -static void iscsit_do_crypto_hash_buf( - struct ahash_request *hash, - const void *buf, - u32 payload_length, - u32 padding, - u8 *pad_bytes, - u8 *data_crc) +static void iscsit_do_crypto_hash_buf(struct ahash_request *hash, + const void *buf, u32 payload_length, u32 padding, + const void *pad_bytes, void *data_crc) { struct scatterlist sg[2]; @@ -1462,9 +1458,9 @@ __iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf, iscsit_mod_dataout_timer(cmd); if ((be32_to_cpu(hdr->offset) + payload_length) > cmd->se_cmd.data_length) { - pr_err("DataOut Offset: %u, Length %u greater than" - " iSCSI Command EDTL %u, protocol error.\n", - hdr->offset, payload_length, cmd->se_cmd.data_length); + pr_err("DataOut Offset: %u, Length %u greater than iSCSI Command EDTL %u, protocol error.\n", + be32_to_cpu(hdr->offset), payload_length, + cmd->se_cmd.data_length); return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID, buf); } @@ -1878,10 +1874,9 @@ static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } if (conn->conn_ops->DataDigest) { - iscsit_do_crypto_hash_buf(conn->conn_rx_hash, - ping_data, payload_length, - padding, cmd->pad_bytes, - (u8 *)&data_crc); + iscsit_do_crypto_hash_buf(conn->conn_rx_hash, ping_data, + payload_length, padding, + cmd->pad_bytes, &data_crc); if (checksum != data_crc) { pr_err("Ping data CRC32C DataDigest" @@ -1962,7 +1957,6 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct iscsi_tmr_req *tmr_req; struct iscsi_tm *hdr; int out_of_order_cmdsn = 0, ret; - bool sess_ref = false; u8 function, tcm_function = TMR_UNKNOWN; hdr = (struct iscsi_tm *) buf; @@ -1995,22 +1989,23 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, cmd->data_direction = DMA_NONE; cmd->tmr_req = kzalloc(sizeof(*cmd->tmr_req), GFP_KERNEL); - if (!cmd->tmr_req) + if (!cmd->tmr_req) { return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + } + + transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops, + conn->sess->se_sess, 0, DMA_NONE, + TCM_SIMPLE_TAG, cmd->sense_buffer + 2); + + target_get_sess_cmd(&cmd->se_cmd, true); /* * TASK_REASSIGN for ERL=2 / connection stays inside of * LIO-Target $FABRIC_MOD */ if (function != ISCSI_TM_FUNC_TASK_REASSIGN) { - transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops, - conn->sess->se_sess, 0, DMA_NONE, - TCM_SIMPLE_TAG, cmd->sense_buffer + 2); - - target_get_sess_cmd(&cmd->se_cmd, true); - sess_ref = true; tcm_function = iscsit_convert_tmf(function); if (tcm_function == TMR_UNKNOWN) { pr_err("Unknown iSCSI TMR Function:" @@ -2101,12 +2096,14 @@ attach: if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) { int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn); - if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) + if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) { out_of_order_cmdsn = 1; - else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) + } else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { + target_put_sess_cmd(&cmd->se_cmd); return 0; - else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) + } else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) { return -1; + } } iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn)); @@ -2126,12 +2123,8 @@ attach: * For connection recovery, this is also the default action for * TMR TASK_REASSIGN. */ - if (sess_ref) { - pr_debug("Handle TMR, using sess_ref=true check\n"); - target_put_sess_cmd(&cmd->se_cmd); - } - iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); + target_put_sess_cmd(&cmd->se_cmd); return 0; } EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd); @@ -2287,10 +2280,9 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, goto reject; if (conn->conn_ops->DataDigest) { - iscsit_do_crypto_hash_buf(conn->conn_rx_hash, - text_in, payload_length, - padding, (u8 *)&pad_bytes, - (u8 *)&data_crc); + iscsit_do_crypto_hash_buf(conn->conn_rx_hash, text_in, + payload_length, padding, + &pad_bytes, &data_crc); if (checksum != data_crc) { pr_err("Text data CRC32C DataDigest" @@ -3978,9 +3970,9 @@ static void iscsit_get_rx_pdu(struct iscsi_conn *conn) return; } - iscsit_do_crypto_hash_buf(conn->conn_rx_hash, - buffer, ISCSI_HDR_LEN, - 0, NULL, (u8 *)&checksum); + iscsit_do_crypto_hash_buf(conn->conn_rx_hash, buffer, + ISCSI_HDR_LEN, 0, NULL, + &checksum); if (digest != checksum) { pr_err("HeaderDigest CRC32C failed," diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 0dd4c45f7575..0ebc4818e132 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1123,7 +1123,7 @@ static struct se_portal_group *lio_target_tiqn_addtpg( ret = core_tpg_register(wwn, &tpg->tpg_se_tpg, SCSI_PROTOCOL_ISCSI); if (ret < 0) - return NULL; + goto free_out; ret = iscsit_tpg_add_portal_group(tiqn, tpg); if (ret != 0) @@ -1135,6 +1135,7 @@ static struct se_portal_group *lio_target_tiqn_addtpg( return &tpg->tpg_se_tpg; out: core_tpg_deregister(&tpg->tpg_se_tpg); +free_out: kfree(tpg); return NULL; } diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 76184094a0cf..5efa42b939a1 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -34,7 +34,7 @@ #include "iscsi_target_erl2.h" #include "iscsi_target.h" -#define OFFLOAD_BUF_SIZE 32768 +#define OFFLOAD_BUF_SIZE 32768U /* * Used to dump excess datain payload for certain error recovery @@ -56,7 +56,7 @@ int iscsit_dump_data_payload( if (conn->sess->sess_ops->RDMAExtensions) return 0; - length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len; + length = min(buf_len, OFFLOAD_BUF_SIZE); buf = kzalloc(length, GFP_ATOMIC); if (!buf) { @@ -67,8 +67,7 @@ int iscsit_dump_data_payload( memset(&iov, 0, sizeof(struct kvec)); while (offset < buf_len) { - size = ((offset + length) > buf_len) ? - (buf_len - offset) : length; + size = min(buf_len - offset, length); iov.iov_len = size; iov.iov_base = buf; diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index caab1045742d..29a37b242d30 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1380,10 +1380,8 @@ int iscsi_decode_text_input( char *key, *value; struct iscsi_param *param; - if (iscsi_extract_key_value(start, &key, &value) < 0) { - kfree(tmpbuf); - return -1; - } + if (iscsi_extract_key_value(start, &key, &value) < 0) + goto free_buffer; pr_debug("Got key: %s=%s\n", key, value); @@ -1396,38 +1394,37 @@ int iscsi_decode_text_input( param = iscsi_check_key(key, phase, sender, param_list); if (!param) { - if (iscsi_add_notunderstood_response(key, - value, param_list) < 0) { - kfree(tmpbuf); - return -1; - } + if (iscsi_add_notunderstood_response(key, value, + param_list) < 0) + goto free_buffer; + start += strlen(key) + strlen(value) + 2; continue; } - if (iscsi_check_value(param, value) < 0) { - kfree(tmpbuf); - return -1; - } + if (iscsi_check_value(param, value) < 0) + goto free_buffer; start += strlen(key) + strlen(value) + 2; if (IS_PSTATE_PROPOSER(param)) { - if (iscsi_check_proposer_state(param, value) < 0) { - kfree(tmpbuf); - return -1; - } + if (iscsi_check_proposer_state(param, value) < 0) + goto free_buffer; + SET_PSTATE_RESPONSE_GOT(param); } else { - if (iscsi_check_acceptor_state(param, value, conn) < 0) { - kfree(tmpbuf); - return -1; - } + if (iscsi_check_acceptor_state(param, value, conn) < 0) + goto free_buffer; + SET_PSTATE_ACCEPTOR(param); } } kfree(tmpbuf); return 0; + +free_buffer: + kfree(tmpbuf); + return -1; } int iscsi_encode_text_output( diff --git a/drivers/target/iscsi/iscsi_target_seq_pdu_list.c b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c index e446a09c886b..f65e5e584212 100644 --- a/drivers/target/iscsi/iscsi_target_seq_pdu_list.c +++ b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c @@ -25,8 +25,6 @@ #include "iscsi_target_tpg.h" #include "iscsi_target_seq_pdu_list.h" -#define OFFLOAD_BUF_SIZE 32768 - #ifdef DEBUG static void iscsit_dump_seq_list(struct iscsi_cmd *cmd) { diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 594d07a1e995..4b34f71547c6 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -90,10 +90,10 @@ int iscsit_load_discovery_tpg(void) */ param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list); if (!param) - goto out; + goto free_pl_out; if (iscsi_update_param_value(param, "CHAP,None") < 0) - goto out; + goto free_pl_out; tpg->tpg_attrib.authentication = 0; @@ -105,6 +105,8 @@ int iscsit_load_discovery_tpg(void) pr_debug("CORE[0] - Allocated Discovery TPG\n"); return 0; +free_pl_out: + iscsi_release_param_list(tpg->param_list); out: if (tpg->sid == 1) core_tpg_deregister(&tpg->tpg_se_tpg); @@ -119,6 +121,7 @@ void iscsit_release_discovery_tpg(void) if (!tpg) return; + iscsi_release_param_list(tpg->param_list); core_tpg_deregister(&tpg->tpg_se_tpg); kfree(tpg); diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 54f20f184dd6..4435bf374d2d 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -695,6 +695,8 @@ void iscsit_release_cmd(struct iscsi_cmd *cmd) struct iscsi_session *sess; struct se_cmd *se_cmd = &cmd->se_cmd; + WARN_ON(!list_empty(&cmd->i_conn_node)); + if (cmd->conn) sess = cmd->conn->sess; else @@ -717,6 +719,8 @@ void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool check_queues) { struct iscsi_conn *conn = cmd->conn; + WARN_ON(!list_empty(&cmd->i_conn_node)); + if (cmd->data_direction == DMA_TO_DEVICE) { iscsit_stop_dataout_timer(cmd); iscsit_free_r2ts_from_list(cmd); diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 928127642574..e46ca968009c 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -918,7 +918,7 @@ static int core_alua_update_tpg_primary_metadata( { unsigned char *md_buf; struct t10_wwn *wwn = &tg_pt_gp->tg_pt_gp_dev->t10_wwn; - char path[ALUA_METADATA_PATH_LEN]; + char *path; int len, rc; md_buf = kzalloc(ALUA_MD_BUF_LEN, GFP_KERNEL); @@ -927,8 +927,6 @@ static int core_alua_update_tpg_primary_metadata( return -ENOMEM; } - memset(path, 0, ALUA_METADATA_PATH_LEN); - len = snprintf(md_buf, ALUA_MD_BUF_LEN, "tg_pt_gp_id=%hu\n" "alua_access_state=0x%02x\n" @@ -937,11 +935,14 @@ static int core_alua_update_tpg_primary_metadata( tg_pt_gp->tg_pt_gp_alua_access_state, tg_pt_gp->tg_pt_gp_alua_access_status); - snprintf(path, ALUA_METADATA_PATH_LEN, - "%s/alua/tpgs_%s/%s", db_root, &wwn->unit_serial[0], - config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item)); - - rc = core_alua_write_tpg_metadata(path, md_buf, len); + rc = -ENOMEM; + path = kasprintf(GFP_KERNEL, "%s/alua/tpgs_%s/%s", db_root, + &wwn->unit_serial[0], + config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item)); + if (path) { + rc = core_alua_write_tpg_metadata(path, md_buf, len); + kfree(path); + } kfree(md_buf); return rc; } @@ -1209,7 +1210,7 @@ static int core_alua_update_tpg_secondary_metadata(struct se_lun *lun) { struct se_portal_group *se_tpg = lun->lun_tpg; unsigned char *md_buf; - char path[ALUA_METADATA_PATH_LEN], wwn[ALUA_SECONDARY_METADATA_WWN_LEN]; + char *path; int len, rc; mutex_lock(&lun->lun_tg_pt_md_mutex); @@ -1221,28 +1222,32 @@ static int core_alua_update_tpg_secondary_metadata(struct se_lun *lun) goto out_unlock; } - memset(path, 0, ALUA_METADATA_PATH_LEN); - memset(wwn, 0, ALUA_SECONDARY_METADATA_WWN_LEN); - - len = snprintf(wwn, ALUA_SECONDARY_METADATA_WWN_LEN, "%s", - se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg)); - - if (se_tpg->se_tpg_tfo->tpg_get_tag != NULL) - snprintf(wwn+len, ALUA_SECONDARY_METADATA_WWN_LEN-len, "+%hu", - se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg)); - len = snprintf(md_buf, ALUA_MD_BUF_LEN, "alua_tg_pt_offline=%d\n" "alua_tg_pt_status=0x%02x\n", atomic_read(&lun->lun_tg_pt_secondary_offline), lun->lun_tg_pt_secondary_stat); - snprintf(path, ALUA_METADATA_PATH_LEN, "%s/alua/%s/%s/lun_%llu", - db_root, se_tpg->se_tpg_tfo->get_fabric_name(), wwn, - lun->unpacked_lun); + if (se_tpg->se_tpg_tfo->tpg_get_tag != NULL) { + path = kasprintf(GFP_KERNEL, "%s/alua/%s/%s+%hu/lun_%llu", + db_root, se_tpg->se_tpg_tfo->get_fabric_name(), + se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg), + se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg), + lun->unpacked_lun); + } else { + path = kasprintf(GFP_KERNEL, "%s/alua/%s/%s/lun_%llu", + db_root, se_tpg->se_tpg_tfo->get_fabric_name(), + se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg), + lun->unpacked_lun); + } + if (!path) { + rc = -ENOMEM; + goto out_free; + } rc = core_alua_write_tpg_metadata(path, md_buf, len); + kfree(path); +out_free: kfree(md_buf); - out_unlock: mutex_unlock(&lun->lun_tg_pt_md_mutex); return rc; diff --git a/drivers/target/target_core_alua.h b/drivers/target/target_core_alua.h index 1902cb5c3b52..fc9637cce825 100644 --- a/drivers/target/target_core_alua.h +++ b/drivers/target/target_core_alua.h @@ -72,15 +72,6 @@ */ #define ALUA_DEFAULT_IMPLICIT_TRANS_SECS 0 #define ALUA_MAX_IMPLICIT_TRANS_SECS 255 -/* - * Used by core_alua_update_tpg_primary_metadata() and - * core_alua_update_tpg_secondary_metadata() - */ -#define ALUA_METADATA_PATH_LEN 512 -/* - * Used by core_alua_update_tpg_secondary_metadata() - */ -#define ALUA_SECONDARY_METADATA_WWN_LEN 256 /* Used by core_alua_update_tpg_(primary,secondary)_metadata */ #define ALUA_MD_BUF_LEN 1024 diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index bd87cc26c6e5..72b1cd1bf9d9 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1611,12 +1611,12 @@ static match_table_t tokens = { {Opt_res_type, "res_type=%d"}, {Opt_res_scope, "res_scope=%d"}, {Opt_res_all_tg_pt, "res_all_tg_pt=%d"}, - {Opt_mapped_lun, "mapped_lun=%lld"}, + {Opt_mapped_lun, "mapped_lun=%u"}, {Opt_target_fabric, "target_fabric=%s"}, {Opt_target_node, "target_node=%s"}, {Opt_tpgt, "tpgt=%d"}, {Opt_port_rtpi, "port_rtpi=%d"}, - {Opt_target_lun, "target_lun=%lld"}, + {Opt_target_lun, "target_lun=%u"}, {Opt_err, NULL} }; @@ -1693,7 +1693,7 @@ static ssize_t target_pr_res_aptpl_metadata_store(struct config_item *item, } break; case Opt_sa_res_key: - ret = kstrtoull(args->from, 0, &tmp_ll); + ret = match_u64(args, &tmp_ll); if (ret < 0) { pr_err("kstrtoull() failed for sa_res_key=\n"); goto out; @@ -1727,10 +1727,10 @@ static ssize_t target_pr_res_aptpl_metadata_store(struct config_item *item, all_tg_pt = (int)arg; break; case Opt_mapped_lun: - ret = match_int(args, &arg); + ret = match_u64(args, &tmp_ll); if (ret) goto out; - mapped_lun = (u64)arg; + mapped_lun = (u64)tmp_ll; break; /* * PR APTPL Metadata for Target Port @@ -1768,10 +1768,10 @@ static ssize_t target_pr_res_aptpl_metadata_store(struct config_item *item, goto out; break; case Opt_target_lun: - ret = match_int(args, &arg); + ret = match_u64(args, &tmp_ll); if (ret) goto out; - target_lun = (u64)arg; + target_lun = (u64)tmp_ll; break; default: break; diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index e9e917cc6441..e1416b007aa4 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -623,8 +623,6 @@ static struct configfs_attribute *target_fabric_port_attrs[] = { NULL, }; -extern struct configfs_item_operations target_core_dev_item_ops; - static int target_fabric_port_link( struct config_item *lun_ci, struct config_item *se_dev_ci) diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index c629817a8854..9b2c0c773022 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -482,6 +482,10 @@ fd_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) struct inode *inode = file->f_mapping->host; int ret; + if (!nolb) { + return 0; + } + if (cmd->se_dev->dev_attrib.pi_prot_type) { ret = fd_do_prot_unmap(cmd, lba, nolb); if (ret) diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 18e3eb16e756..9384d19a7326 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -89,6 +89,7 @@ int target_for_each_device(int (*fn)(struct se_device *dev, void *data), void *data); /* target_core_configfs.c */ +extern struct configfs_item_operations target_core_dev_item_ops; void target_setup_backend_cits(struct target_backend *); /* target_core_fabric_configfs.c */ diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index dd2cd8048582..b024613f9217 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -58,8 +58,10 @@ void core_pr_dump_initiator_port( char *buf, u32 size) { - if (!pr_reg->isid_present_at_reg) + if (!pr_reg->isid_present_at_reg) { buf[0] = '\0'; + return; + } snprintf(buf, size, ",i,0x%s", pr_reg->pr_reg_isid); } @@ -351,6 +353,7 @@ static int core_scsi3_pr_seq_non_holder(struct se_cmd *cmd, u32 pr_reg_type, break; case PR_TYPE_WRITE_EXCLUSIVE_REGONLY: we = 1; + /* fall through */ case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY: /* * Some commands are only allowed for registered I_T Nexuses. @@ -359,6 +362,7 @@ static int core_scsi3_pr_seq_non_holder(struct se_cmd *cmd, u32 pr_reg_type, break; case PR_TYPE_WRITE_EXCLUSIVE_ALLREG: we = 1; + /* fall through */ case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG: /* * Each registered I_T Nexus is a reservation holder. @@ -1521,7 +1525,7 @@ core_scsi3_decode_spec_i_port( tidh_new = kzalloc(sizeof(struct pr_transport_id_holder), GFP_KERNEL); if (!tidh_new) { pr_err("Unable to allocate tidh_new\n"); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + return TCM_INSUFFICIENT_REGISTRATION_RESOURCES; } INIT_LIST_HEAD(&tidh_new->dest_list); tidh_new->dest_tpg = tpg; @@ -1533,7 +1537,7 @@ core_scsi3_decode_spec_i_port( sa_res_key, all_tg_pt, aptpl); if (!local_pr_reg) { kfree(tidh_new); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + return TCM_INSUFFICIENT_REGISTRATION_RESOURCES; } tidh_new->dest_pr_reg = local_pr_reg; /* @@ -1553,7 +1557,7 @@ core_scsi3_decode_spec_i_port( buf = transport_kmap_data_sg(cmd); if (!buf) { - ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + ret = TCM_INSUFFICIENT_REGISTRATION_RESOURCES; goto out; } @@ -1767,7 +1771,7 @@ core_scsi3_decode_spec_i_port( core_scsi3_nodeacl_undepend_item(dest_node_acl); core_scsi3_tpg_undepend_item(dest_tpg); kfree(tidh_new); - ret = TCM_INVALID_PARAMETER_LIST; + ret = TCM_INSUFFICIENT_REGISTRATION_RESOURCES; goto out_unmap; } tidh_new->dest_pr_reg = dest_pr_reg; @@ -1971,24 +1975,21 @@ static int __core_scsi3_write_aptpl_to_file( struct t10_wwn *wwn = &dev->t10_wwn; struct file *file; int flags = O_RDWR | O_CREAT | O_TRUNC; - char path[512]; + char *path; u32 pr_aptpl_buf_len; int ret; loff_t pos = 0; - memset(path, 0, 512); - - if (strlen(&wwn->unit_serial[0]) >= 512) { - pr_err("WWN value for struct se_device does not fit" - " into path buffer\n"); - return -EMSGSIZE; - } + path = kasprintf(GFP_KERNEL, "%s/pr/aptpl_%s", db_root, + &wwn->unit_serial[0]); + if (!path) + return -ENOMEM; - snprintf(path, 512, "%s/pr/aptpl_%s", db_root, &wwn->unit_serial[0]); file = filp_open(path, flags, 0600); if (IS_ERR(file)) { pr_err("filp_open(%s) for APTPL metadata" " failed\n", path); + kfree(path); return PTR_ERR(file); } @@ -1999,6 +2000,7 @@ static int __core_scsi3_write_aptpl_to_file( if (ret < 0) pr_debug("Error writing APTPL metadata file: %s\n", path); fput(file); + kfree(path); return (ret < 0) ? -EIO : 0; } @@ -2103,7 +2105,7 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key, register_type, 0)) { pr_err("Unable to allocate" " struct t10_pr_registration\n"); - return TCM_INVALID_PARAMETER_LIST; + return TCM_INSUFFICIENT_REGISTRATION_RESOURCES; } } else { /* @@ -3215,7 +3217,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, */ buf = transport_kmap_data_sg(cmd); if (!buf) { - ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + ret = TCM_INSUFFICIENT_REGISTRATION_RESOURCES; goto out_put_pr_reg; } @@ -3267,7 +3269,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, buf = transport_kmap_data_sg(cmd); if (!buf) { - ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + ret = TCM_INSUFFICIENT_REGISTRATION_RESOURCES; goto out_put_pr_reg; } proto_ident = (buf[24] & 0x0f); @@ -3466,7 +3468,7 @@ after_iport_check: if (core_scsi3_alloc_registration(cmd->se_dev, dest_node_acl, dest_lun, dest_se_deve, dest_se_deve->mapped_lun, iport_ptr, sa_res_key, 0, aptpl, 2, 1)) { - ret = TCM_INVALID_PARAMETER_LIST; + ret = TCM_INSUFFICIENT_REGISTRATION_RESOURCES; goto out; } spin_lock(&dev->dev_reservation_lock); @@ -3528,8 +3530,6 @@ after_iport_check: core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl); - transport_kunmap_data_sg(cmd); - core_scsi3_put_pr_reg(dest_pr_reg); return 0; out: @@ -4011,6 +4011,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) * Set the ADDITIONAL DESCRIPTOR LENGTH */ put_unaligned_be32(desc_len, &buf[off]); + off += 4; /* * Size of full desctipor header minus TransportID * containing $FABRIC_MOD specific) initiator device/port diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index e22847bd79b9..9c7bc1ca341a 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -133,6 +133,15 @@ static bool __target_check_io_state(struct se_cmd *se_cmd, spin_unlock(&se_cmd->t_state_lock); return false; } + if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) { + if (se_cmd->scsi_status) { + pr_debug("Attempted to abort io tag: %llu early failure" + " status: 0x%02x\n", se_cmd->tag, + se_cmd->scsi_status); + spin_unlock(&se_cmd->t_state_lock); + return false; + } + } if (sess->sess_tearing_down || se_cmd->cmd_wait_set) { pr_debug("Attempted to abort io tag: %llu already shutdown," " skipping\n", se_cmd->tag); @@ -217,7 +226,8 @@ static void core_tmr_drain_tmr_list( * LUN_RESET tmr.. */ spin_lock_irqsave(&dev->se_tmr_lock, flags); - list_del_init(&tmr->tmr_list); + if (tmr) + list_del_init(&tmr->tmr_list); list_for_each_entry_safe(tmr_p, tmr_pp, &dev->dev_tmr_list, tmr_list) { cmd = tmr_p->task_cmd; if (!cmd) { diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 836d552b0385..58caacd54a3b 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -67,7 +67,6 @@ static void transport_complete_task_attr(struct se_cmd *cmd); static int translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason); static void transport_handle_queue_full(struct se_cmd *cmd, struct se_device *dev, int err, bool write_pending); -static int transport_put_cmd(struct se_cmd *cmd); static void target_complete_ok_work(struct work_struct *work); int init_se_kmem_caches(void) @@ -668,7 +667,7 @@ int transport_cmd_finish_abort(struct se_cmd *cmd, int remove) if (transport_cmd_check_stop_to_fabric(cmd)) return 1; if (remove && ack_kref) - ret = transport_put_cmd(cmd); + ret = target_put_sess_cmd(cmd); return ret; } @@ -1730,9 +1729,6 @@ void transport_generic_request_failure(struct se_cmd *cmd, { int ret = 0, post_ret = 0; - if (transport_check_aborted_status(cmd, 1)) - return; - pr_debug("-----[ Storage Engine Exception; sense_reason %d\n", sense_reason); target_show_cmd("-----[ ", cmd); @@ -1741,6 +1737,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, * For SAM Task Attribute emulation for failed struct se_cmd */ transport_complete_task_attr(cmd); + /* * Handle special case for COMPARE_AND_WRITE failure, where the * callback is expected to drop the per device ->caw_sem. @@ -1749,6 +1746,9 @@ void transport_generic_request_failure(struct se_cmd *cmd, cmd->transport_complete_callback) cmd->transport_complete_callback(cmd, false, &post_ret); + if (transport_check_aborted_status(cmd, 1)) + return; + switch (sense_reason) { case TCM_NON_EXISTENT_LUN: case TCM_UNSUPPORTED_SCSI_OPCODE: @@ -1772,8 +1772,8 @@ void transport_generic_request_failure(struct se_cmd *cmd, case TCM_UNSUPPORTED_SEGMENT_DESC_TYPE_CODE: break; case TCM_OUT_OF_RESOURCES: - sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - break; + cmd->scsi_status = SAM_STAT_TASK_SET_FULL; + goto queue_status; case TCM_RESERVATION_CONFLICT: /* * No SENSE Data payload for this case, set SCSI Status @@ -1795,11 +1795,8 @@ void transport_generic_request_failure(struct se_cmd *cmd, cmd->orig_fe_lun, 0x2C, ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS); } - trace_target_cmd_complete(cmd); - ret = cmd->se_tfo->queue_status(cmd); - if (ret) - goto queue_full; - goto check_stop; + + goto queue_status; default: pr_err("Unknown transport error for CDB 0x%02x: %d\n", cmd->t_task_cdb[0], sense_reason); @@ -1816,6 +1813,11 @@ check_stop: transport_cmd_check_stop_to_fabric(cmd); return; +queue_status: + trace_target_cmd_complete(cmd); + ret = cmd->se_tfo->queue_status(cmd); + if (!ret) + goto check_stop; queue_full: transport_handle_queue_full(cmd, cmd->se_dev, ret, false); } @@ -1973,6 +1975,7 @@ void target_execute_cmd(struct se_cmd *cmd) } cmd->t_state = TRANSPORT_PROCESSING; + cmd->transport_state &= ~CMD_T_PRE_EXECUTE; cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); @@ -2010,6 +2013,8 @@ static void target_restart_delayed_cmds(struct se_device *dev) list_del(&cmd->se_delayed_node); spin_unlock(&dev->delayed_cmd_lock); + cmd->transport_state |= CMD_T_SENT; + __target_execute_cmd(cmd, true); if (cmd->sam_task_attr == TCM_ORDERED_TAG) @@ -2045,6 +2050,8 @@ static void transport_complete_task_attr(struct se_cmd *cmd) pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED\n", dev->dev_cur_ordered_id); } + cmd->se_cmd_flags &= ~SCF_TASK_ATTR_SET; + restart: target_restart_delayed_cmds(dev); } @@ -2090,7 +2097,7 @@ static void transport_complete_qf(struct se_cmd *cmd) ret = cmd->se_tfo->queue_data_in(cmd); break; } - /* Fall through for DMA_TO_DEVICE */ + /* fall through */ case DMA_NONE: queue_status: trace_target_cmd_complete(cmd); @@ -2268,7 +2275,7 @@ queue_rsp: goto queue_full; break; } - /* Fall through for DMA_TO_DEVICE */ + /* fall through */ case DMA_NONE: queue_status: trace_target_cmd_complete(cmd); @@ -2352,22 +2359,6 @@ static inline void transport_free_pages(struct se_cmd *cmd) cmd->t_bidi_data_nents = 0; } -/** - * transport_put_cmd - release a reference to a command - * @cmd: command to release - * - * This routine releases our reference to the command and frees it if possible. - */ -static int transport_put_cmd(struct se_cmd *cmd) -{ - BUG_ON(!cmd->se_tfo); - /* - * If this cmd has been setup with target_get_sess_cmd(), drop - * the kref and call ->release_cmd() in kref callback. - */ - return target_put_sess_cmd(cmd); -} - void *transport_kmap_data_sg(struct se_cmd *cmd) { struct scatterlist *sg = cmd->t_data_sg; @@ -2570,7 +2561,20 @@ EXPORT_SYMBOL(transport_generic_new_cmd); static void transport_write_pending_qf(struct se_cmd *cmd) { + unsigned long flags; int ret; + bool stop; + + spin_lock_irqsave(&cmd->t_state_lock, flags); + stop = (cmd->transport_state & (CMD_T_STOP | CMD_T_ABORTED)); + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + + if (stop) { + pr_debug("%s:%d CMD_T_STOP|CMD_T_ABORTED for ITT: 0x%08llx\n", + __func__, __LINE__, cmd->tag); + complete_all(&cmd->t_transport_stop_comp); + return; + } ret = cmd->se_tfo->write_pending(cmd); if (ret) { @@ -2603,7 +2607,7 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) target_wait_free_cmd(cmd, &aborted, &tas); if (!aborted || tas) - ret = transport_put_cmd(cmd); + ret = target_put_sess_cmd(cmd); } else { if (wait_for_tasks) target_wait_free_cmd(cmd, &aborted, &tas); @@ -2619,7 +2623,7 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) transport_lun_remove_cmd(cmd); if (!aborted || tas) - ret = transport_put_cmd(cmd); + ret = target_put_sess_cmd(cmd); } /* * If the task has been internally aborted due to TMR ABORT_TASK @@ -2664,6 +2668,7 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref) ret = -ESHUTDOWN; goto out; } + se_cmd->transport_state |= CMD_T_PRE_EXECUTE; list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); out: spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); @@ -3145,6 +3150,21 @@ static const struct sense_info sense_info_table[] = { .key = NOT_READY, .asc = 0x08, /* LOGICAL UNIT COMMUNICATION FAILURE */ }, + [TCM_INSUFFICIENT_REGISTRATION_RESOURCES] = { + /* + * From spc4r22 section5.7.7,5.7.8 + * If a PERSISTENT RESERVE OUT command with a REGISTER service action + * or a REGISTER AND IGNORE EXISTING KEY service action or + * REGISTER AND MOVE service actionis attempted, + * but there are insufficient device server resources to complete the + * operation, then the command shall be terminated with CHECK CONDITION + * status, with the sense key set to ILLEGAL REQUEST,and the additonal + * sense code set to INSUFFICIENT REGISTRATION RESOURCES. + */ + .key = ILLEGAL_REQUEST, + .asc = 0x55, + .ascq = 0x04, /* INSUFFICIENT REGISTRATION RESOURCES */ + }, }; static int translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 9469695f5871..cc2468a299d3 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -150,6 +150,8 @@ struct tcmu_dev { wait_queue_head_t nl_cmd_wq; char dev_config[TCMU_CONFIG_LEN]; + + int nl_reply_supported; }; #define TCMU_DEV(_se_dev) container_of(_se_dev, struct tcmu_dev, se_dev) @@ -430,7 +432,6 @@ static struct tcmu_cmd *tcmu_alloc_cmd(struct se_cmd *se_cmd) struct se_device *se_dev = se_cmd->se_dev; struct tcmu_dev *udev = TCMU_DEV(se_dev); struct tcmu_cmd *tcmu_cmd; - int cmd_id; tcmu_cmd = kmem_cache_zalloc(tcmu_cmd_cache, GFP_KERNEL); if (!tcmu_cmd) @@ -438,9 +439,6 @@ static struct tcmu_cmd *tcmu_alloc_cmd(struct se_cmd *se_cmd) tcmu_cmd->se_cmd = se_cmd; tcmu_cmd->tcmu_dev = udev; - if (udev->cmd_time_out) - tcmu_cmd->deadline = jiffies + - msecs_to_jiffies(udev->cmd_time_out); tcmu_cmd_reset_dbi_cur(tcmu_cmd); tcmu_cmd->dbi_cnt = tcmu_cmd_get_block_cnt(tcmu_cmd); @@ -451,19 +449,6 @@ static struct tcmu_cmd *tcmu_alloc_cmd(struct se_cmd *se_cmd) return NULL; } - idr_preload(GFP_KERNEL); - spin_lock_irq(&udev->commands_lock); - cmd_id = idr_alloc(&udev->commands, tcmu_cmd, 0, - USHRT_MAX, GFP_NOWAIT); - spin_unlock_irq(&udev->commands_lock); - idr_preload_end(); - - if (cmd_id < 0) { - tcmu_free_cmd(tcmu_cmd); - return NULL; - } - tcmu_cmd->cmd_id = cmd_id; - return tcmu_cmd; } @@ -746,6 +731,30 @@ static inline size_t tcmu_cmd_get_cmd_size(struct tcmu_cmd *tcmu_cmd, return command_size; } +static int tcmu_setup_cmd_timer(struct tcmu_cmd *tcmu_cmd) +{ + struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; + unsigned long tmo = udev->cmd_time_out; + int cmd_id; + + if (tcmu_cmd->cmd_id) + return 0; + + cmd_id = idr_alloc(&udev->commands, tcmu_cmd, 1, USHRT_MAX, GFP_NOWAIT); + if (cmd_id < 0) { + pr_err("tcmu: Could not allocate cmd id.\n"); + return cmd_id; + } + tcmu_cmd->cmd_id = cmd_id; + + if (!tmo) + return 0; + + tcmu_cmd->deadline = round_jiffies_up(jiffies + msecs_to_jiffies(tmo)); + mod_timer(&udev->timeout, tcmu_cmd->deadline); + return 0; +} + static sense_reason_t tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) { @@ -839,7 +848,6 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) entry = (void *) mb + CMDR_OFF + cmd_head; memset(entry, 0, command_size); tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_CMD); - entry->hdr.cmd_id = tcmu_cmd->cmd_id; /* Handle allocating space from the data area */ tcmu_cmd_reset_dbi_cur(tcmu_cmd); @@ -877,6 +885,14 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) } entry->req.iov_bidi_cnt = iov_cnt; + ret = tcmu_setup_cmd_timer(tcmu_cmd); + if (ret) { + tcmu_cmd_free_data(tcmu_cmd, tcmu_cmd->dbi_cnt); + mutex_unlock(&udev->cmdr_lock); + return TCM_OUT_OF_RESOURCES; + } + entry->hdr.cmd_id = tcmu_cmd->cmd_id; + /* * Recalaulate the command's base size and size according * to the actual needs @@ -910,8 +926,6 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) static sense_reason_t tcmu_queue_cmd(struct se_cmd *se_cmd) { - struct se_device *se_dev = se_cmd->se_dev; - struct tcmu_dev *udev = TCMU_DEV(se_dev); struct tcmu_cmd *tcmu_cmd; sense_reason_t ret; @@ -922,9 +936,6 @@ tcmu_queue_cmd(struct se_cmd *se_cmd) ret = tcmu_queue_cmd_ring(tcmu_cmd); if (ret != TCM_NO_SENSE) { pr_err("TCMU: Could not queue command\n"); - spin_lock_irq(&udev->commands_lock); - idr_remove(&udev->commands, tcmu_cmd->cmd_id); - spin_unlock_irq(&udev->commands_lock); tcmu_free_cmd(tcmu_cmd); } @@ -1112,6 +1123,8 @@ static struct se_device *tcmu_alloc_device(struct se_hba *hba, const char *name) init_waitqueue_head(&udev->nl_cmd_wq); spin_lock_init(&udev->nl_cmd_lock); + INIT_RADIX_TREE(&udev->data_blocks, GFP_KERNEL); + return &udev->se_dev; } @@ -1280,10 +1293,54 @@ static void tcmu_dev_call_rcu(struct rcu_head *p) kfree(udev); } +static int tcmu_check_and_free_pending_cmd(struct tcmu_cmd *cmd) +{ + if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { + kmem_cache_free(tcmu_cmd_cache, cmd); + return 0; + } + return -EINVAL; +} + +static void tcmu_blocks_release(struct tcmu_dev *udev) +{ + int i; + struct page *page; + + /* Try to release all block pages */ + mutex_lock(&udev->cmdr_lock); + for (i = 0; i <= udev->dbi_max; i++) { + page = radix_tree_delete(&udev->data_blocks, i); + if (page) { + __free_page(page); + atomic_dec(&global_db_count); + } + } + mutex_unlock(&udev->cmdr_lock); +} + static void tcmu_dev_kref_release(struct kref *kref) { struct tcmu_dev *udev = container_of(kref, struct tcmu_dev, kref); struct se_device *dev = &udev->se_dev; + struct tcmu_cmd *cmd; + bool all_expired = true; + int i; + + vfree(udev->mb_addr); + udev->mb_addr = NULL; + + /* Upper layer should drain all requests before calling this */ + spin_lock_irq(&udev->commands_lock); + idr_for_each_entry(&udev->commands, cmd, i) { + if (tcmu_check_and_free_pending_cmd(cmd) != 0) + all_expired = false; + } + idr_destroy(&udev->commands); + spin_unlock_irq(&udev->commands_lock); + WARN_ON(!all_expired); + + tcmu_blocks_release(udev); call_rcu(&dev->rcu_head, tcmu_dev_call_rcu); } @@ -1306,6 +1363,10 @@ static void tcmu_init_genl_cmd_reply(struct tcmu_dev *udev, int cmd) if (!tcmu_kern_cmd_reply_supported) return; + + if (udev->nl_reply_supported <= 0) + return; + relock: spin_lock(&udev->nl_cmd_lock); @@ -1332,6 +1393,9 @@ static int tcmu_wait_genl_cmd_reply(struct tcmu_dev *udev) if (!tcmu_kern_cmd_reply_supported) return 0; + if (udev->nl_reply_supported <= 0) + return 0; + pr_debug("sleeping for nl reply\n"); wait_for_completion(&nl_cmd->complete); @@ -1476,8 +1540,6 @@ static int tcmu_configure_device(struct se_device *dev) WARN_ON(udev->data_size % PAGE_SIZE); WARN_ON(udev->data_size % DATA_BLOCK_SIZE); - INIT_RADIX_TREE(&udev->data_blocks, GFP_KERNEL); - info->version = __stringify(TCMU_MAILBOX_VERSION); info->mem[0].name = "tcm-user command & data buffer"; @@ -1506,6 +1568,12 @@ static int tcmu_configure_device(struct se_device *dev) dev->dev_attrib.emulate_write_cache = 0; dev->dev_attrib.hw_queue_depth = 128; + /* If user didn't explicitly disable netlink reply support, use + * module scope setting. + */ + if (udev->nl_reply_supported >= 0) + udev->nl_reply_supported = tcmu_kern_cmd_reply_supported; + /* * Get a ref incase userspace does a close on the uio device before * LIO has initiated tcmu_free_device. @@ -1527,6 +1595,7 @@ err_netlink: uio_unregister_device(&udev->uio_info); err_register: vfree(udev->mb_addr); + udev->mb_addr = NULL; err_vzalloc: kfree(info->name); info->name = NULL; @@ -1534,37 +1603,11 @@ err_vzalloc: return ret; } -static int tcmu_check_and_free_pending_cmd(struct tcmu_cmd *cmd) -{ - if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { - kmem_cache_free(tcmu_cmd_cache, cmd); - return 0; - } - return -EINVAL; -} - static bool tcmu_dev_configured(struct tcmu_dev *udev) { return udev->uio_info.uio_dev ? true : false; } -static void tcmu_blocks_release(struct tcmu_dev *udev) -{ - int i; - struct page *page; - - /* Try to release all block pages */ - mutex_lock(&udev->cmdr_lock); - for (i = 0; i <= udev->dbi_max; i++) { - page = radix_tree_delete(&udev->data_blocks, i); - if (page) { - __free_page(page); - atomic_dec(&global_db_count); - } - } - mutex_unlock(&udev->cmdr_lock); -} - static void tcmu_free_device(struct se_device *dev) { struct tcmu_dev *udev = TCMU_DEV(dev); @@ -1576,9 +1619,6 @@ static void tcmu_free_device(struct se_device *dev) static void tcmu_destroy_device(struct se_device *dev) { struct tcmu_dev *udev = TCMU_DEV(dev); - struct tcmu_cmd *cmd; - bool all_expired = true; - int i; del_timer_sync(&udev->timeout); @@ -1586,20 +1626,6 @@ static void tcmu_destroy_device(struct se_device *dev) list_del(&udev->node); mutex_unlock(&root_udev_mutex); - vfree(udev->mb_addr); - - /* Upper layer should drain all requests before calling this */ - spin_lock_irq(&udev->commands_lock); - idr_for_each_entry(&udev->commands, cmd, i) { - if (tcmu_check_and_free_pending_cmd(cmd) != 0) - all_expired = false; - } - idr_destroy(&udev->commands); - spin_unlock_irq(&udev->commands_lock); - WARN_ON(!all_expired); - - tcmu_blocks_release(udev); - tcmu_netlink_event(udev, TCMU_CMD_REMOVED_DEVICE, 0, NULL); uio_unregister_device(&udev->uio_info); @@ -1610,7 +1636,7 @@ static void tcmu_destroy_device(struct se_device *dev) enum { Opt_dev_config, Opt_dev_size, Opt_hw_block_size, Opt_hw_max_sectors, - Opt_err, + Opt_nl_reply_supported, Opt_err, }; static match_table_t tokens = { @@ -1618,6 +1644,7 @@ static match_table_t tokens = { {Opt_dev_size, "dev_size=%u"}, {Opt_hw_block_size, "hw_block_size=%u"}, {Opt_hw_max_sectors, "hw_max_sectors=%u"}, + {Opt_nl_reply_supported, "nl_reply_supported=%d"}, {Opt_err, NULL} }; @@ -1692,6 +1719,17 @@ static ssize_t tcmu_set_configfs_dev_params(struct se_device *dev, ret = tcmu_set_dev_attrib(&args[0], &(dev->dev_attrib.hw_max_sectors)); break; + case Opt_nl_reply_supported: + arg_p = match_strdup(&args[0]); + if (!arg_p) { + ret = -ENOMEM; + break; + } + ret = kstrtoint(arg_p, 0, &udev->nl_reply_supported); + kfree(arg_p); + if (ret < 0) + pr_err("kstrtoint() failed for nl_reply_supported=\n"); + break; default: break; } @@ -1734,8 +1772,7 @@ static ssize_t tcmu_cmd_time_out_show(struct config_item *item, char *page) { struct se_dev_attrib *da = container_of(to_config_group(item), struct se_dev_attrib, da_group); - struct tcmu_dev *udev = container_of(da->da_dev, - struct tcmu_dev, se_dev); + struct tcmu_dev *udev = TCMU_DEV(da->da_dev); return snprintf(page, PAGE_SIZE, "%lu\n", udev->cmd_time_out / MSEC_PER_SEC); } @@ -1842,6 +1879,34 @@ static ssize_t tcmu_dev_size_store(struct config_item *item, const char *page, } CONFIGFS_ATTR(tcmu_, dev_size); +static ssize_t tcmu_nl_reply_supported_show(struct config_item *item, + char *page) +{ + struct se_dev_attrib *da = container_of(to_config_group(item), + struct se_dev_attrib, da_group); + struct tcmu_dev *udev = TCMU_DEV(da->da_dev); + + return snprintf(page, PAGE_SIZE, "%d\n", udev->nl_reply_supported); +} + +static ssize_t tcmu_nl_reply_supported_store(struct config_item *item, + const char *page, size_t count) +{ + struct se_dev_attrib *da = container_of(to_config_group(item), + struct se_dev_attrib, da_group); + struct tcmu_dev *udev = TCMU_DEV(da->da_dev); + s8 val; + int ret; + + ret = kstrtos8(page, 0, &val); + if (ret < 0) + return ret; + + udev->nl_reply_supported = val; + return count; +} +CONFIGFS_ATTR(tcmu_, nl_reply_supported); + static ssize_t tcmu_emulate_write_cache_show(struct config_item *item, char *page) { @@ -1884,6 +1949,7 @@ static struct configfs_attribute *tcmu_attrib_attrs[] = { &tcmu_attr_dev_config, &tcmu_attr_dev_size, &tcmu_attr_emulate_write_cache, + &tcmu_attr_nl_reply_supported, NULL, }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 07002df4f83a..315ae2926e20 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -206,6 +206,7 @@ config HISI_THERMAL config IMX_THERMAL tristate "Temperature sensor driver for Freescale i.MX SoCs" depends on (ARCH_MXC && CPU_THERMAL) || COMPILE_TEST + depends on NVMEM || !NVMEM depends on MFD_SYSCON depends on OF help @@ -408,7 +409,7 @@ config MTK_THERMAL controller present in Mediatek SoCs menu "Broadcom thermal drivers" -depends on ARCH_BCM || COMPILE_TEST +depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST source "drivers/thermal/broadcom/Kconfig" endmenu diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index ae75328945f7..706d74798cbe 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -58,7 +58,7 @@ struct armada_thermal_data { /* Test for a valid sensor value (optional) */ bool (*is_valid)(struct armada_thermal_priv *); - /* Formula coeficients: temp = (b + m * reg) / div */ + /* Formula coeficients: temp = (b - m * reg) / div */ unsigned long coef_b; unsigned long coef_m; unsigned long coef_div; diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig index 42c098e86f84..c106a15bf7f9 100644 --- a/drivers/thermal/broadcom/Kconfig +++ b/drivers/thermal/broadcom/Kconfig @@ -6,6 +6,13 @@ config BCM2835_THERMAL help Support for thermal sensors on Broadcom bcm2835 SoCs. +config BRCMSTB_THERMAL + tristate "Broadcom STB AVS TMON thermal driver" + depends on ARCH_BRCMSTB || COMPILE_TEST + help + Enable this driver if you have a Broadcom STB SoC and would like + thermal framework support. + config BCM_NS_THERMAL tristate "Northstar thermal driver" depends on ARCH_BCM_IPROC || COMPILE_TEST diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile index c6f62e4fd0ee..fae10ecafaef 100644 --- a/drivers/thermal/broadcom/Makefile +++ b/drivers/thermal/broadcom/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o +obj-$(CONFIG_BRCMSTB_THERMAL) += brcmstb_thermal.o obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c new file mode 100644 index 000000000000..1919f91fa756 --- /dev/null +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -0,0 +1,387 @@ +/* + * Broadcom STB AVS TMON thermal sensor driver + * + * Copyright (c) 2015-2017 Broadcom + * + * 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. + * + */ + +#define DRV_NAME "brcmstb_thermal" + +#define pr_fmt(fmt) DRV_NAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AVS_TMON_STATUS 0x00 + #define AVS_TMON_STATUS_valid_msk BIT(11) + #define AVS_TMON_STATUS_data_msk GENMASK(10, 1) + #define AVS_TMON_STATUS_data_shift 1 + +#define AVS_TMON_EN_OVERTEMP_RESET 0x04 + #define AVS_TMON_EN_OVERTEMP_RESET_msk BIT(0) + +#define AVS_TMON_RESET_THRESH 0x08 + #define AVS_TMON_RESET_THRESH_msk GENMASK(10, 1) + #define AVS_TMON_RESET_THRESH_shift 1 + +#define AVS_TMON_INT_IDLE_TIME 0x10 + +#define AVS_TMON_EN_TEMP_INT_SRCS 0x14 + #define AVS_TMON_EN_TEMP_INT_SRCS_high BIT(1) + #define AVS_TMON_EN_TEMP_INT_SRCS_low BIT(0) + +#define AVS_TMON_INT_THRESH 0x18 + #define AVS_TMON_INT_THRESH_high_msk GENMASK(26, 17) + #define AVS_TMON_INT_THRESH_high_shift 17 + #define AVS_TMON_INT_THRESH_low_msk GENMASK(10, 1) + #define AVS_TMON_INT_THRESH_low_shift 1 + +#define AVS_TMON_TEMP_INT_CODE 0x1c +#define AVS_TMON_TP_TEST_ENABLE 0x20 + +/* Default coefficients */ +#define AVS_TMON_TEMP_SLOPE -487 +#define AVS_TMON_TEMP_OFFSET 410040 + +/* HW related temperature constants */ +#define AVS_TMON_TEMP_MAX 0x3ff +#define AVS_TMON_TEMP_MIN -88161 +#define AVS_TMON_TEMP_MASK AVS_TMON_TEMP_MAX + +enum avs_tmon_trip_type { + TMON_TRIP_TYPE_LOW = 0, + TMON_TRIP_TYPE_HIGH, + TMON_TRIP_TYPE_RESET, + TMON_TRIP_TYPE_MAX, +}; + +struct avs_tmon_trip { + /* HW bit to enable the trip */ + u32 enable_offs; + u32 enable_mask; + + /* HW field to read the trip temperature */ + u32 reg_offs; + u32 reg_msk; + int reg_shift; +}; + +static struct avs_tmon_trip avs_tmon_trips[] = { + /* Trips when temperature is below threshold */ + [TMON_TRIP_TYPE_LOW] = { + .enable_offs = AVS_TMON_EN_TEMP_INT_SRCS, + .enable_mask = AVS_TMON_EN_TEMP_INT_SRCS_low, + .reg_offs = AVS_TMON_INT_THRESH, + .reg_msk = AVS_TMON_INT_THRESH_low_msk, + .reg_shift = AVS_TMON_INT_THRESH_low_shift, + }, + /* Trips when temperature is above threshold */ + [TMON_TRIP_TYPE_HIGH] = { + .enable_offs = AVS_TMON_EN_TEMP_INT_SRCS, + .enable_mask = AVS_TMON_EN_TEMP_INT_SRCS_high, + .reg_offs = AVS_TMON_INT_THRESH, + .reg_msk = AVS_TMON_INT_THRESH_high_msk, + .reg_shift = AVS_TMON_INT_THRESH_high_shift, + }, + /* Automatically resets chip when above threshold */ + [TMON_TRIP_TYPE_RESET] = { + .enable_offs = AVS_TMON_EN_OVERTEMP_RESET, + .enable_mask = AVS_TMON_EN_OVERTEMP_RESET_msk, + .reg_offs = AVS_TMON_RESET_THRESH, + .reg_msk = AVS_TMON_RESET_THRESH_msk, + .reg_shift = AVS_TMON_RESET_THRESH_shift, + }, +}; + +struct brcmstb_thermal_priv { + void __iomem *tmon_base; + struct device *dev; + struct thermal_zone_device *thermal; +}; + +static void avs_tmon_get_coeffs(struct thermal_zone_device *tz, int *slope, + int *offset) +{ + *slope = thermal_zone_get_slope(tz); + *offset = thermal_zone_get_offset(tz); +} + +/* Convert a HW code to a temperature reading (millidegree celsius) */ +static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, + u32 code) +{ + const int val = code & AVS_TMON_TEMP_MASK; + int slope, offset; + + avs_tmon_get_coeffs(tz, &slope, &offset); + + return slope * val + offset; +} + +/* + * Convert a temperature value (millidegree celsius) to a HW code + * + * @temp: temperature to convert + * @low: if true, round toward the low side + */ +static inline u32 avs_tmon_temp_to_code(struct thermal_zone_device *tz, + int temp, bool low) +{ + int slope, offset; + + if (temp < AVS_TMON_TEMP_MIN) + return AVS_TMON_TEMP_MAX; /* Maximum code value */ + + avs_tmon_get_coeffs(tz, &slope, &offset); + + if (temp >= offset) + return 0; /* Minimum code value */ + + if (low) + return (u32)(DIV_ROUND_UP(offset - temp, abs(slope))); + else + return (u32)((offset - temp) / abs(slope)); +} + +static int brcmstb_get_temp(void *data, int *temp) +{ + struct brcmstb_thermal_priv *priv = data; + u32 val; + long t; + + val = __raw_readl(priv->tmon_base + AVS_TMON_STATUS); + + if (!(val & AVS_TMON_STATUS_valid_msk)) { + dev_err(priv->dev, "reading not valid\n"); + return -EIO; + } + + val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift; + + t = avs_tmon_code_to_temp(priv->thermal, val); + if (t < 0) + *temp = 0; + else + *temp = t; + + return 0; +} + +static void avs_tmon_trip_enable(struct brcmstb_thermal_priv *priv, + enum avs_tmon_trip_type type, int en) +{ + struct avs_tmon_trip *trip = &avs_tmon_trips[type]; + u32 val = __raw_readl(priv->tmon_base + trip->enable_offs); + + dev_dbg(priv->dev, "%sable trip, type %d\n", en ? "en" : "dis", type); + + if (en) + val |= trip->enable_mask; + else + val &= ~trip->enable_mask; + + __raw_writel(val, priv->tmon_base + trip->enable_offs); +} + +static int avs_tmon_get_trip_temp(struct brcmstb_thermal_priv *priv, + enum avs_tmon_trip_type type) +{ + struct avs_tmon_trip *trip = &avs_tmon_trips[type]; + u32 val = __raw_readl(priv->tmon_base + trip->reg_offs); + + val &= trip->reg_msk; + val >>= trip->reg_shift; + + return avs_tmon_code_to_temp(priv->thermal, val); +} + +static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv, + enum avs_tmon_trip_type type, + int temp) +{ + struct avs_tmon_trip *trip = &avs_tmon_trips[type]; + u32 val, orig; + + dev_dbg(priv->dev, "set temp %d to %d\n", type, temp); + + /* round toward low temp for the low interrupt */ + val = avs_tmon_temp_to_code(priv->thermal, temp, + type == TMON_TRIP_TYPE_LOW); + + val <<= trip->reg_shift; + val &= trip->reg_msk; + + orig = __raw_readl(priv->tmon_base + trip->reg_offs); + orig &= ~trip->reg_msk; + orig |= val; + __raw_writel(orig, priv->tmon_base + trip->reg_offs); +} + +static int avs_tmon_get_intr_temp(struct brcmstb_thermal_priv *priv) +{ + u32 val; + + val = __raw_readl(priv->tmon_base + AVS_TMON_TEMP_INT_CODE); + return avs_tmon_code_to_temp(priv->thermal, val); +} + +static irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data) +{ + struct brcmstb_thermal_priv *priv = data; + int low, high, intr; + + low = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_LOW); + high = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_HIGH); + intr = avs_tmon_get_intr_temp(priv); + + dev_dbg(priv->dev, "low/intr/high: %d/%d/%d\n", + low, intr, high); + + /* Disable high-temp until next threshold shift */ + if (intr >= high) + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0); + /* Disable low-temp until next threshold shift */ + if (intr <= low) + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0); + + /* + * Notify using the interrupt temperature, in case the temperature + * changes before it can next be read out + */ + thermal_zone_device_update(priv->thermal, intr); + + return IRQ_HANDLED; +} + +static int brcmstb_set_trips(void *data, int low, int high) +{ + struct brcmstb_thermal_priv *priv = data; + + dev_dbg(priv->dev, "set trips %d <--> %d\n", low, high); + + /* + * Disable low-temp if "low" is too small. As per thermal framework + * API, we use -INT_MAX rather than INT_MIN. + */ + if (low <= -INT_MAX) { + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0); + } else { + avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_LOW, low); + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 1); + } + + /* Disable high-temp if "high" is too big. */ + if (high == INT_MAX) { + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0); + } else { + avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_HIGH, high); + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 1); + } + + return 0; +} + +static struct thermal_zone_of_device_ops of_ops = { + .get_temp = brcmstb_get_temp, + .set_trips = brcmstb_set_trips, +}; + +static const struct of_device_id brcmstb_thermal_id_table[] = { + { .compatible = "brcm,avs-tmon" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table); + +static int brcmstb_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal; + struct brcmstb_thermal_priv *priv; + struct resource *res; + int irq, ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->tmon_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->tmon_base)) + return PTR_ERR(priv->tmon_base); + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + thermal = thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &of_ops); + if (IS_ERR(thermal)) { + ret = PTR_ERR(thermal); + dev_err(&pdev->dev, "could not register sensor: %d\n", ret); + return ret; + } + + priv->thermal = thermal; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get IRQ\n"); + ret = irq; + goto err; + } + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + brcmstb_tmon_irq_thread, IRQF_ONESHOT, + DRV_NAME, priv); + if (ret < 0) { + dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); + goto err; + } + + dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); + + return 0; + +err: + thermal_zone_of_sensor_unregister(&pdev->dev, thermal); + return ret; +} + +static int brcmstb_thermal_exit(struct platform_device *pdev) +{ + struct brcmstb_thermal_priv *priv = platform_get_drvdata(pdev); + struct thermal_zone_device *thermal = priv->thermal; + + if (thermal) + thermal_zone_of_sensor_unregister(&pdev->dev, priv->thermal); + + return 0; +} + +static struct platform_driver brcmstb_thermal_driver = { + .probe = brcmstb_thermal_probe, + .remove = brcmstb_thermal_exit, + .driver = { + .name = DRV_NAME, + .of_match_table = brcmstb_thermal_id_table, + }, +}; +module_platform_driver(brcmstb_thermal_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("Broadcom STB AVS TMON thermal driver"); diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 908a8014cf76..dc63aba092e4 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -696,7 +696,7 @@ __cpufreq_cooling_register(struct device_node *np, bool first; if (IS_ERR_OR_NULL(policy)) { - pr_err("%s: cpufreq policy isn't valid: %p", __func__, policy); + pr_err("%s: cpufreq policy isn't valid: %p\n", __func__, policy); return ERR_PTR(-EINVAL); } diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index bd3572c41585..2d855a96cdd9 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -23,222 +23,450 @@ #include #include #include +#include #include "thermal_core.h" -#define TEMP0_TH (0x4) -#define TEMP0_RST_TH (0x8) -#define TEMP0_CFG (0xC) -#define TEMP0_EN (0x10) -#define TEMP0_INT_EN (0x14) -#define TEMP0_INT_CLR (0x18) -#define TEMP0_RST_MSK (0x1C) -#define TEMP0_VALUE (0x28) - -#define HISI_TEMP_BASE (-60) -#define HISI_TEMP_RESET (100000) - -#define HISI_MAX_SENSORS 4 +#define HI6220_TEMP0_LAG (0x0) +#define HI6220_TEMP0_TH (0x4) +#define HI6220_TEMP0_RST_TH (0x8) +#define HI6220_TEMP0_CFG (0xC) +#define HI6220_TEMP0_CFG_SS_MSK (0xF000) +#define HI6220_TEMP0_CFG_HDAK_MSK (0x30) +#define HI6220_TEMP0_EN (0x10) +#define HI6220_TEMP0_INT_EN (0x14) +#define HI6220_TEMP0_INT_CLR (0x18) +#define HI6220_TEMP0_RST_MSK (0x1C) +#define HI6220_TEMP0_VALUE (0x28) + +#define HI3660_OFFSET(chan) ((chan) * 0x40) +#define HI3660_TEMP(chan) (HI3660_OFFSET(chan) + 0x1C) +#define HI3660_TH(chan) (HI3660_OFFSET(chan) + 0x20) +#define HI3660_LAG(chan) (HI3660_OFFSET(chan) + 0x28) +#define HI3660_INT_EN(chan) (HI3660_OFFSET(chan) + 0x2C) +#define HI3660_INT_CLR(chan) (HI3660_OFFSET(chan) + 0x30) + +#define HI6220_TEMP_BASE (-60000) +#define HI6220_TEMP_RESET (100000) +#define HI6220_TEMP_STEP (785) +#define HI6220_TEMP_LAG (3500) + +#define HI3660_TEMP_BASE (-63780) +#define HI3660_TEMP_STEP (205) +#define HI3660_TEMP_LAG (4000) + +#define HI6220_DEFAULT_SENSOR 2 +#define HI3660_DEFAULT_SENSOR 1 struct hisi_thermal_sensor { - struct hisi_thermal_data *thermal; struct thermal_zone_device *tzd; - - long sensor_temp; uint32_t id; uint32_t thres_temp; }; struct hisi_thermal_data { - struct mutex thermal_lock; /* protects register data */ + int (*get_temp)(struct hisi_thermal_data *data); + int (*enable_sensor)(struct hisi_thermal_data *data); + int (*disable_sensor)(struct hisi_thermal_data *data); + int (*irq_handler)(struct hisi_thermal_data *data); struct platform_device *pdev; struct clk *clk; - struct hisi_thermal_sensor sensors[HISI_MAX_SENSORS]; - - int irq, irq_bind_sensor; - bool irq_enabled; - + struct hisi_thermal_sensor sensor; void __iomem *regs; + int irq; }; -/* in millicelsius */ -static inline int _step_to_temp(int step) +/* + * The temperature computation on the tsensor is as follow: + * Unit: millidegree Celsius + * Step: 200/255 (0.7843) + * Temperature base: -60°C + * + * The register is programmed in temperature steps, every step is 785 + * millidegree and begins at -60 000 m°C + * + * The temperature from the steps: + * + * Temp = TempBase + (steps x 785) + * + * and the steps from the temperature: + * + * steps = (Temp - TempBase) / 785 + * + */ +static inline int hi6220_thermal_step_to_temp(int step) { - /* - * Every step equals (1 * 200) / 255 celsius, and finally - * need convert to millicelsius. - */ - return (HISI_TEMP_BASE * 1000 + (step * 200000 / 255)); + return HI6220_TEMP_BASE + (step * HI6220_TEMP_STEP); } -static inline long _temp_to_step(long temp) +static inline int hi6220_thermal_temp_to_step(int temp) { - return ((temp - HISI_TEMP_BASE * 1000) * 255) / 200000; + return DIV_ROUND_UP(temp - HI6220_TEMP_BASE, HI6220_TEMP_STEP); } -static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data, - struct hisi_thermal_sensor *sensor) +/* + * for Hi3660, + * Step: 189/922 (0.205) + * Temperature base: -63.780°C + * + * The register is programmed in temperature steps, every step is 205 + * millidegree and begins at -63 780 m°C + */ +static inline int hi3660_thermal_step_to_temp(int step) { - long val; + return HI3660_TEMP_BASE + step * HI3660_TEMP_STEP; +} - mutex_lock(&data->thermal_lock); +static inline int hi3660_thermal_temp_to_step(int temp) +{ + return DIV_ROUND_UP(temp - HI3660_TEMP_BASE, HI3660_TEMP_STEP); +} - /* disable interrupt */ - writel(0x0, data->regs + TEMP0_INT_EN); - writel(0x1, data->regs + TEMP0_INT_CLR); +/* + * The lag register contains 5 bits encoding the temperature in steps. + * + * Each time the temperature crosses the threshold boundary, an + * interrupt is raised. It could be when the temperature is going + * above the threshold or below. However, if the temperature is + * fluctuating around this value due to the load, we can receive + * several interrupts which may not desired. + * + * We can setup a temperature representing the delta between the + * threshold and the current temperature when the temperature is + * decreasing. + * + * For instance: the lag register is 5°C, the threshold is 65°C, when + * the temperature reaches 65°C an interrupt is raised and when the + * temperature decrease to 65°C - 5°C another interrupt is raised. + * + * A very short lag can lead to an interrupt storm, a long lag + * increase the latency to react to the temperature changes. In our + * case, that is not really a problem as we are polling the + * temperature. + * + * [0:4] : lag register + * + * The temperature is coded in steps, cf. HI6220_TEMP_STEP. + * + * Min : 0x00 : 0.0 °C + * Max : 0x1F : 24.3 °C + * + * The 'value' parameter is in milliCelsius. + */ +static inline void hi6220_thermal_set_lag(void __iomem *addr, int value) +{ + writel(DIV_ROUND_UP(value, HI6220_TEMP_STEP) & 0x1F, + addr + HI6220_TEMP0_LAG); +} - /* disable module firstly */ - writel(0x0, data->regs + TEMP0_EN); +static inline void hi6220_thermal_alarm_clear(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_INT_CLR); +} - /* select sensor id */ - writel((sensor->id << 12), data->regs + TEMP0_CFG); +static inline void hi6220_thermal_alarm_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_INT_EN); +} - /* enable module */ - writel(0x1, data->regs + TEMP0_EN); +static inline void hi6220_thermal_alarm_set(void __iomem *addr, int temp) +{ + writel(hi6220_thermal_temp_to_step(temp) | 0x0FFFFFF00, + addr + HI6220_TEMP0_TH); +} - usleep_range(3000, 5000); +static inline void hi6220_thermal_reset_set(void __iomem *addr, int temp) +{ + writel(hi6220_thermal_temp_to_step(temp), addr + HI6220_TEMP0_RST_TH); +} - val = readl(data->regs + TEMP0_VALUE); - val = _step_to_temp(val); +static inline void hi6220_thermal_reset_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_RST_MSK); +} - mutex_unlock(&data->thermal_lock); +static inline void hi6220_thermal_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_EN); +} - return val; +static inline int hi6220_thermal_get_temperature(void __iomem *addr) +{ + return hi6220_thermal_step_to_temp(readl(addr + HI6220_TEMP0_VALUE)); } -static void hisi_thermal_enable_bind_irq_sensor - (struct hisi_thermal_data *data) +/* + * [0:6] lag register + * + * The temperature is coded in steps, cf. HI3660_TEMP_STEP. + * + * Min : 0x00 : 0.0 °C + * Max : 0x7F : 26.0 °C + * + */ +static inline void hi3660_thermal_set_lag(void __iomem *addr, + int id, int value) { - struct hisi_thermal_sensor *sensor; + writel(DIV_ROUND_UP(value, HI3660_TEMP_STEP) & 0x7F, + addr + HI3660_LAG(id)); +} - mutex_lock(&data->thermal_lock); +static inline void hi3660_thermal_alarm_clear(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_INT_CLR(id)); +} - sensor = &data->sensors[data->irq_bind_sensor]; +static inline void hi3660_thermal_alarm_enable(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_INT_EN(id)); +} - /* setting the hdak time */ - writel(0x0, data->regs + TEMP0_CFG); +static inline void hi3660_thermal_alarm_set(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_TH(id)); +} + +static inline int hi3660_thermal_get_temperature(void __iomem *addr, int id) +{ + return hi3660_thermal_step_to_temp(readl(addr + HI3660_TEMP(id))); +} + +/* + * Temperature configuration register - Sensor selection + * + * Bits [19:12] + * + * 0x0: local sensor (default) + * 0x1: remote sensor 1 (ACPU cluster 1) + * 0x2: remote sensor 2 (ACPU cluster 0) + * 0x3: remote sensor 3 (G3D) + */ +static inline void hi6220_thermal_sensor_select(void __iomem *addr, int sensor) +{ + writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_SS_MSK) | + (sensor << 12), addr + HI6220_TEMP0_CFG); +} + +/* + * Temperature configuration register - Hdak conversion polling interval + * + * Bits [5:4] + * + * 0x0 : 0.768 ms + * 0x1 : 6.144 ms + * 0x2 : 49.152 ms + * 0x3 : 393.216 ms + */ +static inline void hi6220_thermal_hdak_set(void __iomem *addr, int value) +{ + writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_HDAK_MSK) | + (value << 4), addr + HI6220_TEMP0_CFG); +} + +static int hi6220_thermal_irq_handler(struct hisi_thermal_data *data) +{ + hi6220_thermal_alarm_clear(data->regs, 1); + return 0; +} + +static int hi3660_thermal_irq_handler(struct hisi_thermal_data *data) +{ + hi3660_thermal_alarm_clear(data->regs, data->sensor.id, 1); + return 0; +} + +static int hi6220_thermal_get_temp(struct hisi_thermal_data *data) +{ + return hi6220_thermal_get_temperature(data->regs); +} + +static int hi3660_thermal_get_temp(struct hisi_thermal_data *data) +{ + return hi3660_thermal_get_temperature(data->regs, data->sensor.id); +} + +static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data) +{ + /* disable sensor module */ + hi6220_thermal_enable(data->regs, 0); + hi6220_thermal_alarm_enable(data->regs, 0); + hi6220_thermal_reset_enable(data->regs, 0); + + clk_disable_unprepare(data->clk); + + return 0; +} + +static int hi3660_thermal_disable_sensor(struct hisi_thermal_data *data) +{ + /* disable sensor module */ + hi3660_thermal_alarm_enable(data->regs, data->sensor.id, 0); + return 0; +} + +static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data) +{ + struct hisi_thermal_sensor *sensor = &data->sensor; + int ret; + + /* enable clock for tsensor */ + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; /* disable module firstly */ - writel(0x0, data->regs + TEMP0_RST_MSK); - writel(0x0, data->regs + TEMP0_EN); + hi6220_thermal_reset_enable(data->regs, 0); + hi6220_thermal_enable(data->regs, 0); /* select sensor id */ - writel((sensor->id << 12), data->regs + TEMP0_CFG); + hi6220_thermal_sensor_select(data->regs, sensor->id); + + /* setting the hdak time */ + hi6220_thermal_hdak_set(data->regs, 0); + + /* setting lag value between current temp and the threshold */ + hi6220_thermal_set_lag(data->regs, HI6220_TEMP_LAG); /* enable for interrupt */ - writel(_temp_to_step(sensor->thres_temp) | 0x0FFFFFF00, - data->regs + TEMP0_TH); + hi6220_thermal_alarm_set(data->regs, sensor->thres_temp); - writel(_temp_to_step(HISI_TEMP_RESET), data->regs + TEMP0_RST_TH); + hi6220_thermal_reset_set(data->regs, HI6220_TEMP_RESET); /* enable module */ - writel(0x1, data->regs + TEMP0_RST_MSK); - writel(0x1, data->regs + TEMP0_EN); - - writel(0x0, data->regs + TEMP0_INT_CLR); - writel(0x1, data->regs + TEMP0_INT_EN); + hi6220_thermal_reset_enable(data->regs, 1); + hi6220_thermal_enable(data->regs, 1); - usleep_range(3000, 5000); + hi6220_thermal_alarm_clear(data->regs, 0); + hi6220_thermal_alarm_enable(data->regs, 1); - mutex_unlock(&data->thermal_lock); + return 0; } -static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data) +static int hi3660_thermal_enable_sensor(struct hisi_thermal_data *data) { - mutex_lock(&data->thermal_lock); + unsigned int value; + struct hisi_thermal_sensor *sensor = &data->sensor; - /* disable sensor module */ - writel(0x0, data->regs + TEMP0_INT_EN); - writel(0x0, data->regs + TEMP0_RST_MSK); - writel(0x0, data->regs + TEMP0_EN); + /* disable interrupt */ + hi3660_thermal_alarm_enable(data->regs, sensor->id, 0); - mutex_unlock(&data->thermal_lock); -} + /* setting lag value between current temp and the threshold */ + hi3660_thermal_set_lag(data->regs, sensor->id, HI3660_TEMP_LAG); -static int hisi_thermal_get_temp(void *_sensor, int *temp) -{ - struct hisi_thermal_sensor *sensor = _sensor; - struct hisi_thermal_data *data = sensor->thermal; + /* set interrupt threshold */ + value = hi3660_thermal_temp_to_step(sensor->thres_temp); + hi3660_thermal_alarm_set(data->regs, sensor->id, value); - int sensor_id = -1, i; - long max_temp = 0; + /* enable interrupt */ + hi3660_thermal_alarm_clear(data->regs, sensor->id, 1); + hi3660_thermal_alarm_enable(data->regs, sensor->id, 1); - *temp = hisi_thermal_get_sensor_temp(data, sensor); + return 0; +} - sensor->sensor_temp = *temp; +static int hi6220_thermal_probe(struct hisi_thermal_data *data) +{ + struct platform_device *pdev = data->pdev; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; - for (i = 0; i < HISI_MAX_SENSORS; i++) { - if (!data->sensors[i].tzd) - continue; + data->get_temp = hi6220_thermal_get_temp; + data->enable_sensor = hi6220_thermal_enable_sensor; + data->disable_sensor = hi6220_thermal_disable_sensor; + data->irq_handler = hi6220_thermal_irq_handler; - if (data->sensors[i].sensor_temp >= max_temp) { - max_temp = data->sensors[i].sensor_temp; - sensor_id = i; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(data->regs)) { + dev_err(dev, "failed to get io address\n"); + return PTR_ERR(data->regs); } - /* If no sensor has been enabled, then skip to enable irq */ - if (sensor_id == -1) - return 0; - - mutex_lock(&data->thermal_lock); - data->irq_bind_sensor = sensor_id; - mutex_unlock(&data->thermal_lock); - - dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n", - sensor->id, data->irq_enabled, *temp, sensor->thres_temp); - /* - * Bind irq to sensor for two cases: - * Reenable alarm IRQ if temperature below threshold; - * if irq has been enabled, always set it; - */ - if (data->irq_enabled) { - hisi_thermal_enable_bind_irq_sensor(data); - return 0; + data->clk = devm_clk_get(dev, "thermal_clk"); + if (IS_ERR(data->clk)) { + ret = PTR_ERR(data->clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get thermal clk: %d\n", ret); + return ret; } - if (max_temp < sensor->thres_temp) { - data->irq_enabled = true; - hisi_thermal_enable_bind_irq_sensor(data); - enable_irq(data->irq); - } + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; + + data->sensor.id = HI6220_DEFAULT_SENSOR; return 0; } -static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = { - .get_temp = hisi_thermal_get_temp, -}; +static int hi3660_thermal_probe(struct hisi_thermal_data *data) +{ + struct platform_device *pdev = data->pdev; + struct device *dev = &pdev->dev; + struct resource *res; + + data->get_temp = hi3660_thermal_get_temp; + data->enable_sensor = hi3660_thermal_enable_sensor; + data->disable_sensor = hi3660_thermal_disable_sensor; + data->irq_handler = hi3660_thermal_irq_handler; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(data->regs)) { + dev_err(dev, "failed to get io address\n"); + return PTR_ERR(data->regs); + } -static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev) + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; + + data->sensor.id = HI3660_DEFAULT_SENSOR; + + return 0; +} + +static int hisi_thermal_get_temp(void *__data, int *temp) { - struct hisi_thermal_data *data = dev; + struct hisi_thermal_data *data = __data; + struct hisi_thermal_sensor *sensor = &data->sensor; - disable_irq_nosync(irq); - data->irq_enabled = false; + *temp = data->get_temp(data); - return IRQ_WAKE_THREAD; + dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n", + sensor->id, *temp, sensor->thres_temp); + + return 0; } +static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = { + .get_temp = hisi_thermal_get_temp, +}; + static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) { struct hisi_thermal_data *data = dev; - struct hisi_thermal_sensor *sensor; - int i; + struct hisi_thermal_sensor *sensor = &data->sensor; + int temp = 0; - mutex_lock(&data->thermal_lock); - sensor = &data->sensors[data->irq_bind_sensor]; + data->irq_handler(data); - dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n", - sensor->thres_temp / 1000); - mutex_unlock(&data->thermal_lock); + hisi_thermal_get_temp(data, &temp); - for (i = 0; i < HISI_MAX_SENSORS; i++) { - if (!data->sensors[i].tzd) - continue; + if (temp >= sensor->thres_temp) { + dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n", + temp, sensor->thres_temp); - thermal_zone_device_update(data->sensors[i].tzd, + thermal_zone_device_update(data->sensor.tzd, THERMAL_EVENT_UNSPECIFIED); + + } else { + dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n", + temp, sensor->thres_temp); } return IRQ_HANDLED; @@ -246,17 +474,14 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) static int hisi_thermal_register_sensor(struct platform_device *pdev, struct hisi_thermal_data *data, - struct hisi_thermal_sensor *sensor, - int index) + struct hisi_thermal_sensor *sensor) { int ret, i; const struct thermal_trip *trip; - sensor->id = index; - sensor->thermal = data; - sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, - sensor->id, sensor, &hisi_of_thermal_ops); + sensor->id, data, + &hisi_of_thermal_ops); if (IS_ERR(sensor->tzd)) { ret = PTR_ERR(sensor->tzd); sensor->tzd = NULL; @@ -278,7 +503,14 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev, } static const struct of_device_id of_hisi_thermal_match[] = { - { .compatible = "hisilicon,tsensor" }, + { + .compatible = "hisilicon,tsensor", + .data = hi6220_thermal_probe + }, + { + .compatible = "hisilicon,hi3660-tsensor", + .data = hi3660_thermal_probe + }, { /* end */ } }; MODULE_DEVICE_TABLE(of, of_hisi_thermal_match); @@ -295,88 +527,63 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor, static int hisi_thermal_probe(struct platform_device *pdev) { struct hisi_thermal_data *data; - struct resource *res; - int i; + int const (*platform_probe)(struct hisi_thermal_data *); + struct device *dev = &pdev->dev; int ret; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - mutex_init(&data->thermal_lock); data->pdev = pdev; + platform_set_drvdata(pdev, data); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->regs)) { - dev_err(&pdev->dev, "failed to get io address\n"); - return PTR_ERR(data->regs); + platform_probe = of_device_get_match_data(dev); + if (!platform_probe) { + dev_err(dev, "failed to get probe func\n"); + return -EINVAL; } - data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) - return data->irq; - - ret = devm_request_threaded_irq(&pdev->dev, data->irq, - hisi_thermal_alarm_irq, - hisi_thermal_alarm_irq_thread, - 0, "hisi_thermal", data); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); + ret = platform_probe(data); + if (ret) return ret; - } - platform_set_drvdata(pdev, data); - - data->clk = devm_clk_get(&pdev->dev, "thermal_clk"); - if (IS_ERR(data->clk)) { - ret = PTR_ERR(data->clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get thermal clk: %d\n", ret); + ret = hisi_thermal_register_sensor(pdev, data, + &data->sensor); + if (ret) { + dev_err(dev, "failed to register thermal sensor: %d\n", ret); return ret; } - /* enable clock for thermal */ - ret = clk_prepare_enable(data->clk); + ret = data->enable_sensor(data); if (ret) { - dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); + dev_err(dev, "Failed to setup the sensor: %d\n", ret); return ret; } - hisi_thermal_enable_bind_irq_sensor(data); - irq_get_irqchip_state(data->irq, IRQCHIP_STATE_MASKED, - &data->irq_enabled); - - for (i = 0; i < HISI_MAX_SENSORS; ++i) { - ret = hisi_thermal_register_sensor(pdev, data, - &data->sensors[i], i); - if (ret) - dev_err(&pdev->dev, - "failed to register thermal sensor: %d\n", ret); - else - hisi_thermal_toggle_sensor(&data->sensors[i], true); + if (data->irq) { + ret = devm_request_threaded_irq(dev, data->irq, NULL, + hisi_thermal_alarm_irq_thread, + IRQF_ONESHOT, "hisi_thermal", data); + if (ret < 0) { + dev_err(dev, "failed to request alarm irq: %d\n", ret); + return ret; + } } + hisi_thermal_toggle_sensor(&data->sensor, true); + return 0; } static int hisi_thermal_remove(struct platform_device *pdev) { struct hisi_thermal_data *data = platform_get_drvdata(pdev); - int i; + struct hisi_thermal_sensor *sensor = &data->sensor; - for (i = 0; i < HISI_MAX_SENSORS; i++) { - struct hisi_thermal_sensor *sensor = &data->sensors[i]; + hisi_thermal_toggle_sensor(sensor, false); - if (!sensor->tzd) - continue; - - hisi_thermal_toggle_sensor(sensor, false); - } - - hisi_thermal_disable_sensor(data); - clk_disable_unprepare(data->clk); + data->disable_sensor(data); return 0; } @@ -386,10 +593,7 @@ static int hisi_thermal_suspend(struct device *dev) { struct hisi_thermal_data *data = dev_get_drvdata(dev); - hisi_thermal_disable_sensor(data); - data->irq_enabled = false; - - clk_disable_unprepare(data->clk); + data->disable_sensor(data); return 0; } @@ -397,16 +601,8 @@ static int hisi_thermal_suspend(struct device *dev) static int hisi_thermal_resume(struct device *dev) { struct hisi_thermal_data *data = dev_get_drvdata(dev); - int ret; - ret = clk_prepare_enable(data->clk); - if (ret) - return ret; - - data->irq_enabled = true; - hisi_thermal_enable_bind_irq_sensor(data); - - return 0; + return data->enable_sensor(data); } #endif diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 4798b4b1fd77..e7d4ffc3de7f 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -25,6 +25,7 @@ #include #include #include +#include #define REG_SET 0x4 #define REG_CLR 0x8 @@ -94,7 +95,7 @@ struct imx_thermal_data { struct thermal_cooling_device *cdev; enum thermal_device_mode mode; struct regmap *tempmon; - u32 c1, c2; /* See formula in imx_get_sensor_data() */ + u32 c1, c2; /* See formula in imx_init_calib() */ int temp_passive; int temp_critical; int temp_max; @@ -177,7 +178,7 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp) n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT; - /* See imx_get_sensor_data() for formula derivation */ + /* See imx_init_calib() for formula derivation */ *temp = data->c2 - n_meas * data->c1; /* Update alarm value to next higher trip point for TEMPMON_IMX6Q */ @@ -346,29 +347,12 @@ static struct thermal_zone_device_ops imx_tz_ops = { .set_trip_temp = imx_set_trip_temp, }; -static int imx_get_sensor_data(struct platform_device *pdev) +static int imx_init_calib(struct platform_device *pdev, u32 val) { struct imx_thermal_data *data = platform_get_drvdata(pdev); - struct regmap *map; int t1, n1; - int ret; - u32 val; u64 temp64; - map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "fsl,tempmon-data"); - if (IS_ERR(map)) { - ret = PTR_ERR(map); - dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret); - return ret; - } - - ret = regmap_read(map, OCOTP_ANA1, &val); - if (ret) { - dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret); - return ret; - } - if (val == 0 || val == ~0) { dev_err(&pdev->dev, "invalid sensor calibration data\n"); return -EINVAL; @@ -405,12 +389,12 @@ static int imx_get_sensor_data(struct platform_device *pdev) data->c1 = temp64; data->c2 = n1 * data->c1 + 1000 * t1; - /* use OTP for thermal grade */ - ret = regmap_read(map, OCOTP_MEM0, &val); - if (ret) { - dev_err(&pdev->dev, "failed to read temp grade: %d\n", ret); - return ret; - } + return 0; +} + +static void imx_init_temp_grade(struct platform_device *pdev, u32 val) +{ + struct imx_thermal_data *data = platform_get_drvdata(pdev); /* The maximum die temp is specified by the Temperature Grade */ switch ((val >> 6) & 0x3) { @@ -438,6 +422,55 @@ static int imx_get_sensor_data(struct platform_device *pdev) */ data->temp_critical = data->temp_max - (1000 * 5); data->temp_passive = data->temp_max - (1000 * 10); +} + +static int imx_init_from_tempmon_data(struct platform_device *pdev) +{ + struct regmap *map; + int ret; + u32 val; + + map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "fsl,tempmon-data"); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret); + return ret; + } + + ret = regmap_read(map, OCOTP_ANA1, &val); + if (ret) { + dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret); + return ret; + } + ret = imx_init_calib(pdev, val); + if (ret) + return ret; + + ret = regmap_read(map, OCOTP_MEM0, &val); + if (ret) { + dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret); + return ret; + } + imx_init_temp_grade(pdev, val); + + return 0; +} + +static int imx_init_from_nvmem_cells(struct platform_device *pdev) +{ + int ret; + u32 val; + + ret = nvmem_cell_read_u32(&pdev->dev, "calib", &val); + if (ret) + return ret; + imx_init_calib(pdev, val); + + ret = nvmem_cell_read_u32(&pdev->dev, "temp_grade", &val); + if (ret) + return ret; + imx_init_temp_grade(pdev, val); return 0; } @@ -514,10 +547,21 @@ static int imx_thermal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - ret = imx_get_sensor_data(pdev); - if (ret) { - dev_err(&pdev->dev, "failed to get sensor data\n"); - return ret; + if (of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) { + ret = imx_init_from_nvmem_cells(pdev); + if (ret == -EPROBE_DEFER) + return ret; + if (ret) { + dev_err(&pdev->dev, "failed to init from nvmem: %d\n", + ret); + return ret; + } + } else { + ret = imx_init_from_tempmon_data(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to init from from fsl,tempmon-data\n"); + return ret; + } } /* Make sure sensor is in known good state for measurements */ diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index f02341f7134d..80bbf9ce2fb6 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -30,6 +30,10 @@ /* Skylake thermal reporting device */ #define PCI_DEVICE_ID_PROC_SKL_THERMAL 0x1903 +/* CannonLake thermal reporting device */ +#define PCI_DEVICE_ID_PROC_CNL_THERMAL 0x5a03 +#define PCI_DEVICE_ID_PROC_CFL_THERMAL 0x3E83 + /* Braswell thermal reporting device */ #define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC @@ -461,6 +465,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT1_THERMAL)}, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTX_THERMAL)}, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTP_THERMAL)}, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CNL_THERMAL)}, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CFL_THERMAL)}, { 0, }, }; diff --git a/drivers/thermal/intel_bxt_pmic_thermal.c b/drivers/thermal/intel_bxt_pmic_thermal.c index ef6b32242ccb..94cfd0064c43 100644 --- a/drivers/thermal/intel_bxt_pmic_thermal.c +++ b/drivers/thermal/intel_bxt_pmic_thermal.c @@ -166,7 +166,7 @@ static irqreturn_t pmic_thermal_irq_handler(int irq, void *data) struct pmic_thermal_data *td; struct intel_soc_pmic *pmic; struct regmap *regmap; - u8 reg_val, mask, irq_stat, trip; + u8 reg_val, mask, irq_stat; u16 reg, evt_stat_reg; int i, j, ret; @@ -201,7 +201,6 @@ static irqreturn_t pmic_thermal_irq_handler(int irq, void *data) if (regmap_read(regmap, evt_stat_reg, &ret)) return IRQ_HANDLED; - trip = td->maps[i].trip_config[j].trip_num; tzd = thermal_zone_get_zone_by_name(td->maps[i].handle); if (!IS_ERR(tzd)) thermal_zone_device_update(tzd, diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c index c60b1cfcc64e..8a7f69b4b022 100644 --- a/drivers/thermal/intel_pch_thermal.c +++ b/drivers/thermal/intel_pch_thermal.c @@ -30,6 +30,8 @@ #define PCH_THERMAL_DID_WPT 0x9CA4 /* Wildcat Point */ #define PCH_THERMAL_DID_SKL 0x9D31 /* Skylake PCH */ #define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */ +#define PCH_THERMAL_DID_CNL 0x9Df9 /* CNL PCH */ +#define PCH_THERMAL_DID_CNL_H 0xA379 /* CNL-H PCH */ /* Wildcat Point-LP PCH Thermal registers */ #define WPT_TEMP 0x0000 /* Temperature */ @@ -278,6 +280,7 @@ enum board_ids { board_hsw, board_wpt, board_skl, + board_cnl, }; static const struct board_info { @@ -296,6 +299,10 @@ static const struct board_info { .name = "pch_skylake", .ops = &pch_dev_ops_wpt, }, + [board_cnl] = { + .name = "pch_cannonlake", + .ops = &pch_dev_ops_wpt, + }, }; static int intel_pch_thermal_probe(struct pci_dev *pdev, @@ -398,6 +405,10 @@ static const struct pci_device_id intel_pch_thermal_id[] = { .driver_data = board_skl, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H), .driver_data = board_skl, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL), + .driver_data = board_cnl, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H), + .driver_data = board_cnl, }, { 0, }, }; MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index d718cd179ddb..4540e892b61d 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -675,13 +675,13 @@ static int __init powerclamp_probe(void) { if (!x86_match_cpu(intel_powerclamp_ids)) { - pr_err("CPU does not support MWAIT"); + pr_err("CPU does not support MWAIT\n"); return -ENODEV; } /* The goal for idle time alignment is to achieve package cstate. */ if (!has_pkg_state_counter()) { - pr_info("No package C-state available"); + pr_info("No package C-state available\n"); return -ENODEV; } diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index f50241962ad2..95f987d5aa71 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c @@ -125,7 +125,7 @@ static int qpnp_tm_get_temp(void *data, int *temp) if (!temp) return -EINVAL; - if (IS_ERR(chip->adc)) { + if (!chip->adc) { ret = qpnp_tm_update_temp_no_adc(chip); if (ret < 0) return ret; @@ -224,67 +224,53 @@ static int qpnp_tm_probe(struct platform_device *pdev) return irq; /* ADC based measurements are optional */ - chip->adc = iio_channel_get(&pdev->dev, "thermal"); - if (PTR_ERR(chip->adc) == -EPROBE_DEFER) - return PTR_ERR(chip->adc); + chip->adc = devm_iio_channel_get(&pdev->dev, "thermal"); + if (IS_ERR(chip->adc)) { + ret = PTR_ERR(chip->adc); + chip->adc = NULL; + if (ret == -EPROBE_DEFER) + return ret; + } chip->base = res; ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type); if (ret < 0) { dev_err(&pdev->dev, "could not read type\n"); - goto fail; + return ret; } ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype); if (ret < 0) { dev_err(&pdev->dev, "could not read subtype\n"); - goto fail; + return ret; } if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) { dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", type, subtype); - ret = -ENODEV; - goto fail; + return -ENODEV; } ret = qpnp_tm_init(chip); if (ret < 0) { dev_err(&pdev->dev, "init failed\n"); - goto fail; + return ret; } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qpnp_tm_isr, IRQF_ONESHOT, node->name, chip); if (ret < 0) - goto fail; + return ret; chip->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, chip, &qpnp_tm_sensor_ops); if (IS_ERR(chip->tz_dev)) { dev_err(&pdev->dev, "failed to register sensor\n"); - ret = PTR_ERR(chip->tz_dev); - goto fail; + return PTR_ERR(chip->tz_dev); } return 0; - -fail: - if (!IS_ERR(chip->adc)) - iio_channel_release(chip->adc); - - return ret; -} - -static int qpnp_tm_remove(struct platform_device *pdev) -{ - struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev); - - if (!IS_ERR(chip->adc)) - iio_channel_release(chip->adc); - - return 0; } static const struct of_device_id qpnp_tm_match_table[] = { @@ -299,7 +285,6 @@ static struct platform_driver qpnp_tm_driver = { .of_match_table = qpnp_tm_match_table, }, .probe = qpnp_tm_probe, - .remove = qpnp_tm_remove, }; module_platform_driver(qpnp_tm_driver); diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 203aca44a2bb..561a0a332208 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "thermal_core.h" @@ -90,10 +91,6 @@ struct rcar_gen3_thermal_priv { struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; unsigned int num_tscs; spinlock_t lock; /* Protect interrupts on and off */ - const struct rcar_gen3_thermal_data *data; -}; - -struct rcar_gen3_thermal_data { void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc); }; @@ -278,7 +275,12 @@ static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data) return IRQ_HANDLED; } -static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) +static const struct soc_device_attribute r8a7795es1[] = { + { .soc_id = "r8a7795", .revision = "ES1.*" }, + { /* sentinel */ } +}; + +static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc) { rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 0x0); @@ -303,7 +305,7 @@ static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) usleep_range(1000, 2000); } -static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) +static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc) { u32 reg_val; @@ -324,17 +326,9 @@ static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) usleep_range(1000, 2000); } -static const struct rcar_gen3_thermal_data r8a7795_data = { - .thermal_init = r8a7795_thermal_init, -}; - -static const struct rcar_gen3_thermal_data r8a7796_data = { - .thermal_init = r8a7796_thermal_init, -}; - static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { - { .compatible = "renesas,r8a7795-thermal", .data = &r8a7795_data}, - { .compatible = "renesas,r8a7796-thermal", .data = &r8a7796_data}, + { .compatible = "renesas,r8a7795-thermal", }, + { .compatible = "renesas,r8a7796-thermal", }, {}, }; MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); @@ -371,7 +365,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->data = of_device_get_match_data(dev); + priv->thermal_init = rcar_gen3_thermal_init; + if (soc_device_match(r8a7795es1)) + priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1; spin_lock_init(&priv->lock); @@ -423,7 +419,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) priv->tscs[i] = tsc; - priv->data->thermal_init(tsc); + priv->thermal_init(tsc); rcar_gen3_thermal_calc_coefs(&tsc->coef, ptat, thcode[i]); zone = devm_thermal_zone_of_sensor_register(dev, i, tsc, @@ -476,7 +472,7 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev) for (i = 0; i < priv->num_tscs; i++) { struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; - priv->data->thermal_init(tsc); + priv->thermal_init(tsc); rcar_gen3_thermal_set_trips(tsc, tsc->low, tsc->high); } diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 206035139110..f36375d5a16c 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -242,6 +242,45 @@ struct tsadc_table { int temp; }; +static const struct tsadc_table rv1108_table[] = { + {0, -40000}, + {374, -40000}, + {382, -35000}, + {389, -30000}, + {397, -25000}, + {405, -20000}, + {413, -15000}, + {421, -10000}, + {429, -5000}, + {436, 0}, + {444, 5000}, + {452, 10000}, + {460, 15000}, + {468, 20000}, + {476, 25000}, + {483, 30000}, + {491, 35000}, + {499, 40000}, + {507, 45000}, + {515, 50000}, + {523, 55000}, + {531, 60000}, + {539, 65000}, + {547, 70000}, + {555, 75000}, + {562, 80000}, + {570, 85000}, + {578, 90000}, + {586, 95000}, + {594, 100000}, + {602, 105000}, + {610, 110000}, + {618, 115000}, + {626, 120000}, + {634, 125000}, + {TSADCV2_DATA_MASK, 125000}, +}; + static const struct tsadc_table rk3228_code_table[] = { {0, -40000}, {588, -40000}, @@ -779,6 +818,30 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, writel_relaxed(val, regs + TSADCV2_INT_EN); } +static const struct rockchip_tsadc_chip rv1108_tsadc_data = { + .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ + .chn_num = 1, /* one channel for tsadc */ + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = { + .id = rv1108_table, + .length = ARRAY_SIZE(rv1108_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + static const struct rockchip_tsadc_chip rk3228_tsadc_data = { .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ .chn_num = 1, /* one channel for tsadc */ @@ -927,6 +990,10 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = { }; static const struct of_device_id of_rockchip_thermal_match[] = { + { + .compatible = "rockchip,rv1108-tsadc", + .data = (void *)&rv1108_tsadc_data, + }, { .compatible = "rockchip,rk3228-tsadc", .data = (void *)&rk3228_tsadc_data, diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index be95826631b7..ee047ca43084 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -31,8 +31,7 @@ * If the temperature is higher than a trip point, * a. if the trend is THERMAL_TREND_RAISING, use higher cooling * state for this trip point - * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling - * state for this trip point + * b. if the trend is THERMAL_TREND_DROPPING, do nothing * c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit * for this trip point * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit @@ -94,9 +93,11 @@ static unsigned long get_target_state(struct thermal_instance *instance, if (!throttle) next_target = THERMAL_NO_TARGET; } else { - next_target = cur_state - 1; - if (next_target > instance->upper) - next_target = instance->upper; + if (!throttle) { + next_target = cur_state - 1; + if (next_target > instance->upper) + next_target = instance->upper; + } } break; case THERMAL_TREND_DROP_FULL: diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 7d2db23d71a3..075db1de5e53 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -483,7 +483,7 @@ static int throttrip_program(struct device *dev, unsigned int throt; u32 r, reg_off; - if (!dev || !sg || !stc || !stc->init) + if (!sg || !stc || !stc->init) return -EINVAL; temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 73f55d6a1721..46d3005335c7 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -126,37 +126,22 @@ static int gadc_thermal_probe(struct platform_device *pdev) gti->dev = &pdev->dev; platform_set_drvdata(pdev, gti); - gti->channel = iio_channel_get(&pdev->dev, "sensor-channel"); + gti->channel = devm_iio_channel_get(&pdev->dev, "sensor-channel"); if (IS_ERR(gti->channel)) { ret = PTR_ERR(gti->channel); dev_err(&pdev->dev, "IIO channel not found: %d\n", ret); return ret; } - gti->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, - gti, &gadc_thermal_ops); + gti->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, gti, + &gadc_thermal_ops); if (IS_ERR(gti->tz_dev)) { ret = PTR_ERR(gti->tz_dev); dev_err(&pdev->dev, "Thermal zone sensor register failed: %d\n", ret); - goto sensor_fail; + return ret; } - return 0; - -sensor_fail: - iio_channel_release(gti->channel); - - return ret; -} - -static int gadc_thermal_remove(struct platform_device *pdev) -{ - struct gadc_thermal_info *gti = platform_get_drvdata(pdev); - - thermal_zone_of_sensor_unregister(&pdev->dev, gti->tz_dev); - iio_channel_release(gti->channel); - return 0; } @@ -172,7 +157,6 @@ static struct platform_driver gadc_thermal_driver = { .of_match_table = of_adc_thermal_match, }, .probe = gadc_thermal_probe, - .remove = gadc_thermal_remove, }; module_platform_driver(gadc_thermal_driver); diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index c211a8e4a210..b4f981daeaf2 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -278,7 +278,8 @@ int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) if (data) { cpufreq_cooling_unregister(data->cool_dev); - cpufreq_cpu_put(data->policy); + if (data->policy) + cpufreq_cpu_put(data->policy); } return 0; diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 7a4c8022c023..af4da9507180 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -156,42 +156,34 @@ static int store_utf8(u16 c, char *p) int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) { struct vc_data *vc = vc_cons[fg_console].d; - int sel_mode, new_sel_start, new_sel_end, spc; + int new_sel_start, new_sel_end, spc; + struct tiocl_selection v; char *bp, *obp; int i, ps, pe, multiplier; u16 c; int mode; poke_blanked_console(); + if (copy_from_user(&v, sel, sizeof(*sel))) + return -EFAULT; - { unsigned short xs, ys, xe, ye; + v.xs = limit(v.xs - 1, vc->vc_cols - 1); + v.ys = limit(v.ys - 1, vc->vc_rows - 1); + v.xe = limit(v.xe - 1, vc->vc_cols - 1); + v.ye = limit(v.ye - 1, vc->vc_rows - 1); + ps = v.ys * vc->vc_size_row + (v.xs << 1); + pe = v.ye * vc->vc_size_row + (v.xe << 1); - if (!access_ok(VERIFY_READ, sel, sizeof(*sel))) - return -EFAULT; - __get_user(xs, &sel->xs); - __get_user(ys, &sel->ys); - __get_user(xe, &sel->xe); - __get_user(ye, &sel->ye); - __get_user(sel_mode, &sel->sel_mode); - xs--; ys--; xe--; ye--; - xs = limit(xs, vc->vc_cols - 1); - ys = limit(ys, vc->vc_rows - 1); - xe = limit(xe, vc->vc_cols - 1); - ye = limit(ye, vc->vc_rows - 1); - ps = ys * vc->vc_size_row + (xs << 1); - pe = ye * vc->vc_size_row + (xe << 1); - - if (sel_mode == TIOCL_SELCLEAR) { - /* useful for screendump without selection highlights */ - clear_selection(); - return 0; - } - - if (mouse_reporting() && (sel_mode & TIOCL_SELMOUSEREPORT)) { - mouse_report(tty, sel_mode & TIOCL_SELBUTTONMASK, xs, ys); - return 0; - } - } + if (v.sel_mode == TIOCL_SELCLEAR) { + /* useful for screendump without selection highlights */ + clear_selection(); + return 0; + } + + if (mouse_reporting() && (v.sel_mode & TIOCL_SELMOUSEREPORT)) { + mouse_report(tty, v.sel_mode & TIOCL_SELBUTTONMASK, v.xs, v.ys); + return 0; + } if (ps > pe) /* make sel_start <= sel_end */ { @@ -210,7 +202,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t else use_unicode = 0; - switch (sel_mode) + switch (v.sel_mode) { case TIOCL_SELCHAR: /* character-by-character selection */ new_sel_start = ps; diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 2d2b420598b2..d61be307256a 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -843,58 +843,44 @@ int vt_ioctl(struct tty_struct *tty, case VT_RESIZEX: { - struct vt_consize __user *vtconsize = up; - ushort ll,cc,vlin,clin,vcol,ccol; + struct vt_consize v; if (!perm) return -EPERM; - if (!access_ok(VERIFY_READ, vtconsize, - sizeof(struct vt_consize))) { - ret = -EFAULT; - break; - } + if (copy_from_user(&v, up, sizeof(struct vt_consize))) + return -EFAULT; /* FIXME: Should check the copies properly */ - __get_user(ll, &vtconsize->v_rows); - __get_user(cc, &vtconsize->v_cols); - __get_user(vlin, &vtconsize->v_vlin); - __get_user(clin, &vtconsize->v_clin); - __get_user(vcol, &vtconsize->v_vcol); - __get_user(ccol, &vtconsize->v_ccol); - vlin = vlin ? vlin : vc->vc_scan_lines; - if (clin) { - if (ll) { - if (ll != vlin/clin) { - /* Parameters don't add up */ - ret = -EINVAL; - break; - } - } else - ll = vlin/clin; + if (!v.v_vlin) + v.v_vlin = vc->vc_scan_lines; + if (v.v_clin) { + int rows = v.v_vlin/v.v_clin; + if (v.v_rows != rows) { + if (v.v_rows) /* Parameters don't add up */ + return -EINVAL; + v.v_rows = rows; + } } - if (vcol && ccol) { - if (cc) { - if (cc != vcol/ccol) { - ret = -EINVAL; - break; - } - } else - cc = vcol/ccol; + if (v.v_vcol && v.v_ccol) { + int cols = v.v_vcol/v.v_ccol; + if (v.v_cols != cols) { + if (v.v_cols) + return -EINVAL; + v.v_cols = cols; + } } - if (clin > 32) { - ret = -EINVAL; - break; - } - + if (v.v_clin > 32) + return -EINVAL; + for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons[i].d) continue; console_lock(); - if (vlin) - vc_cons[i].d->vc_scan_lines = vlin; - if (clin) - vc_cons[i].d->vc_font.height = clin; + if (v.v_vlin) + vc_cons[i].d->vc_scan_lines = v.v_vlin; + if (v.v_clin) + vc_cons[i].d->vc_font.height = v.v_clin; vc_cons[i].d->vc_resize_user = 1; - vc_resize(vc_cons[i].d, cc, ll); + vc_resize(vc_cons[i].d, v.v_cols, v.v_rows); console_unlock(); } break; diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 1bc9be829fb4..71517b3c5558 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -210,12 +210,6 @@ static struct workqueue_struct *vhost_scsi_workqueue; static DEFINE_MUTEX(vhost_scsi_mutex); static LIST_HEAD(vhost_scsi_list); -static int iov_num_pages(void __user *iov_base, size_t iov_len) -{ - return (PAGE_ALIGN((unsigned long)iov_base + iov_len) - - ((unsigned long)iov_base & PAGE_MASK)) >> PAGE_SHIFT; -} - static void vhost_scsi_done_inflight(struct kref *kref) { struct vhost_scsi_inflight *inflight; @@ -618,48 +612,31 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq, struct vhost_scsi_tpg *tpg, */ static int vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd, - void __user *ptr, - size_t len, + struct iov_iter *iter, struct scatterlist *sgl, bool write) { - unsigned int npages = 0, offset, nbytes; - unsigned int pages_nr = iov_num_pages(ptr, len); - struct scatterlist *sg = sgl; struct page **pages = cmd->tvc_upages; - int ret, i; - - if (pages_nr > VHOST_SCSI_PREALLOC_UPAGES) { - pr_err("vhost_scsi_map_to_sgl() pages_nr: %u greater than" - " preallocated VHOST_SCSI_PREALLOC_UPAGES: %u\n", - pages_nr, VHOST_SCSI_PREALLOC_UPAGES); - return -ENOBUFS; - } + struct scatterlist *sg = sgl; + ssize_t bytes; + size_t offset; + unsigned int npages = 0; - ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages); + bytes = iov_iter_get_pages(iter, pages, LONG_MAX, + VHOST_SCSI_PREALLOC_UPAGES, &offset); /* No pages were pinned */ - if (ret < 0) - goto out; - /* Less pages pinned than wanted */ - if (ret != pages_nr) { - for (i = 0; i < ret; i++) - put_page(pages[i]); - ret = -EFAULT; - goto out; - } + if (bytes <= 0) + return bytes < 0 ? bytes : -EFAULT; - while (len > 0) { - offset = (uintptr_t)ptr & ~PAGE_MASK; - nbytes = min_t(unsigned int, PAGE_SIZE - offset, len); - sg_set_page(sg, pages[npages], nbytes, offset); - ptr += nbytes; - len -= nbytes; - sg++; - npages++; - } + iov_iter_advance(iter, bytes); -out: - return ret; + while (bytes) { + unsigned n = min_t(unsigned, PAGE_SIZE - offset, bytes); + sg_set_page(sg++, pages[npages++], n, offset); + bytes -= n; + offset = 0; + } + return npages; } static int @@ -687,24 +664,20 @@ vhost_scsi_iov_to_sgl(struct vhost_scsi_cmd *cmd, bool write, struct iov_iter *iter, struct scatterlist *sg, int sg_count) { - size_t off = iter->iov_offset; - int i, ret; - - for (i = 0; i < iter->nr_segs; i++) { - void __user *base = iter->iov[i].iov_base + off; - size_t len = iter->iov[i].iov_len - off; + struct scatterlist *p = sg; + int ret; - ret = vhost_scsi_map_to_sgl(cmd, base, len, sg, write); + while (iov_iter_count(iter)) { + ret = vhost_scsi_map_to_sgl(cmd, iter, sg, write); if (ret < 0) { - for (i = 0; i < sg_count; i++) { - struct page *page = sg_page(&sg[i]); + while (p < sg) { + struct page *page = sg_page(p++); if (page) put_page(page); } return ret; } sg += ret; - off = 0; } return 0; } diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5e58f5ec0a28..2f615b7f1c9f 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -905,16 +905,6 @@ config FB_LEO This is the frame buffer device driver for the SBUS-based Sun ZX (leo) frame buffer cards. -config FB_IGA - bool "IGA 168x display support" - depends on (FB = y) && SPARC32 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the framebuffer device for the INTERGRAPHICS 1680 and - successor frame buffer cards. - config FB_XVR500 bool "Sun XVR-500 3DLABS Wildcat support" depends on (FB = y) && PCI && SPARC64 diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 8895536a20d6..115961e0721b 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -65,7 +65,6 @@ obj-$(CONFIG_FB_HGA) += hgafb.o obj-$(CONFIG_FB_XVR500) += sunxvr500.o obj-$(CONFIG_FB_XVR2500) += sunxvr2500.o obj-$(CONFIG_FB_XVR1000) += sunxvr1000.o -obj-$(CONFIG_FB_IGA) += igafb.o obj-$(CONFIG_FB_APOLLO) += dnfb.o obj-$(CONFIG_FB_Q40) += q40fb.o obj-$(CONFIG_FB_TGA) += tgafb.o diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 3ec72f19114b..a9a8272f7a6e 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -2272,10 +2272,10 @@ static void aty_bl_exit(struct backlight_device *bd) static void aty_calc_mem_refresh(struct atyfb_par *par, int xclk) { - const int ragepro_tbl[] = { + static const int ragepro_tbl[] = { 44, 50, 55, 66, 75, 80, 100 }; - const int ragexl_tbl[] = { + static const int ragexl_tbl[] = { 50, 66, 75, 83, 90, 95, 100, 105, 110, 115, 120, 125, 133, 143, 166 }; diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index 1e2ec360f8c1..4d77daeecf99 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -1454,9 +1454,9 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg /* * Timer function for delayed LVDS panel power up/down */ -static void radeon_lvds_timer_func(unsigned long data) +static void radeon_lvds_timer_func(struct timer_list *t) { - struct radeonfb_info *rinfo = (struct radeonfb_info *)data; + struct radeonfb_info *rinfo = from_timer(rinfo, t, lvds_timer); radeon_engine_idle(); @@ -1534,7 +1534,7 @@ void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode, static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *regs, unsigned long freq) { - const struct { + static const struct { int divider; int bitvalue; } *post_div, @@ -2291,9 +2291,7 @@ static int radeonfb_pci_register(struct pci_dev *pdev, rinfo->pdev = pdev; spin_lock_init(&rinfo->reg_lock); - init_timer(&rinfo->lvds_timer); - rinfo->lvds_timer.function = radeon_lvds_timer_func; - rinfo->lvds_timer.data = (unsigned long)rinfo; + timer_setup(&rinfo->lvds_timer, radeon_lvds_timer_func, 0); c1 = ent->device >> 8; c2 = ent->device & 0xff; diff --git a/drivers/video/fbdev/aty/radeon_pm.c b/drivers/video/fbdev/aty/radeon_pm.c index f7c253dd5899..7137c12cbcee 100644 --- a/drivers/video/fbdev/aty/radeon_pm.c +++ b/drivers/video/fbdev/aty/radeon_pm.c @@ -1208,9 +1208,11 @@ static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo) case 1: if (mc & 0x4) break; + /* fall through */ case 2: dll_sleep_mask |= MDLL_R300_RDCK__MRDCKB_SLEEP; dll_reset_mask |= MDLL_R300_RDCK__MRDCKB_RESET; + /* fall through */ case 0: dll_sleep_mask |= MDLL_R300_RDCK__MRDCKA_SLEEP; dll_reset_mask |= MDLL_R300_RDCK__MRDCKA_RESET; @@ -1219,6 +1221,7 @@ static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo) case 1: if (!(mc & 0x4)) break; + /* fall through */ case 2: dll_sleep_mask |= MDLL_R300_RDCK__MRDCKD_SLEEP; dll_reset_mask |= MDLL_R300_RDCK__MRDCKD_RESET; diff --git a/drivers/video/fbdev/au1200fb.c b/drivers/video/fbdev/au1200fb.c index 5f04b4096c42..87d5a62bf6ca 100644 --- a/drivers/video/fbdev/au1200fb.c +++ b/drivers/video/fbdev/au1200fb.c @@ -1518,7 +1518,7 @@ static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id) static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) { struct fb_info *fbi = fbdev->fb_info; - int bpp; + int bpp, ret; fbi->fbops = &au1200fb_fb_ops; @@ -1546,15 +1546,14 @@ static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) } fbi->pseudo_palette = kcalloc(16, sizeof(u32), GFP_KERNEL); - if (!fbi->pseudo_palette) { + if (!fbi->pseudo_palette) return -ENOMEM; - } - if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { + ret = fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0); + if (ret < 0) { print_err("Fail to allocate colormap (%d entries)", - AU1200_LCD_NBR_PALETTE_ENTRIES); - kfree(fbi->pseudo_palette); - return -EFAULT; + AU1200_LCD_NBR_PALETTE_ENTRIES); + return ret; } strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id)); @@ -1668,10 +1667,6 @@ static int au1200fb_drv_probe(struct platform_device *dev) printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); - /* shut gcc up */ - ret = 0; - fbdev = NULL; - for (plane = 0; plane < device_count; ++plane) { bpp = winbpp(win->w[plane].mode_winctrl1); if (win->w[plane].xres == 0) @@ -1681,8 +1676,10 @@ static int au1200fb_drv_probe(struct platform_device *dev) fbi = framebuffer_alloc(sizeof(struct au1200fb_device), &dev->dev); - if (!fbi) + if (!fbi) { + ret = -ENOMEM; goto failed; + } _au1200fb_infos[plane] = fbi; fbdev = fbi->par; @@ -1701,7 +1698,8 @@ static int au1200fb_drv_probe(struct platform_device *dev) if (!fbdev->fb_mem) { print_err("fail to allocate frambuffer (size: %dK))", fbdev->fb_len / 1024); - return -ENOMEM; + ret = -ENOMEM; + goto failed; } /* @@ -1718,7 +1716,8 @@ static int au1200fb_drv_probe(struct platform_device *dev) print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); /* Init FB data */ - if ((ret = au1200fb_init_fbinfo(fbdev)) < 0) + ret = au1200fb_init_fbinfo(fbdev); + if (ret < 0) goto failed; /* Register new framebuffer */ @@ -1758,21 +1757,26 @@ static int au1200fb_drv_probe(struct platform_device *dev) return 0; failed: - /* NOTE: This only does the current plane/window that failed; others are still active */ - if (fbi) { + for (plane = 0; plane < device_count; ++plane) { + fbi = _au1200fb_infos[plane]; + if (!fbi) + break; + + /* Clean up all probe data */ + unregister_framebuffer(fbi); if (fbi->cmap.len != 0) fb_dealloc_cmap(&fbi->cmap); kfree(fbi->pseudo_palette); + + framebuffer_release(fbi); + _au1200fb_infos[plane] = NULL; } - if (plane == 0) - free_irq(AU1200_LCD_INT, (void*)dev); return ret; } static int au1200fb_drv_remove(struct platform_device *dev) { struct au1200fb_platdata *pd = platform_get_drvdata(dev); - struct au1200fb_device *fbdev; struct fb_info *fbi; int plane; @@ -1781,7 +1785,6 @@ static int au1200fb_drv_remove(struct platform_device *dev) for (plane = 0; plane < device_count; ++plane) { fbi = _au1200fb_infos[plane]; - fbdev = fbi->par; /* Clean up all probe data */ unregister_framebuffer(fbi); diff --git a/drivers/video/fbdev/cirrusfb.c b/drivers/video/fbdev/cirrusfb.c index d992aa5eb3f0..b3be06dd2908 100644 --- a/drivers/video/fbdev/cirrusfb.c +++ b/drivers/video/fbdev/cirrusfb.c @@ -1477,10 +1477,12 @@ static void init_vgachip(struct fb_info *info) mdelay(100); /* mode */ vga_wgfx(cinfo->regbase, CL_GR31, 0x00); - case BT_GD5480: /* fall through */ + /* fall through */ + case BT_GD5480: /* from Klaus' NetBSD driver: */ vga_wgfx(cinfo->regbase, CL_GR2F, 0x00); - case BT_ALPINE: /* fall through */ + /* fall through */ + case BT_ALPINE: /* put blitter into 542x compat */ vga_wgfx(cinfo->regbase, CL_GR33, 0x00); break; diff --git a/drivers/video/fbdev/controlfb.h b/drivers/video/fbdev/controlfb.h index 6026c60fc100..261522fabdac 100644 --- a/drivers/video/fbdev/controlfb.h +++ b/drivers/video/fbdev/controlfb.h @@ -141,5 +141,7 @@ static struct max_cmodes control_mac_modes[] = { {{ 1, 2}}, /* 1152x870, 75Hz */ {{ 0, 1}}, /* 1280x960, 75Hz */ {{ 0, 1}}, /* 1280x1024, 75Hz */ + {{ 1, 2}}, /* 1152x768, 60Hz */ + {{ 0, 1}}, /* 1600x1024, 60Hz */ }; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 04612f938bab..929ca472c524 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -395,10 +395,10 @@ static void fb_flashcursor(struct work_struct *work) console_unlock(); } -static void cursor_timer_handler(unsigned long dev_addr) +static void cursor_timer_handler(struct timer_list *t) { - struct fb_info *info = (struct fb_info *) dev_addr; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_ops *ops = from_timer(ops, t, cursor_timer); + struct fb_info *info = ops->info; queue_work(system_power_efficient_wq, &info->queue); mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); @@ -414,8 +414,7 @@ static void fbcon_add_cursor_timer(struct fb_info *info) if (!info->queue.func) INIT_WORK(&info->queue, fb_flashcursor); - setup_timer(&ops->cursor_timer, cursor_timer_handler, - (unsigned long) info); + timer_setup(&ops->cursor_timer, cursor_timer_handler, 0); mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); ops->flags |= FBCON_FLAGS_CURSOR_TIMER; } @@ -714,6 +713,7 @@ static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, if (!err) { ops->cur_blink_jiffies = HZ / 5; + ops->info = info; info->fbcon_par = ops; if (vc) @@ -962,6 +962,7 @@ static const char *fbcon_startup(void) ops->graphics = 1; ops->cur_rotate = -1; ops->cur_blink_jiffies = HZ / 5; + ops->info = info; info->fbcon_par = ops; if (initial_rotation != -1) p->con_rotate = initial_rotation; diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 18f3ac144237..9f7744fbc962 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -69,6 +69,7 @@ struct fbcon_ops { struct timer_list cursor_timer; /* Cursor timer */ struct fb_cursor cursor_state; struct display *p; + struct fb_info *info; int currcon; /* Current VC. */ int cur_blink_jiffies; int cursor_flash; diff --git a/drivers/video/fbdev/dnfb.c b/drivers/video/fbdev/dnfb.c index 7b1492d34e98..5505fa00c634 100644 --- a/drivers/video/fbdev/dnfb.c +++ b/drivers/video/fbdev/dnfb.c @@ -115,7 +115,7 @@ static struct fb_ops dn_fb_ops = { .fb_imageblit = cfb_imageblit, }; -struct fb_var_screeninfo dnfb_var = { +static const struct fb_var_screeninfo dnfb_var = { .xres = 1280, .yres = 1024, .xres_virtual = 2048, @@ -242,16 +242,13 @@ static int dnfb_probe(struct platform_device *dev) info->screen_base = (u_char *) info->fix.smem_start; err = fb_alloc_cmap(&info->cmap, 2, 0); - if (err < 0) { - framebuffer_release(info); - return err; - } + if (err < 0) + goto release_framebuffer; err = register_framebuffer(info); if (err < 0) { fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - return err; + goto release_framebuffer; } platform_set_drvdata(dev, info); @@ -265,6 +262,10 @@ static int dnfb_probe(struct platform_device *dev) printk("apollo frame buffer alive and kicking !\n"); return err; + +release_framebuffer: + framebuffer_release(info); + return err; } static struct platform_driver dnfb_driver = { diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c index 7f6c9e6cfc6c..3b70044773b6 100644 --- a/drivers/video/fbdev/goldfishfb.c +++ b/drivers/video/fbdev/goldfishfb.c @@ -304,12 +304,18 @@ static int goldfish_fb_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_fb_of_match[] = { + { .compatible = "google,goldfish-fb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_fb_of_match); static struct platform_driver goldfish_fb_driver = { .probe = goldfish_fb_probe, .remove = goldfish_fb_remove, .driver = { - .name = "goldfish_fb" + .name = "goldfish_fb", + .of_match_table = goldfish_fb_of_match, } }; diff --git a/drivers/video/fbdev/igafb.c b/drivers/video/fbdev/igafb.c deleted file mode 100644 index 486f18897414..000000000000 --- a/drivers/video/fbdev/igafb.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * linux/drivers/video/igafb.c -- Frame buffer device for IGA 1682 - * - * Copyright (C) 1998 Vladimir Roganov and Gleb Raiko - * - * This driver is partly based on the Frame buffer device for ATI Mach64 - * and partially on VESA-related code. - * - * Copyright (C) 1997-1998 Geert Uytterhoeven - * Copyright (C) 1998 Bernd Harries - * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - */ - -/****************************************************************************** - - TODO: - Despite of IGA Card has advanced graphic acceleration, - initial version is almost dummy and does not support it. - Support for video modes and acceleration must be added - together with accelerated X-Windows driver implementation. - - Most important thing at this moment is that we have working - JavaEngine1 console & X with new console interface. - -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef CONFIG_SPARC -#include -#include -#endif - -#include